Skip to content

Commit c4df3ff

Browse files
authored
Merge pull request #56 from IntentProof/add-codeql-gitleaks-gates
Add CodeQL and secret scanning CI gates
2 parents 843b6dc + f4b2fbe commit c4df3ff

5 files changed

Lines changed: 185 additions & 0 deletions

File tree

.github/codeql-allowlist.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# CodeQL finding allowlist with expiry model.
2+
# New entries require security on-call approval.
3+
# Expired entries fail CI via scripts/check-codeql-allowlist.sh.
4+
allowlist: []

.github/workflows/codeql.yml

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
name: codeql
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
schedule:
9+
- cron: "0 6 * * 1"
10+
11+
permissions:
12+
actions: read
13+
contents: read
14+
security-events: write
15+
16+
jobs:
17+
allowlist-expiry:
18+
name: "IntentProof Security: CodeQL Allowlist"
19+
runs-on: ubuntu-latest
20+
steps:
21+
- uses: actions/checkout@v4
22+
23+
- name: Validate allowlist expiry dates
24+
run: bash ./scripts/check-codeql-allowlist.sh
25+
26+
gitleaks:
27+
name: "IntentProof Security: Secret Scan"
28+
runs-on: ubuntu-latest
29+
steps:
30+
- uses: actions/checkout@v4
31+
with:
32+
fetch-depth: 0
33+
34+
- name: Install gitleaks
35+
run: |
36+
curl -sSfL \
37+
"https://github.com/gitleaks/gitleaks/releases/download/v8.24.2/gitleaks_8.24.2_linux_x64.tar.gz" \
38+
| tar -xz
39+
sudo install -m 755 gitleaks /usr/local/bin/gitleaks
40+
41+
- name: Run gitleaks
42+
run: gitleaks detect --source . --config .gitleaks.toml --verbose --redact
43+
44+
analyze:
45+
name: "IntentProof Security: CodeQL (${{ matrix.language }})"
46+
needs: allowlist-expiry
47+
runs-on: ubuntu-latest
48+
strategy:
49+
fail-fast: false
50+
matrix:
51+
language: [python]
52+
steps:
53+
- uses: actions/checkout@v4
54+
55+
- uses: actions/setup-python@v5
56+
with:
57+
python-version: "3.11"
58+
59+
- name: Install package for CodeQL
60+
run: pip install -e ".[dev]"
61+
62+
- name: Initialize CodeQL
63+
uses: github/codeql-action/init@v3
64+
with:
65+
languages: ${{ matrix.language }}
66+
queries: security-and-quality
67+
68+
- name: Perform CodeQL Analysis
69+
uses: github/codeql-action/analyze@v3
70+
with:
71+
category: "/language:${{ matrix.language }}"

.gitleaks.toml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# IntentProof gitleaks configuration.
2+
# See https://github.com/gitleaks/gitleaks for rule documentation.
3+
4+
title = "IntentProof gitleaks config"
5+
6+
[extend]
7+
useDefault = true
8+
9+
[allowlist]
10+
description = "Global allowlist paths for false positives"
11+
regexTarget = "match"
12+
paths = [
13+
'''\.git/''',
14+
'''\.coverage''',
15+
'''tests/fixtures/''',
16+
]
17+
# Example test vectors and documentation placeholders only.
18+
regexes = [
19+
'''EXAMPLE|PLACEHOLDER|REDACTED|xxxxxxxx''',
20+
'''_instance_private_key:\s*Ed25519PrivateKey''',
21+
]

.pre-commit-config.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
repos:
2+
- repo: https://github.com/gitleaks/gitleaks
3+
rev: v8.24.2
4+
hooks:
5+
- id: gitleaks

scripts/check-codeql-allowlist.sh

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#!/usr/bin/env bash
2+
# Validate .github/codeql-allowlist.yml expiry dates.
3+
# Expired entries fail with a clear message for security on-call follow-up.
4+
set -euo pipefail
5+
6+
ALLOWLIST_FILE="${1:-.github/codeql-allowlist.yml}"
7+
8+
if [[ ! -f "$ALLOWLIST_FILE" ]]; then
9+
echo "No allowlist file at $ALLOWLIST_FILE; skipping expiry check."
10+
exit 0
11+
fi
12+
13+
if ! command -v python3 >/dev/null 2>&1; then
14+
echo "python3 is required to validate $ALLOWLIST_FILE" >&2
15+
exit 1
16+
fi
17+
18+
python3 - "$ALLOWLIST_FILE" <<'PY'
19+
import datetime
20+
import re
21+
import sys
22+
23+
path = sys.argv[1]
24+
text = open(path, encoding="utf-8").read()
25+
26+
# Minimal parser for our allowlist schema (no PyYAML dependency).
27+
entries = []
28+
current = None
29+
for line in text.splitlines():
30+
stripped = line.strip()
31+
if stripped.startswith("- "):
32+
if current:
33+
entries.append(current)
34+
current = {}
35+
item = stripped[2:].strip()
36+
m = re.search(r"rule_id:\s*(\S+)", item)
37+
if m:
38+
current["rule_id"] = m.group(1)
39+
m = re.search(r"expires:\s*(\S+)", item)
40+
if m:
41+
current["expires"] = m.group(1)
42+
continue
43+
if current is None:
44+
continue
45+
m = re.match(r"expires:\s*(\S+)", stripped)
46+
if m:
47+
current["expires"] = m.group(1)
48+
m = re.match(r"rule_id:\s*(\S+)", stripped)
49+
if m:
50+
current["rule_id"] = m.group(1)
51+
if current:
52+
entries.append(current)
53+
54+
today = datetime.date.today()
55+
expired = []
56+
for idx, entry in enumerate(entries):
57+
expires_raw = entry.get("expires")
58+
if not expires_raw:
59+
print(
60+
f"{path}: allowlist[{idx}] missing expires date "
61+
"(required for security on-call approval model)",
62+
file=sys.stderr,
63+
)
64+
sys.exit(1)
65+
try:
66+
expires = datetime.date.fromisoformat(str(expires_raw))
67+
except ValueError:
68+
print(
69+
f"{path}: allowlist[{idx}] has invalid expires date: {expires_raw!r}",
70+
file=sys.stderr,
71+
)
72+
sys.exit(1)
73+
if expires < today:
74+
rule_id = entry.get("rule_id", "<unknown>")
75+
expired.append(f"{rule_id} (expired {expires.isoformat()})")
76+
77+
if expired:
78+
print("Allowlist expired; contact security on-call to extend or remove:", file=sys.stderr)
79+
for item in expired:
80+
print(f" - {item}", file=sys.stderr)
81+
sys.exit(1)
82+
83+
print(f"PASS: {len(entries)} allowlist entries are current.")
84+
PY

0 commit comments

Comments
 (0)