.
This commit is contained in:
parent
104c7266a8
commit
db5d576afc
1 changed files with 103 additions and 77 deletions
176
.checks/pre-push
176
.checks/pre-push
|
|
@ -1,114 +1,140 @@
|
|||
#!/usr/bin/env bash
|
||||
# Advanced Pre-Push Security and Quality Check Script
|
||||
|
||||
# Fail fast on any error
|
||||
set -euo pipefail
|
||||
|
||||
# Color codes
|
||||
# Colors
|
||||
readonly RED='\033[0;31m'
|
||||
readonly GREEN='\033[0;32m'
|
||||
readonly YELLOW='\033[1;33m'
|
||||
readonly NC='\033[0m'
|
||||
|
||||
# Paths
|
||||
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
||||
LOG_DIR="${REPO_ROOT}/.checks/logs"
|
||||
readonly REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
||||
readonly LOG_DIR="${REPO_ROOT}/.checks/logs"
|
||||
|
||||
# Ensure log directory exists
|
||||
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"
|
||||
|
||||
# Logging functions
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR] $*${NC}" >&2
|
||||
}
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO] $*${NC}"
|
||||
# Files that git considers "scannable" (tracked + untracked not ignored)
|
||||
scannable_files() {
|
||||
git ls-files --cached --modified --others --exclude-standard
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING] $*${NC}"
|
||||
# Files listed as ignored by git (used to build tool-specific skip args)
|
||||
ignored_files() {
|
||||
git ls-files -o -i --exclude-standard
|
||||
}
|
||||
|
||||
# Vulnerability Scanning with Trivy
|
||||
run_trivy_scan() {
|
||||
# Check if Trivy is installed
|
||||
if ! command -v trivy &>/dev/null; then
|
||||
log_warning "Trivy not installed. Skipping vulnerability scan."
|
||||
# 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
|
||||
"$@"
|
||||
}
|
||||
|
||||
# Ensure we have files to scan
|
||||
local files
|
||||
mapfile -t files < <(git ls-files)
|
||||
|
||||
run_trivy() {
|
||||
run_tool trivy bash -c '
|
||||
files=("$@")
|
||||
if [[ ${#files[@]} -eq 0 ]]; then
|
||||
log_warning "No files to scan with Trivy"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Running Trivy vulnerability scan..."
|
||||
|
||||
# Run Trivy scan
|
||||
if ! trivy fs "${files[@]}" \
|
||||
--exit-code 1 \
|
||||
--severity CRITICAL,HIGH,MEDIUM,LOW,UNKNOWN \
|
||||
--no-progress \
|
||||
>"$LOG_DIR/trivy.log" 2>&1; then
|
||||
log_error "Trivy scan failed. See $LOG_DIR/trivy.log"
|
||||
return 1
|
||||
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)
|
||||
}
|
||||
|
||||
# Secrets Detection with Trufflehog
|
||||
run_trufflehog_scan() {
|
||||
# Check if Trufflehog and jq are installed
|
||||
if ! (command -v trufflehog &>/dev/null && command -v jq &>/dev/null); then
|
||||
log_warning "TruffleHog or jq not installed. Skipping secrets scan."
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Running TruffleHog secrets scan..."
|
||||
local tmpf
|
||||
run_trufflehog() {
|
||||
run_tool trufflehog bash -c '
|
||||
files=("$@")
|
||||
tmpf="$(mktemp)"
|
||||
|
||||
# Scan files
|
||||
if ! trufflehog filesystem . --json >"$tmpf" 2>"$LOG_DIR/trufflehog.log"; then
|
||||
log_error "Trufflehog scan failed"
|
||||
rm -f "$tmpf"
|
||||
return 1
|
||||
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
|
||||
|
||||
# Check for verified secrets
|
||||
local verified
|
||||
verified=$(jq 'select(.verified==true)' "$tmpf" | wc -l | tr -d ' ')
|
||||
|
||||
if [[ "$verified" -gt 0 ]]; then
|
||||
cp "$tmpf" "$LOG_DIR/trufflehog-findings.json"
|
||||
log_error "Verified secrets found. See $LOG_DIR/trufflehog-findings.json"
|
||||
rm -f "$tmpf"
|
||||
return 1
|
||||
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 execution function
|
||||
main() {
|
||||
prepare_logs
|
||||
|
||||
# Precompute skip args for trivy (safe quoting)
|
||||
export SKIP_ARGS
|
||||
SKIP_ARGS="$(trivy_skip_args || true)"
|
||||
|
||||
local failed=0
|
||||
|
||||
# Run each check and track failures
|
||||
run_trivy_scan || failed=1
|
||||
run_trufflehog_scan || failed=1
|
||||
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
|
||||
|
||||
# Exit with error if any checks failed
|
||||
if [[ $failed -eq 1 ]]; then
|
||||
log_error "Some pre-push checks failed. Aborting push."
|
||||
if [[ $failed -ne 0 ]]; then
|
||||
err "Pre-push checks failed. Check logs in $LOG_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "All pre-push checks passed successfully!"
|
||||
log "All pre-push checks passed."
|
||||
}
|
||||
|
||||
# Run the main function
|
||||
main "$@"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue