From 8c3e0f9554581917475b52f609030044ed90539d Mon Sep 17 00:00:00 2001 From: Flea Flicker Date: Tue, 14 Apr 2026 16:10:04 +0000 Subject: [PATCH 1/2] feat(GRO-631): add security headers to nginx.conf Add X-Content-Type-Options, X-Frame-Options, Referrer-Policy, X-XSS-Protection, and Permissions-Policy headers to server block and static assets location. Co-Authored-By: Paperclip --- apps/web/nginx.conf | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apps/web/nginx.conf b/apps/web/nginx.conf index 89955f0..a70f242 100644 --- a/apps/web/nginx.conf +++ b/apps/web/nginx.conf @@ -3,10 +3,22 @@ server { root /usr/share/nginx/html; index index.html; + # Security headers + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always; + # Cache static assets location ~* \.(js|css|png|svg|ico|woff2)$ { expires 1y; add_header Cache-Control "public, immutable"; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always; } # Proxy API calls to the API service From 70e9465b68017020447efdaacc72c4133977ba15 Mon Sep 17 00:00:00 2001 From: Flea Flicker Date: Tue, 14 Apr 2026 16:22:23 +0000 Subject: [PATCH 2/2] fix(GRO-631): add tag validation to promote-prod workflow - Validate tag format against regex YYYY.MM.DD-sha7 before proceeding - Verify image exists in GHCR using gh api with packages: read permission - Add packages: read permission to job permissions block Co-Authored-By: Paperclip --- .github/workflows/promote-prod.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/.github/workflows/promote-prod.yml b/.github/workflows/promote-prod.yml index 483e8cd..110d1a3 100644 --- a/.github/workflows/promote-prod.yml +++ b/.github/workflows/promote-prod.yml @@ -14,7 +14,29 @@ jobs: runs-on: ubuntu-latest permissions: contents: read + packages: read steps: + - name: Validate tag format + run: | + TAG="${{ inputs.tag }}" + if ! echo "$TAG" | grep -qE '^[0-9]{4}\.[0-9]{2}\.[0-9]{2}-[a-f0-9]{7}$'; then + echo "::error::Invalid tag format: '$TAG'. Expected format: YYYY.MM.DD-sha7 (e.g. 2026.03.28-f1b85bf)" + exit 1 + fi + echo "Tag format valid: $TAG" + + - name: Verify image exists in GHCR + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + TAG="${{ inputs.tag }}" + # Check that the API image exists — if API was pushed, web/migrate were too + if ! gh api "/orgs/groombook/packages/container/api/versions" --jq ".[].metadata.container.tags[]" 2>/dev/null | grep -qF "$TAG"; then + echo "::error::Image ghcr.io/groombook/api:$TAG not found in GHCR. Verify the tag was built and pushed." + exit 1 + fi + echo "Image verified: ghcr.io/groombook/api:$TAG exists" + - name: Generate infra repo token id: infra-token uses: tibdex/github-app-token@v2