Back to glossary

DevSecOps

DevSecOps builds security into the CI/CD pipeline: checks run as automated jobs on every change, and a failing check blocks the merge or deploy.

Also known as Development, Security, and Operations, SecDevOps, DevOpsSec, Secure DevOps

DevSecOps (development, security, and operations, sometimes written SecDevOps) is the practice of enforcing security inside the automated pipeline that turns a commit into a release. Security checks run as pipeline jobs, policies live in version control, and a failing check blocks the merge or deploy. It exists because teams that ship daily cannot wait on manual review.

The mechanism is simple: anything the pipeline can check automatically, it checks on every change, and the result gates the next stage. What most DevSecOps material leaves out is that the pipeline is also the most valuable target in the software supply chain, and every security tool added to it runs with the pipeline's credentials. In the March 2026 Trivy GitHub Actions compromise, attackers force-pushed 75 of 76 version tags of aquasecurity/trivy-action, the official GitHub Action for the Trivy vulnerability scanner. More than 10,000 workflow files referenced it. A DevSecOps control became an infostealer. Our position: judge a DevSecOps program by what code its privileged jobs execute, and whether anyone knows before they run, not by how many scanners it has added.

What is DevSecOps?

#

DevSecOps is DevOps with security enforced by the same automation that builds, tests, and ships the code. To see why it emerged, start with what DevOps changed.

DevOps collapsed the hand-off between developers and operators into a pipeline. A commit triggers continuous integration (CI), which builds and tests it; continuous delivery (CD) packages and deploys it. The pipeline acts on the team's behalf, so it holds standing authority: a token that can push to the default branch, credentials that can publish to a registry, keys that can deploy to production. That design is what lets a team ship many times a day.

Security review did not fit that loop. A penetration test or an architecture review that takes two weeks cannot gate a change that ships in twenty minutes, so the review either became the bottleneck or got skipped. By early 2012, Gartner had published a report titled "DevOpsSec: Creating the Agile Triangle," arguing that security had to join the loop rather than sit at its end (SiliconANGLE, April 2012). NIST later formalized the idea for microservices in SP 800-204C, published March 8, 2022.

Mechanically, DevSecOps rests on three moves:

  1. Checks become pipeline jobs. Static analysis, dependency analysis, secret detection, and infrastructure scanning run on every pull request and build, not on a release candidate.
  2. Policy becomes code. Rules such as "no critical findings in production images" or "every deploy must be signed" live in the repository and are evaluated by the pipeline, which is policy as code.
  3. Findings go to the author. A failed check lands in the pull request of the person who introduced the problem, while the change is still in their head. This is the shift left half of DevSecOps.

The trade-off is the one NIST names in its 2025 DevSecOps practice guide: the approach "relies on automated production flows, which can quickly propagate security risks directly into production" (NIST SP 1800-44, initial preliminary draft). Automation that ships a fix in twenty minutes ships a compromise in twenty minutes too.

What is the difference between DevOps and DevSecOps?

#

The difference between DevOps and DevSecOps is where security decisions are made and enforced. DevOps automates build, test, and deploy; DevSecOps adds security checks to that automation and lets them block a change.

DimensionDevOpsDevSecOps
GoalShip changes quickly and reliablyShip quickly with security enforced by the pipeline
When security runsBefore release, by a separate teamOn every commit, pull request, build, and deploy
Who fixes a findingSecurity files a ticket against the releaseThe author of the change, in the pull request
Where policy livesDocuments and review checklistsVersion-controlled rules the pipeline evaluates
What blocks a releaseA manual sign-offA failing automated check
Typical failure modeReview becomes the bottleneck and gets bypassedAlert fatigue, and the pipeline itself becomes the attack surface

Verdict: any team that ships more often than its security team can review should run DevSecOps, which today means nearly every team. A manual gate still earns its place in slow, regulated release trains such as firmware or medical devices, but as an addition to the automated checks, never a replacement. The last row is the one this article is about.

Anatomy of a DevSecOps pipeline

#

A DevSecOps pipeline places one or more checks at each point where code changes hands. Each check is good at a specific class of problem and blind to others. The table maps the usual control at each stage to what it catches and what it misses.

StageTypical DevSecOps controlCatchesMisses
CodeIDE linters, pre-commit secret detectionInsecure patterns, committed secretsMalicious IDE extensions, including compromised security extensions
Pull requestSAST, dependency diff, secret scanning, reviewInjection flaws, new dependencies with known CVEs, leaked tokensMalicious packages, which carry no CVE
BuildDependency install, container and IaC scanning, SBOM generationVulnerable base images, misconfigurationsInstall scripts that run before any scanner reads them, and scanner actions that run with build credentials
TestDAST, integration testsRuntime flaws in your applicationDependency behavior outside tested paths
ReleaseSigning and provenanceTampering after the buildCompromise inside the build: provenance attests a poisoned build as faithfully as a clean one
Deploy and operateAdmission policies, runtime monitoringUnsigned images, configuration driftCredentials already exfiltrated upstream

Read the right-hand column top to bottom and a pattern appears. The controls were designed to find flaws in code your team wrote or configured. The attacks that defined 2025 and 2026 arrived as code your team did not write, executing inside jobs your team trusts. As we put it in our guide to hardening GitHub Actions against outside threats: "Scanning tells you what is in the tree. The install is where code executes."

What tools are used in DevSecOps?

#

DevSecOps tools fall into a handful of categories, each run as a pipeline step:

  • Static application security testing (SAST) reads your source for injection, unsafe deserialization, and similar flaws.
  • Software composition analysis (SCA) inventories open source dependencies and matches them against vulnerability databases.
  • Secret scanning finds credentials in commits and history.
  • Container and infrastructure-as-code scanners check images, Terraform, Kubernetes manifests, and CloudFormation.
  • Dynamic application security testing (DAST) probes a running build.
  • Policy engines evaluate rules at merge and deploy time.

Every one of these tools is third-party code that the pipeline downloads and executes. Most are distributed exactly the way attackers like: as a GitHub Action referenced by a mutable tag, a container image referenced by latest, or an IDE extension that auto-updates. And by design they read the most sensitive material a repository has: source, infrastructure definitions, and often secrets. That combination is why TeamPCP's campaign against security tools went after scanners.

So the useful question about a DevSecOps tool is not only what it detects. It is what it can reach. Four questions cover most of it:

  1. What credentials does the job running it hold? A filesystem scanner needs to read the checkout. It does not need a publish token, a cloud key, or id-token: write.
  2. How is it referenced? A tag can move. A full commit SHA or an image digest cannot.
  3. Where does it send data? Most scanners download a vulnerability database. Few need to upload anything, so the job's egress allowlist can be short.
  4. Does it update itself? IDE extensions and latest image tags change under you without a pull request.

Applied to a scanner, those answers produce a job like this one: no secrets, a read-only token, no persisted git credentials, and every action pinned to a commit.

YAML
jobs:
  scan:
    runs-on: ubuntu-latest
    permissions:
      contents: read          # no write token, no id-token
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          persist-credentials: false
      - uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # 0.35.0
        with:
          scan-type: fs
          format: sarif
          output: trivy.sarif

Isolation does not make a compromised scanner safe. It makes the compromise far less useful: the scanner can still read your source and lie about the results, but it has no secret to steal and no token to push with. The SHAs above resolve to v7.0.1 and 0.35.0 as of November 5, 2026. 0.35.0 is the one trivy-action tag the March 2026 attacker left untouched. It is also the only unprefixed version tag left in the repository; the others now exist only with a v prefix, so a workflow still referencing @0.33.0 no longer resolves.

When the security tool is the payload

#

The Trivy compromise is the clearest illustration of the DevSecOps blind spot, because the malicious code ran inside the security step itself. Each poisoned tag pointed to a new commit whose entrypoint.sh was 204 lines long, according to our analysis of the trivy-action tag compromise.

Figure 1 shows a scan job with four steps: checkout, npm ci, aquasecurity/trivy-action@0.33.0, and a SARIF upload. Every step can read the job's GITHUB_TOKEN, repository secrets, cloud keys, SSH keys, and Kubernetes tokens. The scanner's entrypoint.sh is expanded beside it. Lines 4 to 36 collect secrets from runner memory and the filesystem, lines 39 to 68 encrypt them, and lines 70 to 105 exfiltrate them and clean up. Lines 106 to 204 are the unmodified Trivy scan, whose normal output and passing job are all the developer sees.

The stealer read runner process environments from /proc/<pid>/environ. On GitHub-hosted runners, where sudo needs no password, it dumped the memory of the Runner.Worker process and searched it for the pattern GitHub uses to hold secrets. It encrypted what it found with AES-256-CBC, wrapped the key with RSA-4096, and posted it to scan.aquasecurtiy[.]org, a typosquat of Aqua Security's domain. Then the real scan ran and printed normal results. The pipeline stayed green.

The forged commits copied each original commit's author, timestamps, and message, so git log looked normal. Three details gave them away: the commits were unsigned where the originals carried GitHub's web-flow signature, each claimed a 2021 or 2022 date while its parent was dated March 2026, and each touched only entrypoint.sh. GitHub's releases page even showed an "Immutable" badge next to the poisoned tags.

Trivy's official advisory, CVE-2026-33634, puts the trivy-action exposure at roughly 12 hours, from about 17:43 UTC on March 19 to 05:40 UTC on March 20, 2026. The root cause was not a GitHub flaw. It was residual access from an earlier compromise of Trivy's own CI: the maintainers wrote that "credential rotation was performed but was not atomic." Our scanner began flagging the malicious actions at 19:15 UTC, roughly 90 minutes after the first tag moved, and generated 182 threat feed entries for the campaign. A pipeline that ran in that first window was already exposed.

Trivy was not the only security tool hit. Figure 2 and the table below it reconstruct the 2026 sequence from the advisory and our coverage, mapped to the channel through which each tool reached a pipeline.

Date (2026)EventDistribution channel
Late FebruaryAn attacker exploits a GitHub Actions workflow in Trivy's repository and steals credentials; disclosed March 1CI pipeline
February 27 to 28Trivy VS Code extension 1.8.12 and 1.8.13 on Open VSX launch local AI coding agents in permissive modes to inventory credentialsIDE extension
March 19 to 2075 of 76 trivy-action tags and every setup-trivy tag repointed; Trivy v0.69.4 binary and images published with the stealer; the binary was live about three hoursGitHub Actions, release binaries, container images
March 22 to 23Docker Hub images 0.69.5 and 0.69.6 carry the same payloadContainer registry
By March 24TeamPCP claims Checkmarx KICS GitHub Actions, Open VSX extensions, and a trojanized LiteLLM release on PyPIActions, IDE extensions, PyPI
April 22Malicious images in the official checkmarx/kics Docker Hub repository, plus Checkmarx VS Code extensions that fetch and run a credential stealerContainer registry, IDE extensions
October 8GhostAction commits a workflow named security-audit.yml to 346 repositoriesStolen maintainer credentials

Two of the targets were scanners: Trivy and Checkmarx's KICS. The Checkmarx KICS compromise modified the bundled KICS binary to encrypt and exfiltrate an uncensored scan report, which for an infrastructure-as-code scanner means the credentials sitting in Terraform and Kubernetes files. The last row needed no compromised tool at all. The GhostAction wave used stolen maintainer accounts to commit a workflow named security-audit.yml, with commit messages like "Add security audit workflow." It posted the repository's Actions secrets, plus cloud, AI service, and other API keys found anywhere in its git history, to a hardcoded IP address. In a team that has absorbed DevSecOps culture, a commit that adds a security audit is the kind nobody questions.

None of this is new in kind. A year earlier, the tj-actions/changed-files compromise repointed tags v1 through v45.0.7 on an action used by more than 23,000 repositories. What changed in 2026 is that attackers aimed at the tools teams added specifically to be safer.

Why do DevSecOps pipelines miss supply chain attacks?

#

DevSecOps pipelines miss supply chain attacks because their checks look for known flaws, while supply chain attacks deliver working malicious code. A malicious package gets no CVE, because it is behaving exactly as its author intended. The CVE and NVD pipeline is also under strain, so even for real vulnerabilities the feed most scanners match against is not a complete source of truth.

The second gap is ordering. In a typical build job, dependency installation runs before any scanner step, and package managers have historically run install scripts during that install. We ran a three-dependency install under npm 12.2.2 on November 5, 2026, to show two questions a pipeline needs answered:

Plain text
$ npm install esbuild@0.25.0 sharp@0.34.3 left-pad@1.3.0
added 14 packages, and audited 15 packages in 1s
...
1 high severity vulnerability
...
npm warn install-scripts 2 packages had install scripts blocked because they are not covered by allowScripts:
npm warn install-scripts   esbuild@0.25.0 (postinstall: node install.js)
npm warn install-scripts   sharp@0.34.3 (install: node install/check.js)

The audit line answers "which installed packages have known advisories": one, in sharp's bundled native libraries. The install-scripts lines answer a different question: "which packages would have executed code on this runner during install." That answer has nothing to do with CVEs. Under npm 10, which ships with Node 22, both scripts would have run before the next step started, with the job's environment in reach. npm v12 made that execution opt-in when it shipped in July 2026 with install scripts off by default; pnpm made the same change in version 10. Default-on install scripts were a design mistake, and the fix is correct. It is not complete. Code can still run at import time or during tests, and an approval written by package name rather than pinned to a version carries over to the next release, compromised or not. npm 12 pins approvals to the exact version by default; we confirmed npm install-scripts approve esbuild writes "esbuild@0.25.0": true.

The third gap is noise. A pipeline that fails the build on every high-severity CVE trains developers to override it. Most CVE alerts describe code paths the application never calls, so reachability, not severity score, should decide what blocks a merge. A gate developers trust is worth more than a gate that fires constantly.

The fourth gap is the pipeline's own dependencies. OpenAI's macOS app-signing workflow ran a compromised axios release on March 31, 2026, during the axios compromise. OpenAI's disclosure traced the root cause to a workflow that "used a floating tag, as opposed to a specific commit hash, and did not have a configured minimumReleaseAge." Both are pipeline configuration, not application code, and neither is what a typical DevSecOps scanner checks.

Securing the pipeline that secures the code

#

The controls that close these gaps treat the pipeline as a production system that executes third-party code by design, which is the core of CI/CD security. Each one has a cost and a limit.

  • Separate the jobs that run untrusted code from the jobs that hold credentials. Tests, builds, and scanners run without secrets; publishing runs in its own job behind a gated environment. This costs pipeline complexity, and the artifact passed between jobs becomes shared state that must be validated.
  • Pin what each job executes. Use a workflow lockfile where your platform supports one, a full commit SHA elsewhere, and image digests for containers. Pinning decides which code runs, not whether it is safe: a SHA pinned to a malicious commit is reproducibly malicious.
  • Turn install-time execution off and slow intake down. Keep install scripts disabled with an explicit allowlist, and add a dependency cooldown so a version must be public for some days before the pipeline will resolve it. Most malicious versions are removed within hours. A cooldown delays legitimate security fixes by the same window.
  • Analyze dependencies and actions for behavior, not just CVEs. In October 2025 we added GitHub Actions scanning, which resolves each action tag to the commit it points to at scan time and statically analyzes the action's bundled JavaScript for malicious, obfuscated, and network-active code. It flags a moved tag on the next scan, not at the moment a runner resolves it, so it narrows the window rather than closing it.
  • Verify provenance downstream, but do not stop there. Provenance proves where an artifact was built. It cannot prove what ran during the build, and attackers who hold a maintainer's credentials publish through legitimate pipelines with valid provenance.

None of these controls answers the hardest case. In the October 2026 LiteLLM compromise, the attacker held a GitHub organization owner's token. Eleven days before the malicious release, they pushed secret-stealing workflows to throwaway branches and collected, among other things, the API key for the project's secret-scanning service. GhostAction worked the same way, through stolen maintainer accounts. Neither needed an injection or a retagged action. The attackers authenticated, and the pipeline did what it was told. DevSecOps automated the checks a security team used to run by hand. It has not yet automated the question a security team would ask first: what is this job about to execute, who changed it, and why does it need that credential?