@praxis.guard/auditor-cli 0.0.33 → 0.0.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/dist/approval/argv-fingerprint.d.ts +10 -1
  2. package/dist/approval/argv-fingerprint.d.ts.map +1 -1
  3. package/dist/approval/argv-fingerprint.js +10 -1
  4. package/dist/approval/argv-fingerprint.js.map +1 -1
  5. package/dist/approval/hook-inline-approval.d.ts +2 -0
  6. package/dist/approval/hook-inline-approval.d.ts.map +1 -1
  7. package/dist/approval/hook-inline-approval.js +6 -2
  8. package/dist/approval/hook-inline-approval.js.map +1 -1
  9. package/dist/approval/mcp-flow.d.ts +4 -2
  10. package/dist/approval/mcp-flow.d.ts.map +1 -1
  11. package/dist/approval/mcp-flow.js +9 -3
  12. package/dist/approval/mcp-flow.js.map +1 -1
  13. package/dist/approval/redeem.d.ts +2 -0
  14. package/dist/approval/redeem.d.ts.map +1 -1
  15. package/dist/approval/redeem.js +7 -2
  16. package/dist/approval/redeem.js.map +1 -1
  17. package/dist/bridge/execution-ticket.d.ts +3 -0
  18. package/dist/bridge/execution-ticket.d.ts.map +1 -1
  19. package/dist/bridge/execution-ticket.js +38 -9
  20. package/dist/bridge/execution-ticket.js.map +1 -1
  21. package/dist/bridge/shell-approval-bridge.d.ts +14 -5
  22. package/dist/bridge/shell-approval-bridge.d.ts.map +1 -1
  23. package/dist/bridge/shell-approval-bridge.js +47 -24
  24. package/dist/bridge/shell-approval-bridge.js.map +1 -1
  25. package/dist/hooks/agent-message.d.ts.map +1 -1
  26. package/dist/hooks/agent-message.js +26 -14
  27. package/dist/hooks/agent-message.js.map +1 -1
  28. package/dist/hooks/before-shell-io.d.ts +3 -0
  29. package/dist/hooks/before-shell-io.d.ts.map +1 -0
  30. package/dist/hooks/before-shell-io.js +26 -0
  31. package/dist/hooks/before-shell-io.js.map +1 -0
  32. package/dist/hooks/before-shell-mutate.d.ts +23 -0
  33. package/dist/hooks/before-shell-mutate.d.ts.map +1 -0
  34. package/dist/hooks/before-shell-mutate.js +74 -0
  35. package/dist/hooks/before-shell-mutate.js.map +1 -0
  36. package/dist/hooks/before-shell-skipped.d.ts +11 -0
  37. package/dist/hooks/before-shell-skipped.d.ts.map +1 -0
  38. package/dist/hooks/before-shell-skipped.js +49 -0
  39. package/dist/hooks/before-shell-skipped.js.map +1 -0
  40. package/dist/hooks/before-shell-types.d.ts +12 -0
  41. package/dist/hooks/before-shell-types.d.ts.map +1 -0
  42. package/dist/hooks/before-shell-types.js +2 -0
  43. package/dist/hooks/before-shell-types.js.map +1 -0
  44. package/dist/hooks/run-before-shell.d.ts +2 -10
  45. package/dist/hooks/run-before-shell.d.ts.map +1 -1
  46. package/dist/hooks/run-before-shell.js +63 -142
  47. package/dist/hooks/run-before-shell.js.map +1 -1
  48. package/dist/index.d.ts +2 -2
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +2 -2
  51. package/dist/index.js.map +1 -1
  52. package/dist/mcp/evaluate-guard.d.ts.map +1 -1
  53. package/dist/mcp/evaluate-guard.js +20 -9
  54. package/dist/mcp/evaluate-guard.js.map +1 -1
  55. package/dist/mcp/guard-approval-block.d.ts +1 -0
  56. package/dist/mcp/guard-approval-block.d.ts.map +1 -1
  57. package/dist/mcp/guard-approval-block.js +1 -0
  58. package/dist/mcp/guard-approval-block.js.map +1 -1
  59. package/dist/policies.v1.json +4 -0
  60. package/dist/policy/index.d.ts +4 -0
  61. package/dist/policy/index.d.ts.map +1 -1
  62. package/dist/policy/index.js +6 -0
  63. package/dist/policy/index.js.map +1 -1
  64. package/dist/shell/analyze-command-aggregate.d.ts +16 -0
  65. package/dist/shell/analyze-command-aggregate.d.ts.map +1 -0
  66. package/dist/shell/analyze-command-aggregate.js +89 -0
  67. package/dist/shell/analyze-command-aggregate.js.map +1 -0
  68. package/dist/shell/analyze-command-invocations.d.ts +11 -0
  69. package/dist/shell/analyze-command-invocations.d.ts.map +1 -0
  70. package/dist/shell/analyze-command-invocations.js +113 -0
  71. package/dist/shell/analyze-command-invocations.js.map +1 -0
  72. package/dist/shell/analyze-command.d.ts +7 -0
  73. package/dist/shell/analyze-command.d.ts.map +1 -0
  74. package/dist/shell/analyze-command.js +46 -0
  75. package/dist/shell/analyze-command.js.map +1 -0
  76. package/dist/shell/analyze-command.types.d.ts +38 -0
  77. package/dist/shell/analyze-command.types.d.ts.map +1 -0
  78. package/dist/shell/analyze-command.types.js +2 -0
  79. package/dist/shell/analyze-command.types.js.map +1 -0
  80. package/dist/shell/evaluate.d.ts +15 -18
  81. package/dist/shell/evaluate.d.ts.map +1 -1
  82. package/dist/shell/evaluate.js +57 -47
  83. package/dist/shell/evaluate.js.map +1 -1
  84. package/dist/shell/governed-tools.d.ts +18 -1
  85. package/dist/shell/governed-tools.d.ts.map +1 -1
  86. package/dist/shell/governed-tools.js +60 -1
  87. package/dist/shell/governed-tools.js.map +1 -1
  88. package/dist/shell/guard-eval.d.ts +15 -0
  89. package/dist/shell/guard-eval.d.ts.map +1 -0
  90. package/dist/shell/guard-eval.js +35 -0
  91. package/dist/shell/guard-eval.js.map +1 -0
  92. package/dist/shell/parse-segments.d.ts +14 -0
  93. package/dist/shell/parse-segments.d.ts.map +1 -0
  94. package/dist/shell/parse-segments.js +41 -0
  95. package/dist/shell/parse-segments.js.map +1 -0
  96. package/package.json +1 -1
@@ -1,6 +1,26 @@
1
1
  function reasonsSummary(reasons) {
2
2
  return reasons.length > 0 ? reasons.join(",") : "policy";
3
3
  }
4
+ /** First lines in user_message — Cursor delivers this on deny, not agent_message. */
5
+ function mutateGuardWaitUserLead() {
6
+ return ("Required next step: call MCP tool `guard_wait` on the Praxis guard server before retrying this action.\n\n");
7
+ }
8
+ function mutateGuardWaitUserAgentBlock(proposalKind, argvJson, inline) {
9
+ if (inline) {
10
+ return (`Agent: MUST call MCP \`guard_wait\` now with \`context.approval.request_id\` = "${inline.request_id}", ` +
11
+ `\`context.wait_ms\` = 120000, and \`proposal\` matching this block ` +
12
+ `(\`kind\`: "${proposalKind}", \`argv\`: ${argvJson}). ` +
13
+ `Do not retry the command until \`guard_wait\` resolves. ` +
14
+ `Do not call \`guard\` instead of \`guard_wait\`. ` +
15
+ `Do not suggest running this command in the user's terminal or via another tool.`);
16
+ }
17
+ return (`Agent: MUST call MCP \`guard_wait\` now with \`context.wait_ms\` = 120000 and \`proposal\` matching this block ` +
18
+ `(\`kind\`: "${proposalKind}", \`argv\`: ${argvJson}). ` +
19
+ `If \`guard_wait\` returns \`require_approval\`, pass \`context.approval.request_id\` from the response and call \`guard_wait\` again until redeemed. ` +
20
+ `Direct the human to \`approval.open_url\` when shown. After \`guard_wait\` succeeds, retry this exact invocation once. ` +
21
+ `Do not call \`guard\` instead of \`guard_wait\`. ` +
22
+ `Do not suggest running this command in the user's terminal or via another tool.`);
23
+ }
4
24
  export function formatHookDenyMessages(input) {
5
25
  const reasons = reasonsSummary(input.reasons);
6
26
  const argvJson = JSON.stringify([...input.argv]);
@@ -23,20 +43,12 @@ export function formatHookDenyMessages(input) {
23
43
  if (input.tier === "MUTATE") {
24
44
  const proposalKind = input.hook === "beforeMCPExecution" ? "mcp" : "shell";
25
45
  const inline = input.inlineApproval;
26
- const userMessage = inline
27
- ? `${blockedLabel}. Human approval required.\n` +
28
- `Approval request ${inline.request_id} is pending — open ${inline.open_url}\n\n` +
29
- `Agent: MUST call MCP \`guard_wait\` now with \`context.approval.request_id\` = "${inline.request_id}", ` +
30
- `\`context.wait_ms\` = 120000, and \`proposal\` matching this block ` +
31
- `(\`kind\`: "${proposalKind}", \`argv\`: ${argvJson}). ` +
32
- `Do not retry the command until \`guard_wait\` resolves. ` +
33
- `Do not suggest running this command in the user's terminal or via another tool.`
34
- : `${blockedLabel}. Human approval required.\n\n` +
35
- `Agent: call MCP \`guard_wait\` (preferred) or \`guard\` with \`mode\`: "enforce" and \`proposal\` matching this block ` +
36
- `(\`kind\`: "${proposalKind}", \`argv\`: ${argvJson}). ` +
37
- `On \`require_approval\`, direct the human to \`approval.open_url\` or Praxis Approvals. ` +
38
- `After approval, retry this exact invocation once (hook consumes execution ticket). ` +
39
- `Do not suggest running this command in the user's terminal or via another tool.`;
46
+ const userMessage = mutateGuardWaitUserLead() +
47
+ `${blockedLabel}. Human approval required (MUTATE policy).\n` +
48
+ (inline
49
+ ? `Approval request ${inline.request_id} is pending human may open ${inline.open_url} while you run \`guard_wait\`.\n\n`
50
+ : "\n") +
51
+ mutateGuardWaitUserAgentBlock(proposalKind, argvJson, inline ?? null);
40
52
  const inlineAgent = inline
41
53
  ? ` Pending approval request_id=${inline.request_id} open_url=${inline.open_url}. ` +
42
54
  `MUST call MCP \`guard_wait\` with this request_id, \`context.wait_ms\` (e.g. 120000), and the same proposal before any retry. ` +
@@ -1 +1 @@
1
- {"version":3,"file":"agent-message.js","sourceRoot":"","sources":["../../src/hooks/agent-message.ts"],"names":[],"mappings":"AAsBA,SAAS,cAAc,CAAC,OAA0B;IAChD,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,KAAkC;IACvE,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IACjD,MAAM,YAAY,GAChB,KAAK,CAAC,IAAI,KAAK,oBAAoB;QACjC,CAAC,CAAC,KAAK,CAAC,QAAQ;YACd,CAAC,CAAC,0BAA0B,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC,QAAQ,EAAE;YAC5D,CAAC,CAAC,0BAA0B,KAAK,CAAC,IAAI,GAAG;QAC3C,CAAC,CAAC,0BAA0B,KAAK,CAAC,IAAI,GAAG,CAAC;IAE9C,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QACjC,OAAO;YACL,YAAY,EACV,GAAG,YAAY,iDAAiD;gBAChE,wFAAwF;gBACxF,6EAA6E;YAC/E,aAAa,EACX,qGAAqG;gBACrG,8EAA8E;gBAC9E,8HAA8H;gBAC9H,WAAW,OAAO,UAAU,QAAQ,EAAE;SACzC,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,KAAK,oBAAoB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;QAC3E,MAAM,MAAM,GAAG,KAAK,CAAC,cAAc,CAAC;QAEpC,MAAM,WAAW,GAAG,MAAM;YACxB,CAAC,CAAC,GAAG,YAAY,8BAA8B;gBAC7C,oBAAoB,MAAM,CAAC,UAAU,sBAAsB,MAAM,CAAC,QAAQ,MAAM;gBAChF,mFAAmF,MAAM,CAAC,UAAU,KAAK;gBACzG,qEAAqE;gBACrE,eAAe,YAAY,gBAAgB,QAAQ,KAAK;gBACxD,0DAA0D;gBAC1D,iFAAiF;YACnF,CAAC,CAAC,GAAG,YAAY,gCAAgC;gBAC/C,wHAAwH;gBACxH,eAAe,YAAY,gBAAgB,QAAQ,KAAK;gBACxD,0FAA0F;gBAC1F,qFAAqF;gBACrF,iFAAiF,CAAC;QAEtF,MAAM,WAAW,GAAG,MAAM;YACxB,CAAC,CAAC,gCAAgC,MAAM,CAAC,UAAU,aAAa,MAAM,CAAC,QAAQ,IAAI;gBACjF,gIAAgI;gBAChI,8FAA8F;YAChG,CAAC,CAAC,EAAE,CAAC;QAEP,OAAO;YACL,YAAY,EAAE,WAAW;YACzB,aAAa,EACX,sCAAsC,OAAO,UAAU,QAAQ,IAAI;gBACnE,mIAAmI;gBACnI,WAAW;gBACX,CAAC,MAAM;oBACL,CAAC,CAAC,2DAA2D;wBAC3D,eAAe,YAAY,wFAAwF;oBACrH,CAAC,CAAC,kHAAkH;wBAClH,eAAe,YAAY,8DAA8D;wBACzF,wFAAwF;wBACxF,oFAAoF,CAAC;SAC5F,CAAC;IACJ,CAAC;IAED,OAAO;QACL,YAAY,EAAE,GAAG,YAAY,GAAG;QAChC,aAAa,EACX,0BAA0B,KAAK,CAAC,IAAI,cAAc,OAAO,UAAU,QAAQ,IAAI;YAC/E,8EAA8E;KACjF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mCAAmC,CAAC,IAEnD;IACC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,OAAO,2FAA2F,CAAC;IACrG,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"agent-message.js","sourceRoot":"","sources":["../../src/hooks/agent-message.ts"],"names":[],"mappings":"AAsBA,SAAS,cAAc,CAAC,OAA0B;IAChD,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC3D,CAAC;AAED,qFAAqF;AACrF,SAAS,uBAAuB;IAC9B,OAAO,CACL,4GAA4G,CAC7G,CAAC;AACJ,CAAC;AAED,SAAS,6BAA6B,CAAC,YAAoB,EAAE,QAAgB,EAAE,MAAqC;IAClH,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,CACL,mFAAmF,MAAM,CAAC,UAAU,KAAK;YACzG,qEAAqE;YACrE,eAAe,YAAY,gBAAgB,QAAQ,KAAK;YACxD,0DAA0D;YAC1D,mDAAmD;YACnD,iFAAiF,CAClF,CAAC;IACJ,CAAC;IACD,OAAO,CACL,iHAAiH;QACjH,eAAe,YAAY,gBAAgB,QAAQ,KAAK;QACxD,uJAAuJ;QACvJ,yHAAyH;QACzH,mDAAmD;QACnD,iFAAiF,CAClF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,KAAkC;IACvE,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IACjD,MAAM,YAAY,GAChB,KAAK,CAAC,IAAI,KAAK,oBAAoB;QACjC,CAAC,CAAC,KAAK,CAAC,QAAQ;YACd,CAAC,CAAC,0BAA0B,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC,QAAQ,EAAE;YAC5D,CAAC,CAAC,0BAA0B,KAAK,CAAC,IAAI,GAAG;QAC3C,CAAC,CAAC,0BAA0B,KAAK,CAAC,IAAI,GAAG,CAAC;IAE9C,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QACjC,OAAO;YACL,YAAY,EACV,GAAG,YAAY,iDAAiD;gBAChE,wFAAwF;gBACxF,6EAA6E;YAC/E,aAAa,EACX,qGAAqG;gBACrG,8EAA8E;gBAC9E,8HAA8H;gBAC9H,WAAW,OAAO,UAAU,QAAQ,EAAE;SACzC,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,KAAK,oBAAoB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;QAC3E,MAAM,MAAM,GAAG,KAAK,CAAC,cAAc,CAAC;QAEpC,MAAM,WAAW,GACf,uBAAuB,EAAE;YACzB,GAAG,YAAY,8CAA8C;YAC7D,CAAC,MAAM;gBACL,CAAC,CAAC,oBAAoB,MAAM,CAAC,UAAU,gCAAgC,MAAM,CAAC,QAAQ,oCAAoC;gBAC1H,CAAC,CAAC,IAAI,CAAC;YACT,6BAA6B,CAAC,YAAY,EAAE,QAAQ,EAAE,MAAM,IAAI,IAAI,CAAC,CAAC;QAExE,MAAM,WAAW,GAAG,MAAM;YACxB,CAAC,CAAC,gCAAgC,MAAM,CAAC,UAAU,aAAa,MAAM,CAAC,QAAQ,IAAI;gBACjF,gIAAgI;gBAChI,8FAA8F;YAChG,CAAC,CAAC,EAAE,CAAC;QAEP,OAAO;YACL,YAAY,EAAE,WAAW;YACzB,aAAa,EACX,sCAAsC,OAAO,UAAU,QAAQ,IAAI;gBACnE,mIAAmI;gBACnI,WAAW;gBACX,CAAC,MAAM;oBACL,CAAC,CAAC,2DAA2D;wBAC3D,eAAe,YAAY,wFAAwF;oBACrH,CAAC,CAAC,kHAAkH;wBAClH,eAAe,YAAY,8DAA8D;wBACzF,wFAAwF;wBACxF,oFAAoF,CAAC;SAC5F,CAAC;IACJ,CAAC;IAED,OAAO;QACL,YAAY,EAAE,GAAG,YAAY,GAAG;QAChC,aAAa,EACX,0BAA0B,KAAK,CAAC,IAAI,cAAc,OAAO,UAAU,QAAQ,IAAI;YAC/E,8EAA8E;KACjF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mCAAmC,CAAC,IAEnD;IACC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,OAAO,2FAA2F,CAAC;IACrG,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function readStdinJson<T>(): Promise<T>;
2
+ export declare function tryAppendAuditEvent(evt: Record<string, unknown>, auditLogRoot?: string): Promise<void>;
3
+ //# sourceMappingURL=before-shell-io.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"before-shell-io.d.ts","sourceRoot":"","sources":["../../src/hooks/before-shell-io.ts"],"names":[],"mappings":"AAEA,wBAAsB,aAAa,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAanD;AAED,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,CAAC,EAAE,MAAM,iBAO5F"}
@@ -0,0 +1,26 @@
1
+ import { appendAuditJsonl } from "../audit/jsonl.js";
2
+ export async function readStdinJson() {
3
+ return await new Promise((resolve, reject) => {
4
+ let data = "";
5
+ process.stdin.setEncoding("utf8");
6
+ process.stdin.on("data", (chunk) => (data += chunk));
7
+ process.stdin.on("end", () => {
8
+ try {
9
+ resolve(JSON.parse(data));
10
+ }
11
+ catch (e) {
12
+ reject(e);
13
+ }
14
+ });
15
+ });
16
+ }
17
+ export async function tryAppendAuditEvent(evt, auditLogRoot) {
18
+ try {
19
+ await appendAuditJsonl(evt, auditLogRoot);
20
+ }
21
+ catch (e) {
22
+ const msg = e instanceof Error ? e.message : String(e);
23
+ process.stderr.write(`[auditor] audit log append failed: ${msg}\n`);
24
+ }
25
+ }
26
+ //# sourceMappingURL=before-shell-io.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"before-shell-io.js","sourceRoot":"","sources":["../../src/hooks/before-shell-io.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,OAAO,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC3C,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAClC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC;QACrD,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YAC3B,IAAI,CAAC;gBACH,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YAC5B,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,MAAM,CAAC,CAAC,CAAC,CAAC;YACZ,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,GAA4B,EAAE,YAAqB;IAC3F,IAAI,CAAC;QACH,MAAM,gBAAgB,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACvD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sCAAsC,GAAG,IAAI,CAAC,CAAC;IACtE,CAAC;AACH,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { Tier } from "../policy/index.js";
2
+ import type { ShellAnalysis } from "../shell/analyze-command.js";
3
+ import type { BeforeShellExecutionResponse } from "./before-shell-types.js";
4
+ export type ShellMutateHookPermission = {
5
+ permission: BeforeShellExecutionResponse["permission"];
6
+ ticketConsumed: boolean;
7
+ inlineApproval: {
8
+ request_id: string;
9
+ open_url: string;
10
+ } | null;
11
+ approvalFlowSignal: string | null;
12
+ reasons: string[];
13
+ };
14
+ export declare function resolveShellMutateHookPermission(input: {
15
+ tier: Tier;
16
+ argv: string[];
17
+ analysis: ShellAnalysis;
18
+ storageRoot: string;
19
+ rawDisplay: string;
20
+ policyRevision: number | null;
21
+ initialReasons: string[];
22
+ }): Promise<ShellMutateHookPermission>;
23
+ //# sourceMappingURL=before-shell-mutate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"before-shell-mutate.d.ts","sourceRoot":"","sources":["../../src/hooks/before-shell-mutate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAM/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAC;AAE5E,MAAM,MAAM,yBAAyB,GAAG;IACtC,UAAU,EAAE,4BAA4B,CAAC,YAAY,CAAC,CAAC;IACvD,cAAc,EAAE,OAAO,CAAC;IACxB,cAAc,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAChE,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF,wBAAsB,gCAAgC,CAAC,KAAK,EAAE;IAC5D,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,aAAa,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAyErC"}
@@ -0,0 +1,74 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { resolveShellApprovalHash } from "../approval/argv-fingerprint.js";
3
+ import { tryHookInlineApprovalRequest } from "../approval/hook-inline-approval.js";
4
+ import { resolveMutateApproval } from "../approval/mcp-flow.js";
5
+ import { readPendingApprovalIndex } from "../bridge/pending-approval-index.js";
6
+ import { tryConsumeExecutionTicket } from "../bridge/execution-ticket.js";
7
+ export async function resolveShellMutateHookPermission(input) {
8
+ const { tier, argv, analysis, storageRoot, rawDisplay, policyRevision, initialReasons } = input;
9
+ const reasons = [...initialReasons];
10
+ let permission = tier === "READ" ? "allow" : "deny";
11
+ let ticketConsumed = false;
12
+ let approvalFlowSignal = null;
13
+ let inlineApproval = null;
14
+ if (tier !== "MUTATE") {
15
+ return { permission, ticketConsumed, inlineApproval, approvalFlowSignal, reasons };
16
+ }
17
+ const approvalHash = resolveShellApprovalHash({
18
+ kind: "shell",
19
+ argv,
20
+ approval_fingerprint: analysis.approval_fingerprint_payload,
21
+ });
22
+ ticketConsumed = await tryConsumeExecutionTicket(argv, {
23
+ storageRoot,
24
+ kind: "shell",
25
+ approval_fingerprint: analysis.approval_fingerprint_payload,
26
+ });
27
+ if (ticketConsumed) {
28
+ return { permission: "allow", ticketConsumed, inlineApproval, approvalFlowSignal, reasons };
29
+ }
30
+ const pending = await readPendingApprovalIndex(approvalHash, { storageRoot });
31
+ if (pending) {
32
+ const autoRedeem = await resolveMutateApproval({
33
+ argv: [...argv],
34
+ proposalKind: "shell",
35
+ storageRoot,
36
+ rawDisplay,
37
+ eventId: randomUUID(),
38
+ policyRevision,
39
+ reasons,
40
+ approval: { request_id: pending.request_id },
41
+ waitMs: 0,
42
+ approval_fingerprint: analysis.approval_fingerprint_payload,
43
+ });
44
+ if (autoRedeem.kind === "allow" && autoRedeem.ticketRecorded) {
45
+ ticketConsumed = await tryConsumeExecutionTicket(argv, {
46
+ storageRoot,
47
+ kind: "shell",
48
+ approval_fingerprint: analysis.approval_fingerprint_payload,
49
+ });
50
+ if (ticketConsumed) {
51
+ return { permission: "allow", ticketConsumed, inlineApproval, approvalFlowSignal, reasons };
52
+ }
53
+ }
54
+ approvalFlowSignal = "retry_without_guard_wait_resolve";
55
+ reasons.push("retry_without_guard_wait_resolve");
56
+ inlineApproval = { request_id: pending.request_id, open_url: pending.open_url };
57
+ return { permission: "deny", ticketConsumed, inlineApproval, approvalFlowSignal, reasons };
58
+ }
59
+ const created = await tryHookInlineApprovalRequest({
60
+ argv: [...argv],
61
+ kind: "shell",
62
+ rawDisplay,
63
+ policyRevision,
64
+ reasons,
65
+ eventId: randomUUID(),
66
+ storageRoot,
67
+ approval_fingerprint: analysis.approval_fingerprint_payload,
68
+ });
69
+ if (created) {
70
+ inlineApproval = { request_id: created.request_id, open_url: created.open_url };
71
+ }
72
+ return { permission: "deny", ticketConsumed, inlineApproval, approvalFlowSignal, reasons };
73
+ }
74
+ //# sourceMappingURL=before-shell-mutate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"before-shell-mutate.js","sourceRoot":"","sources":["../../src/hooks/before-shell-mutate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { ShellAnalysis } from "../shell/analyze-command.js";
2
+ import type { BeforeShellExecutionPayload } from "./before-shell-types.js";
3
+ export declare function handleSkippedShellHook(input: {
4
+ payload: BeforeShellExecutionPayload;
5
+ skipReason: string;
6
+ policyRevision: number | null;
7
+ auditLogRoot: string;
8
+ decisionStarted: number;
9
+ analysis?: ShellAnalysis;
10
+ }): Promise<void>;
11
+ //# sourceMappingURL=before-shell-skipped.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"before-shell-skipped.d.ts","sourceRoot":"","sources":["../../src/hooks/before-shell-skipped.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAEjE,OAAO,KAAK,EAAE,2BAA2B,EAAgC,MAAM,yBAAyB,CAAC;AAEzG,wBAAsB,sBAAsB,CAAC,KAAK,EAAE;IAClD,OAAO,EAAE,2BAA2B,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B,GAAG,OAAO,CAAC,IAAI,CAAC,CAkDhB"}
@@ -0,0 +1,49 @@
1
+ import { getInstallId } from "../cli/install-id.js";
2
+ import { sendGuardEvent } from "../telemetry/guard-events.js";
3
+ import { tryAppendAuditEvent } from "./before-shell-io.js";
4
+ export async function handleSkippedShellHook(input) {
5
+ const { payload, skipReason, policyRevision, auditLogRoot, decisionStarted, analysis } = input;
6
+ const latency_ms = performance.now() - decisionStarted;
7
+ await tryAppendAuditEvent({
8
+ ts: new Date().toISOString(),
9
+ hook: "beforeShellExecution",
10
+ cwd: payload.cwd,
11
+ command: payload.command,
12
+ ...(analysis
13
+ ? { segments: analysis.segments }
14
+ : {}),
15
+ status: "skipped",
16
+ skipped: true,
17
+ skip_reason: skipReason,
18
+ tier: "READ",
19
+ permission: "allow",
20
+ ticketConsumed: false,
21
+ reasons: [`${skipReason}(policy_not_evaluated)`],
22
+ latency_ms,
23
+ }, auditLogRoot);
24
+ const response = { permission: "allow" };
25
+ process.stdout.write(JSON.stringify(response, null, 2));
26
+ await sendGuardEvent({
27
+ ts: new Date().toISOString(),
28
+ status: "skipped",
29
+ skipped: true,
30
+ skip_reason: skipReason,
31
+ tool: "auditor-hook",
32
+ command_path: null,
33
+ verb: null,
34
+ resource: null,
35
+ reason: skipReason,
36
+ cmd: payload.command,
37
+ tier: "READ",
38
+ decision: "allow",
39
+ latency_ms,
40
+ installId: getInstallId(),
41
+ kind: "shell",
42
+ ...(policyRevision !== null ? { policy_revision: policyRevision } : {}),
43
+ meta: {
44
+ hook: "beforeShellExecution",
45
+ ticketConsumed: false,
46
+ },
47
+ });
48
+ }
49
+ //# sourceMappingURL=before-shell-skipped.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"before-shell-skipped.js","sourceRoot":"","sources":["../../src/hooks/before-shell-skipped.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAG3D,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,KAO5C;IACC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IAC/F,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE,GAAG,eAAe,CAAC;IAEvD,MAAM,mBAAmB,CACvB;QACE,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAC5B,IAAI,EAAE,sBAAsB;QAC5B,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,GAAG,CAAC,QAAQ;YACV,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE;YACjC,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,EAAE,SAAS;QACjB,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,UAAU;QACvB,IAAI,EAAE,MAAM;QACZ,UAAU,EAAE,OAAO;QACnB,cAAc,EAAE,KAAK;QACrB,OAAO,EAAE,CAAC,GAAG,UAAU,wBAAwB,CAAC;QAChD,UAAU;KACX,EACD,YAAY,CACb,CAAC;IAEF,MAAM,QAAQ,GAAiC,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;IACvE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAExD,MAAM,cAAc,CAAC;QACnB,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAC5B,MAAM,EAAE,SAAS;QACjB,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,UAAU;QACvB,IAAI,EAAE,cAAc;QACpB,YAAY,EAAE,IAAI;QAClB,IAAI,EAAE,IAAI;QACV,QAAQ,EAAE,IAAI;QACd,MAAM,EAAE,UAAU;QAClB,GAAG,EAAE,OAAO,CAAC,OAAO;QACpB,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,OAAO;QACjB,UAAU;QACV,SAAS,EAAE,YAAY,EAAE;QACzB,IAAI,EAAE,OAAO;QACb,GAAG,CAAC,cAAc,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,IAAI,EAAE;YACJ,IAAI,EAAE,sBAAsB;YAC5B,cAAc,EAAE,KAAK;SACtB;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,12 @@
1
+ /** Cursor `beforeShellExecution` stdin (see https://cursor.com/docs/hooks.md). */
2
+ export type BeforeShellExecutionPayload = {
3
+ command: string;
4
+ cwd?: string;
5
+ sandbox?: boolean;
6
+ };
7
+ export type BeforeShellExecutionResponse = {
8
+ permission: "allow" | "deny" | "ask";
9
+ user_message?: string;
10
+ agent_message?: string;
11
+ };
12
+ //# sourceMappingURL=before-shell-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"before-shell-types.d.ts","sourceRoot":"","sources":["../../src/hooks/before-shell-types.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,MAAM,MAAM,2BAA2B,GAAG;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,UAAU,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=before-shell-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"before-shell-types.js","sourceRoot":"","sources":["../../src/hooks/before-shell-types.ts"],"names":[],"mappings":""}
@@ -1,13 +1,5 @@
1
- export type BeforeShellExecutionPayload = {
2
- command: string;
3
- cwd?: string;
4
- sandbox?: boolean;
5
- };
6
- export type BeforeShellExecutionResponse = {
7
- permission: "allow" | "deny" | "ask";
8
- user_message?: string;
9
- agent_message?: string;
10
- };
1
+ import type { BeforeShellExecutionResponse } from "./before-shell-types.js";
2
+ export type { BeforeShellExecutionPayload, BeforeShellExecutionResponse } from "./before-shell-types.js";
11
3
  /**
12
4
  * Cursor `beforeShellExecution` contract: read one JSON object from stdin, write one JSON object to stdout.
13
5
  */
@@ -1 +1 @@
1
- {"version":3,"file":"run-before-shell.d.ts","sourceRoot":"","sources":["../../src/hooks/run-before-shell.ts"],"names":[],"mappings":"AAoBA,MAAM,MAAM,2BAA2B,GAAG;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,UAAU,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AA+BF;;GAEG;AACH,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,IAAI,CAAC,CAyMjE;AAED,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,OAAO,GAAG,4BAA4B,CAMtF"}
1
+ {"version":3,"file":"run-before-shell.d.ts","sourceRoot":"","sources":["../../src/hooks/run-before-shell.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAA+B,4BAA4B,EAAE,MAAM,yBAAyB,CAAC;AAEzG,YAAY,EAAE,2BAA2B,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAC;AAOzG;;GAEG;AACH,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,IAAI,CAAC,CAsJjE;AAED,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,OAAO,GAAG,4BAA4B,CAMtF"}
@@ -1,168 +1,87 @@
1
- import { classifyArgv, loadPoliciesV1, readPoliciesV1Revision } from "../policy/index.js";
2
- import { appendAuditJsonl } from "../audit/jsonl.js";
1
+ import { loadPoliciesV1, readPoliciesV1Revision } from "../policy/index.js";
3
2
  import { getInstallId } from "../cli/install-id.js";
4
- import { DEFAULT_GOVERNED_SHELL_TOOLS } from "../shell/governed-tools.js";
5
- import { parseCommandToArgv } from "../shell/parse.js";
6
- import { stripTrailingBenignShellRedirectsForMetacharCheck } from "../shell/strip-trailing-benign-shell-redirs.js";
3
+ import { analyzeShellCommand } from "../shell/analyze-command.js";
4
+ import { commandMayContainGovernedTool } from "../shell/governed-tools.js";
7
5
  import { resolveGuardStorageRoot } from "../bridge/guard-storage-root.js";
8
- import { tryConsumeExecutionTicket } from "../bridge/execution-ticket.js";
9
- import { tryHookInlineApprovalRequest } from "../approval/hook-inline-approval.js";
10
- import { readPendingApprovalIndex } from "../bridge/pending-approval-index.js";
11
- import { argvSha256 } from "../approval/argv-fingerprint.js";
12
- import { resolveMutateApproval } from "../approval/mcp-flow.js";
13
- import { formatHookAllowViaCredentialMessage, formatHookDenyMessages, } from "./agent-message.js";
14
- import { randomUUID } from "node:crypto";
15
6
  import { sendGuardEvent } from "../telemetry/guard-events.js";
7
+ import { resolveShellMutateHookPermission } from "./before-shell-mutate.js";
8
+ import { readStdinJson, tryAppendAuditEvent } from "./before-shell-io.js";
9
+ import { handleSkippedShellHook } from "./before-shell-skipped.js";
10
+ import { formatHookAllowViaCredentialMessage, formatHookDenyMessages, } from "./agent-message.js";
16
11
  function tierToPermission(tier) {
17
12
  if (tier === "READ")
18
13
  return "allow";
19
14
  return "deny";
20
15
  }
21
- async function readStdinJson() {
22
- return await new Promise((resolve, reject) => {
23
- let data = "";
24
- process.stdin.setEncoding("utf8");
25
- process.stdin.on("data", (chunk) => (data += chunk));
26
- process.stdin.on("end", () => {
27
- try {
28
- resolve(JSON.parse(data));
29
- }
30
- catch (e) {
31
- reject(e);
32
- }
33
- });
34
- });
35
- }
36
- async function tryAppendAuditEvent(evt, auditLogRoot) {
37
- try {
38
- await appendAuditJsonl(evt, auditLogRoot);
39
- }
40
- catch (e) {
41
- const msg = e instanceof Error ? e.message : String(e);
42
- process.stderr.write(`[auditor] audit log append failed: ${msg}\n`);
43
- }
44
- }
45
16
  /**
46
17
  * Cursor `beforeShellExecution` contract: read one JSON object from stdin, write one JSON object to stdout.
47
18
  */
48
19
  export async function runBeforeShellHookFromStdin() {
49
20
  const payload = await readStdinJson();
50
21
  const decisionStarted = performance.now();
51
- const argv = parseCommandToArgv(payload.command);
52
- const commandForRawMeta = stripTrailingBenignShellRedirectsForMetacharCheck(payload.command);
53
- const rawMetacharacters = /(;|&&|\|\||\||`|>|<|\$\()/.test(commandForRawMeta);
54
- const tool = argv[0];
55
- if (!tool || !DEFAULT_GOVERNED_SHELL_TOOLS.includes(tool)) {
56
- const skipReason = !tool ? "no_command" : "ungoverned_shell_tool";
22
+ const command = payload.command;
23
+ if (!commandMayContainGovernedTool(command)) {
24
+ const skipReason = command.trim() ? "no_governed_invocation" : "no_command";
57
25
  const auditLogRoot = typeof payload.cwd === "string" && payload.cwd.trim() ? payload.cwd.trim() : undefined;
58
- // Decision is known here; do not await policy meta read before NET decision latency.
59
- const latency_ms = performance.now() - decisionStarted;
60
- await tryAppendAuditEvent({
61
- ts: new Date().toISOString(),
62
- hook: "beforeShellExecution",
63
- cwd: payload.cwd,
64
- command: payload.command,
65
- argv,
66
- status: "skipped",
67
- skipped: true,
68
- skip_reason: skipReason,
69
- tier: "READ",
70
- permission: "allow",
71
- ticketConsumed: false,
72
- reasons: [`${skipReason}(policy_not_evaluated)`],
73
- latency_ms,
74
- }, auditLogRoot);
75
- const response = { permission: "allow" };
76
- process.stdout.write(JSON.stringify(response, null, 2));
77
26
  const policyRevision = await readPoliciesV1Revision();
78
- await sendGuardEvent({
79
- ts: new Date().toISOString(),
80
- status: "skipped",
81
- skipped: true,
82
- skip_reason: skipReason,
83
- tool: "auditor-hook",
84
- command_path: argv[0] ?? null,
85
- verb: argv[1] ?? null,
86
- resource: argv.length > 2 ? argv.slice(2).join(" ") : null,
87
- reason: skipReason,
88
- cmd: payload.command,
89
- tier: "READ",
90
- decision: "allow",
91
- latency_ms,
92
- installId: getInstallId(),
93
- kind: "shell",
94
- ...(policyRevision !== null ? { policy_revision: policyRevision } : {}),
95
- meta: {
96
- hook: "beforeShellExecution",
97
- ticketConsumed: false,
98
- },
27
+ await handleSkippedShellHook({
28
+ payload,
29
+ skipReason,
30
+ policyRevision,
31
+ auditLogRoot: auditLogRoot ?? "",
32
+ decisionStarted,
99
33
  });
100
34
  return;
101
35
  }
102
36
  const [policy, policyRevision] = await Promise.all([loadPoliciesV1(), readPoliciesV1Revision()]);
103
- const { classification, flags } = classifyArgv(policy, argv);
104
- let tier = classification.tier;
37
+ const analysis = analyzeShellCommand(command, policy);
38
+ const storageRoot = resolveGuardStorageRoot(typeof payload.cwd === "string" ? payload.cwd : undefined);
39
+ if (analysis.skipped) {
40
+ await handleSkippedShellHook({
41
+ payload,
42
+ skipReason: "no_governed_invocation",
43
+ policyRevision,
44
+ auditLogRoot: storageRoot,
45
+ decisionStarted,
46
+ analysis,
47
+ });
48
+ return;
49
+ }
50
+ const argv = analysis.primary.canonical_argv;
51
+ const evaluation = analysis.primary.evaluation;
52
+ const tier = analysis.tier;
105
53
  const reasons = [];
106
- if (!classification.matched)
54
+ if (!evaluation.classification.matched)
107
55
  reasons.push("unknown_command(default_deny)");
108
- if (flags.metacharacters || rawMetacharacters)
56
+ if (evaluation.flags.metacharacters || analysis.raw_metacharacters)
109
57
  reasons.push("metacharacters");
110
- if (flags.dangerous_flags)
58
+ if (evaluation.flags.dangerous_flags)
111
59
  reasons.push("dangerous_flags");
112
- if ((flags.metacharacters || rawMetacharacters) && tier === "READ")
113
- tier = "MUTATE";
114
- if (flags.dangerous_flags)
115
- tier = "DESTRUCTIVE";
116
- const storageRoot = resolveGuardStorageRoot(typeof payload.cwd === "string" ? payload.cwd : undefined);
60
+ if (analysis.fail_closed)
61
+ reasons.push("unparseable(fail_closed)");
62
+ if (analysis.invocations.length > 1) {
63
+ reasons.push(`multiple_governed_invocations(${analysis.invocations.length})`);
64
+ }
117
65
  let permission = tierToPermission(tier);
118
66
  let ticketConsumed = false;
119
67
  let approvalFlowSignal = null;
120
- if (permission === "deny" && tier === "MUTATE") {
121
- ticketConsumed = await tryConsumeExecutionTicket(argv, { storageRoot, kind: "shell" });
122
- if (ticketConsumed)
123
- permission = "allow";
124
- }
125
68
  let inlineApproval = null;
126
69
  if (permission === "deny" && tier === "MUTATE") {
127
- const pending = await readPendingApprovalIndex(argvSha256(argv), { storageRoot });
128
- if (pending) {
129
- const autoRedeem = await resolveMutateApproval({
130
- argv: [...argv],
131
- proposalKind: "shell",
132
- storageRoot,
133
- rawDisplay: payload.command,
134
- eventId: randomUUID(),
135
- policyRevision,
136
- reasons,
137
- approval: { request_id: pending.request_id },
138
- waitMs: 0,
139
- });
140
- if (autoRedeem.kind === "allow" && autoRedeem.ticketRecorded) {
141
- ticketConsumed = await tryConsumeExecutionTicket(argv, { storageRoot, kind: "shell" });
142
- if (ticketConsumed) {
143
- permission = "allow";
144
- }
145
- }
146
- if (permission === "deny") {
147
- approvalFlowSignal = "retry_without_guard_wait_resolve";
148
- reasons.push("retry_without_guard_wait_resolve");
149
- inlineApproval = { request_id: pending.request_id, open_url: pending.open_url };
150
- }
151
- }
152
- else {
153
- const created = await tryHookInlineApprovalRequest({
154
- argv: [...argv],
155
- kind: "shell",
156
- rawDisplay: payload.command,
157
- policyRevision,
158
- reasons,
159
- eventId: randomUUID(),
160
- storageRoot,
161
- });
162
- if (created) {
163
- inlineApproval = { request_id: created.request_id, open_url: created.open_url };
164
- }
165
- }
70
+ const mutate = await resolveShellMutateHookPermission({
71
+ tier,
72
+ argv,
73
+ analysis,
74
+ storageRoot,
75
+ rawDisplay: payload.command,
76
+ policyRevision,
77
+ initialReasons: reasons,
78
+ });
79
+ permission = mutate.permission;
80
+ ticketConsumed = mutate.ticketConsumed;
81
+ approvalFlowSignal = mutate.approvalFlowSignal;
82
+ inlineApproval = mutate.inlineApproval;
83
+ reasons.length = 0;
84
+ reasons.push(...mutate.reasons);
166
85
  }
167
86
  const allowMessage = formatHookAllowViaCredentialMessage({ ticketConsumed });
168
87
  const denyMessages = permission === "deny"
@@ -184,7 +103,6 @@ export async function runBeforeShellHookFromStdin() {
184
103
  user_message: denyMessages.user_message,
185
104
  agent_message: denyMessages.agent_message,
186
105
  };
187
- const auditLogRoot = storageRoot;
188
106
  const latency_ms = performance.now() - decisionStarted;
189
107
  await tryAppendAuditEvent({
190
108
  ts: new Date().toISOString(),
@@ -192,8 +110,12 @@ export async function runBeforeShellHookFromStdin() {
192
110
  cwd: payload.cwd,
193
111
  command: payload.command,
194
112
  argv,
195
- classification,
196
- flags,
113
+ governed_argv: argv,
114
+ invocation_count: analysis.invocations.length,
115
+ segments: analysis.segments,
116
+ operators: analysis.operators,
117
+ classification: evaluation.classification,
118
+ flags: evaluation.flags,
197
119
  tier,
198
120
  permission,
199
121
  ticketConsumed,
@@ -201,10 +123,8 @@ export async function runBeforeShellHookFromStdin() {
201
123
  reasons,
202
124
  approval_flow_signal: approvalFlowSignal,
203
125
  latency_ms,
204
- }, auditLogRoot);
205
- // Write stdout immediately so Cursor gets the response without waiting for network.
126
+ }, storageRoot);
206
127
  process.stdout.write(JSON.stringify(response, null, 2));
207
- // Upload event to backend (keep process alive until done).
208
128
  const status = permission === "allow" ? "passed" : "blocked";
209
129
  await sendGuardEvent({
210
130
  ts: new Date().toISOString(),
@@ -226,6 +146,7 @@ export async function runBeforeShellHookFromStdin() {
226
146
  ticketConsumed,
227
147
  approval_request_id: inlineApproval?.request_id ?? null,
228
148
  approval_flow_signal: approvalFlowSignal,
149
+ invocation_count: analysis.invocations.length,
229
150
  },
230
151
  });
231
152
  }