68 lines
2.0 KiB
JavaScript
68 lines
2.0 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Secret scanner — fails if known secret patterns appear in tracked files.
|
|
* Run before committing: node scripts/check-secrets.mjs
|
|
* Exit 0 = clean, exit 1 = secrets found.
|
|
*
|
|
* Patterns (loose on purpose — catches real keys, tolerates placeholders):
|
|
* - sb_secret_* (Supabase service role) unless the token is a placeholder
|
|
* - sk-[A-Za-z0-9]{20,} (OpenAI/Anthropic-style)
|
|
* - AKIA[0-9A-Z]{16} (AWS)
|
|
*/
|
|
import { execSync } from "node:child_process";
|
|
|
|
const patterns = [
|
|
{ name: "supabase-service-role", re: /sb_secret_[A-Za-z0-9_]{10,}/ },
|
|
{ name: "openai-style-key", re: /sk-[A-Za-z0-9]{20,}/ },
|
|
{ name: "aws-access-key", re: /AKIA[0-9A-Z]{16}/ },
|
|
{ name: "bearer-jwt", re: /eyJ[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{10,}/ },
|
|
];
|
|
|
|
// Files that are allowed to mention placeholder-shaped values
|
|
const placeholders = new Set([
|
|
"sb_secret_placeholder",
|
|
"sk-your-key-here",
|
|
"sk-ant-your-key-here",
|
|
]);
|
|
|
|
let tracked;
|
|
try {
|
|
tracked = execSync("git ls-files", { encoding: "utf8" })
|
|
.split("\n")
|
|
.filter(Boolean);
|
|
} catch {
|
|
console.error("Not a git repo — run from the repository root.");
|
|
process.exit(1);
|
|
}
|
|
|
|
const findings = [];
|
|
for (const file of tracked) {
|
|
if (/package-lock\.json$/.test(file)) continue; // lockfiles: skip (noise)
|
|
let content;
|
|
try {
|
|
content = require("node:fs").readFileSync(file, "utf8");
|
|
} catch {
|
|
continue; // binary / unreadable
|
|
}
|
|
for (const { name, re } of patterns) {
|
|
const match = content.match(re);
|
|
if (match) {
|
|
const value = match[0];
|
|
if (placeholders.has(value)) continue;
|
|
findings.push({ file, name, value });
|
|
}
|
|
}
|
|
}
|
|
|
|
if (findings.length === 0) {
|
|
console.log("✅ No secrets found in tracked files.");
|
|
process.exit(0);
|
|
}
|
|
|
|
console.error("❌ Possible secrets found in tracked files:");
|
|
for (const { file, name, value } of findings) {
|
|
console.error(` - ${file} [${name}]: ${value}`);
|
|
}
|
|
console.error("\nScrub the value, rotate the key if it was ever live, then re-run.");
|
|
process.exit(1);
|