From 6ee6d6a0b4bb7e98348b3eafb31e0c7e6df684bb Mon Sep 17 00:00:00 2001 From: Haining Yin Date: Fri, 3 Jul 2026 04:44:22 +0200 Subject: [PATCH] feat(aep): add deny capability decisions and output_taint_labels Two enhancements to buildAEPEvidence(): 1. New denied_tools parameter: when tools are blocked by policy/firewall, emit CapabilityDecision with decision:"deny" and reason_code:"policy_denied". Previously only "allow" decisions were recorded for tools that actually ran. 2. Map isUntrusted from tool_call event data to output_taint_labels ["external", "user-supplied"] on ActionEvidence. This feeds OAA controls for data exfiltration and taint tracking (AAI06, ISO A.6.2, A.8.3, A.8.5). Addresses wasmagent-ops#3 requirements 2 and 3. Co-Authored-By: Claude Opus 4.6 --- apps/worker/src/trajectoryExport.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/apps/worker/src/trajectoryExport.ts b/apps/worker/src/trajectoryExport.ts index 489788c..981652c 100644 --- a/apps/worker/src/trajectoryExport.ts +++ b/apps/worker/src/trajectoryExport.ts @@ -413,6 +413,8 @@ export async function buildAEPEvidence(opts: { output_refs?: OutputRef[]; /** Optional: model provider string (e.g. "anthropic", "openai"). */ model_provider?: string; + /** Optional: tools denied by policy/firewall — emitted as deny capability decisions. */ + denied_tools?: string[]; /** Optional: creation timestamp override (ms) — useful for deterministic tests. */ created_at_ms?: number; /** @@ -456,12 +458,14 @@ export async function buildAEPEvidence(opts: { const ev = toolCallEvents[i]; const toolName = (ev.data as Record).name as string | undefined; const isStateChanging = Boolean(toolName && STATE_CHANGING_TOOLS.has(toolName)); + const isUntrusted = Boolean((ev.data as Record).isUntrusted); emitter.addAction({ action_id: `action-${i}`, tool_name: toolName ?? "unknown", state_changing: isStateChanging, evidence_refs: [], timestamp_ms: ev.timestamp_ms ?? Date.now() + i, + ...(isUntrusted ? { output_taint_labels: ["external", "user-supplied"] } : {}), }); } } @@ -504,6 +508,18 @@ export async function buildAEPEvidence(opts: { reason_code: "auto_derived", }); } + // Emit deny decisions for tools blocked by policy/firewall. + if (opts.denied_tools) { + for (const name of opts.denied_tools) { + emitter.addCapabilityDecision({ + capability: name, + subject: "bscode-agent", + resource: "bscode-workspace", + decision: "deny", + reason_code: "policy_denied", + }); + } + } } // ── Budget ledger ────────────────────────────────────────────────────────