@kenhkl/agent-moss 0.17.8 → 0.17.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +146 -52
- package/dist/engine/coordinator.d.ts +4 -0
- package/dist/engine/coordinator.js +95 -30
- package/dist/server.js +146 -52
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1246,31 +1246,42 @@ function hardenExistingPaths() {
|
|
|
1246
1246
|
function chainDetailMode() {
|
|
1247
1247
|
return (env("AGENT_MOSS_CHAIN_DETAIL", "redacted") || "redacted").toLowerCase();
|
|
1248
1248
|
}
|
|
1249
|
-
function redactChain(chain, currentSessionId) {
|
|
1249
|
+
function redactChain(chain, currentInstanceId = "", currentSessionId = "") {
|
|
1250
1250
|
if (!chain || chain.length === 0) return [];
|
|
1251
1251
|
if (chainDetailMode() === "full") return [...chain];
|
|
1252
1252
|
return chain.map((step) => {
|
|
1253
1253
|
if (Array.isArray(step) && step.length >= 3) {
|
|
1254
1254
|
const [sid, seq, , ts] = step;
|
|
1255
|
-
|
|
1255
|
+
const inst = step.length > 5 ? String(step[5] ?? "") : "";
|
|
1256
|
+
const sameInstance = !!(inst && currentInstanceId && inst === String(currentInstanceId));
|
|
1257
|
+
const sameSessionFallback = !inst && !!currentSessionId && String(sid) === String(currentSessionId);
|
|
1258
|
+
if (sameInstance || sameSessionFallback) return [...step];
|
|
1256
1259
|
return ["<other-session>", seq, "<\u5DF2\u9690\u85CF>", ts ?? ""];
|
|
1257
1260
|
}
|
|
1258
1261
|
return step;
|
|
1259
1262
|
});
|
|
1260
1263
|
}
|
|
1261
|
-
function redactGraph(graph, currentSessionId) {
|
|
1264
|
+
function redactGraph(graph, currentInstanceId = "", currentSessionId = "") {
|
|
1262
1265
|
if (!graph) return null;
|
|
1263
1266
|
if (chainDetailMode() === "full") return graph;
|
|
1264
1267
|
const nodes = (graph.nodes || []).map((n) => {
|
|
1265
1268
|
const m = { ...n };
|
|
1266
|
-
|
|
1269
|
+
const inst = String(m.instance_id ?? "") || "";
|
|
1270
|
+
const sid = String(m.session_id ?? "") || "";
|
|
1271
|
+
const sameInstance = !!(inst && currentInstanceId && inst === String(currentInstanceId));
|
|
1272
|
+
const sameSessionFallback = !inst && !!currentSessionId && sid === String(currentSessionId);
|
|
1273
|
+
if (!(sameInstance || sameSessionFallback)) {
|
|
1267
1274
|
m.session_id = "<other-session>";
|
|
1268
1275
|
m.action_detail = "<\u5DF2\u9690\u85CF>";
|
|
1269
1276
|
m.resources = [];
|
|
1270
1277
|
}
|
|
1271
1278
|
return m;
|
|
1272
1279
|
});
|
|
1273
|
-
|
|
1280
|
+
const out = { nodes, edges: graph.edges || [] };
|
|
1281
|
+
for (const key of ["property_id", "property_short", "property_name", "property_expression"]) {
|
|
1282
|
+
if (graph[key] !== void 0) out[key] = graph[key];
|
|
1283
|
+
}
|
|
1284
|
+
return out;
|
|
1274
1285
|
}
|
|
1275
1286
|
function sessionSweepThreshold() {
|
|
1276
1287
|
return envInt("AGENT_MOSS_SESSION_SWEEP_THRESHOLD", 50);
|
|
@@ -1433,6 +1444,8 @@ var init_models = __esm({
|
|
|
1433
1444
|
session_id: z.string(),
|
|
1434
1445
|
/** 来源 agent(AgentGate 逐会话带上):记录在链路径里供告警/取证,不参与链匹配。 */
|
|
1435
1446
|
agent_id: z.string().optional(),
|
|
1447
|
+
/** 来源实例:仅用于展示层脱敏(同实例不隐藏),不参与链匹配。 */
|
|
1448
|
+
instance_id: z.string().optional(),
|
|
1436
1449
|
action_history: z.array(ActionItemSchema).default([])
|
|
1437
1450
|
})).default([]).describe("Multi-session historical actions (optional): for L2.2 formal engine cross-session combo-chain / sensitive-data-flow verification. Provided by caller (incl. future agentgate)."),
|
|
1438
1451
|
a_next: ActionItemSchema.describe("Next action to be executed"),
|
|
@@ -1440,6 +1453,7 @@ var init_models = __esm({
|
|
|
1440
1453
|
os_type: z.string().default("").describe("OS type: 'linux', 'windows', or empty for auto-detect"),
|
|
1441
1454
|
cwd: z.string().default("").describe("Current working directory"),
|
|
1442
1455
|
agent_id: z.string().default("").describe("Caller agent identifier, e.g. 'xiaoo' / 'opencode' / 'opendesk'. Empty = generic mode (no agent-specific rules)."),
|
|
1456
|
+
instance_id: z.string().default("").describe("Agent installation/instance identifier. Used only for display-layer redaction: nodes from the same instance are returned unredacted. Does not affect detection."),
|
|
1443
1457
|
metadata: z.record(z.unknown()).optional().describe("Extended metadata")
|
|
1444
1458
|
});
|
|
1445
1459
|
AnalyzeResponseSchema = z.object({
|
|
@@ -1477,7 +1491,9 @@ var init_models = __esm({
|
|
|
1477
1491
|
// 节点在性质中的角色:source/carrier/sink(P2)或阶段名(P1)
|
|
1478
1492
|
role: z.string().optional(),
|
|
1479
1493
|
// 是否为违例见证节点(当前动作补全链 / 触发外传)
|
|
1480
|
-
witness: z.boolean().optional()
|
|
1494
|
+
witness: z.boolean().optional(),
|
|
1495
|
+
// 来源实例(仅展示层脱敏:同实例不隐藏)
|
|
1496
|
+
instance_id: z.string().optional()
|
|
1481
1497
|
})
|
|
1482
1498
|
),
|
|
1483
1499
|
edges: z.array(
|
|
@@ -9436,7 +9452,7 @@ function markIntegrity(actionDetail) {
|
|
|
9436
9452
|
}
|
|
9437
9453
|
return { integrity: "medium", isLowSource: false };
|
|
9438
9454
|
}
|
|
9439
|
-
function initNode(sessionId, seq, actionType, actionDetail, timestamp, agentId = "") {
|
|
9455
|
+
function initNode(sessionId, seq, actionType, actionDetail, timestamp, agentId = "", instanceId = "") {
|
|
9440
9456
|
const { integrity, isLowSource } = markIntegrity(actionDetail);
|
|
9441
9457
|
return {
|
|
9442
9458
|
sessionId,
|
|
@@ -9445,13 +9461,14 @@ function initNode(sessionId, seq, actionType, actionDetail, timestamp, agentId =
|
|
|
9445
9461
|
actionDetail,
|
|
9446
9462
|
stage: markStage(actionDetail),
|
|
9447
9463
|
agentId: agentId || void 0,
|
|
9464
|
+
instanceId: instanceId || void 0,
|
|
9448
9465
|
resources: extractResources(actionDetail),
|
|
9449
9466
|
integrity,
|
|
9450
9467
|
isLowSource,
|
|
9451
9468
|
timestamp: timestamp || void 0
|
|
9452
9469
|
};
|
|
9453
9470
|
}
|
|
9454
|
-
function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId = "") {
|
|
9471
|
+
function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId = "", instanceId = "") {
|
|
9455
9472
|
const nodes = [];
|
|
9456
9473
|
const edges = [];
|
|
9457
9474
|
const stageFlags = /* @__PURE__ */ new Set();
|
|
@@ -9462,7 +9479,8 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId
|
|
|
9462
9479
|
actionType: String(a.actionType || ""),
|
|
9463
9480
|
actionDetail: ad,
|
|
9464
9481
|
calledAt: a.calledAt || void 0,
|
|
9465
|
-
agentId: a.agentId || agentId || void 0
|
|
9482
|
+
agentId: a.agentId || agentId || void 0,
|
|
9483
|
+
instanceId: a.instanceId || instanceId || void 0
|
|
9466
9484
|
});
|
|
9467
9485
|
}
|
|
9468
9486
|
if (aNext) {
|
|
@@ -9470,11 +9488,12 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId
|
|
|
9470
9488
|
actionType: String(aNext.actionType || ""),
|
|
9471
9489
|
actionDetail: String(aNext.actionDetail || ""),
|
|
9472
9490
|
calledAt: aNext.calledAt || void 0,
|
|
9473
|
-
agentId: aNext.agentId || agentId || void 0
|
|
9491
|
+
agentId: aNext.agentId || agentId || void 0,
|
|
9492
|
+
instanceId: aNext.instanceId || instanceId || void 0
|
|
9474
9493
|
});
|
|
9475
9494
|
}
|
|
9476
9495
|
details.forEach((d, seq) => {
|
|
9477
|
-
const node = initNode(sessionId, seq, d.actionType, d.actionDetail, d.calledAt, d.agentId || "");
|
|
9496
|
+
const node = initNode(sessionId, seq, d.actionType, d.actionDetail, d.calledAt, d.agentId || "", d.instanceId || "");
|
|
9478
9497
|
nodes.push(node);
|
|
9479
9498
|
if (node.stage) stageFlags.add(node.stage);
|
|
9480
9499
|
});
|
|
@@ -9486,12 +9505,18 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId
|
|
|
9486
9505
|
function buildMultiSessionGraph(sessions, aNext) {
|
|
9487
9506
|
const mg = { sessions: [], crossEdges: [] };
|
|
9488
9507
|
if (!sessions || sessions.length === 0) {
|
|
9489
|
-
mg.sessions.push(buildSessionGraph([], aNext, "current"));
|
|
9508
|
+
mg.sessions.push(buildSessionGraph([], aNext, "current", "", aNext?.instanceId || ""));
|
|
9490
9509
|
return mg;
|
|
9491
9510
|
}
|
|
9492
9511
|
for (const s of sessions) {
|
|
9493
9512
|
const sid = String(s.sessionId || "s");
|
|
9494
|
-
mg.sessions.push(buildSessionGraph(
|
|
9513
|
+
mg.sessions.push(buildSessionGraph(
|
|
9514
|
+
s.actionHistory || [],
|
|
9515
|
+
void 0,
|
|
9516
|
+
sid,
|
|
9517
|
+
s.agentId || "",
|
|
9518
|
+
s.instanceId || ""
|
|
9519
|
+
));
|
|
9495
9520
|
}
|
|
9496
9521
|
if (aNext) {
|
|
9497
9522
|
const last = mg.sessions[mg.sessions.length - 1];
|
|
@@ -9502,13 +9527,14 @@ function buildMultiSessionGraph(sessions, aNext) {
|
|
|
9502
9527
|
String(aNext.actionType || ""),
|
|
9503
9528
|
String(aNext.actionDetail || ""),
|
|
9504
9529
|
aNext.calledAt || void 0,
|
|
9505
|
-
aNext.agentId || ""
|
|
9530
|
+
aNext.agentId || "",
|
|
9531
|
+
aNext.instanceId || ""
|
|
9506
9532
|
));
|
|
9507
9533
|
if (last.nodes.length > 1) {
|
|
9508
9534
|
last.edges.push({ src: last.nodes.length - 2, dst: last.nodes.length - 1, kind: "next" });
|
|
9509
9535
|
}
|
|
9510
9536
|
} else {
|
|
9511
|
-
mg.sessions.push(buildSessionGraph([], aNext, "current"));
|
|
9537
|
+
mg.sessions.push(buildSessionGraph([], aNext, "current", "", aNext.instanceId || ""));
|
|
9512
9538
|
}
|
|
9513
9539
|
}
|
|
9514
9540
|
addCrossDataEdges(mg);
|
|
@@ -9572,7 +9598,8 @@ function graphFromPath(nodes, path6, edgeType = "next", propertyMeta, roles, wit
|
|
|
9572
9598
|
seq: item.seq,
|
|
9573
9599
|
stage: n ? n.stage : "",
|
|
9574
9600
|
action_detail: n ? n.actionDetail : item.detail,
|
|
9575
|
-
resources: n ? [...n.resources].sort() : []
|
|
9601
|
+
resources: n ? [...n.resources].sort() : [],
|
|
9602
|
+
instance_id: n ? n.instanceId || "" : ""
|
|
9576
9603
|
});
|
|
9577
9604
|
if (gNodes.length > 1) {
|
|
9578
9605
|
gEdges.push({ from: gNodes.length - 2, to: gNodes.length - 1, type: edgeType });
|
|
@@ -9740,7 +9767,8 @@ function checkSensitiveDataFlow(graph) {
|
|
|
9740
9767
|
stage: n.stage,
|
|
9741
9768
|
action_detail: n.actionDetail,
|
|
9742
9769
|
resources: [...n.resources].sort(),
|
|
9743
|
-
role
|
|
9770
|
+
role,
|
|
9771
|
+
instance_id: n.instanceId || ""
|
|
9744
9772
|
});
|
|
9745
9773
|
if (k > 0) {
|
|
9746
9774
|
const prev = chain[k - 1];
|
|
@@ -9890,14 +9918,23 @@ function checkNeverExfilAfterPrivesc(graph) {
|
|
|
9890
9918
|
if (candidates.length > 0) {
|
|
9891
9919
|
const pre = nodes[candidates[candidates.length - 1]];
|
|
9892
9920
|
const post = nodes[firstEv];
|
|
9921
|
+
const path6 = [
|
|
9922
|
+
{ sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
|
|
9923
|
+
{ sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
|
|
9924
|
+
];
|
|
9893
9925
|
return [{
|
|
9894
9926
|
propertyId: "no-exfil-after-privesc",
|
|
9895
9927
|
reason: "\u63D0\u6743\u540E\u51FA\u73B0\u5916\u4F20\uFF1A\u63D0\u6743(\u524D) \u2192 \u5916\u4F20(\u540E)\uFF0C\u8FDD\u53CD \u25A1(PRIVESC\u2192\u25C7EXFIL) \u7A97\u53E3\u7EA6\u675F",
|
|
9896
9928
|
riskLevel: "critical",
|
|
9897
|
-
path:
|
|
9898
|
-
|
|
9899
|
-
|
|
9900
|
-
|
|
9929
|
+
path: path6,
|
|
9930
|
+
graph: graphFromPath(nodes, path6, "next", {
|
|
9931
|
+
property_id: "no-exfil-after-privesc",
|
|
9932
|
+
property_short: "P4",
|
|
9933
|
+
property_name: "\u63D0\u6743\u540E\u7981\u6B62\u5916\u4F20"
|
|
9934
|
+
}, ["privesc", "sink"], true, [
|
|
9935
|
+
{ token: "PRIVESC", node_index: 0 },
|
|
9936
|
+
{ token: "EXFIL", node_index: 1 }
|
|
9937
|
+
])
|
|
9901
9938
|
}];
|
|
9902
9939
|
}
|
|
9903
9940
|
return violations;
|
|
@@ -9907,14 +9944,23 @@ function checkNeverExfilAfterPrivesc(graph) {
|
|
|
9907
9944
|
for (let j = i + 1; j < nodes.length; j++) {
|
|
9908
9945
|
if (!isExfilNode(nodes[j])) continue;
|
|
9909
9946
|
if (privescInWindow(nodes, [i], j, windowSec).length === 0) continue;
|
|
9947
|
+
const path6 = [
|
|
9948
|
+
{ sessionId: nodes[i].sessionId, seq: nodes[i].seq, detail: nodes[i].actionDetail.slice(0, 60) },
|
|
9949
|
+
{ sessionId: nodes[j].sessionId, seq: nodes[j].seq, detail: nodes[j].actionDetail.slice(0, 60) }
|
|
9950
|
+
];
|
|
9910
9951
|
violations.push({
|
|
9911
9952
|
propertyId: "no-exfil-after-privesc",
|
|
9912
9953
|
reason: "\u63D0\u6743\u540E\u51FA\u73B0\u5916\u4F20\uFF1A\u63D0\u6743(\u524D) \u2192 \u5916\u4F20(\u540E)\uFF0C\u8FDD\u53CD \u25A1(PRIVESC\u2192\u25C7EXFIL) \u7A97\u53E3\u7EA6\u675F",
|
|
9913
9954
|
riskLevel: "critical",
|
|
9914
|
-
path:
|
|
9915
|
-
|
|
9916
|
-
|
|
9917
|
-
|
|
9955
|
+
path: path6,
|
|
9956
|
+
graph: graphFromPath(nodes, path6, "next", {
|
|
9957
|
+
property_id: "no-exfil-after-privesc",
|
|
9958
|
+
property_short: "P4",
|
|
9959
|
+
property_name: "\u63D0\u6743\u540E\u7981\u6B62\u5916\u4F20"
|
|
9960
|
+
}, ["privesc", "sink"], true, [
|
|
9961
|
+
{ token: "PRIVESC", node_index: 0 },
|
|
9962
|
+
{ token: "EXFIL", node_index: 1 }
|
|
9963
|
+
])
|
|
9918
9964
|
});
|
|
9919
9965
|
return violations;
|
|
9920
9966
|
}
|
|
@@ -9939,14 +9985,23 @@ function checkNeverExfilAfterCred(graph) {
|
|
|
9939
9985
|
}
|
|
9940
9986
|
if (pre) {
|
|
9941
9987
|
const post = nodes[firstEv];
|
|
9988
|
+
const path6 = [
|
|
9989
|
+
{ sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
|
|
9990
|
+
{ sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
|
|
9991
|
+
];
|
|
9942
9992
|
return [{
|
|
9943
9993
|
propertyId: "no-exfil-after-credential",
|
|
9944
9994
|
reason: "\u8BFB\u53D6\u51ED\u636E\u540E\u51FA\u73B0\u5916\u4F20\uFF0C\u8FDD\u53CD \u25A1(CRED\u2192\xAC\u25C7EXFIL)",
|
|
9945
9995
|
riskLevel: "critical",
|
|
9946
|
-
path:
|
|
9947
|
-
|
|
9948
|
-
|
|
9949
|
-
|
|
9996
|
+
path: path6,
|
|
9997
|
+
graph: graphFromPath(nodes, path6, "data", {
|
|
9998
|
+
property_id: "no-exfil-after-credential",
|
|
9999
|
+
property_short: "P5",
|
|
10000
|
+
property_name: "\u8BFB\u53D6\u51ED\u636E\u540E\u7981\u6B62\u5916\u4F20"
|
|
10001
|
+
}, ["source", "sink"], true, [
|
|
10002
|
+
{ token: "CRED", node_index: 0 },
|
|
10003
|
+
{ token: "EXFIL", node_index: 1 }
|
|
10004
|
+
])
|
|
9950
10005
|
}];
|
|
9951
10006
|
}
|
|
9952
10007
|
return violations;
|
|
@@ -9955,14 +10010,23 @@ function checkNeverExfilAfterCred(graph) {
|
|
|
9955
10010
|
if (!isCredNode(nodes[i])) continue;
|
|
9956
10011
|
for (let j = i + 1; j < nodes.length; j++) {
|
|
9957
10012
|
if (isExfilNode(nodes[j])) {
|
|
10013
|
+
const path6 = [
|
|
10014
|
+
{ sessionId: nodes[i].sessionId, seq: nodes[i].seq, detail: nodes[i].actionDetail.slice(0, 60) },
|
|
10015
|
+
{ sessionId: nodes[j].sessionId, seq: nodes[j].seq, detail: nodes[j].actionDetail.slice(0, 60) }
|
|
10016
|
+
];
|
|
9958
10017
|
violations.push({
|
|
9959
10018
|
propertyId: "no-exfil-after-credential",
|
|
9960
10019
|
reason: "\u8BFB\u53D6\u51ED\u636E\u540E\u51FA\u73B0\u5916\u4F20\uFF0C\u8FDD\u53CD \u25A1(CRED\u2192\xAC\u25C7EXFIL)",
|
|
9961
10020
|
riskLevel: "critical",
|
|
9962
|
-
path:
|
|
9963
|
-
|
|
9964
|
-
|
|
9965
|
-
|
|
10021
|
+
path: path6,
|
|
10022
|
+
graph: graphFromPath(nodes, path6, "data", {
|
|
10023
|
+
property_id: "no-exfil-after-credential",
|
|
10024
|
+
property_short: "P5",
|
|
10025
|
+
property_name: "\u8BFB\u53D6\u51ED\u636E\u540E\u7981\u6B62\u5916\u4F20"
|
|
10026
|
+
}, ["source", "sink"], true, [
|
|
10027
|
+
{ token: "CRED", node_index: 0 },
|
|
10028
|
+
{ token: "EXFIL", node_index: 1 }
|
|
10029
|
+
])
|
|
9966
10030
|
});
|
|
9967
10031
|
return violations;
|
|
9968
10032
|
}
|
|
@@ -10114,11 +10178,19 @@ function checkSensitiveDataAggregation(graph) {
|
|
|
10114
10178
|
if (!isExfilNode(n)) continue;
|
|
10115
10179
|
const srcs = sourcesFor(i);
|
|
10116
10180
|
if (srcs.size >= 2) {
|
|
10181
|
+
const path6 = [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }];
|
|
10117
10182
|
violations.push({
|
|
10118
10183
|
propertyId: "sensitive-data-aggregation",
|
|
10119
10184
|
reason: `\u654F\u611F\u6570\u636E\u805A\u5408\u5916\u4F20\uFF1Aegress \u805A\u5408 ${srcs.size} \u4E2A\u4E8B\u4EF6\u6E90\u6570\u636E\uFF08Rule 12\uFF09`,
|
|
10120
10185
|
riskLevel: "critical",
|
|
10121
|
-
path:
|
|
10186
|
+
path: path6,
|
|
10187
|
+
graph: graphFromPath(nodes, path6, "next", {
|
|
10188
|
+
property_id: "sensitive-data-aggregation",
|
|
10189
|
+
property_short: "P8",
|
|
10190
|
+
property_name: "\u654F\u611F\u6570\u636E\u805A\u5408\u5916\u4F20"
|
|
10191
|
+
}, ["sink"], true, [
|
|
10192
|
+
{ token: "EXFIL", node_index: 0 }
|
|
10193
|
+
])
|
|
10122
10194
|
});
|
|
10123
10195
|
if (witness !== void 0) return violations;
|
|
10124
10196
|
}
|
|
@@ -10383,7 +10455,7 @@ var init_stateful = __esm({
|
|
|
10383
10455
|
for (const s of sessions || []) {
|
|
10384
10456
|
const sid = String(s.sessionId || "s");
|
|
10385
10457
|
for (const a of s.actionHistory || []) {
|
|
10386
|
-
const node = this.appendNode(a, sid);
|
|
10458
|
+
const node = this.appendNode(a, sid, false, s.agentId || "", s.instanceId || "");
|
|
10387
10459
|
if (!node) {
|
|
10388
10460
|
continue;
|
|
10389
10461
|
}
|
|
@@ -10397,8 +10469,8 @@ var init_stateful = __esm({
|
|
|
10397
10469
|
}
|
|
10398
10470
|
/** 增量推进:把当前动作追加到时间线 → 链指针推进 → 判定。 */
|
|
10399
10471
|
advance(args) {
|
|
10400
|
-
const { aNext, sessionId = "current", isError = false, agentId = "" } = args;
|
|
10401
|
-
const node = this.appendNode(aNext, sessionId, isError, agentId);
|
|
10472
|
+
const { aNext, sessionId = "current", isError = false, agentId = "", instanceId = "" } = args;
|
|
10473
|
+
const node = this.appendNode(aNext, sessionId, isError, agentId, instanceId);
|
|
10402
10474
|
if (!node) {
|
|
10403
10475
|
return { seq: this.seqNext, node: null, matched: null, pendingPrefixes: this.pendingPrefixes, exfil: [], shouldBlock: false };
|
|
10404
10476
|
}
|
|
@@ -10418,7 +10490,7 @@ var init_stateful = __esm({
|
|
|
10418
10490
|
}
|
|
10419
10491
|
};
|
|
10420
10492
|
}
|
|
10421
|
-
appendNode(action, sessionId, isError = false, agentId = "") {
|
|
10493
|
+
appendNode(action, sessionId, isError = false, agentId = "", instanceId = "") {
|
|
10422
10494
|
const ad = String(action.actionDetail || "");
|
|
10423
10495
|
const at = String(action.actionType || "");
|
|
10424
10496
|
if (!ad) return null;
|
|
@@ -10436,6 +10508,7 @@ var init_stateful = __esm({
|
|
|
10436
10508
|
instances: extractInstances(ad, stage),
|
|
10437
10509
|
isError,
|
|
10438
10510
|
agentId: String(action.agentId || agentId || ""),
|
|
10511
|
+
instanceId: String(action.instanceId || instanceId || ""),
|
|
10439
10512
|
lifecycle: lifecycleFor(ad, stage, isError),
|
|
10440
10513
|
timestamp: typeof action.calledAt === "string" ? action.calledAt : _StatefulChainTracker.now()
|
|
10441
10514
|
};
|
|
@@ -10579,7 +10652,8 @@ var init_stateful = __esm({
|
|
|
10579
10652
|
seq: n.seq,
|
|
10580
10653
|
stage: n.stage,
|
|
10581
10654
|
timestamp: n.timestamp,
|
|
10582
|
-
agentId: n.agentId
|
|
10655
|
+
agentId: n.agentId,
|
|
10656
|
+
instanceId: n.instanceId
|
|
10583
10657
|
});
|
|
10584
10658
|
const idx = stages.indexOf(n.stage);
|
|
10585
10659
|
if (idx + 1 < stages.length) {
|
|
@@ -10602,7 +10676,8 @@ var init_stateful = __esm({
|
|
|
10602
10676
|
stage: p.stage,
|
|
10603
10677
|
action_detail: n ? n.actionDetail : "",
|
|
10604
10678
|
resources: n ? [...n.resources].sort() : [],
|
|
10605
|
-
role: stages[i] || ""
|
|
10679
|
+
role: stages[i] || "",
|
|
10680
|
+
instance_id: p.instanceId || ""
|
|
10606
10681
|
};
|
|
10607
10682
|
});
|
|
10608
10683
|
const gEdges = [];
|
|
@@ -10670,7 +10745,7 @@ var init_stateful = __esm({
|
|
|
10670
10745
|
risk_level: "critical",
|
|
10671
10746
|
path: chain.map((j) => {
|
|
10672
10747
|
const n = this.nodes_[j];
|
|
10673
|
-
return [n.sessionId, n.seq, n.actionDetail.slice(0, 60), n.timestamp];
|
|
10748
|
+
return [n.sessionId, n.seq, n.actionDetail.slice(0, 60), n.timestamp, null, n.instanceId];
|
|
10674
10749
|
}),
|
|
10675
10750
|
graph: this.taintGraph(chain)
|
|
10676
10751
|
}];
|
|
@@ -10702,7 +10777,8 @@ var init_stateful = __esm({
|
|
|
10702
10777
|
stage: n.stage,
|
|
10703
10778
|
action_detail: n.actionDetail,
|
|
10704
10779
|
resources: [...n.resources].sort(),
|
|
10705
|
-
role
|
|
10780
|
+
role,
|
|
10781
|
+
instance_id: n.instanceId
|
|
10706
10782
|
});
|
|
10707
10783
|
if (k > 0) {
|
|
10708
10784
|
const prev = chain[k - 1];
|
|
@@ -10774,6 +10850,8 @@ var init_stateful = __esm({
|
|
|
10774
10850
|
seq: Number(raw.seq || 0),
|
|
10775
10851
|
sessionId: String(raw.sessionId || ""),
|
|
10776
10852
|
agentId: String(raw.agentId || ""),
|
|
10853
|
+
instanceId: String(raw.instanceId || ""),
|
|
10854
|
+
// 旧快照无此字段 → 空
|
|
10777
10855
|
actionType: String(raw.actionType || ""),
|
|
10778
10856
|
actionDetail: String(raw.actionDetail || ""),
|
|
10779
10857
|
stage: String(raw.stage || ""),
|
|
@@ -10918,9 +10996,9 @@ var init_chain_state = __esm({
|
|
|
10918
10996
|
delete this.data[agentId];
|
|
10919
10997
|
this._scheduleWrite();
|
|
10920
10998
|
}
|
|
10921
|
-
applyAction(agentId, aNext, sessionId, isError = false, sourceAgent = "") {
|
|
10999
|
+
applyAction(agentId, aNext, sessionId, isError = false, sourceAgent = "", instanceId = "") {
|
|
10922
11000
|
const t = this.load(agentId) || new StatefulChainTracker(agentId);
|
|
10923
|
-
const r = t.advance({ aNext, sessionId, isError, agentId: sourceAgent });
|
|
11001
|
+
const r = t.advance({ aNext, sessionId, isError, agentId: sourceAgent, instanceId });
|
|
10924
11002
|
this.save(t);
|
|
10925
11003
|
return r;
|
|
10926
11004
|
}
|
|
@@ -11238,12 +11316,21 @@ async function runAnalysis(input) {
|
|
|
11238
11316
|
formalSessions.push({
|
|
11239
11317
|
sessionId: s.sessionId || "s",
|
|
11240
11318
|
...s.agentId ? { agentId: s.agentId } : {},
|
|
11319
|
+
...s.instanceId ? { instanceId: s.instanceId } : {},
|
|
11241
11320
|
actionHistory: s.actionHistory || []
|
|
11242
11321
|
});
|
|
11243
11322
|
}
|
|
11244
11323
|
}
|
|
11245
|
-
formalSessions.push({
|
|
11246
|
-
|
|
11324
|
+
formalSessions.push({
|
|
11325
|
+
sessionId: "current",
|
|
11326
|
+
agentId: input.agentId,
|
|
11327
|
+
instanceId: input.instanceId || "",
|
|
11328
|
+
actionHistory: input.actionHistory
|
|
11329
|
+
});
|
|
11330
|
+
const formalResult = formalEngine.verify(formalSessions, {
|
|
11331
|
+
...input.aNext,
|
|
11332
|
+
instanceId: input.instanceId || ""
|
|
11333
|
+
});
|
|
11247
11334
|
if (!formalResult.verified) {
|
|
11248
11335
|
const v = formalResult.violations[0];
|
|
11249
11336
|
if (isFormalObserveMode()) {
|
|
@@ -12327,6 +12414,9 @@ async function analyzeRoute(c) {
|
|
|
12327
12414
|
`[AgentMoss] \u26A0\uFE0F \u8DE8\u4F1A\u8BDD\u6279\u91CF\u8BBF\u95EE: key=${_sweepKey} \u7A97\u53E3\u5185 ${_sweepN} \u4E2A\u4E0D\u540C session\uFF08\u9608\u503C ${sessionSweepThreshold()}\uFF09`
|
|
12328
12415
|
);
|
|
12329
12416
|
}
|
|
12417
|
+
const instanceId = String(
|
|
12418
|
+
req.instance_id || req.metadata?.instance_id || ""
|
|
12419
|
+
);
|
|
12330
12420
|
const result = await analyze({
|
|
12331
12421
|
sessionId: req.session_id,
|
|
12332
12422
|
promptSession: req.prompt_session,
|
|
@@ -12342,8 +12432,9 @@ async function analyzeRoute(c) {
|
|
|
12342
12432
|
})),
|
|
12343
12433
|
sessionHistory: (req.session_history || []).map((s) => ({
|
|
12344
12434
|
sessionId: s.session_id,
|
|
12345
|
-
// 来源 agent
|
|
12435
|
+
// 来源 agent/实例(AgentGate 逐会话带上,用于链路径取证/展示脱敏;不参与链匹配)
|
|
12346
12436
|
...s.agent_id ? { agentId: s.agent_id } : {},
|
|
12437
|
+
...s.instance_id ? { instanceId: s.instance_id } : {},
|
|
12347
12438
|
actionHistory: (s.action_history || []).map((h) => ({
|
|
12348
12439
|
actionType: h.action_type,
|
|
12349
12440
|
actionDetail: h.action_detail,
|
|
@@ -12367,11 +12458,13 @@ async function analyzeRoute(c) {
|
|
|
12367
12458
|
})),
|
|
12368
12459
|
aNext: {
|
|
12369
12460
|
actionType: req.a_next.action_type,
|
|
12370
|
-
actionDetail: req.a_next.action_detail
|
|
12461
|
+
actionDetail: req.a_next.action_detail,
|
|
12462
|
+
instanceId
|
|
12371
12463
|
},
|
|
12372
12464
|
reason: req.reason,
|
|
12373
12465
|
osType,
|
|
12374
12466
|
cwd: req.cwd,
|
|
12467
|
+
instanceId,
|
|
12375
12468
|
// agent_id:AgentGate V1 normalizer 历史上把 agent_type 塞 metadata,
|
|
12376
12469
|
// 顶层为空时兜底(OpenDesk 的 agent-moss 需 agent_id 加载专属规则)。
|
|
12377
12470
|
agentId,
|
|
@@ -12407,8 +12500,9 @@ async function analyzeRoute(c) {
|
|
|
12407
12500
|
{ actionType: req.a_next.action_type, actionDetail: req.a_next.action_detail },
|
|
12408
12501
|
req.session_id,
|
|
12409
12502
|
Boolean(meta.is_error),
|
|
12410
|
-
agentId
|
|
12503
|
+
agentId,
|
|
12411
12504
|
// 来源:真实 agent(桶 key 可能是 __system__)
|
|
12505
|
+
instanceId
|
|
12412
12506
|
);
|
|
12413
12507
|
const judgment = incMatchedToDeny(inc);
|
|
12414
12508
|
if (judgment) {
|
|
@@ -12429,8 +12523,8 @@ async function analyzeRoute(c) {
|
|
|
12429
12523
|
confidence: 90,
|
|
12430
12524
|
analysis_duration_ms: Math.round((performance.now() - t0) * 10) / 10,
|
|
12431
12525
|
// 跨会话节点脱敏(时间线按 agent_id 共享,原样返回等于泄露别人的命令原文)
|
|
12432
|
-
violation_chain: redactChain(judgment.violation_chain, req.session_id),
|
|
12433
|
-
violation_graph: redactGraph(judgment.violation_graph, req.session_id)
|
|
12526
|
+
violation_chain: redactChain(judgment.violation_chain, instanceId, req.session_id),
|
|
12527
|
+
violation_graph: redactGraph(judgment.violation_graph, instanceId, req.session_id)
|
|
12434
12528
|
})
|
|
12435
12529
|
);
|
|
12436
12530
|
}
|
|
@@ -12452,7 +12546,7 @@ async function analyzeRoute(c) {
|
|
|
12452
12546
|
confidence: result.confidence,
|
|
12453
12547
|
analysis_duration_ms: Math.round(durationMs * 10) / 10,
|
|
12454
12548
|
violation_chain: result.violation_chain ?? [],
|
|
12455
|
-
violation_graph: redactGraph(result.violation_graph, req.session_id)
|
|
12549
|
+
violation_graph: redactGraph(result.violation_graph, instanceId, req.session_id)
|
|
12456
12550
|
})
|
|
12457
12551
|
);
|
|
12458
12552
|
} catch (err) {
|
|
@@ -81,6 +81,7 @@ interface AnalysisInput {
|
|
|
81
81
|
* 由调用方(含未来 agentgate 非侵入式采集)提供。 */
|
|
82
82
|
sessionHistory?: {
|
|
83
83
|
sessionId?: string;
|
|
84
|
+
instanceId?: string;
|
|
84
85
|
actionHistory: {
|
|
85
86
|
actionType: string;
|
|
86
87
|
actionDetail: string;
|
|
@@ -90,12 +91,15 @@ interface AnalysisInput {
|
|
|
90
91
|
aNext: {
|
|
91
92
|
actionType: string;
|
|
92
93
|
actionDetail: string;
|
|
94
|
+
instanceId?: string;
|
|
93
95
|
};
|
|
94
96
|
reason: string;
|
|
95
97
|
osType: string;
|
|
96
98
|
cwd: string;
|
|
97
99
|
/** 调用方 agent 标识(如 'xiaoo' / 'opencode' / 'opendesk')。空 = 通用模式。 */
|
|
98
100
|
agentId: string;
|
|
101
|
+
/** 调用方实例标识(安装级)。仅用于展示层脱敏:同实例节点不隐藏。 */
|
|
102
|
+
instanceId?: string;
|
|
99
103
|
/** 调用方场景标识(如 'bank'),用于加载场景定制规则(客户沙箱的目录访问边界/禁 ssh 等)。
|
|
100
104
|
* 空时兜底环境变量 AGENT_MOSS_SCENARIO(部署级,客户沙箱设一次,所有请求生效)。 */
|
|
101
105
|
scenarioId?: string;
|
|
@@ -8369,7 +8369,7 @@ function markIntegrity(actionDetail) {
|
|
|
8369
8369
|
}
|
|
8370
8370
|
return { integrity: "medium", isLowSource: false };
|
|
8371
8371
|
}
|
|
8372
|
-
function initNode(sessionId, seq, actionType, actionDetail, timestamp, agentId = "") {
|
|
8372
|
+
function initNode(sessionId, seq, actionType, actionDetail, timestamp, agentId = "", instanceId = "") {
|
|
8373
8373
|
const { integrity, isLowSource } = markIntegrity(actionDetail);
|
|
8374
8374
|
return {
|
|
8375
8375
|
sessionId,
|
|
@@ -8378,13 +8378,14 @@ function initNode(sessionId, seq, actionType, actionDetail, timestamp, agentId =
|
|
|
8378
8378
|
actionDetail,
|
|
8379
8379
|
stage: markStage(actionDetail),
|
|
8380
8380
|
agentId: agentId || void 0,
|
|
8381
|
+
instanceId: instanceId || void 0,
|
|
8381
8382
|
resources: extractResources(actionDetail),
|
|
8382
8383
|
integrity,
|
|
8383
8384
|
isLowSource,
|
|
8384
8385
|
timestamp: timestamp || void 0
|
|
8385
8386
|
};
|
|
8386
8387
|
}
|
|
8387
|
-
function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId = "") {
|
|
8388
|
+
function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId = "", instanceId = "") {
|
|
8388
8389
|
const nodes = [];
|
|
8389
8390
|
const edges = [];
|
|
8390
8391
|
const stageFlags = /* @__PURE__ */ new Set();
|
|
@@ -8395,7 +8396,8 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId
|
|
|
8395
8396
|
actionType: String(a.actionType || ""),
|
|
8396
8397
|
actionDetail: ad,
|
|
8397
8398
|
calledAt: a.calledAt || void 0,
|
|
8398
|
-
agentId: a.agentId || agentId || void 0
|
|
8399
|
+
agentId: a.agentId || agentId || void 0,
|
|
8400
|
+
instanceId: a.instanceId || instanceId || void 0
|
|
8399
8401
|
});
|
|
8400
8402
|
}
|
|
8401
8403
|
if (aNext) {
|
|
@@ -8403,11 +8405,12 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId
|
|
|
8403
8405
|
actionType: String(aNext.actionType || ""),
|
|
8404
8406
|
actionDetail: String(aNext.actionDetail || ""),
|
|
8405
8407
|
calledAt: aNext.calledAt || void 0,
|
|
8406
|
-
agentId: aNext.agentId || agentId || void 0
|
|
8408
|
+
agentId: aNext.agentId || agentId || void 0,
|
|
8409
|
+
instanceId: aNext.instanceId || instanceId || void 0
|
|
8407
8410
|
});
|
|
8408
8411
|
}
|
|
8409
8412
|
details.forEach((d, seq) => {
|
|
8410
|
-
const node = initNode(sessionId, seq, d.actionType, d.actionDetail, d.calledAt, d.agentId || "");
|
|
8413
|
+
const node = initNode(sessionId, seq, d.actionType, d.actionDetail, d.calledAt, d.agentId || "", d.instanceId || "");
|
|
8411
8414
|
nodes.push(node);
|
|
8412
8415
|
if (node.stage) stageFlags.add(node.stage);
|
|
8413
8416
|
});
|
|
@@ -8419,12 +8422,18 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId
|
|
|
8419
8422
|
function buildMultiSessionGraph(sessions, aNext) {
|
|
8420
8423
|
const mg = { sessions: [], crossEdges: [] };
|
|
8421
8424
|
if (!sessions || sessions.length === 0) {
|
|
8422
|
-
mg.sessions.push(buildSessionGraph([], aNext, "current"));
|
|
8425
|
+
mg.sessions.push(buildSessionGraph([], aNext, "current", "", aNext?.instanceId || ""));
|
|
8423
8426
|
return mg;
|
|
8424
8427
|
}
|
|
8425
8428
|
for (const s of sessions) {
|
|
8426
8429
|
const sid = String(s.sessionId || "s");
|
|
8427
|
-
mg.sessions.push(buildSessionGraph(
|
|
8430
|
+
mg.sessions.push(buildSessionGraph(
|
|
8431
|
+
s.actionHistory || [],
|
|
8432
|
+
void 0,
|
|
8433
|
+
sid,
|
|
8434
|
+
s.agentId || "",
|
|
8435
|
+
s.instanceId || ""
|
|
8436
|
+
));
|
|
8428
8437
|
}
|
|
8429
8438
|
if (aNext) {
|
|
8430
8439
|
const last = mg.sessions[mg.sessions.length - 1];
|
|
@@ -8435,13 +8444,14 @@ function buildMultiSessionGraph(sessions, aNext) {
|
|
|
8435
8444
|
String(aNext.actionType || ""),
|
|
8436
8445
|
String(aNext.actionDetail || ""),
|
|
8437
8446
|
aNext.calledAt || void 0,
|
|
8438
|
-
aNext.agentId || ""
|
|
8447
|
+
aNext.agentId || "",
|
|
8448
|
+
aNext.instanceId || ""
|
|
8439
8449
|
));
|
|
8440
8450
|
if (last.nodes.length > 1) {
|
|
8441
8451
|
last.edges.push({ src: last.nodes.length - 2, dst: last.nodes.length - 1, kind: "next" });
|
|
8442
8452
|
}
|
|
8443
8453
|
} else {
|
|
8444
|
-
mg.sessions.push(buildSessionGraph([], aNext, "current"));
|
|
8454
|
+
mg.sessions.push(buildSessionGraph([], aNext, "current", "", aNext.instanceId || ""));
|
|
8445
8455
|
}
|
|
8446
8456
|
}
|
|
8447
8457
|
addCrossDataEdges(mg);
|
|
@@ -8495,7 +8505,8 @@ function graphFromPath(nodes, path5, edgeType = "next", propertyMeta, roles, wit
|
|
|
8495
8505
|
seq: item.seq,
|
|
8496
8506
|
stage: n ? n.stage : "",
|
|
8497
8507
|
action_detail: n ? n.actionDetail : item.detail,
|
|
8498
|
-
resources: n ? [...n.resources].sort() : []
|
|
8508
|
+
resources: n ? [...n.resources].sort() : [],
|
|
8509
|
+
instance_id: n ? n.instanceId || "" : ""
|
|
8499
8510
|
});
|
|
8500
8511
|
if (gNodes.length > 1) {
|
|
8501
8512
|
gEdges.push({ from: gNodes.length - 2, to: gNodes.length - 1, type: edgeType });
|
|
@@ -8666,7 +8677,8 @@ function checkSensitiveDataFlow(graph) {
|
|
|
8666
8677
|
stage: n.stage,
|
|
8667
8678
|
action_detail: n.actionDetail,
|
|
8668
8679
|
resources: [...n.resources].sort(),
|
|
8669
|
-
role
|
|
8680
|
+
role,
|
|
8681
|
+
instance_id: n.instanceId || ""
|
|
8670
8682
|
});
|
|
8671
8683
|
if (k > 0) {
|
|
8672
8684
|
const prev = chain[k - 1];
|
|
@@ -8824,14 +8836,23 @@ function checkNeverExfilAfterPrivesc(graph) {
|
|
|
8824
8836
|
if (candidates.length > 0) {
|
|
8825
8837
|
const pre = nodes[candidates[candidates.length - 1]];
|
|
8826
8838
|
const post = nodes[firstEv];
|
|
8839
|
+
const path5 = [
|
|
8840
|
+
{ sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
|
|
8841
|
+
{ sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
|
|
8842
|
+
];
|
|
8827
8843
|
return [{
|
|
8828
8844
|
propertyId: "no-exfil-after-privesc",
|
|
8829
8845
|
reason: "\u63D0\u6743\u540E\u51FA\u73B0\u5916\u4F20\uFF1A\u63D0\u6743(\u524D) \u2192 \u5916\u4F20(\u540E)\uFF0C\u8FDD\u53CD \u25A1(PRIVESC\u2192\u25C7EXFIL) \u7A97\u53E3\u7EA6\u675F",
|
|
8830
8846
|
riskLevel: "critical",
|
|
8831
|
-
path:
|
|
8832
|
-
|
|
8833
|
-
|
|
8834
|
-
|
|
8847
|
+
path: path5,
|
|
8848
|
+
graph: graphFromPath(nodes, path5, "next", {
|
|
8849
|
+
property_id: "no-exfil-after-privesc",
|
|
8850
|
+
property_short: "P4",
|
|
8851
|
+
property_name: "\u63D0\u6743\u540E\u7981\u6B62\u5916\u4F20"
|
|
8852
|
+
}, ["privesc", "sink"], true, [
|
|
8853
|
+
{ token: "PRIVESC", node_index: 0 },
|
|
8854
|
+
{ token: "EXFIL", node_index: 1 }
|
|
8855
|
+
])
|
|
8835
8856
|
}];
|
|
8836
8857
|
}
|
|
8837
8858
|
return violations;
|
|
@@ -8841,14 +8862,23 @@ function checkNeverExfilAfterPrivesc(graph) {
|
|
|
8841
8862
|
for (let j = i + 1; j < nodes.length; j++) {
|
|
8842
8863
|
if (!isExfilNode(nodes[j])) continue;
|
|
8843
8864
|
if (privescInWindow(nodes, [i], j, windowSec).length === 0) continue;
|
|
8865
|
+
const path5 = [
|
|
8866
|
+
{ sessionId: nodes[i].sessionId, seq: nodes[i].seq, detail: nodes[i].actionDetail.slice(0, 60) },
|
|
8867
|
+
{ sessionId: nodes[j].sessionId, seq: nodes[j].seq, detail: nodes[j].actionDetail.slice(0, 60) }
|
|
8868
|
+
];
|
|
8844
8869
|
violations.push({
|
|
8845
8870
|
propertyId: "no-exfil-after-privesc",
|
|
8846
8871
|
reason: "\u63D0\u6743\u540E\u51FA\u73B0\u5916\u4F20\uFF1A\u63D0\u6743(\u524D) \u2192 \u5916\u4F20(\u540E)\uFF0C\u8FDD\u53CD \u25A1(PRIVESC\u2192\u25C7EXFIL) \u7A97\u53E3\u7EA6\u675F",
|
|
8847
8872
|
riskLevel: "critical",
|
|
8848
|
-
path:
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8873
|
+
path: path5,
|
|
8874
|
+
graph: graphFromPath(nodes, path5, "next", {
|
|
8875
|
+
property_id: "no-exfil-after-privesc",
|
|
8876
|
+
property_short: "P4",
|
|
8877
|
+
property_name: "\u63D0\u6743\u540E\u7981\u6B62\u5916\u4F20"
|
|
8878
|
+
}, ["privesc", "sink"], true, [
|
|
8879
|
+
{ token: "PRIVESC", node_index: 0 },
|
|
8880
|
+
{ token: "EXFIL", node_index: 1 }
|
|
8881
|
+
])
|
|
8852
8882
|
});
|
|
8853
8883
|
return violations;
|
|
8854
8884
|
}
|
|
@@ -8873,14 +8903,23 @@ function checkNeverExfilAfterCred(graph) {
|
|
|
8873
8903
|
}
|
|
8874
8904
|
if (pre) {
|
|
8875
8905
|
const post = nodes[firstEv];
|
|
8906
|
+
const path5 = [
|
|
8907
|
+
{ sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
|
|
8908
|
+
{ sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
|
|
8909
|
+
];
|
|
8876
8910
|
return [{
|
|
8877
8911
|
propertyId: "no-exfil-after-credential",
|
|
8878
8912
|
reason: "\u8BFB\u53D6\u51ED\u636E\u540E\u51FA\u73B0\u5916\u4F20\uFF0C\u8FDD\u53CD \u25A1(CRED\u2192\xAC\u25C7EXFIL)",
|
|
8879
8913
|
riskLevel: "critical",
|
|
8880
|
-
path:
|
|
8881
|
-
|
|
8882
|
-
|
|
8883
|
-
|
|
8914
|
+
path: path5,
|
|
8915
|
+
graph: graphFromPath(nodes, path5, "data", {
|
|
8916
|
+
property_id: "no-exfil-after-credential",
|
|
8917
|
+
property_short: "P5",
|
|
8918
|
+
property_name: "\u8BFB\u53D6\u51ED\u636E\u540E\u7981\u6B62\u5916\u4F20"
|
|
8919
|
+
}, ["source", "sink"], true, [
|
|
8920
|
+
{ token: "CRED", node_index: 0 },
|
|
8921
|
+
{ token: "EXFIL", node_index: 1 }
|
|
8922
|
+
])
|
|
8884
8923
|
}];
|
|
8885
8924
|
}
|
|
8886
8925
|
return violations;
|
|
@@ -8889,14 +8928,23 @@ function checkNeverExfilAfterCred(graph) {
|
|
|
8889
8928
|
if (!isCredNode(nodes[i])) continue;
|
|
8890
8929
|
for (let j = i + 1; j < nodes.length; j++) {
|
|
8891
8930
|
if (isExfilNode(nodes[j])) {
|
|
8931
|
+
const path5 = [
|
|
8932
|
+
{ sessionId: nodes[i].sessionId, seq: nodes[i].seq, detail: nodes[i].actionDetail.slice(0, 60) },
|
|
8933
|
+
{ sessionId: nodes[j].sessionId, seq: nodes[j].seq, detail: nodes[j].actionDetail.slice(0, 60) }
|
|
8934
|
+
];
|
|
8892
8935
|
violations.push({
|
|
8893
8936
|
propertyId: "no-exfil-after-credential",
|
|
8894
8937
|
reason: "\u8BFB\u53D6\u51ED\u636E\u540E\u51FA\u73B0\u5916\u4F20\uFF0C\u8FDD\u53CD \u25A1(CRED\u2192\xAC\u25C7EXFIL)",
|
|
8895
8938
|
riskLevel: "critical",
|
|
8896
|
-
path:
|
|
8897
|
-
|
|
8898
|
-
|
|
8899
|
-
|
|
8939
|
+
path: path5,
|
|
8940
|
+
graph: graphFromPath(nodes, path5, "data", {
|
|
8941
|
+
property_id: "no-exfil-after-credential",
|
|
8942
|
+
property_short: "P5",
|
|
8943
|
+
property_name: "\u8BFB\u53D6\u51ED\u636E\u540E\u7981\u6B62\u5916\u4F20"
|
|
8944
|
+
}, ["source", "sink"], true, [
|
|
8945
|
+
{ token: "CRED", node_index: 0 },
|
|
8946
|
+
{ token: "EXFIL", node_index: 1 }
|
|
8947
|
+
])
|
|
8900
8948
|
});
|
|
8901
8949
|
return violations;
|
|
8902
8950
|
}
|
|
@@ -9053,11 +9101,19 @@ function checkSensitiveDataAggregation(graph) {
|
|
|
9053
9101
|
if (!isExfilNode(n)) continue;
|
|
9054
9102
|
const srcs = sourcesFor(i);
|
|
9055
9103
|
if (srcs.size >= 2) {
|
|
9104
|
+
const path5 = [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }];
|
|
9056
9105
|
violations.push({
|
|
9057
9106
|
propertyId: "sensitive-data-aggregation",
|
|
9058
9107
|
reason: `\u654F\u611F\u6570\u636E\u805A\u5408\u5916\u4F20\uFF1Aegress \u805A\u5408 ${srcs.size} \u4E2A\u4E8B\u4EF6\u6E90\u6570\u636E\uFF08Rule 12\uFF09`,
|
|
9059
9108
|
riskLevel: "critical",
|
|
9060
|
-
path:
|
|
9109
|
+
path: path5,
|
|
9110
|
+
graph: graphFromPath(nodes, path5, "next", {
|
|
9111
|
+
property_id: "sensitive-data-aggregation",
|
|
9112
|
+
property_short: "P8",
|
|
9113
|
+
property_name: "\u654F\u611F\u6570\u636E\u805A\u5408\u5916\u4F20"
|
|
9114
|
+
}, ["sink"], true, [
|
|
9115
|
+
{ token: "EXFIL", node_index: 0 }
|
|
9116
|
+
])
|
|
9061
9117
|
});
|
|
9062
9118
|
if (witness !== void 0) return violations;
|
|
9063
9119
|
}
|
|
@@ -9434,12 +9490,21 @@ async function runAnalysis(input) {
|
|
|
9434
9490
|
formalSessions.push({
|
|
9435
9491
|
sessionId: s.sessionId || "s",
|
|
9436
9492
|
...s.agentId ? { agentId: s.agentId } : {},
|
|
9493
|
+
...s.instanceId ? { instanceId: s.instanceId } : {},
|
|
9437
9494
|
actionHistory: s.actionHistory || []
|
|
9438
9495
|
});
|
|
9439
9496
|
}
|
|
9440
9497
|
}
|
|
9441
|
-
formalSessions.push({
|
|
9442
|
-
|
|
9498
|
+
formalSessions.push({
|
|
9499
|
+
sessionId: "current",
|
|
9500
|
+
agentId: input.agentId,
|
|
9501
|
+
instanceId: input.instanceId || "",
|
|
9502
|
+
actionHistory: input.actionHistory
|
|
9503
|
+
});
|
|
9504
|
+
const formalResult = formalEngine.verify(formalSessions, {
|
|
9505
|
+
...input.aNext,
|
|
9506
|
+
instanceId: input.instanceId || ""
|
|
9507
|
+
});
|
|
9443
9508
|
if (!formalResult.verified) {
|
|
9444
9509
|
const v = formalResult.violations[0];
|
|
9445
9510
|
if (isFormalObserveMode()) {
|
package/dist/server.js
CHANGED
|
@@ -1092,31 +1092,42 @@ function hardenExistingPaths() {
|
|
|
1092
1092
|
function chainDetailMode() {
|
|
1093
1093
|
return (env("AGENT_MOSS_CHAIN_DETAIL", "redacted") || "redacted").toLowerCase();
|
|
1094
1094
|
}
|
|
1095
|
-
function redactChain(chain, currentSessionId) {
|
|
1095
|
+
function redactChain(chain, currentInstanceId = "", currentSessionId = "") {
|
|
1096
1096
|
if (!chain || chain.length === 0) return [];
|
|
1097
1097
|
if (chainDetailMode() === "full") return [...chain];
|
|
1098
1098
|
return chain.map((step) => {
|
|
1099
1099
|
if (Array.isArray(step) && step.length >= 3) {
|
|
1100
1100
|
const [sid, seq, , ts] = step;
|
|
1101
|
-
|
|
1101
|
+
const inst = step.length > 5 ? String(step[5] ?? "") : "";
|
|
1102
|
+
const sameInstance = !!(inst && currentInstanceId && inst === String(currentInstanceId));
|
|
1103
|
+
const sameSessionFallback = !inst && !!currentSessionId && String(sid) === String(currentSessionId);
|
|
1104
|
+
if (sameInstance || sameSessionFallback) return [...step];
|
|
1102
1105
|
return ["<other-session>", seq, "<\u5DF2\u9690\u85CF>", ts ?? ""];
|
|
1103
1106
|
}
|
|
1104
1107
|
return step;
|
|
1105
1108
|
});
|
|
1106
1109
|
}
|
|
1107
|
-
function redactGraph(graph, currentSessionId) {
|
|
1110
|
+
function redactGraph(graph, currentInstanceId = "", currentSessionId = "") {
|
|
1108
1111
|
if (!graph) return null;
|
|
1109
1112
|
if (chainDetailMode() === "full") return graph;
|
|
1110
1113
|
const nodes = (graph.nodes || []).map((n) => {
|
|
1111
1114
|
const m = { ...n };
|
|
1112
|
-
|
|
1115
|
+
const inst = String(m.instance_id ?? "") || "";
|
|
1116
|
+
const sid = String(m.session_id ?? "") || "";
|
|
1117
|
+
const sameInstance = !!(inst && currentInstanceId && inst === String(currentInstanceId));
|
|
1118
|
+
const sameSessionFallback = !inst && !!currentSessionId && sid === String(currentSessionId);
|
|
1119
|
+
if (!(sameInstance || sameSessionFallback)) {
|
|
1113
1120
|
m.session_id = "<other-session>";
|
|
1114
1121
|
m.action_detail = "<\u5DF2\u9690\u85CF>";
|
|
1115
1122
|
m.resources = [];
|
|
1116
1123
|
}
|
|
1117
1124
|
return m;
|
|
1118
1125
|
});
|
|
1119
|
-
|
|
1126
|
+
const out = { nodes, edges: graph.edges || [] };
|
|
1127
|
+
for (const key of ["property_id", "property_short", "property_name", "property_expression"]) {
|
|
1128
|
+
if (graph[key] !== void 0) out[key] = graph[key];
|
|
1129
|
+
}
|
|
1130
|
+
return out;
|
|
1120
1131
|
}
|
|
1121
1132
|
function sessionSweepThreshold() {
|
|
1122
1133
|
return envInt("AGENT_MOSS_SESSION_SWEEP_THRESHOLD", 50);
|
|
@@ -8884,7 +8895,7 @@ function markIntegrity(actionDetail) {
|
|
|
8884
8895
|
}
|
|
8885
8896
|
return { integrity: "medium", isLowSource: false };
|
|
8886
8897
|
}
|
|
8887
|
-
function initNode(sessionId, seq, actionType, actionDetail, timestamp, agentId = "") {
|
|
8898
|
+
function initNode(sessionId, seq, actionType, actionDetail, timestamp, agentId = "", instanceId = "") {
|
|
8888
8899
|
const { integrity, isLowSource } = markIntegrity(actionDetail);
|
|
8889
8900
|
return {
|
|
8890
8901
|
sessionId,
|
|
@@ -8893,13 +8904,14 @@ function initNode(sessionId, seq, actionType, actionDetail, timestamp, agentId =
|
|
|
8893
8904
|
actionDetail,
|
|
8894
8905
|
stage: markStage(actionDetail),
|
|
8895
8906
|
agentId: agentId || void 0,
|
|
8907
|
+
instanceId: instanceId || void 0,
|
|
8896
8908
|
resources: extractResources(actionDetail),
|
|
8897
8909
|
integrity,
|
|
8898
8910
|
isLowSource,
|
|
8899
8911
|
timestamp: timestamp || void 0
|
|
8900
8912
|
};
|
|
8901
8913
|
}
|
|
8902
|
-
function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId = "") {
|
|
8914
|
+
function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId = "", instanceId = "") {
|
|
8903
8915
|
const nodes = [];
|
|
8904
8916
|
const edges = [];
|
|
8905
8917
|
const stageFlags = /* @__PURE__ */ new Set();
|
|
@@ -8910,7 +8922,8 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId
|
|
|
8910
8922
|
actionType: String(a.actionType || ""),
|
|
8911
8923
|
actionDetail: ad,
|
|
8912
8924
|
calledAt: a.calledAt || void 0,
|
|
8913
|
-
agentId: a.agentId || agentId || void 0
|
|
8925
|
+
agentId: a.agentId || agentId || void 0,
|
|
8926
|
+
instanceId: a.instanceId || instanceId || void 0
|
|
8914
8927
|
});
|
|
8915
8928
|
}
|
|
8916
8929
|
if (aNext) {
|
|
@@ -8918,11 +8931,12 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId
|
|
|
8918
8931
|
actionType: String(aNext.actionType || ""),
|
|
8919
8932
|
actionDetail: String(aNext.actionDetail || ""),
|
|
8920
8933
|
calledAt: aNext.calledAt || void 0,
|
|
8921
|
-
agentId: aNext.agentId || agentId || void 0
|
|
8934
|
+
agentId: aNext.agentId || agentId || void 0,
|
|
8935
|
+
instanceId: aNext.instanceId || instanceId || void 0
|
|
8922
8936
|
});
|
|
8923
8937
|
}
|
|
8924
8938
|
details.forEach((d, seq) => {
|
|
8925
|
-
const node = initNode(sessionId, seq, d.actionType, d.actionDetail, d.calledAt, d.agentId || "");
|
|
8939
|
+
const node = initNode(sessionId, seq, d.actionType, d.actionDetail, d.calledAt, d.agentId || "", d.instanceId || "");
|
|
8926
8940
|
nodes.push(node);
|
|
8927
8941
|
if (node.stage) stageFlags.add(node.stage);
|
|
8928
8942
|
});
|
|
@@ -8934,12 +8948,18 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId
|
|
|
8934
8948
|
function buildMultiSessionGraph(sessions, aNext) {
|
|
8935
8949
|
const mg = { sessions: [], crossEdges: [] };
|
|
8936
8950
|
if (!sessions || sessions.length === 0) {
|
|
8937
|
-
mg.sessions.push(buildSessionGraph([], aNext, "current"));
|
|
8951
|
+
mg.sessions.push(buildSessionGraph([], aNext, "current", "", aNext?.instanceId || ""));
|
|
8938
8952
|
return mg;
|
|
8939
8953
|
}
|
|
8940
8954
|
for (const s of sessions) {
|
|
8941
8955
|
const sid = String(s.sessionId || "s");
|
|
8942
|
-
mg.sessions.push(buildSessionGraph(
|
|
8956
|
+
mg.sessions.push(buildSessionGraph(
|
|
8957
|
+
s.actionHistory || [],
|
|
8958
|
+
void 0,
|
|
8959
|
+
sid,
|
|
8960
|
+
s.agentId || "",
|
|
8961
|
+
s.instanceId || ""
|
|
8962
|
+
));
|
|
8943
8963
|
}
|
|
8944
8964
|
if (aNext) {
|
|
8945
8965
|
const last = mg.sessions[mg.sessions.length - 1];
|
|
@@ -8950,13 +8970,14 @@ function buildMultiSessionGraph(sessions, aNext) {
|
|
|
8950
8970
|
String(aNext.actionType || ""),
|
|
8951
8971
|
String(aNext.actionDetail || ""),
|
|
8952
8972
|
aNext.calledAt || void 0,
|
|
8953
|
-
aNext.agentId || ""
|
|
8973
|
+
aNext.agentId || "",
|
|
8974
|
+
aNext.instanceId || ""
|
|
8954
8975
|
));
|
|
8955
8976
|
if (last.nodes.length > 1) {
|
|
8956
8977
|
last.edges.push({ src: last.nodes.length - 2, dst: last.nodes.length - 1, kind: "next" });
|
|
8957
8978
|
}
|
|
8958
8979
|
} else {
|
|
8959
|
-
mg.sessions.push(buildSessionGraph([], aNext, "current"));
|
|
8980
|
+
mg.sessions.push(buildSessionGraph([], aNext, "current", "", aNext.instanceId || ""));
|
|
8960
8981
|
}
|
|
8961
8982
|
}
|
|
8962
8983
|
addCrossDataEdges(mg);
|
|
@@ -9020,7 +9041,8 @@ function graphFromPath(nodes, path6, edgeType = "next", propertyMeta, roles, wit
|
|
|
9020
9041
|
seq: item.seq,
|
|
9021
9042
|
stage: n ? n.stage : "",
|
|
9022
9043
|
action_detail: n ? n.actionDetail : item.detail,
|
|
9023
|
-
resources: n ? [...n.resources].sort() : []
|
|
9044
|
+
resources: n ? [...n.resources].sort() : [],
|
|
9045
|
+
instance_id: n ? n.instanceId || "" : ""
|
|
9024
9046
|
});
|
|
9025
9047
|
if (gNodes.length > 1) {
|
|
9026
9048
|
gEdges.push({ from: gNodes.length - 2, to: gNodes.length - 1, type: edgeType });
|
|
@@ -9188,7 +9210,8 @@ function checkSensitiveDataFlow(graph) {
|
|
|
9188
9210
|
stage: n.stage,
|
|
9189
9211
|
action_detail: n.actionDetail,
|
|
9190
9212
|
resources: [...n.resources].sort(),
|
|
9191
|
-
role
|
|
9213
|
+
role,
|
|
9214
|
+
instance_id: n.instanceId || ""
|
|
9192
9215
|
});
|
|
9193
9216
|
if (k > 0) {
|
|
9194
9217
|
const prev = chain[k - 1];
|
|
@@ -9338,14 +9361,23 @@ function checkNeverExfilAfterPrivesc(graph) {
|
|
|
9338
9361
|
if (candidates.length > 0) {
|
|
9339
9362
|
const pre = nodes[candidates[candidates.length - 1]];
|
|
9340
9363
|
const post = nodes[firstEv];
|
|
9364
|
+
const path6 = [
|
|
9365
|
+
{ sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
|
|
9366
|
+
{ sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
|
|
9367
|
+
];
|
|
9341
9368
|
return [{
|
|
9342
9369
|
propertyId: "no-exfil-after-privesc",
|
|
9343
9370
|
reason: "\u63D0\u6743\u540E\u51FA\u73B0\u5916\u4F20\uFF1A\u63D0\u6743(\u524D) \u2192 \u5916\u4F20(\u540E)\uFF0C\u8FDD\u53CD \u25A1(PRIVESC\u2192\u25C7EXFIL) \u7A97\u53E3\u7EA6\u675F",
|
|
9344
9371
|
riskLevel: "critical",
|
|
9345
|
-
path:
|
|
9346
|
-
|
|
9347
|
-
|
|
9348
|
-
|
|
9372
|
+
path: path6,
|
|
9373
|
+
graph: graphFromPath(nodes, path6, "next", {
|
|
9374
|
+
property_id: "no-exfil-after-privesc",
|
|
9375
|
+
property_short: "P4",
|
|
9376
|
+
property_name: "\u63D0\u6743\u540E\u7981\u6B62\u5916\u4F20"
|
|
9377
|
+
}, ["privesc", "sink"], true, [
|
|
9378
|
+
{ token: "PRIVESC", node_index: 0 },
|
|
9379
|
+
{ token: "EXFIL", node_index: 1 }
|
|
9380
|
+
])
|
|
9349
9381
|
}];
|
|
9350
9382
|
}
|
|
9351
9383
|
return violations;
|
|
@@ -9355,14 +9387,23 @@ function checkNeverExfilAfterPrivesc(graph) {
|
|
|
9355
9387
|
for (let j = i + 1; j < nodes.length; j++) {
|
|
9356
9388
|
if (!isExfilNode(nodes[j])) continue;
|
|
9357
9389
|
if (privescInWindow(nodes, [i], j, windowSec).length === 0) continue;
|
|
9390
|
+
const path6 = [
|
|
9391
|
+
{ sessionId: nodes[i].sessionId, seq: nodes[i].seq, detail: nodes[i].actionDetail.slice(0, 60) },
|
|
9392
|
+
{ sessionId: nodes[j].sessionId, seq: nodes[j].seq, detail: nodes[j].actionDetail.slice(0, 60) }
|
|
9393
|
+
];
|
|
9358
9394
|
violations.push({
|
|
9359
9395
|
propertyId: "no-exfil-after-privesc",
|
|
9360
9396
|
reason: "\u63D0\u6743\u540E\u51FA\u73B0\u5916\u4F20\uFF1A\u63D0\u6743(\u524D) \u2192 \u5916\u4F20(\u540E)\uFF0C\u8FDD\u53CD \u25A1(PRIVESC\u2192\u25C7EXFIL) \u7A97\u53E3\u7EA6\u675F",
|
|
9361
9397
|
riskLevel: "critical",
|
|
9362
|
-
path:
|
|
9363
|
-
|
|
9364
|
-
|
|
9365
|
-
|
|
9398
|
+
path: path6,
|
|
9399
|
+
graph: graphFromPath(nodes, path6, "next", {
|
|
9400
|
+
property_id: "no-exfil-after-privesc",
|
|
9401
|
+
property_short: "P4",
|
|
9402
|
+
property_name: "\u63D0\u6743\u540E\u7981\u6B62\u5916\u4F20"
|
|
9403
|
+
}, ["privesc", "sink"], true, [
|
|
9404
|
+
{ token: "PRIVESC", node_index: 0 },
|
|
9405
|
+
{ token: "EXFIL", node_index: 1 }
|
|
9406
|
+
])
|
|
9366
9407
|
});
|
|
9367
9408
|
return violations;
|
|
9368
9409
|
}
|
|
@@ -9387,14 +9428,23 @@ function checkNeverExfilAfterCred(graph) {
|
|
|
9387
9428
|
}
|
|
9388
9429
|
if (pre) {
|
|
9389
9430
|
const post = nodes[firstEv];
|
|
9431
|
+
const path6 = [
|
|
9432
|
+
{ sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
|
|
9433
|
+
{ sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
|
|
9434
|
+
];
|
|
9390
9435
|
return [{
|
|
9391
9436
|
propertyId: "no-exfil-after-credential",
|
|
9392
9437
|
reason: "\u8BFB\u53D6\u51ED\u636E\u540E\u51FA\u73B0\u5916\u4F20\uFF0C\u8FDD\u53CD \u25A1(CRED\u2192\xAC\u25C7EXFIL)",
|
|
9393
9438
|
riskLevel: "critical",
|
|
9394
|
-
path:
|
|
9395
|
-
|
|
9396
|
-
|
|
9397
|
-
|
|
9439
|
+
path: path6,
|
|
9440
|
+
graph: graphFromPath(nodes, path6, "data", {
|
|
9441
|
+
property_id: "no-exfil-after-credential",
|
|
9442
|
+
property_short: "P5",
|
|
9443
|
+
property_name: "\u8BFB\u53D6\u51ED\u636E\u540E\u7981\u6B62\u5916\u4F20"
|
|
9444
|
+
}, ["source", "sink"], true, [
|
|
9445
|
+
{ token: "CRED", node_index: 0 },
|
|
9446
|
+
{ token: "EXFIL", node_index: 1 }
|
|
9447
|
+
])
|
|
9398
9448
|
}];
|
|
9399
9449
|
}
|
|
9400
9450
|
return violations;
|
|
@@ -9403,14 +9453,23 @@ function checkNeverExfilAfterCred(graph) {
|
|
|
9403
9453
|
if (!isCredNode(nodes[i])) continue;
|
|
9404
9454
|
for (let j = i + 1; j < nodes.length; j++) {
|
|
9405
9455
|
if (isExfilNode(nodes[j])) {
|
|
9456
|
+
const path6 = [
|
|
9457
|
+
{ sessionId: nodes[i].sessionId, seq: nodes[i].seq, detail: nodes[i].actionDetail.slice(0, 60) },
|
|
9458
|
+
{ sessionId: nodes[j].sessionId, seq: nodes[j].seq, detail: nodes[j].actionDetail.slice(0, 60) }
|
|
9459
|
+
];
|
|
9406
9460
|
violations.push({
|
|
9407
9461
|
propertyId: "no-exfil-after-credential",
|
|
9408
9462
|
reason: "\u8BFB\u53D6\u51ED\u636E\u540E\u51FA\u73B0\u5916\u4F20\uFF0C\u8FDD\u53CD \u25A1(CRED\u2192\xAC\u25C7EXFIL)",
|
|
9409
9463
|
riskLevel: "critical",
|
|
9410
|
-
path:
|
|
9411
|
-
|
|
9412
|
-
|
|
9413
|
-
|
|
9464
|
+
path: path6,
|
|
9465
|
+
graph: graphFromPath(nodes, path6, "data", {
|
|
9466
|
+
property_id: "no-exfil-after-credential",
|
|
9467
|
+
property_short: "P5",
|
|
9468
|
+
property_name: "\u8BFB\u53D6\u51ED\u636E\u540E\u7981\u6B62\u5916\u4F20"
|
|
9469
|
+
}, ["source", "sink"], true, [
|
|
9470
|
+
{ token: "CRED", node_index: 0 },
|
|
9471
|
+
{ token: "EXFIL", node_index: 1 }
|
|
9472
|
+
])
|
|
9414
9473
|
});
|
|
9415
9474
|
return violations;
|
|
9416
9475
|
}
|
|
@@ -9562,11 +9621,19 @@ function checkSensitiveDataAggregation(graph) {
|
|
|
9562
9621
|
if (!isExfilNode(n)) continue;
|
|
9563
9622
|
const srcs = sourcesFor(i);
|
|
9564
9623
|
if (srcs.size >= 2) {
|
|
9624
|
+
const path6 = [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }];
|
|
9565
9625
|
violations.push({
|
|
9566
9626
|
propertyId: "sensitive-data-aggregation",
|
|
9567
9627
|
reason: `\u654F\u611F\u6570\u636E\u805A\u5408\u5916\u4F20\uFF1Aegress \u805A\u5408 ${srcs.size} \u4E2A\u4E8B\u4EF6\u6E90\u6570\u636E\uFF08Rule 12\uFF09`,
|
|
9568
9628
|
riskLevel: "critical",
|
|
9569
|
-
path:
|
|
9629
|
+
path: path6,
|
|
9630
|
+
graph: graphFromPath(nodes, path6, "next", {
|
|
9631
|
+
property_id: "sensitive-data-aggregation",
|
|
9632
|
+
property_short: "P8",
|
|
9633
|
+
property_name: "\u654F\u611F\u6570\u636E\u805A\u5408\u5916\u4F20"
|
|
9634
|
+
}, ["sink"], true, [
|
|
9635
|
+
{ token: "EXFIL", node_index: 0 }
|
|
9636
|
+
])
|
|
9570
9637
|
});
|
|
9571
9638
|
if (witness !== void 0) return violations;
|
|
9572
9639
|
}
|
|
@@ -9831,7 +9898,7 @@ var init_stateful = __esm({
|
|
|
9831
9898
|
for (const s of sessions || []) {
|
|
9832
9899
|
const sid = String(s.sessionId || "s");
|
|
9833
9900
|
for (const a of s.actionHistory || []) {
|
|
9834
|
-
const node = this.appendNode(a, sid);
|
|
9901
|
+
const node = this.appendNode(a, sid, false, s.agentId || "", s.instanceId || "");
|
|
9835
9902
|
if (!node) {
|
|
9836
9903
|
continue;
|
|
9837
9904
|
}
|
|
@@ -9845,8 +9912,8 @@ var init_stateful = __esm({
|
|
|
9845
9912
|
}
|
|
9846
9913
|
/** 增量推进:把当前动作追加到时间线 → 链指针推进 → 判定。 */
|
|
9847
9914
|
advance(args) {
|
|
9848
|
-
const { aNext, sessionId = "current", isError = false, agentId = "" } = args;
|
|
9849
|
-
const node = this.appendNode(aNext, sessionId, isError, agentId);
|
|
9915
|
+
const { aNext, sessionId = "current", isError = false, agentId = "", instanceId = "" } = args;
|
|
9916
|
+
const node = this.appendNode(aNext, sessionId, isError, agentId, instanceId);
|
|
9850
9917
|
if (!node) {
|
|
9851
9918
|
return { seq: this.seqNext, node: null, matched: null, pendingPrefixes: this.pendingPrefixes, exfil: [], shouldBlock: false };
|
|
9852
9919
|
}
|
|
@@ -9866,7 +9933,7 @@ var init_stateful = __esm({
|
|
|
9866
9933
|
}
|
|
9867
9934
|
};
|
|
9868
9935
|
}
|
|
9869
|
-
appendNode(action, sessionId, isError = false, agentId = "") {
|
|
9936
|
+
appendNode(action, sessionId, isError = false, agentId = "", instanceId = "") {
|
|
9870
9937
|
const ad = String(action.actionDetail || "");
|
|
9871
9938
|
const at = String(action.actionType || "");
|
|
9872
9939
|
if (!ad) return null;
|
|
@@ -9884,6 +9951,7 @@ var init_stateful = __esm({
|
|
|
9884
9951
|
instances: extractInstances(ad, stage),
|
|
9885
9952
|
isError,
|
|
9886
9953
|
agentId: String(action.agentId || agentId || ""),
|
|
9954
|
+
instanceId: String(action.instanceId || instanceId || ""),
|
|
9887
9955
|
lifecycle: lifecycleFor(ad, stage, isError),
|
|
9888
9956
|
timestamp: typeof action.calledAt === "string" ? action.calledAt : _StatefulChainTracker.now()
|
|
9889
9957
|
};
|
|
@@ -10027,7 +10095,8 @@ var init_stateful = __esm({
|
|
|
10027
10095
|
seq: n.seq,
|
|
10028
10096
|
stage: n.stage,
|
|
10029
10097
|
timestamp: n.timestamp,
|
|
10030
|
-
agentId: n.agentId
|
|
10098
|
+
agentId: n.agentId,
|
|
10099
|
+
instanceId: n.instanceId
|
|
10031
10100
|
});
|
|
10032
10101
|
const idx = stages.indexOf(n.stage);
|
|
10033
10102
|
if (idx + 1 < stages.length) {
|
|
@@ -10050,7 +10119,8 @@ var init_stateful = __esm({
|
|
|
10050
10119
|
stage: p.stage,
|
|
10051
10120
|
action_detail: n ? n.actionDetail : "",
|
|
10052
10121
|
resources: n ? [...n.resources].sort() : [],
|
|
10053
|
-
role: stages[i] || ""
|
|
10122
|
+
role: stages[i] || "",
|
|
10123
|
+
instance_id: p.instanceId || ""
|
|
10054
10124
|
};
|
|
10055
10125
|
});
|
|
10056
10126
|
const gEdges = [];
|
|
@@ -10118,7 +10188,7 @@ var init_stateful = __esm({
|
|
|
10118
10188
|
risk_level: "critical",
|
|
10119
10189
|
path: chain.map((j) => {
|
|
10120
10190
|
const n = this.nodes_[j];
|
|
10121
|
-
return [n.sessionId, n.seq, n.actionDetail.slice(0, 60), n.timestamp];
|
|
10191
|
+
return [n.sessionId, n.seq, n.actionDetail.slice(0, 60), n.timestamp, null, n.instanceId];
|
|
10122
10192
|
}),
|
|
10123
10193
|
graph: this.taintGraph(chain)
|
|
10124
10194
|
}];
|
|
@@ -10150,7 +10220,8 @@ var init_stateful = __esm({
|
|
|
10150
10220
|
stage: n.stage,
|
|
10151
10221
|
action_detail: n.actionDetail,
|
|
10152
10222
|
resources: [...n.resources].sort(),
|
|
10153
|
-
role
|
|
10223
|
+
role,
|
|
10224
|
+
instance_id: n.instanceId
|
|
10154
10225
|
});
|
|
10155
10226
|
if (k > 0) {
|
|
10156
10227
|
const prev = chain[k - 1];
|
|
@@ -10222,6 +10293,8 @@ var init_stateful = __esm({
|
|
|
10222
10293
|
seq: Number(raw.seq || 0),
|
|
10223
10294
|
sessionId: String(raw.sessionId || ""),
|
|
10224
10295
|
agentId: String(raw.agentId || ""),
|
|
10296
|
+
instanceId: String(raw.instanceId || ""),
|
|
10297
|
+
// 旧快照无此字段 → 空
|
|
10225
10298
|
actionType: String(raw.actionType || ""),
|
|
10226
10299
|
actionDetail: String(raw.actionDetail || ""),
|
|
10227
10300
|
stage: String(raw.stage || ""),
|
|
@@ -10366,9 +10439,9 @@ var init_chain_state = __esm({
|
|
|
10366
10439
|
delete this.data[agentId];
|
|
10367
10440
|
this._scheduleWrite();
|
|
10368
10441
|
}
|
|
10369
|
-
applyAction(agentId, aNext, sessionId, isError = false, sourceAgent = "") {
|
|
10442
|
+
applyAction(agentId, aNext, sessionId, isError = false, sourceAgent = "", instanceId = "") {
|
|
10370
10443
|
const t = this.load(agentId) || new StatefulChainTracker(agentId);
|
|
10371
|
-
const r = t.advance({ aNext, sessionId, isError, agentId: sourceAgent });
|
|
10444
|
+
const r = t.advance({ aNext, sessionId, isError, agentId: sourceAgent, instanceId });
|
|
10372
10445
|
this.save(t);
|
|
10373
10446
|
return r;
|
|
10374
10447
|
}
|
|
@@ -10686,12 +10759,21 @@ async function runAnalysis(input) {
|
|
|
10686
10759
|
formalSessions.push({
|
|
10687
10760
|
sessionId: s.sessionId || "s",
|
|
10688
10761
|
...s.agentId ? { agentId: s.agentId } : {},
|
|
10762
|
+
...s.instanceId ? { instanceId: s.instanceId } : {},
|
|
10689
10763
|
actionHistory: s.actionHistory || []
|
|
10690
10764
|
});
|
|
10691
10765
|
}
|
|
10692
10766
|
}
|
|
10693
|
-
formalSessions.push({
|
|
10694
|
-
|
|
10767
|
+
formalSessions.push({
|
|
10768
|
+
sessionId: "current",
|
|
10769
|
+
agentId: input.agentId,
|
|
10770
|
+
instanceId: input.instanceId || "",
|
|
10771
|
+
actionHistory: input.actionHistory
|
|
10772
|
+
});
|
|
10773
|
+
const formalResult = formalEngine.verify(formalSessions, {
|
|
10774
|
+
...input.aNext,
|
|
10775
|
+
instanceId: input.instanceId || ""
|
|
10776
|
+
});
|
|
10695
10777
|
if (!formalResult.verified) {
|
|
10696
10778
|
const v = formalResult.violations[0];
|
|
10697
10779
|
if (isFormalObserveMode()) {
|
|
@@ -11062,6 +11144,8 @@ var AnalyzeRequestSchema = z.object({
|
|
|
11062
11144
|
session_id: z.string(),
|
|
11063
11145
|
/** 来源 agent(AgentGate 逐会话带上):记录在链路径里供告警/取证,不参与链匹配。 */
|
|
11064
11146
|
agent_id: z.string().optional(),
|
|
11147
|
+
/** 来源实例:仅用于展示层脱敏(同实例不隐藏),不参与链匹配。 */
|
|
11148
|
+
instance_id: z.string().optional(),
|
|
11065
11149
|
action_history: z.array(ActionItemSchema).default([])
|
|
11066
11150
|
})).default([]).describe("Multi-session historical actions (optional): for L2.2 formal engine cross-session combo-chain / sensitive-data-flow verification. Provided by caller (incl. future agentgate)."),
|
|
11067
11151
|
a_next: ActionItemSchema.describe("Next action to be executed"),
|
|
@@ -11069,6 +11153,7 @@ var AnalyzeRequestSchema = z.object({
|
|
|
11069
11153
|
os_type: z.string().default("").describe("OS type: 'linux', 'windows', or empty for auto-detect"),
|
|
11070
11154
|
cwd: z.string().default("").describe("Current working directory"),
|
|
11071
11155
|
agent_id: z.string().default("").describe("Caller agent identifier, e.g. 'xiaoo' / 'opencode' / 'opendesk'. Empty = generic mode (no agent-specific rules)."),
|
|
11156
|
+
instance_id: z.string().default("").describe("Agent installation/instance identifier. Used only for display-layer redaction: nodes from the same instance are returned unredacted. Does not affect detection."),
|
|
11072
11157
|
metadata: z.record(z.unknown()).optional().describe("Extended metadata")
|
|
11073
11158
|
});
|
|
11074
11159
|
var AnalyzeResponseSchema = z.object({
|
|
@@ -11106,7 +11191,9 @@ var AnalyzeResponseSchema = z.object({
|
|
|
11106
11191
|
// 节点在性质中的角色:source/carrier/sink(P2)或阶段名(P1)
|
|
11107
11192
|
role: z.string().optional(),
|
|
11108
11193
|
// 是否为违例见证节点(当前动作补全链 / 触发外传)
|
|
11109
|
-
witness: z.boolean().optional()
|
|
11194
|
+
witness: z.boolean().optional(),
|
|
11195
|
+
// 来源实例(仅展示层脱敏:同实例不隐藏)
|
|
11196
|
+
instance_id: z.string().optional()
|
|
11110
11197
|
})
|
|
11111
11198
|
),
|
|
11112
11199
|
edges: z.array(
|
|
@@ -11998,6 +12085,9 @@ async function analyzeRoute(c) {
|
|
|
11998
12085
|
`[AgentMoss] \u26A0\uFE0F \u8DE8\u4F1A\u8BDD\u6279\u91CF\u8BBF\u95EE: key=${_sweepKey} \u7A97\u53E3\u5185 ${_sweepN} \u4E2A\u4E0D\u540C session\uFF08\u9608\u503C ${sessionSweepThreshold()}\uFF09`
|
|
11999
12086
|
);
|
|
12000
12087
|
}
|
|
12088
|
+
const instanceId = String(
|
|
12089
|
+
req.instance_id || req.metadata?.instance_id || ""
|
|
12090
|
+
);
|
|
12001
12091
|
const result = await analyze({
|
|
12002
12092
|
sessionId: req.session_id,
|
|
12003
12093
|
promptSession: req.prompt_session,
|
|
@@ -12013,8 +12103,9 @@ async function analyzeRoute(c) {
|
|
|
12013
12103
|
})),
|
|
12014
12104
|
sessionHistory: (req.session_history || []).map((s) => ({
|
|
12015
12105
|
sessionId: s.session_id,
|
|
12016
|
-
// 来源 agent
|
|
12106
|
+
// 来源 agent/实例(AgentGate 逐会话带上,用于链路径取证/展示脱敏;不参与链匹配)
|
|
12017
12107
|
...s.agent_id ? { agentId: s.agent_id } : {},
|
|
12108
|
+
...s.instance_id ? { instanceId: s.instance_id } : {},
|
|
12018
12109
|
actionHistory: (s.action_history || []).map((h) => ({
|
|
12019
12110
|
actionType: h.action_type,
|
|
12020
12111
|
actionDetail: h.action_detail,
|
|
@@ -12038,11 +12129,13 @@ async function analyzeRoute(c) {
|
|
|
12038
12129
|
})),
|
|
12039
12130
|
aNext: {
|
|
12040
12131
|
actionType: req.a_next.action_type,
|
|
12041
|
-
actionDetail: req.a_next.action_detail
|
|
12132
|
+
actionDetail: req.a_next.action_detail,
|
|
12133
|
+
instanceId
|
|
12042
12134
|
},
|
|
12043
12135
|
reason: req.reason,
|
|
12044
12136
|
osType,
|
|
12045
12137
|
cwd: req.cwd,
|
|
12138
|
+
instanceId,
|
|
12046
12139
|
// agent_id:AgentGate V1 normalizer 历史上把 agent_type 塞 metadata,
|
|
12047
12140
|
// 顶层为空时兜底(OpenDesk 的 agent-moss 需 agent_id 加载专属规则)。
|
|
12048
12141
|
agentId,
|
|
@@ -12078,8 +12171,9 @@ async function analyzeRoute(c) {
|
|
|
12078
12171
|
{ actionType: req.a_next.action_type, actionDetail: req.a_next.action_detail },
|
|
12079
12172
|
req.session_id,
|
|
12080
12173
|
Boolean(meta.is_error),
|
|
12081
|
-
agentId
|
|
12174
|
+
agentId,
|
|
12082
12175
|
// 来源:真实 agent(桶 key 可能是 __system__)
|
|
12176
|
+
instanceId
|
|
12083
12177
|
);
|
|
12084
12178
|
const judgment = incMatchedToDeny(inc);
|
|
12085
12179
|
if (judgment) {
|
|
@@ -12100,8 +12194,8 @@ async function analyzeRoute(c) {
|
|
|
12100
12194
|
confidence: 90,
|
|
12101
12195
|
analysis_duration_ms: Math.round((performance.now() - t0) * 10) / 10,
|
|
12102
12196
|
// 跨会话节点脱敏(时间线按 agent_id 共享,原样返回等于泄露别人的命令原文)
|
|
12103
|
-
violation_chain: redactChain(judgment.violation_chain, req.session_id),
|
|
12104
|
-
violation_graph: redactGraph(judgment.violation_graph, req.session_id)
|
|
12197
|
+
violation_chain: redactChain(judgment.violation_chain, instanceId, req.session_id),
|
|
12198
|
+
violation_graph: redactGraph(judgment.violation_graph, instanceId, req.session_id)
|
|
12105
12199
|
})
|
|
12106
12200
|
);
|
|
12107
12201
|
}
|
|
@@ -12123,7 +12217,7 @@ async function analyzeRoute(c) {
|
|
|
12123
12217
|
confidence: result.confidence,
|
|
12124
12218
|
analysis_duration_ms: Math.round(durationMs * 10) / 10,
|
|
12125
12219
|
violation_chain: result.violation_chain ?? [],
|
|
12126
|
-
violation_graph: redactGraph(result.violation_graph, req.session_id)
|
|
12220
|
+
violation_graph: redactGraph(result.violation_graph, instanceId, req.session_id)
|
|
12127
12221
|
})
|
|
12128
12222
|
);
|
|
12129
12223
|
} catch (err) {
|