@muxoai/platform 1.3.1 → 1.3.3
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/README.md +10 -2
- package/dist/clerk.d.ts +35 -0
- package/dist/clerk.js +201 -0
- package/dist/cli.js +456 -39
- package/dist/engine.d.ts +21 -1
- package/dist/engine.js +245 -23
- package/dist/mcp.js +2 -1
- package/dist/policy.d.ts +9 -0
- package/dist/policy.js +73 -0
- package/dist/providers/composio-instant.d.ts +3 -0
- package/dist/providers/composio-instant.js +629 -530
- package/dist/providers/microsoft-governance.d.ts +61 -2
- package/dist/providers/microsoft-governance.js +458 -24
- package/dist/providers/stripe-projects.d.ts +4 -1
- package/dist/providers/stripe-projects.js +64 -10
- package/dist/providers/supermemory.d.ts +26 -5
- package/dist/providers/supermemory.js +136 -26
- package/dist/sdk.d.ts +170 -2
- package/dist/sdk.js +115 -4
- package/dist/server.js +178 -17
- package/dist/store.d.ts +25 -1
- package/dist/store.js +236 -3
- package/dist/types.d.ts +86 -0
- package/dist/ui/console-html.d.ts +23 -0
- package/dist/ui/console-html.js +2440 -2226
- 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 +2 -2
|
@@ -1,7 +1,28 @@
|
|
|
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
3
|
const SDK_VERSION = "5.0.0";
|
|
4
4
|
const POLICY_ID = "muxo-microsoft-agent-governance-v5";
|
|
5
|
+
export const RING_LABELS = {
|
|
6
|
+
[ExecutionRing.Ring0]: "Ring 0 (Kernel / Destructive & Root Boundary)",
|
|
7
|
+
[ExecutionRing.Ring1]: "Ring 1 (Supervisor / Provisioning, Financial & Admin)",
|
|
8
|
+
[ExecutionRing.Ring2]: "Ring 2 (Standard Agent / Governed Write)",
|
|
9
|
+
[ExecutionRing.Ring3]: "Ring 3 (Unprivileged Sandbox / Read-Only)",
|
|
10
|
+
};
|
|
11
|
+
function requiredRingForRisk(riskLevel) {
|
|
12
|
+
switch (riskLevel) {
|
|
13
|
+
case "destructive":
|
|
14
|
+
return ExecutionRing.Ring0;
|
|
15
|
+
case "admin":
|
|
16
|
+
case "financial":
|
|
17
|
+
case "provisioning":
|
|
18
|
+
return ExecutionRing.Ring1;
|
|
19
|
+
case "write":
|
|
20
|
+
return ExecutionRing.Ring2;
|
|
21
|
+
case "read":
|
|
22
|
+
default:
|
|
23
|
+
return ExecutionRing.Ring3;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
5
26
|
// PKCS#8 DER prefix for a 32-byte Ed25519 private key seed (OID 1.3.101.112)
|
|
6
27
|
const ED25519_PKCS8_PREFIX = Buffer.from([
|
|
7
28
|
0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70,
|
|
@@ -39,12 +60,41 @@ function deriveDeterministicIdentity(seedMaterial, agentId, capabilities, opts)
|
|
|
39
60
|
}
|
|
40
61
|
export class MicrosoftGovernanceAdapter {
|
|
41
62
|
scanner;
|
|
63
|
+
contextPoisoningDetector;
|
|
64
|
+
facetRegistry;
|
|
65
|
+
trustManager;
|
|
66
|
+
killSwitch;
|
|
67
|
+
activeKillSwitches = new Map();
|
|
42
68
|
seedMaterial;
|
|
43
69
|
auditLoggers = new Map();
|
|
44
70
|
identities = new Map();
|
|
45
71
|
defaultIdentity;
|
|
46
72
|
constructor(seed) {
|
|
47
73
|
this.scanner = new McpSecurityScanner();
|
|
74
|
+
this.contextPoisoningDetector = new ContextPoisoningDetector({
|
|
75
|
+
maxContextSizeBytes: 100_000,
|
|
76
|
+
maxEntriesPerSession: 250,
|
|
77
|
+
entropyThreshold: 5.5,
|
|
78
|
+
similarityThreshold: 0.92,
|
|
79
|
+
enableIsolation: true,
|
|
80
|
+
});
|
|
81
|
+
this.facetRegistry = new FacetRegistry();
|
|
82
|
+
this.facetRegistry.register("sql", extractSqlFacets);
|
|
83
|
+
this.facetRegistry.register("k8s", extractK8sFacets);
|
|
84
|
+
this.facetRegistry.register("infra", (sub) => ({
|
|
85
|
+
provider: String(sub.provider ?? "").toLowerCase(),
|
|
86
|
+
service: String(sub.service ?? "").toLowerCase(),
|
|
87
|
+
action: String(sub.action ?? "read").toLowerCase(),
|
|
88
|
+
destructive: Boolean(sub.destructive),
|
|
89
|
+
}));
|
|
90
|
+
this.trustManager = new TrustManager({
|
|
91
|
+
initialScore: 0.75,
|
|
92
|
+
decayFactor: 0.98,
|
|
93
|
+
});
|
|
94
|
+
this.killSwitch = new KillSwitch({
|
|
95
|
+
enabled: true,
|
|
96
|
+
defaultSubstituteAgentId: "muxo-safe-mode-guardian",
|
|
97
|
+
});
|
|
48
98
|
this.seedMaterial = Buffer.isBuffer(seed)
|
|
49
99
|
? seed
|
|
50
100
|
: Buffer.from(String(seed ||
|
|
@@ -94,12 +144,273 @@ export class MicrosoftGovernanceAdapter {
|
|
|
94
144
|
}
|
|
95
145
|
return logger;
|
|
96
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Scans memory or prompt context for OWASP ASI-06 Context Poisoning (prompt injection,
|
|
149
|
+
* roleplay overrides, delimiter injection, high-entropy exfiltration payloads, and session isolation).
|
|
150
|
+
*/
|
|
151
|
+
scanMemoryContent(params) {
|
|
152
|
+
const safeAgentKey = params.workspaceId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
153
|
+
const safeSessionKey = (params.sessionId || "default").replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
154
|
+
const entryId = params.entryId || `mem_${crypto.randomBytes(6).toString("hex")}`;
|
|
155
|
+
const entry = {
|
|
156
|
+
id: entryId,
|
|
157
|
+
entryId,
|
|
158
|
+
agentId: safeAgentKey,
|
|
159
|
+
sessionId: safeSessionKey,
|
|
160
|
+
role: params.role || "user",
|
|
161
|
+
content: String(params.content || ""),
|
|
162
|
+
timestamp: new Date().toISOString(),
|
|
163
|
+
};
|
|
164
|
+
const findings = this.contextPoisoningDetector.scanEntry(entry);
|
|
165
|
+
// Augment with credential/secret exfiltration detection in memory content
|
|
166
|
+
if (/(cat|curl|wget)\s+.*(\/etc\/passwd|\.env|id_rsa|AWS_SECRET_ACCESS_KEY)/i.test(entry.content) ||
|
|
167
|
+
/send\s+(all\s+)?(api\s+keys|secrets|tokens|credentials)\s+to\s+https?:\/\//i.test(entry.content)) {
|
|
168
|
+
findings.push({
|
|
169
|
+
entryId: entry.id,
|
|
170
|
+
patternId: "MEMORY_EXFILTRATION_DIRECTIVE",
|
|
171
|
+
patternName: "Persistent Memory Credential Exfiltration Directive",
|
|
172
|
+
severity: "critical",
|
|
173
|
+
description: "Memory payload attempts to persist credential or file exfiltration instructions across agent runs",
|
|
174
|
+
matchedText: entry.content.slice(0, 100),
|
|
175
|
+
timestamp: Date.now(),
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
const hasCritical = findings.some((f) => f.severity === "critical");
|
|
179
|
+
const hasHigh = findings.some((f) => f.severity === "high");
|
|
180
|
+
const hasMedium = findings.some((f) => f.severity === "medium");
|
|
181
|
+
const riskLevel = hasCritical
|
|
182
|
+
? "critical"
|
|
183
|
+
: hasHigh
|
|
184
|
+
? "high"
|
|
185
|
+
: hasMedium
|
|
186
|
+
? "medium"
|
|
187
|
+
: findings.length > 0
|
|
188
|
+
? "low"
|
|
189
|
+
: "none";
|
|
190
|
+
const safe = !hasCritical && !hasHigh;
|
|
191
|
+
if (params.recordInDetector !== false && safe) {
|
|
192
|
+
try {
|
|
193
|
+
this.contextPoisoningDetector.addEntry(entry);
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
// Ignore session capacity overflow in long-running processes
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const integrity = this.contextPoisoningDetector.verifyIntegrity(safeAgentKey, safeSessionKey);
|
|
200
|
+
const integrityValid = typeof integrity === "boolean" ? integrity : Boolean(integrity?.valid ?? true);
|
|
201
|
+
return {
|
|
202
|
+
safe,
|
|
203
|
+
riskLevel,
|
|
204
|
+
findingsCount: findings.length,
|
|
205
|
+
findings: findings.map((f) => ({
|
|
206
|
+
patternId: String(f.patternId || ""),
|
|
207
|
+
patternName: String(f.patternName || ""),
|
|
208
|
+
severity: String(f.severity || ""),
|
|
209
|
+
description: String(f.description || ""),
|
|
210
|
+
})),
|
|
211
|
+
integrityValid,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
getContextPoisoningReport(workspaceId, sessionId = "default") {
|
|
215
|
+
const safeAgentKey = workspaceId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
216
|
+
const safeSessionKey = sessionId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
217
|
+
return this.contextPoisoningDetector.scan(safeAgentKey, safeSessionKey);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Extracts structured SQL, Kubernetes, and Infrastructure protocol facets from tool input.
|
|
221
|
+
*/
|
|
222
|
+
extractActionFacets(tool, input) {
|
|
223
|
+
const rawContext = {};
|
|
224
|
+
// Detect SQL payload in input
|
|
225
|
+
const candidateSql = typeof input.sql === "string"
|
|
226
|
+
? input.sql
|
|
227
|
+
: typeof input.statement === "string"
|
|
228
|
+
? input.statement
|
|
229
|
+
: typeof input.query === "string" &&
|
|
230
|
+
(tool.slug.includes("sql") ||
|
|
231
|
+
tool.slug.includes("postgres") ||
|
|
232
|
+
tool.slug.includes("neon") ||
|
|
233
|
+
tool.slug.includes("turso") ||
|
|
234
|
+
tool.slug.includes("supabase") ||
|
|
235
|
+
tool.slug.includes("db") ||
|
|
236
|
+
/^\s*(SELECT|INSERT|UPDATE|DELETE|DROP|TRUNCATE|ALTER|CREATE|WITH)\b/i.test(input.query))
|
|
237
|
+
? input.query
|
|
238
|
+
: null;
|
|
239
|
+
if (candidateSql) {
|
|
240
|
+
rawContext.sql = { query: candidateSql };
|
|
241
|
+
}
|
|
242
|
+
// Detect Kubernetes payload in input
|
|
243
|
+
const candidateK8sPath = typeof input.k8s_path === "string"
|
|
244
|
+
? input.k8s_path
|
|
245
|
+
: typeof input.path === "string" &&
|
|
246
|
+
(input.path.startsWith("/api/v1/") ||
|
|
247
|
+
input.path.startsWith("/apis/"))
|
|
248
|
+
? input.path
|
|
249
|
+
: null;
|
|
250
|
+
if (candidateK8sPath) {
|
|
251
|
+
rawContext.k8s = {
|
|
252
|
+
method: typeof input.method === "string" ? input.method : "GET",
|
|
253
|
+
path: candidateK8sPath,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
else if (input.k8s &&
|
|
257
|
+
typeof input.k8s === "object" &&
|
|
258
|
+
!Array.isArray(input.k8s)) {
|
|
259
|
+
rawContext.k8s = input.k8s;
|
|
260
|
+
}
|
|
261
|
+
// Detect Infrastructure / Resource provisioning facets
|
|
262
|
+
if (tool.lane === "stripe_projects" ||
|
|
263
|
+
tool.slug.startsWith("resource.") ||
|
|
264
|
+
tool.riskLevel === "provisioning" ||
|
|
265
|
+
tool.riskLevel === "destructive") {
|
|
266
|
+
rawContext.infra = {
|
|
267
|
+
provider: tool.providerSlug,
|
|
268
|
+
service: tool.upstreamSlug,
|
|
269
|
+
action: tool.riskLevel,
|
|
270
|
+
destructive: tool.riskLevel === "destructive" ||
|
|
271
|
+
tool.slug.includes("delete") ||
|
|
272
|
+
tool.slug.includes("destroy"),
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
const extracted = extractProtocolFacets(rawContext, this.facetRegistry);
|
|
276
|
+
const result = {};
|
|
277
|
+
if (extracted.sql) {
|
|
278
|
+
const sqlVerb = String(extracted.sql.verb || "UNKNOWN").toUpperCase();
|
|
279
|
+
const isDestructiveSql = sqlVerb === "DROP" ||
|
|
280
|
+
sqlVerb === "TRUNCATE" ||
|
|
281
|
+
(sqlVerb === "UNKNOWN" && Boolean(candidateSql?.includes(";"))) ||
|
|
282
|
+
(sqlVerb === "DELETE" &&
|
|
283
|
+
Boolean(candidateSql) &&
|
|
284
|
+
!/\bWHERE\b/i.test(candidateSql || ""));
|
|
285
|
+
result.sql = {
|
|
286
|
+
verb: sqlVerb,
|
|
287
|
+
target: String(extracted.sql.target || ""),
|
|
288
|
+
tables: String(extracted.sql.tables || ""),
|
|
289
|
+
functions: String(extracted.sql.functions || ""),
|
|
290
|
+
destructive: isDestructiveSql,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
if (extracted.k8s) {
|
|
294
|
+
result.k8s = {
|
|
295
|
+
verb: String(extracted.k8s.verb || ""),
|
|
296
|
+
resource: String(extracted.k8s.resource || ""),
|
|
297
|
+
namespace: String(extracted.k8s.namespace || ""),
|
|
298
|
+
name: String(extracted.k8s.name || ""),
|
|
299
|
+
subresource: String(extracted.k8s.subresource || ""),
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
if (extracted.infra) {
|
|
303
|
+
result.infra = {
|
|
304
|
+
provider: String(extracted.infra.provider || tool.providerSlug),
|
|
305
|
+
service: String(extracted.infra.service || tool.upstreamSlug),
|
|
306
|
+
action: String(extracted.infra.action || tool.riskLevel),
|
|
307
|
+
destructive: Boolean(extracted.infra.destructive),
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
return result;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Evaluates hardware-style privilege rings (Ring 0..Ring 3) via Microsoft RingEnforcer.
|
|
314
|
+
*/
|
|
315
|
+
evaluateRingEnforcement(workspace, tool, ownerApproved) {
|
|
316
|
+
const requiredRing = requiredRingForRisk(tool.riskLevel);
|
|
317
|
+
const policy = workspace.policy;
|
|
318
|
+
const requiresApproval = policy.mode === "enforce" &&
|
|
319
|
+
(policy.requireApprovalFor || []).includes(tool.riskLevel);
|
|
320
|
+
const agentRing = ownerApproved
|
|
321
|
+
? ExecutionRing.Ring0
|
|
322
|
+
: policy.executionRing !== undefined
|
|
323
|
+
? policy.executionRing
|
|
324
|
+
: requiresApproval
|
|
325
|
+
? ExecutionRing.Ring2
|
|
326
|
+
: requiredRing;
|
|
327
|
+
const enforcer = new RingEnforcer({
|
|
328
|
+
agentRing,
|
|
329
|
+
defaultRing: ExecutionRing.Ring3,
|
|
330
|
+
actionRings: {
|
|
331
|
+
"read:*": ExecutionRing.Ring3,
|
|
332
|
+
"write:*": ExecutionRing.Ring2,
|
|
333
|
+
"admin:*": ExecutionRing.Ring1,
|
|
334
|
+
"financial:*": ExecutionRing.Ring1,
|
|
335
|
+
"provisioning:*": ExecutionRing.Ring1,
|
|
336
|
+
"destructive:*": ExecutionRing.Ring0,
|
|
337
|
+
},
|
|
338
|
+
});
|
|
339
|
+
const actionKey = `${tool.riskLevel}:${tool.slug}`;
|
|
340
|
+
const allowed = enforcer.canExecute(actionKey);
|
|
341
|
+
return {
|
|
342
|
+
agentRing,
|
|
343
|
+
requiredRing,
|
|
344
|
+
allowed,
|
|
345
|
+
ringLabel: RING_LABELS[requiredRing] || `Ring ${requiredRing}`,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Records an agent execution outcome in the Bayesian TrustManager and returns the updated TrustScore.
|
|
350
|
+
*/
|
|
351
|
+
recordAgentOutcome(workspaceId, succeeded, weight = 0.05) {
|
|
352
|
+
if (succeeded) {
|
|
353
|
+
this.trustManager.recordSuccess(workspaceId, weight);
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
this.trustManager.recordFailure(workspaceId, weight * 2);
|
|
357
|
+
}
|
|
358
|
+
return this.trustManager.getTrustScore(workspaceId);
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Returns the current Bayesian TrustScore for a workspace agent boundary.
|
|
362
|
+
*/
|
|
363
|
+
getWorkspaceTrustScore(workspaceId) {
|
|
364
|
+
const score = this.trustManager.getTrustScore(workspaceId);
|
|
365
|
+
return {
|
|
366
|
+
overall: Number(score.overall.toFixed(4)),
|
|
367
|
+
tier: score.tier,
|
|
368
|
+
reliability: Number(score.dimensions.reliability.toFixed(4)),
|
|
369
|
+
consistency: Number(score.dimensions.consistency.toFixed(4)),
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Activates the emergency KillSwitch for a workspace agent boundary.
|
|
374
|
+
*/
|
|
375
|
+
async activateKillSwitch(workspaceId, reason, action = "quarantine") {
|
|
376
|
+
const result = await this.killSwitch.kill(workspaceId, {
|
|
377
|
+
action,
|
|
378
|
+
reason: reason || "Emergency kill-switch activated by workspace operator",
|
|
379
|
+
});
|
|
380
|
+
this.activeKillSwitches.set(workspaceId, result);
|
|
381
|
+
this.trustManager.recordFailure(workspaceId, 0.25);
|
|
382
|
+
return result;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Resumes a workspace agent boundary after a KillSwitch quarantine/suspension.
|
|
386
|
+
*/
|
|
387
|
+
resumeKillSwitch(workspaceId) {
|
|
388
|
+
const existed = this.activeKillSwitches.delete(workspaceId);
|
|
389
|
+
return {
|
|
390
|
+
resumed: existed,
|
|
391
|
+
workspaceId,
|
|
392
|
+
resumedAt: new Date().toISOString(),
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
isKillSwitchActive(workspaceId) {
|
|
396
|
+
return this.activeKillSwitches.get(workspaceId) || null;
|
|
397
|
+
}
|
|
97
398
|
/**
|
|
98
399
|
* Compiles the workspace's GovernancePolicy into a declarative Microsoft PolicyEngine instance.
|
|
99
400
|
*/
|
|
100
401
|
createPolicyEngine(policy) {
|
|
101
402
|
const engine = new PolicyEngine();
|
|
102
403
|
const rules = [];
|
|
404
|
+
if (policy.killSwitchActive) {
|
|
405
|
+
rules.push({
|
|
406
|
+
name: "deny-kill-switch-active",
|
|
407
|
+
ruleAction: "deny",
|
|
408
|
+
priority: 200,
|
|
409
|
+
condition: {
|
|
410
|
+
workspace_active: true,
|
|
411
|
+
},
|
|
412
|
+
});
|
|
413
|
+
}
|
|
103
414
|
for (const blockedSlug of policy.blockedToolSlugs || []) {
|
|
104
415
|
const normalized = blockedSlug.trim();
|
|
105
416
|
if (!normalized)
|
|
@@ -113,6 +424,44 @@ export class MicrosoftGovernanceAdapter {
|
|
|
113
424
|
},
|
|
114
425
|
});
|
|
115
426
|
}
|
|
427
|
+
if (policy.mode === "enforce" && policy.blockDestructiveSql !== false) {
|
|
428
|
+
rules.push({
|
|
429
|
+
name: "deny-multi-statement-sql-injection",
|
|
430
|
+
ruleAction: "deny",
|
|
431
|
+
priority: 96,
|
|
432
|
+
condition: {
|
|
433
|
+
"sql.verb": "UNKNOWN",
|
|
434
|
+
"sql.destructive": true,
|
|
435
|
+
},
|
|
436
|
+
});
|
|
437
|
+
rules.push({
|
|
438
|
+
name: "require-approval-destructive-sql-drop",
|
|
439
|
+
ruleAction: "require_approval",
|
|
440
|
+
priority: 94,
|
|
441
|
+
condition: {
|
|
442
|
+
"sql.verb": "DROP",
|
|
443
|
+
owner_approved: false,
|
|
444
|
+
},
|
|
445
|
+
});
|
|
446
|
+
rules.push({
|
|
447
|
+
name: "require-approval-destructive-sql-truncate",
|
|
448
|
+
ruleAction: "require_approval",
|
|
449
|
+
priority: 94,
|
|
450
|
+
condition: {
|
|
451
|
+
"sql.verb": "TRUNCATE",
|
|
452
|
+
owner_approved: false,
|
|
453
|
+
},
|
|
454
|
+
});
|
|
455
|
+
rules.push({
|
|
456
|
+
name: "deny-k8s-delete-kube-system",
|
|
457
|
+
ruleAction: "deny",
|
|
458
|
+
priority: 95,
|
|
459
|
+
condition: {
|
|
460
|
+
"k8s.verb": "delete",
|
|
461
|
+
"k8s.namespace": "kube-system",
|
|
462
|
+
},
|
|
463
|
+
});
|
|
464
|
+
}
|
|
116
465
|
if (policy.mode === "enforce") {
|
|
117
466
|
for (const risk of policy.requireApprovalFor || []) {
|
|
118
467
|
rules.push({
|
|
@@ -147,24 +496,38 @@ export class MicrosoftGovernanceAdapter {
|
|
|
147
496
|
}
|
|
148
497
|
/**
|
|
149
498
|
* Runs the full Microsoft Agent Governance pipeline:
|
|
150
|
-
* Agent ──► McpSecurityScanner ──► PolicyEngine ──► AgentIdentity (Ed25519 DID) ──► AuditLogger ──► DecisionRecord
|
|
499
|
+
* Agent ──► KillSwitch ──► McpSecurityScanner & ContextPoisoningDetector (ASI-06) ──► ProtocolFacets (SQL/K8s/Infra) ──► RingEnforcer ──► PolicyEngine ──► TrustManager ──► AgentIdentity (Ed25519 DID) ──► AuditLogger ──► DecisionRecord
|
|
151
500
|
*/
|
|
152
501
|
evaluateAction(params) {
|
|
153
502
|
const { workspace, actorId, tool, input, reason, stage } = params;
|
|
154
503
|
const ownerApproved = Boolean(params.ownerApproved);
|
|
155
504
|
const decidedAt = new Date().toISOString();
|
|
156
|
-
// 1. McpSecurityScanner: inspect tool definition + input arguments
|
|
505
|
+
// 1. McpSecurityScanner + ContextPoisoningDetector (OWASP ASI-06): inspect tool definition + input arguments
|
|
157
506
|
const serializedInput = JSON.stringify(input || {});
|
|
158
507
|
const scanResult = this.scanner.scan({
|
|
159
508
|
name: tool.slug,
|
|
160
509
|
description: `${tool.description}\nReason: ${reason}\nArgs: ${serializedInput}`,
|
|
161
510
|
parameters: tool.inputSchema,
|
|
162
511
|
});
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
512
|
+
const contextPoisoning = this.scanMemoryContent({
|
|
513
|
+
workspaceId: workspace.id,
|
|
514
|
+
sessionId: stage,
|
|
515
|
+
role: "user",
|
|
516
|
+
content: `${reason}\n${serializedInput}`,
|
|
517
|
+
recordInDetector: false,
|
|
518
|
+
});
|
|
519
|
+
const extraFindings = [
|
|
520
|
+
...(scanResult.threats || []).map((t) => ({
|
|
521
|
+
category: String(t.type || "mcp_scan"),
|
|
522
|
+
severity: String(t.severity || "medium"),
|
|
523
|
+
description: String(t.description || "Security scanner finding"),
|
|
524
|
+
})),
|
|
525
|
+
...contextPoisoning.findings.map((f) => ({
|
|
526
|
+
category: `asi06_${f.patternId.toLowerCase()}`,
|
|
527
|
+
severity: f.severity,
|
|
528
|
+
description: `${f.patternName}: ${f.description}`,
|
|
529
|
+
})),
|
|
530
|
+
];
|
|
168
531
|
// Additional argument-level prompt-injection / secret-exfiltration heuristics
|
|
169
532
|
const suspiciousPatterns = [
|
|
170
533
|
{
|
|
@@ -190,8 +553,15 @@ export class MicrosoftGovernanceAdapter {
|
|
|
190
553
|
}
|
|
191
554
|
}
|
|
192
555
|
const highSeverityFinding = extraFindings.some((f) => f.severity === "high" || f.severity === "critical");
|
|
193
|
-
// 2.
|
|
556
|
+
// 2. Protocol Facets Extraction (SQL, Kubernetes, Infrastructure)
|
|
557
|
+
const protocolFacets = this.extractActionFacets(tool, input);
|
|
558
|
+
// 3. Hardware-style Execution Ring Enforcement (Ring 0..Ring 3)
|
|
559
|
+
const ringEnforcement = this.evaluateRingEnforcement(workspace, tool, ownerApproved);
|
|
560
|
+
// 4. PolicyEngine + KillSwitch evaluation
|
|
194
561
|
const policy = workspace.policy;
|
|
562
|
+
const activeKill = this.isKillSwitchActive(workspace.id);
|
|
563
|
+
const killSwitchActive = Boolean(activeKill || policy.killSwitchActive);
|
|
564
|
+
const killSwitchReason = activeKill?.reason || policy.killSwitchReason || null;
|
|
195
565
|
const policyEngine = this.createPolicyEngine(policy);
|
|
196
566
|
const identity = this.getWorkspaceIdentity(workspace);
|
|
197
567
|
const policyDecision = policyEngine.evaluatePolicy(identity.did, {
|
|
@@ -201,18 +571,36 @@ export class MicrosoftGovernanceAdapter {
|
|
|
201
571
|
stage,
|
|
202
572
|
owner_approved: ownerApproved,
|
|
203
573
|
workspace_active: true,
|
|
574
|
+
...(protocolFacets.sql ? { sql: protocolFacets.sql } : {}),
|
|
575
|
+
...(protocolFacets.k8s ? { k8s: protocolFacets.k8s } : {}),
|
|
576
|
+
...(protocolFacets.infra ? { infra: protocolFacets.infra } : {}),
|
|
204
577
|
});
|
|
205
578
|
let finalDecision = policyDecision.action === "deny"
|
|
206
579
|
? "deny"
|
|
207
580
|
: policyDecision.action === "require_approval"
|
|
208
581
|
? "require_approval"
|
|
209
582
|
: "allow";
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
583
|
+
let matchedRule = policyDecision.matchedRule ?? null;
|
|
584
|
+
// Enforce KillSwitch, blocked tools, high-severity scanner/context-poisoning findings, or Ring breach
|
|
585
|
+
if (killSwitchActive) {
|
|
586
|
+
finalDecision = "deny";
|
|
587
|
+
matchedRule = "kill-switch-emergency-quarantine";
|
|
588
|
+
}
|
|
589
|
+
else if ((policy.blockedToolSlugs || []).includes(tool.slug)) {
|
|
213
590
|
finalDecision = "deny";
|
|
591
|
+
matchedRule = matchedRule || `deny-blocked-tool-${tool.slug}`;
|
|
592
|
+
}
|
|
593
|
+
else if (highSeverityFinding && policy.mode === "enforce") {
|
|
594
|
+
finalDecision = "deny";
|
|
595
|
+
matchedRule = "mcp-security-scanner-high-severity";
|
|
596
|
+
this.trustManager.recordFailure(workspace.id, 0.1);
|
|
597
|
+
}
|
|
598
|
+
else if (!ringEnforcement.allowed && finalDecision === "allow" && policy.mode === "enforce") {
|
|
599
|
+
finalDecision = "require_approval";
|
|
600
|
+
matchedRule = `ring-enforcer-requires-ring-${ringEnforcement.requiredRing}`;
|
|
214
601
|
}
|
|
215
|
-
|
|
602
|
+
const trustScore = this.getWorkspaceTrustScore(workspace.id);
|
|
603
|
+
// 5. AuditLogger: append tamper-evident SHA-256 hash-chained log entry
|
|
216
604
|
const auditLogger = this.getWorkspaceAuditLogger(workspace.id);
|
|
217
605
|
const auditDecision = finalDecision === "deny" ? "deny" : "allow";
|
|
218
606
|
const auditEntry = auditLogger.log({
|
|
@@ -222,11 +610,13 @@ export class MicrosoftGovernanceAdapter {
|
|
|
222
610
|
skillAuditMetadata: {
|
|
223
611
|
skillName: tool.slug,
|
|
224
612
|
skillOrigin: tool.lane,
|
|
225
|
-
provenanceSourceTrust: "
|
|
613
|
+
provenanceSourceTrust: trustScore.tier === "Verified" || trustScore.tier === "Trusted"
|
|
614
|
+
? "trusted"
|
|
615
|
+
: undefined,
|
|
226
616
|
},
|
|
227
617
|
});
|
|
228
618
|
const chainVerified = auditLogger.verify();
|
|
229
|
-
//
|
|
619
|
+
// 6. AgentIdentity (Ed25519 DID): cryptographically sign the decision record
|
|
230
620
|
const bindingHash = crypto
|
|
231
621
|
.createHash("sha256")
|
|
232
622
|
.update(JSON.stringify({
|
|
@@ -259,12 +649,14 @@ export class MicrosoftGovernanceAdapter {
|
|
|
259
649
|
const publicKeyHex = Buffer.from(identity.publicKey).toString("hex");
|
|
260
650
|
const verdict = finalDecision === "require_approval" ? "escalate" : finalDecision;
|
|
261
651
|
const decisionReason = finalDecision === "deny"
|
|
262
|
-
?
|
|
263
|
-
? `Blocked by
|
|
264
|
-
:
|
|
652
|
+
? killSwitchActive
|
|
653
|
+
? `Blocked by Emergency KillSwitch: ${killSwitchReason || "Workspace agent boundary is quarantined"}`
|
|
654
|
+
: highSeverityFinding
|
|
655
|
+
? `Blocked by McpSecurityScanner / ContextPoisoningDetector: ${extraFindings.map((f) => f.description).join("; ")}`
|
|
656
|
+
: `Denied by policy rule ${matchedRule || "default"}`
|
|
265
657
|
: finalDecision === "require_approval"
|
|
266
|
-
? `Escalated for human owner approval (${tool.riskLevel} risk lane)`
|
|
267
|
-
: `Allowed by Microsoft Agent Governance (${
|
|
658
|
+
? `Escalated for human owner approval (${tool.riskLevel} risk lane, ${ringEnforcement.ringLabel})`
|
|
659
|
+
: `Allowed by Microsoft Agent Governance (${matchedRule || "allow-governed-action"})`;
|
|
268
660
|
return {
|
|
269
661
|
id: `dec_${crypto.randomBytes(8).toString("hex")}`,
|
|
270
662
|
sdk: "@microsoft/agent-governance-sdk",
|
|
@@ -280,12 +672,12 @@ export class MicrosoftGovernanceAdapter {
|
|
|
280
672
|
decision: finalDecision,
|
|
281
673
|
verdict,
|
|
282
674
|
reason: decisionReason,
|
|
283
|
-
matchedRule
|
|
675
|
+
matchedRule,
|
|
284
676
|
securityScan: {
|
|
285
|
-
safe: scanResult.safe && !highSeverityFinding,
|
|
677
|
+
safe: scanResult.safe && !highSeverityFinding && contextPoisoning.safe,
|
|
286
678
|
riskLevel: highSeverityFinding
|
|
287
679
|
? "high"
|
|
288
|
-
: scanResult.risk_score > 50
|
|
680
|
+
: scanResult.risk_score > 50 || contextPoisoning.findingsCount > 0
|
|
289
681
|
? "medium"
|
|
290
682
|
: "low",
|
|
291
683
|
findingsCount: extraFindings.length,
|
|
@@ -300,6 +692,20 @@ export class MicrosoftGovernanceAdapter {
|
|
|
300
692
|
previousHash: auditEntry.previousHash,
|
|
301
693
|
chainVerified,
|
|
302
694
|
},
|
|
695
|
+
protocolFacets,
|
|
696
|
+
ringEnforcement,
|
|
697
|
+
trustScore,
|
|
698
|
+
contextPoisoning: {
|
|
699
|
+
safe: contextPoisoning.safe,
|
|
700
|
+
riskLevel: contextPoisoning.riskLevel,
|
|
701
|
+
findingsCount: contextPoisoning.findingsCount,
|
|
702
|
+
findings: contextPoisoning.findings,
|
|
703
|
+
},
|
|
704
|
+
killSwitch: {
|
|
705
|
+
active: killSwitchActive,
|
|
706
|
+
killedAt: activeKill?.killedAt || null,
|
|
707
|
+
reason: killSwitchReason,
|
|
708
|
+
},
|
|
303
709
|
ed25519Signature,
|
|
304
710
|
signature: ed25519Signature,
|
|
305
711
|
bindingHash,
|
|
@@ -398,13 +804,41 @@ export class MicrosoftGovernanceAdapter {
|
|
|
398
804
|
const identity = this.getWorkspaceIdentity(workspace);
|
|
399
805
|
const auditLogger = this.getWorkspaceAuditLogger(workspace.id);
|
|
400
806
|
const entries = auditLogger.getEntries();
|
|
807
|
+
const activeKill = this.isKillSwitchActive(workspace.id);
|
|
808
|
+
const killSwitchActive = Boolean(activeKill || workspace.policy.killSwitchActive);
|
|
809
|
+
const contextPoisoningReport = this.getContextPoisoningReport(workspace.id, "default");
|
|
401
810
|
return {
|
|
402
811
|
sdk: "@microsoft/agent-governance-sdk",
|
|
403
812
|
sdkVersion: SDK_VERSION,
|
|
404
813
|
policyId: POLICY_ID,
|
|
405
|
-
pipeline: "Agent ──► McpSecurityScanner ──► PolicyEngine ──► AgentIdentity (Ed25519 DID) ──► AuditLogger ──► DecisionRecord",
|
|
814
|
+
pipeline: "Agent ──► KillSwitch ──► McpSecurityScanner & ContextPoisoningDetector (ASI-06) ──► ProtocolFacets (SQL/K8s/Infra) ──► RingEnforcer ──► PolicyEngine ──► TrustManager ──► AgentIdentity (Ed25519 DID) ──► AuditLogger ──► DecisionRecord",
|
|
815
|
+
modules: [
|
|
816
|
+
"McpSecurityScanner",
|
|
817
|
+
"ContextPoisoningDetector",
|
|
818
|
+
"ProtocolFacets",
|
|
819
|
+
"RingEnforcer",
|
|
820
|
+
"PolicyEngine",
|
|
821
|
+
"TrustManager",
|
|
822
|
+
"KillSwitch",
|
|
823
|
+
"AgentIdentity",
|
|
824
|
+
"AuditLogger",
|
|
825
|
+
],
|
|
406
826
|
agentIdentity: identity.toJSON(),
|
|
407
827
|
policy: workspace.policy,
|
|
828
|
+
trustScore: this.getWorkspaceTrustScore(workspace.id),
|
|
829
|
+
killSwitch: {
|
|
830
|
+
active: killSwitchActive,
|
|
831
|
+
killedAt: activeKill?.killedAt || null,
|
|
832
|
+
reason: activeKill?.reason || workspace.policy.killSwitchReason || null,
|
|
833
|
+
historyCount: this.killSwitch.getHistory().length,
|
|
834
|
+
},
|
|
835
|
+
contextPoisoning: {
|
|
836
|
+
poisoned: Boolean(contextPoisoningReport.poisoned ??
|
|
837
|
+
(contextPoisoningReport.findings?.length > 0)),
|
|
838
|
+
riskScore: Number(contextPoisoningReport.riskScore ?? 0),
|
|
839
|
+
findingsCount: contextPoisoningReport.findings.length,
|
|
840
|
+
findings: contextPoisoningReport.findings.slice(-10),
|
|
841
|
+
},
|
|
408
842
|
auditChain: {
|
|
409
843
|
entriesCount: entries.length,
|
|
410
844
|
chainVerified: auditLogger.verify(),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type MuxoConfig } from "../config.js";
|
|
2
2
|
import type { MuxoTool } from "../types.js";
|
|
3
|
+
export declare function isPaidCloudProvisioning(tool: MuxoTool, input?: Record<string, unknown>): boolean;
|
|
3
4
|
export declare class StripeProjectsAdapter {
|
|
4
5
|
private readonly config;
|
|
5
6
|
private readonly tools;
|
|
@@ -32,11 +33,13 @@ export declare class StripeProjectsAdapter {
|
|
|
32
33
|
redirectUrl: string | null;
|
|
33
34
|
message: string;
|
|
34
35
|
}>;
|
|
35
|
-
pullEnvVarsFromWorkdir(): Promise<Record<string, string>>;
|
|
36
|
+
pullEnvVarsFromWorkdir(syncFromCli?: boolean): Promise<Record<string, string>>;
|
|
36
37
|
executeTool(params: {
|
|
37
38
|
workspaceId: string;
|
|
38
39
|
tool: MuxoTool;
|
|
39
40
|
input: Record<string, unknown>;
|
|
41
|
+
stripeApiKey?: string | null;
|
|
42
|
+
masterWalletDepleted?: boolean;
|
|
40
43
|
}): Promise<{
|
|
41
44
|
output: Record<string, unknown>;
|
|
42
45
|
provisionedResource?: {
|