mirror of
https://github.com/affaan-m/ECC.git
synced 2026-10-14 12:15:29 +02:00
Carry forward PR #3055 at92f53c4contoef648e01, preserving contributor attribution and current main. Focused maintainer fixes reuse shared input ceilings, preserve late root context through outer bootstrap, bound frontmatter before immutable parsing, and assert real oversized input in the limits workflow.
112 lines
3.0 KiB
JavaScript
112 lines
3.0 KiB
JavaScript
'use strict';
|
|
|
|
const REGISTERED_HOOK_MAX_STDIN_BYTES = 1024 * 1024;
|
|
const MAX_CONTEXT_TOKEN_CHARS = 256;
|
|
|
|
function decodeJsonString(raw) {
|
|
try {
|
|
return JSON.parse(`"${raw}"`);
|
|
} catch {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
function createHookContextScanner() {
|
|
const context = { hookEventName: '', toolName: '', stopHookActive: null };
|
|
let depth = 0;
|
|
let inString = false;
|
|
let escaped = false;
|
|
let stringRole = '';
|
|
let stringBuffer = '';
|
|
let currentKey = '';
|
|
let state = 'root';
|
|
let primitive = '';
|
|
|
|
const commitPrimitive = () => {
|
|
if (currentKey === 'stop_hook_active') {
|
|
if (primitive === 'true') context.stopHookActive = true;
|
|
else if (primitive === 'false') context.stopHookActive = false;
|
|
}
|
|
primitive = '';
|
|
};
|
|
|
|
const push = chunk => {
|
|
for (const char of String(chunk || '')) {
|
|
if (inString) {
|
|
if (escaped) {
|
|
if (stringRole && stringBuffer.length < MAX_CONTEXT_TOKEN_CHARS) {
|
|
stringBuffer += `\\${char}`;
|
|
}
|
|
escaped = false;
|
|
continue;
|
|
}
|
|
if (char === '\\') {
|
|
escaped = true;
|
|
continue;
|
|
}
|
|
if (char === '"') {
|
|
const value = decodeJsonString(stringBuffer);
|
|
if (stringRole === 'key') {
|
|
currentKey = value;
|
|
state = 'colon';
|
|
} else if (stringRole === 'value') {
|
|
if (currentKey === 'hook_event_name') context.hookEventName = value;
|
|
else if (currentKey === 'tool_name') context.toolName = value;
|
|
state = 'afterValue';
|
|
}
|
|
inString = false;
|
|
stringRole = '';
|
|
stringBuffer = '';
|
|
continue;
|
|
}
|
|
if (stringRole && stringBuffer.length < MAX_CONTEXT_TOKEN_CHARS) stringBuffer += char;
|
|
continue;
|
|
}
|
|
|
|
if (char === '"') {
|
|
inString = true;
|
|
stringRole = depth === 1 && state === 'key'
|
|
? 'key'
|
|
: depth === 1 && state === 'value'
|
|
&& (currentKey === 'hook_event_name' || currentKey === 'tool_name')
|
|
? 'value'
|
|
: '';
|
|
stringBuffer = '';
|
|
continue;
|
|
}
|
|
if (char === '{' || char === '[') {
|
|
depth += 1;
|
|
if (depth === 1) state = 'key';
|
|
continue;
|
|
}
|
|
if (char === '}' || char === ']') {
|
|
if (depth === 1 && primitive) commitPrimitive();
|
|
depth = Math.max(0, depth - 1);
|
|
continue;
|
|
}
|
|
if (depth !== 1 || /\s/.test(char)) continue;
|
|
if (state === 'colon' && char === ':') {
|
|
state = 'value';
|
|
primitive = '';
|
|
continue;
|
|
}
|
|
if ((state === 'value' || state === 'afterValue') && char === ',') {
|
|
if (state === 'value') commitPrimitive();
|
|
currentKey = '';
|
|
state = 'key';
|
|
continue;
|
|
}
|
|
if (state === 'value' && currentKey === 'stop_hook_active' && primitive.length < 5) {
|
|
primitive += char;
|
|
}
|
|
}
|
|
};
|
|
|
|
return { context, push };
|
|
}
|
|
|
|
module.exports = {
|
|
REGISTERED_HOOK_MAX_STDIN_BYTES,
|
|
createHookContextScanner,
|
|
};
|