fix(verdict-gate): accept only a full 40- or 64-hex reviewed commit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-10-26 13:13:22 +01:00
commit cf7af30b46
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
3 changed files with 77 additions and 15 deletions

View file

@ -27,7 +27,12 @@
import { spawnSync } from 'node:child_process';
import { LESSONS_FILE_NAME, MARK_START, MARK_END, withPointer } from './recurrence.mjs';
const SHA_RE = /^[0-9a-f]{7,40}$/i;
/**
* A full commit id: 40 hex (SHA-1) or 64 hex (SHA-256), nothing shorter (M11).
* A short id can also be a branch name, which resolves to wherever the branch
* points now (DJ3).
*/
export const FULL_SHA_RE = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i;
const LESSONS_REL = `.claude/rules/${LESSONS_FILE_NAME}`; // posix, as `git diff` prints it
const CLAUDE_REL = 'CLAUDE.md';
const SYMLINK_MODE = '120000';
@ -41,8 +46,9 @@ export function reviewedSha(reviewText) {
const fm = /^---\r?\n([\s\S]*?)\r?\n---\r?\n/.exec(String(reviewText ?? ''));
const m = fm && /^scope_sha_end:[ \t]*(["']?)([^"'\s#]*)\1[ \t]*(?:#.*)?$/m.exec(fm[1]);
// A commit sha only: a ref name (HEAD, main, a tag) resolves to whatever it
// points at now, and coverage would pass whatever was reviewed (round 4).
return m && SHA_RE.test(m[2]) ? m[2] : '';
// points at now, and coverage would pass whatever was reviewed (round 4);
// so can a short id that is also a branch name (M11).
return m && FULL_SHA_RE.test(m[2]) ? m[2] : '';
}
function git(repo, args) {
@ -101,14 +107,17 @@ export function checkReviewedCommit({ repo, sha } = {}) {
if (typeof repo !== 'string' || repo === '') return stale('no repository given');
const given = String(sha ?? '');
const notCommit = `reviewed commit ${given} is not a commit in this repository`;
// 1. Hex only, so a value like `--output=/x` never reaches git as an option.
if (!SHA_RE.test(given)) return stale(notCommit);
// 1. Full hex only, so a value like `--output=/x` never reaches git as an option.
if (!FULL_SHA_RE.test(given)) return stale(notCommit);
const resolved = git(repo, ['rev-parse', '--verify', '--quiet', `${given}^{commit}`]);
if (resolved.status !== 0) {
const note = gitNote(resolved);
return stale(note ? `${notCommit} (${note})` : notCommit);
}
const full = resolved.stdout.trim();
// A ref named like an id (64 hex in a SHA-1 repository) also resolves: the
// id must be the commit itself, not a name that points at one (M11).
if (full !== given.toLowerCase()) return stale(`${given} is not a full commit id in this repository`);
// 2. The review is of HEAD itself.
const head = git(repo, ['rev-parse', '--verify', 'HEAD']);