Skip to content

Commit 1ee156c

Browse files
committed
Limit jwt file permissions
1 parent e293ced commit 1ee156c

File tree

2 files changed

+63
-2
lines changed

2 files changed

+63
-2
lines changed

cli/commands/jwt/jwt.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ const (
3636
FlagOutputPath = "output-path"
3737
FlagInputPath = "input-path"
3838
ConfigFolder = "config"
39+
40+
secretDirPerms os.FileMode = 0o700
41+
secretFilePerms os.FileMode = 0o600
3942
)
4043

4144
// Commands creates a new command for managing JWT secrets.
@@ -143,7 +146,7 @@ func generateAuthSecretInFile(cmd *cobra.Command, fileName string) error {
143146
}
144147

145148
if !exists {
146-
if err = fs.MkdirAll(fileDir, os.ModePerm); err != nil {
149+
if err = fs.MkdirAll(fileDir, secretDirPerms); err != nil {
147150
return err
148151
}
149152
}
@@ -153,12 +156,24 @@ func generateAuthSecretInFile(cmd *cobra.Command, fileName string) error {
153156
return err
154157
}
155158

159+
fileExists, err := afero.Exists(fs, fileName)
160+
if err != nil {
161+
return err
162+
}
163+
156164
if err = afero.WriteFile(
157-
fs, fileName, []byte(secret.Hex()), os.ModePerm,
165+
fs, fileName, []byte(secret.Hex()), secretFilePerms,
158166
); err != nil {
159167
return err
160168
}
161169

170+
// WriteFile only sets permissions on file creation, so explicitly chmod if the file already existed.
171+
if fileExists {
172+
if err = fs.Chmod(fileName, secretFilePerms); err != nil {
173+
return err
174+
}
175+
}
176+
162177
cmd.Printf(
163178
"Successfully wrote new JSON-RPC authentication secret to: %s",
164179
fileName,

cli/commands/jwt/jwt_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,52 @@ func Test_NewGenerateJWTCommand(t *testing.T) {
106106
})
107107
}
108108

109+
func Test_GenerateJWTCommand_FilePermissions(t *testing.T) {
110+
t.Parallel()
111+
112+
t.Run("new file should have 0600 permissions", func(t *testing.T) {
113+
tempDir := t.TempDir()
114+
outputPath := filepath.Join(tempDir, "jwt.hex")
115+
116+
cmd := jwt.NewGenerateJWTCommand()
117+
cmd.SetArgs([]string{"--output-path", outputPath})
118+
require.NoError(t, cmd.Execute())
119+
120+
info, err := os.Stat(outputPath)
121+
require.NoError(t, err)
122+
require.Equal(t, os.FileMode(0600), info.Mode().Perm(),
123+
"new JWT file should have 0600 permissions")
124+
})
125+
126+
t.Run("pre-existing file with permissive permissions should be fixed", func(t *testing.T) {
127+
tempDir := t.TempDir()
128+
outputPath := filepath.Join(tempDir, "jwt.hex")
129+
130+
// Create file with world-readable permissions
131+
err := os.WriteFile(outputPath, []byte("old_content"), 0755)
132+
require.NoError(t, err)
133+
134+
// Verify it has permissive permissions
135+
info, err := os.Stat(outputPath)
136+
require.NoError(t, err)
137+
require.Equal(t, os.FileMode(0755), info.Mode().Perm())
138+
139+
// Run the generate command
140+
cmd := jwt.NewGenerateJWTCommand()
141+
cmd.SetArgs([]string{"--output-path", outputPath})
142+
require.NoError(t, cmd.Execute())
143+
144+
// Verify permissions are now restricted
145+
info, err = os.Stat(outputPath)
146+
require.NoError(t, err)
147+
require.Equal(t, os.FileMode(0600), info.Mode().Perm(),
148+
"pre-existing JWT file should have permissions fixed to 0600")
149+
150+
// Also verify the content is valid
151+
checkAuthFileIntegrity(t, outputPath)
152+
})
153+
}
154+
109155
func checkAuthFileIntegrity(tb testing.TB, fPath string) {
110156
tb.Helper()
111157
fs := afero.NewOsFs()

0 commit comments

Comments
 (0)