mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-11 15:39:04 +02:00
feat(hooks): memorable-user-prompt-hook.ts — consent gate, deny veto, HIGH-tier pre-scan, fail-closed receipt, trust envelope; runExternal in spawn-bin
The PR's hook exec'd the vendor binary with the full environment and passed its stdout to Claude verbatim. It is now the house pattern: a fail-open bash shim over a .ts twin that (1) gates on the memorable_recall consent key, (2) skips repos whose trust policy is deny or read-only, (3) scans the prompt (raw bytes and decoded string leaves) and refuses to hand over a HIGH-tier credential shape, (4) writes a fail-closed egress receipt naming the local executable it ran, (5) spawns the vendor in its own process group with an allowlisted environment and group-kills it on timeout, (6) accepts only a string additionalContext back, caps it at 8 KiB on a UTF-8 boundary and wraps it in the trust envelope, and (7) records an `output-written` outcome after the stdout write completes. One deadline clock (4.5 s) undercuts Claude Code's 5 s kill and bounds both ledger writes through the new lockBudgetMs option on writeReceipt/writeOutcome (default unchanged). spawn-bin gains runExternal for external executables (detached group, stderr drained, stdin EPIPE handled, stdout capped, win32 refused). The wiring test pins the sink fail-closed and sweeps hosts/. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
3034769813
commit
d4dbeb6d42
@@ -58,21 +58,6 @@ describe('gstack-memorable', () => {
|
||||
expect(remaining).toEqual(['/foreign/hook']);
|
||||
});
|
||||
|
||||
test('hook delegates stdin/stdout and fails open when Memorable is unavailable', () => {
|
||||
const f = fixture();
|
||||
const payload = '{"session_id":"s1","prompt":"repeat the task"}';
|
||||
const delegated = spawnSync(HOOK, [], { env: envFor(f), input: payload, encoding: 'utf8' });
|
||||
expect(delegated.status).toBe(0);
|
||||
expect(delegated.stdout).toContain('"additionalContext":"remembered"');
|
||||
expect(readFileSync(f.log, 'utf8')).toContain('hook user-prompt');
|
||||
|
||||
const missingEnv = { ...process.env, HOME: f.home, MEMORABLE_BIN: join(f.home, 'missing') };
|
||||
const missing = spawnSync(HOOK, [], { env: missingEnv, input: payload, encoding: 'utf8' });
|
||||
expect(missing.status).toBe(0);
|
||||
expect(missing.stdout).toBe('');
|
||||
expect(missing.stderr).toBe('');
|
||||
});
|
||||
|
||||
test('enable refuses when Memorable already registered the hook itself', () => {
|
||||
// Memorable's own installer (`memorable start`, `setup`, `install-hooks`)
|
||||
// writes this same UserPromptSubmit hook under its own name, and that is
|
||||
|
||||
Reference in New Issue
Block a user