From 11575a6b7a9ea9d06abddad1eac1a8b3ccaa4f97 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 19 Mar 2026 09:27:46 -0500 Subject: [PATCH] Move placeholder secret validation to compile-time for release builds Shift the reject_placeholder_secrets() check from an unconditional runtime gate to a compile-time gate in build.rs for release builds, while keeping a cfg-guarded runtime safety net. Debug/test builds now pass through with the default trusted-server.toml placeholders, so developers no longer need env var overrides for local iteration. --- crates/common/build.rs | 21 ++++++++++++++++----- crates/common/src/settings.rs | 5 +++-- crates/common/src/settings_data.rs | 24 +++++++++++------------- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/crates/common/build.rs b/crates/common/build.rs index b2803135..4566d07b 100644 --- a/crates/common/build.rs +++ b/crates/common/build.rs @@ -38,14 +38,25 @@ fn main() { // Merge base TOML with environment variable overrides and write output. // Panics if admin endpoints are not covered by a handler. - // Note: placeholder secret rejection is intentionally NOT done here. - // The base trusted-server.toml ships with placeholder secrets that - // production deployments override via TRUSTED_SERVER__* env vars at - // build time. Runtime startup (get_settings) rejects any remaining - // placeholders so a misconfigured deployment fails fast. let settings = settings::Settings::from_toml_and_env(&toml_content) .expect("Failed to parse settings at build time"); + // Reject placeholder secrets in release builds so a misconfigured + // production deployment fails at compile time rather than at runtime. + // Debug builds (cargo test, cargo build) allow placeholders so that + // the base trusted-server.toml works without env var overrides. + // + // The runtime code uses `cfg(not(debug_assertions))` for the same + // purpose; both align under standard debug/release profiles. + let profile = std::env::var("PROFILE").expect("should have PROFILE set by Cargo"); + if profile == "release" { + settings.reject_placeholder_secrets().expect( + "Release build must not contain placeholder secrets. \ + Override them with TRUSTED_SERVER__PUBLISHER__PROXY_SECRET \ + and TRUSTED_SERVER__SYNTHETIC__SECRET_KEY environment variables.", + ); + } + let merged_toml = toml::to_string_pretty(&settings).expect("Failed to serialize settings to TOML"); diff --git a/crates/common/src/settings.rs b/crates/common/src/settings.rs index 74fd8696..1eb06be7 100644 --- a/crates/common/src/settings.rs +++ b/crates/common/src/settings.rs @@ -219,8 +219,9 @@ impl Synthetic { /// /// Placeholder detection is intentionally **not** performed here because /// this validator runs at build time (via `from_toml_and_env`) when the - /// config legitimately contains placeholder values. Placeholder rejection - /// happens at runtime via [`Settings::reject_placeholder_secrets`]. + /// config may legitimately contain placeholder values (e.g. debug builds). + /// Placeholder rejection happens in release builds at both compile time + /// (`build.rs`) and runtime ([`Settings::reject_placeholder_secrets`]). /// /// # Errors /// diff --git a/crates/common/src/settings_data.rs b/crates/common/src/settings_data.rs index d764f0bd..fa1a6771 100644 --- a/crates/common/src/settings_data.rs +++ b/crates/common/src/settings_data.rs @@ -34,7 +34,11 @@ pub fn get_settings() -> Result> { message: "Failed to validate configuration".to_string(), })?; - // Reject known placeholder values for secrets that feed into cryptographic operations. + // Reject known placeholder values for secrets that feed into cryptographic + // operations. Release builds also enforce this at compile time (build.rs), + // so this is a runtime safety net. Debug builds skip the check so the + // default trusted-server.toml works without env var overrides. + #[cfg(not(debug_assertions))] settings.reject_placeholder_secrets()?; if !settings.proxy.certificate_check { @@ -140,18 +144,12 @@ mod tests { } /// Smoke-test the full `get_settings()` pipeline (embedded bytes → UTF-8 → - /// parse → validate → placeholder check). The build-time TOML ships with - /// placeholder secrets, so the expected outcome is an [`InsecureDefault`] - /// error — but reaching that error proves every earlier stage succeeded. + /// parse → validate). Debug builds skip the placeholder-secret check so + /// the default `trusted-server.toml` works without env var overrides. + /// Release builds enforce placeholders at both compile time (`build.rs`) + /// and runtime, but tests run in debug mode. #[test] - fn get_settings_rejects_embedded_placeholder_secrets() { - let err = super::get_settings().expect_err("should reject embedded placeholder secrets"); - assert!( - matches!( - err.current_context(), - TrustedServerError::InsecureDefault { .. } - ), - "should fail with InsecureDefault, got: {err}" - ); + fn get_settings_allows_placeholders_in_debug_builds() { + super::get_settings().expect("debug builds should allow placeholder secrets"); } }