fix: security & privacy (rate limiter fix, csrf session cookie, logout on server, drafts stale token passw, login timing, password over 72b, JWT_SECRET complexity, SMTP TLS, CSV export neutralised, fonts bundled, upload metadata, hourly cleanup job, privacy.md report)

This commit is contained in:
Florian Sylvain committed 2026-10-22 00:49:19 +02:00
1 parent e2812ee0b9
commit ea98c5ec05
118 files changed
+3828 -130

No files matched your search

+70 -3
View File
@@ -2,16 +2,20 @@ package test
import (
"RenewCMS/internal/infrastructure/config"
"errors"
"strings"
"testing"
"time"
)
const testJWTSecret = "test-secret-0123456789abcdef0123456789"
func setBaseSettingsEnv(t *testing.T) {
t.Helper()
t.Setenv("ENVIRONMENT", "production")
t.Setenv("PUBLIC_URL", "https://cms.example.com")
t.Setenv("UPLOAD_DIR", "./data/uploads")
t.Setenv("JWT_SECRET", "test-secret")
t.Setenv("JWT_SECRET", testJWTSecret)
t.Setenv("DB_FILE", "./data/renewcms.db")
t.Setenv("PORT", "8080")
t.Setenv("SMTP_LOGIN", "smtp-login@example.com")
@@ -42,8 +46,8 @@ func TestNewSettingsFromEnvReadsAllFields(t *testing.T) {
if settings.UploadDir != "./data/uploads" {
t.Errorf("expected UploadDir %q, got %q", "./data/uploads", settings.UploadDir)
}
if settings.JWTSecret != "test-secret" {
t.Errorf("expected JWTSecret %q, got %q", "test-secret", settings.JWTSecret)
if settings.JWTSecret != testJWTSecret {
t.Errorf("expected JWTSecret %q, got %q", testJWTSecret, settings.JWTSecret)
}
if settings.DBFile != "./data/renewcms.db" {
t.Errorf("expected DBFile %q, got %q", "./data/renewcms.db", settings.DBFile)
@@ -175,3 +179,66 @@ func TestSettingsIsProductionFalseForNonProductionEnvironment(t *testing.T) {
t.Error("expected IsProduction to be false for environment \"development\"")
}
}
func TestNewSettingsFromEnvRefusesAWeakJWTSecret(t *testing.T) {
cases := map[string]string{
"empty": "",
"short": "test-secret",
"one byte short": "0123456789abcdef0123456789abcde",
"the .env.example placeholder": "change-me-to-a-long-random-string",
"the docker.env placeholder": "replace-with-output-of-openssl-rand-hex-32",
}
for name, secret := range cases {
t.Run(name, func(t *testing.T) {
setBaseSettingsEnv(t)
t.Setenv("JWT_SECRET", secret)
_, err := config.NewSettingsFromEnv()
if !errors.Is(err, config.ErrWeakJWTSecret) {
t.Errorf("expected ErrWeakJWTSecret, got %v", err)
}
if err != nil && !strings.Contains(err.Error(), "openssl rand -hex 32") {
t.Errorf("expected the error to say how to generate a secret, got %q", err)
}
})
}
t.Run("A secret of exactly the minimum length is accepted", func(t *testing.T) {
setBaseSettingsEnv(t)
t.Setenv("JWT_SECRET", strings.Repeat("k", config.MinJWTSecretBytes))
if _, err := config.NewSettingsFromEnv(); err != nil {
t.Errorf("expected no error, got %v", err)
}
})
}
func TestNewSettingsFromEnvReadsSmtpAllowInsecure(t *testing.T) {
for raw, want := range map[string]bool{"": false, "false": false, "true": true, "TRUE": true} {
t.Run("value "+raw, func(t *testing.T) {
setBaseSettingsEnv(t)
t.Setenv("SMTP_ALLOW_INSECURE", raw)
settings, err := config.NewSettingsFromEnv()
if err != nil {
t.Fatal(err)
}
if settings.Smtp.AllowInsecure != want {
t.Errorf("expected AllowInsecure %v, got %v", want, settings.Smtp.AllowInsecure)
}
})
}
t.Run("An unparsable value names the variable", func(t *testing.T) {
setBaseSettingsEnv(t)
t.Setenv("SMTP_ALLOW_INSECURE", "maybe")
_, err := config.NewSettingsFromEnv()
if err == nil || !strings.Contains(err.Error(), "SMTP_ALLOW_INSECURE") {
t.Errorf("expected an error naming SMTP_ALLOW_INSECURE, got %v", err)
}
})
}