From 94f965fe3ad4269e7c517f2008a0cf8dac0c1160 Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Fri, 28 Aug 2026 17:37:53 +0200 Subject: [PATCH] Add the no-binaries gate with a path-scoped allowlist 621 binaries are tracked here and they are supposed to be: 155 PDFs, 275 PNGs, 68 JPGs, the submitted reports and figures. Decision 3 keeps them and skips the history rewrite, so a blanket block would be wrong. What the gate is for is stopping NEW junk. So the allowlist is scoped by path rather than by extension alone. Documents, figures and datasets are allowed under Programming/ and NotProgramming/; compiled output (.o .so .a .exe .dll .dylib .class .pyc) is allowed nowhere and has no entry at all; and the repo root, gates/, scripts/ and .github/ have no entry either, so a binary dropped in the tooling directories is blocked. The .tgz entries are load-bearing -- EOPSY ships its simulators as ftp/{sched,memory}.tgz and `make setup` extracts them. Verified three ways, because a gate that passes everything is vacuous: - all 2749 tracked files -> exit 0 - probe.exe at root, probe.png in gates/ -> BLOCKED, exit 1 - a real submitted PDF -> exit 0 plus shellcheck clean. The script is a copy of testsAndMisc's, which is where this gate lives today -- it is not in the shared ~/utils set. The header says so and says to move it to ~/utils rather than let the two drift, since a hand-copied gate is what produced four forks of the 250-line cap. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_0172dj34FDpao6wsix8ZuCzE --- .binary-allowlist | 72 +++++++++++++++++++++++++++ scripts/check_no_binaries.sh | 95 ++++++++++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+) create mode 100644 .binary-allowlist create mode 100755 scripts/check_no_binaries.sh diff --git a/.binary-allowlist b/.binary-allowlist new file mode 100644 index 00000000..b3f3f4ef --- /dev/null +++ b/.binary-allowlist @@ -0,0 +1,72 @@ +# Binary files this repo commits on purpose. One bash glob per line. +# +# Read by scripts/check_no_binaries.sh. Note that matching uses bash `case`, +# where `*` DOES cross `/` -- so `Programming/*.pdf` matches a .pdf at any +# depth under Programming/. +# +# Policy for this repo (approved decision 3). This is an archive of submitted +# university coursework: the reports, figures, schematics and datasets ARE the +# deliverable, so they stay, and history is not rewritten. What the gate is +# actually here to stop is NEW junk -- compiled output, and binaries dropped +# into the tooling directories. +# +# So the allowlist is scoped by PATH, not blanket: +# - documents/figures/data are allowed under Programming/ and NotProgramming/ +# - compiled output (.o .so .a .exe .dll .dylib .class .pyc .pyo) is allowed +# NOWHERE, and has no entry below. 189 such files were untracked in +# d4075278; .gitignore keeps them out. +# - the repo root, gates/, scripts/ and .github/ have no entry either, so a +# binary added there is blocked. +# +# Every pattern here must also survive `git ls-files | git check-ignore +# --stdin --no-index` printing nothing -- an allowlisted file that .gitignore +# also matches fails silently on `git add` rather than erroring. + +# --- Reports and presentations (155 .pdf, 3 .docx, 1 .doc, 1 .pptx) --------- +Programming/*.pdf +NotProgramming/*.pdf +Programming/*.doc +NotProgramming/*.doc +Programming/*.docx +NotProgramming/*.docx +Programming/*.ppt +NotProgramming/*.ppt +Programming/*.pptx +NotProgramming/*.pptx + +# --- Figures, screenshots, schematics (275 .png, 68 .jpg, 52 .gif, ...) ----- +Programming/*.png +NotProgramming/*.png +Programming/*.jpg +NotProgramming/*.jpg +Programming/*.jpeg +NotProgramming/*.jpeg +Programming/*.gif +NotProgramming/*.gif +Programming/*.bmp +NotProgramming/*.bmp +Programming/*.svg +NotProgramming/*.svg +Programming/*.webp +NotProgramming/*.webp +Programming/*.ico +NotProgramming/*.ico +Programming/*.tif +NotProgramming/*.tif +Programming/*.tiff +NotProgramming/*.tiff + +# --- Fonts shipped inside a submitted project ------------------------------ +Programming/*.ttf +NotProgramming/*.ttf + +# --- Datasets and course-supplied archives --------------------------------- +# The .tgz entries matter: EOPSY ships its simulators as ftp/sched.tgz and +# ftp/memory.tgz, and `make setup` extracts them. Blocking those would break +# the lab. The .zip files are submitted result bundles and course inputs. +Programming/*.zip +NotProgramming/*.zip +Programming/*.tgz +NotProgramming/*.tgz +Programming/*.bin +NotProgramming/*.bin diff --git a/scripts/check_no_binaries.sh b/scripts/check_no_binaries.sh new file mode 100755 index 00000000..080428a8 --- /dev/null +++ b/scripts/check_no_binaries.sh @@ -0,0 +1,95 @@ +#!/bin/bash +# Pre-commit hook: block binary and image files from being committed. +# +# Copied from ~/testsAndMisc/meta/scripts/check_no_binaries.sh, which is where +# this gate lives today (it is NOT in the shared ~/utils gate set). Keep the two +# in step, and prefer moving it to ~/utils over letting them drift -- a +# hand-copied gate is exactly what produced four forks of the 250-line cap. +# +# Only the remedy text differs: "move it to a sibling _binaries/ dir" is wrong +# advice here, because in a coursework archive the PDFs and figures ARE the +# submitted deliverable (approved decision 3: allowlist in place, no history +# rewrite). +# Allowed exceptions are listed in .binary-allowlist (one glob pattern per line). +set -euo pipefail + +ALLOWLIST_FILE=".binary-allowlist" + +# Binary/image extensions to block +BLOCKED_EXTENSIONS=( + # Images + png jpg jpeg gif webp svg ico bmp tiff tif psd + # Audio/Video + mp3 mp4 wav avi mkv flac ogg wma aac m4a mov wmv flv + # Archives + zip tar gz tgz bz2 xz 7z rar + # Documents + pdf doc docx xls xlsx ppt pptx + # Fonts + ttf woff woff2 eot otf + # Compiled / binary + o so a exe dll dylib pyc pyo class + # Data + apkg bin flat db sqlite sqlite3 +) + +# Build regex pattern from extensions +pattern="" +for ext in "${BLOCKED_EXTENSIONS[@]}"; do + if [ -z "$pattern" ]; then + pattern="\\.(${ext}" + else + pattern="${pattern}|${ext}" + fi +done +pattern="${pattern})$" + +# Load allowlist +allowed_patterns=() +if [ -f "$ALLOWLIST_FILE" ]; then + while IFS= read -r line; do + # Skip comments and empty lines + [[ "$line" =~ ^[[:space:]]*# ]] && continue + [[ -z "${line// /}" ]] && continue + allowed_patterns+=("$line") + done < "$ALLOWLIST_FILE" +fi + +is_allowed() { + local file="$1" + for pat in "${allowed_patterns[@]+"${allowed_patterns[@]}"}"; do + # Use bash pattern matching (glob) + # shellcheck disable=SC2254 + case "$file" in + $pat) return 0 ;; + esac + done + return 1 +} + +found=0 +for file in "$@"; do + # Skip files not tracked by git (deleted from index or untracked) + git ls-files --error-unmatch "$file" >/dev/null 2>&1 || continue + # Check if the file matches a blocked extension + if echo "$file" | grep -qiE "$pattern"; then + if is_allowed "$file"; then + continue + fi + echo "BLOCKED: $file" + echo " Binary/image files should not be committed to the repo." + echo " In this coursework archive the submitted reports, figures and" + echo " datasets are allowed -- but only under Programming/ or NotProgramming/." + echo " To allow this file, add a glob pattern to $ALLOWLIST_FILE" + found=1 + fi +done + +if [ "$found" -eq 1 ]; then + echo "" + echo "ERROR: Attempted to commit binary/image files." + echo "Options:" + echo " 1. A submitted deliverable? Add a glob to $ALLOWLIST_FILE" + echo " 2. Build output? Add it to .gitignore instead, never the allowlist" + exit 1 +fi