fix: harden bitbucket-pipelines.yml with security and correctness improvements

This commit is contained in:
Siddhant Medar
2026-04-14 16:24:40 -05:00
parent fb1266ba33
commit f3a36eea1e
+46 -15
View File
@@ -18,6 +18,12 @@
# Only feature/*, bugfix/*, hotfix/*, dev, stg have PR pipelines defined.
# Any other branch name simply gets NO PR pipeline → checks won't pass
# → PR cannot be merged. No catch-all '**' pattern (that caused double runs).
#
# SETUP REQUIRED: enforce these gates by enabling "Require a successful
# build to merge" for dev, stg, and main in:
# Bitbucket → Repository settings → Branch permissions
# Without this, the gates are advisory only — PRs can be merged regardless
# of whether the gate, lint, tests, or AI review pass or fail.
# =============================================================================
@@ -43,7 +49,7 @@ definitions:
# then syncs both dev and test dependency groups from pyproject.toml
- script: &install
pip install uv==0.11.5;
uv sync --group dev --group test;
uv sync --frozen --group dev --group test;
# ---------------------------------------------------------------------------
# Reusable step definitions
@@ -89,12 +95,17 @@ definitions:
# and posts review comments directly on the pull request.
- step: &ai-code-review
name: AI Code Review
image: python:3.12-slim
image: python:3.12.7-slim # slim is fine here — no uv sync
# SETUP REQUIRED: configure OIDC provider in
# Bitbucket → Workspace settings → Security → OpenID Connect
# and add the matching trust policy to the IAM role below.
# Without it, the step fails at startup before any script runs.
oidc: true # enables OIDC token injection
script:
# Slim image doesn't include git — install it + AWS/HTTP libs
- apt-get update && apt-get install -y git
- pip install boto3 requests
# --no-install-recommends skips git-man/less/openssh-client etc.
- apt-get update && apt-get install -y --no-install-recommends git
- pip install "boto3==1.35.*" "requests==2.32.*"
# --- AWS OIDC authentication setup ---
# How it works:
@@ -105,16 +116,30 @@ definitions:
- export AWS_REGION=us-east-1
- export AWS_ROLE_ARN="arn:aws:iam::975049960860:role/DoczyAI-Bitbucket-OIDC"
- export AWS_WEB_IDENTITY_TOKEN_FILE="$(pwd)/web-identity-token"
- echo "$BITBUCKET_STEP_OIDC_TOKEN" > "$(pwd)/web-identity-token"
# ↑ Quoted to prevent word-splitting if token has special chars
- printf '%s' "$BITBUCKET_STEP_OIDC_TOKEN" > "$(pwd)/web-identity-token"
# printf (not echo) avoids appending a trailing newline to the JWT
# Clone the review agent repo (private — needs BITBUCKET_CLONE_TOKEN)
# SETUP REQUIRED: set BITBUCKET_CLONE_TOKEN (marked "Secured") in
# Bitbucket → Repository settings → Repository variables
# Guard against missing token so failure is self-diagnosing
- |
if [ -z "${BITBUCKET_CLONE_TOKEN}" ]; then
echo "BLOCKED: BITBUCKET_CLONE_TOKEN not set."
echo "Set it in Repository settings → Repository variables (Secured)."
exit 1
fi
# -q flag suppresses URL output to avoid leaking the token in logs
- git clone -q "https://x-token-auth:${BITBUCKET_CLONE_TOKEN}@bitbucket.org/${BITBUCKET_WORKSPACE}/code-review-agent.git"
# Build the PR URL and run the agent
# Build the PR URL and run the agent.
# The || echo below only covers the python main.py call:
# agent crashes, AWS STS errors, and runtime exceptions become
# warnings instead of hard failures. A failure in git clone or
# apt-get above is still a hard failure (rare in practice).
# AI review is advisory, not a gate.
- export PR_URL="https://bitbucket.org/${BITBUCKET_WORKSPACE}/${BITBUCKET_REPO_SLUG}/pull-requests/${BITBUCKET_PR_ID}"
- cd code-review-agent && python main.py "$PR_URL"
- cd code-review-agent && python main.py "$PR_URL" || echo "WARNING: AI review step failed (non-blocking)"
# =============================================================================
@@ -139,7 +164,7 @@ pipelines:
# ===========================================================================
# Each entry matches a SOURCE branch pattern (the branch the PR comes
# FROM, not the branch it targets). The gate step inside validates
# the DESTINATION branch using ALLOWED_TARGETS.
# the DESTINATION branch.
#
# Flow for every PR:
# Step 1: Gate → is this PR targeting the correct branch?
@@ -356,7 +381,10 @@ pipelines:
patch=$((patch + 1))
fi
# Create the annotated tag and push it to the remote
# Create the annotated tag and push it to the remote.
# SETUP REQUIRED: enable "Repository write access" in
# Bitbucket → Repository settings → Pipelines
# Without it, git push fails with 403 and releases don't work.
- |
new_tag="v${major}.${minor}.${patch}"
echo "Creating tag: ${new_tag}"
@@ -375,7 +403,7 @@ pipelines:
image: atlassian/default-image:4
script:
# Read the tag that was created in step 1
- export RELEASE_TAG=$(cat release_tag.txt)
- export RELEASE_TAG="$(cat release_tag.txt)"
- echo "Deploying ${RELEASE_TAG}"
# DEPLOY_COMMAND is a repository variable configured in
@@ -400,7 +428,7 @@ pipelines:
rollback-prod:
- variables:
- name: ROLLBACK_TAG
default: v0.0.0 # user must change this to a real tag
default: "" # user MUST provide a real tag (e.g. v1.2.0)
# Step 1: Validate the tag exists
- step:
@@ -422,11 +450,14 @@ pipelines:
exit 1
fi
# Verify the tag exists in the remote
# Verify the ref exists AND is specifically a tag (not a branch,
# HEAD, or commit hash). git rev-parse would accept any ref;
# `git tag -l` + exact-match grep ensures we only accept tags.
- git fetch --tags --force
- |
if ! git rev-parse "${ROLLBACK_TAG}" >/dev/null 2>&1; then
echo "BLOCKED: tag ${ROLLBACK_TAG} does not exist."
if ! git tag -l "${ROLLBACK_TAG}" | grep -qxF "${ROLLBACK_TAG}"; then
echo "BLOCKED: ${ROLLBACK_TAG} is not a tag."
echo "Rollback requires a release tag (e.g. v1.2.0), not a branch/commit."
exit 1
fi
- echo "Tag ${ROLLBACK_TAG} found — proceeding with rollback."