fix(redact): stop reporting .env.local as an internal hostname

`internal.hostname` ends in `.local|.prod|.staging|…`, so `.env.local`
matches on `env.local` and a dotenv FILENAME is reported as a leaked
internal host.

The collision is not exotic. It fires on `--env-file=.env.local` in an npm
script, `.env.staging` in a README, `.env.prod` in a .gitignore — ordinary
lines on branches that leak nothing. Measured on one private repo, three of
four MEDIUM findings in a routine push were this, and the fourth was a
deleted localhost URL. That ratio is the real cost: a scanner that reports
package.json is one people learn to skim, and skimming is how the HIGH
finding it exists for gets missed.

The guard follows the `insideUuid` precedent and stays deliberately narrow —
it exempts only a span beginning `env.` immediately preceded by a dot, i.e.
the literal `.env.<suffix>` form. `api.corp.local`, `build-7.internal` and
`myenv.local` all still report.

The test pins both directions, and the negative controls are the point: an
exemption written as "any span ending .local" would pass the dotenv half
while quietly gutting the pattern for every real host. Verified red/green —
with the validate hook removed, exactly the 6 dotenv cases fail and all 9
real-host controls still pass.
This commit is contained in:
David Park
2026-08-31 20:52:28 +00:00
committed by Garry Tan
parent b77c1923c8
commit cc94bc34ba
2 changed files with 106 additions and 0 deletions
@@ -0,0 +1,77 @@
/**
* internal.hostname vs dotenv FILENAMES.
*
* `.env.local` ends in `.local`, so the internal-hostname pattern matches on
* `env.local` and reports a filename as a leaked internal host. This is not an
* exotic collision: `--env-file=.env.local` in an npm script, `.env.staging` in
* a README, `.env.prod` in a .gitignore. It fires on branches that leak
* nothing, and a scanner that cries wolf on package.json is a scanner people
* learn to skim past — which costs far more than the finding was ever worth.
*
* The guard has to stay narrow, so this file pins BOTH directions. The
* negative controls are the point: an exemption written as "any span ending
* .local" would pass the dotenv half while quietly gutting the pattern for
* every real host.
*/
import { describe, test, expect } from "bun:test";
import { scan } from "../lib/redact-engine";
import { isDotenvFilename } from "../lib/redact-patterns";
const flagsHost = (s: string): boolean =>
scan(s, { repoVisibility: "private" }).findings.some((f) => f.id === "internal.hostname");
describe("internal.hostname — real internal hosts stay flagged", () => {
const REAL_HOSTS: [string, string][] = [
[".internal", "curl http://build-7.internal/health"],
[".corp", "ssh jump.corp"],
[".local", "ping printer.local"],
[".lan", "nas.lan is down"],
[".prod", "deploy to shipping.prod now"],
[".staging", "hit api.staging first"],
["multi-label, dotted prefix", "host: api.corp.local"],
["not a dotenv file", "myenv.local resolves"],
["env as a real subdomain", "https://env.prod//status"],
];
for (const [label, input] of REAL_HOSTS) {
test(label, () => {
expect(flagsHost(input)).toBe(true);
});
}
});
describe("internal.hostname — dotenv filenames are not hosts", () => {
const DOTENV: [string, string][] = [
["npm script", '"dev": "tsx --env-file=.env.local scripts/x.ts"'],
["bare filename", "copy .env.example to .env.local"],
["staging", "secrets live in .env.staging"],
["prod", "never commit .env.prod"],
["gitignore line", ".env.local"],
["path prefix", "apps/web/.env.local"],
];
for (const [label, input] of DOTENV) {
test(label, () => {
expect(flagsHost(input)).toBe(false);
});
}
});
describe("isDotenvFilename — unit", () => {
const matchFor = (input: string): RegExpExecArray => {
const re = /\b([a-z0-9][a-z0-9\-]*\.(?:internal|corp|local|lan|prod|staging))\b/i;
const m = re.exec(input);
if (!m) throw new Error(`pattern did not match: ${input}`);
return m;
};
test("exempts a dot-prefixed env filename", () => {
expect(isDotenvFilename(matchFor("--env-file=.env.local"))).toBe(true);
});
test("does not exempt env.local without the leading dot", () => {
expect(isDotenvFilename(matchFor("host env.local here"))).toBe(false);
});
test("does not exempt a dot-prefixed host that is not env", () => {
expect(isDotenvFilename(matchFor("api.corp.local"))).toBe(false);
});
});