Snyk can be wired into a CI/CD pipeline in three distinct ways, and teams often assume they're interchangeable: install the CLI, drop in a marketplace extension, or reference a prebuilt pipe. They are not the same mechanism. The generic snyk CLI is a standalone binary or npm package that you invoke yourself, parse the exit code from, and wire into whatever reporting your pipeline supports. The Azure Pipelines integration is a Marketplace extension that registers a native build task with typed inputs and a secure service connection. The Bitbucket Pipelines integration is a Docker-based "pipe" configured through YAML variables rather than shell flags. Each approach handles authentication, result surfacing, and build-gating differently, which changes how findings get triaged, how secrets are stored, and how much glue code a security team has to maintain. This post walks through the mechanical differences so you know what you're actually running.
What does the generic Snyk CLI workflow look like?
The generic CLI workflow is: install the binary, authenticate, run a scan command, and handle the exit code yourself. A typical setup pulls the CLI via npm install -g snyk, a standalone binary download, or a Docker image, then authenticates either interactively with snyk auth or non-interactively by setting a SNYK_TOKEN environment variable. From there, commands like snyk test, snyk container test, or snyk iac test run against the manifest, image, or Terraform/CloudFormation files in the working directory, and snyk monitor separately pushes a snapshot to Snyk's platform for ongoing tracking. The CLI communicates pass/fail through process exit codes: 0 means no vulnerabilities found, 1 means vulnerabilities were found at or above the configured threshold, and 2 means the scan itself errored out. Nothing about this is CI-specific — the same commands run identically on a laptop, in Jenkins, in GitHub Actions, or in Azure Pipelines and Bitbucket Pipelines using their generic "run a script" step types. Any reporting beyond the exit code (SARIF upload, PR comments, build summaries) is something you build yourself.
How does the Snyk Azure Pipelines extension change the integration mechanism?
It replaces a shell command with a first-class, typed build task installed from the Visual Studio Marketplace. Rather than adding a bash/PowerShell step that calls the CLI, teams install the "Snyk Security Scan" extension at the Azure DevOps organization level, which then exposes a task — referenced in YAML as SnykSecurityScan@1 or added visually in the classic pipeline editor — with structured inputs instead of raw CLI flags: a test type selector (open source, container, or IaC), a severity threshold dropdown, and a boolean for whether a failed scan should fail the build. Authentication is handled through an Azure DevOps service connection, a credential object stored in Azure DevOps' secured endpoint store rather than a plain pipeline variable, which the task references by name so the token never appears in YAML or logs. Because it's a registered task, Azure Pipelines can also surface its output in the native build summary UI, giving reviewers a scan result view without anyone writing custom log-parsing or artifact-publishing steps.
How does the Snyk Bitbucket Pipe differ from both the CLI and the Azure extension?
It packages the CLI inside a versioned Docker image invoked declaratively from bitbucket-pipelines.yml, configured through named variables rather than a UI form. Bitbucket's pipeline model doesn't have a marketplace-installed task system like Azure DevOps; instead, Atlassian's "Pipes" catalog offers a shorthand for running a specific container with a specific interface. A step referencing pipe: snyk/snyk-scan:<version> pulls that image from its registry and runs it inside the pipeline's own Docker-based build environment, with behavior configured through a variables: block — for example SNYK_TOKEN, LANGUAGE, SEVERITY_THRESHOLD, or a MONITOR flag — instead of CLI arguments. There's no dedicated results tab the way there is in Azure Pipelines' build summary; output is written to the step's console log and to Bitbucket's pipe-specific report annotations on the pipeline run page. Mechanically, this sits between the raw CLI and the Azure extension: it's more structured than a bare shell command (fixed image, named config, pinned version) but lighter-weight than a registered task with typed UI inputs.
How do authentication and credential handling differ across the three methods?
Each method stores the Snyk API token in a different trust boundary, which matters for audit and rotation. With the generic CLI, the token is typically set as a plain environment variable or repository/pipeline secret — for example a masked variable in Bitbucket's repository settings, or a pipeline variable in Azure DevOps — and it's the pipeline author's responsibility to make sure it's marked secret and never echoed to logs. The Azure Pipelines extension moves the token into a service connection, a distinct object in Azure DevOps' project settings with its own permission model (which pipelines and which stages can use it), separate from ordinary pipeline variables. The Bitbucket pipe still relies on a Bitbucket-managed secured repository or workspace variable referenced by name (commonly SNYK_TOKEN) inside the pipe's variables block — closer to the CLI's model than to Azure's dedicated connection object, since Bitbucket doesn't have a separate service-connection abstraction. In practice this means an Azure DevOps environment gives admins one more layer of credential-scoping than either the raw CLI or the Bitbucket pipe.
How does build-breaking (gating) logic differ across these integrations?
Gating is expressed as an exit code in the CLI, a typed task input in Azure Pipelines, and a pipe variable in Bitbucket — three different places to look when a build unexpectedly passes or fails. With the raw CLI, a pipeline fails a stage because snyk test returns exit code 1, and any nuance (fail only on high/critical, ignore certain paths) is enforced through CLI flags like --severity-threshold=high combined with the shell's own exit-code handling. The Azure Pipelines task exposes this as explicit fields in the task configuration — a severity threshold dropdown and a "fail on issues" toggle — so the gating policy is visible in the pipeline UI without reading YAML or scripts. The Bitbucket pipe expresses the same policy through a SEVERITY_THRESHOLD variable inside the pipe block, which under the hood still resolves to the same CLI flag being passed into the container. The net effect is the same enforcement, but troubleshooting why a build did or didn't break requires checking a different location depending on which integration path was used — a real source of confusion when teams run some repos on Azure DevOps and others on Bitbucket.
How Safeguard Helps
Whichever mechanism a team uses to run Snyk — a bare CLI step, the Azure Pipelines extension, or the Bitbucket pipe — the underlying question for a security or platform team is the same: which pipelines actually ran a scan, with which threshold, using which credential, and did that credential and container come from a trusted source. Safeguard focuses on that layer of the problem rather than the scanner itself. It gives teams a single view across CI systems of what scanning steps exist in each pipeline definition, what secrets and service connections those steps depend on, and whether the images or extensions being pulled in (like a pipe's Docker image or a Marketplace task) are pinned to known-good versions rather than floating tags. Because gating logic and credential storage live in different places across Azure Pipelines, Bitbucket Pipelines, and ad hoc CLI usage, drift is easy to introduce silently — a threshold changed in one pipeline's YAML, a service connection reused more broadly than intended, a pipe left on an unpinned tag. Safeguard's supply chain visibility is built to catch that kind of drift across the whole CI/CD estate, independent of which specific tool or integration style produced the underlying scan results, so security teams can verify pipeline configuration and provenance instead of trusting that every repo's setup matches the one they last reviewed.