Bitbucket Pipelines runs your CI/CD directly within Bitbucket Cloud. Its tight integration with the Bitbucket platform makes it convenient, but that same integration means a misconfigured pipeline can expose your repository secrets, deployment credentials, and infrastructure access. Here is how to lock it down.
Securing Pipeline Variables
Bitbucket supports repository, deployment, and workspace-level variables.
Repository Variables
Repository settings > Pipelines > Repository variables
Mark secrets as Secured variables. Secured variables are:
- Encrypted at rest.
- Masked in pipeline logs.
- Not available in pull request pipelines from forked repositories.
# bitbucket-pipelines.yml
pipelines:
default:
- step:
script:
- echo "Using secured variable"
- deploy --token $DEPLOY_TOKEN # Secured variable
Deployment Variables
Deployment variables are scoped to specific deployment environments (staging, production). They are only available in steps that target that environment:
pipelines:
branches:
main:
- step:
name: Deploy to Production
deployment: production
script:
- deploy --token $PROD_DEPLOY_TOKEN # Only available in production deployment
This is critical for security. A developer working on a feature branch cannot access production deployment credentials.
Variable Hierarchy
Variables are resolved in this order:
- Step-level variables
- Deployment-level variables
- Repository-level variables
- Workspace-level variables
A step-level variable can shadow a repository-level variable. Be aware of this when debugging.
Deployment Permissions
Bitbucket allows you to restrict who can deploy to specific environments:
Repository settings > Deployments
For each environment:
- Set Required reviewers who must approve deployments.
- Configure Branch restrictions to limit which branches can deploy.
pipelines:
branches:
main:
- step:
name: Deploy to Staging
deployment: staging
script:
- ./deploy.sh staging
- step:
name: Deploy to Production
deployment: production
trigger: manual # Requires manual trigger
script:
- ./deploy.sh production
The trigger: manual setting requires someone to click a button in the Bitbucket UI to deploy to production. Combined with required reviewers, this creates a deployment gate.
Pipeline Configuration Security
Step Images
Specify Docker images explicitly with tags or digests:
pipelines:
default:
- step:
image: node:20-alpine # Explicit version, not 'latest'
script:
- npm ci
- npm test
Using latest means your pipeline could break or become compromised if the image is updated. Pin to specific versions.
Service Containers
Service containers (databases, caches) run alongside your pipeline steps:
definitions:
services:
postgres:
image: postgres:16-alpine
variables:
POSTGRES_DB: testdb
POSTGRES_USER: test
POSTGRES_PASSWORD: $TEST_DB_PASSWORD # Use a variable, not a hardcode
Never hardcode credentials in service container definitions.
Cache Security
Bitbucket caches dependency directories between pipeline runs. If a cache is poisoned (contains malicious files), subsequent builds are affected:
definitions:
caches:
node: node_modules
pipelines:
default:
- step:
caches:
- node
script:
- npm ci # npm ci ignores node_modules and installs from lockfile
Use npm ci instead of npm install with caches. npm ci deletes node_modules and reinstalls from the lockfile, which prevents cache poisoning from affecting your build.
Security Scanning in Pipelines
Bitbucket does not include built-in security scanners like GitLab does. You need to add them manually.
Dependency Scanning
pipelines:
default:
- step:
name: Security Scan
image: node:20-alpine
script:
- npm ci
- npm audit --audit-level=high
- npx @cyclonedx/cyclonedx-npm --output-file sbom.json
artifacts:
- sbom.json
Container Scanning
- step:
name: Container Scan
image: aquasec/trivy:latest
script:
- trivy image --exit-code 1 --severity CRITICAL,HIGH $DOCKER_IMAGE
SAST
- step:
name: SAST
image: returntocorp/semgrep
script:
- semgrep --config auto --error .
Pull Request Pipeline Security
Bitbucket can run pipelines on pull requests, but with security considerations:
Fork Pipelines
Pull requests from forks should not have access to your repository's secured variables. Bitbucket handles this by default: secured variables are not available in fork pipelines.
However, the pipeline code itself comes from the fork. A malicious fork could modify bitbucket-pipelines.yml to exfiltrate environment variables or secrets through other means. Review pipeline configuration changes in pull requests from forks.
Required Builds
Configure merge checks to require successful builds:
Repository settings > Branch permissions > Merge checks
- Require at least 1 successful build.
- Require specific pipelines to pass.
IP Allowlisting
If your deployment targets restrict access by IP, use Bitbucket's IP ranges for Pipelines:
# Bitbucket Pipelines IPs are documented at
# https://support.atlassian.com/bitbucket-cloud/docs/what-are-the-bitbucket-cloud-ip-addresses-i-should-use-to-configure-my-corporate-firewall/
Whitelist only the Bitbucket Pipelines IP ranges, not all of Bitbucket Cloud.
How Safeguard.sh Helps
Safeguard.sh fills the security scanning gap in Bitbucket Pipelines. While Bitbucket does not include built-in vulnerability scanning like GitLab, Safeguard.sh integrates directly into your Bitbucket CI pipeline to ingest SBOMs, scan dependencies, and provide continuous monitoring. It aggregates findings across all your Bitbucket repositories and gives your security team a centralized dashboard for managing supply chain risk, regardless of which CI/CD platform your teams use.