diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index fe07ad0..1ea6580 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -31,6 +31,14 @@ jobs: - name: Build site run: npm run build + - name: Guard — no TBD markers may ship in built HTML + run: | + if grep -rl 'TBD:' dist 2>/dev/null | grep -E '\.(html|svg)$'; then + echo "Built output contains unfilled markers." + echo "Fill in imprint.astro and privacy.astro operator fields before merging to main." + exit 1 + fi + - name: Validate HTML run: npm run lint:html @@ -80,6 +88,11 @@ jobs: SFTP_HOST: ${{ secrets.SFTP_HOST }} SFTP_PATH: ${{ secrets.SFTP_PATH }} run: | + # Fail loudly if any secret is missing — otherwise rsync --delete + # could be directed at the SSH user's home root. + : "${SFTP_USER:?SFTP_USER secret must be set}" + : "${SFTP_HOST:?SFTP_HOST secret must be set}" + : "${SFTP_PATH:?SFTP_PATH secret must be set}" rsync -avz --delete \ -e "ssh -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=yes -o UserKnownHostsFile=~/.ssh/known_hosts" \ dist/ "$SFTP_USER@$SFTP_HOST:$SFTP_PATH/" diff --git a/lighthouserc.cjs b/lighthouserc.cjs index 23983bf..a9606c0 100644 --- a/lighthouserc.cjs +++ b/lighthouserc.cjs @@ -8,7 +8,7 @@ module.exports = { 'http://localhost/imprint/index.html', 'http://localhost/privacy/index.html', ], - numberOfRuns: 1, + numberOfRuns: 3, settings: { preset: 'desktop', }, diff --git a/public/robots.txt b/public/robots.txt index 137a33f..c2a49f4 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,4 +1,2 @@ User-agent: * Allow: / - -Sitemap: https://www.cameleer.io/sitemap-index.xml diff --git a/src/components/sections/DualValueProps.astro b/src/components/sections/DualValueProps.astro index c92ceeb..6c8a86b 100644 --- a/src/components/sections/DualValueProps.astro +++ b/src/components/sections/DualValueProps.astro @@ -4,6 +4,8 @@ interface Tile { capability: string; } +// tile.capability is a compile-time constant defined below — never feed +// user-supplied or CMS content into set:html further down (XSS risk). const tiles: Tile[] = [ { outcome: 'Cut debugging time in half.', diff --git a/src/middleware.ts b/src/middleware.ts index ab8f1c3..daad692 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -26,13 +26,13 @@ export function buildSecurityHeaders(): Record { "object-src 'none'", ].join('; '); + // Must match .htaccess and the Cloudflare Transform Rule in OPERATOR-CHECKLIST.md. const permissionsPolicy = [ 'geolocation=()', 'microphone=()', 'camera=()', 'payment=()', 'usb=()', - 'fullscreen=(self)', ].join(', '); return {