fix: block extension declaration deletions

This commit is contained in:
Mikael Hugo 2026-05-05 18:28:07 +02:00
parent 180f8e131e
commit 0d440bed7a
3 changed files with 68 additions and 6 deletions

View file

@ -96,7 +96,8 @@
"typecheck": "npm run build:pi && tsc --noEmit",
"typecheck:extensions": "npm run check:versioned-json && tsc --noEmit --project tsconfig.extensions.json",
"check:sf-inventory": "node scripts/check-sf-extension-inventory.mjs",
"check:versioned-json": "node scripts/check-versioned-json.mjs && npm run check:sf-inventory",
"check:protected-deletions": "node scripts/check-protected-deletions.mjs",
"check:versioned-json": "node scripts/check-protected-deletions.mjs && node scripts/check-versioned-json.mjs && npm run check:sf-inventory",
"format": "biome format --write .",
"format:check": "biome format .",
"lint": "npm run check:versioned-json && biome check .",

View file

@ -0,0 +1,40 @@
#!/usr/bin/env node
import { execFileSync } from "node:child_process";
const PROTECTED_PATHS = [":(glob)src/resources/extensions/**/*.d.ts"];
function git(args) {
return execFileSync("git", args, {
stdio: ["ignore", "pipe", "pipe"],
encoding: "utf-8",
}).trim();
}
function listDeleted(cached) {
const args = ["diff", "--name-only", "--diff-filter=D"];
if (cached) args.push("--cached");
args.push("--", ...PROTECTED_PATHS);
const out = git(args);
return out ? out.split("\n").filter(Boolean) : [];
}
const stagedOnly = process.argv.includes("--cached");
const deleted = stagedOnly
? listDeleted(true)
: [...new Set([...listDeleted(false), ...listDeleted(true)])];
if (deleted.length > 0) {
const mode = stagedOnly ? "staged " : "";
process.stderr.write(
`check-protected-deletions: refusing ${mode}protected declaration deletions:\n`,
);
for (const path of deleted) {
process.stderr.write(` ${path}\n`);
}
process.stderr.write(
"\nRestore these files or make an explicit reviewed deletion outside automation.\n",
);
process.exit(1);
}
process.stdout.write("check-protected-deletions: ok\n");

View file

@ -11,6 +11,7 @@ import {
import { join } from "node:path";
const MARKER = "# sf-secret-scan";
const PROTECTED_DELETIONS_MARKER = "# sf-protected-deletions";
function git(args) {
return execFileSync("git", args, {
@ -24,19 +25,35 @@ const repoRoot = git(["rev-parse", "--show-toplevel"]);
const hookDir = join(gitDir, "hooks");
const hookFile = join(hookDir, "pre-commit");
const hookCommand = `node "${join(repoRoot, "scripts", "secret-scan.mjs")}"`;
const protectedDeletionsCommand = `node "${join(
repoRoot,
"scripts",
"check-protected-deletions.mjs",
)}" --cached`;
mkdirSync(hookDir, { recursive: true });
if (existsSync(hookFile)) {
const current = readFileSync(hookFile, "utf8");
if (current.includes(MARKER)) {
process.stdout.write("secret-scan pre-commit hook already installed.\n");
const additions = [];
if (!current.includes(MARKER)) {
additions.push(MARKER, hookCommand);
}
if (!current.includes(PROTECTED_DELETIONS_MARKER)) {
additions.push(
PROTECTED_DELETIONS_MARKER,
"# Pre-commit hook: block accidental deletion of hand-written extension declarations",
protectedDeletionsCommand,
);
}
if (additions.length === 0) {
process.stdout.write("sf pre-commit hooks already installed.\n");
process.exit(0);
}
const next = `${current.replace(/\s*$/, "\n")}${MARKER}\n${hookCommand}\n`;
const next = `${current.replace(/\s*$/, "\n")}${additions.join("\n")}\n`;
writeFileSync(hookFile, next, "utf8");
process.stdout.write("secret-scan appended to existing pre-commit hook.\n");
process.stdout.write("sf pre-commit hooks appended to existing hook.\n");
process.exit(0);
}
@ -46,6 +63,10 @@ const hookBody = [
"# Pre-commit hook: scan staged files for hardcoded secrets",
hookCommand,
"",
"# sf-protected-deletions",
"# Pre-commit hook: block accidental deletion of hand-written extension declarations",
protectedDeletionsCommand,
"",
].join("\n");
writeFileSync(hookFile, hookBody, "utf8");
@ -55,4 +76,4 @@ try {
// Best effort on Windows filesystems that do not honor chmod.
}
process.stdout.write("secret-scan pre-commit hook installed.\n");
process.stdout.write("sf pre-commit hooks installed.\n");