Skip to content

CI/CD Pipeline Security

The pipeline that builds and ships your software is itself a high-value target. It holds powerful credentials, executes code from many sources, and has direct access to production. Attackers increasingly go after the pipeline rather than the application — a single poisoned build can compromise every downstream consumer. Securing the pipeline is therefore as important as securing the code it builds.

The threat model: OWASP Top 10 CI/CD Security Risks

The OWASP Top 10 CI/CD Security Risks is the canonical reference. Key risks include:

  • Insufficient flow control mechanisms (CICD-SEC-1) — the ability to push code or artifacts to production without adequate review or gates. Anyone who can merge to main can deploy to production.
  • Inadequate identity and access management (CICD-SEC-2) — stale, shared, or over-privileged human and machine identities across the SCM, CI, and artifact systems; no MFA on accounts that can change pipelines.
  • Dependency-chain abuse (CICD-SEC-3) — dependency confusion, typosquatting, and malicious packages pulled during the build. The pipeline's package manager runs with the same trust as CI secrets.
  • Poisoned Pipeline Execution / PPE (CICD-SEC-4) — injecting malicious commands into the build by manipulating pipeline configuration or scripts. Example: a PR modifies a GitHub Actions workflow file to exfiltrate secrets to an external URL. Defense: require reviews for workflow file changes; use pull_request_target carefully; never interpolate untrusted values (github.event.pull_request.title, branch names, issue bodies) directly into run: scripts — pass them through environment variables instead.
  • Insufficient PBAC / pipeline-based access controls (CICD-SEC-5) — runners and jobs with far more access than they need; secrets available to all jobs in a repository.
  • Insufficient credential hygiene (CICD-SEC-6) — long-lived, over-privileged secrets exposed to pipeline steps; secrets printed to logs; secrets shared across environments.
  • Ungoverned usage of third-party services (CICD-SEC-8) — unvetted GitHub Actions, Jenkins plugins, and integrations running with access to the pipeline and its credentials.
  • Improper artifact integrity validation (CICD-SEC-9) — artifacts or code that can be injected into the pipeline without verification; addressed by signing and verification (see Artifact Signing and Provenance).

Hardening the pipeline

Least-privilege tokens

Scope permissions explicitly and narrowly. Every permission not granted is a capability an attacker cannot abuse:

# GitHub Actions: explicit minimal permissions
permissions:
  contents: read        # read source code only
  packages: write       # push to GitHub Packages
  id-token: write       # request OIDC token for keyless signing
  # All other permissions are denied by default

OIDC workload identity — replace long-lived secrets

Exchange short-lived OIDC tokens for cloud credentials instead of storing long-lived cloud access keys as CI secrets:

- name: Configure AWS credentials via OIDC
  uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: arn:aws:iam::123456789:role/ci-deploy
    aws-region: us-east-1
    # No AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY needed

The OIDC token is valid for minutes and cannot be reused; a leaked token expires before it can be weaponized.

Pin third-party Actions to immutable commit SHAs

Tags like @v3 are mutable — they can be silently redirected to malicious code. This has happened repeatedly: the March 2025 tj-actions/changed-files compromise and the March 2026 hijacking of nearly all aquasecurity/trivy-action tags both exfiltrated CI secrets from workflows that referenced mutable tags. Pin to the commit digest:

# Mutable tag — do not use this in production
- uses: actions/checkout@v4

# Immutable SHA pin — use this
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

Use tools like Dependabot, Renovate, or pinact to automate SHA pin updates as new versions are released. GitHub also lets organizations enforce SHA pinning through Actions policy, and supports immutable releases that prevent tags from being moved after publication. The examples in this guideline use version tags for readability; pin them in real workflows.

Harden runners

  • Ephemeral, isolated runners — use ephemeral GitHub-hosted or self-hosted runners that start clean and are destroyed after each job. Persistent runners accumulate state that attackers can abuse.
  • Network egress control — start in audit mode to learn the build's normal destinations, then move to block. Restrict what external hosts runners can reach during a build. A build that only needs npm and GitHub should not be able to reach arbitrary cloud APIs. Use StepSecurity Harden-Runner for GitHub Actions:
- uses: step-security/harden-runner@v2
  with:
    egress-policy: block
    allowed-endpoints: >
      registry.npmjs.org:443
      api.github.com:443
  • Secrets scoping — in GitHub, environment secrets are only available to workflow jobs that target that environment; use environment protection rules to gate access.

Protect fork/PR workflows

Pull requests from forks are a common PPE vector. Apply these controls:

  • Require approval before running CI on first-time contributor PRs (pull_request workflows from forks do not receive secrets; pull_request_target does and must be used carefully).
  • Never expose secrets to untrusted PRs, and never check out and execute fork code in a pull_request_target or workflow_run job that has secrets or a write token.
  • Use persist-credentials: false on actions/checkout so the token is not left in .git/config for later steps.
  • Protect workflow files with CODEOWNERS so any change to .github/workflows/ requires security team review.

Pipeline configuration as a security perimeter

The pipeline definition (.github/workflows/, Jenkinsfile, .gitlab-ci.yml) is executable code. Treat it accordingly:

  • User role separation and least privilege — separate permissions across user types. Contributors should have permissions to submit changes via branches and pull requests, but no direct write access to default branch workflows, runner configurations, or environment secrets. Reserve administrative permissions (such as managing runners, organization secrets, and webhooks) to platform or DevSecOps teams with MFA and SSO enforced.
  • Protected branches for pipeline definitions — enforce branch protection on all default and release branches (main, master, release/*) hosting pipeline definitions. Prohibit direct pushes, force pushes, and branch deletions. Configure protection rules so administrators and repository owners cannot bypass review requirements.
  • Appropriate review and approval requirements — require pull request reviews before merging any pipeline changes. Mandate at least one independent, qualified approver (using CODEOWNERS to route changes to platform or security teams), and prohibit author self-approval.
  • Dismiss stale approvals on commit — automatically invalidate previously granted pull request approvals whenever new commits are pushed, preventing unreviewed changes from slipping into the pipeline.
  • Require passing status checks — require automated validation checks (linters, syntax validation, and static analysis with tools like zizmor or Semgrep CI rules) to pass before pipeline changes can merge.
  • Environment deployment approval gates — protect sensitive and production environments with deployment protection rules requiring explicit, authorized human sign-off before jobs can assume deployment roles or access environment secrets.
  • Centralized and reusable pipeline definitions — maintain standard build and security gate definitions in centralized, read-only repositories (such as GitHub Reusable Workflows, GitLab CI/CD components, or Jenkins Shared Libraries). Application repositories reference these templates by immutable commit SHA or version tag, preventing individual teams from bypassing or disabling required security controls.

Reusable pipeline definitions and centralized governance

Maintaining separate, copy-pasted pipeline configurations across dozens or hundreds of application repositories violates the DRY (Don't Repeat Yourself) principle and introduces severe governance risks: security scanner configs drift out of date, mandatory compliance checks are accidentally or intentionally omitted, and remediation requires editing hundreds of repositories individually. Managing shared pipeline definitions in a dedicated, centrally governed repository provides a secure, scalable "paved road".

Key benefits

  • Pipeline integrity — Prevents individual repository contributors or compromised repository-level credentials from tampering with, weakening, or bypassing mandatory build and security checks.
  • Consistent security enforcement — Standardizes required security steps (secret scanning, SAST, SCA, container scanning, artifact signing, and SBOM generation) across the entire organization.
  • Reduced duplication (DRY) — Common build, test, and release logic is authored and maintained once. Vulnerability patches, tool upgrades, and compliance updates propagate efficiently.
  • Scalability — A centralized platform or security team can maintain and harden enterprise delivery workflows without requiring per-repository maintenance from application teams.

Consumption mechanisms and trade-offs

Organizations should choose a consumption mechanism suitable for their tooling landscape while balancing security and operational trade-offs:

  • Native CI platform reuse mechanisms (e.g., GitHub Actions Reusable Workflows and Composite Actions, GitLab CI include:file and CI/CD Components, Azure Pipelines templates, Tekton Pipelines):
  • Strengths: Provide native parameter validation, isolated job execution environments, and native credential scoping.
  • Trade-offs: Tightly coupled to specific CI platforms. Organizations must configure organizational policies or rulesets to prevent projects from bypassing or overriding the required workflow templates.
  • Git submodules or subtrees:
  • Strengths: Well-suited for sharing modular build scripts, security hook bundles, or pipeline definitions across heterogeneous, on-premises, or legacy CI systems that lack native remote workflow inclusion. Git submodules naturally lock to a specific commit SHA in the parent repository.
  • Trade-offs: Submodules are not mandatory and introduce developer friction: they require explicit initialization (git submodule update --init) in CI runners and local checkouts, complicate branching, and carry risks of URL tampering or recursive checkout abuse if .gitmodules is not strictly controlled and reviewed.
  • Containerized / packaged task runners:
  • Strengths: Bundling pipeline tasks into signed, containerized CLI tools creates reproducible, platform-agnostic execution.
  • Trade-offs: Requires maintaining and securing the container build pipeline, registry access controls, and base image vulnerability management.

Version pinning and tampering prevention

Shared pipelines represent a concentrated point of failure; an untrusted modification to a shared template affects every downstream project:

  • Pin to immutable commit SHAs — Always reference centralized workflows by an exact commit SHA or cryptographically signed, immutable release tag. Avoid referencing mutable branch heads (e.g., @main, @master) or moving tags, which expose consumers to silent supply-chain tampering and unexpected breaking changes.
  • Tier-0 repository protection — Host shared definitions in a dedicated repository with strict access controls: mandate multi-party code review with CODEOWNERS from security and platform teams, enforce signed commits, and prohibit direct pushes.
  • Controlled update rollouts — Treat pipeline updates like software dependencies. Use automated dependency management tools (e.g., Dependabot, Renovate) to propose pinned version bumps via pull requests, allowing teams to validate updates in staging pipelines before merging.
  • Prevent downstream overrides — Structure reusable workflows so that critical security steps and parameters cannot be disabled, shadowed, or bypassed by caller repositories.

Observability, tracking, and measurable enforcement

Centralization requires visibility into actual adoption and mechanisms to detect non-compliance:

  • Pipeline consumption tracking and auditing — Maintain an automated inventory of which repositories and branches consume shared workflows, including the specific versions or commit SHAs in use. Regularly query CI audit logs to detect stale, unauthorized, or deprecated pipeline usage.
  • Policy enforcement and mandatory templates — Implement organization-level guardrails (e.g., GitHub Repository Rulesets, GitLab required compliance pipelines, or custom admission webhooks) that require production services to execute approved, centralized pipeline templates before allowing merges or deployments.
  • Automated compliance alerting — Alert platform and security teams when builds run with unauthorized custom workflows or when repositories fail to update deprecated pipeline versions within defined grace periods.

Measure and enforce continuously

Use automated tooling to keep the pipeline hardened over time — egress monitoring on runners, workflow static analysis, and repository security scoring — and feed the results into your governance program. A hardened pipeline today can drift back to a vulnerable state without continuous enforcement.

OpenSSF Scorecard provides a free, automated security score for open-source repositories that covers many pipeline hygiene dimensions (branch protection, dependency pinning, SAST, signing):

scorecard --repo=github.com/myorg/myrepo --format json | jq .checks

Other CI platforms

The same principles apply beyond GitHub Actions: in GitLab CI use protected branches and variables, id_tokens for OIDC instead of stored cloud keys, and pinned include: references and component versions; in Jenkins restrict Script Security approvals, pin and audit plugins, and run builds on ephemeral agents rather than the controller; in all systems, separate build and deploy identities and keep production credentials out of untrusted-code jobs.

Common pitfalls and anti-patterns

  • Direct write access or admin bypass on pipeline configurations — allowing direct commits to pipeline files or permitting authors to approve their own workflow changes undermines all CI/CD security controls.
  • Secrets stored as plain-text environment variables in pipeline config — anyone who can read the pipeline file can read the secret. Use the platform's secret store, not inline values.
  • One long-lived AWS/GCP key for all CI jobs — a single leaked key compromises every environment. Move to OIDC with per-environment roles.
  • pull_request_target with unchecked-out code from the fork — this gives fork PRs access to secrets. This is the most common PPE vulnerability in GitHub Actions.
  • Third-party Actions from unverified authors — an Action with write access to the workspace and access to secrets can exfiltrate credentials. Audit every third-party Action used.
  • No egress control on runners — a compromised runner with unrestricted internet access can quietly exfiltrate secrets or establish C2 connections.
  • Security scans disabled "temporarily" in CI — "temporary" disables become permanent. Protect security step configurations in CODEOWNERS just as you protect workflow files.
  • Copy-pasted pipeline configurations across repositories — leads to configuration drift, unpatched scanner configurations, and bypassing of mandatory security steps. Maintain shared templates in a central repository.
  • Referencing reusable pipelines via mutable refs — referencing shared workflows by @main or floating tags allows unreviewed upstream changes or compromises to execute immediately across all consuming repositories.

Maturity progression

Starter — Enable branch protection on main (require PR reviews, status checks). Move cloud credentials to OIDC workload identity. Set explicit permissions: blocks on all workflow jobs. Audit the list of third-party Actions in use.

Intermediate — Pin all third-party Actions to SHA digests (use Dependabot to manage). Adopt centralized, reusable pipeline templates pinned to immutable commit SHAs for core build and security steps. Add CODEOWNERS for .github/workflows/. Deploy StepSecurity Harden-Runner with egress monitoring. Add zizmor or Semgrep CI rules to scan workflow files. Score repositories with OpenSSF Scorecard and set minimum score targets.

Advanced — Enforce egress allowlists (block mode) on all runners. Enforce mandatory centralized pipeline templates organization-wide (e.g., via repository rulesets or compliance pipelines) and continuously audit template adoption. Use ephemeral, isolated self-hosted runners for sensitive builds. Sign all pipeline outputs (see Artifact Signing and Provenance). Automate Allstar policy enforcement across the GitHub organization. Report pipeline security score trends to engineering leadership quarterly.

Metrics and KPIs

Metric Target
% of repositories with branch protection on main 100%
% of CI jobs using OIDC (no long-lived cloud keys) 100%
% of third-party Actions pinned to SHA digest 100%
% of repositories consuming approved, SHA-pinned reusable pipelines 100%
OpenSSF Scorecard average across organization > 7.0 / 10
Repositories with unreviewed workflow file changes 0
Egress violations detected on runners Tracked; alerts on anomalies

Tools1

Open-source

  • Allstar - Enforces security policies across GitHub organizations (branch protection, binary artifacts, SECURITY.md, outside collaborators); alerts or opens issues on violations.
  • OpenSSF Scorecard - Scores repositories on supply-chain security best practices across 20+ checks; integrates as a GitHub Actions workflow with badge support.
  • pinact - Pins GitHub Actions and reusable workflows to commit SHAs (keeping the version as a comment) and can verify existing pins in CI.
  • StepSecurity Harden-Runner - Egress filtering and runtime security for GitHub Actions runners; monitors and optionally blocks unauthorized outbound connections.
  • zizmor - Static analysis tool for GitHub Actions workflow security vulnerabilities (script injection, PPE patterns, dangerous triggers).

Commercial

  • Cycode - Application security and pipeline/supply-chain protection; detects misconfigured pipelines and enforces policy across GitHub, GitLab, and Bitbucket.
  • Legit Security - Security posture management for the software factory; maps every pipeline, detects risky configurations, and tracks pipeline security posture over time.


  1. Listed in alphabetical order. ↩