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
8 changes: 3 additions & 5 deletions .github/renovate.json5
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@
// Renovate config for nwarila-platform/github-terraform-framework.
//
// Per nwarila-platform/.github ADR-0004 (Use Renovate for Dependency
// Updates with Per-Template Baselines), this consumer extends only its
// type-template — NWarila/terraform-runner-template — which carries the
// complete Renovate baseline for every Terraform-runner consumer in the
// portfolio. Stack-wide settings live there and propagate transparently.
// Updates with Per-Template Baselines), this framework repo extends only
// its type-template: NWarila/terraform-framework-template.
//
// Anything below the `extends` array is a repo-specific override. Keep
// it minimal: stack-wide concerns belong in the type-template, not here.

"extends": ["github>NWarila/terraform-runner-template"]
"extends": ["github>NWarila/terraform-framework-template"]
}
25 changes: 0 additions & 25 deletions .github/workflows/codeql.yaml

This file was deleted.

73 changes: 73 additions & 0 deletions .github/workflows/reusable-codeql.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: Reusable CodeQL

# CodeQL static analysis. Default language matrix is `actions` (analyzes
# GitHub Actions workflows for injection / supply-chain bugs). Consumers
# with additional languages (e.g. python tools, javascript) can extend the
# matrix via the `languages` input.
#
# Caller pattern:
# uses: <owner>/<template-repo>/.github/workflows/reusable-codeql.yaml@<sha>
# permissions:
# contents: read
# security-events: write
# actions: read

on:
workflow_call:
inputs:
languages:
description: |
JSON array of CodeQL languages to analyze. Defaults to ["actions"]
for Terraform repos (analyzes only the workflow files, not the
Terraform code which CodeQL doesn't support natively).
Consumers with python tools can pass '["actions","python"]'.
required: false
type: string
default: '["actions"]'
timeout_minutes:
description: Job timeout. Larger codebases may need more.
required: false
type: number
default: 30

permissions: {}

jobs:
analyze:
name: CodeQL (${{ matrix.language }})
runs-on: ubuntu-latest
timeout-minutes: ${{ inputs.timeout_minutes }}
permissions:
contents: read
security-events: write
actions: read

strategy:
fail-fast: false
matrix:
language: ${{ fromJSON(inputs.languages) }}

steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Initialize CodeQL
uses: github/codeql-action/init@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3
with:
languages: ${{ matrix.language }}

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3
with:
upload: never
output: codeql-results

- name: Upload CodeQL SARIF to GitHub Security
uses: github/codeql-action/upload-sarif@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3
# Best-effort publishing: GitHub Security SARIF upload can be
# unavailable on private repos, but the analysis above remains the gate.
continue-on-error: true
with:
sarif_file: codeql-results
197 changes: 197 additions & 0 deletions .github/workflows/reusable-iac-security.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
name: Reusable IaC Security

# Universal IaC security scanning consumed by Terraform template
# repositories. Three independent jobs run in parallel:
#
# - Trivy: filesystem misconfig + secret scanning, fail on HIGH/CRITICAL
# - Gitleaks: full-history secret detection
# - zizmor: GitHub Actions security analysis (injection via ${{ }},
# untrusted input as code, dangerous triggers, etc.)
#
# All three upload SARIF where applicable so findings appear in the
# consumer's GitHub Security tab.
#
# Caller pattern:
# uses: <owner>/<template-repo>/.github/workflows/reusable-iac-security.yaml@<sha>

on:
workflow_call:
inputs:
trivy_severity:
description: Comma-separated Trivy severity levels that fail the build.
required: false
type: string
default: "HIGH,CRITICAL"
enable_zizmor:
description: Run zizmor (GitHub Actions security analysis).
required: false
type: boolean
default: true
enable_trivy:
description: Run Trivy filesystem scan.
required: false
type: boolean
default: true
enable_gitleaks:
description: Run Gitleaks history scan.
required: false
type: boolean
default: true
zizmor_advisory:
description: |
When true, zizmor still runs and uploads SARIF, but never fails
the job. Useful for consumers transitioning toward contract
conformance whose existing bespoke workflows have known findings.
Default is false (strict — block on error-level findings).
required: false
type: boolean
default: false
trivy_advisory:
description: |
When true, Trivy still runs and uploads SARIF, but never fails the
job. Same transition story as zizmor_advisory.
required: false
type: boolean
default: false
gitleaks_advisory:
description: |
When true, Gitleaks still runs and reports findings, but never
fails the job. Same transition story as zizmor_advisory.
required: false
type: boolean
default: false

permissions: {}

jobs:
trivy:
name: Trivy (filesystem & secrets)
if: ${{ inputs.enable_trivy }}
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
security-events: write
actions: read
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Run Trivy policy and generate SARIF
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
scan-type: "fs"
scan-ref: "."
format: "sarif"
output: "trivy-results.sarif"
severity: ${{ inputs.trivy_severity }}
scanners: "misconfig,secret"
exit-code: ${{ inputs.trivy_advisory && '0' || '1' }}

- name: Upload Trivy SARIF to GitHub Security
# SARIF upload requires GitHub Advanced Security on private repos.
# continue-on-error keeps the scan job green even when the upload
# is rejected — findings still appear in the run log.
uses: github/codeql-action/upload-sarif@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3
if: always()
continue-on-error: true
with:
sarif_file: "trivy-results.sarif"

gitleaks:
name: Gitleaks (secret scan)
if: ${{ inputs.enable_gitleaks }}
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
actions: read
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
persist-credentials: false

- name: Run Gitleaks
id: gitleaks_run
uses: docker://ghcr.io/gitleaks/gitleaks:v8.30.1@sha256:c00b6bd0aeb3071cbcb79009cb16a60dd9e0a7c60e2be9ab65d25e6bc8abbb7f
continue-on-error: ${{ inputs.gitleaks_advisory }}
with:
args: detect --source . --redact --verbose --no-banner

- name: Surface Gitleaks advisory result
if: ${{ inputs.gitleaks_advisory && steps.gitleaks_run.outcome == 'failure' }}
run: |
echo "::warning::Gitleaks reported findings (advisory mode — not blocking)."

zizmor:
name: zizmor (Actions security)
if: ${{ inputs.enable_zizmor }}
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
security-events: write
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"

- name: Install zizmor
# renovate: datasource=pypi depName=zizmor
run: python -m pip install --no-cache-dir zizmor==1.24.1

- name: Run zizmor
# Emit SARIF for the GitHub Security tab; --persona regular catches
# findings most projects care about (omit auditor-only false positives).
env:
ZIZMOR_ADVISORY: ${{ inputs.zizmor_advisory }}
run: |
set -o pipefail
zizmor --format sarif --persona regular .github/workflows/ \
> zizmor.sarif || rc=$?
echo "zizmor exit code: ${rc:-0}"
# Re-run for human-readable output in the log.
zizmor --persona regular .github/workflows/ || true
if [ "${ZIZMOR_ADVISORY}" = "true" ]; then
echo "zizmor in advisory mode — findings are visible in the run "
echo "log and on the Security tab but do not fail the job."
exit 0
fi
# Strict mode (default): block on error-level findings.
python <<'PY'
import json, sys
with open("zizmor.sarif", encoding="utf-8") as fh:
sarif = json.load(fh)
blocking = []
for run in sarif.get("runs", []):
for result in run.get("results", []):
level = result.get("level", "warning")
if level == "error":
msg = result.get("message", {}).get("text", "")
loc = result.get("locations", [{}])[0]
uri = loc.get("physicalLocation", {}).get("artifactLocation", {}).get("uri", "?")
blocking.append(f"{uri}: {msg}")
if blocking:
print("zizmor blocking findings:")
for b in blocking:
print(f" - {b}")
sys.exit(1)
print("no blocking zizmor findings")
PY

- name: Upload zizmor SARIF
if: always()
continue-on-error: true
uses: github/codeql-action/upload-sarif@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3
with:
sarif_file: zizmor.sarif
category: zizmor
Loading
Loading