sec(database): redact plaintext credentials from logs + tighten env-var handling (closes #440, #441, #444, #446)#520
Conversation
…ar handling - #440: replace fmt.Printf with log.Printf in migration runner so admin password activity goes to stderr (log sink) rather than stdout, which CloudWatch ingests into persistent log groups; also remove the phrase "with password" from the created/activated confirmation line to reduce correlation with Secrets Manager access logs - #441: warn to stderr when both DB_PASSWORD and DB_PASSWORD_SECRET are set; plaintext DB_PASSWORD is visible via lambda:GetFunction without requiring secretsmanager:GetSecretValue; local-dev workflows still work - #444: pass a redacted DSN ("REDACTED" placeholder) to pgxpool.ParseConfig so that parse errors never echo the real password in the error chain; inject the real password directly into ConnConfig.Password after successful parsing - #446: suppress the pgx "args" key (SQL bound parameters) at debug level in stdLogger.Log; operators who need parameter tracing must opt in via DB_LOG_BIND_PARAMETERS=true Regression tests added for all four issues. Closes #440, #441, #444, #446
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThis PR hardens database credential handling by preventing plaintext passwords from leaking into error messages, logs, and stdout. Changes include configuration validation warnings, DSN redaction during parsing, centralized log sanitization for pgx traces, and migration output redirection from stdout to stderr. ChangesCredential and Logging Security
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@internal/database/postgres/migrations/migrate_security_test.go`:
- Around line 18-78: The test
TestEnsureAdminUserWithPassword_LogsToStderr_NotStdout currently simulates
log.Printf instead of invoking ensureAdminUserWithPassword/ensureAdminUser, so
fix the test by actually calling ensureAdminUserWithPassword (or
ensureAdminUser) inside the test and arrange inputs/mocks so execution reaches
the logging and bcrypt path but stops before any real DB access (e.g., inject a
mock DB/pool that returns an error or a nil-safe fake, or pass a configuration
that triggers validation failure before DB calls). Ensure the test still
captures stdout/stderr as before and asserts stdout is empty and the log buffer
contains the email but not the plaintext password; update the parallel test at
lines 82-114 the same way.
- Around line 119-151: The tests are no-ops because they never call the
functions under test; update TestBuildMigrateDSN_PasswordNotInLogs to actually
invoke buildMigrateDSN with a pgx-like config/DSN containing the sentinel
password (or construct the same inputs the function expects), capture log output
via log.SetOutput(&logBuf) as already done, assert the returned DSN contains the
sentinelPassword, and assert logBuf.String() does NOT contain the
sentinelPassword; likewise update TestMaybeForceVersion_NonNumericError to call
maybeForceMigrationVersion with a config containing a non-numeric version (the
same inputs the real function expects), capture log output, assert the function
returns the expected error/behavior for non-numeric input and that no sensitive
details are logged; reference buildMigrateDSN and maybeForceMigrationVersion to
locate the implementation and the test functions to change.
In `@internal/database/security_test.go`:
- Around line 149-155: Make the test force a parse error so the redaction check
actually runs: change the input passed to the parser (the call that currently
produces err) to an intentionally invalid DSN/string so parsing will fail, and
add an explicit assertion that err is non-nil (use require.Error or assert.Error
on err) before the assert.NotContains check that verifies realPassword is not
present; keep the existing NotContains assertion and allow "REDACTED" as
acceptable.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 52db4e78-a27d-43c5-a04b-ef0386567525
📒 Files selected for processing (5)
internal/database/config.gointernal/database/connection.gointernal/database/postgres/migrations/migrate.gointernal/database/postgres/migrations/migrate_security_test.gointernal/database/security_test.go
CR major findings: the four tests were calling log.Printf directly or reading env vars instead of invoking the actual target functions, so they could not catch a regression back to the old behavior. - migrate_security_test.go: refactor to call ensureAdminUserWithPassword, ensureAdminUser, buildMigrateDSN, and maybeForceMigrationVersion directly. Extract helpers (newUnreachablePool, captureLogOutput, captureStdout) to eliminate duplication. Use a pgxpool pointed at 127.0.0.1:1 (lazy, never dials) so functions log before Exec fails with connection refused. Pass nil migrate.Migrate for the non-numeric path (strconv.Atoi errors before m.Force is touched). buildMigrateDSN now receives a real *pgxpool.Config built from pgxpool.ParseConfig so the return value is asserted to contain the sentinel. - security_test.go: add require.Error before the NotContains assertion in TestBuildPoolConfig_ParseError_NoPasswordLeak so the redaction check is non-vacuous (invalid sslmode guarantees a parse error from pgx).
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
Summary
fmt.Printfwithlog.Printfin the migration runner so admin password-processing activity goes to stderr (the log sink) rather than stdout, which CloudWatch ingests into persistent log groups. Also remove the phrase "with password" from the created/activated confirmation line to reduce correlation with Secrets Manager access logs.DB_PASSWORDandDB_PASSWORD_SECRETare set. PlaintextDB_PASSWORDis readable vialambda:GetFunctionwithout requiringsecretsmanager:GetSecretValue. Local-dev workflows still work (not a hard error).password=REDACTED) topgxpool.ParseConfigso that any parse error never echoes the real credential in the error chain. The real password is injected directly intoConnConfig.Passwordafter successful parsing."args"key (SQL bound parameters) at debug level instdLogger.Log. Parameters can carry session tokens, bcrypt hashes, or approval tokens. Operators who need parameter tracing must opt in viaDB_LOG_BIND_PARAMETERS=true.The
stdLogger.Logfunction was refactored into two pure helpers (isSensitiveKey,sanitizeLogData) to keep cyclomatic complexity below the project's gocyclo limit of 10.Test plan
go test ./internal/database/... ./internal/server/...passes (172 + 325 tests green)TestValidateRequiredFields_BothPasswordAndSecret_WarnOnStderr- regression for sec: DB_PASSWORD plaintext env var accepted without guard #441: warning on stderr, no error returnedTestBuildPoolConfig_ParseConfigDoesNotExposePassword- regression for sec: pgxpool.ParseConfig error may include plaintext password in error chain #444: real password inConnConfig.Password, parse succeedsTestBuildPoolConfig_ParseError_NoPasswordLeak- regression for sec: pgxpool.ParseConfig error may include plaintext password in error chain #444: parse errors do not echo the real passwordTestSanitizeLogData_ArgsKeyStrippedAtDebugByDefault- regression for sec: DB_LOG_LEVEL=debug enables pgx query tracing that may log SQL bound parameters #446:argsstripped at debug levelTestSanitizeLogData_ArgsKeyKeptWhenOptedIn- regression for sec: DB_LOG_LEVEL=debug enables pgx query tracing that may log SQL bound parameters #446:argspreserved withDB_LOG_BIND_PARAMETERS=trueTestEnsureAdminUserWithPassword_LogsToStderr_NotStdout- regression for sec: plaintext admin password echo'd to stdout during DB migration startup #440: log.Printf does not write to stdoutSummary by CodeRabbit
Bug Fixes
New Features
Tests