fix(security): fail-fast on missing JWT_SECRET, harden CSP, strip hardcoded passwords #3
Reference in New Issue
Block a user
Delete Branch "devin/1776538631-fix-jwt-and-csp-hardening"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
PR #3 of the 11-PR completion sequence. Addresses the two highest-severity findings in the repo review:
NewServersilently falling back to a per-process ephemeral JWT secret (or a predictable time-based one onrand.Readerror).Content-Security-Policythat shipped'unsafe-inline','unsafe-eval', and the private192.168.11.xRPC CIDRs to every visitor.It also removes hardcoded
L@kers2010/L@ker$2010defaults from 7 helper scripts + 2 top-levelEXECUTE_*.shrunners and theREADME.md/docs/DEPLOYMENT.mdoperator tables, and introducesdocs/SECURITY.mdwith the rotation checklist.JWT secret loading
<ref_snippet file="/home/ubuntu/repos/explorer-monorepo/backend/api/rest/server.go" lines="66-92" />
JWT_SECRETmust be ≥32 bytes; otherwiselog.Fatal.JWT_SECRETis required whenAPP_ENV=productionorGO_ENV=production.crypto/rand. Arand.Readfailure is now fatal — the previousephemeral-jwt-secret-<unix_nano>fallback is gone.CSP hardening
<ref_snippet file="/home/ubuntu/repos/explorer-monorepo/backend/api/rest/server.go" lines="36-49" />
defaultDevCSPdrops'unsafe-inline'and'unsafe-eval'.frame-ancestors 'none',base-uri 'self',form-action 'self'.CSP_HEADERis required in production (fatal on startup if unset).Hardcoded password removal
All 9 occurrences of
L@kers2010/L@ker$2010that live in tracked files other than the three docs being deleted by PR #2 (START_HERE.md,LETSENCRYPT_CONFIGURATION_GUIDE.md) are gone:scripts/analyze-besu-logs.sh,scripts/check-besu-config.sh,scripts/check-besu-logs-with-password.sh,scripts/check-failed-transaction-details.sh,scripts/enable-besu-debug-api.sh,scripts/set-vmid-password.sh,scripts/set-vmid-password-correct.sh—SSH_PASSWORD/NEW_PASSWORDnow required via env or argv; fail-fast withexit 2pointing atdocs/SECURITY.md.EXECUTE_DEPLOYMENT.sh,EXECUTE_NOW.sh—DB_PASSWORD(andRPC_URL) are now:?guarded.README.md— hardcodedDatabase Password: L@ker$2010replaced with an env-variable reference table.docs/DEPLOYMENT.md—PASSWORD: SSH password (default: L@kers2010)replaced with "required; no default".New
docs/SECURITY.mdJWT_SECRET, vendor API keys, gitleaks history audit).Tests
<ref_file file="/home/ubuntu/repos/explorer-monorepo/backend/api/rest/server_security_test.go" />
TestLoadJWTSecretAcceptsSufficientlyLongValueTestLoadJWTSecretStripsSurroundingWhitespaceTestLoadJWTSecretGeneratesEphemeralInDevelopment(asserts the new ephemeral secret does NOT start with the oldephemeral-jwt-secret-prefix)TestIsProductionEnv(7 cases)TestDefaultDevCSPHasNoUnsafeDirectivesOrPrivateCIDRsVerification
go build ./...— clean.go vet ./...— clean.go test ./api/rest/ -run 'LoadJWTSecret|IsProduction|DefaultDevCSP'— passes.L@kers\?2010\|L@ker\$2010across all*.sh,*.go,*.yml,*.yaml,*.md— only remaining hits are inSTART_HERE.mdandLETSENCRYPT_CONFIGURATION_GUIDE.md, which are deleted by PR #2.Completion criterion advanced
Still outstanding: rotation of the leaked credentials (operator-side, tracked in
docs/SECURITY.md) and gitleaks CI wiring (PR #5).backend/api/rest/server.go: - NewServer() now delegates to loadJWTSecret(), which: - Rejects JWT_SECRET < 32 bytes (log.Fatal). - Requires JWT_SECRET when APP_ENV=production or GO_ENV=production. - Generates a 32-byte crypto/rand ephemeral secret in dev only. - Treats rand.Read failure as fatal (removes the prior time-based fallback that was deterministic and forgeable). - Default Content-Security-Policy rewritten: - Drops 'unsafe-inline' and 'unsafe-eval'. - Drops private CIDRs (192.168.11.221:854[5|6]). - Adds frame-ancestors 'none', base-uri 'self', form-action 'self'. - CSP_HEADER is required in production; fatal if unset there. backend/api/rest/server_security_test.go (new): - Covers the three loadJWTSecret() paths (valid, whitespace-trimmed, ephemeral in dev). - Covers isProductionEnv() across APP_ENV / GO_ENV combinations. - Asserts defaultDevCSP contains no unsafe directives or private CIDRs and includes the frame-ancestors / base-uri / form-action directives. scripts/*.sh: - Removed 'L@kers2010' default value from SSH_PASSWORD / NEW_PASSWORD in 7 helper scripts. Each script now fails with exit 2 and points to docs/SECURITY.md if the password isn't supplied via env or argv. EXECUTE_DEPLOYMENT.sh, EXECUTE_NOW.sh: - Replaced hardcoded DB_PASSWORD='L@ker$2010' with a ':?' guard that aborts with a clear error if DB_PASSWORD (and, for EXECUTE_DEPLOYMENT, RPC_URL) is not exported. Other env vars keep sensible non-secret defaults via ${VAR:-default}. README.md: - Removed the hardcoded Database Password / RPC URL lines. Replaced with an env-variable reference table pointing at docs/SECURITY.md and docs/DATABASE_CONNECTION_GUIDE.md. docs/DEPLOYMENT.md: - Replaced 'PASSWORD: SSH password (default: L@kers2010)' with a required-no-default contract and a link to docs/SECURITY.md. docs/SECURITY.md (new): - Full secret inventory keyed to the env variable name and the file that consumes it. - Five-step rotation checklist covering the Postgres role, the Proxmox VM SSH password, JWT_SECRET, vendor API keys, and a gitleaks-based history audit. - Explicit note that merging secret-scrub PRs does NOT invalidate already-leaked credentials; rotation is the operator's responsibility. Verification: - go build ./... + go vet ./... pass clean. - Targeted tests (LoadJWTSecret*, IsProduction*, DefaultDevCSP*) pass. Advances completion criterion 2 (Secrets & config hardened). Residual leakage from START_HERE.md / LETSENCRYPT_CONFIGURATION_GUIDE.md is handled by PR #2 (doc consolidation), which deletes those files.