@kenhkl/agent-moss 0.16.4 → 0.16.5
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 +68 -9
- package/dist/engine/coordinator.js +67 -8
- package/dist/scenarios/bank/rules.json +3 -3
- package/dist/server.js +68 -9
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5724,6 +5724,27 @@ function parseIf(content, args) {
|
|
|
5724
5724
|
${chosen}
|
|
5725
5725
|
${content.slice(fiPos + 2)}`;
|
|
5726
5726
|
}
|
|
5727
|
+
function stripNonExecutableLines(content) {
|
|
5728
|
+
if (!content) return content;
|
|
5729
|
+
const out = [];
|
|
5730
|
+
let heredocEnd = null;
|
|
5731
|
+
const lines = content.split("\n");
|
|
5732
|
+
for (let i = 0; i < lines.length; i++) {
|
|
5733
|
+
const line = lines[i];
|
|
5734
|
+
if (heredocEnd !== null) {
|
|
5735
|
+
if (line.trim() === heredocEnd) heredocEnd = null;
|
|
5736
|
+
out.push(line);
|
|
5737
|
+
continue;
|
|
5738
|
+
}
|
|
5739
|
+
const m = HEREDOC_RE.exec(line);
|
|
5740
|
+
if (m) heredocEnd = m[1];
|
|
5741
|
+
const stripped = line.replace(/^\s+/, "");
|
|
5742
|
+
if (i === 0 && stripped.startsWith("#!")) continue;
|
|
5743
|
+
if (stripped.startsWith("#")) continue;
|
|
5744
|
+
out.push(line);
|
|
5745
|
+
}
|
|
5746
|
+
return out.join("\n");
|
|
5747
|
+
}
|
|
5727
5748
|
function markLoopsReachable(content) {
|
|
5728
5749
|
return content;
|
|
5729
5750
|
}
|
|
@@ -5745,12 +5766,13 @@ function extractCallsFromContent(content) {
|
|
|
5745
5766
|
}
|
|
5746
5767
|
return calls;
|
|
5747
5768
|
}
|
|
5748
|
-
var CHILD_CALL_RE2, ShellBackend;
|
|
5769
|
+
var HEREDOC_RE, CHILD_CALL_RE2, ShellBackend;
|
|
5749
5770
|
var init_shell_backend = __esm({
|
|
5750
5771
|
"src/engine/script-engine/backends/shell-backend.ts"() {
|
|
5751
5772
|
"use strict";
|
|
5752
5773
|
init_esm_shims();
|
|
5753
5774
|
init_symbolic();
|
|
5775
|
+
HEREDOC_RE = /<<-?\s*['"]?(\w+)['"]?/;
|
|
5754
5776
|
CHILD_CALL_RE2 = /(?:^|\s|&&|\|\|)\s*((?:\/bin\/)?bash|sh|zsh|(?:python|python3|node|ruby|perl|php|lua))\s+(\S+(?:\.(?:sh|shrc|py|js|rb|pl|php|lua))?)([^\n;|&]*?)$/gmi;
|
|
5755
5777
|
ShellBackend = class {
|
|
5756
5778
|
language = "shell";
|
|
@@ -5780,6 +5802,7 @@ var init_shell_backend = __esm({
|
|
|
5780
5802
|
bound = symOut;
|
|
5781
5803
|
}
|
|
5782
5804
|
}
|
|
5805
|
+
bound = stripNonExecutableLines(bound);
|
|
5783
5806
|
const loopMarked = markLoopsReachable(bound);
|
|
5784
5807
|
const calls = extractCallsFromContent(loopMarked);
|
|
5785
5808
|
return {
|
|
@@ -6192,7 +6215,30 @@ function resolvePath2(p) {
|
|
|
6192
6215
|
}
|
|
6193
6216
|
return resolve9(expanded);
|
|
6194
6217
|
}
|
|
6195
|
-
function
|
|
6218
|
+
function scanExtensionPatterns(ext, content) {
|
|
6219
|
+
const pats = ext?.extra_command_patterns;
|
|
6220
|
+
if (!pats || pats.length === 0) return null;
|
|
6221
|
+
for (const p of pats) {
|
|
6222
|
+
const src = typeof p === "string" ? p : p?.pattern;
|
|
6223
|
+
if (!src) continue;
|
|
6224
|
+
let re;
|
|
6225
|
+
try {
|
|
6226
|
+
re = new RegExp(src, "i");
|
|
6227
|
+
} catch {
|
|
6228
|
+
continue;
|
|
6229
|
+
}
|
|
6230
|
+
if (re.test(content)) {
|
|
6231
|
+
const risk = typeof p === "string" ? "high" : p.risk_level || "high";
|
|
6232
|
+
return {
|
|
6233
|
+
riskLevel: risk,
|
|
6234
|
+
riskType: typeof p === "string" ? "sensitive_action" : p.risk_type || "sensitive_action",
|
|
6235
|
+
reason: typeof p === "string" ? `\u547D\u4E2D\u6269\u5C55\u89C4\u5219: ${src}` : p.reason || `\u547D\u4E2D\u6269\u5C55\u89C4\u5219: ${src}`
|
|
6236
|
+
};
|
|
6237
|
+
}
|
|
6238
|
+
}
|
|
6239
|
+
return null;
|
|
6240
|
+
}
|
|
6241
|
+
function scanScriptChainWithL1(chain, input, ext) {
|
|
6196
6242
|
for (const node of chain.nodes) {
|
|
6197
6243
|
if (node.error || !node.preprocessedContent || node.l1Hit) continue;
|
|
6198
6244
|
const virtualInput = {
|
|
@@ -6200,6 +6246,19 @@ function scanScriptChainWithL1(chain, input) {
|
|
|
6200
6246
|
aNext: { actionType: "bash", actionDetail: node.preprocessedContent }
|
|
6201
6247
|
};
|
|
6202
6248
|
const l1 = scanDangerousCommandPatterns("bash", node.preprocessedContent, input.osType);
|
|
6249
|
+
const extHit = scanExtensionPatterns(ext, node.preprocessedContent);
|
|
6250
|
+
if (extHit) {
|
|
6251
|
+
node.l1Hit = true;
|
|
6252
|
+
node.l1RiskLevel = extHit.riskLevel;
|
|
6253
|
+
node.l1RiskType = extHit.riskType;
|
|
6254
|
+
node.l1Reason = extHit.reason;
|
|
6255
|
+
if (extHit.riskLevel === "critical") {
|
|
6256
|
+
chain.hasCritical = true;
|
|
6257
|
+
chain.firstHitNode = node;
|
|
6258
|
+
break;
|
|
6259
|
+
}
|
|
6260
|
+
continue;
|
|
6261
|
+
}
|
|
6203
6262
|
if (l1 && !l1.allowed) {
|
|
6204
6263
|
node.l1Hit = true;
|
|
6205
6264
|
node.l1RiskLevel = l1.riskLevel;
|
|
@@ -6263,7 +6322,7 @@ function scanScriptChainKeywords(chain) {
|
|
|
6263
6322
|
}
|
|
6264
6323
|
return chain;
|
|
6265
6324
|
}
|
|
6266
|
-
function analyzeScriptContent2(input) {
|
|
6325
|
+
function analyzeScriptContent2(input, ext) {
|
|
6267
6326
|
const actionType = input.aNext.actionType.toLowerCase();
|
|
6268
6327
|
if (actionType !== "bash") return emptyChain();
|
|
6269
6328
|
const { path: scriptPath, args: scriptArgs } = extractScriptPathWithArgs(input.aNext.actionDetail);
|
|
@@ -6295,7 +6354,7 @@ function analyzeScriptContent2(input) {
|
|
|
6295
6354
|
const tracker = new RecursiveTracker(l1Adapter);
|
|
6296
6355
|
const chain = tracker.track(resolvedPath, isInline ? null : scriptArgs, input.cwd || process.cwd());
|
|
6297
6356
|
if (chain.nodes.length === 0) return chain;
|
|
6298
|
-
scanScriptChainWithL1(chain, input);
|
|
6357
|
+
scanScriptChainWithL1(chain, input, ext);
|
|
6299
6358
|
scanScriptChainWithL2(chain, input);
|
|
6300
6359
|
scanScriptChainKeywords(chain);
|
|
6301
6360
|
return chain;
|
|
@@ -8973,9 +9032,13 @@ async function runAnalysis(input) {
|
|
|
8973
9032
|
}
|
|
8974
9033
|
}
|
|
8975
9034
|
}
|
|
9035
|
+
const agentExt = mergeAgentExtensions(
|
|
9036
|
+
isLayerEnabled("agent_rules_enabled") ? getAgentExtensions(input.agentId) : null,
|
|
9037
|
+
isLayerEnabled("scenario_rules_enabled") ? getScenarioExtensionsMulti(input.scenarioId || process.env.AGENT_MOSS_SCENARIO || "") : null
|
|
9038
|
+
);
|
|
8976
9039
|
let scriptChain = null;
|
|
8977
9040
|
if (input.aNext.actionType.toLowerCase() === "bash") {
|
|
8978
|
-
scriptChain = analyzeScriptContent2(input);
|
|
9041
|
+
scriptChain = analyzeScriptContent2(input, agentExt);
|
|
8979
9042
|
if (scriptChain.nodes.length > 0) {
|
|
8980
9043
|
for (const node of scriptChain.nodes) {
|
|
8981
9044
|
if (node.unanalyzable) {
|
|
@@ -9027,10 +9090,6 @@ async function runAnalysis(input) {
|
|
|
9027
9090
|
const actionType = input.aNext.actionType.toLowerCase();
|
|
9028
9091
|
const rawDetail = extractRawCommand(input.aNext.actionDetail);
|
|
9029
9092
|
const lowerDetail = rawDetail.toLowerCase();
|
|
9030
|
-
const agentExt = mergeAgentExtensions(
|
|
9031
|
-
isLayerEnabled("agent_rules_enabled") ? getAgentExtensions(input.agentId) : null,
|
|
9032
|
-
isLayerEnabled("scenario_rules_enabled") ? getScenarioExtensionsMulti(input.scenarioId || process.env.AGENT_MOSS_SCENARIO || "") : null
|
|
9033
|
-
);
|
|
9034
9093
|
if (FULLY_SAFE_TOOLS.has(actionType)) {
|
|
9035
9094
|
return {
|
|
9036
9095
|
allowed: true,
|
|
@@ -5901,6 +5901,28 @@ function parseIf(content, args) {
|
|
|
5901
5901
|
${chosen}
|
|
5902
5902
|
${content.slice(fiPos + 2)}`;
|
|
5903
5903
|
}
|
|
5904
|
+
var HEREDOC_RE = /<<-?\s*['"]?(\w+)['"]?/;
|
|
5905
|
+
function stripNonExecutableLines(content) {
|
|
5906
|
+
if (!content) return content;
|
|
5907
|
+
const out = [];
|
|
5908
|
+
let heredocEnd = null;
|
|
5909
|
+
const lines = content.split("\n");
|
|
5910
|
+
for (let i = 0; i < lines.length; i++) {
|
|
5911
|
+
const line = lines[i];
|
|
5912
|
+
if (heredocEnd !== null) {
|
|
5913
|
+
if (line.trim() === heredocEnd) heredocEnd = null;
|
|
5914
|
+
out.push(line);
|
|
5915
|
+
continue;
|
|
5916
|
+
}
|
|
5917
|
+
const m = HEREDOC_RE.exec(line);
|
|
5918
|
+
if (m) heredocEnd = m[1];
|
|
5919
|
+
const stripped = line.replace(/^\s+/, "");
|
|
5920
|
+
if (i === 0 && stripped.startsWith("#!")) continue;
|
|
5921
|
+
if (stripped.startsWith("#")) continue;
|
|
5922
|
+
out.push(line);
|
|
5923
|
+
}
|
|
5924
|
+
return out.join("\n");
|
|
5925
|
+
}
|
|
5904
5926
|
function markLoopsReachable(content) {
|
|
5905
5927
|
return content;
|
|
5906
5928
|
}
|
|
@@ -5951,6 +5973,7 @@ var ShellBackend = class {
|
|
|
5951
5973
|
bound = symOut;
|
|
5952
5974
|
}
|
|
5953
5975
|
}
|
|
5976
|
+
bound = stripNonExecutableLines(bound);
|
|
5954
5977
|
const loopMarked = markLoopsReachable(bound);
|
|
5955
5978
|
const calls = extractCallsFromContent(loopMarked);
|
|
5956
5979
|
return {
|
|
@@ -6417,7 +6440,30 @@ function resolvePath2(p) {
|
|
|
6417
6440
|
}
|
|
6418
6441
|
return resolve9(expanded);
|
|
6419
6442
|
}
|
|
6420
|
-
function
|
|
6443
|
+
function scanExtensionPatterns(ext, content) {
|
|
6444
|
+
const pats = ext?.extra_command_patterns;
|
|
6445
|
+
if (!pats || pats.length === 0) return null;
|
|
6446
|
+
for (const p of pats) {
|
|
6447
|
+
const src = typeof p === "string" ? p : p?.pattern;
|
|
6448
|
+
if (!src) continue;
|
|
6449
|
+
let re;
|
|
6450
|
+
try {
|
|
6451
|
+
re = new RegExp(src, "i");
|
|
6452
|
+
} catch {
|
|
6453
|
+
continue;
|
|
6454
|
+
}
|
|
6455
|
+
if (re.test(content)) {
|
|
6456
|
+
const risk = typeof p === "string" ? "high" : p.risk_level || "high";
|
|
6457
|
+
return {
|
|
6458
|
+
riskLevel: risk,
|
|
6459
|
+
riskType: typeof p === "string" ? "sensitive_action" : p.risk_type || "sensitive_action",
|
|
6460
|
+
reason: typeof p === "string" ? `\u547D\u4E2D\u6269\u5C55\u89C4\u5219: ${src}` : p.reason || `\u547D\u4E2D\u6269\u5C55\u89C4\u5219: ${src}`
|
|
6461
|
+
};
|
|
6462
|
+
}
|
|
6463
|
+
}
|
|
6464
|
+
return null;
|
|
6465
|
+
}
|
|
6466
|
+
function scanScriptChainWithL1(chain, input, ext) {
|
|
6421
6467
|
for (const node of chain.nodes) {
|
|
6422
6468
|
if (node.error || !node.preprocessedContent || node.l1Hit) continue;
|
|
6423
6469
|
const virtualInput = {
|
|
@@ -6425,6 +6471,19 @@ function scanScriptChainWithL1(chain, input) {
|
|
|
6425
6471
|
aNext: { actionType: "bash", actionDetail: node.preprocessedContent }
|
|
6426
6472
|
};
|
|
6427
6473
|
const l1 = scanDangerousCommandPatterns("bash", node.preprocessedContent, input.osType);
|
|
6474
|
+
const extHit = scanExtensionPatterns(ext, node.preprocessedContent);
|
|
6475
|
+
if (extHit) {
|
|
6476
|
+
node.l1Hit = true;
|
|
6477
|
+
node.l1RiskLevel = extHit.riskLevel;
|
|
6478
|
+
node.l1RiskType = extHit.riskType;
|
|
6479
|
+
node.l1Reason = extHit.reason;
|
|
6480
|
+
if (extHit.riskLevel === "critical") {
|
|
6481
|
+
chain.hasCritical = true;
|
|
6482
|
+
chain.firstHitNode = node;
|
|
6483
|
+
break;
|
|
6484
|
+
}
|
|
6485
|
+
continue;
|
|
6486
|
+
}
|
|
6428
6487
|
if (l1 && !l1.allowed) {
|
|
6429
6488
|
node.l1Hit = true;
|
|
6430
6489
|
node.l1RiskLevel = l1.riskLevel;
|
|
@@ -6488,7 +6547,7 @@ function scanScriptChainKeywords(chain) {
|
|
|
6488
6547
|
}
|
|
6489
6548
|
return chain;
|
|
6490
6549
|
}
|
|
6491
|
-
function analyzeScriptContent2(input) {
|
|
6550
|
+
function analyzeScriptContent2(input, ext) {
|
|
6492
6551
|
const actionType = input.aNext.actionType.toLowerCase();
|
|
6493
6552
|
if (actionType !== "bash") return emptyChain();
|
|
6494
6553
|
const { path: scriptPath, args: scriptArgs } = extractScriptPathWithArgs(input.aNext.actionDetail);
|
|
@@ -6520,7 +6579,7 @@ function analyzeScriptContent2(input) {
|
|
|
6520
6579
|
const tracker = new RecursiveTracker(l1Adapter);
|
|
6521
6580
|
const chain = tracker.track(resolvedPath, isInline ? null : scriptArgs, input.cwd || process.cwd());
|
|
6522
6581
|
if (chain.nodes.length === 0) return chain;
|
|
6523
|
-
scanScriptChainWithL1(chain, input);
|
|
6582
|
+
scanScriptChainWithL1(chain, input, ext);
|
|
6524
6583
|
scanScriptChainWithL2(chain, input);
|
|
6525
6584
|
scanScriptChainKeywords(chain);
|
|
6526
6585
|
return chain;
|
|
@@ -8343,9 +8402,13 @@ async function runAnalysis(input) {
|
|
|
8343
8402
|
}
|
|
8344
8403
|
}
|
|
8345
8404
|
}
|
|
8405
|
+
const agentExt = mergeAgentExtensions(
|
|
8406
|
+
isLayerEnabled("agent_rules_enabled") ? getAgentExtensions(input.agentId) : null,
|
|
8407
|
+
isLayerEnabled("scenario_rules_enabled") ? getScenarioExtensionsMulti(input.scenarioId || process.env.AGENT_MOSS_SCENARIO || "") : null
|
|
8408
|
+
);
|
|
8346
8409
|
let scriptChain = null;
|
|
8347
8410
|
if (input.aNext.actionType.toLowerCase() === "bash") {
|
|
8348
|
-
scriptChain = analyzeScriptContent2(input);
|
|
8411
|
+
scriptChain = analyzeScriptContent2(input, agentExt);
|
|
8349
8412
|
if (scriptChain.nodes.length > 0) {
|
|
8350
8413
|
for (const node of scriptChain.nodes) {
|
|
8351
8414
|
if (node.unanalyzable) {
|
|
@@ -8397,10 +8460,6 @@ async function runAnalysis(input) {
|
|
|
8397
8460
|
const actionType = input.aNext.actionType.toLowerCase();
|
|
8398
8461
|
const rawDetail = extractRawCommand(input.aNext.actionDetail);
|
|
8399
8462
|
const lowerDetail = rawDetail.toLowerCase();
|
|
8400
|
-
const agentExt = mergeAgentExtensions(
|
|
8401
|
-
isLayerEnabled("agent_rules_enabled") ? getAgentExtensions(input.agentId) : null,
|
|
8402
|
-
isLayerEnabled("scenario_rules_enabled") ? getScenarioExtensionsMulti(input.scenarioId || process.env.AGENT_MOSS_SCENARIO || "") : null
|
|
8403
|
-
);
|
|
8404
8463
|
if (FULLY_SAFE_TOOLS.has(actionType)) {
|
|
8405
8464
|
return {
|
|
8406
8465
|
allowed: true,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"scenario_id": "bank",
|
|
3
|
-
"description": "银行沙箱场景定制规则。客户约束:沙箱用户(sandbox)仅允许读写 /home/sandbox、/opt/sandbox、/tmp 三个目录,其他根目录一律禁止读写;禁止远程访问/横向移动(ssh 家族 + scp/sftp/sshpass/ssh-copy-id + 用 ssh 的 rsync)。本地操作(ssh-keygen/ssh-add/ssh-agent、本地 rsync
|
|
3
|
+
"description": "银行沙箱场景定制规则。客户约束:沙箱用户(sandbox)仅允许读写 /home/sandbox、/opt/sandbox、/tmp 三个目录,其他根目录一律禁止读写;禁止远程访问/横向移动(ssh 家族 + scp/sftp/sshpass/ssh-copy-id + 用 ssh 的 rsync)。本地操作(ssh-keygen/ssh-add/ssh-agent、本地 rsync)不受影响。另放行 /dev/null、/dev/stdout、/dev/stderr —— 这三个是 666/可写、仅作流丢弃与输出重定向用的标准设备文件(`2>/dev/null` 是最常见的运维写法),不放行会让大量正常命令被误拦;/dev 下**其余**文件(/dev/mem、/dev/sda、/dev/shm…)照常拦。",
|
|
4
4
|
"extra_command_patterns": [
|
|
5
5
|
{
|
|
6
|
-
"pattern": "(?<![\\w.:~/-])/(?:(?!home/sandbox(?:\\s|/|$)|opt/sandbox(?:\\s|/|$)|tmp(?:\\s|/|$))[^\\s;&|>\"'`]*|[^\\s;&|>\"'`]*/\\.\\.[^\\s;&|>\"'`]*)|(?:^|[\\s;&|<>=(`'\"!/])(?:\\.\\./|\\.\\.(?=\\s|$|[;&|]))|(?:^|[\\s;&|<>=(`'\"!])~(?!sandbox\\b)[\\w-]+",
|
|
6
|
+
"pattern": "(?<![\\w.:~/-])/(?:(?!home/sandbox(?:\\s|/|$)|opt/sandbox(?:\\s|/|$)|tmp(?:\\s|/|$)|dev/null(?=[\\s;&|<>)\"'`]|$)|dev/stdout(?=[\\s;&|<>)\"'`]|$)|dev/stderr(?=[\\s;&|<>)\"'`]|$))[^\\s;&|>\"'`]*|[^\\s;&|>\"'`]*/\\.\\.[^\\s;&|>\"'`]*)|(?:^|[\\s;&|<>=(`'\"!/])(?:\\.\\./|\\.\\.(?=\\s|$|[;&|]))|(?:^|[\\s;&|<>=(`'\"!])~(?!sandbox\\b)[\\w-]+",
|
|
7
7
|
"risk_level": "high",
|
|
8
8
|
"risk_type": "file_access",
|
|
9
|
-
"reason": "bank 场景:访问允许目录(/home/sandbox、/opt/sandbox、/tmp
|
|
9
|
+
"reason": "bank 场景:访问允许目录(/home/sandbox、/opt/sandbox、/tmp,以及 /dev/null、/dev/stdout、/dev/stderr 三个标准流重定向目标)之外的路径(含根目录 /),或用 .. 逃逸 / ~其它用户"
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
"pattern": "(?:^|[;&|\\n]|\\$\\(|`)\\s*(?:sudo\\s+(?:-\\S+\\s+)*|doas\\s+|nohup\\s+|time\\s+|timeout\\s+(?:\\S+\\s+)?|env\\s+(?:\\w+=\\S*\\s+)*|command\\s+|builtin\\s+|exec\\s+|xargs\\s+(?:-\\S+\\s+)*)*ssh(?![\\w-])(?!-keygen|-add|-agent)|(?:^|[;&|\\n]|\\$\\(|`)\\s*(?:sudo\\s+(?:-\\S+\\s+)*|doas\\s+|nohup\\s+|time\\s+|timeout\\s+(?:\\S+\\s+)?|env\\s+(?:\\w+=\\S*\\s+)*|command\\s+|builtin\\s+|exec\\s+|xargs\\s+(?:-\\S+\\s+)*)*(?:scp|sftp|sshpass|ssh-copy-id)(?![\\w-])|(?:^|[;&|\\n]|\\$\\(|`)\\s*(?:sudo\\s+(?:-\\S+\\s+)*|doas\\s+|nohup\\s+|time\\s+|timeout\\s+(?:\\S+\\s+)?|env\\s+(?:\\w+=\\S*\\s+)*|command\\s+|builtin\\s+|exec\\s+|xargs\\s+(?:-\\S+\\s+)*)*rsync\\b[^\\n]*?\\s(?:-e|--rsh)[=\\s]",
|
package/dist/server.js
CHANGED
|
@@ -5723,6 +5723,27 @@ function parseIf(content, args) {
|
|
|
5723
5723
|
${chosen}
|
|
5724
5724
|
${content.slice(fiPos + 2)}`;
|
|
5725
5725
|
}
|
|
5726
|
+
function stripNonExecutableLines(content) {
|
|
5727
|
+
if (!content) return content;
|
|
5728
|
+
const out = [];
|
|
5729
|
+
let heredocEnd = null;
|
|
5730
|
+
const lines = content.split("\n");
|
|
5731
|
+
for (let i = 0; i < lines.length; i++) {
|
|
5732
|
+
const line = lines[i];
|
|
5733
|
+
if (heredocEnd !== null) {
|
|
5734
|
+
if (line.trim() === heredocEnd) heredocEnd = null;
|
|
5735
|
+
out.push(line);
|
|
5736
|
+
continue;
|
|
5737
|
+
}
|
|
5738
|
+
const m = HEREDOC_RE.exec(line);
|
|
5739
|
+
if (m) heredocEnd = m[1];
|
|
5740
|
+
const stripped = line.replace(/^\s+/, "");
|
|
5741
|
+
if (i === 0 && stripped.startsWith("#!")) continue;
|
|
5742
|
+
if (stripped.startsWith("#")) continue;
|
|
5743
|
+
out.push(line);
|
|
5744
|
+
}
|
|
5745
|
+
return out.join("\n");
|
|
5746
|
+
}
|
|
5726
5747
|
function markLoopsReachable(content) {
|
|
5727
5748
|
return content;
|
|
5728
5749
|
}
|
|
@@ -5744,12 +5765,13 @@ function extractCallsFromContent(content) {
|
|
|
5744
5765
|
}
|
|
5745
5766
|
return calls;
|
|
5746
5767
|
}
|
|
5747
|
-
var CHILD_CALL_RE2, ShellBackend;
|
|
5768
|
+
var HEREDOC_RE, CHILD_CALL_RE2, ShellBackend;
|
|
5748
5769
|
var init_shell_backend = __esm({
|
|
5749
5770
|
"src/engine/script-engine/backends/shell-backend.ts"() {
|
|
5750
5771
|
"use strict";
|
|
5751
5772
|
init_esm_shims();
|
|
5752
5773
|
init_symbolic();
|
|
5774
|
+
HEREDOC_RE = /<<-?\s*['"]?(\w+)['"]?/;
|
|
5753
5775
|
CHILD_CALL_RE2 = /(?:^|\s|&&|\|\|)\s*((?:\/bin\/)?bash|sh|zsh|(?:python|python3|node|ruby|perl|php|lua))\s+(\S+(?:\.(?:sh|shrc|py|js|rb|pl|php|lua))?)([^\n;|&]*?)$/gmi;
|
|
5754
5776
|
ShellBackend = class {
|
|
5755
5777
|
language = "shell";
|
|
@@ -5779,6 +5801,7 @@ var init_shell_backend = __esm({
|
|
|
5779
5801
|
bound = symOut;
|
|
5780
5802
|
}
|
|
5781
5803
|
}
|
|
5804
|
+
bound = stripNonExecutableLines(bound);
|
|
5782
5805
|
const loopMarked = markLoopsReachable(bound);
|
|
5783
5806
|
const calls = extractCallsFromContent(loopMarked);
|
|
5784
5807
|
return {
|
|
@@ -6191,7 +6214,30 @@ function resolvePath2(p) {
|
|
|
6191
6214
|
}
|
|
6192
6215
|
return resolve9(expanded);
|
|
6193
6216
|
}
|
|
6194
|
-
function
|
|
6217
|
+
function scanExtensionPatterns(ext, content) {
|
|
6218
|
+
const pats = ext?.extra_command_patterns;
|
|
6219
|
+
if (!pats || pats.length === 0) return null;
|
|
6220
|
+
for (const p of pats) {
|
|
6221
|
+
const src = typeof p === "string" ? p : p?.pattern;
|
|
6222
|
+
if (!src) continue;
|
|
6223
|
+
let re;
|
|
6224
|
+
try {
|
|
6225
|
+
re = new RegExp(src, "i");
|
|
6226
|
+
} catch {
|
|
6227
|
+
continue;
|
|
6228
|
+
}
|
|
6229
|
+
if (re.test(content)) {
|
|
6230
|
+
const risk = typeof p === "string" ? "high" : p.risk_level || "high";
|
|
6231
|
+
return {
|
|
6232
|
+
riskLevel: risk,
|
|
6233
|
+
riskType: typeof p === "string" ? "sensitive_action" : p.risk_type || "sensitive_action",
|
|
6234
|
+
reason: typeof p === "string" ? `\u547D\u4E2D\u6269\u5C55\u89C4\u5219: ${src}` : p.reason || `\u547D\u4E2D\u6269\u5C55\u89C4\u5219: ${src}`
|
|
6235
|
+
};
|
|
6236
|
+
}
|
|
6237
|
+
}
|
|
6238
|
+
return null;
|
|
6239
|
+
}
|
|
6240
|
+
function scanScriptChainWithL1(chain, input, ext) {
|
|
6195
6241
|
for (const node of chain.nodes) {
|
|
6196
6242
|
if (node.error || !node.preprocessedContent || node.l1Hit) continue;
|
|
6197
6243
|
const virtualInput = {
|
|
@@ -6199,6 +6245,19 @@ function scanScriptChainWithL1(chain, input) {
|
|
|
6199
6245
|
aNext: { actionType: "bash", actionDetail: node.preprocessedContent }
|
|
6200
6246
|
};
|
|
6201
6247
|
const l1 = scanDangerousCommandPatterns("bash", node.preprocessedContent, input.osType);
|
|
6248
|
+
const extHit = scanExtensionPatterns(ext, node.preprocessedContent);
|
|
6249
|
+
if (extHit) {
|
|
6250
|
+
node.l1Hit = true;
|
|
6251
|
+
node.l1RiskLevel = extHit.riskLevel;
|
|
6252
|
+
node.l1RiskType = extHit.riskType;
|
|
6253
|
+
node.l1Reason = extHit.reason;
|
|
6254
|
+
if (extHit.riskLevel === "critical") {
|
|
6255
|
+
chain.hasCritical = true;
|
|
6256
|
+
chain.firstHitNode = node;
|
|
6257
|
+
break;
|
|
6258
|
+
}
|
|
6259
|
+
continue;
|
|
6260
|
+
}
|
|
6202
6261
|
if (l1 && !l1.allowed) {
|
|
6203
6262
|
node.l1Hit = true;
|
|
6204
6263
|
node.l1RiskLevel = l1.riskLevel;
|
|
@@ -6262,7 +6321,7 @@ function scanScriptChainKeywords(chain) {
|
|
|
6262
6321
|
}
|
|
6263
6322
|
return chain;
|
|
6264
6323
|
}
|
|
6265
|
-
function analyzeScriptContent2(input) {
|
|
6324
|
+
function analyzeScriptContent2(input, ext) {
|
|
6266
6325
|
const actionType = input.aNext.actionType.toLowerCase();
|
|
6267
6326
|
if (actionType !== "bash") return emptyChain();
|
|
6268
6327
|
const { path: scriptPath, args: scriptArgs } = extractScriptPathWithArgs(input.aNext.actionDetail);
|
|
@@ -6294,7 +6353,7 @@ function analyzeScriptContent2(input) {
|
|
|
6294
6353
|
const tracker = new RecursiveTracker(l1Adapter);
|
|
6295
6354
|
const chain = tracker.track(resolvedPath, isInline ? null : scriptArgs, input.cwd || process.cwd());
|
|
6296
6355
|
if (chain.nodes.length === 0) return chain;
|
|
6297
|
-
scanScriptChainWithL1(chain, input);
|
|
6356
|
+
scanScriptChainWithL1(chain, input, ext);
|
|
6298
6357
|
scanScriptChainWithL2(chain, input);
|
|
6299
6358
|
scanScriptChainKeywords(chain);
|
|
6300
6359
|
return chain;
|
|
@@ -8972,9 +9031,13 @@ async function runAnalysis(input) {
|
|
|
8972
9031
|
}
|
|
8973
9032
|
}
|
|
8974
9033
|
}
|
|
9034
|
+
const agentExt = mergeAgentExtensions(
|
|
9035
|
+
isLayerEnabled("agent_rules_enabled") ? getAgentExtensions(input.agentId) : null,
|
|
9036
|
+
isLayerEnabled("scenario_rules_enabled") ? getScenarioExtensionsMulti(input.scenarioId || process.env.AGENT_MOSS_SCENARIO || "") : null
|
|
9037
|
+
);
|
|
8975
9038
|
let scriptChain = null;
|
|
8976
9039
|
if (input.aNext.actionType.toLowerCase() === "bash") {
|
|
8977
|
-
scriptChain = analyzeScriptContent2(input);
|
|
9040
|
+
scriptChain = analyzeScriptContent2(input, agentExt);
|
|
8978
9041
|
if (scriptChain.nodes.length > 0) {
|
|
8979
9042
|
for (const node of scriptChain.nodes) {
|
|
8980
9043
|
if (node.unanalyzable) {
|
|
@@ -9026,10 +9089,6 @@ async function runAnalysis(input) {
|
|
|
9026
9089
|
const actionType = input.aNext.actionType.toLowerCase();
|
|
9027
9090
|
const rawDetail = extractRawCommand(input.aNext.actionDetail);
|
|
9028
9091
|
const lowerDetail = rawDetail.toLowerCase();
|
|
9029
|
-
const agentExt = mergeAgentExtensions(
|
|
9030
|
-
isLayerEnabled("agent_rules_enabled") ? getAgentExtensions(input.agentId) : null,
|
|
9031
|
-
isLayerEnabled("scenario_rules_enabled") ? getScenarioExtensionsMulti(input.scenarioId || process.env.AGENT_MOSS_SCENARIO || "") : null
|
|
9032
|
-
);
|
|
9033
9092
|
if (FULLY_SAFE_TOOLS.has(actionType)) {
|
|
9034
9093
|
return {
|
|
9035
9094
|
allowed: true,
|