Skip to content

Commit 216fca1

Browse files
author
Jonathan D.A. Jewell
committed
chore: add security validation pre-commit hooks
Adds validation hooks for: - SHA-pinned GitHub Actions - SPDX license headers - Workflow permissions declarations - CodeQL language matrix validation Configure with: git config core.hooksPath hooks
1 parent 11a0e6d commit 216fca1

File tree

4 files changed

+106
-0
lines changed

4 files changed

+106
-0
lines changed

hooks/validate-codeql.sh

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/usr/bin/env bash
2+
# SPDX-License-Identifier: AGPL-3.0-or-later
3+
# Pre-commit hook: Validate CodeQL language matrix matches repo
4+
set -euo pipefail
5+
6+
CODEQL_FILE=".github/workflows/codeql.yml"
7+
[ -f "$CODEQL_FILE" ] || exit 0
8+
9+
# Detect languages in repo
10+
HAS_JS=$(find . -name "*.js" -o -name "*.ts" -o -name "*.jsx" -o -name "*.tsx" 2>/dev/null | grep -v node_modules | head -1)
11+
HAS_PY=$(find . -name "*.py" 2>/dev/null | grep -v __pycache__ | head -1)
12+
HAS_GO=$(find . -name "*.go" 2>/dev/null | head -1)
13+
HAS_RS=$(find . -name "*.rs" 2>/dev/null | head -1)
14+
15+
# Check if matrix includes unsupported languages
16+
if grep -q "language:.*python" "$CODEQL_FILE" && [ -z "$HAS_PY" ]; then
17+
echo "WARNING: CodeQL configured for Python but no .py files found"
18+
fi
19+
if grep -q "language:.*go" "$CODEQL_FILE" && [ -z "$HAS_GO" ]; then
20+
echo "WARNING: CodeQL configured for Go but no .go files found"
21+
fi
22+
if grep -q "language:.*javascript" "$CODEQL_FILE" && [ -z "$HAS_JS" ]; then
23+
echo "WARNING: CodeQL configured for JavaScript but no JS/TS files found"
24+
fi
25+
26+
# Rust/OCaml are not supported - should use 'actions' only
27+
if [ -n "$HAS_RS" ]; then
28+
if grep -q "language:.*rust" "$CODEQL_FILE"; then
29+
echo "ERROR: CodeQL does not support Rust - use ['actions'] instead"
30+
exit 1
31+
fi
32+
fi
33+
34+
exit 0

hooks/validate-permissions.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env bash
2+
# SPDX-License-Identifier: AGPL-3.0-or-later
3+
# Pre-commit hook: Validate workflow permissions declarations
4+
set -euo pipefail
5+
ERRORS=0
6+
for workflow in .github/workflows/*.yml .github/workflows/*.yaml; do
7+
[ -f "$workflow" ] || continue
8+
if ! grep -qE '^permissions:' "$workflow"; then
9+
echo "ERROR: Missing top-level permissions in $workflow"
10+
ERRORS=$((ERRORS + 1))
11+
fi
12+
done
13+
[ $ERRORS -gt 0 ] && exit 1
14+
exit 0

hooks/validate-sha-pins.sh

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/env bash
2+
# SPDX-License-Identifier: AGPL-3.0-or-later
3+
# Pre-commit hook: Validate GitHub Actions are SHA-pinned
4+
5+
set -euo pipefail
6+
7+
ERRORS=0
8+
9+
for workflow in .github/workflows/*.yml .github/workflows/*.yaml; do
10+
[ -f "$workflow" ] || continue
11+
12+
# Find uses: lines that aren't SHA-pinned
13+
while IFS= read -r line; do
14+
if [[ "$line" =~ uses:.*@ ]]; then
15+
# Check if it has a SHA (40 hex chars)
16+
if ! echo "$line" | grep -qE '@[a-f0-9]{40}'; then
17+
echo "ERROR: Unpinned action in $workflow"
18+
echo " $line"
19+
echo " Actions must use SHA pins: uses: action/name@SHA # version"
20+
ERRORS=$((ERRORS + 1))
21+
fi
22+
fi
23+
done < "$workflow"
24+
done
25+
26+
if [ $ERRORS -gt 0 ]; then
27+
echo ""
28+
echo "Found $ERRORS unpinned actions. Please SHA-pin all GitHub Actions."
29+
echo "Use: gh api repos/OWNER/REPO/git/matching-refs/tags/VERSION to find SHAs"
30+
exit 1
31+
fi
32+
33+
exit 0

hooks/validate-spdx.sh

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#!/usr/bin/env bash
2+
# SPDX-License-Identifier: AGPL-3.0-or-later
3+
# Pre-commit hook: Validate SPDX headers in workflow files
4+
5+
set -euo pipefail
6+
7+
ERRORS=0
8+
SPDX_PATTERN="^# SPDX-License-Identifier:"
9+
10+
for workflow in .github/workflows/*.yml .github/workflows/*.yaml; do
11+
[ -f "$workflow" ] || continue
12+
13+
first_line=$(head -n1 "$workflow")
14+
if ! echo "$first_line" | grep -qE "$SPDX_PATTERN"; then
15+
echo "ERROR: Missing SPDX header in $workflow"
16+
echo " First line should be: # SPDX-License-Identifier: AGPL-3.0-or-later"
17+
ERRORS=$((ERRORS + 1))
18+
fi
19+
done
20+
21+
if [ $ERRORS -gt 0 ]; then
22+
exit 1
23+
fi
24+
25+
exit 0

0 commit comments

Comments
 (0)