Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/codeql-allowlist.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# CodeQL finding allowlist with expiry model.
# New entries require security on-call approval.
# Expired entries fail CI via scripts/check-codeql-allowlist.sh.
allowlist: []
71 changes: 71 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: codeql

on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: "0 6 * * 1"

permissions:
actions: read
contents: read
security-events: write

jobs:
allowlist-expiry:
name: "IntentProof Security: CodeQL Allowlist"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Validate allowlist expiry dates
run: bash ./scripts/check-codeql-allowlist.sh

gitleaks:
name: "IntentProof Security: Secret Scan"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Install gitleaks
run: |
curl -sSfL \
"https://github.com/gitleaks/gitleaks/releases/download/v8.24.2/gitleaks_8.24.2_linux_x64.tar.gz" \
| tar -xz
sudo install -m 755 gitleaks /usr/local/bin/gitleaks

- name: Run gitleaks
run: gitleaks detect --source . --config .gitleaks.toml --verbose --redact

analyze:
name: "IntentProof Security: CodeQL (${{ matrix.language }})"
needs: allowlist-expiry
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [python]
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install package for CodeQL
run: pip install -e ".[dev]"

- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
queries: security-and-quality

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{ matrix.language }}"
21 changes: 21 additions & 0 deletions .gitleaks.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# IntentProof gitleaks configuration.
# See https://github.com/gitleaks/gitleaks for rule documentation.

title = "IntentProof gitleaks config"

[extend]
useDefault = true

[allowlist]
description = "Global allowlist paths for false positives"
regexTarget = "match"
paths = [
'''\.git/''',
'''\.coverage''',
'''tests/fixtures/''',
]
# Example test vectors and documentation placeholders only.
regexes = [
'''EXAMPLE|PLACEHOLDER|REDACTED|xxxxxxxx''',
'''_instance_private_key:\s*Ed25519PrivateKey''',
]
Comment thread
cursor[bot] marked this conversation as resolved.
5 changes: 5 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.24.2
hooks:
- id: gitleaks
84 changes: 84 additions & 0 deletions scripts/check-codeql-allowlist.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env bash
# Validate .github/codeql-allowlist.yml expiry dates.
# Expired entries fail with a clear message for security on-call follow-up.
set -euo pipefail

ALLOWLIST_FILE="${1:-.github/codeql-allowlist.yml}"

if [[ ! -f "$ALLOWLIST_FILE" ]]; then
echo "No allowlist file at $ALLOWLIST_FILE; skipping expiry check."
exit 0
fi

if ! command -v python3 >/dev/null 2>&1; then
echo "python3 is required to validate $ALLOWLIST_FILE" >&2
exit 1
fi

python3 - "$ALLOWLIST_FILE" <<'PY'
import datetime
import re
import sys

path = sys.argv[1]
text = open(path, encoding="utf-8").read()

# Minimal parser for our allowlist schema (no PyYAML dependency).
entries = []
current = None
for line in text.splitlines():
stripped = line.strip()
if stripped.startswith("- "):
if current:
entries.append(current)
current = {}
item = stripped[2:].strip()
m = re.search(r"rule_id:\s*(\S+)", item)
if m:
current["rule_id"] = m.group(1)
m = re.search(r"expires:\s*(\S+)", item)
if m:
current["expires"] = m.group(1)
continue
Comment thread
cursor[bot] marked this conversation as resolved.
if current is None:
continue
m = re.match(r"expires:\s*(\S+)", stripped)
if m:
current["expires"] = m.group(1)
m = re.match(r"rule_id:\s*(\S+)", stripped)
if m:
current["rule_id"] = m.group(1)
if current:
entries.append(current)

today = datetime.date.today()
expired = []
for idx, entry in enumerate(entries):
expires_raw = entry.get("expires")
if not expires_raw:
print(
f"{path}: allowlist[{idx}] missing expires date "
"(required for security on-call approval model)",
file=sys.stderr,
)
sys.exit(1)
try:
expires = datetime.date.fromisoformat(str(expires_raw))
except ValueError:
print(
f"{path}: allowlist[{idx}] has invalid expires date: {expires_raw!r}",
file=sys.stderr,
)
sys.exit(1)
if expires < today:
rule_id = entry.get("rule_id", "<unknown>")
expired.append(f"{rule_id} (expired {expires.isoformat()})")

if expired:
print("Allowlist expired; contact security on-call to extend or remove:", file=sys.stderr)
for item in expired:
print(f" - {item}", file=sys.stderr)
sys.exit(1)

print(f"PASS: {len(entries)} allowlist entries are current.")
PY
Loading