Introduction
Your perimeter is hardened. WAF is tuned. MFA is everywhere. Then someone merges a pull request, a GitHub Action fires, and three minutes later a backdoored artifact ships to production. Nobody touched a firewall. Nobody needed to.
We test the app. We test the network. We rarely test the machine that builds the app — and that machine has write access to everything downstream.
A modern pipeline consumes source code, executes code during the build, installs dependencies from external sources, holds or requests secrets, assumes cloud identities to provision and deploy infrastructure, produces release artifacts, publishes them, sometimes signs them, and pushes them into production. At nearly every step it is making a trust decision on the organization's behalf. That is why compromising CI/CD matters: it can bypass application-layer and network-layer controls entirely, because the pipeline is not behind those controls — it is what puts things behind them.
The Pipeline Is Part of Your Security Boundary
It is tempting to treat CI/CD as plumbing: automation sitting between a developer writing code and that code running in production. That framing undersells what the pipeline actually does.

The useful lens throughout this piece is trust: where it is granted, what execution context carries it, what identity holds it, and how far it can travel before anyone notices. Assessing each link in isolation misses the point. Source control can look clean, dependencies can look clean, runners can look clean — and the chain can still fail once you ask the question that actually matters:
Where can trust be abused to move from one stage to the next?
A contributor who can influence a workflow file inherits whatever that workflow can do. A dependency pulled without verification inherits the build job's execution context. A runner not torn down between jobs inherits whatever the last job left behind. That question drives everything below.
How We Perform a CI/CD Supply-Chain Pentest
1. Source Control Review
We start upstream of the pipeline, since most privilege escalation into CI/CD begins here: branch protection, pull request approval requirements, CODEOWNERS enforcement, signed commits, repository and environment secrets, personal access token scoping, OAuth app access, and how fork-based contributions are handled.
Two questions guide this phase. Can a low-privileged developer modify the pipeline definition itself? Can an external contributor influence a workflow that runs with elevated privilege?
2. Pipeline and Workflow Review
We read workflow files closely: triggers such as pull_request, pull_request_target, and workflow_run; GITHUB_TOKEN permissions; which third-party actions are referenced and how they are pinned; where shell steps interpolate user-controlled input directly; and what conditions gate artifact publication or deployment.
pull_request_target deserves precision, since it is often misrepresented. It is not inherently dangerous. It becomes dangerous when a privileged workflow checks out and executes untrusted code from a pull request while still retaining access to privileged tokens or secrets — the combination of untrusted input, privileged context, available credentials, and unsafe workflow design. We look for that combination specifically, not the trigger name alone.
Broadly, the question is: where does attacker-controlled input — a PR title, a branch name, a commit message, a file added in a fork — become something the pipeline executes with real credentials?
3. Dependency Assessment
In ecosystems such as npm, package installation can execute lifecycle scripts like preinstall and postinstall. In CI, a malicious or compromised dependency can turn a routine install into code execution inside the build job, with whatever privilege that job holds.
We check whether dependencies are pinned, whether a lockfile is enforced in CI, whether private and public registries are explicitly separated, whether resolution can fall back to a public registry, whether transitive dependencies get the same scrutiny as direct ones, whether lifecycle scripts are permitted, whether an attacker could introduce or replace a dependency, and what the build environment exposes once that code runs.
Dependency confusion is worth naming specifically. Without strict separation, an attacker can publish a same-named package publicly and have it resolved instead of the internal one. The real control is preventing resolution from an untrusted source in the first place: private registries with explicit configuration, scoped package names, enforced lockfiles, pinning, and allowlisting where appropriate.
4. Secrets and Identity Assessment
We enumerate what the pipeline can access — cloud credentials, deployment tokens, registry credentials, signing keys, API keys — and assess scope, privilege, lifetime, and environment separation. Can an unrelated build step read a deployment credential? Are secrets echoed into logs? Is the same credential reused across pipelines when it does not need to be?
The finding we see most often is not a secret being exposed outright. It is a secret scoped far more broadly than the job using it actually requires.
5. Runner Assessment
Ephemeral versus persistent runners changes the risk calculus entirely. A persistent runner can allow artifacts, credentials, processes, workspace contents, caches, or persistence mechanisms from one job to affect subsequent jobs — including jobs from a different team or trust level.
We check whether runners are ephemeral, whether they are shared across trust boundaries, whether they execute jobs from untrusted contributors, what privilege they run at, whether jobs can reach the Docker socket, what network segments and cloud IAM permissions they hold, whether workspaces are actually cleaned, whether caches are isolated, and whether a compromised job can persist past its own completion.
6. Artifact Integrity Assessment
At each transition in the build chain — Source → Build → Artifact → Registry → Deployment → Production — two questions apply: Can this artifact be modified without detection? Does the next stage actually verify what it consumes, or does it simply trust the reference it was given?
If deployment references mutable identifiers (a floating tag rather than an immutable digest) without independent integrity or provenance checks, anyone with write access to the artifact repository may be able to substitute what actually gets deployed — without touching the original build.
It is worth being precise about the difference between signing, provenance, and an SBOM. Signing answers: was this artifact signed by a trusted signer? Provenance answers: where did this come from, and which build process produced it? An SBOM lists what is inside an artifact — it does not by itself prove the artifact was not tampered with after it was built. We check whether any of these controls exist, and whether deployment verifies them at deploy time rather than generating them for audit purposes only.
7. Blast Radius Assessment
Severity is rarely about the initial weakness alone. It is about what trusted systems the compromised pipeline can subsequently reach. A misconfigured trigger is low-to-medium severity on its own. Chained to an over-scoped deployment credential reaching every environment, it becomes critical.
A typical escalation chain looks like this:
Low-Privilege Developer
↓
Repository Modification
↓
Pipeline Execution
↓
Build-Step Code Execution
↓
CI Credential Access
↓
Artifact Registry
↓
Deployment Identity
↓
ProductionIt's Already Happening: Real-World Precedents
None of the failure modes above are hypothetical. Four disclosed incidents illustrate how different attack paths through the supply chain play out against production systems.
SolarWinds. Attackers compromised the build process itself — injecting a backdoor into the signed Orion software update before it shipped to thousands of downstream customers. No consumer of the update had any way to distinguish it from a legitimate release. The trust extended at signing time was the trust being abused.
Codecov. A compromised uploader script allowed attackers to harvest CI environment variables — including secrets — from any CI job that ran bash {(curl -s https://codecov.io/bash). The attack required no action from end-users beyond using the uploader as documented. Environment exposure during a build step was the trust boundary crossed.
event-stream (npm). A socially engineered maintainer account takeover allowed a malicious actor to publish a version of the widely-used event-stream package containing a targeted backdoor. The attack illustrates that package-level trust can be revoked at any point in a maintainer's tenure, not just at initial publish.
XZ Utils. A years-long infiltration of a maintainer role resulted in a backdoor being introduced into a compression library present in many Linux distributions. The attacker operated as a trusted contributor for an extended period before the malicious change was caught.
What these incidents share is not technique. It is that a compromise at one trusted point in the software supply chain can propagate to every downstream consumer of that trust — often silently and for a long time before anyone notices.
Sample Finding: Over-Privileged CI/CD Deployment Credential
| Field | Detail |
|---|---|
| Observation | A single CI/CD credential has deploy access to DEV, STAGING, and PROD. |
| Attack path | Low-privilege contributor → pipeline modification → pipeline code execution → CI credential access → production deployment access. |
| Security impact | A compromise originating in a lower-trust workflow — a fork PR, a compromised dependency, a phished contributor account — can cross environment boundaries and obtain production deployment capability without touching production directly. |
| Recommendation | Environment-specific identities rather than one shared credential, least-privilege scoping per identity, explicit promotion controls between environments, separate deployment credentials, an approval-gated production environment, and short-lived credentials where supported. |
Build and Deployment Trust Should Not Be the Same Thing
Worth checking on every engagement: does the system that builds software also have unrestricted authority to deploy it everywhere?
Separating build trust from deployment trust — so that producing an artifact and authorizing its release are distinct actions, ideally with distinct identities — can substantially reduce blast radius:
Build
↓
Artifact
↓
Verification
↓
Approval / Promotion
↓
DeploymentThis is not the only valid architecture, but its absence is often what turns a compromised build into a compromised production environment in a single step.
Lab: One Compromised Pipeline, Three Environments

Run this fully isolated, with synthetic credentials only. Never use real cloud access or real production systems.
Step 1: Simulated Malicious Dependency
In an isolated lab repository, introduce a test dependency whose install script writes a harmless marker:
CI_TEST_MARKER=DEPENDENCY_EXECUTEDThis alone proves that dependency installation equals code execution inside the build job.
Step 2: Synthetic CI Credential
Create a fake deployment credential with no real access to anything. From the build step above, check whether it is readable. This tests exposure, not exploitation.
Step 3: Three Isolated Environments
Stand up DEV, STAGING, and a PROD-like environment, each behind its own synthetic credential. Determine whether the single pipeline identity reaches all three.
Step 4: Artifact Substitution
Publish a test artifact, then attempt to replace it before deployment pulls it. Check whether digest, signature, or provenance is verified — or whether deployment simply trusts what it is handed.
Step 5: Runner Persistence
Write a marker (RUNNER_PERSISTENCE_TEST) from one job, then run a second unrelated job on the same runner and check whether the marker is still there. That is enough to prove or disprove cross-job contamination without building an actual persistence mechanism.
Together these steps demonstrate the full chain: a single low-privilege entry point escalating through an over-scoped credential into reach across every environment the pipeline touches.
Building Defenses
Each layer below closes one part of the attack surface.
Source control: Protected branches and mandatory reviews; CODEOWNERS enforcement; restricted permission to modify workflow files; MFA on all contributor accounts; short-lived tokens instead of long-lived personal access tokens.
Pipeline: Least-privilege GITHUB_TOKEN permissions per workflow; pinned third-party actions; isolated build runners; restricted network egress from build jobs.
On action pinning specifically — a mutable reference such as uses: some-org/some-action@v1 can point to different code over time, since the tag can be moved. Pinning to a commit instead:
uses: some-org/some-action@{full-commit-sha}means the code that runs is fixed and auditable. This does not make the action automatically safe; a malicious commit can still be pinned. It removes the ability for the reference to silently change underneath you — which is the integrity property that matters.
Dependencies: Private registries with explicit source configuration; enforced lockfiles and pinned versions; dependency provenance checks; controlled resolution so builds cannot silently fall back to public sources.
Secrets: Centralized secret management; short-lived credentials over static ones; environment-specific identities instead of one shared credential; no secrets committed to source or written to logs.
Artifacts: Immutable digest references instead of mutable tags; signature verification; provenance attestation; SBOMs as a supplement to — not a substitute for — integrity checks; verification happening at deployment time, not only at build time.
Monitoring: Alerting on workflow file changes; unusual credential use; anomalous runner behavior; unexpected artifact publication; deployment events outside normal patterns.
Conclusion
A CI/CD pipeline is not merely a tool that builds the application. It is part of the application's security boundary. It knows what code gets built, which dependencies get trusted, which credentials are available to it, which artifact gets released, and in many environments, which artifact reaches production.
If a pentest stops at the application, it may be testing the thing being protected while ignoring the system that is trusted to change it. The practical starting point for any team is the same short list: know exactly what your pipeline can do, give it the least access that lets it do its job, separate build trust from deployment trust, verify every artifact before it moves downstream, and log everything so you can reconstruct what happened when something slips through.
Connect with QuassrCyberTech Experts
Ready to strengthen your organization's security posture?
At QuassrCyberTech, our specialized cybersecurity solutions help organizations stay ahead of evolving risks. From Vulnerability Assessment and Penetration Testing (VAPT) to Security Operations Center (SOC) implementation, Red Team exercises, and Governance, Risk, and Compliance (GRC) frameworks, we provide the expertise and tools needed to secure your digital assets and infrastructure.
Contact us today to develop a customized security strategy for your organization.
References
- CISA / NSA - Defending Continuous Integration/Continuous Delivery (CI/CD) Environments
- GitHub - Security hardening for GitHub Actions
- OWASP - Top 10 CI/CD Security Risks
- SLSA - Supply-chain Levels for Software Artifacts
- MITRE ATT&CK - Supply Chain Compromise (T1195)
- Aqua Security - SolarWinds Attack: How the Supply Chain Was Compromised
- Checkmarx Security - Codecov Supply Chain Attack Explained
