feat: harden community production foundation through story 1.5
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crank_config::{ConfigSource, ProcessKind, parse_process};
|
||||
|
||||
fn config(secret: &str) -> crank_config::EffectiveConfig {
|
||||
let vars = [
|
||||
("CRANK_MASTER_KEY", secret),
|
||||
("CRANK_SESSION_SECRET", secret),
|
||||
("CRANK_PASSWORD_PEPPER", secret),
|
||||
("CRANK_BOOTSTRAP_ADMIN_EMAIL", "owner@example.test"),
|
||||
("CRANK_BOOTSTRAP_ADMIN_PASSWORD", secret),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|(key, value)| (key.to_owned(), value.to_owned()))
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
parse_process(ProcessKind::AdminApi, ConfigSource::from_utf8(vars)).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secrets_are_absent_from_debug_display_and_fingerprint() {
|
||||
let first = config("CANARY_ONE");
|
||||
let second = config("CANARY_TWO");
|
||||
let rendered = format!("{first:?}");
|
||||
assert!(!rendered.contains("CANARY_ONE"));
|
||||
assert_eq!(first.fingerprint(), second.fingerprint());
|
||||
assert_eq!(first.fingerprint().len(), 64);
|
||||
assert!(
|
||||
first
|
||||
.fingerprint()
|
||||
.bytes()
|
||||
.all(|byte| byte.is_ascii_hexdigit() && !byte.is_ascii_uppercase())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn effective_semantics_not_input_spelling_drive_fingerprint() {
|
||||
let mut canonical = [
|
||||
("CRANK_MASTER_KEY", "master"),
|
||||
("CRANK_SESSION_SECRET", "session"),
|
||||
("CRANK_PASSWORD_PEPPER", "pepper"),
|
||||
("CRANK_BOOTSTRAP_ADMIN_EMAIL", "owner@example.test"),
|
||||
("CRANK_BOOTSTRAP_ADMIN_PASSWORD", "password"),
|
||||
("CRANK_TRUST_FORWARDED_HEADERS", "true"),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|(key, value)| (key.to_owned(), value.to_owned()))
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
let mut compatibility = canonical.clone();
|
||||
compatibility.insert("CRANK_TRUST_FORWARDED_HEADERS".into(), "yes".into());
|
||||
|
||||
let canonical_config = parse_process(
|
||||
ProcessKind::AdminApi,
|
||||
ConfigSource::from_utf8(canonical.clone()),
|
||||
)
|
||||
.unwrap();
|
||||
let compatibility_config = parse_process(
|
||||
ProcessKind::AdminApi,
|
||||
ConfigSource::from_utf8(compatibility),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
canonical_config.fingerprint(),
|
||||
compatibility_config.fingerprint()
|
||||
);
|
||||
assert_eq!(compatibility_config.deprecations().len(), 1);
|
||||
|
||||
canonical.insert("CRANK_SESSION_TTL_HOURS".into(), "48".into());
|
||||
let changed = parse_process(ProcessKind::AdminApi, ConfigSource::from_utf8(canonical)).unwrap();
|
||||
assert_ne!(changed.fingerprint(), compatibility_config.fingerprint());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diagnostics_are_bounded_json_and_never_echo_secret_canaries() {
|
||||
let canary = "CANARY_SECRET_VALUE";
|
||||
let vars = [
|
||||
("CRANK_MASTER_KEY", canary),
|
||||
("CRANK_SESSION_SECRET", canary),
|
||||
("CRANK_PASSWORD_PEPPER", canary),
|
||||
("CRANK_BOOTSTRAP_ADMIN_EMAIL", "owner@example.test"),
|
||||
("CRANK_BOOTSTRAP_ADMIN_PASSWORD", canary),
|
||||
(
|
||||
"CRANK_DATABASE_URL",
|
||||
"postgres://owner:CANARY_SECRET_VALUE@db/crank",
|
||||
),
|
||||
("POSTGRES_PASSWORD", canary),
|
||||
("CRANK_CACHE_BACKEND", "memory"),
|
||||
("CRANK_CACHE_URL", "redis://:CANARY_SECRET_VALUE@cache:6379"),
|
||||
(
|
||||
"CRANK_SENTRY_DSN",
|
||||
"https://CANARY_SECRET_VALUE@sentry.test/1",
|
||||
),
|
||||
("CRANK_METRICS_BEARER_TOKEN", canary),
|
||||
(
|
||||
"OTEL_EXPORTER_OTLP_HEADERS",
|
||||
"authorization=CANARY_SECRET_VALUE",
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|(key, value)| (key.to_owned(), value.to_owned()))
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
let error = parse_process(ProcessKind::AdminApi, ConfigSource::from_utf8(vars)).unwrap_err();
|
||||
let display = error.to_string();
|
||||
let json = error.to_json();
|
||||
assert!(json.len() <= 65_536);
|
||||
assert!(serde_json::from_str::<serde_json::Value>(&json).is_ok());
|
||||
assert!(!display.contains(canary));
|
||||
assert!(!json.contains(canary));
|
||||
assert!(error.diagnostics().len() <= 100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_projection_debug_omits_urls_hosts_paths_and_identity_values() {
|
||||
let mut vars = [
|
||||
("CRANK_MASTER_KEY", "master"),
|
||||
("CRANK_SESSION_SECRET", "session"),
|
||||
("CRANK_PASSWORD_PEPPER", "pepper"),
|
||||
("CRANK_BOOTSTRAP_ADMIN_EMAIL", "owner@CANARY.test"),
|
||||
("CRANK_BOOTSTRAP_ADMIN_PASSWORD", "password"),
|
||||
("CRANK_STORAGE_ROOT", "/CANARY/private/storage"),
|
||||
("POSTGRES_HOST", "CANARY-db.internal"),
|
||||
("CRANK_OUTBOUND_ALLOWED_HOSTS", "CANARY-api.internal"),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k.to_owned(), v.to_owned()))
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
vars.insert(
|
||||
"CRANK_BASE_URL".into(),
|
||||
"https://CANARY.example.test".into(),
|
||||
);
|
||||
let config = parse_process(ProcessKind::AdminApi, ConfigSource::from_utf8(vars)).unwrap();
|
||||
let rendered = format!("{:?}", config.admin().unwrap());
|
||||
assert!(!rendered.contains("CANARY"), "{rendered}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalized_database_and_admin_default_urls_drive_fingerprint() {
|
||||
let base = [
|
||||
("CRANK_MASTER_KEY", "master"),
|
||||
("CRANK_SESSION_SECRET", "session"),
|
||||
("CRANK_PASSWORD_PEPPER", "pepper"),
|
||||
("CRANK_BOOTSTRAP_ADMIN_EMAIL", "owner@example.test"),
|
||||
("CRANK_BOOTSTRAP_ADMIN_PASSWORD", "password"),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k.to_owned(), v.to_owned()))
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
let implicit =
|
||||
parse_process(ProcessKind::AdminApi, ConfigSource::from_utf8(base.clone())).unwrap();
|
||||
let mut explicit = base.clone();
|
||||
explicit.insert("CRANK_BASE_URL".into(), "http://localhost:3000".into());
|
||||
let explicit = parse_process(ProcessKind::AdminApi, ConfigSource::from_utf8(explicit)).unwrap();
|
||||
assert_eq!(implicit.fingerprint(), explicit.fingerprint());
|
||||
|
||||
let mut url = base;
|
||||
url.insert(
|
||||
"CRANK_DATABASE_URL".into(),
|
||||
"postgres://crank:rotated@postgres/crank".into(),
|
||||
);
|
||||
let url = parse_process(ProcessKind::AdminApi, ConfigSource::from_utf8(url)).unwrap();
|
||||
assert_eq!(implicit.fingerprint(), url.fingerprint());
|
||||
|
||||
let tls = [
|
||||
("CRANK_MASTER_KEY", "master"),
|
||||
("CRANK_SESSION_SECRET", "session"),
|
||||
("CRANK_PASSWORD_PEPPER", "pepper"),
|
||||
("CRANK_BOOTSTRAP_ADMIN_EMAIL", "owner@example.test"),
|
||||
("CRANK_BOOTSTRAP_ADMIN_PASSWORD", "password"),
|
||||
(
|
||||
"CRANK_DATABASE_URL",
|
||||
"postgres://crank:rotated@postgres/crank?sslmode=require",
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k.to_owned(), v.to_owned()))
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
let tls = parse_process(ProcessKind::AdminApi, ConfigSource::from_utf8(tls)).unwrap();
|
||||
assert_ne!(implicit.fingerprint(), tls.fingerprint());
|
||||
}
|
||||
Reference in New Issue
Block a user