Safeguard
Product•

Add Safeguard to Bitbucket Pipelines: SBOM, Scan and Policy Gate

Add one Bitbucket Pipelines step that runs safeguard ci scan to build an SBOM, scan dependencies and fail the build on a severity or a policy gate.

Safeguard Security Team
40 min read

To add Safeguard to Bitbucket Pipelines, create a Safeguard secret key with the scans:trigger and scans:read scopes, store it as a secured repository variable named SAFEGUARD_API_KEY, and add one step that installs the Safeguard CLI and runs safeguard ci scan --wait --fail-on critical. Each push and pull request then becomes a scanned version of a Safeguard project with an SBOM and dependency findings. Add --fail-on-gate when you want your organization's policies, not just a severity, to decide whether the build passes.

There is no Pipe and no Docker image to install beyond a small base image. The whole integration is a plain shell step, which is why it is easy to read, easy to pin and easy to remove.

This guide follows Safeguard's docs, explains what each flag does to the build, covers the policy gate in detail and lists the limits. Every YAML block and command below is copied from docs.safeguard.sh as fetched on 25 October 2026. We have not run these snippets in a live Bitbucket workspace for this post, so test them in a branch before you make the step required.

Key takeaways

  • The Safeguard CLI (GA) runs inside Bitbucket Pipelines as one step. The documented step uses alpine:3.20, installs the CLI with the official installer and runs safeguard ci scan.
  • SBOM generation (GA) and SCA with dependency vulnerability matching (GA) happen on every scan. With the default mode, Safeguard also runs SAST (GA) on the connected repository.
  • --fail-on critical fails the step on a dependency vulnerability at or above that severity. --fail-on-gate fails it when a policy gate is not Passed or Warning.
  • Policies and gates (GA) use three actions, Block, Warn and Notify. Approval of a known risk goes through policy exceptions, not through the pipeline.
  • --local scans on the Bitbucket runner, so only results leave the build. It covers the SBOM and dependency vulnerabilities, not SAST.
  • Autonomous remediation (GA) can open fix pull requests in Bitbucket when the integration has write scopes. It ships switched off, with every category set to Manual.
  • Attestation and signing (GA) records live on the Attestation tab in the app. The docs say there is no CLI command for signing yet, so signing is not a Bitbucket Pipelines step today.

Why put a supply chain check in Bitbucket Pipelines at all

Most teams already have a pipeline that builds, tests and deploys. What they often lack is a step that answers, on every change, what is in this build, whether any of it carries a known vulnerability, and whether it meets the rules the team agreed on. Without that step the answers arrive late: an auditor asks for the SBOM of a release nobody kept, or a policy sits in a wiki that no build reads.

The standards that shape software supply chain programs keep pointing at the build as the place to answer these questions.

NIST's Secure Software Development Framework, SP 800-218 version 1.1, is the clearest example. Practice PO.4.1 asks organizations to define criteria for software security checks and track them throughout the SDLC. PO.4.2 asks them to implement mechanisms that gather the information needed to support those criteria, and one of its examples is to automate decision making using the criteria. PW.4.4 asks you to verify that acquired open source and other third party components comply with your requirements throughout their life cycles, and its second example is to build automatic detection of known vulnerabilities into the toolchain. PS.3.2 asks you to collect, safeguard, maintain and share provenance data for all components of each release, and it names the SBOM as one way to do that. RV.1.1 covers gathering information about potential vulnerabilities in the software you release and the components it uses.

A step that generates an SBOM, matches it against vulnerability data and enforces a written policy maps onto most of those practices. It does not satisfy them alone; the table near the end shows what you still own.

Executive Order 14028 set the same direction for software sold to the US federal government. Section 4(e) lists the practices the secure software guidance must include, and item (vii) is providing a purchaser an SBOM for each product, directly or by publishing it on a public website. NIST's own mapping table in SP 800-218 lines up 4(e)(vii) with practice PS.3.2.

OMB Memorandum M-22-18 then turned the guidance into procurement terms. It says an agency may require an SBOM in solicitation requirements, based on the criticality of the software, and that SBOMs must be generated in one of the data formats defined in the NTIA minimum elements report or successor CISA guidance. It also says agencies may require other artifacts, such as the output of automated tools that check for known or potential vulnerabilities.

The NTIA report, "The Minimum Elements for a Software Bill of Materials," lists seven baseline data fields: supplier, component name, version of the component, other unique identifiers, dependency relationship, author of SBOM data, and timestamp. CISA now hosts the SBOM program and has published updates to the minimum elements, so check its SBOM page for the version your customers cite.

The NTIA report names three formats; the two most build tools emit are SPDX and CycloneDX. SPDX is an international open standard, published as ISO/IEC 5962:2021, with version 3.0 now available. CycloneDX is developed by the OWASP Foundation and standardised by Ecma International as ECMA-424.

If you sell into regulated industries or the public sector, these documents are why buyers ask for SBOMs per release and evidence that vulnerability checks ran.

For background on SBOM content and formats in Safeguard, see SBOM generation and analysis in Safeguard. This post stays on the Bitbucket Pipelines wiring.

How the Safeguard step works in Bitbucket Pipelines

Safeguard's CI/CD integration docs describe a deliberately small design. One step in your pipeline runs safeguard ci scan. Each push becomes a new version of its Safeguard project, named after the branch and commit, for example main-a1b2c3d. The step can fail the build when the scan finds vulnerabilities at or above a severity you choose, or when a policy gate fails.

The docs are explicit that there is no plugin, Action or Pipe to install. Every CI system runs the CLI in a plain shell step on a Linux or macOS runner with bash and curl. For Bitbucket, the docs' step uses the alpine:3.20 image, adds bash, curl and CA certificates with apk, installs the CLI, and runs the scan.

The first run creates the Safeguard project. Nothing else needs to exist in Safeguard before you push.

What the scan produces

Each run of safeguard ci scan produces a project version in Safeguard. In the default mode, the docs list three outputs: SAST, SBOM, and dependency vulnerabilities. In the --local mode, the outputs are the SBOM and dependency vulnerabilities.

Once a version exists, the Explore SBOM docs show where to look in the app: the Info, Dependencies, Vulnerabilities, Suppliers and Licenses and Findings tabs, among others. The SBOM can be exported from the app in SPDX (JSON, XML, RDF) and CycloneDX (JSON, XML), plus spreadsheet, PDF and raw JSON exports.

Where the scan runs

This is the most important choice in the setup, and the docs give it its own table.

ModeCommandWhat it needsWhat it scans
On Safeguard (default)safeguard ci scanThe repository connected as an integration, or public on bitbucket.orgSAST, SBOM, dependency vulnerabilities
On the runnersafeguard ci scan --localA checkout in the job; no integrationSBOM, dependency vulnerabilities

In the default mode, the CLI asks Safeguard to scan the commit, and Safeguard clones the repository through the Bitbucket integration. In the local mode, the CLI reads the checkout that Bitbucket Pipelines already cloned for the step, and the docs say only the results leave the runner. The local mode reads dependencies from lockfiles and manifests such as package-lock.json, go.sum, requirements.txt and pom.xml. For the full list your CLI build reads, check the language and ecosystem map and the CLI support matrix in the docs.

There is one Bitbucket specific wrinkle. At the time of writing, Safeguard's Bitbucket integration page carries a notice that Bitbucket Cloud retired two API endpoints the private connection uses to check credentials and list workspaces. Until Safeguard ships an update, Verify Credentials on the Private tab can fail with "Invalid credentials" even when the token is correct. Public repositories are not affected. If you are wiring up a private repository today and the connection will not verify, --local is the documented route that needs no integration.

What you need before you start

ItemWhere it comes fromNotes
A Safeguard account and workspaceSafeguard appThe free entry tier covers one existing repo plus one open source repo from GitHub, GitLab or Bitbucket, one project per month, SCA only
A secret key with scans:trigger and scans:readSafeguard, Settings, DeveloperYou can lock the key to one project
Permission to edit repository variablesBitbucket, Repository settingsA workspace variable also works
A bitbucket-pipelines.yml you can changeYour repositoryBitbucket allows one definitions and one pipelines key per file
Optional: a Bitbucket integration in SafeguardSafeguard, Integrations, Bitbucket cardNeeded for the default mode on private repositories; not needed for --local
Optional: policies and gates with outcomes onSafeguard, PoliciesNeeded for --fail-on-gate

If you have not connected Bitbucket to Safeguard before, the integration docs list the Atlassian API token scopes it needs. The least privilege set for scanning is read:user:bitbucket, read:workspace:bitbucket and read:repository:bitbucket. The docs say app passwords no longer work, because Bitbucket stopped creating them on 9 September 2025 and removed them on 28 July 2026, and that repository, project and workspace access tokens do not work in the dialog because it always sends a username. Our older walkthrough, Connect Safeguard to Bitbucket, predates those changes, so follow the current docs page for the credential steps.

Step by step: add the Safeguard step to Bitbucket Pipelines

Step 1. Create the secret key in Safeguard

In the Safeguard app, open Settings, then Developer, and create a secret key with two scopes: scans:trigger and scans:read. The CI/CD docs say you can lock the key to one project. Do that when one repository maps to one project, because a leaked key then reaches one project's scans and nothing else.

The CLI usage docs point out that the interactive CLI has no API key variable: a person signs in once with a device code. The CI subcommand is the exception. safeguard ci scan reads SAFEGUARD_API_KEY from the environment, which is what lets an unattended build authenticate.

If you are not an admin in your Safeguard workspace, the container image docs note that others ask an admin for the key. The same applies here.

Step 2. Store the key as a secured Bitbucket variable

In Bitbucket, go to Repository settings, then Repository variables, and add a variable named SAFEGUARD_API_KEY with Secured switched on. Safeguard's docs say a workspace variable works too.

Atlassian's documentation explains what Secured does. The value is hidden in build logs, matching values are masked, and a secured variable cannot be edited afterwards, only replaced or deleted. Workspace variables sit under Workspace settings, Pipelines, and are shared by every repository in the workspace, so prefer a repository variable unless you really want one key for many repositories. For more on variable scopes and why secured values matter, see Bitbucket Pipelines secured variables and OIDC.

Step 3. Decide where the scan runs

Pick the mode from the table above before you write YAML.

  • Use the default mode if the repository is public on bitbucket.org, or if it is already connected through the Bitbucket integration in Safeguard. You get SAST as well as the SBOM and dependency findings.
  • Use --local if the repository is private and not connected, if your private connection is hitting the credential notice above, or if your policy says source code must not leave the build environment. You get the SBOM and dependency findings.

You can change this later by adding or removing one flag.

Step 4. Add the step to bitbucket-pipelines.yml

This is the step from Safeguard's Bitbucket Pipelines docs, as published:

definitions:
  steps:
    - step: &safeguard-scan
        name: Safeguard scan
        image: alpine:3.20
        script:
          - apk add --no-cache bash curl ca-certificates
          - curl -fsSL https://cli.safeguard.sh/install | SAFEGUARD_INSTALL_DIR=/usr/local/bin bash
          - safeguard ci scan --wait --fail-on critical
pipelines:
  branches:
    main:
      - step: *safeguard-scan
  pull-requests:
    '**':
      - step: *safeguard-scan

What each line does:

  • definitions.steps defines a reusable step and gives it the YAML anchor &safeguard-scan. Atlassian's docs describe anchors (&) as a named chunk of configuration and aliases (*) as references to it, so you write the step once and use it in several pipelines.
  • image: alpine:3.20 runs the step in a small Alpine container. The docs say no Pipe or Docker image is needed beyond this.
  • apk add --no-cache bash curl ca-certificates installs what the installer needs.
  • The curl ... | SAFEGUARD_INSTALL_DIR=/usr/local/bin bash line runs the official installer and puts the safeguard binary in /usr/local/bin, which is already on the PATH.
  • safeguard ci scan --wait --fail-on critical starts the scan, waits for it, and fails the step if a dependency vulnerability at critical severity is found.
  • pipelines.branches.main runs the step on every push to main.
  • pipelines.pull-requests.'**' runs it on every pull request.

If you already have a bitbucket-pipelines.yml, do not paste a second definitions or pipelines key underneath it. The docs' troubleshooting section says Bitbucket allows only one of each, so merge the step into the keys you already have.

Step 5. Choose which branches and pull requests are scanned

Under pipelines.branches, add one entry per branch. Glob patterns work, so 'release/*' covers every release branch.

The pull request block needs more care. Safeguard's docs say pull-requests: '**' scans every pull request whatever its target, and that Bitbucket matches this pattern against the source branch, so it cannot filter by target branch. Atlassian's pipeline start conditions page says the same thing in its own words: the branch named under pull-requests must be the source branch of the pull request. If you only want pull requests from feature branches, write a source branch pattern such as 'feature/*'. If you need target branch logic, Atlassian documents a separate triggers property with conditions; Safeguard's docs do not show it, so test it before you depend on it.

Two Atlassian details matter. A pull request pipeline merges the destination branch into the working branch before it runs, and stops if that merge fails. And pipelines are not triggered by pull requests from a forked repository. Safeguard's docs add that secured variables are not passed to fork pull requests, so even where a fork build does run, the Safeguard step will not have its key.

To scan pushes only, delete the pull-requests block. On a pull request, Safeguard's docs say the source branch is scanned and its commit names the version.

Step 6. Push and read the first result

Commit the file and push. The first run creates the Safeguard project. In the Bitbucket build log you will see the installer, then the scan, then either a passing step or a failing one with the reason.

In the Safeguard app, the project's version is named after the branch and commit, so a push to main at commit a1b2c3d appears as main-a1b2c3d. Open it to see the SBOM, the dependency tree and the findings. The Projects table shows status, findings by severity and the vulnerability count for each project.

The docs' default --timeout for --wait is 30 minutes, and the step fails if the scan takes longer than that.

Step 7. Set the severity threshold you actually want

--fail-on critical is a safe default for a first rollout because it only stops the build for the most severe dependency findings. The docs list the other thresholds: --fail-on high, medium or low. Each one fails at or above that severity.

To report without failing, drop --fail-on entirely. The scan still runs and the results still appear in Safeguard; the step simply passes. That is the right setting for the first week in a repository with a long backlog, so you can see the size of the problem before you block anyone.

The docs are clear that SAST findings are scanned and shown in Safeguard but are not part of --fail-on yet. --fail-on is about dependency vulnerabilities.

Step 8. Add a policy gate

A severity threshold is a blunt rule. Most teams want something closer to their actual policy: no critical vulnerabilities, no licenses outside an approved list, no abandoned packages, with documented exceptions. That is what Safeguard's policies and gates are for, and --fail-on-gate is how the pipeline respects them.

The documented flag is --fail-on-gate <gate>, where the value is the gate's name, its id, or all. Used with --wait, it fails the step when the gate is not Passed or Warning. The policy outcomes docs describe the exit behaviour precisely:

  • exit 0 when the gate is Passed or Warning, with warnings and notified policies printed;
  • non-zero when the gate is Failed, printing the Block policies that failed;
  • non-zero when the gate is Error or Not evaluated, or when its policies were not evaluated before --timeout, with a message that says why.

--fail-on keeps working and can be combined with --fail-on-gate. A combined script line, assembled from the documented flags, looks like this:

          - safeguard ci scan --wait --fail-on critical --fail-on-gate all

We have not run that combination. Check safeguard ci scan --help on your CLI version before relying on it in a required build.

Two preconditions are easy to miss. First, policy outcomes are turned on per workspace by Safeguard. The docs say that if your workspace does not show an Outcome column on the Gates and Policies tables, you ask your Safeguard contact to turn it on, and that while it is off, policies and gates behave as before. Second, the policies and gates docs say gates are evaluated by the platform on the scans it runs for your connected repositories. If you plan to use --local and a gate together, confirm with your Safeguard contact that gate evaluation covers results from runner side scans in your workspace before you make the gate required.

The next section covers how gates decide their outcome.

Step 9. Make the build required in Bitbucket

A failing step only stops a merge if Bitbucket is told to care. In Bitbucket, that is a merge check on the target branch that requires successful builds. We cover the Bitbucket side, including which checks warn and which block, in Bitbucket merge checks: require successful builds. Turn the requirement on only after a week or two of report only runs, once the backlog is under control.

Step 10. Pin the CLI version

The installer resolves the current release on every run, so a new release could change a build without anyone touching the pipeline. Safeguard's docs give the pin as an environment variable on the install line, placed before SAFEGUARD_INSTALL_DIR:

          - curl -fsSL https://cli.safeguard.sh/install | SAFEGUARD_VERSION=<version> SAFEGUARD_INSTALL_DIR=/usr/local/bin bash

Replace the placeholder with a release from the CLI releases list. Every published release is listed at cli.safeguard.sh/releases, according to the installation docs.

The installation docs say the shell installer checks each archive against the release's published checksums.txt and fails closed if that file is missing. If your organization does not allow piping a remote script into a shell, the same page shows how to download the installer, read it and run it separately; the docs themselves say never to pipe a URL you have not read into a shell.

How policy gates decide pass or fail

The gate is where your written policy becomes a build decision, so it is worth understanding exactly how Safeguard computes it.

Policies and gates

The policies and gates docs split the model in two. Policies are rules that evaluate your SBOMs and generate findings. Gates are checkpoints that enforce policies: they integrate with CI/CD, block when policies fail, and route exceptions to approvers.

You create a policy under Settings, then Policies, then Create Policy. You define conditions with the visual builder or YAML, set severity and actions, then save and enable. The docs list condition types including vulnerability severity, CVSS score, EPSS score, license, component age and maintenance status, and templates such as No Critical Vulnerabilities, License Compliance and No Abandoned Packages. Policy creation from plain English prompts (GA) is also available; see plain English security policies to release gates.

The three actions

Each policy has one action, and the action decides what a failed policy does to its gate.

ActionWhen the policy failsEffect on --fail-on-gate
BlockThe gate failsThe step fails
WarnThe gate passes, with a warning naming the policyThe step passes and prints the warning
NotifyThe gate passes, and Safeguard sends a notificationThe step passes and lists the notified policy

Notify goes to your workspace's configured channels: the in-app bell, connected chat integrations, and email according to each person's preferences. The docs say Notify does not repeat the same failure on every evaluation. You are notified when a policy goes from passing to failing, and again when a failing policy finds new matching issues. A policy that keeps failing with the same matches is not notified again, which keeps a busy pipeline from flooding a channel.

A policy's outcome

Each evaluation records one of four outcomes for a policy.

OutcomeMeaning
FailedThe policy's search found matching issues
PassedThe search ran to the end and found none
ErrorThe policy could not be evaluated, for example a missing or failed search
Not evaluatedThe policy has not been evaluated on this gate yet

The docs state plainly that an Error is never counted as a pass. That matters in CI: a broken policy fails closed rather than quietly letting builds through.

New policies have no threshold, so any match fails them. Older policies created with a threshold above zero keep it, and fail only when the number of matches reaches that threshold.

A gate's outcome

The gate's outcome follows from its linked policies, in a fixed order:

  1. No policies linked: Not evaluated.
  2. Any Block policy failed: Failed.
  3. Otherwise, any policy could not be evaluated: Error, with the reason.
  4. Otherwise, any policy not evaluated yet: Not evaluated.
  5. Otherwise, any Warn policy failed: Warning. The gate passes and the warning names the policy.
  6. Otherwise: Passed. Failed Notify policies are listed as notified.

A gate that has never been evaluated shows Not evaluated, never Passed. With --fail-on-gate, Not evaluated fails the step. So a new gate with no linked policies will fail your build until you link at least one policy. Link the policies first, then add the flag.

Exceptions instead of pipeline overrides

When a team has a legitimate reason to ship with a known finding, the documented route is a policy exception, not an edit to the pipeline. In the app, someone clicks Request Exception, selects the policy and finding, writes a business justification and sets an expiry date. Approvers are notified, review the request and grant or deny it, and the decision is recorded in the audit trail.

That flow is the part of SSDF PO.4.1 that people forget: its fifth example is recording security check approvals, rejections and exception requests as part of the workflow. A pipeline that someone edits to drop --fail-on on a Friday evening leaves no such record. An expiring exception does.

Choosing between --fail-on and --fail-on-gate

You want toUseWhy
Start quickly with one rule everyone understands--fail-on criticalNo policy setup; fails on critical dependency vulnerabilities only
See the backlog without blocking anyoneNo fail flagScan runs and results appear in Safeguard; step passes
Enforce license, age or maintenance rules--fail-on-gate <gate>Severity thresholds cannot express these
Let some policies warn while others block--fail-on-gate <gate>Warn and Notify pass the gate; Block fails it
Handle approved exceptions with an audit trail--fail-on-gate <gate> plus exceptionsThe exception changes the gate outcome, not the pipeline
Keep a hard floor even if a gate is misconfiguredBoth flags togetherThe severity rule still applies if the gate passes
Gate on exploitation signalsAn EPSS policy on the gateEPSS score is a documented policy condition; KEV is not listed as one

Options and variations

The docs list a handful of changes you can make to the documented step. Here they are in one place.

ChangeHow
Report onlyDrop --fail-on critical
Another threshold--fail-on high (or medium, low)
Scan on the runnerAdd --local (the step already has the clone)
A specific projectAdd --project-id with the project's id
Pin the CLI versionSAFEGUARD_VERSION=<version> before SAFEGUARD_INSTALL_DIR on the install line
Fail on a policy gateAdd --fail-on-gate with a gate name, id or all (requires --wait)

A specific project

By default, the CLI works out which Safeguard project matches the repository. If more than one project or integration fits, the troubleshooting table says you get PROJECT_AMBIGUOUS or INTEGRATION_AMBIGUOUS, and the fix is to pass --project-id (or --integration-id on the first run). In the app, a project's Set up CI/CD button opens the setup with that project's id already filled in.

Release branches

Add more entries under pipelines.branches, reusing the same anchor:

pipelines:
  branches:
    main:
      - step: *safeguard-scan
    'release/*':
      - step: *safeguard-scan

This uses only the documented glob support and the anchor from the docs' step. Each release branch push becomes its own project version, which gives you a scanned SBOM per release candidate.

Container images the pipeline builds

If your Bitbucket pipeline builds a container image, Safeguard's container image docs describe a second, separate scan. You add one command after the image is built:

safeguard ci scan --image "ghcr.io/acme/api:$TAG" --environment prod --wait --fail-on critical

--image implies a scan on the runner, and only the results leave it. The image is read from the local Docker daemon when the step runs on the machine that built it, or from a registry otherwise, in which case you set SAFEGUARD_REGISTRY_USERNAME and SAFEGUARD_REGISTRY_PASSWORD as secured variables. --environment names the version, so ghcr.io/acme/api:1.4.2 scanned with --environment prod becomes prod-1.4.2. The docs suggest --fail-on critical in production and no fail flag in development.

The docs show a full example for one CI system only, and the Bitbucket specifics of running Docker inside a step are Atlassian's to document. Treat this as the command to add after your existing build, not as a complete step. The wider per environment pattern is in track container images per environment.

Keeping SBOM and scan files with the build

safeguard ci scan stores the SBOM and findings in Safeguard, exportable in SPDX or CycloneDX. If you also want a copy attached to the build, the CI/CD docs describe a second route. safeguard scan --dir . --output out/ runs fully on the runner and writes results as files. The CLI usage page lists the results, including vuln.json, license.json and collection.json, in a folder per scan. You can keep the output folder as a Bitbucket artifact; Atlassian's artifact docs give a maximum retention window of 14 days, so copy anything you must keep longer to durable storage. Our post on Bitbucket Pipelines artifacts covers the details.

Be aware of three differences from ci scan.

  • safeguard scan reports without failing. It exits 0 when the scan ran, whatever it found, 2 when results were written but the vulnerability scan did not run, and 1 when the command failed. The docs show gating on the written files with jq instead.
  • On a fresh CI container, safeguard scan needs its open source scanner tools on the PATH, or TOOLS_DIR pointing at them. The interactive terminal downloads them on first run; a CI container usually has never run it. The usage docs name the tools.
  • Results written this way stay on the runner unless you upload them. They do not create a project version in Safeguard.

If you produce or receive an SBOM file in the build, safeguard validate checks CycloneDX (JSON and XML), SPDX (JSON, YAML, tag-value and RDF) and VEX documents against their specifications. --strict treats warnings as errors. It is a cheap step to add before you hand an SBOM to a customer.

Fix pull requests and signing

Fix pull requests in Bitbucket

Scanning tells you what is wrong. Autonomous remediation (GA) is how Safeguard proposes the change. Griffin opens tested, policy gated fix pull requests, on one repository or in bulk. The Bitbucket integration docs say that with write scopes, Safeguard can open fix and upgrade pull requests, comment on pull requests and set a build status for the quality gate. The optional scopes are write:repository:bitbucket and write:pullrequest:bitbucket for fix pull requests and read:pullrequest:bitbucket for reading pull requests and posting comments.

Autonomous remediation ships switched off, with every category set to Manual. Nothing opens in your Bitbucket repository until someone in your workspace turns a category on and the integration has the write scopes. A fix pull request then runs through the same pull-requests pipeline you set up above, so the Safeguard step checks the fix like any other change. For the categories and strategies, see autonomous remediation in Safeguard.

Signing and attestation

Attestation and signing (GA) is a separate capability from the CI scan. Safeguard's attestation docs say attestations are in-toto format, signed with Safeguard's own key held in a cloud key management service, and verifiable with cosign against Safeguard's published public key. Signatures are not written to a public transparency log, so you rely on Safeguard to protect the key.

For Bitbucket Pipelines specifically, the docs say there is no CLI command for signing yet, and no CLI command for verifying or listing attestations yet. The CI step those docs describe is not listed for Bitbucket. So signing is not something you add to bitbucket-pipelines.yml today. Attestation records for your projects appear on their Attestation tab in the Safeguard app. If you need signed provenance for artifacts your Bitbucket pipeline builds, plan that separately, and see Bitbucket Pipelines signing and provenance for the general options.

Troubleshooting a failing step

The docs do not publish a sample console transcript for ci scan, so we will not invent one. They do document the error codes you will see when a run fails for the wrong reason.

MessageWhat it meansWhat to do
AUTHENTICATION_REQUIRED or INVALID_SECRET_KEYSAFEGUARD_API_KEY is missing, invalid, expired or revokedCheck the variable's name and that the step can read it
MISSING_SCOPEThe key lacks scans:trigger or scans:readCreate a key with both
PROJECT_NOT_ALLOWEDThe key is locked to another projectUse a key for this project
PROJECT_AMBIGUOUS or INTEGRATION_AMBIGUOUSMore than one project or integration fitsPass --project-id (or --integration-id on the first run)
PROJECT_NOT_RESCANNABLEThe project has no repository Safeguard can cloneConnect the repository under Integrations, or use --local
SCAN_NOT_DISPATCHED or SCAN_NOT_STARTEDThe platform could not start the scanRetry; if it persists, contact support with the build URL
The step never runsThe branch is not in the branch list, or a fork pull request has no secretsCheck pipelines.branches and fork behaviour

Requirements, what Safeguard does, and what you still own

Requirement or expectationWhat the Bitbucket step with Safeguard doesWhat you still own
SSDF PO.4.1: define security check criteriaPolicies hold the criteria; gates apply them to each scanDeciding the criteria and who approves exceptions
SSDF PO.4.2: gather information that supports the criteriaEach push and pull request produces an SBOM and findingsMaking the step required and keeping it running
SSDF PW.4.4: automatic detection of known vulnerabilities in componentsDependency vulnerability matching on every scanFixing or accepting what it finds, within your timelines
SSDF PS.3.2 and EO 14028 4(e)(vii): SBOM per releaseA scanned project version per branch and commit, exportable as SPDX or CycloneDXChoosing which versions are releases and delivering SBOMs to buyers
M-22-18: SBOM in an NTIA format, plus vulnerability check artifacts if askedSPDX and CycloneDX exports; scan results per versionThe self-attestation form and your contract terms
Recorded approvals and exceptionsException requests with justification, expiry and audit trailReviewing exceptions before they expire
Fix known vulnerabilities quicklyFix pull requests when autonomous remediation is turned on (it ships off)Reviewing and merging fixes; tests that catch regressions
Signed provenance for built artifactsAttestation records in the app; no signing command in Bitbucket Pipelines todaySigning artifacts your pipeline builds, if a customer requires it

What Safeguard does not do

Being specific about limits saves you a failed audit or a broken release later.

  • It does not review your bitbucket-pipelines.yml. The Safeguard step scans your code and dependencies. It does not check the pipeline file itself for risky settings such as unpinned images, fork build exposure or remote scripts piped into a shell. Those are your pipeline hygiene; see pull request builds and poisoned config.
  • SAST does not fail the build through --fail-on. SAST findings from the default mode appear in Safeguard, but the docs say they are not part of --fail-on yet. Use the app, or a policy that covers them if your workspace supports it, to act on code findings.
  • --local does not run SAST. The runner side mode covers the SBOM and dependency vulnerabilities only. If you need SAST on a private repository, it has to be connected so Safeguard can clone it.
  • It does not scan fork pull requests. Bitbucket does not trigger pipelines for pull requests from forks, and secured variables are not passed to fork builds. Contributions from forks need a separate review path.
  • It does not sign your build artifacts from the pipeline. There is no CLI command for signing yet. Attestation records live in the app.
  • It does not filter pull requests by target branch. pull-requests patterns match the source branch. Target based logic needs Bitbucket's own triggers conditions, which Safeguard's docs do not cover.
  • It does not make a gate pass without policies. A gate with no linked policies is Not evaluated, which fails --fail-on-gate. And policy outcomes must be switched on for your workspace by Safeguard.
  • It does not fix vulnerabilities in software you do not build. A vendor appliance, a hosted service or the base operating system of your Bitbucket runner is outside the scan. Those fixes come from the vendor.

Common mistakes

Appending the docs' file under an existing one. Bitbucket allows one definitions and one pipelines key. Merge the Safeguard step into the keys you already have, or the file will not validate.

Blocking on day one. Turning on --fail-on high and a required build in a repository with years of dependency drift stops every pull request at once. Start report only, read the backlog in Safeguard, fix or except what matters, then tighten.

Expecting pull-requests to filter by target. '**' runs on every pull request, and a pattern like 'main' matches pull requests whose source branch is main, not those that target it.

Adding --fail-on-gate before linking policies. A gate with nothing linked is Not evaluated and fails the step. Link policies, check the gate's Outcome column in the app, then add the flag.

Not pinning the CLI. Without SAFEGUARD_VERSION, every build installs the newest release. Pin it, and update the pin on purpose.

Editing the pipeline to get past a finding. Dropping the fail flag to ship leaves no record. Request an exception with a justification and an expiry instead.

Treating safeguard scan like ci scan. The runner side safeguard scan never fails on findings and does not create a project version in Safeguard. Use it to keep files, not as your gate.

Using an app password for the integration. The docs say app passwords no longer work for Bitbucket. Use an Atlassian API token with Bitbucket scopes and your account email as the username.

FAQ

Is there a Safeguard Pipe for Bitbucket Pipelines?

No. Safeguard's CI/CD docs say there is no plugin, Action or Pipe to install. The integration is one shell step that installs the CLI and runs safeguard ci scan, in an alpine:3.20 image or any Linux or macOS runner with bash and curl.

How do I fail a Bitbucket build on critical vulnerabilities with Safeguard?

Use safeguard ci scan --wait --fail-on critical in the step, as in the docs' bitbucket-pipelines.yml. Change critical to high, medium or low for a stricter threshold, or drop --fail-on to report without failing.

How do I make a Safeguard policy gate fail the pipeline?

Add --fail-on-gate with the gate's name, id or all, together with --wait. The step exits non-zero when the gate is Failed, Error or Not evaluated, and passes when it is Passed or Warning. Policy outcomes must be on for your workspace.

Does Safeguard generate an SBOM in Bitbucket Pipelines?

Yes. Each safeguard ci scan creates a project version with an SBOM and dependency findings, named after the branch and commit. You can export the SBOM from the app in SPDX or CycloneDX, among other formats.

Does my source code leave the Bitbucket runner?

In the default mode, Safeguard clones the repository through its Bitbucket integration, or from bitbucket.org for public repositories. With --local, the scan reads the checkout on the runner and the docs say only the results leave it.

Where do I store the Safeguard API key in Bitbucket?

As a repository variable named SAFEGUARD_API_KEY with Secured on, under Repository settings, Repository variables. A workspace variable also works. Create the key in Safeguard under Settings, Developer, with scans:trigger and scans:read.

Why does the Safeguard step not run on pull requests from forks?

Bitbucket does not trigger pipelines for pull requests from forked repositories, and secured variables are not passed to fork builds. Review fork contributions another way, or bring them into a branch in the main repository.

Can Safeguard open fix pull requests in Bitbucket?

Yes, through autonomous remediation (GA), when the Bitbucket integration has the write scopes the docs list. It ships switched off, with every category set to Manual, so nothing opens until you turn it on.

Can I sign build artifacts with Safeguard in Bitbucket Pipelines?

Not today. The attestation docs say there is no CLI command for signing yet. Attestation records appear on the Attestation tab in the app, signed with Safeguard's key and verifiable with cosign.

Where to go next

The guides for GitHub and GitLab use the same ci scan step and gate model, so your policies carry over. To decide what to fix first, see prioritise vulnerabilities with reachability, EPSS and KEV, and for how Safeguard should touch your code, repo connection, CLI or local runner.

Download the Safeguard apps and CLI at https://safeguard.sh/download, and read the full documentation at https://docs.safeguard.sh/docs .

A pipeline gate tells you a vulnerable version is about to ship; it does not make the safe version appear. The next section explains how Safeguard closes the gap between a failed build and a fixed one.

Fix it without waiting for upstream

Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.

1. Autonomous remediation: pull requests, not tickets

When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation categories include dependencies, licenses, security posture and findings, and each one is set separately. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.

2. Gold Open Source: the safe version in seconds, free

gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds.

3. Gold Registry: start from components that are already fixed

Gold Registry provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs and a completed license check, with signed build provenance (signed with Safeguard's key and verifiable with cosign, SLSA Build Level 3 as the design target) and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.

When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. Ask which packages it covers before you plan around it.

Your situationWhat to use
A fixed version existsAutonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version
You need to know today whether you are affectedGold Open Source lookup and "Am I affected?" checker, free and without login
You want vulnerable versions to stop shippingSafeguard policies and gates in CI
Upstream has no fix yet, or will not ship oneA custom Gold request; fork and patch
You want to start clean on new projectsPull hardened components and images from Gold Registry

One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.

See how Safeguard would help

The first demo is a real Safeguard CLI session. You install the CLI, run safeguard to open the interactive terminal and ask it to "fix the lodash CVE in this repo". It reads the finding and the advisory, asks before any command that changes something, edits package.json to lodash 4.17.21, runs npm install and npm test, prints a summary and pushes nothing; it ends with /permissions, /model and /scan .. This is the same CLI your Bitbucket step installs, and it is the quickest way to fix a finding that just failed your build before you push again.

Click through it at your own pace, or open it full screen.

The second demo sets up the local runner on your own machine, approves it with its device code, and then scans from the terminal. It shows the same idea as --local in the pipeline: the scan runs where your code already is, and only results go to Safeguard. Use it to check a branch locally before the Bitbucket step does.

Click through it at your own pace, or open it full screen.

The third demo adds Safeguard to Claude from the connector directory, signs in with OAuth and chooses which tools Claude can use. The policy outcomes docs say Safeguard's MCP server has read-only tools for gate outcomes, policy outcomes and evaluation history when outcomes are on and your access settings allow them, so you can ask Claude why the gate on your last Bitbucket build failed.

Click through it at your own pace, or open it full screen.

Quick check

Your new Safeguard gate has no linked policies. What happens when the Bitbucket step runs with --fail-on-gate?

The gate's outcome is Not evaluated, and --fail-on-gate exits non-zero for Not evaluated, so the step fails. Link at least one policy to the gate before you add the flag.

You add --local to the Safeguard step. Which results do you lose compared with the default mode?

SAST. The local mode scans the checkout on the runner and produces the SBOM and dependency vulnerabilities only. The default mode, which needs the repository connected or public on bitbucket.org, also runs SAST.

Sources

Last reviewed: 25 October 2026. Product capabilities change; check docs.safeguard.sh for the current state.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.