Fix weak and inconsistent secret default validation#467
Fix weak and inconsistent secret default validation#467ChristianPavilonis wants to merge 7 commits intomainfrom
Conversation
Reject all known placeholder values for synthetic.secret_key and publisher.proxy_secret at runtime startup so deployments using default secrets fail fast instead of running with predictable cryptographic keys.
aram356
left a comment
There was a problem hiding this comment.
Review Summary
| # | Severity | File | Finding |
|---|---|---|---|
| 1 | HIGH | settings.rs | proxy_secret has no validation — empty string passes and produces a deterministic crypto key |
| 2 | MEDIUM | settings_data.rs | Test is environment-dependent and can flap when CI overrides secrets |
| 3 | MEDIUM | settings.rs | Placeholder policy is scattered across two types and will drift |
| 4 | LOW | settings_data.rs | Only the first insecure field is reported; operator must fix-and-redeploy to find the second |
| 5 | NIT | settings.rs | Spurious #[allow(dead_code)] on public items that are actually used |
…r checks - Add #[validate(length(min = 1))] to proxy_secret to prevent empty strings from producing a deterministic crypto key - Centralize placeholder validation into Settings::reject_placeholder_secrets() which collects all violations into a single error - Rewrite tests to use explicit TOML input instead of build-time embedded config, making them deterministic across CI environments - Remove spurious #[allow(dead_code)] from public items that are used
Review Feedback AddressedAll 5 items from @aram356's review have been addressed in cbef697:
All 480 tests pass, |
aram356
left a comment
There was a problem hiding this comment.
Summary
Centralizes placeholder-secret rejection into Settings::reject_placeholder_secrets(), adds proxy_secret validation, and makes tests deterministic. Addresses all findings from the previous review round.
Blocking
🔧 wrench
- Removed
test_get_settingssmoke test not replaced: The originaltest_get_settingswas the only test exercising the fullget_settings()end-to-end path (embedded bytes → UTF-8 → parse → validate → placeholder check). The new tests only exerciseSettings::from_toml()+reject_placeholder_secrets()directly. A regression in the glue code would go undetected. (settings_data.rs)
❓ question
toml_with_secretsreplacement is fragile and can silently no-op:str::replace()returns the original string unchanged if the pattern doesn't match. Ifcrate_test_settings_str()changes quoting or default values, replacements silently do nothing. Theaccepts_non_placeholder_secretstest is especially vulnerable — both old and new values are non-placeholders, so it passes even if replacement fails. (settings_data.rs:56-66)
Non-blocking
🤔 thinking
- Case-sensitive placeholder matching:
"Secret-Key"or"SECRET-KEY"bypasses the check while still being predictable. Considereq_ignore_ascii_case. (settings.rs:205-207)
🌱 seedling
- Minimum length for crypto secrets:
min = 1prevents empty but"x"still passes validation then feeds intoSha256::digestfor XChaCha20-Poly1305 key derivation. Considermin = 16as a follow-up. (settings.rs:27, 194)
⛏ nitpick
- Double space in doc comment:
"...offending field. This centralises..."— rest of codebase uses single space. (settings.rs:388)
CI Status
- fmt: PASS
- clippy: PASS
- rust tests: PASS
- js tests: PASS
- CodeQL: FAIL (appears infrastructure-related, not caused by this PR)
prk-Jr
left a comment
There was a problem hiding this comment.
Summary
The runtime secret-validation change is directionally right, but I found one blocking regression: the new Settings::reject_placeholder_secrets() helper now makes the branch fail CodeQL with 6 new high-severity cleartext-logging alerts, and one of those sinks is a real full-settings debug log. I also have a couple of API/test-coverage concerns and one docs consistency issue.
Blocking
🔧 wrench
- Secret-taint regression now fails CodeQL: Adding secret-aware validation as a method on
Settingstaints the whole struct, and the branch now failsCodeQLwith 6 new high-severity cleartext-logging alerts. One concrete sink is the full settings debug log incrates/fastly/src/main.rs:42.
Non-blocking
📝 note
- Docs/examples still describe the old placeholder rules: the implementation now rejects
synthetic.secret_key = "trusted-server"andpublisher.proxy_secret = "change-me-proxy-secret", but the docs still describe only"secret_key"/"secret-key"as invalid and still show placeholder-style proxy-secret examples. Seedocs/guide/configuration.md:361,docs/guide/configuration.md:922,docs/guide/configuration.md:1033, anddocs/guide/error-reference.md:64.
CI Status
- cargo fmt: PASS
- cargo test: PASS
- vitest: PASS
- CodeQL: FAIL (6 new high-severity alerts)
…placeholder validation
Summary
synthetic.secret_key("secret-key","secret_key","trusted-server") andpublisher.proxy_secret("change-me-proxy-secret") at runtime startup so misconfigured deployments fail fast.is_placeholder_secret_key,is_placeholder_proxy_secret) with explicit placeholder lists, replacing the inconsistent checks that missed the actual TOML defaults.InsecureSecretKeytoInsecureDefault { field }so the error message identifies which secret triggered rejection.Changes
crates/common/src/error.rsInsecureSecretKey→InsecureDefault { field: String }with a descriptive display messagecrates/common/src/settings.rsPROXY_SECRET_PLACEHOLDERSconst +is_placeholder_proxy_secret()onPublisher; replacedvalidate_secret_key()withSECRET_KEY_PLACEHOLDERSconst +is_placeholder_secret_key()onSynthetic; added 4 unit testscrates/common/src/settings_data.rs== "secret-key"check with calls to both predicates; returns field-specificInsecureDefaulterrors; updated test to assert placeholder rejectioncrates/common/build.rsCloses
Closes #406
Test plan
cargo test --workspacecargo clippy --all-targets --all-features -- -D warningscargo fmt --all -- --checkChecklist
unwrap()in production code — useexpect("should ...")tracingmacros (notprintln!)