Add defense-in-depth binary file protection system

Layer 1: Pre-commit hook (scripts/check_no_binaries.sh) blocks 60+
binary/image extensions with .binary-allowlist for build-essential exceptions.

Layer 2: Comprehensive .gitignore binary patterns with ! overrides
for allowlisted files (app icons).

Layer 3: Agent exclusions - .copilotignore, files.exclude, and
search.exclude all mirror the same patterns to prevent Copilot
from hitting the 20-image URL limit.
This commit is contained in:
Krzysztof Rudnicki 2026-03-25 21:09:03 +01:00
parent c444f0451a
commit 16c6b207e6
6 changed files with 243 additions and 34 deletions

12
.binary-allowlist Normal file
View File

@ -0,0 +1,12 @@
# Binary files allowed in the repository.
# One glob pattern per line. These are essential for builds and cannot be external.
# Lines starting with # are comments.
# Flutter app launcher icons (required for Android builds)
pomodoro_app/android/app/src/main/res/mipmap-*/ic_launcher.png
# Pomodoro app packaging icon
pomodoro_app/packaging/arch/*.svg
# Battery status favicon
TS/battery-status/public/favicon.svg

View File

@ -1,5 +1,8 @@
# ===========================================================================
# Binary / image files (** prefix for recursive matching)
**/*.apkg
# Mirrors .gitignore binary block — defense-in-depth for Copilot context
# ===========================================================================
# Images
**/*.png
**/*.jpg
**/*.jpeg
@ -8,28 +11,64 @@
**/*.svg
**/*.ico
**/*.bmp
**/*.pdf
**/*.tiff
**/*.tif
**/*.psd
# Audio / Video
**/*.mp3
**/*.mp4
**/*.wav
**/*.avi
**/*.mkv
**/*.flac
**/*.ogg
**/*.wma
**/*.aac
**/*.m4a
**/*.mov
# Archives
**/*.zip
**/*.tar.gz
**/*.tgz
**/*.bin
**/*.exe
**/*.so
**/*.o
**/*.a
**/*.pyc
**/*.pyo
**/*.class
**/*.wav
**/*.mp3
**/*.mp4
**/*.avi
**/*.bz2
**/*.xz
**/*.7z
**/*.rar
# Documents
**/*.pdf
**/*.doc
**/*.docx
**/*.xls
**/*.xlsx
**/*.ppt
**/*.pptx
# Fonts
**/*.ttf
**/*.woff
**/*.woff2
**/*.eot
**/*.otf
# Compiled / binary
**/*.o
**/*.a
**/*.so
**/*.exe
**/*.dll
**/*.dylib
**/*.pyc
**/*.pyo
**/*.class
# Data
**/*.apkg
**/*.bin
**/*.flat
**/*.db
**/*.sqlite
**/*.sqlite3
# ===========================================================================
# Large directories
# ===========================================================================
# Virtual environments
.venv/
**/.venv/
@ -49,29 +88,23 @@ pomodoro_app/build/
sonic_pi/build/
CPP/mini_browser/build/
# C random JPG images (46k+ files)
C/misc/randomJPG/
# Generated / preview images
**/generated_images_*/
**/preview_images/
# Anki decks (2GB+ with images and .venvs)
# Anki decks
python_pkg/anki_decks/
# Image-heavy directories
python_pkg/articles/uploads/
# Image-heavy directories (moved to ../testsAndMisc_binaries/)
python_pkg/praca_magisterska_video/images/
python_pkg/praca_magisterska_video/
python_pkg/articles/uploads/
python_pkg/download_cats/http_cat_cache/
python_pkg/praca_magisterska_video/
C/misc/randomJPG/
# Files/dirs with many embedded image references
python_pkg/praca_magisterska_video/
# Files with many embedded image references
python_pkg/cinema_planner/pasted_content.txt
# Downloaded image caches
python_pkg/download_cats/http_cat_cache/
# C build artifacts
**/*.out
**/random_engine

81
.gitignore vendored
View File

@ -40,18 +40,85 @@ testem.log
# System files
.DS_Store
Thumbs.db
*.mkv
# ===========================================================================
# BINARY / IMAGE FILES - Move to ../testsAndMisc_binaries/ instead
# See .binary-allowlist for exceptions required by builds
# ===========================================================================
# Images
*.png
*.jpg
*.jpeg
*.gif
*.webp
*.bmp
*.ico
*.tiff
*.tif
*.psd
# SVG is text but often used as image asset
*.svg
# Audio / Video
*.mp3
*.mp4
imageviewer
title_test.png
*.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
# Data / binary
*.apkg
*.bin
*.flat
*.db
*.sqlite
*.sqlite3
# Compiled
*.exe
*.dll
*.dylib
# Explicitly allowed binary files (override above with !)
!pomodoro_app/android/app/src/main/res/mipmap-*/ic_launcher.png
!pomodoro_app/packaging/arch/pomodoro-app.svg
!TS/battery-status/public/favicon.svg
# ===========================================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[codz]
*$py.class
# C extensions
*.so
# C extensions (*.so covered in binary block above)
# Distribution / packaging
.Python
@ -293,8 +360,7 @@ python_pkg/music_gen/output/
python_pkg/screen_locker/sick_day_state.json
python_pkg/screen_locker/workout_log.json.bak
preview_images
# Anki generated packages (large binary files)
*.apkg
# Anki generated packages (*.apkg covered in binary block above)
# Geographic data cache (can be regenerated)
python_pkg/geo_cache/
@ -326,4 +392,3 @@ cinema_plan_*.txt
# Binary images moved to external storage (testsAndMisc_binaries/)
python_pkg/praca_magisterska_video/images/
*.apkg

View File

@ -55,6 +55,17 @@ repos:
args: [--fix=lf]
- id: requirements-txt-fixer
# ===========================================================================
# BINARY BLOCKER - Prevent binary/image files from being committed
# ===========================================================================
- repo: local
hooks:
- id: no-binaries
name: Block binary/image files
entry: scripts/check_no_binaries.sh
language: script
always_run: false
# ===========================================================================
# NOQA BLOCKER - Zero tolerance for noqa/type:ignore suppression comments
# ===========================================================================

View File

@ -37,8 +37,14 @@
"**/*.o": true,
"**/*.flat": true,
"**/*.wav": true,
"**/*.mp3": true,
"**/*.ttf": true,
"**/*.woff": true,
"**/*.woff2": true,
"**/*.class": true,
"**/*.bin": true,
"**/*.db": true,
"**/*.sqlite": true,
"**/node_modules": true,
"python_pkg/anki_decks": true,
"python_pkg/praca_magisterska_video": true,

82
scripts/check_no_binaries.sh Executable file
View File

@ -0,0 +1,82 @@
#!/bin/bash
# Pre-commit hook: block binary and image files from being committed.
# 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
# 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 " Move to ../testsAndMisc_binaries/ instead."
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. Move files to ../testsAndMisc_binaries/ (preferred)"
echo " 2. Add pattern to $ALLOWLIST_FILE (for essential files only)"
exit 1
fi