Ctrl K
ComunidadGuías…

scripts/privacy-scan.mjs

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { execFileSync } from "node:child_process";
import { readdir, readFile } from "node:fs/promises";
import { extname, join, relative } from "node:path";

const root = new URL("../", import.meta.url);
// This generated cache is excluded from both Git and container context. Never
// exempt a tracked copy: an accidentally staged cache must still fail the scan.
const generatedCache = ".impeccable/hook.cache.json";
let omitUntrackedCache = false;
try { omitUntrackedCache = execFileSync("git", ["ls-files", "--", generatedCache], { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim() === ""; } catch { /* Without Git, scan everything as before. */ }
const textExtensions = new Set([
  ".css",
  ".html",
  ".js",
  ".json",
  ".md",
  ".mjs",
  ".ts",
  ".tsx",
  ".yaml",
  ".yml"
]);
const ignoredDirectories = new Set([
  ".git",
  "coverage",
  "dist",
  "node_modules",
  "private",
  "data",
  "backups"
]);
const extensionlessTextFiles = new Set([
  ".dockerignore",
  ".env.example",
  ".gitignore",
  ".npmrc",
  "LICENSE"
]);

const prohibited = [
  {
    label: "private Gmail message identifier",
    // Match a populated Gmail-style hexadecimal message ID, not the legacy
    // schema field name used by synthetic importer tests.
    pattern: /\b(?:email|gmail)_id\b\s*:\s*["'`][0-9a-f]{12,}["'`]/i
  },
  { label: "Amazon order number", pattern: /\b\d{3}-\d{7}-\d{7}\b/ },
  { label: "Bambu order number", pattern: /\bEN\d{18,}\b/ },
  { label: "private inventory source path", pattern: /(?:^|["'`\s])\.\.\/inventory\/inventory\.json\b/ },
  { label: "plaintext bearer token", pattern: /authorization\s*[:=]\s*["'`]bearer\s+[a-z0-9._~-]{32,}/i },
  {
    label: "legacy brand identifier",
    pattern: new RegExp(["forge", "[ _-]?", "ledger"].join(""), "i")
  },
  { label: "local macOS user path", pattern: /\/Users\/[^/\s]+\// },
  { label: "private deployment address", pattern: /\b192\.168\.4\.(?:34|54)\b/ },
  {
    label: "maintainer private identity",
    pattern: new RegExp(`\\b${["Grae", "tzer"].join("")}\\b`, "i")
  }
];

async function collect(directory) {
  const entries = await readdir(directory, { withFileTypes: true });
  const files = [];
  for (const entry of entries) {
    if (entry.isDirectory() && ignoredDirectories.has(entry.name)) continue;
    const path = join(directory, entry.name);
    if (omitUntrackedCache && relative(root.pathname, path) === generatedCache) continue;
    if (entry.isDirectory()) files.push(...(await collect(path)));
    else if (textExtensions.has(extname(entry.name)) || extensionlessTextFiles.has(entry.name)) files.push(path);
  }
  return files;
}

const findings = [];
for (const path of await collect(root.pathname)) {
  const content = await readFile(path, "utf8");
  for (const rule of prohibited) {
    if (rule.pattern.test(content)) {
      findings.push(`${relative(root.pathname, path)}: ${rule.label}`);
    }
  }
}

if (findings.length > 0) {
  console.error("BenchLedger privacy scan failed:\n" + findings.map((finding) => `- ${finding}`).join("\n"));
  process.exit(1);
}

console.log("BenchLedger privacy scan passed.");