Add Trivy security scanning to git hooks
ci/woodpecker/push/woodpecker Pipeline was successful Details

- Add mandatory Trivy image scanning to both pre-commit and pre-push hooks
- Remove interactive prompts from install script, add --force flag instead
- Add automatic cleanup of temporary Docker images after scanning
- Check for Docker and Trivy dependencies before running hooks
This commit is contained in:
Colin 2026-01-24 09:58:50 -05:00
parent 2907277b52
commit 305a0ece5e
Signed by: colin
SSH Key Fingerprint: SHA256:nRPCQTeMFLdGytxRQmPVK9VXY3/ePKQ5lGRyJhT5DY8
2 changed files with 187 additions and 70 deletions

View File

@ -1,10 +1,33 @@
#!/bin/bash #!/bin/bash
# Installation script for Git hooks in Hastebin # Installation script for Git hooks in Hastebin
# This script sets up pre-commit hooks (tests) and pre-push hooks (unused code scans) # This script sets up pre-commit hooks (tests + security scan) and pre-push hooks (unused code + security scan)
#
# Usage:
# ./install-git-hooks.sh [--force]
#
# Options:
# --force Overwrite existing hooks without prompting (backs up existing hooks)
# Without this flag, fails if hooks already exist
set -e set -e
# Parse arguments
FORCE_MODE=false
while [[ $# -gt 0 ]]; do
case $1 in
--force)
FORCE_MODE=true
shift
;;
*)
echo "Unknown option: $1"
echo "Usage: $0 [--force]"
exit 1
;;
esac
done
# Colors for output # Colors for output
RED='\033[0;31m' RED='\033[0;31m'
GREEN='\033[0;32m' GREEN='\033[0;32m'
@ -16,40 +39,49 @@ NC='\033[0m' # No Color
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || echo "$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)") REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || echo "$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)")
HOOKS_DIR="$REPO_ROOT/.git/hooks" HOOKS_DIR="$REPO_ROOT/.git/hooks"
PRE_COMMIT_HOOK="$HOOKS_DIR/pre-commit" PRE_COMMIT_HOOK="$HOOKS_DIR/pre-commit"
PRE_PUSH_HOOK="$HOOKS_DIR/pre-push"
SCRIPT_DIR="$REPO_ROOT/scripts" SCRIPT_DIR="$REPO_ROOT/scripts"
echo -e "${BLUE}🔧 Installing Git hooks for Hastebin${NC}" echo -e "${BLUE}Installing Git hooks for Hastebin${NC}"
echo "" echo ""
# Check if we're in a git repository # Check if we're in a git repository
if [ ! -d "$REPO_ROOT/.git" ]; then if [ ! -d "$REPO_ROOT/.git" ]; then
echo -e "${RED}Error: Not a git repository${NC}" echo -e "${RED}Error: Not a git repository${NC}"
exit 1 exit 1
fi fi
# Create hooks directory if it doesn't exist # Create hooks directory if it doesn't exist
mkdir -p "$HOOKS_DIR" mkdir -p "$HOOKS_DIR"
# Check if pre-commit hook already exists # Function to handle existing hook
if [ -f "$PRE_COMMIT_HOOK" ]; then handle_existing_hook() {
echo -e "${YELLOW}⚠️ Pre-commit hook already exists${NC}" local hook_path="$1"
read -p "Do you want to overwrite it? (y/N): " -n 1 -r local hook_name="$2"
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then if [ -f "$hook_path" ]; then
echo -e "${YELLOW}Skipping hook installation${NC}" if [ "$FORCE_MODE" = true ]; then
exit 0 # Backup existing hook with timestamp
mv "$hook_path" "$hook_path.backup.$(date +%Y%m%d_%H%M%S)"
echo -e "${YELLOW}Backed up existing $hook_name hook${NC}"
else
echo -e "${RED}Error: $hook_name hook already exists at $hook_path${NC}"
echo -e "${YELLOW}Use --force to overwrite (will backup existing hook)${NC}"
exit 1
fi
fi fi
# Backup existing hook }
mv "$PRE_COMMIT_HOOK" "$PRE_COMMIT_HOOK.backup.$(date +%Y%m%d_%H%M%S)"
echo -e "${YELLOW}Backed up existing hook${NC}"
fi
# Create the pre-commit hook # Check for existing hooks
handle_existing_hook "$PRE_COMMIT_HOOK" "pre-commit"
handle_existing_hook "$PRE_PUSH_HOOK" "pre-push"
# --- Create the pre-commit hook ---
cat > "$PRE_COMMIT_HOOK" << 'HOOK_EOF' cat > "$PRE_COMMIT_HOOK" << 'HOOK_EOF'
#!/bin/bash #!/bin/bash
# Git pre-commit hook for Hastebin # Git pre-commit hook for Hastebin
# Runs tests before allowing commits to prevent pushing broken code # Runs tests and security scans before allowing commits
set -e set -e
@ -60,11 +92,31 @@ YELLOW='\033[0;33m'
NC='\033[0m' # No Color NC='\033[0m' # No Color
echo -e "${YELLOW}Running pre-commit checks...${NC}" echo -e "${YELLOW}Running pre-commit checks...${NC}"
echo ""
# Get the repository root # Get the repository root
REPO_ROOT=$(git rev-parse --show-toplevel) REPO_ROOT=$(git rev-parse --show-toplevel)
cd "$REPO_ROOT" cd "$REPO_ROOT"
# Check required dependencies
MISSING_DEPS=0
if ! command -v docker &> /dev/null; then
echo -e "${RED}Docker not found - required for security scanning${NC}"
MISSING_DEPS=1
fi
if ! command -v trivy &> /dev/null; then
echo -e "${RED}Trivy not found - required for security scanning${NC}"
echo " Install: brew install trivy"
MISSING_DEPS=1
fi
if [ $MISSING_DEPS -ne 0 ]; then
echo -e "${RED}Missing required dependencies. Commit aborted.${NC}"
exit 1
fi
# Check if node_modules exists, if not, install dependencies # Check if node_modules exists, if not, install dependencies
if [ ! -d "node_modules" ]; then if [ ! -d "node_modules" ]; then
echo -e "${YELLOW}Installing dependencies...${NC}" echo -e "${YELLOW}Installing dependencies...${NC}"
@ -72,11 +124,11 @@ if [ ! -d "node_modules" ]; then
fi fi
# Run the core tests (faster than full test suite) # Run the core tests (faster than full test suite)
echo -e "${YELLOW}Running core tests...${NC}" echo -e "${YELLOW}[1/2] Running core tests...${NC}"
if npm run test:core; then if npm run test:core; then
echo -e "${GREEN}Core tests passed${NC}" echo -e "${GREEN}Core tests passed${NC}"
else else
echo -e "${RED}Core tests failed. Commit aborted.${NC}" echo -e "${RED}Core tests failed. Commit aborted.${NC}"
echo -e "${YELLOW}To skip this check, use: git commit --no-verify${NC}" echo -e "${YELLOW}To skip this check, use: git commit --no-verify${NC}"
exit 1 exit 1
fi fi
@ -86,37 +138,31 @@ if [ -d "test-data" ]; then
rm -rf test-data rm -rf test-data
fi fi
echo -e "${GREEN}✅ Pre-commit checks passed${NC}" # Run Trivy security scan
echo ""
echo -e "${YELLOW}[2/2] Running security scan (Trivy image scan)...${NC}"
if "$REPO_ROOT/scripts/scan-trivy-image.sh"; then
echo -e "${GREEN}Security scan passed${NC}"
else
echo -e "${RED}Security scan found vulnerabilities. Commit aborted.${NC}"
echo -e "${YELLOW}To skip this check, use: git commit --no-verify${NC}"
exit 1
fi
echo ""
echo -e "${GREEN}Pre-commit checks passed${NC}"
exit 0 exit 0
HOOK_EOF HOOK_EOF
# Make the hook executable
chmod +x "$PRE_COMMIT_HOOK" chmod +x "$PRE_COMMIT_HOOK"
echo -e "${GREEN}Pre-commit hook installed${NC}"
echo -e "${GREEN}✅ Pre-commit hook installed successfully${NC}" # --- Create the pre-push hook ---
cat > "$PRE_PUSH_HOOK" << 'HOOK_EOF'
# --- Pre-push hook ---
PRE_PUSH_HOOK="$HOOKS_DIR/pre-push"
# Check if pre-push hook already exists
if [ -f "$PRE_PUSH_HOOK" ]; then
echo -e "${YELLOW}⚠️ Pre-push hook already exists${NC}"
read -p "Do you want to overwrite it? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo -e "${YELLOW}Skipping pre-push hook installation${NC}"
else
mv "$PRE_PUSH_HOOK" "$PRE_PUSH_HOOK.backup.$(date +%Y%m%d_%H%M%S)"
echo -e "${YELLOW}Backed up existing pre-push hook${NC}"
fi
fi
if [ ! -f "$PRE_PUSH_HOOK" ] || [[ $REPLY =~ ^[Yy]$ ]]; then
cat > "$PRE_PUSH_HOOK" << 'HOOK_EOF'
#!/bin/bash #!/bin/bash
# Git pre-push hook for Hastebin # Git pre-push hook for Hastebin
# Scans for unused code/dependencies before pushing # Scans for unused code/dependencies and runs security scan before pushing
set -e set -e
@ -127,11 +173,31 @@ YELLOW='\033[0;33m'
NC='\033[0m' # No Color NC='\033[0m' # No Color
echo -e "${YELLOW}Running pre-push checks...${NC}" echo -e "${YELLOW}Running pre-push checks...${NC}"
echo ""
# Get the repository root # Get the repository root
REPO_ROOT=$(git rev-parse --show-toplevel) REPO_ROOT=$(git rev-parse --show-toplevel)
cd "$REPO_ROOT" cd "$REPO_ROOT"
# Check required dependencies
MISSING_DEPS=0
if ! command -v docker &> /dev/null; then
echo -e "${RED}Docker not found - required for security scanning${NC}"
MISSING_DEPS=1
fi
if ! command -v trivy &> /dev/null; then
echo -e "${RED}Trivy not found - required for security scanning${NC}"
echo " Install: brew install trivy"
MISSING_DEPS=1
fi
if [ $MISSING_DEPS -ne 0 ]; then
echo -e "${RED}Missing required dependencies. Push aborted.${NC}"
exit 1
fi
# Check if node_modules exists, if not, install dependencies # Check if node_modules exists, if not, install dependencies
if [ ! -d "node_modules" ]; then if [ ! -d "node_modules" ]; then
echo -e "${YELLOW}Installing dependencies...${NC}" echo -e "${YELLOW}Installing dependencies...${NC}"
@ -141,48 +207,61 @@ fi
# Scan for unused code/dependencies # Scan for unused code/dependencies
SCAN_FAILED=0 SCAN_FAILED=0
echo -e "${YELLOW}Scanning for unused files/exports/dependencies (knip)...${NC}" echo -e "${YELLOW}[1/3] Scanning for unused files/exports/dependencies (knip)...${NC}"
if npx --yes knip 2>/dev/null; then if npx --yes knip 2>/dev/null; then
echo -e "${GREEN}knip passed${NC}" echo -e "${GREEN}knip passed${NC}"
else else
echo -e "${RED}knip found unused code${NC}" echo -e "${RED}knip found unused code${NC}"
SCAN_FAILED=1 SCAN_FAILED=1
fi fi
echo -e "${YELLOW}Scanning for unused npm dependencies (depcheck)...${NC}" echo ""
echo -e "${YELLOW}[2/3] Scanning for unused npm dependencies (depcheck)...${NC}"
if npx --yes depcheck 2>/dev/null; then if npx --yes depcheck 2>/dev/null; then
echo -e "${GREEN}depcheck passed${NC}" echo -e "${GREEN}depcheck passed${NC}"
else else
echo -e "${RED}depcheck found issues${NC}" echo -e "${RED}depcheck found issues${NC}"
SCAN_FAILED=1 SCAN_FAILED=1
fi fi
if [ $SCAN_FAILED -ne 0 ]; then if [ $SCAN_FAILED -ne 0 ]; then
echo -e "${RED}Unused code/dependencies detected. Push aborted.${NC}" echo -e "${RED}Unused code/dependencies detected. Push aborted.${NC}"
echo -e "${YELLOW}To skip this check, use: git push --no-verify${NC}" echo -e "${YELLOW}To skip this check, use: git push --no-verify${NC}"
exit 1 exit 1
fi fi
echo -e "${GREEN}✅ Pre-push checks passed${NC}" # Run Trivy security scan
echo ""
echo -e "${YELLOW}[3/3] Running security scan (Trivy image scan)...${NC}"
if "$REPO_ROOT/scripts/scan-trivy-image.sh"; then
echo -e "${GREEN}Security scan passed${NC}"
else
echo -e "${RED}Security scan found vulnerabilities. Push aborted.${NC}"
echo -e "${YELLOW}To skip this check, use: git push --no-verify${NC}"
exit 1
fi
echo ""
echo -e "${GREEN}Pre-push checks passed${NC}"
exit 0 exit 0
HOOK_EOF HOOK_EOF
chmod +x "$PRE_PUSH_HOOK" chmod +x "$PRE_PUSH_HOOK"
echo -e "${GREEN}✅ Pre-push hook installed successfully${NC}" echo -e "${GREEN}Pre-push hook installed${NC}"
fi
echo "" echo ""
echo -e "${BLUE}Hooks installed:${NC}" echo -e "${BLUE}Hooks installed:${NC}"
echo -e "${BLUE} - pre-commit: runs core tests before each commit${NC}" echo -e "${BLUE} - pre-commit: core tests + Trivy security scan${NC}"
echo -e "${BLUE} - pre-push: scans for unused code/deps before each push${NC}" echo -e "${BLUE} - pre-push: knip + depcheck + Trivy security scan${NC}"
echo -e "${YELLOW}To skip hooks, use: git commit --no-verify / git push --no-verify${NC}" echo -e "${YELLOW}To skip hooks, use: git commit --no-verify / git push --no-verify${NC}"
echo "" echo ""
# Check if dependencies are installed # Check if dependencies are installed
if [ ! -d "$REPO_ROOT/node_modules" ]; then if [ ! -d "$REPO_ROOT/node_modules" ]; then
echo -e "${YELLOW}⚠️ Dependencies not found. Installing...${NC}" echo -e "${YELLOW}Dependencies not found. Installing...${NC}"
cd "$REPO_ROOT" cd "$REPO_ROOT"
npm ci npm ci
echo -e "${GREEN}Dependencies installed${NC}" echo -e "${GREEN}Dependencies installed${NC}"
fi fi
echo -e "${GREEN}🎉 Git hooks installation complete!${NC}" echo -e "${GREEN}Git hooks installation complete!${NC}"

View File

@ -1,33 +1,65 @@
#!/bin/bash #!/bin/bash
# Run Trivy image security scan # Run Trivy image security scan
# Builds the Docker image and scans it for vulnerabilities # Builds the Docker image locally and scans it for vulnerabilities
#
# Usage:
# ./scan-trivy-image.sh [image-name]
#
# Exit codes:
# 0 - No HIGH/CRITICAL vulnerabilities found
# 1 - Vulnerabilities found or error occurred
set -e set -e
IMAGE_NAME="${1:-hastebin:test}" IMAGE_NAME="${1:-hastebin:local-scan}"
echo "🐳 Building Docker image: $IMAGE_NAME" # Colors for output
docker build -t "$IMAGE_NAME" --no-cache . RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color
echo "" # Cleanup function to remove temporary image
echo "🔒 Running Trivy image security scan..." cleanup() {
if docker image inspect "$IMAGE_NAME" &> /dev/null; then
echo -e "${YELLOW}Cleaning up temporary image: $IMAGE_NAME${NC}"
docker rmi "$IMAGE_NAME" --force &> /dev/null || true
fi
}
# Set trap to cleanup on exit (success or failure)
trap cleanup EXIT
# Check dependencies BEFORE building
if ! command -v docker &> /dev/null; then
echo -e "${RED}Docker not found. Please install Docker.${NC}"
exit 1
fi
# Check if trivy is installed
if ! command -v trivy &> /dev/null; then if ! command -v trivy &> /dev/null; then
echo "Trivy not found. Please install it:" echo -e "${RED}Trivy not found. Please install it:${NC}"
echo " brew install trivy" echo " brew install trivy"
echo " or visit: https://aquasecurity.github.io/trivy/latest/getting-started/installation/" echo " or visit: https://aquasecurity.github.io/trivy/latest/getting-started/installation/"
exit 1 exit 1
fi fi
# Build the Docker image (always clean build, no cache)
echo -e "${YELLOW}Building Docker image: $IMAGE_NAME${NC}"
docker build -t "$IMAGE_NAME" --no-cache .
echo ""
echo -e "${YELLOW}Running Trivy image security scan...${NC}"
# Show version # Show version
trivy --version trivy --version
echo "" echo ""
echo "📦 Scanning Docker image for vulnerabilities..." echo -e "${YELLOW}Scanning Docker image for vulnerabilities...${NC}"
echo "" echo ""
# Scan image with exit code 1 (fail on HIGH/CRITICAL vulnerabilities) # Scan image with exit code 1 (fail on HIGH/CRITICAL vulnerabilities)
# Store result to allow cleanup even on failure
SCAN_RESULT=0
trivy image \ trivy image \
--timeout 10m \ --timeout 10m \
--scanners vuln \ --scanners vuln \
@ -35,7 +67,13 @@ trivy image \
--ignore-unfixed \ --ignore-unfixed \
--exit-code 1 \ --exit-code 1 \
--format table \ --format table \
"$IMAGE_NAME" "$IMAGE_NAME" || SCAN_RESULT=$?
echo "" echo ""
echo "✅ Trivy image scan completed!"
if [ $SCAN_RESULT -eq 0 ]; then
echo -e "${GREEN}Trivy image scan completed - no HIGH/CRITICAL vulnerabilities found!${NC}"
else
echo -e "${RED}Trivy image scan found HIGH/CRITICAL vulnerabilities!${NC}"
exit $SCAN_RESULT
fi