wgutils
Version:
Tools for managing working groups
90 lines • 3.72 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkPr = void 0;
const node_child_process_1 = require("node:child_process");
const node_util_1 = require("node:util");
// I don't trust the minified version, but have checked the raw code
// @ts-ignore
const parse_js_1 = __importDefault(require("parse-diff/parse.js"));
const parseDiff = parse_js_1.default;
const execAsync = (0, node_util_1.promisify)(node_child_process_1.exec);
const safeChars = /^[^\x00-\x08\x0B\x0C\x0E-\x1F\u200B\u200C\u200D\uFEFF\uE000-\uF8FF]*$/u;
function checkPatch(config, patch, expectedHash) {
const lines = patch.split("\n");
const hash = lines[0].split(" ")[1];
if (hash !== expectedHash) {
throw new Error(`Unexpected hash - ${hash} !== ${expectedHash}`);
}
const SUMMARY_START = "\n\n---\n ";
const SUMMARY_END = "\n\n";
const summaryStart = patch.indexOf(SUMMARY_START);
if (summaryStart < 0) {
throw new Error("Diff parse failure - could not find summaryStart");
}
const summaryEnd = patch.indexOf(SUMMARY_END, summaryStart + SUMMARY_START.length);
if (summaryEnd < 0) {
throw new Error("Diff parse failure - could not find summaryEnd");
}
const _summary = patch.slice(summaryStart + SUMMARY_START.length, summaryEnd);
const diffText = patch.slice(summaryEnd + SUMMARY_END.length);
const diff = parseDiff(diffText);
if (diff.length !== 1) {
throw new Error(`Too many files changed`);
}
const file = diff[0];
if (!file.from || !file.to || file.from !== file.to) {
throw new Error(`File was renamed`);
}
const agendasFolder = (config.repoSubpath ? config.repoSubpath.replace(/\/+$/, "") + "/" : "") +
(config.agendasFolder
? config.agendasFolder.replace(/\/+$/, "")
: "agendas");
if (!file.to.startsWith(agendasFolder)) {
throw new Error(`Not within the '${agendasFolder}' folder: '${file.to}'`);
}
if (!/^\/[0-9]+\/[0-9]{1,2}-[A-Za-z]+\/[0-9]+-[-a-z0-9]+\.md$/.test(file.to.substring(agendasFolder.length))) {
throw new Error(`Not an agenda file: ${file.to}`);
}
for (const chunk of file.chunks) {
for (const change of chunk.changes) {
switch (change.type) {
case "normal": {
continue;
}
case "add": {
if (!safeChars.test(change.content)) {
throw new Error(`Aborting due to unexpected characters`);
}
break;
}
case "del": {
// Deleting a blank link is allowed; otherwise reject
if (change.content.slice(1).trim() !== "") {
throw new Error(`Aborting due to deletion`);
}
break;
}
default: {
const never = change;
throw new Error(`Aborting due to unexpected change type ${JSON.stringify(never)}`);
}
}
}
}
}
async function checkPr(config, prNumber, hash) {
if (!Number.isFinite(prNumber)) {
throw new Error(`Invalid PR number`);
}
if (!/^[a-z0-9]{40}$/.test(hash)) {
throw new Error(`Invalid commit hash`);
}
const { stdout: patch } = await execAsync(`gh pr diff ${prNumber} --patch`);
checkPatch(config, patch, hash);
console.log("Looks safe");
}
exports.checkPr = checkPr;
//# sourceMappingURL=index.js.map