import { readFile } from 'node:fs/promises'; import { dist, requireDist } from './lib/dist.mjs'; import { walk } from './lib/walk.mjs'; const failures = []; await requireDist(); const files = await walk(dist); const ALLOWED_JS_ASSET_PATTERNS = [ /[/\\]_astro[/\\]Analytics\.astro_astro_type_script_index_0_lang\.[\w-]+\.js$/, ]; const jsFiles = files.filter( (file) => file.endsWith('.js') && !ALLOWED_JS_ASSET_PATTERNS.some((pattern) => pattern.test(file)) ); if (jsFiles.length > 0) { failures.push( `Unexpected JavaScript assets:\n${jsFiles.map((file) => `- ${file}`).join('\n')}` ); } // Script tags are only allowed if they declare one of these safe `type` // attributes or match one of the known executable scripts below. All other // scripts, including untyped ones, which default to executable JavaScript, are // flagged. const SAFE_SCRIPT_TYPES = new Set([ 'application/ld+json', 'importmap', 'speculationrules', ]); const ANALYTICS_SCRIPT_SRC_PATTERN = /\bsrc=["']\/_astro\/Analytics\.astro_astro_type_script_index_0_lang\.[\w-]+\.js["']/i; function isSafeScriptTag(tag) { if (tag.includes('data-theme-script')) return true; if (tag.includes('data-thumbnail-iframe-script')) return true; if (tag.includes('data-video-thumbnail-script')) return true; if (ANALYTICS_SCRIPT_SRC_PATTERN.test(tag)) return true; const typeMatch = tag.match(/\btype=["']([^"']+)["']/i); if (!typeMatch) return false; return SAFE_SCRIPT_TYPES.has(typeMatch[1].trim().toLowerCase()); } for (const file of files.filter((candidate) => candidate.endsWith('.html'))) { const html = await readFile(file, 'utf8'); const scripts = (html.match(/]*>/gi) ?? []).filter( (tag) => !isSafeScriptTag(tag) ); if (scripts.length) { failures.push(`Unexpected script tag in ${file}:\n${scripts.join('\n')}`); } // Inline event handlers (onclick=, onload=, etc.) execute JavaScript even // without a