diff --git a/website/README.md b/website/README.md
index 20aeb12..85b488a 100644
--- a/website/README.md
+++ b/website/README.md
@@ -49,8 +49,13 @@ to GitHub Pages, so the official OWASP site reflects release-controlled source.
Manual runs can deploy only when dispatched from `main`.
GitHub Pages does not support custom response headers. The document-level
-content security policy covers supported directives, but hosting-level headers
-such as `frame-ancestors` require a configurable hosting edge.
+content security policy in `src/layouts/Base.astro` covers only the directives
+a `` policy actually enforces. `frame-ancestors` (and
+`report-uri`/`report-to`, `sandbox`) are ignored in a meta policy, so
+clickjacking protection is **not** in place: it needs a real
+`Content-Security-Policy` or `X-Frame-Options` HTTP response header from a
+configurable hosting edge. `scripts/verify-site.mjs` deliberately does not
+assert `frame-ancestors` so CI never reports protection that does not exist.
## One-time maintainer setup
diff --git a/website/astro.config.mjs b/website/astro.config.mjs
index 545f823..f834e3e 100644
--- a/website/astro.config.mjs
+++ b/website/astro.config.mjs
@@ -6,7 +6,22 @@ export default defineConfig({
site: 'https://owasp.github.io',
base: '/openshield',
integrations: [sitemap()],
+ build: {
+ // Never inline hoisted ]+)/i.exec(attrs);
+ const type = typeMatch ? typeMatch[1].toLowerCase() : '';
+ if (type === 'application/json' || type === 'application/ld+json' || type === 'speculationrules') continue;
+ if (body) {
+ failures.push(`${relative} has an inline script element that script-src 'self' will block on the deployed site`);
+ break;
+ }
+ }
+ // Only directives a CSP actually enforces. frame-ancestors
+ // is deliberately absent: browsers ignore it in a meta policy, and GitHub
+ // Pages cannot set the HTTP response header that would make it effective, so
+ // asserting its presence here would report clickjacking protection that does
+ // not exist. That protection has to come from a real edge/header if the site
+ // ever moves to configurable hosting.
+ for (const directive of ['object-src \'none\'', 'base-uri \'self\'', 'form-action \'self\'']) {
+ if (!csp.includes(directive)) failures.push(`${relative} is missing CSP directive ${directive}`);
+ }
for (const match of html.matchAll(/href="([^"]+)"/g)) {
const href = match[1];
if (!href.startsWith('/openshield/')) continue;
diff --git a/website/src/layouts/Base.astro b/website/src/layouts/Base.astro
index ff9f1f6..ae143ad 100644
--- a/website/src/layouts/Base.astro
+++ b/website/src/layouts/Base.astro
@@ -98,11 +98,16 @@ const siteJsonLd = {
{breadcrumbs && }
-
+