Goal
I kept nearly publishing things I didn't mean to. A contact email that only belonged in one place. A sample token that looked real enough to worry me. Digit runs in SVG icons that looked like phone numbers when they weren't.
I wanted a second look that runs on my machine only, before anything leaves for GitHub. No cloud upload of the repo. No matched text printed into the terminal for someone else to screenshot later.
What it is
PortfolioGuard is a small offline Python helper for a Git repo. Point it at the working tree, the staged files, a commit range or the reachable history. It prints the file, the line and the rule name. It never prints the matched text itself.
A clean exit means nothing outside the allowlist turned up in the paths it actually scanned. It is not a guarantee that the repo is free of sensitive material.
There is also a pre-push hook you can install, and a GitHub Action for the cases where a local hook never runs, for example a cloud agent with a custom hooks path.
How I use it
- Scan the working tree while I'm editing.
- Scan history when I'm about to open a repo up or change who can see it.
- Let the Action fail the job if a push or pull request still has findings.
- Keep any local allowlist out of a Pages served tree, so GitHub Pages never serves
/.portfolioguard-allowlist.
What got quieter in 0.3.1
Earlier scans were noisy in ways that made demos look worse than the risk.
- SVG coordinate data in animal icons was tripping the phone number rule. Ordinary phone detection stays on. The viewBox style digit runs do not.
- LinkedIn activity and share URNs in footers and blog links were doing the same. Those URN tails are ignored now.
- Intentional public contact addresses can sit in
allowed_email_valuesinportfolioguard.toml, matched exactly and without regard to case. Private addresses stay findings unless you list them on purpose.
That last point matters for a site that shows [email protected] on purpose. I want the scanner to respect a public contact without teaching it to ignore every email.
A small tip that shipped today
Self scanning the PortfolioGuard repo still hit email_address on the README example that uses that same public contact address. The fix was a narrow path and rule allowlist entry for the docs example, the same pattern already used for the toml sample.
It is a tiny change. It is also the kind of thing I want the tool to handle calmly, so a documented example does not look like a real leak.
Honest limits
A clean exit does not mean skipped folders, over size files or binaries are fine. By default it skips names like node_modules, dist, build and .venv, and it skips files over the size cap. Those paths can still get committed and published. If you are about to publish a static site from a skipped name, take it out of the skip list or raise the size cap first.
The Action and the pre-push hook still matter. A clean laptop scan does not help if something else pushes without the hook.
Findings never print the matched text. That is deliberate. Review the file yourself before you allowlist anything.
Weak spots
- It is a helper, not a full secret management platform. It will miss things that do not match a rule.
- Allowlists are powerful. A lazy allowlist can hide a real problem.
- Keeping personal projects that should stay separate is still a manual review for me, not a default rule in the tool.
What I'd change next
- Keep the false positive list short by fixing the engine, not by growing allowlists.
- Stay honest on the Lab page whenever the limits change.
- Only add new secret rules when they earn their keep in real scans.
About demos
The public repo has deliberately fake fixtures for tests. Do not treat those as live credentials. A scan of this site or another portfolio should use a local allowlist that never gets committed into a Pages served tree.