site/.checks/pre-push
2026-02-10 22:55:34 +01:00

140 lines
3.7 KiB
Bash
Executable file

#!/usr/bin/env bash
set -euo pipefail
# Colors
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly NC='\033[0m'
readonly REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
readonly LOG_DIR="${REPO_ROOT}/.checks/logs"
log() { echo -e "${GREEN}[INFO] $*${NC}"; }
warn() { echo -e "${YELLOW}[WARN] $*${NC}"; }
err() { echo -e "${RED}[ERROR] $*${NC}" >&2; }
prepare_logs() {
rm -rf "$LOG_DIR"
mkdir -p "$LOG_DIR"
}
# Files that git considers "scannable" (tracked + untracked not ignored)
scannable_files() {
git ls-files --cached --modified --others --exclude-standard
}
# Files listed as ignored by git (used to build tool-specific skip args)
ignored_files() {
git ls-files -o -i --exclude-standard
}
# Build Trivy skip args from git-ignored paths
trivy_skip_args() {
local path dir
while IFS= read -r path; do
[[ -z "$path" ]] && continue
dir="$(dirname "$path")"
printf -- '--skip-files=%q ' "$path"
printf -- '--skip-dirs=%q ' "$dir"
done < <(ignored_files)
}
run_tool() {
local name="$1"; shift
if ! command -v "$name" &>/dev/null; then
warn "$name not installed; skipping."
return 0
fi
"$@"
}
run_trivy() {
run_tool trivy bash -c '
files=("$@")
if [[ ${#files[@]} -eq 0 ]]; then
echo "no files"; exit 0
fi
# Expand skip args passed via environment variable SKIP_ARGS
eval trivy fs "${files[@]}" $SKIP_ARGS --exit-code 1 --severity CRITICAL,HIGH,MEDIUM,LOW,UNKNOWN --no-progress
' _ $(scannable_files)
}
run_trufflehog() {
run_tool trufflehog bash -c '
files=("$@")
tmpf="$(mktemp)"
printf "%s\n" "${files[@]}" | xargs trufflehog filesystem --json >"$tmpf" 2>"'"$LOG_DIR"'/trufflehog.log" || true
if command -v jq &>/dev/null; then
if jq -e '"'"'any(inputs; .verified==true)'"'"' "$tmpf" >/dev/null 2>&1; then
cp "$tmpf" "'"$LOG_DIR"'/trufflehog-findings.json"
echo "verified"; exit 2
fi
fi
rm -f "$tmpf"
' _ $(git ls-files)
local rc=$?; [[ $rc -eq 2 ]] && { err "TruffleHog found verified secrets. See $LOG_DIR/trufflehog-findings.json"; return 1; }
return 0
}
run_git_secrets() {
run_tool git-secrets git-secrets --scan >"$LOG_DIR/git-secrets.log" 2>&1 || {
err "git-secrets detected potential secrets. See $LOG_DIR/git-secrets.log"; return 1; }
}
run_bandit() {
run_tool bandit bash -c '
py=("$@")
[[ ${#py[@]} -eq 0 ]] && exit 0
bandit -r "${py[@]}" -n 5 -lll -f json -o "'"$LOG_DIR"'/bandit.json" 2>"'"$LOG_DIR"'/bandit.log"
' _ $(git ls-files '*.py')
}
run_npm_audit() {
if [[ -f package.json ]]; then
run_tool npm npm audit --audit-level=high >"$LOG_DIR/npm-audit.log" 2>&1 || {
err "npm audit found issues. See $LOG_DIR/npm-audit.log"; return 1; }
fi
}
run_pip_audit() {
if [[ -f requirements.txt ]] || [[ -f pyproject.toml ]]; then
run_tool pip-audit pip-audit -r requirements.txt >"$LOG_DIR/pip-audit.log" 2>&1 || {
err "pip-audit found issues. See $LOG_DIR/pip-audit.log"; return 1; }
fi
}
run_stylelint() {
run_tool stylelint bash -c '
files=("$@")
[[ ${#files[@]} -eq 0 ]] && exit 0
stylelint "${files[@]}" --formatter json > "'"$LOG_DIR"'/stylelint.json" 2>"'"$LOG_DIR"'/stylelint.log"
' _ $(git ls-files '*.{css,scss,sass,less}')
}
main() {
prepare_logs
# Precompute skip args for trivy (safe quoting)
export SKIP_ARGS
SKIP_ARGS="$(trivy_skip_args || true)"
local failed=0
run_trivy || failed=1
run_trufflehog || failed=1
run_git_secrets || failed=1
run_bandit || failed=1
run_npm_audit || failed=1
run_pip_audit || failed=1
run_stylelint || failed=1
if [[ $failed -ne 0 ]]; then
err "Pre-push checks failed. Check logs in $LOG_DIR"
exit 1
fi
log "All pre-push checks passed."
}
main "$@"