From 56b2a365285e76031426dd85141bb83be4af07a6 Mon Sep 17 00:00:00 2001 From: Jens Caers Date: Wed, 26 Aug 2026 22:12:52 +0200 Subject: [PATCH] frontend: add dependency-free source policy lint --- frontend/e2e/sourcePolicyLint.mjs | 43 +++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 frontend/e2e/sourcePolicyLint.mjs diff --git a/frontend/e2e/sourcePolicyLint.mjs b/frontend/e2e/sourcePolicyLint.mjs new file mode 100644 index 00000000..8b6eeed2 --- /dev/null +++ b/frontend/e2e/sourcePolicyLint.mjs @@ -0,0 +1,43 @@ +import fs from "node:fs"; +import path from "node:path"; +import process from "node:process"; + +const root = path.resolve(import.meta.dirname, "..", "src"); +const extensions = new Set([".ts", ".tsx"]); +const checks = [ + { pattern: /\bdebugger\s*;/g, message: "debugger statement" }, + { pattern: /@ts-ignore\b/g, message: "@ts-ignore is forbidden; use an explicit typed boundary" }, + { pattern: /\beval\s*\(/g, message: "eval() is forbidden" }, + { pattern: /\bnew\s+Function\s*\(/g, message: "new Function() is forbidden" }, +]; + +function walk(directory) { + const entries = fs.readdirSync(directory, { withFileTypes: true }); + const files = []; + for (const entry of entries) { + const full = path.join(directory, entry.name); + if (entry.isDirectory()) files.push(...walk(full)); + else if (extensions.has(path.extname(entry.name))) files.push(full); + } + return files; +} + +const violations = []; +for (const file of walk(root)) { + const text = fs.readFileSync(file, "utf8"); + for (const check of checks) { + check.pattern.lastIndex = 0; + for (const match of text.matchAll(check.pattern)) { + const line = text.slice(0, match.index).split("\n").length; + violations.push(`${path.relative(path.resolve(root, ".."), file)}:${line}: ${check.message}`); + } + } +} + +if (violations.length > 0) { + console.error("Frontend source-policy violations:"); + for (const violation of violations) console.error(` - ${violation}`); + process.exit(1); +} + +console.log("Frontend source-policy lint passed.");