@muxoai/sdk 1.2.3 → 1.4.2
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/.agents/plugins/marketplace.json +22 -0
- package/.claude-plugin/marketplace.json +32 -0
- package/.codex/config.toml +12 -0
- package/ARCHITECTURE.md +121 -0
- package/README.md +85 -31
- package/dist/clerk.d.ts +35 -0
- package/dist/clerk.js +201 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +2380 -0
- package/dist/composio-catalog.d.ts +234 -0
- package/dist/composio-catalog.js +1894 -0
- package/dist/config.d.ts +36 -0
- package/dist/config.js +115 -0
- package/dist/durable-workflow.d.ts +45 -0
- package/dist/durable-workflow.js +90 -0
- package/dist/engine.d.ts +465 -0
- package/dist/engine.js +2560 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/mcp-schema.d.ts +286 -0
- package/dist/mcp-schema.js +2559 -0
- package/dist/mcp.d.ts +22 -0
- package/dist/mcp.js +1356 -0
- package/dist/memory-graph.d.ts +21 -0
- package/dist/memory-graph.js +173 -0
- package/dist/nl.d.ts +34 -0
- package/dist/nl.js +756 -0
- package/dist/oauth.d.ts +32 -0
- package/dist/oauth.js +134 -0
- package/dist/policy.d.ts +190 -0
- package/dist/policy.js +1597 -0
- package/dist/providers/composio-instant.d.ts +18 -0
- package/dist/providers/composio-instant.js +1169 -0
- package/dist/providers/composio-sdk.d.ts +162 -0
- package/dist/providers/composio-sdk.js +1395 -0
- package/dist/providers/microsoft-governance.d.ts +152 -0
- package/dist/providers/microsoft-governance.js +1062 -0
- package/dist/providers/stripe-billing.d.ts +83 -0
- package/dist/providers/stripe-billing.js +611 -0
- package/dist/providers/stripe-projects.d.ts +508 -0
- package/dist/providers/stripe-projects.js +3188 -0
- package/dist/providers/supermemory.d.ts +160 -0
- package/dist/providers/supermemory.js +778 -0
- package/dist/provisioning-managed.d.ts +50 -0
- package/dist/provisioning-managed.js +155 -0
- package/dist/sdk.d.ts +951 -0
- package/dist/sdk.js +748 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.js +3575 -0
- package/dist/store.d.ts +348 -0
- package/dist/store.js +2323 -0
- package/dist/types.d.ts +593 -0
- package/dist/types.js +1 -0
- package/dist/ui/console-html.d.ts +64 -0
- package/dist/ui/console-html.js +3857 -0
- package/dist/ui/design-tokens.d.ts +177 -0
- package/dist/ui/design-tokens.js +210 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/package.json +68 -7
- package/plugins/muxo/.app.json +8 -0
- package/plugins/muxo/.claude-plugin/plugin.json +16 -0
- package/plugins/muxo/.codex-plugin/plugin.json +64 -0
- package/plugins/muxo/.cursor-plugin/plugin.json +30 -0
- package/plugins/muxo/.mcp.json +8 -0
- package/plugins/muxo/assets/logo.png +0 -0
- package/plugins/muxo/assets/logo.svg +6 -0
- package/plugins/muxo/hooks/hooks.json +27 -0
- package/plugins/muxo/hooks/session-start.sh +97 -0
- package/plugins/muxo/hooks/user-prompt-submit.sh +58 -0
- package/plugins/muxo/mcp.json +28 -0
- package/plugins/muxo/plugin.json +116 -0
- package/plugins/muxo/skills/muxo/SKILL.md +153 -0
- package/plugins/muxo/skills/muxo/agents/openai.yaml +12 -0
- package/plugins/muxo/skills/muxo/references/cli.md +63 -0
- package/plugins/muxo/skills/muxo/references/mcp.md +37 -0
- package/plugins/muxo/skills/muxo-activity-summary/SKILL.md +81 -0
- package/plugins/muxo/skills/setup/SKILL.md +42 -0
- package/plugins/muxo/skills/setup/agents/openai.yaml +12 -0
- package/src/catalogues/stripe-projects-catalog.json +9972 -0
- package/vendor/microsoft-governance/README.md +363 -0
- package/vendor/microsoft-governance/dist/audit.d.ts +26 -0
- package/vendor/microsoft-governance/dist/audit.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/audit.js +107 -0
- package/vendor/microsoft-governance/dist/audit.js.map +1 -0
- package/vendor/microsoft-governance/dist/cascade-containment.d.ts +84 -0
- package/vendor/microsoft-governance/dist/cascade-containment.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/cascade-containment.js +387 -0
- package/vendor/microsoft-governance/dist/cascade-containment.js.map +1 -0
- package/vendor/microsoft-governance/dist/client.d.ts +34 -0
- package/vendor/microsoft-governance/dist/client.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/client.js +112 -0
- package/vendor/microsoft-governance/dist/client.js.map +1 -0
- package/vendor/microsoft-governance/dist/context-poisoning.d.ts +58 -0
- package/vendor/microsoft-governance/dist/context-poisoning.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/context-poisoning.js +410 -0
- package/vendor/microsoft-governance/dist/context-poisoning.js.map +1 -0
- package/vendor/microsoft-governance/dist/credential-vault.d.ts +213 -0
- package/vendor/microsoft-governance/dist/credential-vault.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/credential-vault.js +567 -0
- package/vendor/microsoft-governance/dist/credential-vault.js.map +1 -0
- package/vendor/microsoft-governance/dist/discovery.d.ts +80 -0
- package/vendor/microsoft-governance/dist/discovery.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/discovery.js +404 -0
- package/vendor/microsoft-governance/dist/discovery.js.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/channel.d.ts +29 -0
- package/vendor/microsoft-governance/dist/encryption/channel.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/channel.js +78 -0
- package/vendor/microsoft-governance/dist/encryption/channel.js.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/index.d.ts +9 -0
- package/vendor/microsoft-governance/dist/encryption/index.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/index.js +23 -0
- package/vendor/microsoft-governance/dist/encryption/index.js.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/mesh-client.d.ts +267 -0
- package/vendor/microsoft-governance/dist/encryption/mesh-client.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/mesh-client.js +1001 -0
- package/vendor/microsoft-governance/dist/encryption/mesh-client.js.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/ratchet.d.ts +38 -0
- package/vendor/microsoft-governance/dist/encryption/ratchet.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/ratchet.js +223 -0
- package/vendor/microsoft-governance/dist/encryption/ratchet.js.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/registry-client.d.ts +136 -0
- package/vendor/microsoft-governance/dist/encryption/registry-client.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/registry-client.js +369 -0
- package/vendor/microsoft-governance/dist/encryption/registry-client.js.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/x3dh.d.ts +80 -0
- package/vendor/microsoft-governance/dist/encryption/x3dh.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/encryption/x3dh.js +238 -0
- package/vendor/microsoft-governance/dist/encryption/x3dh.js.map +1 -0
- package/vendor/microsoft-governance/dist/framework-adapter.d.ts +63 -0
- package/vendor/microsoft-governance/dist/framework-adapter.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/framework-adapter.js +253 -0
- package/vendor/microsoft-governance/dist/framework-adapter.js.map +1 -0
- package/vendor/microsoft-governance/dist/identity.d.ts +124 -0
- package/vendor/microsoft-governance/dist/identity.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/identity.js +446 -0
- package/vendor/microsoft-governance/dist/identity.js.map +1 -0
- package/vendor/microsoft-governance/dist/index.d.ts +39 -0
- package/vendor/microsoft-governance/dist/index.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/index.js +95 -0
- package/vendor/microsoft-governance/dist/index.js.map +1 -0
- package/vendor/microsoft-governance/dist/kill-switch.d.ts +22 -0
- package/vendor/microsoft-governance/dist/kill-switch.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/kill-switch.js +60 -0
- package/vendor/microsoft-governance/dist/kill-switch.js.map +1 -0
- package/vendor/microsoft-governance/dist/lifecycle.d.ts +50 -0
- package/vendor/microsoft-governance/dist/lifecycle.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/lifecycle.js +113 -0
- package/vendor/microsoft-governance/dist/lifecycle.js.map +1 -0
- package/vendor/microsoft-governance/dist/mcp.d.ts +41 -0
- package/vendor/microsoft-governance/dist/mcp.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/mcp.js +233 -0
- package/vendor/microsoft-governance/dist/mcp.js.map +1 -0
- package/vendor/microsoft-governance/dist/metrics.d.ts +123 -0
- package/vendor/microsoft-governance/dist/metrics.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/metrics.js +289 -0
- package/vendor/microsoft-governance/dist/metrics.js.map +1 -0
- package/vendor/microsoft-governance/dist/oci-manifest.d.ts +49 -0
- package/vendor/microsoft-governance/dist/oci-manifest.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/oci-manifest.js +182 -0
- package/vendor/microsoft-governance/dist/oci-manifest.js.map +1 -0
- package/vendor/microsoft-governance/dist/policy-backends/cedar.d.ts +15 -0
- package/vendor/microsoft-governance/dist/policy-backends/cedar.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/policy-backends/cedar.js +88 -0
- package/vendor/microsoft-governance/dist/policy-backends/cedar.js.map +1 -0
- package/vendor/microsoft-governance/dist/policy-backends/opa.d.ts +17 -0
- package/vendor/microsoft-governance/dist/policy-backends/opa.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/policy-backends/opa.js +74 -0
- package/vendor/microsoft-governance/dist/policy-backends/opa.js.map +1 -0
- package/vendor/microsoft-governance/dist/policy.d.ts +77 -0
- package/vendor/microsoft-governance/dist/policy.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/policy.js +760 -0
- package/vendor/microsoft-governance/dist/policy.js.map +1 -0
- package/vendor/microsoft-governance/dist/prompt-defense.d.ts +37 -0
- package/vendor/microsoft-governance/dist/prompt-defense.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/prompt-defense.js +229 -0
- package/vendor/microsoft-governance/dist/prompt-defense.js.map +1 -0
- package/vendor/microsoft-governance/dist/protocol-facets.d.ts +87 -0
- package/vendor/microsoft-governance/dist/protocol-facets.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/protocol-facets.js +728 -0
- package/vendor/microsoft-governance/dist/protocol-facets.js.map +1 -0
- package/vendor/microsoft-governance/dist/rings.d.ts +17 -0
- package/vendor/microsoft-governance/dist/rings.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/rings.js +61 -0
- package/vendor/microsoft-governance/dist/rings.js.map +1 -0
- package/vendor/microsoft-governance/dist/sandbox.d.ts +61 -0
- package/vendor/microsoft-governance/dist/sandbox.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/sandbox.js +199 -0
- package/vendor/microsoft-governance/dist/sandbox.js.map +1 -0
- package/vendor/microsoft-governance/dist/surface-parity.d.ts +45 -0
- package/vendor/microsoft-governance/dist/surface-parity.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/surface-parity.js +216 -0
- package/vendor/microsoft-governance/dist/surface-parity.js.map +1 -0
- package/vendor/microsoft-governance/dist/trust.d.ts +28 -0
- package/vendor/microsoft-governance/dist/trust.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/trust.js +209 -0
- package/vendor/microsoft-governance/dist/trust.js.map +1 -0
- package/vendor/microsoft-governance/dist/types.d.ts +422 -0
- package/vendor/microsoft-governance/dist/types.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/types.js +29 -0
- package/vendor/microsoft-governance/dist/types.js.map +1 -0
- package/vendor/microsoft-governance/dist/verify.d.ts +135 -0
- package/vendor/microsoft-governance/dist/verify.d.ts.map +1 -0
- package/vendor/microsoft-governance/dist/verify.js +356 -0
- package/vendor/microsoft-governance/dist/verify.js.map +1 -0
- package/vendor/microsoft-governance/package.json +64 -0
- package/index.d.ts +0 -177
- package/index.js +0 -1
|
@@ -0,0 +1,1062 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import { AgentIdentity, AuditLogger, ContextPoisoningDetector, ExecutionRing, FacetRegistry, KillSwitch, McpSecurityScanner, PolicyEngine, RingEnforcer, TrustManager, extractK8sFacets, extractProtocolFacets, extractSqlFacets, } from "../../vendor/microsoft-governance/dist/index.js";
|
|
3
|
+
import { describePolicyInPlainEnglish, evaluateParameterPolicyRules, formatHumanFriendlyExplanation, } from "../policy.js";
|
|
4
|
+
const SDK_VERSION = "5.0.0";
|
|
5
|
+
const POLICY_ID = "muxo-microsoft-agent-governance-v5";
|
|
6
|
+
export const RING_LABELS = {
|
|
7
|
+
[ExecutionRing.Ring0]: "Ring 0 (Kernel / Destructive & Root Boundary)",
|
|
8
|
+
[ExecutionRing.Ring1]: "Ring 1 (Supervisor / Provisioning, Financial & Admin)",
|
|
9
|
+
[ExecutionRing.Ring2]: "Ring 2 (Standard Agent / Governed Write)",
|
|
10
|
+
[ExecutionRing.Ring3]: "Ring 3 (Unprivileged Sandbox / Read-Only)",
|
|
11
|
+
};
|
|
12
|
+
function requiredRingForRisk(riskLevel) {
|
|
13
|
+
switch (riskLevel) {
|
|
14
|
+
case "destructive":
|
|
15
|
+
return ExecutionRing.Ring0;
|
|
16
|
+
case "admin":
|
|
17
|
+
case "financial":
|
|
18
|
+
case "provisioning":
|
|
19
|
+
return ExecutionRing.Ring1;
|
|
20
|
+
case "write":
|
|
21
|
+
return ExecutionRing.Ring2;
|
|
22
|
+
case "read":
|
|
23
|
+
default:
|
|
24
|
+
return ExecutionRing.Ring3;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// PKCS#8 DER prefix for a 32-byte Ed25519 private key seed (OID 1.3.101.112)
|
|
28
|
+
const ED25519_PKCS8_PREFIX = Buffer.from([
|
|
29
|
+
0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70,
|
|
30
|
+
0x04, 0x22, 0x04, 0x20,
|
|
31
|
+
]);
|
|
32
|
+
function deriveDeterministicIdentity(seedMaterial, agentId, capabilities, opts) {
|
|
33
|
+
const rawSeed = crypto
|
|
34
|
+
.createHmac("sha256", seedMaterial)
|
|
35
|
+
.update(`muxo-ed25519-agent-identity:${agentId}`)
|
|
36
|
+
.digest();
|
|
37
|
+
const pkcs8Der = Buffer.concat([ED25519_PKCS8_PREFIX, rawSeed]);
|
|
38
|
+
const privateKeyObj = crypto.createPrivateKey({
|
|
39
|
+
key: pkcs8Der,
|
|
40
|
+
format: "der",
|
|
41
|
+
type: "pkcs8",
|
|
42
|
+
});
|
|
43
|
+
const publicKeyObj = crypto.createPublicKey(privateKeyObj);
|
|
44
|
+
const pubDer = Buffer.from(publicKeyObj.export({ type: "spki", format: "der" }));
|
|
45
|
+
const privDer = Buffer.from(privateKeyObj.export({ type: "pkcs8", format: "der" }));
|
|
46
|
+
const fingerprint = crypto
|
|
47
|
+
.createHash("sha256")
|
|
48
|
+
.update(pubDer)
|
|
49
|
+
.digest("hex")
|
|
50
|
+
.slice(0, 16);
|
|
51
|
+
const did = `did:agentmesh:${agentId}:${fingerprint}`;
|
|
52
|
+
return AgentIdentity.fromJSON({
|
|
53
|
+
did,
|
|
54
|
+
publicKey: pubDer.toString("base64"),
|
|
55
|
+
privateKey: privDer.toString("base64"),
|
|
56
|
+
capabilities,
|
|
57
|
+
name: opts.name || agentId,
|
|
58
|
+
organization: opts.organization || "muxo.ai",
|
|
59
|
+
createdAt: opts.createdAt || new Date().toISOString(),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
export class MicrosoftGovernanceAdapter {
|
|
63
|
+
scanner;
|
|
64
|
+
contextPoisoningDetector;
|
|
65
|
+
facetRegistry;
|
|
66
|
+
trustManager;
|
|
67
|
+
killSwitch;
|
|
68
|
+
activeKillSwitches = new Map();
|
|
69
|
+
seedMaterial;
|
|
70
|
+
auditLoggers = new Map();
|
|
71
|
+
identities = new Map();
|
|
72
|
+
defaultIdentity;
|
|
73
|
+
constructor(seed) {
|
|
74
|
+
this.scanner = new McpSecurityScanner();
|
|
75
|
+
this.contextPoisoningDetector = new ContextPoisoningDetector({
|
|
76
|
+
maxContextSizeBytes: 100_000,
|
|
77
|
+
maxEntriesPerSession: 250,
|
|
78
|
+
entropyThreshold: 5.5,
|
|
79
|
+
similarityThreshold: 0.92,
|
|
80
|
+
enableIsolation: true,
|
|
81
|
+
});
|
|
82
|
+
this.facetRegistry = new FacetRegistry();
|
|
83
|
+
this.facetRegistry.register("sql", extractSqlFacets);
|
|
84
|
+
this.facetRegistry.register("k8s", extractK8sFacets);
|
|
85
|
+
this.facetRegistry.register("infra", (sub) => ({
|
|
86
|
+
provider: String(sub.provider ?? "").toLowerCase(),
|
|
87
|
+
service: String(sub.service ?? "").toLowerCase(),
|
|
88
|
+
action: String(sub.action ?? "read").toLowerCase(),
|
|
89
|
+
destructive: Boolean(sub.destructive),
|
|
90
|
+
}));
|
|
91
|
+
this.trustManager = new TrustManager({
|
|
92
|
+
initialScore: 0.75,
|
|
93
|
+
decayFactor: 0.98,
|
|
94
|
+
});
|
|
95
|
+
this.killSwitch = new KillSwitch({
|
|
96
|
+
enabled: true,
|
|
97
|
+
defaultSubstituteAgentId: "muxo-safe-mode-guardian",
|
|
98
|
+
});
|
|
99
|
+
this.seedMaterial = Buffer.isBuffer(seed)
|
|
100
|
+
? seed
|
|
101
|
+
: Buffer.from(String(seed ||
|
|
102
|
+
process.env.MUXO_VAULT_SECRET ||
|
|
103
|
+
process.env.COMPOSIO_API_KEY ||
|
|
104
|
+
"muxo-governance-ed25519-seed-v5"), "utf8");
|
|
105
|
+
this.defaultIdentity = deriveDeterministicIdentity(this.seedMaterial, "muxo-control-plane", [
|
|
106
|
+
"instant.*",
|
|
107
|
+
"app.*",
|
|
108
|
+
"resource.*",
|
|
109
|
+
"mcp.execute",
|
|
110
|
+
"governance.verify",
|
|
111
|
+
], {
|
|
112
|
+
name: "Muxo Control Plane Agent",
|
|
113
|
+
organization: "muxo.ai",
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Returns (or deterministically derives) the Ed25519 AgentIdentity (`did:agentmesh:...`) for a workspace.
|
|
118
|
+
*/
|
|
119
|
+
getWorkspaceIdentity(workspace) {
|
|
120
|
+
let identity = this.identities.get(workspace.id);
|
|
121
|
+
if (!identity) {
|
|
122
|
+
identity = deriveDeterministicIdentity(this.seedMaterial, `muxo-workspace-${workspace.slug || workspace.id}`, [
|
|
123
|
+
"instant.*",
|
|
124
|
+
"app.*",
|
|
125
|
+
"resource.*",
|
|
126
|
+
"mcp.execute",
|
|
127
|
+
"governance.verify",
|
|
128
|
+
], {
|
|
129
|
+
name: `${workspace.name} Agent Boundary`,
|
|
130
|
+
organization: workspace.id,
|
|
131
|
+
createdAt: workspace.createdAt,
|
|
132
|
+
});
|
|
133
|
+
this.identities.set(workspace.id, identity);
|
|
134
|
+
}
|
|
135
|
+
return identity;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Returns (or lazily initializes) the SHA-256 hash-chained AuditLogger for a workspace.
|
|
139
|
+
*/
|
|
140
|
+
getWorkspaceAuditLogger(workspaceId) {
|
|
141
|
+
let logger = this.auditLoggers.get(workspaceId);
|
|
142
|
+
if (!logger) {
|
|
143
|
+
logger = new AuditLogger();
|
|
144
|
+
this.auditLoggers.set(workspaceId, logger);
|
|
145
|
+
}
|
|
146
|
+
return logger;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Scans memory or prompt context for OWASP ASI-06 Context Poisoning (prompt injection,
|
|
150
|
+
* roleplay overrides, delimiter injection, high-entropy exfiltration payloads, and session isolation).
|
|
151
|
+
*/
|
|
152
|
+
scanMemoryContent(params) {
|
|
153
|
+
const safeAgentKey = params.workspaceId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
154
|
+
const safeSessionKey = (params.sessionId || "default").replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
155
|
+
const entryId = params.entryId || `mem_${crypto.randomBytes(6).toString("hex")}`;
|
|
156
|
+
const entry = {
|
|
157
|
+
id: entryId,
|
|
158
|
+
entryId,
|
|
159
|
+
agentId: safeAgentKey,
|
|
160
|
+
sessionId: safeSessionKey,
|
|
161
|
+
role: params.role || "user",
|
|
162
|
+
content: String(params.content || ""),
|
|
163
|
+
timestamp: new Date().toISOString(),
|
|
164
|
+
};
|
|
165
|
+
const findings = this.contextPoisoningDetector.scanEntry(entry);
|
|
166
|
+
// Augment with credential/secret exfiltration detection in memory content
|
|
167
|
+
if (/(cat|curl|wget)\s+.*(\/etc\/passwd|\.env|id_rsa|AWS_SECRET_ACCESS_KEY)/i.test(entry.content) ||
|
|
168
|
+
/send\s+(all\s+)?(api\s+keys|secrets|tokens|credentials)\s+to\s+https?:\/\//i.test(entry.content)) {
|
|
169
|
+
findings.push({
|
|
170
|
+
entryId: entry.id,
|
|
171
|
+
patternId: "MEMORY_EXFILTRATION_DIRECTIVE",
|
|
172
|
+
patternName: "Persistent Memory Credential Exfiltration Directive",
|
|
173
|
+
severity: "critical",
|
|
174
|
+
description: "Memory payload attempts to persist credential or file exfiltration instructions across agent runs",
|
|
175
|
+
matchedText: entry.content.slice(0, 100),
|
|
176
|
+
timestamp: Date.now(),
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
const hasCritical = findings.some((f) => f.severity === "critical");
|
|
180
|
+
const hasHigh = findings.some((f) => f.severity === "high");
|
|
181
|
+
const hasMedium = findings.some((f) => f.severity === "medium");
|
|
182
|
+
const riskLevel = hasCritical
|
|
183
|
+
? "critical"
|
|
184
|
+
: hasHigh
|
|
185
|
+
? "high"
|
|
186
|
+
: hasMedium
|
|
187
|
+
? "medium"
|
|
188
|
+
: findings.length > 0
|
|
189
|
+
? "low"
|
|
190
|
+
: "none";
|
|
191
|
+
const safe = !hasCritical && !hasHigh;
|
|
192
|
+
if (params.recordInDetector !== false && safe) {
|
|
193
|
+
try {
|
|
194
|
+
this.contextPoisoningDetector.addEntry(entry);
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
// Ignore session capacity overflow in long-running processes
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const integrity = this.contextPoisoningDetector.verifyIntegrity(safeAgentKey, safeSessionKey);
|
|
201
|
+
const integrityValid = typeof integrity === "boolean" ? integrity : Boolean(integrity?.valid ?? true);
|
|
202
|
+
return {
|
|
203
|
+
safe,
|
|
204
|
+
riskLevel,
|
|
205
|
+
findingsCount: findings.length,
|
|
206
|
+
findings: findings.map((f) => ({
|
|
207
|
+
patternId: String(f.patternId || ""),
|
|
208
|
+
patternName: String(f.patternName || ""),
|
|
209
|
+
severity: String(f.severity || ""),
|
|
210
|
+
description: String(f.description || ""),
|
|
211
|
+
})),
|
|
212
|
+
integrityValid,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
getContextPoisoningReport(workspaceId, sessionId = "default") {
|
|
216
|
+
const safeAgentKey = workspaceId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
217
|
+
const safeSessionKey = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
218
|
+
return this.contextPoisoningDetector.scan(safeAgentKey, safeSessionKey);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Extracts structured SQL, Kubernetes, and Infrastructure protocol facets from tool input.
|
|
222
|
+
*/
|
|
223
|
+
extractActionFacets(tool, input) {
|
|
224
|
+
const rawContext = {};
|
|
225
|
+
// Detect SQL payload in input
|
|
226
|
+
const candidateSql = typeof input.sql === "string"
|
|
227
|
+
? input.sql
|
|
228
|
+
: typeof input.statement === "string"
|
|
229
|
+
? input.statement
|
|
230
|
+
: typeof input.query === "string" &&
|
|
231
|
+
(tool.slug.includes("sql") ||
|
|
232
|
+
tool.slug.includes("postgres") ||
|
|
233
|
+
tool.slug.includes("neon") ||
|
|
234
|
+
tool.slug.includes("turso") ||
|
|
235
|
+
tool.slug.includes("supabase") ||
|
|
236
|
+
tool.slug.includes("db") ||
|
|
237
|
+
/^\s*(SELECT|INSERT|UPDATE|DELETE|DROP|TRUNCATE|ALTER|CREATE|WITH)\b/i.test(input.query))
|
|
238
|
+
? input.query
|
|
239
|
+
: null;
|
|
240
|
+
if (candidateSql) {
|
|
241
|
+
rawContext.sql = { query: candidateSql };
|
|
242
|
+
}
|
|
243
|
+
// Detect Kubernetes payload in input
|
|
244
|
+
const candidateK8sPath = typeof input.k8s_path === "string"
|
|
245
|
+
? input.k8s_path
|
|
246
|
+
: typeof input.path === "string" &&
|
|
247
|
+
(input.path.startsWith("/api/v1/") ||
|
|
248
|
+
input.path.startsWith("/apis/"))
|
|
249
|
+
? input.path
|
|
250
|
+
: null;
|
|
251
|
+
if (candidateK8sPath) {
|
|
252
|
+
rawContext.k8s = {
|
|
253
|
+
method: typeof input.method === "string" ? input.method : "GET",
|
|
254
|
+
path: candidateK8sPath,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
else if (input.k8s &&
|
|
258
|
+
typeof input.k8s === "object" &&
|
|
259
|
+
!Array.isArray(input.k8s)) {
|
|
260
|
+
rawContext.k8s = input.k8s;
|
|
261
|
+
}
|
|
262
|
+
// Detect Infrastructure / Resource provisioning facets
|
|
263
|
+
if (tool.lane === "stripe_projects" ||
|
|
264
|
+
tool.slug.startsWith("resource.") ||
|
|
265
|
+
tool.riskLevel === "provisioning" ||
|
|
266
|
+
tool.riskLevel === "destructive") {
|
|
267
|
+
rawContext.infra = {
|
|
268
|
+
provider: tool.providerSlug,
|
|
269
|
+
service: tool.upstreamSlug,
|
|
270
|
+
action: tool.riskLevel,
|
|
271
|
+
destructive: tool.riskLevel === "destructive" ||
|
|
272
|
+
tool.slug.includes("delete") ||
|
|
273
|
+
tool.slug.includes("destroy"),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
const extracted = extractProtocolFacets(rawContext, this.facetRegistry);
|
|
277
|
+
const result = {};
|
|
278
|
+
if (extracted.sql) {
|
|
279
|
+
const sqlVerb = String(extracted.sql.verb || "UNKNOWN").toUpperCase();
|
|
280
|
+
const isDestructiveSql = sqlVerb === "DROP" ||
|
|
281
|
+
sqlVerb === "TRUNCATE" ||
|
|
282
|
+
(sqlVerb === "UNKNOWN" && Boolean(candidateSql?.includes(";"))) ||
|
|
283
|
+
(sqlVerb === "DELETE" &&
|
|
284
|
+
Boolean(candidateSql) &&
|
|
285
|
+
!/\bWHERE\b/i.test(candidateSql || ""));
|
|
286
|
+
result.sql = {
|
|
287
|
+
verb: sqlVerb,
|
|
288
|
+
target: String(extracted.sql.target || ""),
|
|
289
|
+
tables: String(extracted.sql.tables || ""),
|
|
290
|
+
functions: String(extracted.sql.functions || ""),
|
|
291
|
+
destructive: isDestructiveSql,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
if (extracted.k8s) {
|
|
295
|
+
result.k8s = {
|
|
296
|
+
verb: String(extracted.k8s.verb || ""),
|
|
297
|
+
resource: String(extracted.k8s.resource || ""),
|
|
298
|
+
namespace: String(extracted.k8s.namespace || ""),
|
|
299
|
+
name: String(extracted.k8s.name || ""),
|
|
300
|
+
subresource: String(extracted.k8s.subresource || ""),
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
if (extracted.infra) {
|
|
304
|
+
result.infra = {
|
|
305
|
+
provider: String(extracted.infra.provider || tool.providerSlug),
|
|
306
|
+
service: String(extracted.infra.service || tool.upstreamSlug),
|
|
307
|
+
action: String(extracted.infra.action || tool.riskLevel),
|
|
308
|
+
destructive: Boolean(extracted.infra.destructive),
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
return result;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Evaluates hardware-style privilege rings (Ring 0..Ring 3) via Microsoft RingEnforcer.
|
|
315
|
+
*/
|
|
316
|
+
evaluateRingEnforcement(workspace, tool, ownerApproved) {
|
|
317
|
+
const requiredRing = requiredRingForRisk(tool.riskLevel);
|
|
318
|
+
const policy = workspace.policy;
|
|
319
|
+
const requiresApproval = policy.mode === "enforce" &&
|
|
320
|
+
(policy.requireApprovalFor || []).includes(tool.riskLevel);
|
|
321
|
+
const agentRing = ownerApproved
|
|
322
|
+
? ExecutionRing.Ring0
|
|
323
|
+
: policy.executionRing !== undefined
|
|
324
|
+
? policy.executionRing
|
|
325
|
+
: requiresApproval
|
|
326
|
+
? ExecutionRing.Ring2
|
|
327
|
+
: requiredRing;
|
|
328
|
+
const enforcer = new RingEnforcer({
|
|
329
|
+
agentRing,
|
|
330
|
+
defaultRing: ExecutionRing.Ring3,
|
|
331
|
+
actionRings: {
|
|
332
|
+
"read:*": ExecutionRing.Ring3,
|
|
333
|
+
"write:*": ExecutionRing.Ring2,
|
|
334
|
+
"admin:*": ExecutionRing.Ring1,
|
|
335
|
+
"financial:*": ExecutionRing.Ring1,
|
|
336
|
+
"provisioning:*": ExecutionRing.Ring1,
|
|
337
|
+
"destructive:*": ExecutionRing.Ring0,
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
const actionKey = `${tool.riskLevel}:${tool.slug}`;
|
|
341
|
+
const allowed = enforcer.canExecute(actionKey);
|
|
342
|
+
return {
|
|
343
|
+
agentRing,
|
|
344
|
+
requiredRing,
|
|
345
|
+
allowed,
|
|
346
|
+
ringLabel: RING_LABELS[requiredRing] || `Ring ${requiredRing}`,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Records an agent execution outcome in the Bayesian TrustManager and returns the updated TrustScore.
|
|
351
|
+
*/
|
|
352
|
+
recordAgentOutcome(workspaceId, succeeded, weight = 0.05) {
|
|
353
|
+
if (succeeded) {
|
|
354
|
+
this.trustManager.recordSuccess(workspaceId, weight);
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
this.trustManager.recordFailure(workspaceId, weight * 2);
|
|
358
|
+
}
|
|
359
|
+
return this.trustManager.getTrustScore(workspaceId);
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Returns the current Bayesian TrustScore for a workspace agent boundary.
|
|
363
|
+
*/
|
|
364
|
+
getWorkspaceTrustScore(workspaceId) {
|
|
365
|
+
const score = this.trustManager.getTrustScore(workspaceId);
|
|
366
|
+
return {
|
|
367
|
+
overall: Number(score.overall.toFixed(4)),
|
|
368
|
+
tier: score.tier,
|
|
369
|
+
reliability: Number(score.dimensions.reliability.toFixed(4)),
|
|
370
|
+
consistency: Number(score.dimensions.consistency.toFixed(4)),
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Activates the emergency KillSwitch for a workspace agent boundary.
|
|
375
|
+
*/
|
|
376
|
+
async activateKillSwitch(workspaceId, reason, action = "quarantine") {
|
|
377
|
+
const result = await this.killSwitch.kill(workspaceId, {
|
|
378
|
+
action,
|
|
379
|
+
reason: reason || "Emergency kill-switch activated by workspace operator",
|
|
380
|
+
});
|
|
381
|
+
this.activeKillSwitches.set(workspaceId, result);
|
|
382
|
+
this.trustManager.recordFailure(workspaceId, 0.25);
|
|
383
|
+
return result;
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Resumes a workspace agent boundary after a KillSwitch quarantine/suspension.
|
|
387
|
+
*/
|
|
388
|
+
resumeKillSwitch(workspaceId) {
|
|
389
|
+
const existed = this.activeKillSwitches.delete(workspaceId);
|
|
390
|
+
return {
|
|
391
|
+
resumed: existed,
|
|
392
|
+
workspaceId,
|
|
393
|
+
resumedAt: new Date().toISOString(),
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
isKillSwitchActive(workspaceId) {
|
|
397
|
+
return this.activeKillSwitches.get(workspaceId) || null;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Compiles the workspace's GovernancePolicy into a declarative Microsoft PolicyEngine instance.
|
|
401
|
+
*/
|
|
402
|
+
createPolicyEngine(policy) {
|
|
403
|
+
const engine = new PolicyEngine();
|
|
404
|
+
const rules = [];
|
|
405
|
+
if (policy.killSwitchActive) {
|
|
406
|
+
rules.push({
|
|
407
|
+
name: "deny-kill-switch-active",
|
|
408
|
+
ruleAction: "deny",
|
|
409
|
+
priority: 200,
|
|
410
|
+
condition: {
|
|
411
|
+
workspace_active: true,
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
for (const blockedSlug of policy.blockedToolSlugs || []) {
|
|
416
|
+
const normalized = blockedSlug.trim();
|
|
417
|
+
if (!normalized)
|
|
418
|
+
continue;
|
|
419
|
+
rules.push({
|
|
420
|
+
name: `deny-blocked-tool-${normalized.replace(/[^a-zA-Z0-9_-]/g, "_")}`,
|
|
421
|
+
ruleAction: "deny",
|
|
422
|
+
priority: 100,
|
|
423
|
+
condition: {
|
|
424
|
+
tool_slug: normalized,
|
|
425
|
+
},
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
if (policy.mode === "enforce" && policy.blockDestructiveSql !== false) {
|
|
429
|
+
rules.push({
|
|
430
|
+
name: "deny-multi-statement-sql-injection",
|
|
431
|
+
ruleAction: "deny",
|
|
432
|
+
priority: 96,
|
|
433
|
+
condition: {
|
|
434
|
+
"sql.verb": "UNKNOWN",
|
|
435
|
+
"sql.destructive": true,
|
|
436
|
+
},
|
|
437
|
+
});
|
|
438
|
+
rules.push({
|
|
439
|
+
name: "require-approval-destructive-sql-drop",
|
|
440
|
+
ruleAction: "require_approval",
|
|
441
|
+
priority: 94,
|
|
442
|
+
condition: {
|
|
443
|
+
"sql.verb": "DROP",
|
|
444
|
+
owner_approved: false,
|
|
445
|
+
},
|
|
446
|
+
});
|
|
447
|
+
rules.push({
|
|
448
|
+
name: "require-approval-destructive-sql-truncate",
|
|
449
|
+
ruleAction: "require_approval",
|
|
450
|
+
priority: 94,
|
|
451
|
+
condition: {
|
|
452
|
+
"sql.verb": "TRUNCATE",
|
|
453
|
+
owner_approved: false,
|
|
454
|
+
},
|
|
455
|
+
});
|
|
456
|
+
rules.push({
|
|
457
|
+
name: "deny-k8s-delete-kube-system",
|
|
458
|
+
ruleAction: "deny",
|
|
459
|
+
priority: 95,
|
|
460
|
+
condition: {
|
|
461
|
+
"k8s.verb": "delete",
|
|
462
|
+
"k8s.namespace": "kube-system",
|
|
463
|
+
},
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
if (policy.mode === "enforce") {
|
|
467
|
+
for (const risk of policy.requireApprovalFor || []) {
|
|
468
|
+
rules.push({
|
|
469
|
+
name: `require-owner-approval-for-${risk}`,
|
|
470
|
+
ruleAction: "require_approval",
|
|
471
|
+
priority: 80,
|
|
472
|
+
condition: {
|
|
473
|
+
risk_level: risk,
|
|
474
|
+
owner_approved: false,
|
|
475
|
+
},
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
rules.push({
|
|
480
|
+
name: "allow-governed-action",
|
|
481
|
+
ruleAction: "allow",
|
|
482
|
+
priority: 10,
|
|
483
|
+
condition: {
|
|
484
|
+
workspace_active: true,
|
|
485
|
+
},
|
|
486
|
+
});
|
|
487
|
+
engine.loadJson(JSON.stringify({
|
|
488
|
+
apiVersion: "governance.toolkit/v1",
|
|
489
|
+
name: POLICY_ID,
|
|
490
|
+
version: "5.0.0",
|
|
491
|
+
description: "Muxo Unified Execution Boundary Policy",
|
|
492
|
+
scope: "tenant",
|
|
493
|
+
default_action: "deny",
|
|
494
|
+
rules,
|
|
495
|
+
}));
|
|
496
|
+
return engine;
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Runs the full Microsoft Agent Governance pipeline:
|
|
500
|
+
* Agent ──► KillSwitch ──► McpSecurityScanner & ContextPoisoningDetector (ASI-06) ──► ProtocolFacets (SQL/K8s/Infra) ──► RingEnforcer ──► PolicyEngine ──► TrustManager ──► AgentIdentity (Ed25519 DID) ──► AuditLogger ──► DecisionRecord
|
|
501
|
+
*/
|
|
502
|
+
evaluateAction(params) {
|
|
503
|
+
const { workspace, actorId, tool, input, reason, stage } = params;
|
|
504
|
+
const ownerApproved = Boolean(params.ownerApproved);
|
|
505
|
+
const decidedAt = new Date().toISOString();
|
|
506
|
+
// 1. McpSecurityScanner + ContextPoisoningDetector (OWASP ASI-06): inspect tool definition + input arguments
|
|
507
|
+
const serializedInput = JSON.stringify(input || {});
|
|
508
|
+
const scanResult = this.scanner.scan({
|
|
509
|
+
name: tool.slug,
|
|
510
|
+
description: `${tool.description}\nReason: ${reason}\nArgs: ${serializedInput}`,
|
|
511
|
+
parameters: tool.inputSchema,
|
|
512
|
+
});
|
|
513
|
+
const contextPoisoning = this.scanMemoryContent({
|
|
514
|
+
workspaceId: workspace.id,
|
|
515
|
+
sessionId: stage,
|
|
516
|
+
role: "user",
|
|
517
|
+
content: `${reason}\n${serializedInput}`,
|
|
518
|
+
recordInDetector: false,
|
|
519
|
+
});
|
|
520
|
+
const extraFindings = [
|
|
521
|
+
...(scanResult.threats || []).map((t) => ({
|
|
522
|
+
category: String(t.type || "mcp_scan"),
|
|
523
|
+
severity: String(t.severity || "medium"),
|
|
524
|
+
description: String(t.description || "Security scanner finding"),
|
|
525
|
+
})),
|
|
526
|
+
...contextPoisoning.findings.map((f) => ({
|
|
527
|
+
category: `asi06_${f.patternId.toLowerCase()}`,
|
|
528
|
+
severity: f.severity,
|
|
529
|
+
description: `${f.patternName}: ${f.description}`,
|
|
530
|
+
})),
|
|
531
|
+
];
|
|
532
|
+
// Additional argument-level prompt-injection / secret-exfiltration heuristics
|
|
533
|
+
const suspiciousPatterns = [
|
|
534
|
+
{
|
|
535
|
+
regex: /ignore\s+(all\s+)?previous\s+instructions/i,
|
|
536
|
+
category: "prompt_injection",
|
|
537
|
+
severity: "high",
|
|
538
|
+
description: "Detected prompt injection override phrase in tool arguments or reason",
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
regex: /(cat|curl|wget)\s+.*(\/etc\/passwd|\.env|id_rsa|AWS_SECRET_ACCESS_KEY)/i,
|
|
542
|
+
category: "credential_exfiltration",
|
|
543
|
+
severity: "high",
|
|
544
|
+
description: "Detected credential or sensitive file exfiltration pattern in arguments",
|
|
545
|
+
},
|
|
546
|
+
];
|
|
547
|
+
for (const pat of suspiciousPatterns) {
|
|
548
|
+
if (pat.regex.test(serializedInput) || pat.regex.test(reason)) {
|
|
549
|
+
extraFindings.push({
|
|
550
|
+
category: pat.category,
|
|
551
|
+
severity: pat.severity,
|
|
552
|
+
description: pat.description,
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
const highSeverityFinding = extraFindings.some((f) => f.severity === "high" || f.severity === "critical");
|
|
557
|
+
// 2. Protocol Facets Extraction (SQL, Kubernetes, Infrastructure)
|
|
558
|
+
const protocolFacets = this.extractActionFacets(tool, input);
|
|
559
|
+
// 3. PolicyEngine + KillSwitch + Parameter Guardrail evaluation
|
|
560
|
+
const policy = workspace.policy;
|
|
561
|
+
const paramRuleCheck = evaluateParameterPolicyRules({
|
|
562
|
+
policy,
|
|
563
|
+
toolSlug: tool.slug,
|
|
564
|
+
input,
|
|
565
|
+
});
|
|
566
|
+
if (paramRuleCheck.violated && paramRuleCheck.reason) {
|
|
567
|
+
extraFindings.push({
|
|
568
|
+
category: "parameter_policy_rule",
|
|
569
|
+
severity: paramRuleCheck.action === "block" ? "high" : "medium",
|
|
570
|
+
description: paramRuleCheck.reason,
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
const effectiveApproved = ownerApproved ||
|
|
574
|
+
Boolean(paramRuleCheck.explicitAllow && paramRuleCheck.action === "auto_approve");
|
|
575
|
+
// 4. Hardware-style Execution Ring Enforcement (Ring 0..Ring 3)
|
|
576
|
+
const ringEnforcement = this.evaluateRingEnforcement(workspace, tool, effectiveApproved);
|
|
577
|
+
const activeKill = this.isKillSwitchActive(workspace.id);
|
|
578
|
+
const killSwitchActive = Boolean(activeKill || policy.killSwitchActive);
|
|
579
|
+
const killSwitchReason = activeKill?.reason || policy.killSwitchReason || null;
|
|
580
|
+
const policyEngine = this.createPolicyEngine(policy);
|
|
581
|
+
const identity = this.getWorkspaceIdentity(workspace);
|
|
582
|
+
const policyDecision = policyEngine.evaluatePolicy(identity.did, {
|
|
583
|
+
tool_slug: tool.slug,
|
|
584
|
+
risk_level: tool.riskLevel,
|
|
585
|
+
lane: tool.lane,
|
|
586
|
+
stage,
|
|
587
|
+
owner_approved: effectiveApproved,
|
|
588
|
+
workspace_active: true,
|
|
589
|
+
...(protocolFacets.sql ? { sql: protocolFacets.sql } : {}),
|
|
590
|
+
...(protocolFacets.k8s ? { k8s: protocolFacets.k8s } : {}),
|
|
591
|
+
...(protocolFacets.infra ? { infra: protocolFacets.infra } : {}),
|
|
592
|
+
});
|
|
593
|
+
let finalDecision = policyDecision.action === "deny"
|
|
594
|
+
? "deny"
|
|
595
|
+
: policyDecision.action === "require_approval"
|
|
596
|
+
? "require_approval"
|
|
597
|
+
: "allow";
|
|
598
|
+
let matchedRule = paramRuleCheck.explicitAllow &&
|
|
599
|
+
paramRuleCheck.action === "auto_approve" &&
|
|
600
|
+
finalDecision === "allow"
|
|
601
|
+
? paramRuleCheck.matchedRule?.id || "auto-approve-parameter-policy-rule"
|
|
602
|
+
: policyDecision.matchedRule ?? null;
|
|
603
|
+
// Enforce KillSwitch, blocked tools, parameter rules, high-severity scanner/context-poisoning findings, or Ring breach
|
|
604
|
+
if (killSwitchActive) {
|
|
605
|
+
finalDecision = "deny";
|
|
606
|
+
matchedRule = "kill-switch-emergency-quarantine";
|
|
607
|
+
}
|
|
608
|
+
else if ((policy.blockedToolSlugs || []).includes(tool.slug)) {
|
|
609
|
+
finalDecision = "deny";
|
|
610
|
+
matchedRule = matchedRule || `deny-blocked-tool-${tool.slug}`;
|
|
611
|
+
}
|
|
612
|
+
else if (paramRuleCheck.violated &&
|
|
613
|
+
paramRuleCheck.action === "block" &&
|
|
614
|
+
policy.mode === "enforce") {
|
|
615
|
+
finalDecision = "deny";
|
|
616
|
+
matchedRule = paramRuleCheck.matchedRule?.id || "deny-parameter-policy-rule";
|
|
617
|
+
}
|
|
618
|
+
else if (paramRuleCheck.violated &&
|
|
619
|
+
paramRuleCheck.action === "require_approval" &&
|
|
620
|
+
!ownerApproved &&
|
|
621
|
+
policy.mode === "enforce") {
|
|
622
|
+
finalDecision = "require_approval";
|
|
623
|
+
matchedRule = paramRuleCheck.matchedRule?.id || "approval-parameter-policy-rule";
|
|
624
|
+
}
|
|
625
|
+
else if (highSeverityFinding && policy.mode === "enforce") {
|
|
626
|
+
finalDecision = "deny";
|
|
627
|
+
matchedRule = "mcp-security-scanner-high-severity";
|
|
628
|
+
this.trustManager.recordFailure(workspace.id, 0.1);
|
|
629
|
+
}
|
|
630
|
+
else if (!ringEnforcement.allowed && finalDecision === "allow" && policy.mode === "enforce") {
|
|
631
|
+
finalDecision = "require_approval";
|
|
632
|
+
matchedRule = `ring-enforcer-requires-ring-${ringEnforcement.requiredRing}`;
|
|
633
|
+
}
|
|
634
|
+
const trustScore = this.getWorkspaceTrustScore(workspace.id);
|
|
635
|
+
// 5. AuditLogger: append tamper-evident SHA-256 hash-chained log entry
|
|
636
|
+
const auditLogger = this.getWorkspaceAuditLogger(workspace.id);
|
|
637
|
+
const auditDecision = finalDecision === "deny" ? "deny" : "allow";
|
|
638
|
+
const auditEntry = auditLogger.log({
|
|
639
|
+
agentId: identity.did,
|
|
640
|
+
action: `${stage}.${tool.riskLevel}:${tool.slug}`,
|
|
641
|
+
decision: auditDecision,
|
|
642
|
+
skillAuditMetadata: {
|
|
643
|
+
skillName: tool.slug,
|
|
644
|
+
skillOrigin: tool.lane,
|
|
645
|
+
provenanceSourceTrust: trustScore.tier === "Verified" || trustScore.tier === "Trusted"
|
|
646
|
+
? "trusted"
|
|
647
|
+
: undefined,
|
|
648
|
+
},
|
|
649
|
+
});
|
|
650
|
+
const chainVerified = auditLogger.verify();
|
|
651
|
+
// 6. AgentIdentity (Ed25519 DID): cryptographically sign the decision record
|
|
652
|
+
const bindingHash = crypto
|
|
653
|
+
.createHash("sha256")
|
|
654
|
+
.update(JSON.stringify({
|
|
655
|
+
workspaceId: workspace.id,
|
|
656
|
+
actorId,
|
|
657
|
+
toolSlug: tool.slug,
|
|
658
|
+
riskLevel: tool.riskLevel,
|
|
659
|
+
stage,
|
|
660
|
+
input,
|
|
661
|
+
finalDecision,
|
|
662
|
+
auditHash: auditEntry.hash,
|
|
663
|
+
decidedAt,
|
|
664
|
+
}))
|
|
665
|
+
.digest("hex");
|
|
666
|
+
const signaturePayload = [
|
|
667
|
+
POLICY_ID,
|
|
668
|
+
identity.did,
|
|
669
|
+
workspace.id,
|
|
670
|
+
actorId,
|
|
671
|
+
tool.slug,
|
|
672
|
+
tool.riskLevel,
|
|
673
|
+
finalDecision,
|
|
674
|
+
auditEntry.hash,
|
|
675
|
+
auditEntry.previousHash,
|
|
676
|
+
bindingHash,
|
|
677
|
+
decidedAt,
|
|
678
|
+
].join("|");
|
|
679
|
+
const sigBytes = identity.sign(Buffer.from(signaturePayload, "utf8"));
|
|
680
|
+
const ed25519Signature = Buffer.from(sigBytes).toString("hex");
|
|
681
|
+
const publicKeyHex = Buffer.from(identity.publicKey).toString("hex");
|
|
682
|
+
const verdict = finalDecision === "require_approval" ? "escalate" : finalDecision;
|
|
683
|
+
const decisionReason = finalDecision === "deny"
|
|
684
|
+
? killSwitchActive
|
|
685
|
+
? `Blocked by Emergency KillSwitch: ${killSwitchReason || "Workspace agent boundary is quarantined"}`
|
|
686
|
+
: paramRuleCheck.violated && paramRuleCheck.reason
|
|
687
|
+
? paramRuleCheck.reason
|
|
688
|
+
: highSeverityFinding
|
|
689
|
+
? `Blocked by McpSecurityScanner / ContextPoisoningDetector: ${extraFindings.map((f) => f.description).join("; ")}`
|
|
690
|
+
: `Denied by policy rule ${matchedRule || "default"}`
|
|
691
|
+
: finalDecision === "require_approval"
|
|
692
|
+
? paramRuleCheck.violated && paramRuleCheck.reason
|
|
693
|
+
? paramRuleCheck.reason
|
|
694
|
+
: `Escalated for human owner approval (${tool.riskLevel} risk lane, ${ringEnforcement.ringLabel})`
|
|
695
|
+
: `Allowed by Microsoft Agent Governance (${matchedRule || "allow-governed-action"})`;
|
|
696
|
+
const record = {
|
|
697
|
+
id: `dec_${crypto.randomBytes(8).toString("hex")}`,
|
|
698
|
+
sdk: "@microsoft/agent-governance-sdk",
|
|
699
|
+
sdkVersion: SDK_VERSION,
|
|
700
|
+
policyId: POLICY_ID,
|
|
701
|
+
mode: policy.mode,
|
|
702
|
+
agentDid: identity.did,
|
|
703
|
+
agentPublicKey: publicKeyHex,
|
|
704
|
+
actorId,
|
|
705
|
+
workspaceId: workspace.id,
|
|
706
|
+
action: `${stage}.${tool.riskLevel}:${tool.slug}`,
|
|
707
|
+
riskLevel: tool.riskLevel,
|
|
708
|
+
decision: finalDecision,
|
|
709
|
+
verdict,
|
|
710
|
+
reason: decisionReason,
|
|
711
|
+
matchedRule,
|
|
712
|
+
securityScan: {
|
|
713
|
+
safe: scanResult.safe && !highSeverityFinding && contextPoisoning.safe,
|
|
714
|
+
riskLevel: highSeverityFinding
|
|
715
|
+
? "high"
|
|
716
|
+
: scanResult.risk_score > 50 || contextPoisoning.findingsCount > 0
|
|
717
|
+
? "medium"
|
|
718
|
+
: "low",
|
|
719
|
+
findingsCount: extraFindings.length,
|
|
720
|
+
findings: extraFindings,
|
|
721
|
+
},
|
|
722
|
+
auditEntry: {
|
|
723
|
+
timestamp: auditEntry.timestamp,
|
|
724
|
+
agentId: auditEntry.agentId,
|
|
725
|
+
action: auditEntry.action,
|
|
726
|
+
decision: auditDecision,
|
|
727
|
+
hash: auditEntry.hash,
|
|
728
|
+
previousHash: auditEntry.previousHash,
|
|
729
|
+
chainVerified,
|
|
730
|
+
},
|
|
731
|
+
protocolFacets,
|
|
732
|
+
ringEnforcement,
|
|
733
|
+
trustScore,
|
|
734
|
+
contextPoisoning: {
|
|
735
|
+
safe: contextPoisoning.safe,
|
|
736
|
+
riskLevel: contextPoisoning.riskLevel,
|
|
737
|
+
findingsCount: contextPoisoning.findingsCount,
|
|
738
|
+
findings: contextPoisoning.findings,
|
|
739
|
+
},
|
|
740
|
+
killSwitch: {
|
|
741
|
+
active: killSwitchActive,
|
|
742
|
+
killedAt: activeKill?.killedAt || null,
|
|
743
|
+
reason: killSwitchReason,
|
|
744
|
+
},
|
|
745
|
+
ed25519Signature,
|
|
746
|
+
signature: ed25519Signature,
|
|
747
|
+
bindingHash,
|
|
748
|
+
decidedAt,
|
|
749
|
+
};
|
|
750
|
+
record.explanation = explainDecisionRecord(record, policy);
|
|
751
|
+
return record;
|
|
752
|
+
}
|
|
753
|
+
evaluatePlan(params) {
|
|
754
|
+
return this.evaluateAction({
|
|
755
|
+
...params,
|
|
756
|
+
stage: "plan",
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
evaluateExecution(params) {
|
|
760
|
+
return this.evaluateAction({
|
|
761
|
+
workspace: params.workspace,
|
|
762
|
+
actorId: params.actorId,
|
|
763
|
+
tool: params.tool,
|
|
764
|
+
input: params.plan.input,
|
|
765
|
+
reason: params.plan.reason,
|
|
766
|
+
stage: "execute",
|
|
767
|
+
ownerApproved: params.plan.approvalStatus === "approved" ||
|
|
768
|
+
params.plan.approvalStatus === "auto_approved",
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Verifies a DecisionRecord's Ed25519 signature and the workspace's SHA-256 audit hash chain.
|
|
773
|
+
*/
|
|
774
|
+
verifyDecisionRecord(workspaceOrRecord, maybeRecord) {
|
|
775
|
+
const record = maybeRecord || workspaceOrRecord;
|
|
776
|
+
const workspace = maybeRecord ? workspaceOrRecord : undefined;
|
|
777
|
+
const identity = workspace
|
|
778
|
+
? this.getWorkspaceIdentity(workspace)
|
|
779
|
+
: this.identities.get(record.workspaceId) || this.defaultIdentity;
|
|
780
|
+
const stagePrefix = record.action.split(":")[0] || "";
|
|
781
|
+
const toolSlug = record.action.slice(stagePrefix.length + 1);
|
|
782
|
+
const signaturePayload = [
|
|
783
|
+
record.policyId,
|
|
784
|
+
record.agentDid,
|
|
785
|
+
record.workspaceId,
|
|
786
|
+
record.actorId,
|
|
787
|
+
toolSlug,
|
|
788
|
+
record.riskLevel,
|
|
789
|
+
record.decision,
|
|
790
|
+
record.auditEntry.hash,
|
|
791
|
+
record.auditEntry.previousHash,
|
|
792
|
+
record.bindingHash,
|
|
793
|
+
record.decidedAt,
|
|
794
|
+
].join("|");
|
|
795
|
+
let ed25519Verified = false;
|
|
796
|
+
try {
|
|
797
|
+
if (identity.did === record.agentDid) {
|
|
798
|
+
ed25519Verified = identity.verify(Buffer.from(signaturePayload, "utf8"), Buffer.from(record.ed25519Signature, "hex"));
|
|
799
|
+
}
|
|
800
|
+
else if (record.agentPublicKey && record.ed25519Signature) {
|
|
801
|
+
const pubKeyObj = crypto.createPublicKey({
|
|
802
|
+
key: Buffer.from(record.agentPublicKey, "hex"),
|
|
803
|
+
format: "der",
|
|
804
|
+
type: "spki",
|
|
805
|
+
});
|
|
806
|
+
ed25519Verified = crypto.verify(null, Buffer.from(signaturePayload, "utf8"), pubKeyObj, Buffer.from(record.ed25519Signature, "hex"));
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
catch {
|
|
810
|
+
ed25519Verified = false;
|
|
811
|
+
}
|
|
812
|
+
const auditLogger = this.getWorkspaceAuditLogger(record.workspaceId);
|
|
813
|
+
const auditChainVerified = auditLogger.verify();
|
|
814
|
+
return ed25519Verified && auditChainVerified;
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Builds a portable Ed25519-signed AgenticReceipt envelope (compatible with the `agentic-receipts` spec)
|
|
818
|
+
* so external verifiers can validate input/output SHA-256 hashes, hash chaining, and Ed25519 signature
|
|
819
|
+
* using only the workspace's public key and DID without needing the internal vault HMAC key.
|
|
820
|
+
*/
|
|
821
|
+
buildPortableAgenticReceipt(params) {
|
|
822
|
+
const { receipt, run, workspace } = params;
|
|
823
|
+
const identity = workspace
|
|
824
|
+
? this.getWorkspaceIdentity(workspace)
|
|
825
|
+
: this.identities.get(receipt.workspaceId) || this.defaultIdentity;
|
|
826
|
+
const actionType = receipt.lane === "stripe_projects" ? "ApiCall" : "ToolCall";
|
|
827
|
+
const startedAt = run?.startedAt || receipt.issuedAt;
|
|
828
|
+
const completedAt = run?.completedAt || receipt.issuedAt;
|
|
829
|
+
const latencyMs = Math.max(0, Number(run?.durationMs ?? 0));
|
|
830
|
+
const nonce = crypto
|
|
831
|
+
.createHash("sha256")
|
|
832
|
+
.update(`muxo-agentic-receipt-nonce:${receipt.id}:${receipt.runId}`)
|
|
833
|
+
.digest("hex")
|
|
834
|
+
.slice(0, 32);
|
|
835
|
+
const previousReceiptHash = receipt.prevReceiptHash || "genesis_0000000000000000";
|
|
836
|
+
const canonicalPayload = [
|
|
837
|
+
"0.1.0",
|
|
838
|
+
receipt.id,
|
|
839
|
+
identity.did,
|
|
840
|
+
receipt.runId,
|
|
841
|
+
actionType,
|
|
842
|
+
receipt.toolSlug || "",
|
|
843
|
+
receipt.inputHash,
|
|
844
|
+
receipt.outputHash,
|
|
845
|
+
startedAt,
|
|
846
|
+
completedAt,
|
|
847
|
+
String(latencyMs),
|
|
848
|
+
nonce,
|
|
849
|
+
previousReceiptHash || "genesis",
|
|
850
|
+
receipt.receiptHash,
|
|
851
|
+
].join("|");
|
|
852
|
+
const sigBytes = identity.sign(Buffer.from(canonicalPayload, "utf8"));
|
|
853
|
+
const signatureHex = Buffer.from(sigBytes).toString("hex");
|
|
854
|
+
const publicKeyHex = Buffer.from(identity.publicKey).toString("hex");
|
|
855
|
+
return {
|
|
856
|
+
version: "0.1.0",
|
|
857
|
+
receipt_id: receipt.id,
|
|
858
|
+
agent_id: identity.did,
|
|
859
|
+
task_id: receipt.runId,
|
|
860
|
+
action_type: actionType,
|
|
861
|
+
tool_name: receipt.toolSlug,
|
|
862
|
+
model_id: null,
|
|
863
|
+
input_hash: receipt.inputHash,
|
|
864
|
+
output_hash: receipt.outputHash,
|
|
865
|
+
started_at: startedAt,
|
|
866
|
+
completed_at: completedAt,
|
|
867
|
+
latency_ms: latencyMs,
|
|
868
|
+
nonce,
|
|
869
|
+
previous_receipt_hash: previousReceiptHash,
|
|
870
|
+
receipt_hash: receipt.receiptHash,
|
|
871
|
+
signature_algorithm: "Ed25519",
|
|
872
|
+
public_key_hex: publicKeyHex,
|
|
873
|
+
signature_hex: signatureHex,
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Verifies a PortableAgenticReceipt's Ed25519 signature over its canonical hash-chained payload.
|
|
878
|
+
*/
|
|
879
|
+
verifyPortableAgenticReceipt(agenticReceipt, workspace) {
|
|
880
|
+
if (!agenticReceipt || !agenticReceipt.signature_hex || !agenticReceipt.public_key_hex) {
|
|
881
|
+
return false;
|
|
882
|
+
}
|
|
883
|
+
const canonicalPayload = [
|
|
884
|
+
agenticReceipt.version || "0.1.0",
|
|
885
|
+
agenticReceipt.receipt_id,
|
|
886
|
+
agenticReceipt.agent_id,
|
|
887
|
+
agenticReceipt.task_id,
|
|
888
|
+
agenticReceipt.action_type,
|
|
889
|
+
agenticReceipt.tool_name || "",
|
|
890
|
+
agenticReceipt.input_hash,
|
|
891
|
+
agenticReceipt.output_hash,
|
|
892
|
+
agenticReceipt.started_at,
|
|
893
|
+
agenticReceipt.completed_at,
|
|
894
|
+
String(agenticReceipt.latency_ms ?? 0),
|
|
895
|
+
agenticReceipt.nonce,
|
|
896
|
+
agenticReceipt.previous_receipt_hash || "genesis",
|
|
897
|
+
agenticReceipt.receipt_hash,
|
|
898
|
+
].join("|");
|
|
899
|
+
try {
|
|
900
|
+
const identity = workspace ? this.getWorkspaceIdentity(workspace) : null;
|
|
901
|
+
if (identity && identity.did === agenticReceipt.agent_id) {
|
|
902
|
+
return identity.verify(Buffer.from(canonicalPayload, "utf8"), Buffer.from(agenticReceipt.signature_hex, "hex"));
|
|
903
|
+
}
|
|
904
|
+
const pubKeyBytes = Buffer.from(agenticReceipt.public_key_hex, "hex");
|
|
905
|
+
const spkiKey = pubKeyBytes.length === 32
|
|
906
|
+
? Buffer.concat([
|
|
907
|
+
Buffer.from("302a300506032b6570032100", "hex"),
|
|
908
|
+
pubKeyBytes,
|
|
909
|
+
])
|
|
910
|
+
: pubKeyBytes;
|
|
911
|
+
const pubKeyObj = crypto.createPublicKey({
|
|
912
|
+
key: spkiKey,
|
|
913
|
+
format: "der",
|
|
914
|
+
type: "spki",
|
|
915
|
+
});
|
|
916
|
+
return crypto.verify(null, Buffer.from(canonicalPayload, "utf8"), pubKeyObj, Buffer.from(agenticReceipt.signature_hex, "hex"));
|
|
917
|
+
}
|
|
918
|
+
catch {
|
|
919
|
+
return false;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
getStatus(workspace) {
|
|
923
|
+
const identity = workspace
|
|
924
|
+
? this.getWorkspaceIdentity(workspace)
|
|
925
|
+
: this.identities.values().next().value || this.defaultIdentity;
|
|
926
|
+
let totalEntries = 0;
|
|
927
|
+
let allVerified = true;
|
|
928
|
+
for (const logger of this.auditLoggers.values()) {
|
|
929
|
+
totalEntries += logger.length;
|
|
930
|
+
if (!logger.verify())
|
|
931
|
+
allVerified = false;
|
|
932
|
+
}
|
|
933
|
+
return {
|
|
934
|
+
sdk: "@microsoft/agent-governance-sdk",
|
|
935
|
+
sdkVersion: SDK_VERSION,
|
|
936
|
+
policyId: POLICY_ID,
|
|
937
|
+
pipeline: "Agent ──► McpSecurityScanner ──► PolicyEngine ──► AgentIdentity (Ed25519 DID) ──► AuditLogger ──► DecisionRecord",
|
|
938
|
+
agentDid: identity.did,
|
|
939
|
+
publicKeyHex: Buffer.from(identity.publicKey).toString("hex"),
|
|
940
|
+
auditEntriesCount: totalEntries,
|
|
941
|
+
auditChainIntegrity: allVerified,
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* Returns a live governance status report for a workspace.
|
|
946
|
+
*/
|
|
947
|
+
getGovernanceStatus(workspace) {
|
|
948
|
+
const identity = this.getWorkspaceIdentity(workspace);
|
|
949
|
+
const auditLogger = this.getWorkspaceAuditLogger(workspace.id);
|
|
950
|
+
const entries = auditLogger.getEntries();
|
|
951
|
+
const activeKill = this.isKillSwitchActive(workspace.id);
|
|
952
|
+
const killSwitchActive = Boolean(activeKill || workspace.policy.killSwitchActive);
|
|
953
|
+
const contextPoisoningReport = this.getContextPoisoningReport(workspace.id, "default");
|
|
954
|
+
return {
|
|
955
|
+
sdk: "@microsoft/agent-governance-sdk",
|
|
956
|
+
sdkVersion: SDK_VERSION,
|
|
957
|
+
policyId: POLICY_ID,
|
|
958
|
+
pipeline: "Agent ──► KillSwitch ──► McpSecurityScanner & ContextPoisoningDetector (ASI-06) ──► ProtocolFacets (SQL/K8s/Infra) ──► RingEnforcer ──► PolicyEngine ──► TrustManager ──► AgentIdentity (Ed25519 DID) ──► AuditLogger ──► DecisionRecord",
|
|
959
|
+
modules: [
|
|
960
|
+
"McpSecurityScanner",
|
|
961
|
+
"ContextPoisoningDetector",
|
|
962
|
+
"ProtocolFacets",
|
|
963
|
+
"RingEnforcer",
|
|
964
|
+
"PolicyEngine",
|
|
965
|
+
"TrustManager",
|
|
966
|
+
"KillSwitch",
|
|
967
|
+
"AgentIdentity",
|
|
968
|
+
"AuditLogger",
|
|
969
|
+
],
|
|
970
|
+
agentIdentity: identity.toJSON(),
|
|
971
|
+
policy: workspace.policy,
|
|
972
|
+
trustScore: this.getWorkspaceTrustScore(workspace.id),
|
|
973
|
+
killSwitch: {
|
|
974
|
+
active: killSwitchActive,
|
|
975
|
+
killedAt: activeKill?.killedAt || null,
|
|
976
|
+
reason: activeKill?.reason || workspace.policy.killSwitchReason || null,
|
|
977
|
+
historyCount: this.killSwitch.getHistory().length,
|
|
978
|
+
},
|
|
979
|
+
contextPoisoning: {
|
|
980
|
+
poisoned: Boolean(contextPoisoningReport.poisoned ??
|
|
981
|
+
(contextPoisoningReport.findings?.length > 0)),
|
|
982
|
+
riskScore: Number(contextPoisoningReport.riskScore ?? 0),
|
|
983
|
+
findingsCount: contextPoisoningReport.findings.length,
|
|
984
|
+
findings: contextPoisoningReport.findings.slice(-10),
|
|
985
|
+
},
|
|
986
|
+
auditChain: {
|
|
987
|
+
entriesCount: entries.length,
|
|
988
|
+
chainVerified: auditLogger.verify(),
|
|
989
|
+
recentEntries: entries.slice(-15).reverse(),
|
|
990
|
+
},
|
|
991
|
+
plainEnglishPolicy: describePolicyInPlainEnglish(workspace.policy),
|
|
992
|
+
explanation: explainGovernanceStatus({
|
|
993
|
+
workspace,
|
|
994
|
+
agentDid: identity.did,
|
|
995
|
+
trustScore: this.getWorkspaceTrustScore(workspace.id),
|
|
996
|
+
killSwitchActive,
|
|
997
|
+
killSwitchReason: activeKill?.reason || workspace.policy.killSwitchReason || null,
|
|
998
|
+
poisoned: Boolean(contextPoisoningReport.poisoned ??
|
|
999
|
+
(contextPoisoningReport.findings?.length > 0)),
|
|
1000
|
+
findingsCount: contextPoisoningReport.findings.length,
|
|
1001
|
+
auditEntriesCount: entries.length,
|
|
1002
|
+
chainVerified: auditLogger.verify(),
|
|
1003
|
+
}),
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
export function explainDecisionRecord(record, policy, languageMode) {
|
|
1008
|
+
const toolSlug = record.action.includes(":")
|
|
1009
|
+
? record.action.split(":").slice(1).join(":")
|
|
1010
|
+
: record.action;
|
|
1011
|
+
const stage = record.action.startsWith("execute.") ? "Execution" : "Plan";
|
|
1012
|
+
const headline = record.decision === "allow"
|
|
1013
|
+
? `${stage} allowed: ${toolSlug} passed all workspace safety checks.`
|
|
1014
|
+
: record.decision === "require_approval"
|
|
1015
|
+
? `Approval needed: ${toolSlug} (${record.riskLevel}) is paused until you approve it.`
|
|
1016
|
+
: `Action blocked: ${toolSlug} was stopped by your workspace safety rules.`;
|
|
1017
|
+
const scanPlain = record.securityScan.safe
|
|
1018
|
+
? "Safety scan: Clean (no prompt injection, data exfiltration, or destructive command patterns detected)."
|
|
1019
|
+
: `Safety warning: Flagged ${record.securityScan.findingsCount} security issue(s) (${record.securityScan.findings.map((f) => f.description).join("; ")}).`;
|
|
1020
|
+
const whyPlain = record.decision === "allow"
|
|
1021
|
+
? `This ${record.riskLevel}-level action is permitted under your active guardrails.`
|
|
1022
|
+
: record.decision === "require_approval"
|
|
1023
|
+
? `Your guardrails require human sign-off before running ${record.riskLevel} actions.`
|
|
1024
|
+
: record.killSwitch?.active
|
|
1025
|
+
? `Your workspace Emergency Pause is active (${record.killSwitch.reason || "all agents paused"}).`
|
|
1026
|
+
: `Stopped because: ${record.reason}.`;
|
|
1027
|
+
const trustPct = record.trustScore
|
|
1028
|
+
? `${Math.round(record.trustScore.overall * 100)}% (${record.trustScore.tier})`
|
|
1029
|
+
: "75% (Provisional)";
|
|
1030
|
+
const nonTechnical = `${headline} ${whyPlain} ${scanPlain} Agent reliability score: ${trustPct}. Recorded in your tamper-proof audit log.`;
|
|
1031
|
+
const technical = `decisionId=${record.id} | decision=${record.decision} (verdict=${record.verdict}) | matchedRule=${record.matchedRule || "none"} | mode=${record.mode} | ring=${record.ringEnforcement?.agentRing ?? "?"}->${record.ringEnforcement?.requiredRing ?? "?"} (${record.ringEnforcement?.ringLabel || "Ring"}) | securityScan=(safe=${record.securityScan.safe}, risk=${record.securityScan.riskLevel}, findings=${record.securityScan.findingsCount}) | asi06Poisoning=(safe=${record.contextPoisoning?.safe ?? true}, risk=${record.contextPoisoning?.riskLevel || "none"}) | trustScore=${record.trustScore?.overall ?? 0.75} (${record.trustScore?.tier || "Provisional"}) | agentDid=${record.agentDid} | auditHash=${record.auditEntry.hash.slice(0, 16)}... (prev=${record.auditEntry.previousHash.slice(0, 16)}..., chainVerified=${record.auditEntry.chainVerified}) | bindingHash=${record.bindingHash.slice(0, 16)}... | ed25519Sig=${record.ed25519Signature.slice(0, 16)}...`;
|
|
1032
|
+
return formatHumanFriendlyExplanation({
|
|
1033
|
+
headline,
|
|
1034
|
+
nonTechnical,
|
|
1035
|
+
technical,
|
|
1036
|
+
policy,
|
|
1037
|
+
languageMode,
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
export function explainGovernanceStatus(params) {
|
|
1041
|
+
const plainPolicy = describePolicyInPlainEnglish(params.workspace.policy);
|
|
1042
|
+
const trustPct = Math.round((params.trustScore.overall || 0.75) * 100);
|
|
1043
|
+
const headline = params.killSwitchActive
|
|
1044
|
+
? `Emergency Pause Active: All agent actions are paused (${params.killSwitchReason || "operator pause"}).`
|
|
1045
|
+
: `Governance Active: 9 safety modules protecting ${params.workspace.name} (${params.trustScore.tier} trust, ${trustPct}% score).`;
|
|
1046
|
+
const nonTechnical = [
|
|
1047
|
+
headline,
|
|
1048
|
+
`Current posture: ${plainPolicy.headline}`,
|
|
1049
|
+
params.poisoned
|
|
1050
|
+
? `Prompt-injection scanner: ${params.findingsCount} suspicious pattern(s) flagged and isolated.`
|
|
1051
|
+
: "Prompt-injection & memory scanner: Clean (0 threats detected).",
|
|
1052
|
+
`Tamper-proof audit log: ${params.auditEntriesCount} action${params.auditEntriesCount === 1 ? "" : "s"} verified (${params.chainVerified ? "intact" : "check required"}).`,
|
|
1053
|
+
].join(" ");
|
|
1054
|
+
const technical = `sdk=@microsoft/agent-governance-sdk@${SDK_VERSION} | policyId=${POLICY_ID} | agentDid=${params.agentDid} | killSwitchActive=${params.killSwitchActive} | trustScore=${params.trustScore.overall} (tier=${params.trustScore.tier}, reliability=${params.trustScore.reliability}, consistency=${params.trustScore.consistency}) | asi06Poisoned=${params.poisoned} (findings=${params.findingsCount}) | auditChain=(entries=${params.auditEntriesCount}, verified=${params.chainVerified}) | ${plainPolicy.technical}`;
|
|
1055
|
+
return formatHumanFriendlyExplanation({
|
|
1056
|
+
headline,
|
|
1057
|
+
nonTechnical,
|
|
1058
|
+
technical,
|
|
1059
|
+
policy: params.workspace.policy,
|
|
1060
|
+
languageMode: params.languageMode,
|
|
1061
|
+
});
|
|
1062
|
+
}
|