Skip to content

Bump Flask 3.1.1→3.1.3: fix GHSA-68rp-wp8r-4726 (Vary: Cookie) #89

Bump Flask 3.1.1→3.1.3: fix GHSA-68rp-wp8r-4726 (Vary: Cookie)

Bump Flask 3.1.1→3.1.3: fix GHSA-68rp-wp8r-4726 (Vary: Cookie) #89

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
# Minimal permissions by default
permissions:
contents: read
jobs:
go-build-and-test:
name: Go Build & Test
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
matrix:
service: [airlock, registry, tool-firewall, gpu-integrity-watch, mcp-firewall, policy-engine, runtime-attestor, integrity-monitor, incident-recorder]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version: "1.25"
cache-dependency-path: services/${{ matrix.service }}/go.sum
- name: Build
working-directory: services/${{ matrix.service }}
run: CGO_ENABLED=0 go build -ldflags="-s -w" -o /dev/null .
- name: Test
working-directory: services/${{ matrix.service }}
run: go test -v -race -count=1 ./...
- name: Vet
working-directory: services/${{ matrix.service }}
run: go vet ./...
python-test:
name: Python Test & Lint
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- name: Install dependencies (pinned)
run: pip install -r requirements-ci.txt
- name: Lint (syntax check)
run: |
python -m py_compile services/ui/ui/app.py
python -m py_compile services/diffusion-worker/app.py
python -m py_compile services/common/audit_chain.py
python -m py_compile services/common/auth.py
python -m py_compile services/common/mlock_helper.py
python -m py_compile services/agent/agent/app.py
python -m py_compile services/agent/agent/models.py
python -m py_compile services/agent/agent/policy.py
python -m py_compile services/agent/agent/planner.py
python -m py_compile services/agent/agent/executor.py
python -m py_compile services/agent/agent/storage.py
python -m py_compile services/agent/agent/capabilities.py
python -m py_compile services/agent/agent/sandbox.py
- name: Ruff lint
run: ruff check services/ tests/ --select E,F,W --ignore E501,E402
- name: Bandit security scan
run: |
# Fail on high-severity + high-confidence findings.
# Medium/low findings are reported as warnings.
bandit -r services/ -ll --skip B101,B404,B603 -f json -o /tmp/bandit.json || true
python3 -c "
import json, sys
with open('/tmp/bandit.json') as f:
data = json.load(f)
high = [r for r in data.get('results', [])
if r['issue_severity'] == 'HIGH' and r['issue_confidence'] == 'HIGH']
for r in data.get('results', []):
sev = r['issue_severity']
msg = f\"{r['filename']}:{r['line_number']}: [{sev}] {r['issue_text']}\"
if sev == 'HIGH':
print(f'::error ::{msg}')
else:
print(f'::warning ::{msg}')
if high:
print(f'FAIL: {len(high)} high-severity/high-confidence finding(s)')
sys.exit(1)
print('OK: no high-severity/high-confidence findings')
"
- name: Mypy type check (security-sensitive services)
run: |
mypy --ignore-missing-imports \
services/common/ \
services/agent/agent/ \
services/quarantine/quarantine/ \
services/ui/ui/
- name: Test (unit + integration)
env:
PYTHONPATH: services
run: python -m pytest tests/ -v --ignore=tests/test_adversarial.py --ignore=tests/test_m5_acceptance.py -x
- name: Test (adversarial + acceptance)
env:
PYTHONPATH: services
run: python -m pytest tests/test_adversarial.py tests/test_m5_acceptance.py -v --tb=short
shellcheck:
name: Shell Script Lint
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Lint shell scripts
run: |
shellcheck -s bash \
files/system/usr/libexec/secure-ai/*.sh \
files/scripts/build-services.sh \
files/scripts/generate-mok.sh \
files/scripts/first-boot-check.sh \
files/scripts/verify-release.sh
policy-validate:
name: Validate YAML configs
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- name: Install pyyaml
run: pip install pyyaml
- name: Validate YAML files
run: |
python -c "
import yaml, sys, glob
errors = 0
for pattern in ['files/system/etc/secure-ai/**/*.yaml', 'recipes/*.yml']:
for f in glob.glob(pattern, recursive=True):
try:
with open(f) as fh:
yaml.safe_load(fh)
print(f'OK: {f}')
except Exception as e:
print(f'FAIL: {f}: {e}')
errors += 1
sys.exit(errors)
"
check-pins:
name: Verify action pins
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- run: bash .github/scripts/check-action-pins.sh
supply-chain-verify:
name: Supply Chain & SBOM Verification
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version: "1.25"
- name: Install Syft (SBOM generator)
run: curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
- name: Install cosign (signing & attestation)
run: |
COSIGN_VERSION="v2.4.3"
curl -sSfL "https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/cosign-linux-amd64" \
-o /usr/local/bin/cosign
chmod +x /usr/local/bin/cosign
- name: Verify SBOM generation (Go services)
run: |
echo "=== SBOM generation verification ==="
for svc in airlock registry tool-firewall gpu-integrity-watch mcp-firewall \
policy-engine runtime-attestor integrity-monitor incident-recorder; do
echo "--- ${svc} ---"
syft dir:services/${svc} -o cyclonedx-json=/dev/null
echo "OK: ${svc} SBOM generated"
done
- name: Verify SBOM generation (Python services)
run: |
for svc in agent ui quarantine common diffusion-worker search-mediator; do
if [ -d "services/${svc}" ]; then
syft dir:services/${svc} -o cyclonedx-json=/dev/null
echo "OK: ${svc} SBOM generated"
fi
done
- name: Verify cosign is functional
run: |
cosign version
echo "OK: cosign available for signing and attestation"
- name: Verify release workflow has provenance steps
run: |
echo "=== Checking release.yml provenance pipeline ==="
# Verify release workflow exists and contains required supply-chain steps
test -f .github/workflows/release.yml || { echo "FAIL: release.yml missing"; exit 1; }
for keyword in "sbom-action" "attest-build-provenance" "cosign" "cyclonedx" "SHA256SUMS"; do
grep -q "${keyword}" .github/workflows/release.yml || \
{ echo "FAIL: release.yml missing '${keyword}'"; exit 1; }
echo "OK: release.yml contains '${keyword}'"
done
# Verify build workflow has SBOM attestation
test -f .github/workflows/build.yml || { echo "FAIL: build.yml missing"; exit 1; }
for keyword in "sbom-action" "cosign attest" "cyclonedx"; do
grep -q "${keyword}" .github/workflows/build.yml || \
{ echo "FAIL: build.yml missing '${keyword}'"; exit 1; }
echo "OK: build.yml contains '${keyword}'"
done
echo "=== Supply chain verification passed ==="
security-regression:
name: Security Regression Tests
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version: "1.25"
- name: Install Python dependencies
run: pip install -r requirements-ci.txt
- name: Run adversarial Python tests
run: python -m pytest tests/test_adversarial.py -v --tb=short
- name: Run MCP firewall adversarial tests
working-directory: services/mcp-firewall
run: go test -v -race -run TestAdversarial ./...
- name: Run policy-engine adversarial tests
working-directory: services/policy-engine
run: go test -v -race -run TestAdversarial ./...
- name: Run incident-recorder recovery tests
working-directory: services/incident-recorder
run: go test -v -race -run "TestRecovery|TestEscalation|TestForensic|TestLatched" ./...
test-count-check:
name: Test Count Drift Check
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version: "1.25"
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- name: Install Python dependencies
run: pip install -r requirements-ci.txt
- name: Check test counts for drift
run: bash .github/scripts/check-test-counts.sh
dependency-audit:
name: Dependency Vulnerability Audit
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version: "1.25"
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
- name: Install govulncheck
run: go install golang.org/x/vuln/cmd/govulncheck@latest
- name: Go vulnerability scan (enforced)
run: |
echo "=== Go Dependency Vulnerability Scan ==="
VULN_ERRORS=0
for svc in airlock registry tool-firewall gpu-integrity-watch mcp-firewall \
policy-engine runtime-attestor integrity-monitor incident-recorder; do
echo "--- ${svc} ---"
cd "services/${svc}"
if ! govulncheck ./... 2>&1; then
VULN_ERRORS=$((VULN_ERRORS + 1))
echo "::error::${svc}: govulncheck found vulnerabilities"
fi
cd ../..
done
# Check waivers
WAIVED=$(python3 -c "
import json, datetime
with open('.github/vuln-waivers.json') as f:
data = json.load(f)
today = datetime.date.today().isoformat()
active = [w for w in data.get('go', []) if w.get('expires', '') >= today]
print(len(active))
")
EFFECTIVE=$((VULN_ERRORS - WAIVED))
if [ "$EFFECTIVE" -gt 0 ]; then
echo "FAIL: $VULN_ERRORS service(s) have vulnerabilities ($WAIVED waived, $EFFECTIVE unwaived)"
echo "To waive a reviewed finding, add it to .github/vuln-waivers.json"
exit 1
fi
echo "OK: Go vulnerability scan passed ($WAIVED waiver(s) active)"
- name: Python dependency audit (enforced)
run: |
pip install -r requirements-ci.txt
echo "=== Python Dependency Audit ==="
# Run pip-audit, capture output
pip-audit --strict --desc -f json -o /tmp/pip-audit.json 2>/dev/null || true
python3 -c "
import json, sys, datetime
# Load audit results
try:
with open('/tmp/pip-audit.json') as f:
data = json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
print('OK: pip-audit produced no findings')
sys.exit(0)
vulns = data if isinstance(data, list) else data.get('dependencies', [])
findings = [d for d in vulns if d.get('vulns')]
if not findings:
print('OK: no Python dependency vulnerabilities')
sys.exit(0)
# Load waivers
with open('.github/vuln-waivers.json') as f:
waivers = json.load(f)
today = datetime.date.today().isoformat()
waived_ids = {w['id'] for w in waivers.get('python', []) if w.get('expires', '') >= today}
unwaived = 0
for dep in findings:
for v in dep.get('vulns', []):
vid = v.get('id', '')
if vid in waived_ids:
print(f'WAIVED: {dep[\"name\"]} {vid}')
else:
print(f'::error::{dep[\"name\"]}: {vid} — {v.get(\"description\", \"\")}')
unwaived += 1
if unwaived > 0:
print(f'FAIL: {unwaived} unwaived Python vulnerability finding(s)')
print('To waive a reviewed finding, add it to .github/vuln-waivers.json')
sys.exit(1)
print(f'OK: all Python findings waived ({len(waived_ids)} waiver(s) active)')
"
docs-validation:
name: Documentation Validation
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Check for broken internal links
run: |
echo "=== Checking internal doc links ==="
ERRORS=0
# Find all markdown links to local files
for md in $(find docs/ README.md CONTRIBUTING.md SECURITY.md -name '*.md' 2>/dev/null); do
# Extract relative links (not URLs, not anchors)
grep -oP '\[([^\]]*)\]\((?!https?://|#)([^)]+)\)' "$md" 2>/dev/null | \
grep -oP '\(([^)]+)\)' | tr -d '()' | while read -r link; do
# Strip anchor fragments
target="${link%%#*}"
[ -z "$target" ] && continue
# Resolve relative to file's directory
dir=$(dirname "$md")
resolved="${dir}/${target}"
if [ ! -f "$resolved" ] && [ ! -d "$resolved" ]; then
echo "BROKEN: ${md} -> ${link} (resolved: ${resolved})"
ERRORS=$((ERRORS + 1))
fi
done
done
if [ "$ERRORS" -gt 0 ]; then
echo "FAIL: ${ERRORS} broken internal links found"
exit 1
fi
echo "OK: All internal doc links valid"
- name: Verify required docs exist
run: |
echo "=== Checking required documentation ==="
REQUIRED_DOCS=(
"docs/threat-model.md"
"docs/architecture.md"
"docs/api.md"
"docs/security-status.md"
"docs/production-operations.md"
"docs/production-readiness-checklist.md"
"docs/slos.md"
"docs/release-policy.md"
"docs/support-lifecycle.md"
"docs/test-counts.json"
"docs/install/bare-metal.md"
"SECURITY.md"
"CONTRIBUTING.md"
"LICENSE"
)
ERRORS=0
for doc in "${REQUIRED_DOCS[@]}"; do
if [ -f "$doc" ]; then
echo "OK: $doc"
else
echo "MISSING: $doc"
ERRORS=$((ERRORS + 1))
fi
done
if [ "$ERRORS" -gt 0 ]; then
echo "FAIL: ${ERRORS} required document(s) missing"
exit 1
fi
echo "All required documents present"
- name: Validate test-counts.json format
run: |
python3 -c "
import json, sys
with open('docs/test-counts.json') as f:
data = json.load(f)
required = ['generated', 'go', 'go_total', 'python_total', 'grand_total']
for key in required:
if key not in data:
print(f'FAIL: test-counts.json missing key: {key}')
sys.exit(1)
if not isinstance(data['go'], dict):
print('FAIL: go field must be a dict of service -> count')
sys.exit(1)
print(f'OK: test-counts.json valid (total: {data[\"grand_total\"]} tests)')
"