diff --git a/admin/public/auth-check.html b/admin/public/auth-check.html
index 917e4931..c5fc763a 100644
--- a/admin/public/auth-check.html
+++ b/admin/public/auth-check.html
@@ -5,6 +5,7 @@
// This page is loaded in a hidden iframe from the MkDocs header.
// It reads the auth state from this origin's localStorage and
// posts it back to the parent window via postMessage.
+// The parent passes its origin as ?origin=... so we can target the reply.
(function() {
var authenticated = false;
try {
@@ -16,11 +17,20 @@
}
}
} catch(e) {}
+ // Only post back to the declared parent origin (prevents state disclosure to arbitrary embedders)
+ var params = new URLSearchParams(location.search);
+ var targetOrigin = params.get('origin');
+ if (!targetOrigin) return;
+ // Validate targetOrigin is a proper origin (protocol + host, no path)
+ try {
+ var url = new URL(targetOrigin);
+ targetOrigin = url.origin;
+ } catch(e) { return; }
if (window.parent && window.parent !== window) {
window.parent.postMessage({
type: 'cml-auth-status',
authenticated: authenticated
- }, '*');
+ }, targetOrigin);
}
})();
diff --git a/api/src/modules/docs/header-builder.service.ts b/api/src/modules/docs/header-builder.service.ts
index c7ebc3c9..74198f60 100644
--- a/api/src/modules/docs/header-builder.service.ts
+++ b/api/src/modules/docs/header-builder.service.ts
@@ -716,7 +716,7 @@ class HeaderBuilderService {
// 2. Cross-origin check via hidden iframe + postMessage
var iframe = document.createElement('iframe');
iframe.style.display = 'none';
- iframe.src = base + '/auth-check.html';
+ iframe.src = base + '/auth-check.html?origin=' + encodeURIComponent(location.origin);
window.addEventListener('message', function(event) {
if (event.origin !== base) return;
if (event.data && event.data.type === 'cml-auth-status' && event.data.authenticated) {
diff --git a/mkdocs/docs/overrides/main.html b/mkdocs/docs/overrides/main.html
index a1a01e25..14471db0 100644
--- a/mkdocs/docs/overrides/main.html
+++ b/mkdocs/docs/overrides/main.html
@@ -24,8 +24,6 @@
-
-
@@ -89,8 +87,6 @@
-
-
@@ -205,7 +201,7 @@
// 2. Cross-origin check via hidden iframe + postMessage
var iframe = document.createElement('iframe');
iframe.style.display = 'none';
- iframe.src = base + '/auth-check.html';
+ iframe.src = base + '/auth-check.html?origin=' + encodeURIComponent(location.origin);
window.addEventListener('message', function(event) {
if (event.origin !== base) return;
if (event.data && event.data.type === 'cml-auth-status' && event.data.authenticated) {
diff --git a/nginx/conf.d/default.conf.template b/nginx/conf.d/default.conf.template
index 09956015..0f7db57a 100644
--- a/nginx/conf.d/default.conf.template
+++ b/nginx/conf.d/default.conf.template
@@ -7,7 +7,7 @@ server {
# Auth check iframe — allows cross-origin login state detection (MkDocs header)
location = /auth-check.html {
- add_header Content-Security-Policy "frame-ancestors *" always;
+ add_header Content-Security-Policy "frame-ancestors 'self' http://localhost:* http://127.0.0.1:*" always;
set $upstream_admin_authcheck http://changemaker-v2-admin:3000;
proxy_pass $upstream_admin_authcheck;
proxy_set_header Host $host;
diff --git a/nginx/conf.d/services.conf.template b/nginx/conf.d/services.conf.template
index d369ae44..0d00d175 100644
--- a/nginx/conf.d/services.conf.template
+++ b/nginx/conf.d/services.conf.template
@@ -372,7 +372,6 @@ server {
# Auth check iframe — allows root domain to embed this tiny page
# for cross-origin login state detection (MkDocs header)
location = /auth-check.html {
- add_header X-Frame-Options "ALLOW-FROM https://${DOMAIN}" always;
add_header Content-Security-Policy "frame-ancestors 'self' https://${DOMAIN} http://${DOMAIN}" always;
set $upstream_admin_authcheck http://changemaker-v2-admin:3000;
proxy_pass $upstream_admin_authcheck;