@muxoai/sdk 1.3.0 → 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 +2 -2
- package/.claude-plugin/marketplace.json +19 -4
- package/.codex/config.toml +9 -2
- package/ARCHITECTURE.md +16 -6
- package/README.md +69 -24
- package/dist/clerk.d.ts +35 -0
- package/dist/clerk.js +201 -0
- package/dist/cli.js +1589 -103
- package/dist/composio-catalog.d.ts +234 -0
- package/dist/composio-catalog.js +1894 -0
- package/dist/config.d.ts +16 -0
- package/dist/config.js +27 -1
- package/dist/durable-workflow.d.ts +45 -0
- package/dist/durable-workflow.js +90 -0
- package/dist/engine.d.ts +315 -6
- package/dist/engine.js +1778 -94
- package/dist/mcp-schema.d.ts +286 -0
- package/dist/mcp-schema.js +2559 -0
- package/dist/mcp.d.ts +2 -25
- package/dist/mcp.js +949 -507
- package/dist/memory-graph.d.ts +21 -0
- package/dist/memory-graph.js +173 -0
- package/dist/nl.js +214 -35
- 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 +4 -0
- package/dist/providers/composio-instant.js +890 -535
- package/dist/providers/composio-sdk.d.ts +126 -5
- package/dist/providers/composio-sdk.js +902 -79
- package/dist/providers/microsoft-governance.d.ts +89 -3
- package/dist/providers/microsoft-governance.js +673 -26
- package/dist/providers/stripe-billing.d.ts +42 -0
- package/dist/providers/stripe-billing.js +244 -5
- package/dist/providers/stripe-projects.d.ts +459 -4
- package/dist/providers/stripe-projects.js +2674 -122
- package/dist/providers/supermemory.d.ts +102 -6
- package/dist/providers/supermemory.js +609 -55
- package/dist/provisioning-managed.d.ts +50 -0
- package/dist/provisioning-managed.js +155 -0
- package/dist/sdk.d.ts +705 -7
- package/dist/sdk.js +488 -10
- package/dist/server.js +2646 -348
- package/dist/store.d.ts +138 -6
- package/dist/store.js +932 -58
- package/dist/types.d.ts +312 -4
- package/dist/ui/console-html.d.ts +62 -0
- package/dist/ui/console-html.js +3742 -2228
- 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 +5 -4
- package/plugins/muxo/.app.json +1 -1
- package/plugins/muxo/.claude-plugin/plugin.json +3 -3
- package/plugins/muxo/.codex-plugin/plugin.json +19 -10
- package/plugins/muxo/.cursor-plugin/plugin.json +30 -0
- package/plugins/muxo/.mcp.json +1 -33
- package/plugins/muxo/assets/logo.png +0 -0
- package/plugins/muxo/hooks/session-start.sh +16 -4
- package/plugins/muxo/hooks/user-prompt-submit.sh +14 -1
- package/plugins/muxo/mcp.json +0 -10
- package/plugins/muxo/plugin.json +65 -8
- package/plugins/muxo/skills/muxo/SKILL.md +142 -14
- package/plugins/muxo/skills/muxo/agents/openai.yaml +12 -0
- package/plugins/muxo/skills/muxo/references/cli.md +9 -3
- package/plugins/muxo/skills/muxo/references/mcp.md +22 -10
- package/plugins/muxo/skills/muxo-activity-summary/SKILL.md +81 -0
- package/plugins/muxo/skills/setup/SKILL.md +17 -3
- package/plugins/muxo/skills/setup/agents/openai.yaml +12 -0
- package/src/catalogues/stripe-projects-catalog.json +140 -0
|
@@ -1,7 +1,29 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
|
-
import { AgentIdentity, AuditLogger, McpSecurityScanner, PolicyEngine, } from "../../vendor/microsoft-governance/dist/index.js";
|
|
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";
|
|
3
4
|
const SDK_VERSION = "5.0.0";
|
|
4
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
|
+
}
|
|
5
27
|
// PKCS#8 DER prefix for a 32-byte Ed25519 private key seed (OID 1.3.101.112)
|
|
6
28
|
const ED25519_PKCS8_PREFIX = Buffer.from([
|
|
7
29
|
0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70,
|
|
@@ -39,12 +61,41 @@ function deriveDeterministicIdentity(seedMaterial, agentId, capabilities, opts)
|
|
|
39
61
|
}
|
|
40
62
|
export class MicrosoftGovernanceAdapter {
|
|
41
63
|
scanner;
|
|
64
|
+
contextPoisoningDetector;
|
|
65
|
+
facetRegistry;
|
|
66
|
+
trustManager;
|
|
67
|
+
killSwitch;
|
|
68
|
+
activeKillSwitches = new Map();
|
|
42
69
|
seedMaterial;
|
|
43
70
|
auditLoggers = new Map();
|
|
44
71
|
identities = new Map();
|
|
45
72
|
defaultIdentity;
|
|
46
73
|
constructor(seed) {
|
|
47
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
|
+
});
|
|
48
99
|
this.seedMaterial = Buffer.isBuffer(seed)
|
|
49
100
|
? seed
|
|
50
101
|
: Buffer.from(String(seed ||
|
|
@@ -94,12 +145,273 @@ export class MicrosoftGovernanceAdapter {
|
|
|
94
145
|
}
|
|
95
146
|
return logger;
|
|
96
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
|
+
}
|
|
97
399
|
/**
|
|
98
400
|
* Compiles the workspace's GovernancePolicy into a declarative Microsoft PolicyEngine instance.
|
|
99
401
|
*/
|
|
100
402
|
createPolicyEngine(policy) {
|
|
101
403
|
const engine = new PolicyEngine();
|
|
102
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
|
+
}
|
|
103
415
|
for (const blockedSlug of policy.blockedToolSlugs || []) {
|
|
104
416
|
const normalized = blockedSlug.trim();
|
|
105
417
|
if (!normalized)
|
|
@@ -113,6 +425,44 @@ export class MicrosoftGovernanceAdapter {
|
|
|
113
425
|
},
|
|
114
426
|
});
|
|
115
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
|
+
}
|
|
116
466
|
if (policy.mode === "enforce") {
|
|
117
467
|
for (const risk of policy.requireApprovalFor || []) {
|
|
118
468
|
rules.push({
|
|
@@ -147,24 +497,38 @@ export class MicrosoftGovernanceAdapter {
|
|
|
147
497
|
}
|
|
148
498
|
/**
|
|
149
499
|
* Runs the full Microsoft Agent Governance pipeline:
|
|
150
|
-
* Agent ──► McpSecurityScanner ──► PolicyEngine ──► AgentIdentity (Ed25519 DID) ──► AuditLogger ──► DecisionRecord
|
|
500
|
+
* Agent ──► KillSwitch ──► McpSecurityScanner & ContextPoisoningDetector (ASI-06) ──► ProtocolFacets (SQL/K8s/Infra) ──► RingEnforcer ──► PolicyEngine ──► TrustManager ──► AgentIdentity (Ed25519 DID) ──► AuditLogger ──► DecisionRecord
|
|
151
501
|
*/
|
|
152
502
|
evaluateAction(params) {
|
|
153
503
|
const { workspace, actorId, tool, input, reason, stage } = params;
|
|
154
504
|
const ownerApproved = Boolean(params.ownerApproved);
|
|
155
505
|
const decidedAt = new Date().toISOString();
|
|
156
|
-
// 1. McpSecurityScanner: inspect tool definition + input arguments
|
|
506
|
+
// 1. McpSecurityScanner + ContextPoisoningDetector (OWASP ASI-06): inspect tool definition + input arguments
|
|
157
507
|
const serializedInput = JSON.stringify(input || {});
|
|
158
508
|
const scanResult = this.scanner.scan({
|
|
159
509
|
name: tool.slug,
|
|
160
510
|
description: `${tool.description}\nReason: ${reason}\nArgs: ${serializedInput}`,
|
|
161
511
|
parameters: tool.inputSchema,
|
|
162
512
|
});
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
+
];
|
|
168
532
|
// Additional argument-level prompt-injection / secret-exfiltration heuristics
|
|
169
533
|
const suspiciousPatterns = [
|
|
170
534
|
{
|
|
@@ -190,8 +554,29 @@ export class MicrosoftGovernanceAdapter {
|
|
|
190
554
|
}
|
|
191
555
|
}
|
|
192
556
|
const highSeverityFinding = extraFindings.some((f) => f.severity === "high" || f.severity === "critical");
|
|
193
|
-
// 2.
|
|
557
|
+
// 2. Protocol Facets Extraction (SQL, Kubernetes, Infrastructure)
|
|
558
|
+
const protocolFacets = this.extractActionFacets(tool, input);
|
|
559
|
+
// 3. PolicyEngine + KillSwitch + Parameter Guardrail evaluation
|
|
194
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;
|
|
195
580
|
const policyEngine = this.createPolicyEngine(policy);
|
|
196
581
|
const identity = this.getWorkspaceIdentity(workspace);
|
|
197
582
|
const policyDecision = policyEngine.evaluatePolicy(identity.did, {
|
|
@@ -199,20 +584,55 @@ export class MicrosoftGovernanceAdapter {
|
|
|
199
584
|
risk_level: tool.riskLevel,
|
|
200
585
|
lane: tool.lane,
|
|
201
586
|
stage,
|
|
202
|
-
owner_approved:
|
|
587
|
+
owner_approved: effectiveApproved,
|
|
203
588
|
workspace_active: true,
|
|
589
|
+
...(protocolFacets.sql ? { sql: protocolFacets.sql } : {}),
|
|
590
|
+
...(protocolFacets.k8s ? { k8s: protocolFacets.k8s } : {}),
|
|
591
|
+
...(protocolFacets.infra ? { infra: protocolFacets.infra } : {}),
|
|
204
592
|
});
|
|
205
593
|
let finalDecision = policyDecision.action === "deny"
|
|
206
594
|
? "deny"
|
|
207
595
|
: policyDecision.action === "require_approval"
|
|
208
596
|
? "require_approval"
|
|
209
597
|
: "allow";
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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") {
|
|
213
626
|
finalDecision = "deny";
|
|
627
|
+
matchedRule = "mcp-security-scanner-high-severity";
|
|
628
|
+
this.trustManager.recordFailure(workspace.id, 0.1);
|
|
214
629
|
}
|
|
215
|
-
|
|
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
|
|
216
636
|
const auditLogger = this.getWorkspaceAuditLogger(workspace.id);
|
|
217
637
|
const auditDecision = finalDecision === "deny" ? "deny" : "allow";
|
|
218
638
|
const auditEntry = auditLogger.log({
|
|
@@ -222,11 +642,13 @@ export class MicrosoftGovernanceAdapter {
|
|
|
222
642
|
skillAuditMetadata: {
|
|
223
643
|
skillName: tool.slug,
|
|
224
644
|
skillOrigin: tool.lane,
|
|
225
|
-
provenanceSourceTrust: "
|
|
645
|
+
provenanceSourceTrust: trustScore.tier === "Verified" || trustScore.tier === "Trusted"
|
|
646
|
+
? "trusted"
|
|
647
|
+
: undefined,
|
|
226
648
|
},
|
|
227
649
|
});
|
|
228
650
|
const chainVerified = auditLogger.verify();
|
|
229
|
-
//
|
|
651
|
+
// 6. AgentIdentity (Ed25519 DID): cryptographically sign the decision record
|
|
230
652
|
const bindingHash = crypto
|
|
231
653
|
.createHash("sha256")
|
|
232
654
|
.update(JSON.stringify({
|
|
@@ -259,13 +681,19 @@ export class MicrosoftGovernanceAdapter {
|
|
|
259
681
|
const publicKeyHex = Buffer.from(identity.publicKey).toString("hex");
|
|
260
682
|
const verdict = finalDecision === "require_approval" ? "escalate" : finalDecision;
|
|
261
683
|
const decisionReason = finalDecision === "deny"
|
|
262
|
-
?
|
|
263
|
-
? `Blocked by
|
|
264
|
-
:
|
|
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"}`
|
|
265
691
|
: finalDecision === "require_approval"
|
|
266
|
-
?
|
|
267
|
-
|
|
268
|
-
|
|
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 = {
|
|
269
697
|
id: `dec_${crypto.randomBytes(8).toString("hex")}`,
|
|
270
698
|
sdk: "@microsoft/agent-governance-sdk",
|
|
271
699
|
sdkVersion: SDK_VERSION,
|
|
@@ -280,12 +708,12 @@ export class MicrosoftGovernanceAdapter {
|
|
|
280
708
|
decision: finalDecision,
|
|
281
709
|
verdict,
|
|
282
710
|
reason: decisionReason,
|
|
283
|
-
matchedRule
|
|
711
|
+
matchedRule,
|
|
284
712
|
securityScan: {
|
|
285
|
-
safe: scanResult.safe && !highSeverityFinding,
|
|
713
|
+
safe: scanResult.safe && !highSeverityFinding && contextPoisoning.safe,
|
|
286
714
|
riskLevel: highSeverityFinding
|
|
287
715
|
? "high"
|
|
288
|
-
: scanResult.risk_score > 50
|
|
716
|
+
: scanResult.risk_score > 50 || contextPoisoning.findingsCount > 0
|
|
289
717
|
? "medium"
|
|
290
718
|
: "low",
|
|
291
719
|
findingsCount: extraFindings.length,
|
|
@@ -300,11 +728,27 @@ export class MicrosoftGovernanceAdapter {
|
|
|
300
728
|
previousHash: auditEntry.previousHash,
|
|
301
729
|
chainVerified,
|
|
302
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
|
+
},
|
|
303
745
|
ed25519Signature,
|
|
304
746
|
signature: ed25519Signature,
|
|
305
747
|
bindingHash,
|
|
306
748
|
decidedAt,
|
|
307
749
|
};
|
|
750
|
+
record.explanation = explainDecisionRecord(record, policy);
|
|
751
|
+
return record;
|
|
308
752
|
}
|
|
309
753
|
evaluatePlan(params) {
|
|
310
754
|
return this.evaluateAction({
|
|
@@ -369,6 +813,112 @@ export class MicrosoftGovernanceAdapter {
|
|
|
369
813
|
const auditChainVerified = auditLogger.verify();
|
|
370
814
|
return ed25519Verified && auditChainVerified;
|
|
371
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
|
+
}
|
|
372
922
|
getStatus(workspace) {
|
|
373
923
|
const identity = workspace
|
|
374
924
|
? this.getWorkspaceIdentity(workspace)
|
|
@@ -398,18 +948,115 @@ export class MicrosoftGovernanceAdapter {
|
|
|
398
948
|
const identity = this.getWorkspaceIdentity(workspace);
|
|
399
949
|
const auditLogger = this.getWorkspaceAuditLogger(workspace.id);
|
|
400
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");
|
|
401
954
|
return {
|
|
402
955
|
sdk: "@microsoft/agent-governance-sdk",
|
|
403
956
|
sdkVersion: SDK_VERSION,
|
|
404
957
|
policyId: POLICY_ID,
|
|
405
|
-
pipeline: "Agent ──► McpSecurityScanner ──► PolicyEngine ──► AgentIdentity (Ed25519 DID) ──► AuditLogger ──► DecisionRecord",
|
|
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
|
+
],
|
|
406
970
|
agentIdentity: identity.toJSON(),
|
|
407
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
|
+
},
|
|
408
986
|
auditChain: {
|
|
409
987
|
entriesCount: entries.length,
|
|
410
988
|
chainVerified: auditLogger.verify(),
|
|
411
989
|
recentEntries: entries.slice(-15).reverse(),
|
|
412
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
|
+
}),
|
|
413
1004
|
};
|
|
414
1005
|
}
|
|
415
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
|
+
}
|