From 1c5050a0d66244612767eb719182967d874f244c Mon Sep 17 00:00:00 2001 From: Nathan Gillett Date: Thu, 21 May 2026 08:52:45 -0500 Subject: [PATCH 1/5] Add CodeQL and secret scanning CI gates Wire gitleaks, allowlist expiry checks, and CodeQL analysis. Signed-off-by: Nathan Gillett --- .github/codeql-allowlist.yml | 4 ++ .github/workflows/codeql.yml | 51 ++++++++++++++++++++ .gitleaks.toml | 28 +++++++++++ .pre-commit-config.yaml | 5 ++ scripts/check-codeql-allowlist.sh | 80 +++++++++++++++++++++++++++++++ 5 files changed, 168 insertions(+) create mode 100644 .github/codeql-allowlist.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .gitleaks.toml create mode 100644 .pre-commit-config.yaml create mode 100755 scripts/check-codeql-allowlist.sh diff --git a/.github/codeql-allowlist.yml b/.github/codeql-allowlist.yml new file mode 100644 index 0000000..2d26ba8 --- /dev/null +++ b/.github/codeql-allowlist.yml @@ -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: [] diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..4257547 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,51 @@ +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 + - 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 + - uses: gitleaks/gitleaks-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITLEAKS_CONFIG: .gitleaks.toml + 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.12" + - run: pip install -e ".[dev]" + - uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + queries: security-and-quality + - uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{ matrix.language }}" diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 0000000..c251e0f --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,28 @@ +# 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" +paths = [ + '''\.git/''', + '''coverage\.out''', + '''\.coverage''', + '''package-lock\.json''', + '''go\.sum''', + '''codeql-pack\.lock\.yml''', + '''keys/intentproof-security\.asc''', + '''keys/intentproof-security\.fingerprint\.json''', + '''golden/''', + '''fixtures/''', +] + +[allowlist.regexTarget] +# Example test vectors and documentation placeholders only. +regexes = [ + '''EXAMPLE|PLACEHOLDER|REDACTED|xxxxxxxx''', +] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..b8a004b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,5 @@ +repos: + - repo: https://github.com/gitleaks/gitleaks + rev: v8.24.2 + hooks: + - id: gitleaks diff --git a/scripts/check-codeql-allowlist.sh b/scripts/check-codeql-allowlist.sh new file mode 100755 index 0000000..75b68cc --- /dev/null +++ b/scripts/check-codeql-allowlist.sh @@ -0,0 +1,80 @@ +#!/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("- ") and "rule_id:" in stripped: + if current: + entries.append(current) + current = {} + m = re.search(r"rule_id:\s*(\S+)", stripped) + if m: + current["rule_id"] = m.group(1) + continue + 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", "") + 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 From f82149b8fc809918cb9dda02bd94bea6b7db8b18 Mon Sep 17 00:00:00 2001 From: Nathan Gillett Date: Thu, 21 May 2026 09:00:04 -0500 Subject: [PATCH 2/5] fix: use gitleaks CLI and valid allowlist config Replace gitleaks-action (org license required) with pinned v8.24.2 CLI matching pre-commit. Fix allowlist.regexTarget TOML and allowlist Ed25519 type-name false positives. Signed-off-by: Nathan Gillett --- .github/workflows/codeql.yml | 12 ++++++++---- .gitleaks.toml | 5 +++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 4257547..c939c69 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -24,10 +24,14 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: gitleaks/gitleaks-action@v2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITLEAKS_CONFIG: .gitleaks.toml + - 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 mv gitleaks /usr/local/bin/ + - name: Run gitleaks + run: gitleaks detect --source . --config .gitleaks.toml --verbose --redact analyze: name: "IntentProof Security: CodeQL (${{ matrix.language }})" needs: allowlist-expiry diff --git a/.gitleaks.toml b/.gitleaks.toml index c251e0f..c3154fa 100644 --- a/.gitleaks.toml +++ b/.gitleaks.toml @@ -20,9 +20,10 @@ paths = [ '''golden/''', '''fixtures/''', ] - -[allowlist.regexTarget] # Example test vectors and documentation placeholders only. +regexTarget = "line" regexes = [ '''EXAMPLE|PLACEHOLDER|REDACTED|xxxxxxxx''', + '''Ed25519(?:Private|Public)Key''', + '''_instance_private_key''', ] From 533df0423fbc0778cf24899aeb7541b34918c58d Mon Sep 17 00:00:00 2001 From: Nathan Gillett Date: Thu, 21 May 2026 09:09:28 -0500 Subject: [PATCH 3/5] Align security gates with intentproof-tools template Use gitleaks-action, local allowlist script, and tools ref 552da9d for javascript custom queries where applicable. Signed-off-by: Nathan Gillett --- .github/workflows/codeql.yml | 36 ++++++++++++++++++++++++------------ .gitleaks.toml | 15 +++------------ 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c939c69..ad49278 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,4 +1,5 @@ name: codeql + on: push: branches: [main] @@ -6,17 +7,22 @@ on: 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 - - run: bash ./scripts/check-codeql-allowlist.sh + + - name: Validate allowlist expiry dates + run: bash ./scripts/check-codeql-allowlist.sh + gitleaks: name: "IntentProof Security: Secret Scan" runs-on: ubuntu-latest @@ -24,14 +30,13 @@ jobs: - 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 mv gitleaks /usr/local/bin/ + - name: Run gitleaks - run: gitleaks detect --source . --config .gitleaks.toml --verbose --redact + uses: gitleaks/gitleaks-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITLEAKS_CONFIG: .gitleaks.toml + analyze: name: "IntentProof Security: CodeQL (${{ matrix.language }})" needs: allowlist-expiry @@ -42,14 +47,21 @@ jobs: language: [python] steps: - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: - python-version: "3.12" - - run: pip install -e ".[dev]" - - uses: github/codeql-action/init@v3 + 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 - - uses: github/codeql-action/analyze@v3 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 with: category: "/language:${{ matrix.language }}" diff --git a/.gitleaks.toml b/.gitleaks.toml index c3154fa..a1d7e57 100644 --- a/.gitleaks.toml +++ b/.gitleaks.toml @@ -10,20 +10,11 @@ useDefault = true description = "Global allowlist paths for false positives" paths = [ '''\.git/''', - '''coverage\.out''', '''\.coverage''', - '''package-lock\.json''', - '''go\.sum''', - '''codeql-pack\.lock\.yml''', - '''keys/intentproof-security\.asc''', - '''keys/intentproof-security\.fingerprint\.json''', - '''golden/''', - '''fixtures/''', + '''tests/fixtures/''', ] -# Example test vectors and documentation placeholders only. -regexTarget = "line" + +[allowlist.regexTarget] regexes = [ '''EXAMPLE|PLACEHOLDER|REDACTED|xxxxxxxx''', - '''Ed25519(?:Private|Public)Key''', - '''_instance_private_key''', ] From 609bce705ce0bf8a266c403fb7b368ff7bc2832c Mon Sep 17 00:00:00 2001 From: Nathan Gillett Date: Thu, 21 May 2026 18:52:36 -0500 Subject: [PATCH 4/5] fix: align Wave 3 security gates with tools template Use pinned gitleaks CLI, flatten allowlist TOML, and harden the allowlist expiry parser. Point JS CodeQL at the split pack. Signed-off-by: Nathan Gillett --- .github/workflows/codeql.yml | 12 ++++++++---- .gitleaks.toml | 4 ++-- scripts/check-codeql-allowlist.sh | 8 ++++++-- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index ad49278..06a4e1a 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -31,11 +31,15 @@ jobs: 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 - uses: gitleaks/gitleaks-action@v2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITLEAKS_CONFIG: .gitleaks.toml + run: gitleaks detect --source . --config .gitleaks.toml --verbose --redact analyze: name: "IntentProof Security: CodeQL (${{ matrix.language }})" diff --git a/.gitleaks.toml b/.gitleaks.toml index a1d7e57..e3208ee 100644 --- a/.gitleaks.toml +++ b/.gitleaks.toml @@ -8,13 +8,13 @@ useDefault = true [allowlist] description = "Global allowlist paths for false positives" +regexTarget = "match" paths = [ '''\.git/''', '''\.coverage''', '''tests/fixtures/''', ] - -[allowlist.regexTarget] +# Example test vectors and documentation placeholders only. regexes = [ '''EXAMPLE|PLACEHOLDER|REDACTED|xxxxxxxx''', ] diff --git a/scripts/check-codeql-allowlist.sh b/scripts/check-codeql-allowlist.sh index 75b68cc..4c9072a 100755 --- a/scripts/check-codeql-allowlist.sh +++ b/scripts/check-codeql-allowlist.sh @@ -28,13 +28,17 @@ entries = [] current = None for line in text.splitlines(): stripped = line.strip() - if stripped.startswith("- ") and "rule_id:" in stripped: + if stripped.startswith("- "): if current: entries.append(current) current = {} - m = re.search(r"rule_id:\s*(\S+)", stripped) + 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 if current is None: continue From f4b2fbe09d578b1e8de3ad0316dce5631f44c69d Mon Sep 17 00:00:00 2001 From: Nathan Gillett Date: Thu, 21 May 2026 18:53:57 -0500 Subject: [PATCH 5/5] fix: allowlist SDK module-state false positive in gitleaks Skip the _instance_private_key type annotation pattern that triggers generic-api-key on client.py. Signed-off-by: Nathan Gillett --- .gitleaks.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitleaks.toml b/.gitleaks.toml index e3208ee..a675bf7 100644 --- a/.gitleaks.toml +++ b/.gitleaks.toml @@ -17,4 +17,5 @@ paths = [ # Example test vectors and documentation placeholders only. regexes = [ '''EXAMPLE|PLACEHOLDER|REDACTED|xxxxxxxx''', + '''_instance_private_key:\s*Ed25519PrivateKey''', ]