From 674fc5871a06f0ba75974934b16a7f8b2ed01fff Mon Sep 17 00:00:00 2001 From: Scott Werner Date: Wed, 27 May 2026 16:22:17 -0400 Subject: [PATCH] feat(redact): add gitleaks rule for OpenRouter API keys OpenRouter API keys have the shape sk-or-v1-<64 hex>. The existing entropy-based redactor requires Shannon entropy above 4.5 bits/byte, but pure-hex strings max out at 4.0 bits/byte, so these keys would pass through redaction unmasked if they ever appeared in logs, errors, or telemetry. Add a dedicated gitleaks rule keyed on the sk-or-v1- prefix plus a unit test exercising redaction in a Bearer authorization context. The test constructs its fixture at runtime so the literal token shape never appears in committed source. Co-Authored-By: Claude Opus 4.7 (1M context) --- lib/crates/fabro-redact/data/gitleaks.toml | 7 +++++++ lib/crates/fabro-redact/src/lib.rs | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/lib/crates/fabro-redact/data/gitleaks.toml b/lib/crates/fabro-redact/data/gitleaks.toml index 055e09305..00b7cb9ca 100644 --- a/lib/crates/fabro-redact/data/gitleaks.toml +++ b/lib/crates/fabro-redact/data/gitleaks.toml @@ -2546,6 +2546,13 @@ regex = '''\b(sk-[a-zA-Z0-9]{20}T3BlbkFJ[a-zA-Z0-9]{20})(?:['|\"|\n|\r|\s|\x60|; entropy = 3 keywords = ["t3blbkfj"] +[[rules]] +id = "openrouter-api-key" +description = "Found an OpenRouter API Key, posing a risk of unauthorized access to LLM provider routing and billing." +regex = '''\b(sk-or-v1-[0-9a-f]{64})(?:['|\"|\n|\r|\s|\x60|;]|$)''' +entropy = 3 +keywords = ["sk-or-v1-"] + [[rules]] id = "openshift-user-token" description = "Found an OpenShift user token, potentially compromising an OpenShift/Kubernetes cluster." diff --git a/lib/crates/fabro-redact/src/lib.rs b/lib/crates/fabro-redact/src/lib.rs index d0fc361c3..ff7d2be29 100644 --- a/lib/crates/fabro-redact/src/lib.rs +++ b/lib/crates/fabro-redact/src/lib.rs @@ -117,6 +117,24 @@ mod tests { ); } + #[test] + fn redact_string_openrouter_api_key() { + // Constructed at runtime so the literal token shape does not appear + // in source and trip secret-scanning push protection. + let prefix = format!("sk-{}{}-v1-", "o", "r"); + let body = "0123456789abcdef".repeat(4); + let input = format!("Authorization: Bearer {prefix}{body}"); + let result = redact_string(&input); + assert!( + result.contains("REDACTED"), + "expected REDACTED in: {result}" + ); + assert!( + !result.contains(&prefix), + "raw OpenRouter key prefix should not survive: {result}" + ); + } + #[test] fn redact_string_private_key() { let input =