Skip to content

Bitbucket Pipelines provider

Parses bitbucket-pipelines.yml on disk, no Bitbucket API token, no runner install.

Producer workflow

# --bitbucket-path auto-detected when bitbucket-pipelines.yml exists at cwd.
pipeline_check --pipeline bitbucket

# …or pass it explicitly (file or directory).
pipeline_check --pipeline bitbucket --bitbucket-path ci/

What it covers

29 checks · 11 have an autofix patch (--fix).

Check Title Severity Fix
BB-001 pipe: action not pinned to exact version HIGH 🔧 fix
BB-002 Script injection via attacker-controllable context HIGH
BB-003 Variables contain literal secret values CRITICAL
BB-004 Deploy step missing deployment: environment gate MEDIUM
BB-005 Step has no max-time, unbounded build MEDIUM 🔧 fix
BB-006 Artifacts not signed MEDIUM
BB-007 SBOM not produced MEDIUM
BB-008 Credential-shaped literal in pipeline body CRITICAL 🔧 fix
BB-009 pipe: pinned by version rather than sha256 digest LOW
BB-010 Deploy step ingests pull-request artifact unverified CRITICAL
BB-011 AWS auth uses long-lived access keys MEDIUM 🔧 fix
BB-012 Remote script piped to shell interpreter HIGH 🔧 fix
BB-013 Docker run with insecure flags (privileged/host mount) CRITICAL 🔧 fix
BB-014 Package install from insecure source HIGH 🔧 fix
BB-015 No vulnerability scanning step MEDIUM
BB-016 Self-hosted runner without ephemeral marker MEDIUM
BB-017 Repository token written to persistent storage CRITICAL 🔧 fix
BB-018 Cache key derives from attacker-controllable input MEDIUM
BB-019 after-script references secrets HIGH
BB-020 Full clone depth exposes complete history LOW
BB-021 Package install without lockfile enforcement MEDIUM 🔧 fix
BB-022 Dependency update command bypasses lockfile pins MEDIUM 🔧 fix
BB-023 TLS / certificate verification bypass HIGH 🔧 fix
BB-024 No SLSA provenance attestation produced MEDIUM
BB-025 Pipeline contains indicators of malicious activity CRITICAL
BB-026 Dangerous shell idiom (eval, sh -c variable, backtick exec) HIGH
BB-027 Package install bypasses registry integrity (git / path / tarball source) MEDIUM
BB-028 OIDC step without deployment-gated environment HIGH
BB-029 image: (step or service) not pinned by sha256 digest HIGH

BB-001: pipe: action not pinned to exact version

HIGH 🔧 autofix CICD-SEC-3 ESF-S-PIN-DEPS ESF-S-VERIFY-DEPS CWE-829

Bitbucket pipes are docker-image references. Major-only (:1) or missing tags let Atlassian/the publisher swap the image contents. Full semver or sha256 digest is required.

Recommended action

Pin every pipe: to a full semver tag (e.g. atlassian/aws-s3-deploy:1.4.0) or to an immutable SHA. Floating majors like :1 can roll to new code silently.

BB-002: Script injection via attacker-controllable context

HIGH CICD-SEC-4 ESF-D-INJECTION CWE-78

$BITBUCKET_BRANCH, $BITBUCKET_TAG, and $BITBUCKET_PR_* are populated from SCM event metadata the attacker controls. Interpolating them unquoted into a shell command lets a crafted branch or tag name can execute inline.

Recommended action

Always double-quote interpolations of ref-derived variables ("$BITBUCKET_BRANCH"). Avoid passing them to eval, sh -c, or unquoted command arguments.

BB-003: Variables contain literal secret values

CRITICAL CICD-SEC-6 ESF-D-SECRETS CWE-798

Scans definitions.variables and each step's variables: for entries whose KEY looks credential-shaped and whose VALUE is a literal string. AWS access keys are detected by value shape regardless of key name.

Recommended action

Store credentials as Repository / Deployment Variables in Bitbucket's Pipelines settings with the 'Secured' flag, and reference them by name. Prefer short-lived OIDC tokens for cloud access.

BB-004: Deploy step missing deployment: environment gate

MEDIUM CICD-SEC-1 ESF-C-APPROVAL ESF-C-ENV-SEP CWE-284

A step whose name or invoked pipe matches deploy / release / publish / promote should declare a deployment: field so Bitbucket enforces deployment-scoped variables, approvals, and history.

Recommended action

Add deployment: production (or staging / test) to the step. Configure the matching environment in the repo's Deployments settings with required reviewers and secured variables.

BB-005: Step has no max-time, unbounded build

MEDIUM 🔧 autofix CICD-SEC-7 ESF-D-BUILD-TIMEOUT CWE-400

Without max-time, the step runs until Bitbucket's 120-minute global default kills it. Explicit per-step timeouts cap blast radius and cost.

Recommended action

Add max-time: <minutes> to each step, sized to the 95th percentile of historical runtime plus margin. Bounded runs limit the blast radius of a compromised build and prevent runaway minute consumption.

BB-006: Artifacts not signed

MEDIUM CICD-SEC-9 ESF-D-SIGN-ARTIFACTS CWE-345

Unsigned artifacts can't be verified downstream. Passes when cosign / sigstore / slsa-* / notation-sign appears in the pipeline body.

Recommended action

Add a step that runs cosign sign against the built image or archive, using Bitbucket OIDC for keyless signing where possible. Publish the signature next to the artifact and verify it at deploy time.

BB-007: SBOM not produced

MEDIUM CICD-SEC-9 ESF-D-SBOM CWE-1104

Without an SBOM, downstream consumers can't audit the dependency set shipped in the artifact. Passes when CycloneDX / syft / anchore / sbom-tool / Trivy-SBOM appears.

Recommended action

Add an SBOM step, syft . -o cyclonedx-json, Trivy with --format cyclonedx, or Microsoft's sbom-tool. Attach the SBOM as a build artifact.

BB-008: Credential-shaped literal in pipeline body

CRITICAL 🔧 autofix CICD-SEC-6 ESF-D-SECRETS CWE-798

Complements BB-003 (variable-name scan). BB-008 checks every string in the pipeline against the cross-provider credential-pattern catalog, catches secrets pasted into script bodies or environment blocks.

Known false-positive modes

  • Test fixtures and documentation blobs sometimes embed credential-shaped strings (JWT samples, AKIAI... examples). The AWS canonical example AKIAIOSFODNN7EXAMPLE is deliberately NOT suppressed, if it appears in a real pipeline it almost always means a copy-paste from docs was never substituted. Defaults to LOW confidence.

Recommended action

Rotate the exposed credential. Move the value to a Secured Repository or Deployment Variable and reference it by name.

BB-009: pipe: pinned by version rather than sha256 digest

LOW CICD-SEC-3 ESF-S-PIN-DEPS ESF-S-IMMUTABLE CWE-829

BB-001 fails floating tags at HIGH; BB-009 is the stricter tier. Even immutable-looking semver tags can be repointed by the registry; sha256 digests are tamper-evident.

Recommended action

Resolve each pipe to its digest (docker buildx imagetools inspect bitbucketpipelines/<name>:<ver>) and reference it via @sha256:<digest>.

BB-010: Deploy step ingests pull-request artifact unverified

CRITICAL CICD-SEC-4 ESF-D-INJECTION ESF-S-VERIFY-DEPS CWE-494

Bitbucket steps declare artifacts on the producer and downstream steps implicitly receive them. When an unprivileged step produces an artifact and a later deployment: step consumes it without verification, attacker-controlled output flows into the privileged stage.

Recommended action

Add a verification step before the deploy step consumes the artifact: sha256sum -c artifact.sha256 against a manifest the producer signed, or cosign verify over the artifact directly. Alternatively, restrict the artifact-producing step to non-PR pipelines via branches: or custom: triggers.

BB-011: AWS auth uses long-lived access keys

MEDIUM 🔧 autofix CICD-SEC-6 ESF-D-TOKEN-HYGIENE CWE-522

Long-lived AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY values embedded in the pipeline file can't be rotated on a fine-grained schedule. Prefer OIDC or Bitbucket secured variables for cross-cloud access.

Recommended action

Use Bitbucket OIDC with oidc: true on the AWS pipe, or store credentials as secured Bitbucket variables rather than inline values. Remove static AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY from the pipeline file.

BB-012: Remote script piped to shell interpreter

HIGH 🔧 autofix CICD-SEC-3 ESF-S-VERIFY-DEPS CWE-494

Detects curl | bash, wget | sh, and similar patterns that pipe remote content directly into a shell interpreter inside a pipeline. An attacker who controls the remote endpoint (or poisons DNS / CDN) gains arbitrary code execution in the build runner.

Known false-positive modes

  • Established vendor installers (get.docker.com, sh.rustup.rs, bun.sh/install, awscli.amazonaws.com, cli.github.com, ...) ship via HTTPS from their own CDN and are idiomatic. This rule defaults to LOW confidence so CI gates can ignore them with --min-confidence MEDIUM; the finding still surfaces so teams that want cryptographic verification can audit.

Recommended action

Download the script to a file, verify its checksum, then execute it. Or vendor the script into the repository.

BB-013: Docker run with insecure flags (privileged/host mount)

CRITICAL 🔧 autofix CICD-SEC-7 ESF-D-BUILD-ENV CWE-250

Flags like --privileged, --cap-add, --net=host, or host-root volume mounts (-v /:/) in a pipeline give the container full access to the build runner, enabling container escape and lateral movement.

Recommended action

Remove --privileged and --cap-add flags. Use minimal volume mounts. Prefer rootless containers.

BB-014: Package install from insecure source

HIGH 🔧 autofix CICD-SEC-3 ESF-S-VERIFY-DEPS CWE-494

Detects package-manager invocations that use plain HTTP registries (--index-url http://, --registry=http://) or disable TLS verification (--trusted-host, --no-verify) in a pipeline. These patterns allow man-in-the-middle injection of malicious packages.

Recommended action

Use HTTPS registry URLs. Remove --trusted-host and --no-verify flags. Pin to a private registry with TLS.

BB-015: No vulnerability scanning step

MEDIUM CICD-SEC-3 ESF-S-VULN-MGMT CWE-1104

Without a vulnerability scanning step, known-vulnerable dependencies ship to production undetected. The check recognises trivy, grype, snyk, npm audit, yarn audit, safety check, pip-audit, osv-scanner, and govulncheck.

Recommended action

Add a vulnerability scanning step, trivy, grype, snyk test, npm audit, pip-audit, or osv-scanner. Publish results so vulnerabilities surface before deployment.

BB-016: Self-hosted runner without ephemeral marker

MEDIUM CICD-SEC-7 ESF-D-BUILD-ENV ESF-D-PRIV-BUILD CWE-269

Self-hosted runners that persist between jobs leak filesystem and process state. A PR-triggered step writes to a well-known path; a subsequent deploy step on the same runner reads it. Detects runs-on: self.hosted without an ephemeral marker or Docker image override.

Recommended action

Use Docker-based self-hosted runners or configure runners to tear down between jobs. Add 'ephemeral' to runs-on labels or use Bitbucket's runner images that are rebuilt per-job.

BB-017: Repository token written to persistent storage

CRITICAL 🔧 autofix CICD-SEC-6 ESF-D-SECRETS CWE-522

Detects patterns where Bitbucket pipeline tokens are redirected to files or piped through tee. Persisted tokens survive the step boundary and can be exfiltrated by later steps, artifacts, or cache entries.

Recommended action

Never write BITBUCKET_TOKEN or REPOSITORY_OAUTH_ACCESS_TOKEN to files or artifacts. Use the token inline in the command that needs it and let Bitbucket revoke it after the build.

BB-018: Cache key derives from attacker-controllable input

MEDIUM CICD-SEC-4 ESF-D-INJECTION ESF-S-VERIFY-DEPS CWE-345

Bitbucket caches are restored by key. When the key includes a value the attacker controls (branch name, tag, PR ID), a pull-request pipeline can plant a poisoned cache entry that a subsequent default-branch build restores.

Recommended action

Build the cache key from values the attacker cannot control. Prefer hashFiles() on lockfiles enforced by branch protection. Never include $BITBUCKET_BRANCH or PR-related variables in the cache key.

BB-019: after-script references secrets

HIGH CICD-SEC-6 ESF-D-SECRETS CWE-522

Bitbucket's after-script runs unconditionally after the main script block (including on failure). If the after-script references secrets or tokens, those values may leak into build logs or artifacts even when the step fails unexpectedly. This check detects secret-like variable references in after-script blocks.

Recommended action

Move secret-dependent operations into the main script: block. after-script runs even when the step fails and executes in a separate shell context, credential exposure here is harder to audit and more likely to persist in logs.

BB-020: Full clone depth exposes complete history

LOW CICD-SEC-7 ESF-D-BUILD-ENV CWE-250

By default Bitbucket Pipelines clone with depth: 50. Setting depth: full exposes the entire commit history, including any secrets that were committed and later removed. This check flags explicit clone: depth: full settings.

Recommended action

Set clone: depth: 1 (or a small number) in pipeline or step options to limit the amount of repository history available in the build environment. Full clones make it easier to extract secrets that were committed and later removed.

BB-021: Package install without lockfile enforcement

MEDIUM 🔧 autofix CICD-SEC-3 ESF-S-PIN-DEPS CWE-829

Detects package-manager install commands that do not enforce a lockfile or hash verification. Without lockfile enforcement the resolver pulls whatever version is currently latest, exactly the window a supply-chain attacker exploits.

Recommended action

Use lockfile-enforcing install commands: npm ci instead of npm install, pip install --require-hashes -r requirements.txt, yarn install --frozen-lockfile, bundle install --frozen, and go install tool@v1.2.3.

BB-022: Dependency update command bypasses lockfile pins

MEDIUM 🔧 autofix CICD-SEC-3 ESF-S-PIN-DEPS CWE-829

Detects pip install --upgrade, npm update, yarn upgrade, bundle update, cargo update, go get -u, and composer update. These commands bypass lockfile pins and pull whatever version is currently latest. Tooling upgrades (pip install --upgrade pip) are exempted.

Known false-positive modes

  • Common build-tool bootstrapping idioms (pip install --upgrade pip, pip install --upgrade setuptools wheel virtualenv) and security-tool installs (pip install --upgrade pip-audit / cyclonedx-bom / semgrep) are exempted by the DEP_UPDATE_RE tooling allowlist. Other tooling-upgrade idioms not yet on the list can still trip the rule. Defaults to MEDIUM confidence so CI gates can require --min-confidence HIGH to ignore.

Recommended action

Remove dependency-update commands from CI. Use lockfile-pinned install commands (npm ci, pip install -r requirements.txt) and update dependencies via a dedicated PR pipeline (e.g. Dependabot, Renovate).

BB-023: TLS / certificate verification bypass

HIGH 🔧 autofix CICD-SEC-3 ESF-S-VERIFY-DEPS CWE-295

Detects patterns that disable TLS certificate verification: git config http.sslVerify false, NODE_TLS_REJECT_UNAUTHORIZED=0, npm config set strict-ssl false, curl -k, wget --no-check-certificate, PYTHONHTTPSVERIFY=0, and GOINSECURE=. Disabling TLS verification allows MITM injection of malicious packages, repositories, or build tools.

Recommended action

Remove TLS verification bypasses. Fix certificate issues at the source (install CA certificates, configure proper trust stores) instead of disabling verification.

BB-024: No SLSA provenance attestation produced

MEDIUM CICD-SEC-9 ESF-S-PROVENANCE CWE-345

Bitbucket has no native SLSA builder; self-hosted attestation via cosign attest or witness run is the usual path. Pipes like atlassian/cosign-attest (if published) would also match.

Recommended action

Add a step that runs cosign attest against a provenance.intoto.jsonl statement, or integrate the TestifySec witness run attestor. Artifact signing alone (BB-006) doesn't satisfy SLSA Build L3.

BB-025: Pipeline contains indicators of malicious activity

CRITICAL CICD-SEC-4 CICD-SEC-7 ESF-D-INJECTION ESF-S-VERIFY-DEPS CWE-506 CWE-913

Specific indicators only (reverse shells, base64-decoded execution, miner binaries, Discord/Telegram webhooks, credential-dump pipes, audit-erasure commands). Does not replace BB-014 (TLS bypass) or BB-013 (Docker insecure), those are hygiene; this is evidence.

Known false-positive modes

  • Security-training repositories, CTF challenges, and red-team exercise pipelines legitimately contain reverse-shell strings or exfil domains as literals. Matches inside YAML keys / HCL attributes whose names contain example, fixture, sample, demo, or test are auto-suppressed; bare lines in a production pipeline still fire.
  • Defaults to LOW confidence. Filter with --min-confidence MEDIUM to ignore all matches; the rule still surfaces the hit for teams that want to spot-check.

Recommended action

Treat as a potential compromise. Identify the PR that added the matching step(s), rotate any credentials referenced from the pipeline's variable groups, and audit recent builds.

BB-026: Dangerous shell idiom (eval, sh -c variable, backtick exec)

HIGH CICD-SEC-4 ESF-D-INJECTION CWE-95

Complements BB-002 (script injection from untrusted PR context). This rule fires on intrinsically risky idioms, eval, sh -c "$X", backtick exec, regardless of whether the input source is currently trusted.

Known false-positive modes

  • eval "$(ssh-agent -s)" and similar eval "$(<literal-tool>)" bootstrap idioms are intentionally NOT flagged, the substituted command is literal, only its output is eval'd.

Recommended action

Replace eval "$VAR" / sh -c "$VAR" / backtick exec with direct command invocation. Validate or allow-list any value that must feed a dynamic command at the boundary.

BB-027: Package install bypasses registry integrity (git / path / tarball source)

MEDIUM CICD-SEC-3 ESF-S-PIN-DEPS ESF-S-VERIFY-DEPS CWE-829

Complements BB-021 (missing lockfile flag). Git URL installs without a commit pin, local-path installs, and direct tarball URLs bypass the registry integrity controls the lockfile relies on.

Recommended action

Pin git dependencies to a commit SHA. Publish private packages to an internal registry instead of installing from a filesystem path or tarball URL.

BB-028: OIDC step without deployment-gated environment

HIGH CICD-SEC-2 CWE-284

Pairs with IAM-008. IAM-008 verifies the cloud-side trust policy pins audience + subject; this rule verifies the Bitbucket-side workflow can't request a token without a deployment gate. Bitbucket's pull-requests: triggers from forks so OIDC under that branch is always an unbounded blast radius.

Recommended action

Every step that sets oidc: true must also declare a deployment: (production / staging / test). Bitbucket deployments enforce manual approvals, restricted variables, and audit logs that an ungated step bypasses. Steps reached through pull-requests: should never request OIDC tokens, any forked PR can drive the role assumption.

BB-029: image: (step or service) not pinned by sha256 digest

HIGH CICD-SEC-3 ESF-S-PIN-DEPS ESF-S-IMMUTABLE ESF-S-VERIFY-DEPS CWE-829

BB-001 / BB-009 only inspect pipe: references inside script: lists. Step image: directives and definitions.services.<name>.image: define the runtime container the build executes inside (and the auxiliary containers the step talks to over the loopback network). Both surfaces ship code into the build context, a compromised service image (the postgres container, the selenium-grid container, …) can exfiltrate every secret the step touches just as easily as the step image itself. This rule reuses _primitives.image_pinning.classify so the floating-tag semantics match GHA-001 / GL-001 / JF-009 / ADO-009 / CC-003 / K8S-001.

Known false-positive modes

  • Bitbucket-vendored helper images (atlassian/ namespace) are still treated as third-party, the registry can move the tag. Pin them too rather than suppressing the rule globally.

Recommended action

Resolve every image: reference to its current digest (docker buildx imagetools inspect <ref> or crane digest <ref>) and pin via image: name@sha256:<digest>. Floating tags (:latest, :3, no tag) silently swap the runtime image, the build's reproducibility invariant is broken and a registry-side compromise lands inside CI without any local change.


Adding a new Bitbucket Pipelines check

  1. Create a new module at pipeline_check/core/checks/bitbucket/rules/bbNNN_<name>.py exporting a top-level RULE = Rule(...) and a check(path, doc) -> Finding function. The orchestrator auto-discovers RULE and calls check with the parsed YAML document.
  2. Add a mapping for the new ID in pipeline_check/core/standards/data/owasp_cicd_top_10.py (and any other standard that applies).
  3. Drop unsafe/safe snippets at tests/fixtures/per_check/bitbucket/BB-NNN.{unsafe,safe}.yml and add a CheckCase entry in tests/test_per_check_real_examples.py::CASES.
  4. Regenerate this doc:
python scripts/gen_provider_docs.py bitbucket