Files
Web_FreeCAD_Bitbybit/scripts/check-security-baseline.mjs

33 lines
2.1 KiB
JavaScript

import { readFile, readdir } from 'node:fs/promises'
import { resolve } from 'node:path'
const root = resolve(new URL('..', import.meta.url).pathname)
const baseline = JSON.parse(await readFile(resolve(root, 'config/security-baseline.json'), 'utf8'))
const headers = await readFile(resolve(root, baseline.headersPath), 'utf8')
const fail = (message) => { throw new Error(`Security baseline: ${message}`) }
if (baseline.schemaVersion !== 1 || baseline.browserScope !== 'chrome-only') fail('schema or browser scope is invalid.')
for (const [name, value] of Object.entries(baseline.requiredHeaders)) if (!headers.includes(`${name}: ${value}`)) fail(`missing ${name}.`)
const cspLine = headers.split('\n').find((line) => line.includes('Content-Security-Policy:'))
if (!cspLine) fail('Content-Security-Policy is missing.')
for (const [directive, values] of Object.entries(baseline.contentSecurityPolicy)) {
const expected = `${directive} ${values.join(' ')}`
if (!cspLine.includes(expected)) fail(`CSP directive is incomplete: ${expected}`)
}
if (cspLine.includes("'unsafe-eval'")) fail('CSP must not allow unsafe-eval.')
const viteConfig = await readFile(resolve(root, 'vite.config.ts'), 'utf8')
for (const [name, value] of Object.entries(baseline.requiredHeaders)) if (!viteConfig.includes(`'${name}': '${value}'`) && !viteConfig.includes(`"${name}": "${value}"`)) fail(`Vite preview headers do not declare ${name}.`)
const sourceFiles = []
const collect = async (directory) => {
for (const entry of await readdir(directory, { withFileTypes: true })) {
const path = resolve(directory, entry.name)
if (entry.isDirectory()) await collect(path)
else if (/\.(ts|tsx|mjs)$/.test(entry.name)) sourceFiles.push(path)
}
}
await collect(resolve(root, 'src'))
for (const path of sourceFiles) {
const source = await readFile(path, 'utf8')
for (const pattern of baseline.forbiddenSourcePatterns) if (source.includes(pattern)) fail(`forbidden dynamic execution pattern '${pattern}' in ${path.replace(`${root}/`, '')}.`)
}
console.log(JSON.stringify({ status: 'security-baseline-pass', browserScope: baseline.browserScope, sourceFiles: sourceFiles.length, csp: cspLine.trim() }, null, 2))