PRD: Secure Actions — Supply Chain Security for CI Pipeline Actions
Status: Draft Author: CTO Agent Date: 2026-04-03 Updated: 2026-04-04 Issue: CAS-91
Problem
CI pipeline actions (GitHub Actions, GitLab CI templates, etc.) are a critical supply chain vector that most teams leave unmanaged. The majority of workflows pin actions to mutable version tags (actions/checkout@v4) that can be silently replaced by upstream maintainers — or by attackers who compromise an action repo. Even SHA-pinned actions can reference vulnerable dependency trees that no one audits.
CascadeGuard secures container images through the full lifecycle (build, scan, sign, quarantine, publish). The CI pipeline that runs those protections is itself unprotected. A compromised action in a workflow can exfiltrate secrets, inject malicious code into builds, or tamper with artifacts before signing — bypassing every downstream control.
There is no integrated tooling that applies the same supply chain principles (pinning, quarantine delays, vulnerability scanning, allowlisting) to CI pipeline actions. Existing point solutions (StepSecurity Harden-Runner, Socket) address subsets but don’t integrate with the image security posture CascadeGuard already provides.
Scope note: v1 targets GitHub Actions as the initial CI platform. The architecture, CLI, policy schema, and catalog are designed to be CI-platform-agnostic so that GitLab CI, CircleCI, and other providers can be added without breaking changes.
Goals
- Extend CascadeGuard’s supply chain model to CI pipelines — actions get the same pin, scan, quarantine treatment as container images
- Enforce action integrity — every action reference in a workflow resolves to an immutable, audited commit SHA
- Detect upstream risk — continuously monitor action repos for tag moves, force pushes, maintainer changes, and dependency vulnerabilities
- Unify the security posture view — actions appear alongside images in the CascadeGuard dashboard with the same vulnerability, compliance, and trust metrics
- Noun-verb CLI design — new functionality lives under
cascadeguard actions <verb>, and existing flat verbs migrate to noun groups in this release
Non-Goals
- Replacing platform-native action allowlist policies (we augment them with scanning and quarantine)
- Running or executing actions (we analyze them statically)
- Building a hosted action marketplace or mirror (v1 is policy + scanning only)
- Forking or vendoring actions into customer repos
User Personas
DevSecOps Engineer (Primary)
Responsible for CI/CD security posture across 10–100 repos. Needs to enforce action pinning org-wide, respond to upstream action compromises, and report compliance status. Currently does this manually or not at all.
Application Developer
Writes and maintains CI workflows. Needs a frictionless way to pin actions, update them safely, and understand when an action they depend on has a known vulnerability. Does not want to manually look up commit SHAs.
Security Lead / CISO
Needs a single dashboard view of supply chain risk across both container images and CI pipelines. Requires audit evidence that all CI dependencies are pinned, scanned, and policy-compliant.
Functional Requirements
FR-1: SHA Pin Enforcement
| ID | Requirement | Priority | Acceptance Criteria |
|---|---|---|---|
| FR-1.1 | CI check fails any workflow file containing a mutable action reference (@v4, @main, @master, @latest) | P0 | Given a workflow with actions/checkout@v4, when the check runs, then the check fails with a message identifying the unpinned reference and its file:line location |
| FR-1.2 | CI check passes workflow files where all action references are full 40-char commit SHAs | P0 | Given a fully-pinned workflow, when the check runs, then the check passes |
| FR-1.3 | Policy exceptions can allowlist specific actions for tag-based references with documented justification | P1 | Given an action in the exception list, when the check runs, then the check passes with an info-level annotation noting the exception |
FR-2: Action Pinning & Update CLI (cascadeguard actions pin)
| ID | Requirement | Priority | Acceptance Criteria |
|---|---|---|---|
| FR-2.1 | cascadeguard actions pin resolves all mutable tag references in workflow files to their current commit SHA | P0 | Given a workflow with actions/checkout@v4, when the command runs, then the reference is replaced with actions/checkout@<sha> # v4.2.2 |
| FR-2.2 | Pinning preserves a human-readable version comment after the SHA | P0 | The output format is owner/repo@<40-char-sha> # <resolved-tag> |
| FR-2.3 | cascadeguard actions pin --check exits non-zero if any unpinned references exist, without modifying files | P1 | Exit code 1 with a list of unpinned references; exit code 0 if all pinned |
| FR-2.4 | cascadeguard actions pin enforces all configured supply chain controls — delay window (refuses to pin a version released less than N hours ago), allowlist/denylist policy, and trust score thresholds | P1 | Given delay_window_hours: 72 and an action version released 24h ago, the command skips that action and reports the remaining delay. Given a denied action, the command refuses to pin it |
| FR-2.5 | cascadeguard actions pin --update updates already-pinned SHAs to the latest version that satisfies all supply chain controls (delay window, policy, trust) | P1 | Given an action with a newer version released 5 days ago and a 72h delay window, when the command runs, then the SHA is updated to the new version |
| FR-2.6 | cascadeguard actions pin --update --dry-run shows what would change without modifying files | P1 | Output lists each action, current version, available version, and whether supply chain controls are satisfied |
FR-3: Action Audit CLI (cascadeguard actions audit)
| ID | Requirement | Priority | Acceptance Criteria |
|---|---|---|---|
| FR-3.1 | cascadeguard actions audit produces a report of all actions used across workflow files with: owner, repo, pinned SHA (or tag), resolved version, known CVE count | P0 | Output is a table (terminal) or JSON (--json) listing every unique action reference |
| FR-3.2 | Audit flags actions whose pinned SHA no longer matches the tag it was pinned from (drift detection) | P1 | If v4 now points to a different SHA than what’s pinned, the report flags it as “drifted” |
| FR-3.3 | Audit flags actions from repos with fewer than 100 stars, no verified org badge, or last commit older than 12 months | P2 | Trust signal warnings appear in the report for actions below threshold |
FR-4: Actions Policy Configuration
| ID | Requirement | Priority | Acceptance Criteria |
|---|---|---|---|
| FR-4.1 | Actions policy is defined as an extension of the existing .cascadeguard/config.yaml (or as a dedicated file in .cascadeguard/actions-policy.yaml). Both single-file and folder-based config layouts are supported | P0 | The schema is documented; cascadeguard actions policy validate validates the config regardless of layout |
| FR-4.2 | Default policy is deny — any action not in the allowlist fails the CI check | P1 | Given an action not in the allowlist, when the CI check runs, then it fails |
| FR-4.3 | cascadeguard actions policy init generates a starter policy from current workflow usage | P1 | Running the command in a repo produces a policy pre-populated with all currently-used action owners and actions |
FR-5: Action Vulnerability Scanning
| ID | Requirement | Priority | Acceptance Criteria |
|---|---|---|---|
| FR-5.1 | For each pinned action, resolve its action.yml and identify runtime dependencies (Node package.json, Docker base images, composite sub-actions) | P1 | Dependency tree is resolved and stored |
| FR-5.2 | Run Grype/Trivy against the resolved dependency tree and report CVEs with severity | P1 | CVEs appear in cascadeguard actions audit output and in the dashboard |
| FR-5.3 | Composite actions are resolved recursively through the full transitive closure of sub-action references | P1 | A composite action referencing another composite action is fully resolved |
| FR-5.4 | Action vulnerability data appears in the same catalog API that serves image vulnerabilities | P1 | GET /api/catalog/actions/{owner}/{repo} returns vulnerability data in the same schema as image entries |
FR-6: Upstream Monitoring
| ID | Requirement | Priority | Acceptance Criteria |
|---|---|---|---|
| FR-6.1 | Subscribe to GitHub’s webhook/event firehose for monitored action repos to receive real-time notifications of releases, tag moves, and push events | P1 | System receives events within minutes of occurrence; falls back to polling for repos that cannot be webhooked |
| FR-6.2 | Detect when an action tag is moved to a different commit SHA (tag tampering) | P1 | Alert generated within 1 hour of detection |
| FR-6.3 | Detect force pushes to an action repo’s default branch | P2 | Alert generated within 1 hour of detection |
| FR-6.4 | Detect changes to the action repo’s maintainer/collaborator list | P2 | Alert generated within 24 hours of detection |
Detection mechanism: The primary feed is GitHub’s webhook events API. For each action in a customer’s policy, CascadeGuard registers for release, push, member, and public events on the action’s source repo. For repos where webhooks cannot be installed (third-party public repos), a polling fallback checks git ls-remote refs and the GitHub API collaborators/releases endpoints on a configurable interval (default: 15 minutes). The expected cardinality is manageable — a typical enterprise uses 20–50 unique actions; even large orgs rarely exceed 200.
FR-7: Dashboard & Catalog Integration
| ID | Requirement | Priority | Acceptance Criteria |
|---|---|---|---|
| FR-7.1 | ”Actions” tab in the CascadeGuard dashboard shows all monitored actions with: name, pinned version, latest version, CVE count, policy compliance status, trust score | P1 | Tab is visible to authenticated users; data updates within 1 hour of scan |
| FR-7.2 | Actions catalog API provides the same query, filter, and status interface as the images catalog | P1 | GET /api/catalog/actions returns paginated action entries with vulnerability and compliance data |
| FR-7.3 | Actions and images are queryable through a unified search | P2 | Searching “CVE-2024-XXXX” returns both image and action results |
Non-Functional Requirements
| ID | Requirement | Target | Measurement |
|---|---|---|---|
| NFR-1 | actions pin completes for a repo with 20 workflow files | < 30 seconds (p95) | CI timing data |
| NFR-2 | actions audit completes for a repo with 50 unique action references | < 60 seconds (p95) | CI timing data |
| NFR-3 | CI policy check adds to workflow runtime | < 10 seconds (p95) | CI step timing |
| NFR-4 | Upstream tag-move detection latency | < 1 hour from event | Monitoring SLA |
| NFR-5 | Action vulnerability scan freshness | Updated within 24 hours of upstream CVE database update | Scan timestamp delta |
CLI Design: Noun-Verb Pattern
All CascadeGuard CLI commands adopt a noun-verb pattern to group related operations under a resource namespace. This is consistent with kubectl, docker, gh, and other modern CLIs.
Actions commands
cascadeguard actions pin [--check] [--dry-run] [--update]
cascadeguard actions audit [--json] [--severity critical,high]
cascadeguard actions policy init
cascadeguard actions policy validate
Images commands (migration from flat verbs)
cascadeguard images validate # was: cascadeguard validate
cascadeguard images enrol # was: cascadeguard enrol
cascadeguard images check # was: cascadeguard check
cascadeguard images build # was: cascadeguard build
cascadeguard images status # was: cascadeguard status
Policies commands
cascadeguard policies lint # unified policy validation
Note: We are pre-v1 — there is no backwards-compatibility obligation. Flat verbs are removed in favour of the noun-verb structure.
Policy File Schema
Actions policy can be defined as part of the existing .cascadeguard/config.yaml under an actions key, or as a standalone .cascadeguard/actions-policy.yaml file, or as a file inside a .cascadeguard/ config folder (e.g. .cascadeguard/policies/actions.yaml). The CLI auto-discovers whichever layout is present.
# .cascadeguard/actions-policy.yaml (standalone)
# OR .cascadeguard/config.yaml under `actions:` key
# OR .cascadeguard/policies/actions.yaml (folder layout)
version: 1
# Default posture: deny requires explicit allowlisting; warn logs but does not fail
default: deny # deny | warn | allow
# New action versions must be this old before pin commands accept them
delay_window_hours: 72
# Trusted action owners — all actions from these orgs are allowed
allowed_owners:
- actions # GitHub official
- docker
- sigstore
- anchore
- aquasecurity
# Specific allowed actions from non-trusted owners
allowed_actions:
- cloudflare/wrangler-action
- google-github-actions/auth
# Explicitly denied patterns
denied_actions:
- "*/*@master"
- "*/*@main"
- "*/*@latest"
# Per-action exceptions with justification
exceptions:
- action: internal-org/custom-action
reason: "Internal action, pre-approved by security team"
pin_required: false
delay_window_hours: 0Integration with Existing Features
| CascadeGuard Feature | Secure Actions Integration |
|---|---|
| Image quarantine / delay window | Same delay model for action version adoption |
| Grype/Trivy scanning | Extended to scan action dependency trees |
| SBOM generation (future) | Generate SBOMs for action dependency trees |
| Cosign signing (future) | Sign the actions-policy.yaml as attestation |
generate-ci command | Emit SHA-pinned action refs by default |
| Dashboard / CVE view | ”Actions” tab alongside “Images” tab |
| Catalog API | Extend to include action entries |
Dogfooding: Internal Rollout
Apply Secure Actions to CascadeGuard’s own repos before shipping to customers.
| Repo | Current State | Action Needed |
|---|---|---|
| cascadeguard-app | Fully SHA-pinned | Add policy file, enable drift detection |
| cascadeguard-open-secure-images | Mostly SHA-pinned (2 anchore actions were unresolvable) | Resolve anchore/setup-grype and anchore/setup-syft SHAs |
| cascadeguard (core CLI) | Tag-pinned | Run actions pin, add policy file |
| cascadeguard-exemplar | Tag-pinned (auto-generated workflows) | Update generate-ci to emit SHAs, regenerate |
| argocd-eda | Tag-pinned, trivy-action@master | Pin all, replace @master reference |
| cascadeguard-state (formerly image-factory-state) | Needs rename and policy file | Rename repo, add policy file |
Highest-risk finding: Multiple repos reference trivy-action@master — an unversioned, mutable reference to a security-critical action. This should be remediated immediately regardless of this PRD’s timeline.
Open Questions
- Delay window granularity — global default with per-action override, or per-owner override too? Recommendation: global + per-action.
- Action mirror/proxy — do we need a hosted cache of approved action SHAs (like our image proxy), or is policy enforcement sufficient for v1? Recommendation: policy-only for v1, mirror for v2.
- GitHub App vs reusable workflow — CI enforcement can be a GitHub App check (auto-runs on all PRs) or a reusable workflow (repo must opt in). Recommendation: ship reusable workflow first (lower barrier), GitHub App second. This goes in our core open source CLI tool.
Success Criteria
v1 (MVP)
v1 is the minimum viable product — it must be viable enough for customers to adopt.
cascadeguard actions pincorrectly resolves tags to SHAs across all workflow filescascadeguard actions pin --updateupdates pinned SHAs to latest versions satisfying supply chain controlscascadeguard actions auditproduces an accurate inventory with CVE data- Actions policy configuration defined and validated (supports all config layouts)
- CI check (reusable workflow) enforces pin and policy requirements
- Delay window enforcement operational
- Action dependency scanning integrated with Grype/Trivy
- Upstream tag-move and force-push detection active
- Dashboard “Actions” tab live with vulnerability and compliance data
- Actions catalog API operational with same schema as images
- All CascadeGuard-owned repos are fully SHA-pinned with policy files
- Existing flat CLI verbs migrated to noun-verb pattern
- Customer documentation published
Growth Metrics
- 80% of repos with CascadeGuard CI also adopt actions policy within 90 days of feature launch
- Mean time to detect upstream action compromise < 1 hour
- Zero unpinned action references across all customer repos using the policy check