harden pipeline per PR review: locked lockfile, token permissions, image comment

1. Replace 'uv sync --frozen' with 'uv sync --locked'. --frozen silently
   uses a stale lockfile if pyproject.toml is updated without regenerating
   uv.lock, masking missed dependencies. --locked fails loudly with a clear
   error when the lockfile is out of sync. Verified empirically with uv
   0.9.14: --frozen exits 0 on mismatch, --locked exits 1.

2. Add 'chmod 600' on the OIDC token file after writing it. The container
   is already single-user root so this is defensive/cosmetic, but it
   silences security scanners and signals intent.

3. Add a comment explaining why gate steps use atlassian/default-image:4
   instead of python:3.12.7 (gates run bash only, no Python toolchain
   needed — lighter image, faster pull).

Explicitly rejected from the PR review:
- Token-masking sed mitigation (delimiter collision with / in real
  Bitbucket clone tokens; cmd | sed || exit 1 swallows git failures
  without set -o pipefail). Bitbucket's built-in Secured variable
  masking is the correct mitigation and is already documented as a
  setup requirement.
- SSH key alternative for git clone (architecturally worse — more
  secrets to manage; HTTPS+Secured is the Bitbucket-recommended pattern).
- cd/python refactor ('fragile logic bug'). Verified empirically that
  'cd X && python Y || echo Z' with set -e correctly catches both
  cd and python failures via the ||. The step exits 0 as intended
  by the 'advisory, not a gate' design. The suggested refactor would
  introduce a hard-fail regression.
This commit is contained in:
Siddhant Medar
2026-04-15 09:01:35 -05:00
parent 2a0617cd79
commit 4836e36f65
+11 -3
View File
@@ -46,10 +46,12 @@ definitions:
scripts: scripts:
# Installs the uv package manager (pinned to avoid surprise breaks), # Installs the uv package manager (pinned to avoid surprise breaks),
# then syncs both dev and test dependency groups from pyproject.toml # then syncs both dev and test dependency groups from pyproject.toml.
# --locked (NOT --frozen) asserts uv.lock matches pyproject.toml and
# fails loudly on mismatch. --frozen would silently use a stale lockfile.
- script: &install - script: &install
pip install uv==0.11.5; pip install uv==0.11.5;
uv sync --frozen --group dev --group test; uv sync --locked --group dev --group test;
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Reusable step definitions # Reusable step definitions
@@ -116,8 +118,11 @@ definitions:
- export AWS_REGION=us-east-1 - export AWS_REGION=us-east-1
- export AWS_ROLE_ARN="arn:aws:iam::975049960860:role/DoczyAI-Bitbucket-OIDC" - export AWS_ROLE_ARN="arn:aws:iam::975049960860:role/DoczyAI-Bitbucket-OIDC"
- export AWS_WEB_IDENTITY_TOKEN_FILE="$(pwd)/web-identity-token" - export AWS_WEB_IDENTITY_TOKEN_FILE="$(pwd)/web-identity-token"
# printf (not echo) avoids appending a trailing newline to the JWT # printf (not echo) avoids appending a trailing newline to the JWT.
# chmod 600 restricts the token file to owner-only (defensive —
# the container is already single-user root, but silences scanners).
- printf '%s' "$BITBUCKET_STEP_OIDC_TOKEN" > "$(pwd)/web-identity-token" - printf '%s' "$BITBUCKET_STEP_OIDC_TOKEN" > "$(pwd)/web-identity-token"
- chmod 600 "$(pwd)/web-identity-token"
# Clone the review agent repo (private — needs BITBUCKET_CLONE_TOKEN) # Clone the review agent repo (private — needs BITBUCKET_CLONE_TOKEN)
# SETUP REQUIRED: set BITBUCKET_CLONE_TOKEN (marked "Secured") in # SETUP REQUIRED: set BITBUCKET_CLONE_TOKEN (marked "Secured") in
@@ -181,6 +186,9 @@ pipelines:
# Feature branches represent new functionality. They must go through # Feature branches represent new functionality. They must go through
# dev first for integration testing before promotion to stg/main. # dev first for integration testing before promotion to stg/main.
"feature/*": "feature/*":
# Gate steps below use atlassian/default-image:4 (not python:3.12.7)
# because gates only run bash/shell — no Python needed. The default
# image is lighter and avoids pulling a full Python toolchain.
- step: - step:
name: "Gate: feature → dev" name: "Gate: feature → dev"
image: atlassian/default-image:4 image: atlassian/default-image:4