246 lines
8.7 KiB
JavaScript
246 lines
8.7 KiB
JavaScript
import console from "node:console";
|
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
import { dirname, join, resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import process from "node:process";
|
|
|
|
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
let passed = 0;
|
|
let failed = 0;
|
|
let warned = 0;
|
|
const failures = [];
|
|
|
|
function check(name, condition, detail = "") {
|
|
if (condition) {
|
|
passed += 1;
|
|
console.log(` ok ${name}`);
|
|
return;
|
|
}
|
|
failed += 1;
|
|
failures.push(detail ? `${name}: ${detail}` : name);
|
|
console.error(` FAIL ${name}${detail ? ` — ${detail}` : ""}`);
|
|
}
|
|
|
|
function warn(name, detail = "") {
|
|
warned += 1;
|
|
console.warn(` warn ${name}${detail ? ` — ${detail}` : ""}`);
|
|
}
|
|
|
|
function text(path) {
|
|
return readFileSync(join(root, path), "utf8");
|
|
}
|
|
|
|
function has(path, pattern) {
|
|
const value = text(path);
|
|
return typeof pattern === "string" ? value.includes(pattern) : pattern.test(value);
|
|
}
|
|
|
|
const packageDirs = readdirSync(join(root, "packages"), { withFileTypes: true })
|
|
.filter(
|
|
(entry) =>
|
|
entry.isDirectory() && existsSync(join(root, "packages", entry.name, "package.json")),
|
|
)
|
|
.map((entry) => entry.name);
|
|
const manifests = packageDirs
|
|
.map((dir) => ({
|
|
dir,
|
|
manifest: JSON.parse(text(`packages/${dir}/package.json`)),
|
|
}))
|
|
.filter(({ manifest }) => manifest.name?.startsWith("@wrnexus/"));
|
|
|
|
check("root version is 0.7.0", JSON.parse(text("package.json")).version === "0.7.0");
|
|
const temporaryTypecheckFiles = ["focus-shims.d.ts", "tsconfig.focus.json"].filter((path) =>
|
|
existsSync(join(root, path)),
|
|
);
|
|
const rootTsconfig = JSON.parse(text("tsconfig.json"));
|
|
const rootExcludes = new Set(rootTsconfig.exclude ?? []);
|
|
check(
|
|
"root typecheck excludes temporary focused files",
|
|
rootExcludes.has("focus-shims.d.ts") &&
|
|
rootExcludes.has("tsconfig.focus.json") &&
|
|
rootExcludes.has("**/focus-shims.d.ts") &&
|
|
rootExcludes.has("**/tsconfig.focus.json"),
|
|
"Keep the temporary focused typecheck files outside the root TypeScript program.",
|
|
);
|
|
if (temporaryTypecheckFiles.length > 0) {
|
|
warn(
|
|
"temporary focused typecheck files are local leftovers",
|
|
`${temporaryTypecheckFiles.join(", ")} will be removed by: bun run repair:workspace`,
|
|
);
|
|
}
|
|
check(
|
|
"lint and formatting ignore temporary focused files",
|
|
has("eslint.config.js", '"focus-shims.d.ts"') &&
|
|
has("eslint.config.js", '"tsconfig.focus.json"') &&
|
|
has(".prettierignore", "focus-shims.d.ts") &&
|
|
has(".prettierignore", "tsconfig.focus.json"),
|
|
"Temporary focused helpers must not create lint or formatting cascades before workspace repair.",
|
|
);
|
|
check(
|
|
"all framework package versions align",
|
|
manifests.every(({ manifest }) => manifest.version === "0.7.0"),
|
|
[...new Set(manifests.map(({ manifest }) => manifest.version))].join(", "),
|
|
);
|
|
for (const name of ["security", "cache", "image", "observability", "benchmark"]) {
|
|
check(
|
|
`@wrnexus/${name} package exists`,
|
|
existsSync(join(root, "packages", name, "src", "index.ts")),
|
|
);
|
|
}
|
|
|
|
check(
|
|
"context-aware URL sanitizer is wired into compiler",
|
|
has("packages/compiler/src/codegen.ts", "sanitizeUrlAttribute"),
|
|
);
|
|
check(
|
|
"URL security checks avoid lint-blocked control-character regexes",
|
|
has("packages/compiler/src/codegen.ts", "stripAsciiControlAndSpace") &&
|
|
has("packages/security/src/url.ts", "hasAsciiControlOrSpace") &&
|
|
has("packages/syntax/src/diagnostics.ts", "stripAsciiControlAndSpace") &&
|
|
!has("packages/compiler/src/codegen.ts", /\\u0000-\\u0020/) &&
|
|
!has("packages/security/src/url.ts", /\\u0000-\\u0020/) &&
|
|
!has("packages/syntax/src/diagnostics.ts", /\\u0000-\\u0020/),
|
|
"Use character-code checks instead of control-character regular expressions.",
|
|
);
|
|
check(
|
|
"trusted HTML requires an explicit policy",
|
|
has("packages/security/src/trusted-html.ts", "createTrustedHtml"),
|
|
);
|
|
check(
|
|
"reactive URL updates are sanitized",
|
|
has("packages/csr/src/reactive-runtime.ts", "sanitizeReactiveUrl"),
|
|
);
|
|
check(
|
|
"SSR store state uses secure serialization",
|
|
has("packages/ssr/src/store-context.ts", "serializeForHtml"),
|
|
);
|
|
check("prototype pollution keys are rejected", has("packages/security/src/object.ts", "__proto__"));
|
|
check(
|
|
"SSRF helper blocks private networks",
|
|
has("packages/security/src/fetch.ts", "isPrivateAddress"),
|
|
);
|
|
check(
|
|
"CSRF checks Origin and Fetch Metadata",
|
|
has("packages/core/src/csrf.ts", /sec-fetch-site|validateOrigin/),
|
|
);
|
|
check(
|
|
"security headers include CSP and isolation",
|
|
has("packages/core/src/headers.ts", "Origin-Agent-Cluster"),
|
|
);
|
|
check(
|
|
"request limits are automatic runtime middleware",
|
|
has("packages/dev-server/src/runtime.ts", "requestHardening"),
|
|
);
|
|
check(
|
|
"gateway enforces request limits",
|
|
has("packages/dev-server/src/gateway.ts", "GatewayRequestLimits"),
|
|
);
|
|
check(
|
|
"gateway enforces WebSocket limits",
|
|
has("packages/dev-server/src/gateway.ts", "maxQueuedMessages"),
|
|
);
|
|
check(
|
|
"realtime enforces per-room security",
|
|
has("packages/core/src/realtime.ts", "RealtimeSecurityOptions"),
|
|
);
|
|
check(
|
|
"database instrumentation detects N+1 patterns",
|
|
has("packages/db/src/performance.ts", "WRN-DB-DUPLICATE-QUERY"),
|
|
);
|
|
check(
|
|
"CSS performance audit is available",
|
|
has("packages/styles/src/audit.ts", "WRN-CSS-TRANSITION-ALL"),
|
|
);
|
|
check(
|
|
"static runtime analysis is exported",
|
|
has("packages/compiler/src/analysis.ts", "analyzeRuntimeRequirements"),
|
|
);
|
|
check(
|
|
"static pages default to zero framework JavaScript",
|
|
has("packages/dev-server/src/runtime.ts", 'navigation.mode ?? "auto"') &&
|
|
has("packages/dev-server/src/runtime.ts", "scripts.length > 0"),
|
|
);
|
|
check(
|
|
"Brotli delivery is preferred",
|
|
has("packages/dev-server/src/runtime.ts", "brotliCompressSync"),
|
|
);
|
|
check(
|
|
"Happy DOM validation events and elements use one DOM type system",
|
|
has("packages/validation/test/validation.test.ts", "type Event as HappyDOMEvent") &&
|
|
has(
|
|
"packages/validation/test/validation.test.ts",
|
|
"type HTMLInputElement as HappyDOMHTMLInputElement",
|
|
) &&
|
|
has(
|
|
"packages/validation/test/validation.test.ts",
|
|
"type HTMLFormElement as HappyDOMHTMLFormElement",
|
|
) &&
|
|
has(
|
|
"packages/validation/test/validation.test.ts",
|
|
"function windowEvent(win: Window, type: string, init?: IEventInit): HappyDOMEvent",
|
|
) &&
|
|
!has(
|
|
"packages/validation/test/validation.test.ts",
|
|
/as unknown as (?:HTMLInputElement|HTMLFormElement|HTMLElement)/,
|
|
),
|
|
"Use Happy DOM Event and element types together; do not mix them with browser DOM globals.",
|
|
);
|
|
check(
|
|
"DevToolbar security checks include CSRF",
|
|
has("packages/dev-toolbar/src/rules/security.ts", "security/missing-csrf"),
|
|
);
|
|
check(
|
|
"DevToolbar performance checks include hydration",
|
|
has("packages/dev-toolbar/src/rules/performance.ts", "performance/hydration-count"),
|
|
);
|
|
check("release migration includes 0.7.0", has("packages/cli/src/update.ts", 'version: "0.7.0"'));
|
|
check(
|
|
"21-point implementation matrix exists",
|
|
existsSync(join(root, "docs", "SECURITY-PERFORMANCE-0.7.md")),
|
|
);
|
|
check(
|
|
"production audit script exists",
|
|
existsSync(join(root, "scripts", "security-performance-audit.mjs")),
|
|
);
|
|
check(
|
|
"workspace repair command exists",
|
|
existsSync(join(root, "scripts", "repair-workspace.mjs")) &&
|
|
existsSync(join(root, "scripts", "test-workspace-repair.mjs")) &&
|
|
JSON.parse(text("package.json")).scripts?.["repair:workspace"] ===
|
|
"node scripts/repair-workspace.mjs" &&
|
|
JSON.parse(text("package.json")).scripts?.["check:workspace"] ===
|
|
"node scripts/repair-workspace.mjs --check" &&
|
|
has("scripts/release.ts", "check:workspace"),
|
|
);
|
|
check(
|
|
"security audit scans Git-tracked release files",
|
|
has("scripts/security-performance-audit.mjs", 'execFileSync("git", ["ls-files", "-z"]') &&
|
|
has("scripts/security-performance-audit.mjs", "SEC-NO-TRACKED-SECRET-FILES"),
|
|
);
|
|
check("SBOM generator exists", existsSync(join(root, "scripts", "generate-sbom.mjs")));
|
|
check(
|
|
"SBOM includes transitive bun.lock packages",
|
|
has("scripts/generate-sbom.mjs", "lock.packages"),
|
|
);
|
|
check(
|
|
"SBOM parse errors preserve their original cause",
|
|
has("scripts/generate-sbom.mjs", "{ cause: error }") &&
|
|
has("scripts/test-workspace-repair.mjs", 'import process from "node:process"'),
|
|
);
|
|
check(
|
|
"staged packages are secret-scanned and hashed",
|
|
has("scripts/publish-packages.ts", "validateAndHashStage") &&
|
|
has("scripts/publish-packages.ts", "PACKAGE-INTEGRITY.json"),
|
|
);
|
|
check("benchmark runner exists", existsSync(join(root, "scripts", "benchmark-framework.mjs")));
|
|
check("release gate formats generated UI references", has("scripts/release.ts", "--write"));
|
|
|
|
console.log(`\n${passed} passed`);
|
|
console.log(`${warned} warnings`);
|
|
console.log(`${failed} failed`);
|
|
if (failed) {
|
|
console.error("\nFailures:");
|
|
for (const failure of failures) console.error(`- ${failure}`);
|
|
process.exitCode = 1;
|
|
}
|