Skip to content

Supply Chain Security in Azure DevOps: The Risks That Don't Make the Headlines

This page is part of a series on the Trivy supply chain compromise of March 2026. See also: the main article, the chain attack timeline and IOCs, and detecting the compromise in Azure DevOps.

Almost every analysis of the Trivy compromise in March 2026 was written with GitHub Actions in mind. The attack vectors, the mitigations, the tooling — all of it assumes you are running .github/workflows. If you are running Azure DevOps, you may have read those articles, nodded along, and then wondered how much of it actually applies to you.

The answer is: quite a bit, but not in the same way.

Azure Pipelines and GitHub Actions share the same fundamental problem — they both execute third-party or external code inside a privileged environment with access to your secrets and infrastructure. But the trust models differ, the attack surfaces differ, and the blind spots differ. This post works through the differences that matter.

Where GitHub Actions and Azure Pipelines diverge

The Trivy attack exploited a specific property of GitHub Actions: version tags are mutable. Anyone with write access to a repository can silently force-push a tag to point to a completely different commit. There is no lock, no notification, no audit trail that surfaces in the consuming workflow. Seventy-six of seventy-seven trivy-action version tags were rewritten this way, and every downstream pipeline that referenced them by tag started running the attacker's code on its next run.

Azure DevOps Pipelines does not have the ability to pin tasks to a SHA because it uses a marketplace where tasks must go through a controlled vetting process before being enabled. That is a genuine structural difference. Azure DevOps Marketplace tasks are versioned through a publisher approval process — a task author cannot silently swap out a published version the way a GitHub repository maintainer can swap a tag. In Azure DevOps you can pin to a very specific patch version number, which functions similarly to SHA pinning in GitHub Actions.

This does not mean Azure Pipelines is inherently safer. It means the attack surface is different.

Where the risks are equivalent or worse

Script injection via pipeline variables. Azure Pipelines has its own class of supply chain vulnerability that GitHub Actions does not. Legit Security Labs documented a remote code execution vulnerability in Azure Pipelines where attacker-controlled commit messages or other user-controlled variables could be injected into pipeline logging commands, overwriting pipeline variables and achieving full pipeline takeover — with access to all secrets available to the job. This pattern, pipeline variable injection via ##vso[] logging commands, is Azure Pipelines-specific and has no direct equivalent in GitHub Actions. Microsoft's own security guidance warns against relying on PATH in scripts and recommends using runtime parameters rather than variables to prevent argument injection.

Container images are fully shared risk. The compromised Trivy Docker images (aquasec/trivy:0.69.4, 0.69.5, 0.69.6) were just as dangerous when pulled from Azure Pipelines as from GitHub Actions. Whether you run docker pull aquasec/trivy:latest in a pipeline task or a bash script step makes no structural difference. The binary is the binary. If your Azure DevOps pipeline pulls container images from Docker Hub or any other registry, the registry supply chain risk is identical.

Microsoft Security DevOps (MSDO) is an open question. The MicrosoftSecurityDevOps@1 task in Azure Pipelines includes Trivy as one of its built-in scanners and distributes it via an internal NuGet feed rather than directly from Aqua Security's channels. A community issue filed on March 23 (microsoft/security-devops-azdevops#155) asked Microsoft explicitly whether the NuGet feed was confirmed unaffected, whether the package is built from source or repackaged from GitHub Releases, and whether integrity checks would have caught a compromised upstream binary. The issue was closed without a public answer. If you ran MicrosoftSecurityDevOps@1 with Trivy enabled during March 19–22 and cannot confirm the provenance of the binary that executed, treat the pipeline's secrets as potentially exposed.

Self-hosted agents carry the same filesystem risk. The Trivy malware performed an extensive sweep of the host filesystem when running on self-hosted runners, targeting SSH keys, cloud credentials, kubeconfigs, Terraform state, shell history and more. Self-hosted Azure DevOps agents run under the same conditions. If a compromised container image or a script with malicious code executes on a self-hosted agent, it has exactly the same access to the filesystem and environment variables as it would on a GitHub-hosted runner. The persistence mechanism (~/.config/systemd/user/sysmon.py) also lands regardless of which CI platform triggered the job.

Fork builds. Azure Pipelines by default builds pull requests from forks, and Microsoft's own documentation explicitly warns about the risks of this default behaviour. This is the Azure Pipelines equivalent of the pull_request_target misuse that enabled the initial Trivy intrusion. The initial attacker access that started the entire March 2026 campaign came from a misconfigured pull_request_target workflow on GitHub — the same conceptual mistake is possible in Azure Pipelines if fork builds are not properly isolated from secret-bearing jobs.

What Azure DevOps teams should actually do

Pin task versions explicitly. While you cannot pin to a SHA in Azure Pipelines the way you can in GitHub Actions, you can and should pin to a fully qualified patch version rather than using @latest or a major-only version reference. AzureRmWebAppDeployment@4 is not the same as [email protected]. The former can receive silent updates. The latter is fixed.

# Less safe — can receive silent updates
- task: AzureRmWebAppDeployment@4

# Better — pinned to a specific published version
- task: [email protected]

Microsoft also recommends disabling the ability to install and run tasks from the Marketplace at the organisation level, which gives greater control over which code actually executes in a pipeline.

Use workload identity federation instead of stored credentials. Microsoft's pipeline security guidance explicitly recommends using workload identity federation rather than service principals with stored secrets for Azure service connections, and scoping service connections to specific resource groups rather than subscriptions. This is the Azure Pipelines equivalent of OIDC in GitHub Actions — short-lived tokens issued per run rather than long-lived credentials sitting in pipeline variable groups.

Treat variable groups and library secrets with the same care as code. Pipeline variable groups in Azure DevOps are often where the most sensitive credentials live. Audit which pipelines have access to which variable groups, and whether that access is scoped by branch protection rules. A pipeline that only needs to read a version number for a build step should not have access to the same variable group as your production deployment credentials.

Restrict fork builds. Azure Pipelines' default behaviour is to allow fork builds, but secrets are not exposed to fork pipeline runs by default. Ensure that protection has not been disabled, and consider manually triggering fork builds rather than running them automatically. The pattern that enabled the initial Trivy breach — untrusted code running in a context that has secret access — is directly replicable in Azure Pipelines if fork isolation is compromised.

Audit container image provenance. Any pipeline step that pulls a container image by tag rather than by digest is exposed to the same class of risk as a GitHub Actions workflow pinned to a version tag. Review which registries your pipelines pull from, whether those registries were mirroring Docker Hub during the March exposure window, and whether your internal Artifactory, Azure Container Registry or Harbor instance has cached images that have not been re-verified since then.

Verify MSDO explicitly. If you run MicrosoftSecurityDevOps@1, open a support ticket with Microsoft and ask explicitly which Trivy version was distributed via the NuGet feed during March 19–22, and whether the feed builds from source or packages upstream binaries. That is a direct question that deserves a direct answer. Do not assume that Microsoft's internal distribution channel was automatically insulated from the upstream compromise.

The structural point

Azure DevOps has a more controlled task marketplace than GitHub Actions, and that is a real advantage. But it is a narrower advantage than it appears. Most of what makes CI/CD pipelines dangerous as an attack surface — secret access, container image pulls, external script execution, self-hosted agent filesystem exposure — is fully present in Azure Pipelines. The Trivy binary, the Docker images, and any npm or PyPI package installed as part of a build step carry the same risk regardless of whether the pipeline is defined in YAML in a GitHub repository or in an Azure DevOps project.

The tooling for hardening is different. The vocabulary is different. But the underlying question is the same: do you know what code is actually running in your pipeline, and do you know what it has access to?

For most teams running Azure DevOps today, the honest answer to both questions is "not entirely." That is the gap worth closing.

Sources: Microsoft Learn — Secure your Azure Pipelines, Legit Security Labs (Azure Pipelines RCE research), GitHub Issue microsoft/security-devops-azdevops#155, Microsoft Security Blog (Trivy compromise guidance), Docker (Trivy Docker Hub advisory), Aqua Security GHSA-69fq-xp46-6x23