Cover a secret the rules miss¶
rtb-redact recognises shapes it has been taught. A bespoke token, a short
secret, or a credential inside a JSON payload can pass straight through. This is
how to find out, and what to do about it.
Confirm whether your secret is actually caught¶
Do not guess from the rule list — run a realistic line through string and look
at the output. A one-off test is the quickest way:
#[test]
fn our_session_token_is_redacted() {
let line = format!("session refresh failed for {}", sample_token());
let out = rtb_redact::string(&line);
assert!(!out.contains(&sample_token()), "token survived redaction: {out}");
}
Two traps make this test worth writing rather than reasoning about:
- Punctuation changes the answer. The opaque-run rule needs whitespace or a
string end on both sides, so the same token is redacted bare and survives in
quotes or before a
;. - Neighbouring characters change the answer. A string containing none of
@ = ? - _ ., nobearer/basic/tokenand no 40-character run is returned untouched without any rule running at all. Test the line you will actually emit, not the token on its own.
Redact it at the source instead¶
The reliable fix is not to let the secret into the string. In order of preference:
- Hold it in a type that will not format.
secrecy::SecretString, which is whatrtb-credentialsuses, makes the accidental{}impossible. - Substitute before you build the line. Replace the credential with a placeholder where you assemble the message, so the redactor never has to recognise it.
- Log an identifier, not the value. A key id, a fingerprint or a last-four is usually what the log line actually needed.
Then keep string at the boundary anyway, for the strings you did not assemble.
Make the line anchorable if you cannot do either¶
If a token has to appear and you cannot mask it upstream, you can at least make it reachable by the rules. Both of these hold today:
- Put it in a recognised assignment —
api_key=<value>ortoken=<value>— and the query-parameter rule masks the value whatever shape it is. - Prefix it with
Bearer,BasicorTokenand the scheme rule masks whatever follows, up to the next whitespace.
This is a workaround, not a design. It depends on the exact rule set, so pin it with a test.
Get the format added to the crate¶
A credential format that is widely used — a provider prefix other services will hit too — belongs in the catalogue rather than in your workaround. Raise it on the project on GitLab with a shape-matching sample that is not a real credential, and say roughly how long the token is; the prefix rules only fire at 20 characters or more.
There is no way to register a pattern at runtime, and that is deliberate — see Why there is nothing to configure.
Keep the fixture out of a secret scanner's way¶
Test fixtures for this kind of work have to look like real credentials, which is exactly what push-protection scanners block. The crate's own tests build them by concatenation so no whole literal appears in the committed source:
Do the same in yours.
Related¶
- Redaction rules — what is and is not matched
- What rtb-redact does not do
- Redact before an external surface