Skip to content

Pre-commit guard gaps: 500KB warn tier, lower block threshold, first-submission binary detection #6

Description

@djdarcy

Pre-commit guard gaps: 500KB warn tier, lower block threshold, and first-submission binary detection

Problem

The pre-commit hook's large-file check blocks only files over 10 MB (hooks/pre-commit, the du -k / $1 > 10240 check). There is no warning tier below that, and no binary detection at all.

This let a real incident through: two 9.68 MB plain-text capture files were committed to a public repo and shipped in every release tag across several minor versions before anyone noticed. They passed the gate by ~3% (9,916 KB < 10,240 KB). Cleanup required a history rewrite on a public repo with signed tags.

The failure modes the current check cannot catch:

  • Just-under-threshold files: 9.9 MB sails through a 10 MB gate silently — no warning, no prompt, nothing in the commit output.
  • Privacy-heavy small files: a 2 MB filesystem manifest is as much of an exposure as a 10 MB one; size alone was never the real risk signal.
  • Binaries: a first-time binary submission (image, archive, database, compiled artifact) is usually either an accident or something that deserves a deliberate decision (LFS, gitignore, allowlist) — the hook says nothing.

Proposed solution

Tiered size guard plus first-submission binary detection, all in hooks/pre-commit:

  1. Warn tier at >500 KB (default): staged files over 500 KB print a prominent warning with the file name and size, but the commit proceeds. The warning is the point — the incident above would have been caught by a human reading two lines of output.
  2. Block tier lowered to >5 MB (default): same behavior as today's block (exit 1, suggest LFS / gitignore / allowlist), threshold halved. Files that genuinely belong at that size go through the existing .repokit-allowlist bypass.
  3. First-submission binary detection: for staged files that are newly added (--diff-filter=A), detect binary content and warn. Detection is free — git diff --cached --numstat prints - - instead of line counts for binary blobs, so no file reads, no extra processes, and it uses git's own binary heuristic rather than a homegrown one.

Example output:

!  Large file staged: tests/captures/big-output.txt (9,916 KB > 500 KB warn threshold)
   If this is deliberate, consider private/ (untracked) or .repokit-allowlist.
!  New binary file staged: docs/images/diagram.png (git reports binary content)
   First-time binaries deserve a deliberate decision: LFS, .gitignore, or allowlist.

Configuration

Defaults should be overridable per-repo without editing the hook (the hook ships via subtree; local edits leak upstream — see #5). Simplest mechanism consistent with what exists: read optional limits from a .repokit-limits file in repo root (same comment/blank-line conventions as .repokit-allowlist):

# .repokit-limits -- optional; defaults apply when absent
warn_kb = 500
block_kb = 5120
binary_warn = true

.repokit-allowlist continues to bypass both size tiers and the binary warning for listed paths — one escape hatch, already documented, already understood.

Design considerations

  • Warn-not-block at the low tier is deliberate. A 500 KB block would fight legitimate workflows (fixtures, snapshots, vendored assets) and train people to bypass the hook. A warning costs nothing and would have caught the motivating incident.
  • Binary detection only on first submission (--diff-filter=A) keeps noise at zero for repos that already track binaries — modifying an existing tracked binary stays silent.
  • git diff --cached --numstat is the right detector: it reflects exactly what git will store, respects .gitattributes text/binary overrides, and is one process for the whole staged set (matching the hook's existing one-grep-for-everything performance posture).
  • Thresholds are opinions, not physics — 500 KB / 5 MB are proposed defaults; the config file is the pressure valve. GitHub's own UI warns at 50 MB and blocks at 100 MB, which is far too late for the privacy-exposure class of mistake this guard is really for.
  • The size check must keep using the NUL-delimited staged list (paths with spaces) — the existing xargs -0 du -k pattern already handles this; the new tiers slot into the same pipeline.

Acceptance criteria

  • Staged file over the warn threshold (default 500 KB) prints a warning naming the file and its size; commit proceeds
  • Staged file over the block threshold (default 5 MB) blocks the commit with the existing LFS/gitignore/allowlist guidance
  • Newly added (--diff-filter=A) binary file prints a first-submission warning; modified existing binaries stay silent
  • .repokit-allowlist entries bypass all three checks
  • .repokit-limits overrides the default thresholds and can disable the binary warning; absent file means defaults
  • Paths with spaces survive all three checks (NUL-delimited list throughout)
  • A 9.9 MB text file — the incident class — produces a loud warning under defaults
  • Hook README / CONTRIBUTING documents the tiers, defaults, and both config files

Related issues

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions