fix(mindspace): guard published pages against blocked CDN scripts
Pre-bundle Chart.js, auto-rewrite common CDN references at publish and serve time, and block unknown external script src during publication scans so interactive dashboards keep working under CSP. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
const SCRIPT_SRC_PATTERN = /<script\b([^>]*)\bsrc\s*=\s*(['"])([^'"]+)\2([^>]*)>/gi;
|
||||
|
||||
export const KNOWN_PLATFORM_SCRIPT_ALIASES = [
|
||||
{
|
||||
id: 'chart.js',
|
||||
target: '/assets/chart.umd.min.js',
|
||||
patterns: [
|
||||
/cdn\.jsdelivr\.net\/npm\/chart\.js(?:@[^/]+)?\/dist\/chart\.(?:umd\.)?min\.js/i,
|
||||
/cdnjs\.cloudflare\.com\/ajax\/libs\/Chart\.js\/[^/]+\/chart\.(?:umd\.)?min\.js/i,
|
||||
/unpkg\.com\/chart\.js(?:@[^/]+)?\/dist\/chart\.(?:umd\.)?min\.js/i,
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
function normalizeScriptUrl(value) {
|
||||
return String(value ?? '').trim().replace(/&/g, '&');
|
||||
}
|
||||
|
||||
export function isExternalScriptSrc(src) {
|
||||
const normalized = normalizeScriptUrl(src);
|
||||
if (!normalized) return false;
|
||||
if (/^https?:\/\//i.test(normalized)) return true;
|
||||
if (/^\/\//.test(normalized)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function resolveKnownPlatformScriptTarget(src) {
|
||||
const normalized = normalizeScriptUrl(src);
|
||||
if (!isExternalScriptSrc(normalized)) return null;
|
||||
const probe = normalized.startsWith('//') ? `https:${normalized}` : normalized;
|
||||
for (const alias of KNOWN_PLATFORM_SCRIPT_ALIASES) {
|
||||
if (alias.patterns.some((pattern) => pattern.test(probe))) {
|
||||
return alias.target;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function findExternalScriptSources(html) {
|
||||
const sources = [];
|
||||
const seen = new Set();
|
||||
for (const match of String(html ?? '').matchAll(SCRIPT_SRC_PATTERN)) {
|
||||
const src = normalizeScriptUrl(match[3]);
|
||||
if (!isExternalScriptSrc(src) || seen.has(src)) continue;
|
||||
seen.add(src);
|
||||
sources.push(src);
|
||||
}
|
||||
return sources;
|
||||
}
|
||||
|
||||
export function rewriteKnownCdnScriptSources(html) {
|
||||
const rewrites = [];
|
||||
const next = String(html ?? '').replace(
|
||||
SCRIPT_SRC_PATTERN,
|
||||
(full, before, quote, src, after) => {
|
||||
const normalized = normalizeScriptUrl(src);
|
||||
const target = resolveKnownPlatformScriptTarget(normalized);
|
||||
if (!target) return full;
|
||||
rewrites.push({ from: normalized, to: target });
|
||||
return `<script${before}src=${quote}${target}${quote}${after}>`;
|
||||
},
|
||||
);
|
||||
return {
|
||||
html: next,
|
||||
rewrittenCount: rewrites.length,
|
||||
rewrites,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user