Your .env file hides its secrets from most redaction tools

When a deploy fails, the thing you paste into an AI chat is usually a stack trace and the config around it. That config is where the secrets live, and it almost never spells them the way a redaction rule expects.

We know because our own tool got this wrong, and we only noticed while measuring something else.

The miss

We were running a realistic paste through the rule set to produce numbers for another article. Fifteen lines of deploy chatter, deliberately seeded with an email address, a phone number, two internal hostnames, a Stripe key, a GitHub token, a JWT, and a database password. The tool found seven of the eight.

The one it walked straight past:

DB_PASSWORD="Tr0ub4dor&3"

The first guess was the ampersand, or the quotes. Both were wrong. This line matched perfectly:

password = "Tr0ub4dor&3"

Same value, same quotes, same ampersand. The only difference was the three characters DB_ in front.

One character of regular expression

The rule started with a word boundary:

/\b(?:pass(?:word|wd)?|pwd|secret|token|...)\s*[:=]/

\b matches where a word character sits next to a non-word character. In DB_PASSWORD, the character before PASSWORD is an underscore, and underscores are word characters in regular expressions. So is every letter and digit. There is no boundary between DB_ and PASSWORD, which means \bpassword can never match inside it.

The rule was not looking for the word "password". It was looking for the word "password" at the start of a word, and in configuration files it almost never is:

Every one of those slipped through. The bare form password=, which the rule did catch, is the form you find in a tutorial. The prefixed form is the one in your actual repository.

The fix

The left edge had to stop being a word boundary and start being "the beginning of the line, or a character that cannot be part of a key name", with an optional prefix segment allowed after it:

(?:^|[^A-Za-z0-9])(?:[A-Za-z0-9]*[_-])?(?:pass(?:word|wd)?|pwd|secret|token|...)

The risk with loosening a rule is that it starts matching things it should not. Two cases we hold the line on:

Both are now assertions in the test suite, alongside the three prefixed forms, so this cannot quietly come back.

The same paste, after the fix

The output the tool now produces for that deploy message:

Reach me at [EMAIL_1] or [PHONE_1] if it is urgent.

  DB_HOST=[HOST_1]
  DB_PASSWORD="[SECRET_1]"
  STRIPE_KEY=[API_KEY_1]
  export GITHUB_TOKEN=[API_KEY_2]

curl -H "Authorization: Bearer [API_KEY_3]" \
  https://[HOST_2].acme-corp.com/v2/deploy

Order #113-7782991-4406620 is blocked by this.

Eight hits instead of seven. Note what is preserved: the key names stay, only the values are replaced. An AI reading this can still tell you your DB_HOST is wrong without ever seeing what it was. The order number, which is not sensitive, is left alone.

Why we are telling you about our own bug

Because the lesson generalises past this one tool. Any detector built on patterns has edges like this, and the edges are invisible until someone tests the format you actually use. A clean result means "none of the configured patterns matched", never "there is nothing sensitive here".

If you paste config files into AI chats, this is the class of thing worth checking before you trust any tool, ours included: take a line from your own repository, run it through, and see whether it comes back redacted. That takes ten seconds and tells you more than any feature list.