mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-05-14 02:10:07 +08:00
fix: close block-no-verify bypass holes
Backport Jamkris's fix for case-insensitive core.hooksPath overrides and the git commit -tn template-path false positive. Verified locally on current main with 25/25 block-no-verify tests and node tests/run-all.js passing 2369/2369.
This commit is contained in:
parent
393d397efa
commit
6be241a463
@ -35,7 +35,12 @@ const GIT_COMMANDS_WITH_NO_VERIFY = [
|
|||||||
*/
|
*/
|
||||||
const VALID_BEFORE_GIT = ' \t\n\r;&|$`(<{!"\']/.~\\';
|
const VALID_BEFORE_GIT = ' \t\n\r;&|$`(<{!"\']/.~\\';
|
||||||
|
|
||||||
const GIT_CONFIG_KEY_PREFIX = 'core.hooksPath=';
|
// Git config section and variable names are case-insensitive
|
||||||
|
// (subsection names are case-sensitive but core.hooksPath has none),
|
||||||
|
// so we normalize the candidate token to lowercase before matching.
|
||||||
|
// See https://git-scm.com/docs/git-config — "The variable names are
|
||||||
|
// case-insensitive."
|
||||||
|
const GIT_CONFIG_KEY_PREFIX = 'core.hookspath=';
|
||||||
|
|
||||||
const COMMIT_OPTIONS_WITH_VALUE = new Set([
|
const COMMIT_OPTIONS_WITH_VALUE = new Set([
|
||||||
'-m',
|
'-m',
|
||||||
@ -67,7 +72,12 @@ const COMMIT_OPTIONS_WITH_INLINE_VALUE = [
|
|||||||
'--pathspec-from-file=',
|
'--pathspec-from-file=',
|
||||||
];
|
];
|
||||||
|
|
||||||
const COMMIT_SHORT_OPTIONS_WITH_VALUE = new Set(['m', 'F', 'C', 'c']);
|
// Short options that take a value. When seen as part of a combined
|
||||||
|
// short-option token (e.g. -tn), git's parser treats the rest of the
|
||||||
|
// token as the option's value (template path 'n' here), so the scanner
|
||||||
|
// must stop at this character — anything after it is the inline value,
|
||||||
|
// not another flag.
|
||||||
|
const COMMIT_SHORT_OPTIONS_WITH_VALUE = new Set(['m', 'F', 'C', 'c', 't']);
|
||||||
|
|
||||||
function tokenizeShellWords(input, start = 0, end = input.length) {
|
function tokenizeShellWords(input, start = 0, end = input.length) {
|
||||||
const tokens = [];
|
const tokens = [];
|
||||||
@ -413,17 +423,21 @@ function hasHooksPathOverride(input, detected) {
|
|||||||
|
|
||||||
for (let i = 0; i < tokens.length; i++) {
|
for (let i = 0; i < tokens.length; i++) {
|
||||||
const value = tokens[i].value;
|
const value = tokens[i].value;
|
||||||
|
// Git config section + variable names are case-insensitive, so a
|
||||||
|
// bypass attempt like `core.HOOKSPATH=...` or `core.hookspath=...`
|
||||||
|
// must compare against the lowercased token.
|
||||||
|
const lowered = value.toLowerCase();
|
||||||
|
|
||||||
if (value === '-c') {
|
if (value === '-c') {
|
||||||
const next = tokens[i + 1] && tokens[i + 1].value;
|
const next = tokens[i + 1] && tokens[i + 1].value;
|
||||||
if (typeof next === 'string' && next.startsWith(GIT_CONFIG_KEY_PREFIX)) {
|
if (typeof next === 'string' && next.toLowerCase().startsWith(GIT_CONFIG_KEY_PREFIX)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.startsWith(`-c${GIT_CONFIG_KEY_PREFIX}`)) {
|
if (lowered.startsWith(`-c${GIT_CONFIG_KEY_PREFIX}`)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -179,6 +179,24 @@ if (test('blocks plain text input with --no-verify', () => {
|
|||||||
assert.strictEqual(r.code, 2, `expected exit 2, got ${r.code}`);
|
assert.strictEqual(r.code, 2, `expected exit 2, got ${r.code}`);
|
||||||
})) passed++; else failed++;
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
// --- Case-insensitivity of git config keys + -t template short option ---
|
||||||
|
|
||||||
|
if (test('blocks case-variant core.hooksPath (lowercase)', () => {
|
||||||
|
const r = runHook({ tool_input: { command: 'git -c core.hookspath=/dev/null commit -m "msg"' } });
|
||||||
|
assert.strictEqual(r.code, 2, `expected exit 2, got ${r.code}`);
|
||||||
|
assert.ok(/core\.hookspath/i.test(r.stderr), `stderr should mention core.hooksPath: ${r.stderr}`);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('blocks case-variant core.hooksPath (uppercase)', () => {
|
||||||
|
const r = runHook({ tool_input: { command: 'git -c core.HOOKSPATH=/dev/null commit -m "msg"' } });
|
||||||
|
assert.strictEqual(r.code, 2, `expected exit 2, got ${r.code}`);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('still allows -tn (n is the -t template path, not a flag)', () => {
|
||||||
|
const r = runHook({ tool_input: { command: 'git commit -tn -m "msg"' } });
|
||||||
|
assert.strictEqual(r.code, 0, `expected exit 0, got ${r.code}: ${r.stderr}`);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
console.log('─'.repeat(50));
|
console.log('─'.repeat(50));
|
||||||
console.log(`Passed: ${passed} Failed: ${failed}`);
|
console.log(`Passed: ${passed} Failed: ${failed}`);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user