@kenhkl/agent-moss 0.15.4 → 0.15.7
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 +65 -41
- package/dist/engine/coordinator.js +55 -31
- package/dist/rules/l1_rules.json +109 -16
- package/dist/rules/mc_rules.json +2 -30
- package/dist/rules/user_rules.json +2 -71
- package/dist/server.js +60 -36
- package/package.json +2 -1
- package/dist/agents/__init__.py +0 -105
- package/dist/agents/__pycache__/__init__.cpython-310.pyc +0 -0
- package/dist/agents/__pycache__/__init__.cpython-311.pyc +0 -0
- package/dist/agents/__pycache__/__init__.cpython-312.pyc +0 -0
- package/dist/agents/__pycache__/base.cpython-310.pyc +0 -0
- package/dist/agents/__pycache__/base.cpython-311.pyc +0 -0
- package/dist/agents/__pycache__/base.cpython-312.pyc +0 -0
- package/dist/agents/base.py +0 -158
- package/dist/cli.js.map +0 -1
- package/dist/engine/coordinator.js.map +0 -1
- package/dist/scenarios/__init__.py +0 -148
- package/dist/scenarios/__pycache__/__init__.cpython-310.pyc +0 -0
- package/dist/scenarios/__pycache__/__init__.cpython-311.pyc +0 -0
- package/dist/scenarios/__pycache__/__init__.cpython-312.pyc +0 -0
- package/dist/server.js.map +0 -1
package/dist/rules/l1_rules.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"reason": "检测到递归强制删除根目录 (rm -rf /)"
|
|
8
8
|
},
|
|
9
9
|
{
|
|
10
|
-
"pattern": "\\brm\\s+-[a-zA-Z]*[rf][a-zA-Z]*\\s+/(etc|var(?!/tmp)|
|
|
10
|
+
"pattern": "\\brm\\s+-[a-zA-Z]*[rf][a-zA-Z]*\\s+/(?:etc|var(?!/tmp)|usr|boot|root|opt|srv|sys|proc|home(?:/|/[A-Za-z0-9_.-]+/?)?(?=[\\s;|&\\\"']|$))",
|
|
11
11
|
"risk_level": "critical",
|
|
12
12
|
"risk_type": "script_execution",
|
|
13
13
|
"reason": "检测到删除关键系统目录"
|
|
@@ -73,16 +73,16 @@
|
|
|
73
73
|
"reason": "检测到访问系统组密码文件 (/etc/gshadow)"
|
|
74
74
|
},
|
|
75
75
|
{
|
|
76
|
-
"pattern": "
|
|
76
|
+
"pattern": "dd\\s+if=/dev/zero.*\\bof\\s*=\\s*/(?:etc|var|usr|boot|root|opt|srv|sys|proc|home(?:/|/[A-Za-z0-9_.-]+/?)?(?=[\\s;|&\\\"']|$))",
|
|
77
77
|
"risk_level": "critical",
|
|
78
|
-
"risk_type": "
|
|
79
|
-
"reason": "
|
|
78
|
+
"risk_type": "script_execution",
|
|
79
|
+
"reason": "检测到磁盘清零写入关键系统目录 (dd if=/dev/zero of=/系统目录)"
|
|
80
80
|
},
|
|
81
81
|
{
|
|
82
|
-
"pattern": "
|
|
82
|
+
"pattern": "\\.ssh/id_rsa(?!\\.pub)",
|
|
83
83
|
"risk_level": "critical",
|
|
84
|
-
"risk_type": "
|
|
85
|
-
"reason": "
|
|
84
|
+
"risk_type": "file_access",
|
|
85
|
+
"reason": "检测到访问 SSH 私钥文件 (.ssh/id_rsa)"
|
|
86
86
|
},
|
|
87
87
|
{
|
|
88
88
|
"pattern": "mkfs\\.\\w+\\s+/dev/(sd|hd|nvme|vd|loop|mapper|md|dm)\\S+",
|
|
@@ -156,6 +156,12 @@
|
|
|
156
156
|
"risk_type": "script_execution",
|
|
157
157
|
"reason": "检测到远程下载脚本到本地 (curl -o script.sh),存在后续执行风险"
|
|
158
158
|
},
|
|
159
|
+
{
|
|
160
|
+
"pattern": "base64\\s+-d\\s+.*\\|\\s*bash",
|
|
161
|
+
"risk_level": "high",
|
|
162
|
+
"risk_type": "sensitive_action",
|
|
163
|
+
"reason": "base64 解码执行(编码绕过,下载或读取内容后解码执行)"
|
|
164
|
+
},
|
|
159
165
|
{
|
|
160
166
|
"pattern": "(?:curl|wget)\\s+\\S+.*\\b(?:chmod|chown)\\s+\\+x\\b",
|
|
161
167
|
"risk_level": "high",
|
|
@@ -262,7 +268,7 @@
|
|
|
262
268
|
"pattern": "curl\\s+.*\\|\\s*bash",
|
|
263
269
|
"risk_level": "high",
|
|
264
270
|
"risk_type": "sensitive_action",
|
|
265
|
-
"reason": "curl pipe bash
|
|
271
|
+
"reason": "curl pipe bash 执行(下载远程脚本直接执行)"
|
|
266
272
|
},
|
|
267
273
|
{
|
|
268
274
|
"pattern": "curl\\s+.*\\|\\s*(sh|zsh|dash)",
|
|
@@ -282,12 +288,6 @@
|
|
|
282
288
|
"risk_type": "script_execution",
|
|
283
289
|
"reason": "eval 远程代码执行"
|
|
284
290
|
},
|
|
285
|
-
{
|
|
286
|
-
"pattern": "base64\\s+-d\\s+.*\\|\\s*bash",
|
|
287
|
-
"risk_level": "high",
|
|
288
|
-
"risk_type": "sensitive_action",
|
|
289
|
-
"reason": "base64 解码执行 (user_rules)"
|
|
290
|
-
},
|
|
291
291
|
{
|
|
292
292
|
"pattern": "/dev/tcp/|bash\\s+-i\\s+>&",
|
|
293
293
|
"risk_level": "critical",
|
|
@@ -341,6 +341,42 @@
|
|
|
341
341
|
"risk_level": "critical",
|
|
342
342
|
"risk_type": "script_execution",
|
|
343
343
|
"reason": "检测到命令替换隐藏执行($(...)/反引号载荷含写/执行命令),可能产生文件系统副作用"
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
"pattern": "\\biptables\\s+-(?:[A-Za-z]*[FPIX][A-Za-z]*|[A-Za-z]*[FPIX]\\b)",
|
|
347
|
+
"risk_level": "high",
|
|
348
|
+
"risk_type": "script_execution",
|
|
349
|
+
"reason": "检测到修改防火墙规则(iptables -F/-P/-I/-X),可能关闭安全防护或建立隧道(ATT&CK T1562.004)"
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
"pattern": "\\b(?:ssh|scp)\\b[^;\\n]{0,200}?StrictHostKeyChecking\\s*=\\s*(?:no|accept-new)",
|
|
353
|
+
"risk_level": "high",
|
|
354
|
+
"risk_type": "lateral_movement",
|
|
355
|
+
"reason": "检测到 SSH/SCP 跳过主机密钥验证(-o StrictHostKeyChecking=no),允许中间人攻击,可能用于横向移动"
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
"pattern": "\\bwget\\s+(?:-\\S+\\s+)*--post-(?:data|file)\\s*=\\s*(?:\\$\\([^)]+\\)|[^ ]+|\\\"[^\\\"]+\\\"|'[^']+')\\s+(?!https?://(?:127\\.|localhost))https?://[a-zA-Z0-9.-]+",
|
|
359
|
+
"risk_level": "high",
|
|
360
|
+
"risk_type": "data_exfiltration",
|
|
361
|
+
"reason": "检测到 wget --post-data/--post-file 向外部地址发送数据(可能为数据外传,回环地址除外)"
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
"pattern": "\\bcurl\\b(?:[^;\\n]|&(?![&\\s]))*-X\\s+POST\\b[^;\\n]*https?://(?!(?:127\\.|localhost|\\[::1\\]))[a-zA-Z0-9.-]+",
|
|
365
|
+
"risk_level": "high",
|
|
366
|
+
"risk_type": "data_exfiltration",
|
|
367
|
+
"reason": "检测到 curl -X POST 到外部地址(可能为数据外传,回环地址除外)"
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
"pattern": "\\bcurl\\b[^;&\\n]*-F\\s+\\S+[^;&\\n]*https?://(?!(?:127\\.|localhost|\\[::1\\]))[a-zA-Z0-9.-]+",
|
|
371
|
+
"risk_level": "high",
|
|
372
|
+
"risk_type": "data_exfiltration",
|
|
373
|
+
"reason": "检测到 curl -F 表单上传到外部地址(可能为数据外传,回环地址除外)"
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
"pattern": "(?:@reboot|(?:[\\d*,\\-/]+\\s+){4}[\\d*,\\-/]+)\\s+(?:/tmp|/var/tmp|/dev/shm|/run/shm)/\\S+",
|
|
377
|
+
"risk_level": "high",
|
|
378
|
+
"risk_type": "persistence",
|
|
379
|
+
"reason": "检测到定时任务执行世界可写目录(/tmp、/var/tmp、/dev/shm)下的脚本(cron 持久化后门:任何用户可替换该脚本,重启/定时以任务属主身份执行)"
|
|
344
380
|
}
|
|
345
381
|
],
|
|
346
382
|
"injection_detection": [
|
|
@@ -683,6 +719,48 @@
|
|
|
683
719
|
"deny_mode": "deny_write",
|
|
684
720
|
"scope": "root"
|
|
685
721
|
},
|
|
722
|
+
{
|
|
723
|
+
"path": "/etc/cron.hourly/",
|
|
724
|
+
"risk_level": "high",
|
|
725
|
+
"desc": "系统定时任务目录(小时级)——可放置脚本,重启/定时后以 root 执行",
|
|
726
|
+
"deny_mode": "deny_write",
|
|
727
|
+
"scope": "root"
|
|
728
|
+
},
|
|
729
|
+
{
|
|
730
|
+
"path": "/etc/cron.daily/",
|
|
731
|
+
"risk_level": "high",
|
|
732
|
+
"desc": "系统定时任务目录(日级)",
|
|
733
|
+
"deny_mode": "deny_write",
|
|
734
|
+
"scope": "root"
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
"path": "/etc/cron.weekly/",
|
|
738
|
+
"risk_level": "high",
|
|
739
|
+
"desc": "系统定时任务目录(周级)",
|
|
740
|
+
"deny_mode": "deny_write",
|
|
741
|
+
"scope": "root"
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
"path": "/etc/cron.monthly/",
|
|
745
|
+
"risk_level": "high",
|
|
746
|
+
"desc": "系统定时任务目录(月级)",
|
|
747
|
+
"deny_mode": "deny_write",
|
|
748
|
+
"scope": "root"
|
|
749
|
+
},
|
|
750
|
+
{
|
|
751
|
+
"path": "/etc/init.d/",
|
|
752
|
+
"risk_level": "high",
|
|
753
|
+
"desc": "SysV init 启动脚本目录——agent 写入脚本即持久化(重启后以 root 执行,不依赖 cron)",
|
|
754
|
+
"deny_mode": "deny_write",
|
|
755
|
+
"scope": "root"
|
|
756
|
+
},
|
|
757
|
+
{
|
|
758
|
+
"path": "/etc/rc*.d/",
|
|
759
|
+
"risk_level": "high",
|
|
760
|
+
"desc": "SysV init 运行级目录——指向 init.d 的符号链接,控制各运行级启动",
|
|
761
|
+
"deny_mode": "deny_write",
|
|
762
|
+
"scope": "root"
|
|
763
|
+
},
|
|
686
764
|
{
|
|
687
765
|
"path": "/var/spool/cron/",
|
|
688
766
|
"risk_level": "high",
|
|
@@ -697,6 +775,20 @@
|
|
|
697
775
|
"deny_mode": "deny_write",
|
|
698
776
|
"scope": "root"
|
|
699
777
|
},
|
|
778
|
+
{
|
|
779
|
+
"path": ".config/systemd/user/",
|
|
780
|
+
"risk_level": "high",
|
|
781
|
+
"desc": "用户级 systemd 服务目录——写 .service 并 enable 即用户级持久化(登录后运行)",
|
|
782
|
+
"deny_mode": "deny_write",
|
|
783
|
+
"scope": "home"
|
|
784
|
+
},
|
|
785
|
+
{
|
|
786
|
+
"path": ".config/autostart/",
|
|
787
|
+
"risk_level": "high",
|
|
788
|
+
"desc": "桌面自启动目录——写 .desktop 即登录自启动(Desktop Entry,持久化)",
|
|
789
|
+
"deny_mode": "deny_write",
|
|
790
|
+
"scope": "home"
|
|
791
|
+
},
|
|
700
792
|
{
|
|
701
793
|
"path": "/etc/ssh/sshd_config",
|
|
702
794
|
"risk_level": "high",
|
|
@@ -872,5 +964,6 @@
|
|
|
872
964
|
"deny_mode": "deny_write",
|
|
873
965
|
"scope": "home"
|
|
874
966
|
}
|
|
875
|
-
]
|
|
876
|
-
|
|
967
|
+
],
|
|
968
|
+
"_comment": "\n2026-09-11 新增:iptables 防火墙规则修改(T1562.004)+ SSH 跳过密钥验证(横向移动),原在 user_rules.json 下沉至此——攻击形态清晰、可静态判定,user_rules 回归仅存用户自定义例外的本位。"
|
|
969
|
+
}
|
package/dist/rules/mc_rules.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_comment": "L2.2 模型检测规则单一事实源(Python + TS 共用)。原子命题正则 + LTL 公式(前缀 AST)。公式在事件迹位置 0 处评估:LTL 公式满足 = 攻击存在。对应站点 detection-l2-2.html 的
|
|
2
|
+
"_comment": "L2.2 模型检测规则单一事实源(Python + TS 共用)。原子命题正则 + LTL 公式(前缀 AST)。公式在事件迹位置 0 处评估:LTL 公式满足 = 攻击存在。对应站点 detection-l2-2.html 的 9 条 LTL + 5 条 CTL 规则(2026-09 移除 2 条单动作冗余:mc-ltl-010 F(install_persistence) 与 mc-ctl-001 F(clear_logs) —— 它们误伤正常运维,且攻击场景已由链式规则 006/007/008 覆盖)(CTL 活性降级为 G(!φ) 安全不变量,见 docs/DETECTION_METHODS.md 5.4)。",
|
|
3
3
|
"ap_patterns": {
|
|
4
4
|
"reads_credential": [
|
|
5
5
|
"(?:cat|head|tail|less|more|sed|awk|strings|base64|dd)\\s+[^;&|\\n]*[~]?/\\.ssh(?:/[\\w.-]+)?\\b",
|
|
@@ -336,34 +336,6 @@
|
|
|
336
336
|
"risk_level": "critical",
|
|
337
337
|
"action": "block",
|
|
338
338
|
"reason": "敏感数据→归档→外传(mc-ltl-009)"
|
|
339
|
-
},
|
|
340
|
-
{
|
|
341
|
-
"id": "mc-ltl-010",
|
|
342
|
-
"type": "ltl",
|
|
343
|
-
"formula": [
|
|
344
|
-
"F",
|
|
345
|
-
[
|
|
346
|
-
"ap",
|
|
347
|
-
"install_persistence"
|
|
348
|
-
]
|
|
349
|
-
],
|
|
350
|
-
"risk_level": "medium",
|
|
351
|
-
"action": "approval",
|
|
352
|
-
"reason": "持久化机制安装,需审批(mc-ltl-010)"
|
|
353
|
-
},
|
|
354
|
-
{
|
|
355
|
-
"id": "mc-ctl-001",
|
|
356
|
-
"type": "ltl",
|
|
357
|
-
"formula": [
|
|
358
|
-
"F",
|
|
359
|
-
[
|
|
360
|
-
"ap",
|
|
361
|
-
"clear_logs"
|
|
362
|
-
]
|
|
363
|
-
],
|
|
364
|
-
"risk_level": "critical",
|
|
365
|
-
"action": "block",
|
|
366
|
-
"reason": "安全不变量:任何情况不得清除日志(mc-ctl-001,AG(!clear_logs) 线性迹降级:违反 ⟺ 出现 clear_logs ⟺ F(clear_logs) 成立)"
|
|
367
339
|
}
|
|
368
340
|
]
|
|
369
|
-
}
|
|
341
|
+
}
|
|
@@ -1,77 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"sensitive_actions": [
|
|
3
|
-
{
|
|
4
|
-
"pattern": "写入系统文件",
|
|
5
|
-
"description": "写入 /etc, /usr, /boot 等系统目录下的文件"
|
|
6
|
-
},
|
|
7
|
-
{
|
|
8
|
-
"pattern": "发送外部邮件",
|
|
9
|
-
"description": "通过 email 工具发送邮件到外部地址"
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
"pattern": "危险 shell 命令",
|
|
13
|
-
"description": "执行 rm -rf, chmod 777, sudo 等危险命令"
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"pattern": "修改全局配置",
|
|
17
|
-
"description": "修改系统级配置文件或环境变量"
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
"pattern": "支付操作",
|
|
21
|
-
"description": "涉及支付、转账等金融操作"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"pattern": "访问密钥文件",
|
|
25
|
-
"description": "访问 .ssh/id_rsa, .env, credentials 等密钥/凭证文件"
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
"pattern": "网络数据外传",
|
|
29
|
-
"description": "通过 HTTP/wget/curl 等方式将数据发送到外部服务器"
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
"pattern": "curl * -d *",
|
|
33
|
-
"description": "使用 curl 发送数据到外部服务器(可能为数据外传)"
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
"pattern": "curl * POST *",
|
|
37
|
-
"description": "使用 curl POST 请求发送数据到外部服务器"
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
"pattern": "wget *--post-data*",
|
|
41
|
-
"description": "使用 wget 发送 POST 数据到外部服务器"
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
"pattern": "crontab -e",
|
|
45
|
-
"description": "编辑定时任务(可能用于持久化后门)"
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
"pattern": "crontab -r",
|
|
49
|
-
"description": "删除定时任务(可能用于清除审计痕迹)"
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
"pattern": "* | crontab",
|
|
53
|
-
"description": "管道方式安装/修改定时任务(可能用于持久化后门)"
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
"pattern": "iptables -",
|
|
57
|
-
"description": "修改防火墙规则(可能用于关闭安全防护)"
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
"pattern": "ssh* -o StrictHostKeyChecking=no",
|
|
61
|
-
"description": "SSH 跳过主机密钥验证(可能用于横向移动)"
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
"pattern": "base64 -d * bash",
|
|
65
|
-
"description": "Base64 解码后执行(可能用于编码绕过)"
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
"pattern": "curl * | bash",
|
|
69
|
-
"description": "下载远程脚本并执行(远程代码执行风险)"
|
|
70
|
-
}
|
|
71
|
-
],
|
|
2
|
+
"sensitive_actions": [],
|
|
72
3
|
"sensitive_tools": [
|
|
73
4
|
"eval",
|
|
74
5
|
"exec",
|
|
75
6
|
"system"
|
|
76
7
|
]
|
|
77
|
-
}
|
|
8
|
+
}
|
package/dist/server.js
CHANGED
|
@@ -407,6 +407,9 @@ function isLiteralTextCommand(command) {
|
|
|
407
407
|
return false;
|
|
408
408
|
}
|
|
409
409
|
}
|
|
410
|
+
if (/\|\s*\b(?:tee|tac|install)\b/i.test(command)) {
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
410
413
|
return true;
|
|
411
414
|
}
|
|
412
415
|
function extractInlineCode(command) {
|
|
@@ -1307,22 +1310,45 @@ var init_logic_rules = __esm({
|
|
|
1307
1310
|
}
|
|
1308
1311
|
});
|
|
1309
1312
|
|
|
1313
|
+
// src/engine/asset-dir.ts
|
|
1314
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1315
|
+
import { resolve as resolve2 } from "path";
|
|
1316
|
+
function assetDirCandidates(moduleDir, name) {
|
|
1317
|
+
return [
|
|
1318
|
+
resolve2(moduleDir, name),
|
|
1319
|
+
resolve2(moduleDir, "../../..", "agent_moss", name)
|
|
1320
|
+
];
|
|
1321
|
+
}
|
|
1322
|
+
function resolveAssetDir(moduleDir, name) {
|
|
1323
|
+
for (const dir of assetDirCandidates(moduleDir, name)) {
|
|
1324
|
+
if (existsSync3(dir)) return dir;
|
|
1325
|
+
}
|
|
1326
|
+
return null;
|
|
1327
|
+
}
|
|
1328
|
+
var init_asset_dir = __esm({
|
|
1329
|
+
"src/engine/asset-dir.ts"() {
|
|
1330
|
+
"use strict";
|
|
1331
|
+
init_esm_shims();
|
|
1332
|
+
}
|
|
1333
|
+
});
|
|
1334
|
+
|
|
1310
1335
|
// src/engine/skill-loader.ts
|
|
1311
|
-
import { readFileSync as readFileSync3, readdirSync as readdirSync2
|
|
1312
|
-
import { resolve as
|
|
1336
|
+
import { readFileSync as readFileSync3, readdirSync as readdirSync2 } from "fs";
|
|
1337
|
+
import { resolve as resolve3, dirname as dirname2 } from "path";
|
|
1313
1338
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
1314
1339
|
function loadAllSkills() {
|
|
1315
1340
|
if (cachedSkills) return cachedSkills;
|
|
1316
1341
|
cachedSkills = {};
|
|
1317
|
-
const dir =
|
|
1318
|
-
if (!
|
|
1319
|
-
|
|
1342
|
+
const dir = resolveAssetDir(__dirname3, "skills");
|
|
1343
|
+
if (!dir) {
|
|
1344
|
+
const [primary, fallback] = assetDirCandidates(__dirname3, "skills");
|
|
1345
|
+
console.warn("[AgentMoss] Skills directory not found:", primary, "and fallback:", fallback);
|
|
1320
1346
|
return cachedSkills;
|
|
1321
1347
|
}
|
|
1322
1348
|
const files = readdirSync2(dir).filter((f) => f.endsWith(".md"));
|
|
1323
1349
|
for (const f of files) {
|
|
1324
1350
|
const name = f.replace(".md", "");
|
|
1325
|
-
cachedSkills[name] = readFileSync3(
|
|
1351
|
+
cachedSkills[name] = readFileSync3(resolve3(dir, f), "utf-8");
|
|
1326
1352
|
}
|
|
1327
1353
|
console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills from ${dir}`);
|
|
1328
1354
|
return cachedSkills;
|
|
@@ -1376,16 +1402,15 @@ function formatSkillsForPrompt(skills) {
|
|
|
1376
1402
|
${s.skillContent}`
|
|
1377
1403
|
).join("\n\n---\n\n");
|
|
1378
1404
|
}
|
|
1379
|
-
var __filename3, __dirname3,
|
|
1405
|
+
var __filename3, __dirname3, SKILL_KEYWORD_MAP, cachedSkills;
|
|
1380
1406
|
var init_skill_loader = __esm({
|
|
1381
1407
|
"src/engine/skill-loader.ts"() {
|
|
1382
1408
|
"use strict";
|
|
1383
1409
|
init_esm_shims();
|
|
1384
1410
|
init_rules_loader();
|
|
1411
|
+
init_asset_dir();
|
|
1385
1412
|
__filename3 = fileURLToPath4(import.meta.url);
|
|
1386
1413
|
__dirname3 = dirname2(__filename3);
|
|
1387
|
-
SKILLS_DIR = resolve2(__dirname3, "../skills");
|
|
1388
|
-
SKILLS_DIR_FALLBACK = resolve2(__dirname3, "../../../agent_moss/skills");
|
|
1389
1414
|
SKILL_KEYWORD_MAP = (() => {
|
|
1390
1415
|
const map = {};
|
|
1391
1416
|
const cats = loadL3();
|
|
@@ -2555,7 +2580,7 @@ var init_runtime_config = __esm({
|
|
|
2555
2580
|
|
|
2556
2581
|
// src/agents/index.ts
|
|
2557
2582
|
import { readFileSync as readFileSync5, existsSync as existsSync5 } from "fs";
|
|
2558
|
-
import { resolve as
|
|
2583
|
+
import { resolve as resolve4, dirname as dirname4 } from "path";
|
|
2559
2584
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
2560
2585
|
function resolveAgentsDir() {
|
|
2561
2586
|
if (existsSync5(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
|
|
@@ -2575,7 +2600,7 @@ function getAgentExtensions(agentId) {
|
|
|
2575
2600
|
}
|
|
2576
2601
|
if (ext === null) {
|
|
2577
2602
|
const agentsDir = resolveAgentsDir();
|
|
2578
|
-
const rulesFile =
|
|
2603
|
+
const rulesFile = resolve4(agentsDir, agentId, "rules.json");
|
|
2579
2604
|
if (!existsSync5(rulesFile)) {
|
|
2580
2605
|
cache.set(agentId, null);
|
|
2581
2606
|
return null;
|
|
@@ -2622,15 +2647,15 @@ var init_agents = __esm({
|
|
|
2622
2647
|
init_esm_shims();
|
|
2623
2648
|
__filename4 = fileURLToPath6(import.meta.url);
|
|
2624
2649
|
__dirname5 = dirname4(__filename4);
|
|
2625
|
-
AGENTS_DIR_PROD =
|
|
2626
|
-
AGENTS_DIR_DEV_FALLBACK =
|
|
2650
|
+
AGENTS_DIR_PROD = resolve4(__dirname5, "agents");
|
|
2651
|
+
AGENTS_DIR_DEV_FALLBACK = resolve4(__dirname5, "../../../agent_moss/agents");
|
|
2627
2652
|
cache = /* @__PURE__ */ new Map();
|
|
2628
2653
|
}
|
|
2629
2654
|
});
|
|
2630
2655
|
|
|
2631
2656
|
// src/scenarios/index.ts
|
|
2632
2657
|
import { readFileSync as readFileSync6, existsSync as existsSync6 } from "fs";
|
|
2633
|
-
import { resolve as
|
|
2658
|
+
import { resolve as resolve5, dirname as dirname5 } from "path";
|
|
2634
2659
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
2635
2660
|
function resolveScenariosDir() {
|
|
2636
2661
|
if (existsSync6(SCENARIOS_DIR_PROD)) return SCENARIOS_DIR_PROD;
|
|
@@ -2650,7 +2675,7 @@ function getScenarioExtensions(scenarioId) {
|
|
|
2650
2675
|
}
|
|
2651
2676
|
if (ext === null) {
|
|
2652
2677
|
const scenariosDir = resolveScenariosDir();
|
|
2653
|
-
const rulesFile =
|
|
2678
|
+
const rulesFile = resolve5(scenariosDir, scenarioId, "rules.json");
|
|
2654
2679
|
if (!existsSync6(rulesFile)) {
|
|
2655
2680
|
cache2.set(scenarioId, null);
|
|
2656
2681
|
return null;
|
|
@@ -2703,8 +2728,8 @@ var init_scenarios = __esm({
|
|
|
2703
2728
|
init_agents();
|
|
2704
2729
|
__filename5 = fileURLToPath7(import.meta.url);
|
|
2705
2730
|
__dirname6 = dirname5(__filename5);
|
|
2706
|
-
SCENARIOS_DIR_PROD =
|
|
2707
|
-
SCENARIOS_DIR_DEV_FALLBACK =
|
|
2731
|
+
SCENARIOS_DIR_PROD = resolve5(__dirname6, "scenarios");
|
|
2732
|
+
SCENARIOS_DIR_DEV_FALLBACK = resolve5(__dirname6, "../../../agent_moss/scenarios");
|
|
2708
2733
|
cache2 = /* @__PURE__ */ new Map();
|
|
2709
2734
|
}
|
|
2710
2735
|
});
|
|
@@ -2891,7 +2916,7 @@ function buildPathContext(actionDetail) {
|
|
|
2891
2916
|
}
|
|
2892
2917
|
function stripLiteralTextSegments(actionDetail) {
|
|
2893
2918
|
const writeScriptExec = />\s*[/~]\S*\.(sh|py|pl|rb)\b/.test(actionDetail) && /(?:&&|\|\||;|\n|\|)\s*(?:bash|sh|zsh|dash|python|perl|ruby|node)/.test(actionDetail);
|
|
2894
|
-
const pipeToInterpreter = /\|\s*(?:bash|sh|zsh|dash|python|perl|ruby|node)\b/.test(actionDetail);
|
|
2919
|
+
const pipeToInterpreter = /\|\s*(?:bash|sh|zsh|dash|python|perl|ruby|node|crontab|at)\b/.test(actionDetail);
|
|
2895
2920
|
if (writeScriptExec || pipeToInterpreter) {
|
|
2896
2921
|
return actionDetail;
|
|
2897
2922
|
}
|
|
@@ -4032,13 +4057,13 @@ var require_backup = __commonJS({
|
|
|
4032
4057
|
var runBackup = (backup, handler) => {
|
|
4033
4058
|
let rate = 0;
|
|
4034
4059
|
let useDefault = true;
|
|
4035
|
-
return new Promise((
|
|
4060
|
+
return new Promise((resolve10, reject) => {
|
|
4036
4061
|
setImmediate(function step() {
|
|
4037
4062
|
try {
|
|
4038
4063
|
const progress = backup.transfer(rate);
|
|
4039
4064
|
if (!progress.remainingPages) {
|
|
4040
4065
|
backup.close();
|
|
4041
|
-
|
|
4066
|
+
resolve10(progress);
|
|
4042
4067
|
return;
|
|
4043
4068
|
}
|
|
4044
4069
|
if (useDefault) {
|
|
@@ -4517,25 +4542,25 @@ init_skill_loader();
|
|
|
4517
4542
|
|
|
4518
4543
|
// src/engine/template-loader.ts
|
|
4519
4544
|
init_esm_shims();
|
|
4545
|
+
init_asset_dir();
|
|
4520
4546
|
import { readFileSync as readFileSync7, existsSync as existsSync7 } from "fs";
|
|
4521
|
-
import { resolve as
|
|
4547
|
+
import { resolve as resolve6, dirname as dirname6 } from "path";
|
|
4522
4548
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
4523
4549
|
var __filename6 = fileURLToPath8(import.meta.url);
|
|
4524
4550
|
var __dirname7 = dirname6(__filename6);
|
|
4525
|
-
var TEMPLATES_DIR = resolve5(__dirname7, "../templates");
|
|
4526
|
-
var TEMPLATES_DIR_FALLBACK = resolve5(__dirname7, "../../../agent_moss/templates");
|
|
4527
4551
|
var TEMPLATE_FILES = ["prompt1_template.txt", "prompt2_template.txt", "security_judge_template.txt"];
|
|
4528
4552
|
var cachedTemplates = null;
|
|
4529
4553
|
function loadTemplates() {
|
|
4530
4554
|
if (cachedTemplates) return cachedTemplates;
|
|
4531
4555
|
cachedTemplates = {};
|
|
4532
|
-
const dir =
|
|
4533
|
-
if (!
|
|
4534
|
-
|
|
4556
|
+
const dir = resolveAssetDir(__dirname7, "templates");
|
|
4557
|
+
if (!dir) {
|
|
4558
|
+
const [primary, fallback] = assetDirCandidates(__dirname7, "templates");
|
|
4559
|
+
console.warn("[AgentMoss] Templates directory not found:", primary, "and fallback:", fallback);
|
|
4535
4560
|
return cachedTemplates;
|
|
4536
4561
|
}
|
|
4537
4562
|
for (const file of TEMPLATE_FILES) {
|
|
4538
|
-
const filePath =
|
|
4563
|
+
const filePath = resolve6(dir, file);
|
|
4539
4564
|
if (existsSync7(filePath)) {
|
|
4540
4565
|
const key = file.replace(".txt", "").replace("_template", "");
|
|
4541
4566
|
cachedTemplates[key] = readFileSync7(filePath, "utf-8");
|
|
@@ -4548,7 +4573,7 @@ function loadTemplates() {
|
|
|
4548
4573
|
// src/engine/script-analyzer.ts
|
|
4549
4574
|
init_esm_shims();
|
|
4550
4575
|
import { readFileSync as readFileSync8 } from "fs";
|
|
4551
|
-
import { resolve as
|
|
4576
|
+
import { resolve as resolve7, join as join4 } from "path";
|
|
4552
4577
|
var SUSPICIOUS_PATTERNS = [
|
|
4553
4578
|
// Network exfiltration (curl/wget POST in scripts is almost never legitimate)
|
|
4554
4579
|
{ name: "curl POST", pattern: /curl\s+(.*\s)?-X\s+POST/i },
|
|
@@ -4681,7 +4706,7 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
|
|
|
4681
4706
|
}
|
|
4682
4707
|
function readScriptContent(scriptPath, maxLines = 500) {
|
|
4683
4708
|
try {
|
|
4684
|
-
const expandedPath = scriptPath.startsWith("~") ? join4(process.env.HOME || "/home/user", scriptPath.slice(2)) :
|
|
4709
|
+
const expandedPath = scriptPath.startsWith("~") ? join4(process.env.HOME || "/home/user", scriptPath.slice(2)) : resolve7(scriptPath);
|
|
4685
4710
|
const raw = readFileSync8(expandedPath, "utf-8");
|
|
4686
4711
|
const lines = raw.split("\n").slice(0, maxLines);
|
|
4687
4712
|
return { content: lines.join("\n"), lineCount: lines.length, error: "" };
|
|
@@ -4738,7 +4763,7 @@ init_heuristic();
|
|
|
4738
4763
|
init_logic_rules();
|
|
4739
4764
|
init_inline_analyzer();
|
|
4740
4765
|
import { statSync as statSync3, existsSync as existsSync9 } from "fs";
|
|
4741
|
-
import { resolve as
|
|
4766
|
+
import { resolve as resolve9, join as join6, isAbsolute as isAbsolute2 } from "path";
|
|
4742
4767
|
import { homedir as homedir4 } from "os";
|
|
4743
4768
|
|
|
4744
4769
|
// src/engine/script-engine/tracker.ts
|
|
@@ -6441,7 +6466,7 @@ function resolvePath2(p) {
|
|
|
6441
6466
|
if (expanded.startsWith("~/") || expanded === "~") {
|
|
6442
6467
|
expanded = join6(homedir4(), expanded.slice(1));
|
|
6443
6468
|
}
|
|
6444
|
-
return
|
|
6469
|
+
return resolve9(expanded);
|
|
6445
6470
|
}
|
|
6446
6471
|
function scanScriptChainWithL1(chain, input) {
|
|
6447
6472
|
for (const node of chain.nodes) {
|
|
@@ -6817,7 +6842,7 @@ async function analyzeWithLLM(prompt, config) {
|
|
|
6817
6842
|
}
|
|
6818
6843
|
if (attempt < maxRetries) {
|
|
6819
6844
|
console.error(`[AgentMoss] LLM call failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${retryCfg.retryInterval}s:`, err.message);
|
|
6820
|
-
await new Promise((
|
|
6845
|
+
await new Promise((resolve10) => setTimeout(resolve10, retryCfg.retryInterval * 1e3));
|
|
6821
6846
|
} else {
|
|
6822
6847
|
clearTimeout(totalTimeoutTimer);
|
|
6823
6848
|
console.error("[AgentMoss] LLM call failed after all retries:", err.message);
|
|
@@ -10046,12 +10071,12 @@ function createApp() {
|
|
|
10046
10071
|
}
|
|
10047
10072
|
async function findFreePort(startPort, host = "127.0.0.1", scan = 100) {
|
|
10048
10073
|
for (let port = startPort; port < startPort + scan; port++) {
|
|
10049
|
-
const free = await new Promise((
|
|
10074
|
+
const free = await new Promise((resolve10) => {
|
|
10050
10075
|
const server = net.createServer();
|
|
10051
10076
|
server.listen(port, host, () => {
|
|
10052
|
-
server.close(() =>
|
|
10077
|
+
server.close(() => resolve10(true));
|
|
10053
10078
|
});
|
|
10054
|
-
server.on("error", () =>
|
|
10079
|
+
server.on("error", () => resolve10(false));
|
|
10055
10080
|
});
|
|
10056
10081
|
if (free) {
|
|
10057
10082
|
if (port !== startPort) {
|
|
@@ -10092,4 +10117,3 @@ export {
|
|
|
10092
10117
|
//! P6 mc-ltl-rules LTL 模型检测
|
|
10093
10118
|
//! P7 low-integrity-command-exec 低完整性命令执行
|
|
10094
10119
|
//! P8 sensitive-data-aggregation 敏感数据聚合外传
|
|
10095
|
-
//# sourceMappingURL=server.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kenhkl/agent-moss",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.7",
|
|
4
4
|
"description": "Multi-layer security analysis engine for AI agents (TypeScript)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/server.js",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
|
22
22
|
"dist",
|
|
23
|
+
"!dist/**/*.map",
|
|
23
24
|
"README.md"
|
|
24
25
|
],
|
|
25
26
|
"scripts": {
|