Files
omniai-web/scripts/check-governance.mjs

81 lines
2.9 KiB
JavaScript

import fs from "node:fs";
import path from "node:path";
import process from "node:process";
const repoRoot = process.cwd();
const mediaExtensions = new Set([".png", ".jpg", ".jpeg", ".webp", ".gif", ".mp4", ".mov", ".webm", ".avif"]);
const textExtensions = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".json", ".html", ".css", ".md", ".env", ".example"]);
const scanRoots = ["src", "vite.config.ts", "index.html", "package.json", ".env.example"];
const allowedFiles = new Set([
normalizePath("src/data/ossAssets.ts"),
normalizePath("src/utils/ossImageOptimize.ts"),
]);
const forbiddenPatterns = [
{ label: "frontend env config", pattern: /\b(?:import\.meta\.env|VITE_[A-Z0-9_]+)\b/ },
{ label: "direct provider proxy", pattern: /\/dashscope-api\b|dashscope\.aliyuncs\.com/i },
{ label: "third-party demo media host", pattern: /picsum\.photos|xiuxiu-pro(?:-new)?\.meitudata\.com|meitudata\.com/i },
{ label: "hard-coded provider secret marker", pattern: /Bearer\s+sk-|DASHSCOPE_API_KEY|ACCESS_KEY_SECRET|SECRET_ACCESS_KEY/i },
{ label: "local media import", pattern: /from\s+["'][^"']*\/assets\/[^"']*\.(?:png|jpe?g|webp|gif|mp4|mov|webm|avif|svg)["']/i },
];
const failures = [];
function normalizePath(value) {
return value.replace(/\\/g, "/");
}
function walk(targetPath, visitor) {
if (!fs.existsSync(targetPath)) return;
const stat = fs.statSync(targetPath);
if (stat.isDirectory()) {
for (const entry of fs.readdirSync(targetPath)) {
if (entry === "node_modules" || entry === "dist" || entry === ".git") continue;
walk(path.join(targetPath, entry), visitor);
}
return;
}
visitor(targetPath, stat);
}
function report(file, message) {
failures.push(`${normalizePath(path.relative(repoRoot, file))}: ${message}`);
}
walk(path.join(repoRoot, "src", "assets"), (file) => {
if (mediaExtensions.has(path.extname(file).toLowerCase())) {
report(file, "media files must live in OSS, not src/assets");
}
});
for (const root of scanRoots) {
walk(path.join(repoRoot, root), (file) => {
const relative = normalizePath(path.relative(repoRoot, file));
const ext = path.extname(file).toLowerCase();
if (!textExtensions.has(ext) && !relative.endsWith(".env.example")) return;
if (relative.startsWith("src/assets/")) return;
const content = fs.readFileSync(file, "utf8");
const isAllowed = allowedFiles.has(relative);
for (const rule of forbiddenPatterns) {
if (isAllowed && (rule.label === "third-party demo media host" || rule.label === "hard-coded provider secret marker")) {
continue;
}
if (rule.pattern.test(content)) {
report(file, `forbidden ${rule.label}`);
}
}
});
}
if (failures.length) {
console.error("Governance check failed:");
for (const failure of failures) {
console.error(`- ${failure}`);
}
process.exit(1);
}
console.log("Governance check passed.");