@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.
- package/dist/approval/argv-fingerprint.d.ts +10 -1
- package/dist/approval/argv-fingerprint.d.ts.map +1 -1
- package/dist/approval/argv-fingerprint.js +10 -1
- package/dist/approval/argv-fingerprint.js.map +1 -1
- package/dist/approval/hook-inline-approval.d.ts +2 -0
- package/dist/approval/hook-inline-approval.d.ts.map +1 -1
- package/dist/approval/hook-inline-approval.js +6 -2
- package/dist/approval/hook-inline-approval.js.map +1 -1
- package/dist/approval/mcp-flow.d.ts +4 -2
- package/dist/approval/mcp-flow.d.ts.map +1 -1
- package/dist/approval/mcp-flow.js +9 -3
- package/dist/approval/mcp-flow.js.map +1 -1
- package/dist/approval/redeem.d.ts +2 -0
- package/dist/approval/redeem.d.ts.map +1 -1
- package/dist/approval/redeem.js +7 -2
- package/dist/approval/redeem.js.map +1 -1
- package/dist/bridge/execution-ticket.d.ts +3 -0
- package/dist/bridge/execution-ticket.d.ts.map +1 -1
- package/dist/bridge/execution-ticket.js +38 -9
- package/dist/bridge/execution-ticket.js.map +1 -1
- package/dist/bridge/shell-approval-bridge.d.ts +14 -5
- package/dist/bridge/shell-approval-bridge.d.ts.map +1 -1
- package/dist/bridge/shell-approval-bridge.js +47 -24
- package/dist/bridge/shell-approval-bridge.js.map +1 -1
- package/dist/hooks/agent-message.d.ts.map +1 -1
- package/dist/hooks/agent-message.js +26 -14
- package/dist/hooks/agent-message.js.map +1 -1
- package/dist/hooks/before-shell-io.d.ts +3 -0
- package/dist/hooks/before-shell-io.d.ts.map +1 -0
- package/dist/hooks/before-shell-io.js +26 -0
- package/dist/hooks/before-shell-io.js.map +1 -0
- package/dist/hooks/before-shell-mutate.d.ts +23 -0
- package/dist/hooks/before-shell-mutate.d.ts.map +1 -0
- package/dist/hooks/before-shell-mutate.js +74 -0
- package/dist/hooks/before-shell-mutate.js.map +1 -0
- package/dist/hooks/before-shell-skipped.d.ts +11 -0
- package/dist/hooks/before-shell-skipped.d.ts.map +1 -0
- package/dist/hooks/before-shell-skipped.js +49 -0
- package/dist/hooks/before-shell-skipped.js.map +1 -0
- package/dist/hooks/before-shell-types.d.ts +12 -0
- package/dist/hooks/before-shell-types.d.ts.map +1 -0
- package/dist/hooks/before-shell-types.js +2 -0
- package/dist/hooks/before-shell-types.js.map +1 -0
- package/dist/hooks/run-before-shell.d.ts +2 -10
- package/dist/hooks/run-before-shell.d.ts.map +1 -1
- package/dist/hooks/run-before-shell.js +63 -142
- package/dist/hooks/run-before-shell.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp/evaluate-guard.d.ts.map +1 -1
- package/dist/mcp/evaluate-guard.js +20 -9
- package/dist/mcp/evaluate-guard.js.map +1 -1
- package/dist/mcp/guard-approval-block.d.ts +1 -0
- package/dist/mcp/guard-approval-block.d.ts.map +1 -1
- package/dist/mcp/guard-approval-block.js +1 -0
- package/dist/mcp/guard-approval-block.js.map +1 -1
- package/dist/policies.v1.json +4 -0
- package/dist/policy/index.d.ts +4 -0
- package/dist/policy/index.d.ts.map +1 -1
- package/dist/policy/index.js +6 -0
- package/dist/policy/index.js.map +1 -1
- package/dist/shell/analyze-command-aggregate.d.ts +16 -0
- package/dist/shell/analyze-command-aggregate.d.ts.map +1 -0
- package/dist/shell/analyze-command-aggregate.js +89 -0
- package/dist/shell/analyze-command-aggregate.js.map +1 -0
- package/dist/shell/analyze-command-invocations.d.ts +11 -0
- package/dist/shell/analyze-command-invocations.d.ts.map +1 -0
- package/dist/shell/analyze-command-invocations.js +113 -0
- package/dist/shell/analyze-command-invocations.js.map +1 -0
- package/dist/shell/analyze-command.d.ts +7 -0
- package/dist/shell/analyze-command.d.ts.map +1 -0
- package/dist/shell/analyze-command.js +46 -0
- package/dist/shell/analyze-command.js.map +1 -0
- package/dist/shell/analyze-command.types.d.ts +38 -0
- package/dist/shell/analyze-command.types.d.ts.map +1 -0
- package/dist/shell/analyze-command.types.js +2 -0
- package/dist/shell/analyze-command.types.js.map +1 -0
- package/dist/shell/evaluate.d.ts +15 -18
- package/dist/shell/evaluate.d.ts.map +1 -1
- package/dist/shell/evaluate.js +57 -47
- package/dist/shell/evaluate.js.map +1 -1
- package/dist/shell/governed-tools.d.ts +18 -1
- package/dist/shell/governed-tools.d.ts.map +1 -1
- package/dist/shell/governed-tools.js +60 -1
- package/dist/shell/governed-tools.js.map +1 -1
- package/dist/shell/guard-eval.d.ts +15 -0
- package/dist/shell/guard-eval.d.ts.map +1 -0
- package/dist/shell/guard-eval.js +35 -0
- package/dist/shell/guard-eval.js.map +1 -0
- package/dist/shell/parse-segments.d.ts +14 -0
- package/dist/shell/parse-segments.d.ts.map +1 -0
- package/dist/shell/parse-segments.js +41 -0
- package/dist/shell/parse-segments.js.map +1 -0
- 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 =
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
`
|
|
30
|
-
|
|
31
|
-
|
|
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,
|
|
1
|
+
{"version":3,"file":"agent-message.js","sourceRoot":"","sources":["../../src/hooks/agent-message.ts"],"names":[],"mappings":"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"}
|
|
@@ -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 @@
|
|
|
1
|
+
{"version":3,"file":"before-shell-types.js","sourceRoot":"","sources":["../../src/hooks/before-shell-types.ts"],"names":[],"mappings":""}
|
|
@@ -1,13 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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":"
|
|
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 {
|
|
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 {
|
|
5
|
-
import {
|
|
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
|
|
52
|
-
|
|
53
|
-
|
|
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
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
104
|
-
|
|
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 ||
|
|
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 (
|
|
113
|
-
|
|
114
|
-
if (
|
|
115
|
-
|
|
116
|
-
|
|
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
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
196
|
-
|
|
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
|
-
},
|
|
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
|
}
|