mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-22 18:13:41 +08:00
fix(gateguard): rewrite routineBashMsg to use fact-presentation pattern (#1531)
* fix(gateguard): rewrite routineBashMsg to use fact-presentation pattern The imperative 'Quote user's instruction verbatim. Then retry.' phrasing triggers Claude Code's runtime anti-prompt-injection filter, deadlocking the first Bash call of every session. The sibling gates (edit, write, destructive) use multi-point fact-list framing that the runtime accepts. Align routineBashMsg with that pattern to restore the gate's intended behavior without changing run(), state schema, or any public API. Closes #1530 * docs(gateguard): sync SKILL.md routine gate spec with new message format CodeRabbit flagged that skills/gateguard/SKILL.md still described the pre-fix imperative message. Update the Routine Bash Gate section to match the numbered fact-list format used by the new routineBashMsg().
This commit is contained in:
@@ -62,13 +62,7 @@ function hashSessionKey(prefix, value) {
|
||||
}
|
||||
|
||||
function resolveSessionKey(data) {
|
||||
const directCandidates = [
|
||||
data && data.session_id,
|
||||
data && data.sessionId,
|
||||
data && data.session && data.session.id,
|
||||
process.env.CLAUDE_SESSION_ID,
|
||||
process.env.ECC_SESSION_ID,
|
||||
];
|
||||
const directCandidates = [data && data.session_id, data && data.sessionId, data && data.session && data.session.id, process.env.CLAUDE_SESSION_ID, process.env.ECC_SESSION_ID];
|
||||
|
||||
for (const candidate of directCandidates) {
|
||||
const sanitized = sanitizeSessionKey(candidate);
|
||||
@@ -101,12 +95,18 @@ function loadState() {
|
||||
const state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
|
||||
const lastActive = state.last_active || 0;
|
||||
if (Date.now() - lastActive > SESSION_TIMEOUT_MS) {
|
||||
try { fs.unlinkSync(stateFile); } catch (_) { /* ignore */ }
|
||||
try {
|
||||
fs.unlinkSync(stateFile);
|
||||
} catch (_) {
|
||||
/* ignore */
|
||||
}
|
||||
return { checked: [], last_active: Date.now() };
|
||||
}
|
||||
return state;
|
||||
}
|
||||
} catch (_) { /* ignore */ }
|
||||
} catch (_) {
|
||||
/* ignore */
|
||||
}
|
||||
return { checked: [], last_active: Date.now() };
|
||||
}
|
||||
|
||||
@@ -139,7 +139,11 @@ function saveState(state) {
|
||||
fs.renameSync(tmpFile, stateFile);
|
||||
} catch (error) {
|
||||
if (error && (error.code === 'EEXIST' || error.code === 'EPERM')) {
|
||||
try { fs.unlinkSync(stateFile); } catch (_) { /* ignore */ }
|
||||
try {
|
||||
fs.unlinkSync(stateFile);
|
||||
} catch (_) {
|
||||
/* ignore */
|
||||
}
|
||||
fs.renameSync(tmpFile, stateFile);
|
||||
} else {
|
||||
throw error;
|
||||
@@ -147,7 +151,11 @@ function saveState(state) {
|
||||
}
|
||||
} catch (_) {
|
||||
if (tmpFile) {
|
||||
try { fs.unlinkSync(tmpFile); } catch (_) { /* ignore */ }
|
||||
try {
|
||||
fs.unlinkSync(tmpFile);
|
||||
} catch (_) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,7 +194,9 @@ function isChecked(key) {
|
||||
// Ignore files that disappear between readdir/stat/unlink.
|
||||
}
|
||||
}
|
||||
} catch (_) { /* ignore */ }
|
||||
} catch (_) {
|
||||
/* ignore */
|
||||
}
|
||||
})();
|
||||
|
||||
// --- Sanitize file path against injection ---
|
||||
@@ -198,13 +208,15 @@ function sanitizePath(filePath) {
|
||||
const code = char.codePointAt(0);
|
||||
const isAsciiControl = code <= 0x1f || code === 0x7f;
|
||||
const isBidiOverride = (code >= 0x200e && code <= 0x200f) || (code >= 0x202a && code <= 0x202e) || (code >= 0x2066 && code <= 0x2069);
|
||||
sanitized += (isAsciiControl || isBidiOverride) ? ' ' : char;
|
||||
sanitized += isAsciiControl || isBidiOverride ? ' ' : char;
|
||||
}
|
||||
return sanitized.trim().slice(0, 500);
|
||||
}
|
||||
|
||||
function normalizeForMatch(value) {
|
||||
return String(value || '').replace(/\\/g, '/').toLowerCase();
|
||||
return String(value || '')
|
||||
.replace(/\\/g, '/')
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
function isClaudeSettingsPath(filePath) {
|
||||
@@ -265,7 +277,7 @@ function editGateMsg(filePath) {
|
||||
'1. List ALL files that import/require this file (use Grep)',
|
||||
'2. List the public functions/classes affected by this change',
|
||||
'3. If this file reads/writes data files, show field names, structure, and date format (use redacted or synthetic values, not raw production data)',
|
||||
'4. Quote the user\'s current instruction verbatim',
|
||||
"4. Quote the user's current instruction verbatim",
|
||||
'',
|
||||
'Present the facts, then retry the same operation.'
|
||||
].join('\n');
|
||||
@@ -281,7 +293,7 @@ function writeGateMsg(filePath) {
|
||||
'1. Name the file(s) and line(s) that will call this new file',
|
||||
'2. Confirm no existing file serves the same purpose (use Glob)',
|
||||
'3. If this file reads/writes data files, show field names, structure, and date format (use redacted or synthetic values, not raw production data)',
|
||||
'4. Quote the user\'s current instruction verbatim',
|
||||
"4. Quote the user's current instruction verbatim",
|
||||
'',
|
||||
'Present the facts, then retry the same operation.'
|
||||
].join('\n');
|
||||
@@ -295,7 +307,7 @@ function destructiveBashMsg() {
|
||||
'',
|
||||
'1. List all files/data this command will modify or delete',
|
||||
'2. Write a one-line rollback procedure',
|
||||
'3. Quote the user\'s current instruction verbatim',
|
||||
"3. Quote the user's current instruction verbatim",
|
||||
'',
|
||||
'Present the facts, then retry the same operation.'
|
||||
].join('\n');
|
||||
@@ -305,8 +317,12 @@ function routineBashMsg() {
|
||||
return [
|
||||
'[Fact-Forcing Gate]',
|
||||
'',
|
||||
'Quote the user\'s current instruction verbatim.',
|
||||
'Then retry the same operation.'
|
||||
'Before the first Bash command this session, present these facts:',
|
||||
'',
|
||||
'1. The current user request in one sentence',
|
||||
'2. What this specific command verifies or produces',
|
||||
'',
|
||||
'Present the facts, then retry the same operation.'
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
@@ -340,7 +356,7 @@ function run(rawInput) {
|
||||
const rawToolName = data.tool_name || '';
|
||||
const toolInput = data.tool_input || {};
|
||||
// Normalize: case-insensitive matching via lookup map
|
||||
const TOOL_MAP = { 'edit': 'Edit', 'write': 'Write', 'multiedit': 'MultiEdit', 'bash': 'Bash' };
|
||||
const TOOL_MAP = { edit: 'Edit', write: 'Write', multiedit: 'MultiEdit', bash: 'Bash' };
|
||||
const toolName = TOOL_MAP[rawToolName.toLowerCase()] || rawToolName;
|
||||
|
||||
if (toolName === 'Edit' || toolName === 'Write') {
|
||||
|
||||
@@ -84,7 +84,8 @@ Triggers on: `rm -rf`, `git reset --hard`, `git push --force`, `drop table`, etc
|
||||
### Routine Bash Gate (once per session)
|
||||
|
||||
```
|
||||
Quote the user's current instruction verbatim.
|
||||
1. The current user request in one sentence
|
||||
2. What this specific command verifies or produces
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
Reference in New Issue
Block a user