[BRE-2049] Add tag existence check to prevent overwriting images (#528)

Prevents build-bitwarden-lite from overwriting existing production
container image tags by checking both GHCR and ACR registries.

Changes:
- Check both GHCR and ACR before building
- Distinguish 'not found' from 'error' to fail closed
- Only validates version tags (X.Y.Z format)
- Skips check for dev/branch tags to allow rebuilds
- Fails with clear error if tag exists or check fails

Security: Prevents silent failures from registry errors, rate limits,
auth failures, or network timeouts from allowing overwrites.

This provides defense-in-depth protection against tag overwrites
from any workflow source, regardless of how it was triggered.
This commit is contained in:
brandonbiete
2026-06-26 12:13:06 -04:00
committed by GitHub
parent dfcf1937ce
commit 1821ae8710

View File

@@ -279,6 +279,51 @@ jobs:
echo "- Server: ${SERVER_REGISTRY}/*:${SERVER_TAG}" >> $GITHUB_STEP_SUMMARY
echo "- Web: ${WEB_IMAGE}:${WEB_TAG}" >> $GITHUB_STEP_SUMMARY
- name: Check if image tag already exists
env:
IMAGE_TAG: ${{ steps.tag.outputs.image_tag }}
GHCR_REGISTRY: ghcr.io/bitwarden
ACR_REGISTRY: bitwardenprod.azurecr.io
run: |
# Only check version tags (numeric), skip dev/branch tags
if [[ "$IMAGE_TAG" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Checking if version tag ${IMAGE_TAG} already exists..."
# Check GHCR
output=$(skopeo inspect "docker://${GHCR_REGISTRY}/lite:${IMAGE_TAG}" 2>&1)
status=$?
if [[ $status -eq 0 ]]; then
echo "::error::❌ Tag ${IMAGE_TAG} already exists at ${GHCR_REGISTRY}/lite:${IMAGE_TAG}"
echo "::error::Refusing to overwrite existing production image."
echo "::error::If you need to rebuild this version, delete the existing tag first."
exit 1
elif echo "$output" | grep -qiE 'manifest unknown|not found|name unknown'; then
echo "✅ GHCR tag ${IMAGE_TAG} is available"
else
echo "::error::Could not verify GHCR tag existence (skopeo failed): $output"
exit 1
fi
# Check ACR
output=$(skopeo inspect "docker://${ACR_REGISTRY}/lite:${IMAGE_TAG}" 2>&1)
status=$?
if [[ $status -eq 0 ]]; then
echo "::error::❌ Tag ${IMAGE_TAG} already exists at ${ACR_REGISTRY}/lite:${IMAGE_TAG}"
echo "::error::Refusing to overwrite existing production image."
echo "::error::If you need to rebuild this version, delete the existing tag first."
exit 1
elif echo "$output" | grep -qiE 'manifest unknown|not found|name unknown'; then
echo "✅ ACR tag ${IMAGE_TAG} is available"
else
echo "::error::Could not verify ACR tag existence (skopeo failed): $output"
exit 1
fi
echo "✅ Tag ${IMAGE_TAG} is available on both registries"
else
echo "Skipping tag check for non-version tag: ${IMAGE_TAG}"
fi
- name: Build and push Docker image
id: build-docker
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0