Apply final-review cleanup: robots sitemap, CI guards, header parity

- Remove Sitemap line from robots.txt (no @astrojs/sitemap installed; was
  pointing to a 404 that would trip Google Search Console).
- Align Permissions-Policy across all three enforcement layers (middleware,
  .htaccess, Cloudflare Transform Rule in OPERATOR-CHECKLIST) by dropping the
  stray fullscreen=(self) from the middleware.
- Bump Lighthouse CI numberOfRuns from 1 to 3 to dampen CI-runner noise.
- Add CI guard that fails the build if any <TBD:...> marker survives into
  dist/ — prevents a legally incomplete imprint from shipping by accident.
- Add SFTP_* secret null-guard before the rsync --delete step so a missing
  secret fails loudly instead of targeting the SSH user's home root.
- Document the set:html compile-time-constant invariant in DualValueProps.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-24 17:34:27 +02:00
parent 7e0d341c89
commit d98d73b14a
5 changed files with 17 additions and 4 deletions

View File

@@ -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 <TBD:...> 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/"

View File

@@ -8,7 +8,7 @@ module.exports = {
'http://localhost/imprint/index.html',
'http://localhost/privacy/index.html',
],
numberOfRuns: 1,
numberOfRuns: 3,
settings: {
preset: 'desktop',
},

View File

@@ -1,4 +1,2 @@
User-agent: *
Allow: /
Sitemap: https://www.cameleer.io/sitemap-index.xml

View File

@@ -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.',

View File

@@ -26,13 +26,13 @@ export function buildSecurityHeaders(): Record<string, string> {
"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 {