fix(security): never sign tokens with a known JWT secret (FLAW-04)

Without JWT_SECRET, or with one of the published placeholders, the server
generates 32 random bytes once and keeps them in the data directory
(jwt.secret, mode 0600), so tokens cannot be forged and logins survive
restarts. Instances that ran on the old default log in once more.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
sandrodachandClaude Opus 5.5 committed 2026-10-29 07:35:45 +01:00
1 parent beeacf8943
commit 4cdbcbd6b5
5 files changed
+77 -5

No files matched your search

+25
View File
@@ -3,10 +3,13 @@ package config
import (
"log/slog"
"os"
"path/filepath"
"testing"
)
func TestConfigLogLevelAndDebug(t *testing.T) {
// Load writes the generated JWT secret into the data directory; keep it out of the repository
t.Setenv("DATA_DIR", t.TempDir())
os.Setenv("PORT", "9090")
defer os.Unsetenv("PORT")
@@ -56,3 +59,25 @@ func TestConfigLogLevelAndDebug(t *testing.T) {
os.Unsetenv("DEBUG")
os.Unsetenv("LOG_LEVEL")
}
func TestJWTSecretNeverUsesAKnownDefault(t *testing.T) {
dir := t.TempDir()
for _, env := range []string{"", "mindsift-default-selfhosted-jwt-secret-key-2026", "change_this_to_a_secure_random_key_min_32_chars"} {
got := resolveJWTSecret(env, dir)
if got == env || len(got) < 32 {
t.Fatalf("env %q: insecure secret %q", env, got)
}
}
// The generated secret is stable across restarts
first := resolveJWTSecret("", dir)
if again := resolveJWTSecret("", dir); again != first {
t.Fatalf("generated secret must persist, got %q then %q", first, again)
}
info, err := os.Stat(filepath.Join(dir, jwtSecretFile))
if err != nil || info.Mode().Perm() != 0o600 {
t.Fatalf("secret file must exist with mode 0600: %v %v", info, err)
}
if got := resolveJWTSecret("ein-eigener-sehr-langer-zufaelliger-wert-123456", dir); got != "ein-eigener-sehr-langer-zufaelliger-wert-123456" {
t.Fatalf("a real JWT_SECRET must win, got %q", got)
}
}