CI Security Workflow Design¶
Date: 2026-07-24
Goal¶
Extract duplicated Trivy filesystem scanning and CycloneDX SBOM generation from language-specific CI workflows (ci-go, ci-nodejs, ci-aws-terraform) into a dedicated reusable workflow (ci-security) invoked by a repository-wide caller (on-ci-security).
Architecture¶
on-ci-security.yaml schedule + path-filtered push/PR + workflow_dispatch
└─ ci-security.yaml Trivy fs scan + CycloneDX SBOM (repo-wide by default)
on-ci-push-go.yaml
└─ ci-go.yaml test / lint / govulncheck (language-specific)
ci-nodejs / ci-aws-terraform language checks only; Trivy removed
Files¶
| File | Type | Role |
|---|---|---|
ci-security.yaml |
Reusable (workflow_call) |
Trivy vulnerability gate + SBOM artifact |
on-ci-security.yaml |
Caller (dogfood) | Triggers security CI in this repository |
example/on-ci-security.yaml |
Consumer template | Copy into consumer repositories |
ci-security.yaml contract¶
Inputs (alphabetical)¶
| Input | Default | Description |
|---|---|---|
artifact_prefix |
repo |
Prefix for SBOM artifact name (sbom-{prefix}) |
dependency_review_fail_on_severity |
high |
dependency-review fail-on-severity (low, moderate, high, critical) |
scan_ref |
. |
Trivy scan-ref (repository root or monorepo subdirectory) |
trivy_config |
trivy.yaml |
Path to Trivy config file (vuln scan, gate, and SBOM) |
trivy_version |
v0.72.0 |
Trivy version |
Behavior¶
- Checkout repository.
- Install Trivy once (
aquasecurity/setup-trivywith cache). - Run filesystem scans in one step: SARIF (
exit-code: 0), gate (exit-code: 1,ignore-unfixed: false), CycloneDX SBOM (exit-code: 0). All scans honortrivy_config. - Upload Trivy SARIF to GitHub Security.
- Upload SBOM artifact (
retention-days: 30). - On pull requests, run
dependency-review(requiresfetch-depth: 0checkout). - Post PR failure comment when the Trivy gate fails.
- Write job summary via
summarycomposite action.
Language-specific security remains in language workflows:
govulncheck→ci-gonpm/pnpm audit→ci-nodejs
on-ci-security.yaml triggers¶
| Trigger | Purpose |
|---|---|
schedule (0 6 * * *) |
Daily CVE drift detection on unchanged lockfiles |
push / pull_request (path filters) |
Gate new vulnerabilities before merge |
workflow_dispatch |
Manual rerun |
Path filters¶
Security-relevant paths only (dependencies, IaC, containers, application source, Trivy config, workflow definitions):
**/go.mod,**/go.sum,**/*.go**/*.js,**/*.jsx,**/*.ts,**/*.tsx(and e.g.nodejs/**for Node monorepos)**/package.json,**/package-lock.json,**/pnpm-lock.yaml,**/yarn.lock**/*.tf,**/*.tfvars,**/.tflint.hcl**/Dockerfile,**/Dockerfile.*trivy.yaml,.trivyignorenodejs/**(Node monorepos).github/workflows/ci-security.yaml,.github/workflows/on-ci-security.yaml
Risk mitigations¶
1. Consumers must add on-ci-security¶
Risk: Removing Trivy from language CI leaves no scan until the caller is added.
Mitigation:
- Ship
example/on-ci-security.yamlwith remote SHA pin. - Document migration steps below.
- Dogfood
on-ci-security.yamlin this repository.
2. Monorepo workspace SBOM granularity¶
Risk: A single repo-root scan may be insufficient when teams want per-service SBOM artifacts.
Mitigation:
ci-securityexposesscan_refandartifact_prefixinputs.- Callers can define multiple jobs (or a matrix) with different
with:values.
Example (consumer):
jobs:
security-api:
uses: y-miyazaki/config/.github/workflows/ci-security.yaml@<sha> # vX.Y.Z
with:
artifact_prefix: api
scan_ref: services/api
security-web:
uses: y-miyazaki/config/.github/workflows/ci-security.yaml@<sha> # vX.Y.Z
with:
artifact_prefix: web
scan_ref: services/web
3. PR gate + cron coverage¶
Risk: Cron-only scanning does not block vulnerable PRs; path-only scanning misses overnight CVEs.
Mitigation: on-ci-security uses both schedule and path-filtered push / pull_request.
Consumer migration¶
- Copy
example/on-ci-security.yamlto.github/workflows/on-ci-security.yaml. - Replace
<sha>with the releasedy-miyazaki/configcommit SHA. - Adjust
paths:if the repository layout differs (add consumer-specific lockfile or IaC globs). - Remove
trivy.yamlfrom language CI caller path filters (for exampleon-ci-push-go.yaml) so Trivy changes trigger security CI instead of Go CI only. - Upgrade pinned
ci-go,ci-nodejs, andci-aws-terraformrefs to a release that no longer embeds Trivy. - Optional monorepo: add extra
ci-securityjobs withscan_ref/artifact_prefixper service.
Out of scope¶
- Moving
govulncheckintoci-security(stays inci-go; Go reachability is a language concern) - Container image scanning in
cd-go-releaser(binary release only; usecd-aws-go-registrytrivy_image_scan)
Extensions (2026-07-24)¶
| Capability | Location | Gate |
|---|---|---|
dependency-review |
ci-security (dependency-review job, PR only) |
Blocks on fail-on-severity: high |
| Trivy SARIF | ci-security (trivy job) |
Security tab + separate gate step (ignore-unfixed: false; blocks on HIGH/CRITICAL including unfixed) |
| CodeQL + Semgrep | ci-sast |
Semgrep --error; CodeQL via GitHub defaults |
| Trivy image scan | cd-aws-go-registry after ECR push |
Blocks on HIGH/CRITICAL fixable CVEs only (ignore-unfixed: true; base-image CVEs without a vendor fix do not block deploy) |
Note: Image scan runs after push to ECR; the job fails before downstream deploy callers proceed, but the image tag already exists in the registry. Use trivy_image_scan: false to disable for bootstrap repos.
Verification¶
bash .agents/skills/github-actions-validation/scripts/validate.sh .github/workflows/