Pre-Commit Audit — File Size, Anonymity, Secrets
SkillFiles & storageDeliver a fast pre-commit safety scan: file size, anonymity (author / affiliation strings in tex/bib), hardcoded secrets, and invisible-Unicode carriers. Use when the user requests a fast pre-commit safety scan: file size, anonymity (author / affiliation strings in tex/bib), hardcoded secrets, and invisible-Unicode carriers. Triggers: 'audit before commit', 'check before push', 'pre-commit scan', 'safety check'.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Pre-Commit Audit — File Size, Anonymity, Secrets skill
What this skill tells your AI
The instructions your AI receives, as published by flonat/flonat-research in skills/pre-commit-audit/SKILL.md and read by ahel’s review.
Three-pass safety scan before commit. Blocks pain-points: oversized commits (1.3GB parquet incident), CCS-style anonymity breaches, leaked credentials. Each pass is a hard gate; user OKs proceed.
Hard Rules
Existential — block proceed
- Size pass blocks anything >10MB unless gitignored OR user explicitly approves. Threshold matches
block-large-files.shhook for consistency. - Anonymity pass runs only on paper-relevant paths:
paper-*/,*.tex,*.bib,*.mdinside paper directories. Scope is data-driven — we don't false-flag README authorship. - Secrets pass uses an entropy heuristic + known-prefix list. No regex-only matching that misses high-entropy keys. Block on confirmed secret; warn on suspicious-but-uncertain.
- All four passes run on the same file set (default: staged for commit). Don't mix staged-only size check with all-files anonymity check.
- The Unicode pass is the one pass that is always in scope, including infra-only commits. It is identity-blind — it reads codepoints, never names — so it cannot false-flag authorship the way the anonymity pass can.
Format — catch in review
- Output a single consolidated table (file × pass × verdict) — not four separate reports.
- Severity tiers: BLOCK (must fix), WARN (proceed with confirm), OK.
- Always show the file path and line number when flagging — make it copy-paste-fixable.
When to Use
- Before any
git commiton a paper / project repo - Pre-submission checklist (research projects)
- Before pushing to anonymous-artifact repo (the highest-risk anonymity surface)
- After a sub-agent has written files (verify before committing their work)
- Wired as a
session-closePhase 4 sub-step (alternate path to running the hook)
When NOT to Use
- Before commits on infrastructure-only changes (skills, hooks, rules) — anonymity scan would false-flag README author lines. Exception: Phase 4 (Unicode) stays valid there and can be run alone:
pre-commit-audit --unicode-only - Auto-generated commits (CI, dependabot) — they don't need the scan
- When the change is purely metadata (
.gitignore,LICENSE, etc.)
Modes
| Invocation | Scope |
|---|---|
pre-commit-audit | Staged files (default) |
pre-commit-audit --staged | Same as default |
pre-commit-audit --unstaged | Modified-but-not-staged files |
pre-commit-audit --all | Working tree + staged |
pre-commit-audit --unicode-only | Phase 4 only — safe on infra commits |
Architecture
Phase 1 (size) → find files >10MB; check .gitignore status
Phase 2 (anonymity) → grep paper paths for author/affiliation/email
Phase 3 (secrets) → entropy + prefix heuristics on all staged files
Phase 4 (unicode) → invisible-carrier scan via check_invisible_unicode.py
Phase 5 (consolidate) → single table; user OK to proceed
Phase 1: Size
THRESHOLD_BYTES=$((10 * 1024 * 1024))
git diff --cached --name-only --diff-filter=ACM | while read f; do
if [ -f "$f" ]; then
size=$(stat -f%z "$f" 2>/dev/null || stat -c%s "$f" 2>/dev/null || echo 0)
if [ "$size" -gt "$THRESHOLD_BYTES" ]; then
ignored=$(git check-ignore "$f" 2>/dev/null && echo "ignored" || echo "tracked")
echo "$f|$size|$ignored"
fi
fi
done
Verdict: BLOCK if any tracked file >10MB. WARN if gitignored but staged anyway.
Phase 2: Anonymity
Single source of truth: skills/_shared/double-blind-anonymity-checklist.md holds the authoritative pattern list (paper-side P1-P8, artifact-side A1-A6) plus the CCS 2026 #1328 incident report that motivates the checks. Both pre-commit-audit (this skill) and anonymous-artifact reference it; do not duplicate the patterns inline.
Scope for pre-commit-audit: only files staged for commit at paths matching paper-*/, **/*.tex, **/*.bib, **/*.cls, **/*.sty. The full checklist also covers structured-metadata files (pyproject.toml, package.json, Cargo.toml, CITATION.cff, etc.) — those are checked by anonymous-artifact since they only matter when pushing the artifact, not on every commit.
Apply checks P1-P8 from the shared checklist:
- P1 — title page authors blinded
- P2 — no
\thanks{}/\acknowledgements/\funded/\grantrevealing identity - P3 — first-person self-reference uses third person (no "our prior work")
- P4 — self-citation bib entries blinded if author overlap
- P5 — body text doesn't name authors of self-cited works
- P6 — no identifying URLs (personal sites, lab pages, repos with handles)
- P7 — PDF metadata sanitized (
pdfinfo paper.pdf) - P8 — figures contain no identifying watermarks/captions
For each match: print path:line and the matched text (truncated). Verdict: BLOCK on any match in a path containing paper-*/. WARN on matches outside paper directories.
Exception: a frontmatter-style Made by the user in friends-repo / public-repo READMEs is allowed (intentional attribution).
Phase 3: Secrets
Patterns by category:
| Category | Detection |
|---|---|
| API keys with prefix | sk-, sk-proj-, sk-ant-, hf_, gh[a-zA-Z]_, xoxb-, AIza |
| AWS | AKIA[0-9A-Z]{16}, aws_secret_access_key |
| Generic high-entropy strings | length ≥32, base64-ish character set, no whitespace |
.env-style assignments | ^[A-Z_]+=[a-zA-Z0-9+/=_-]{20,} outside .env.example/.env.template |
Skip .env.template, .env.example, *.example, comment lines starting with #.
Verdict: BLOCK on any match in a non-template file.
Phase 4: Invisible Unicode
Catches characters that are invisible on screen but change what a machine reads — zero-width carriers, bidi overrides, tag characters, and space homoglyphs. These arrive by paste (Word, PDF, web page, LLM output) and fail silently: a U+200B inside a citekey makes BibTeX drop the entry with no error, a U+00A0 makes an exact-match string edit fail on a line that looks identical.
Deterministic — delegate to the script rather than grepping by hand:
git diff --cached --name-only --diff-filter=ACM \
| while read -r f; do [ -f "$f" ] && printf '%s\n' "$f"; done \
| xargs -r uv run --no-sync python "<skill-dir>/scripts/check_invisible_unicode.py" --format json
Map the script's severities onto this skill's tiers:
| Script severity | Meaning | Verdict |
|---|---|---|
critical | Zero-width / bidi / tag char, or any invisible inside a citekey, crossref, path, url, bib_entry_key, bib_doi | BLOCK |
warning | Space homoglyph or soft hyphen in prose — renders fine, breaks exact-match editing and grep | WARN |
rescued | Legitimate emoji ZWJ / VS16 sequence | not reported |
Report path:line:col, the U+XXXX label, and the containing construct when the JSON carries a context. The context is most of the signal: the same U+00A0 is a WARN in prose and a BLOCK inside \ref{}.
Fixes are mechanical, so offer them — but never apply silently (Anti-Patterns below):
uv run --no-sync python "<skill-dir>/scripts/check_invisible_unicode.py" <path> --diff # preview
uv run --no-sync python "<skill-dir>/scripts/check_invisible_unicode.py" <path> --apply # write
--apply refuses Overleaf-canonical paths unless given --yes-live-surface, per reconcile-before-rewriting.md; if a flagged file is a live Overleaf surface, surface the finding and let the user fix it in the editor rather than passing that flag on their behalf.
Phase 5: Consolidate + ask
Single table:
File Size Anon Secrets Uni Verdict
────────────────────────────────────────────────────────────────────────────
paper-ccs/paper/main.tex 5KB OK OK OK OK
paper-ccs/paper/references.bib 12KB FAIL OK FAIL BLOCK (line 23: the user; line 8: U+200B in bib_entry_key)
data/raw/results.parquet 1.3GB N/A N/A N/A BLOCK (size; not gitignored)
scripts/sync-creds.sh 2KB OK FAIL OK BLOCK (line 15: hf_<...>)
notes/from-word.md 4KB OK OK WARN WARN (4x U+00A0)
If any BLOCK rows: ask the available structured-question mechanism:
- Fix the issues and rerun — recommended
- Override and proceed — risky, requires explicit confirmation
- Cancel commit — abort
If only WARN rows: ask whether to proceed. If all OK: print "Pre-commit clean ✓" and exit silently.
Cross-References
| Skill / Hook / Rule | Relationship |
|---|---|
block-large-files.sh hook | Runtime enforcement of Phase 1 (this skill is the manual / verbose counterpart) |
scripts/check_invisible_unicode.py | Bundled deterministic engine behind Phase 4. Codepoint tables adapted from guillaumemeyer/watermarks-remover (MIT); severity tiers, LaTeX-context detection, and emoji rescue are local |
reconcile-before-rewriting.md rule | Governs Phase 4's --apply on live Overleaf surfaces |
anonymous-artifact | The anonymity-on-publish workflow; this skill is the pre-commit equivalent |
session-close Phase 4 | Can invoke this skill as part of pre-commit verification |
mark-unverified.md rule | Verbal counterpart for content claims; this skill handles file content |
Anti-Patterns
- Don't scan binary files for anonymity strings — false positives on PDFs, images.
- Don't rely solely on regex for secret detection — entropy heuristics catch keys with novel prefixes.
- Don't auto-fix issues — surface them for the user to handle. Auto-replacement of names risks breaking acknowledgements.
- Don't skip the .gitignore check — large gitignored files staged anyway are a signal of
git add -Amistakes. - Don't require this skill on infra-only commits. Add a path filter or let the user opt out for those — but
--unicode-onlyremains safe and useful there. - Don't run Phase 4's
--applyas part of the audit. The audit reports; the user decides. Auto-rewriting bytes in a file the user is mid-edit on is the exact failurereconcile-before-rewriting.mdexists to prevent. - Don't flag visible non-ASCII. Accented names (
López-Ibáñez), em dashes, and→are legitimate and the script deliberately ignores them; a pass that flags them will be muted within a week.
Signals
- GitHub stars
- 133
- Forks
- 24
- Last commit
- Aug 2026
Advanced
- Catalog kind
- skill
- Gateway key
pre-commit-audit- Source
- github.com/flonat/flonat-research