Everything you ever committed is the product
I audited three of my repos before making things public, and the audit ran five rounds because every round found something new. Not because the searching was sloppy. Because each round searched only where it happened to look. Round one read the tracked files, so round two found git history. Round two read history, so round three found the published artifacts. The leaks mapped onto blind spots, not onto effort, and that observation ended up shaping the whole system.
The worst find was not in git at all. A cache file, properly gitignored, carrying an absolute path from my machine, shipped inside three published releases of an extension, because the packager does not read gitignore. Every git-based check passed, forever, while the file went out to two marketplaces. There is no repo-scoped check that can catch that. Scope, not thoroughness, is the thing to get right.
So the setup I run now has four doors, one per exit. Staged content is screened at commit. The commit message is screened separately, messages are published history too and the staged check structurally cannot see them. The full tracked tree is screened at push, which catches whatever a rebase or a no-verify commit smuggled in. And the built artifact is screened at publish, the only door that would have caught the marketplace leak, wired to the irreversible moment on purpose. A commit can be amended. A published package cannot be recalled from the machines that already fetched it.
Two rules keep the doors honest. Every new class of finding becomes a rule, a pattern if it greps, a checklist line if it is a place nobody had inspected. The audit found something new five rounds in a row precisely because each round’s lesson used to die with its fix. And when there is a choice, remove the surface instead of adding a rule. The single biggest win of the whole audit was an ignore file that cut a published package from thirteen files to five. Content that never ships cannot leak, and no pattern list will ever be as reliable as absence.
One more, because it stings. Patterns only catch what you already thought of. A sentence can disclose a fact about your employer while containing no banned string at all. For that class there is no grep, only a periodic fresh read of everything, asking one question, what does this repo reveal about its author. I treat that read as part of releasing, the same as the version bump.
If you take one thing, take the framing. Publishing is not showing your current code. It is showing every decision, path, and careless message you ever committed, to everyone, forever. Build the doors before the first push, it is genuinely miserable to retrofit.