@kenhkl/agent-moss 0.12.6 → 0.12.8

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.
@@ -822,6 +822,27 @@
822
822
  "desc": "环境变量/凭据配置文件",
823
823
  "deny_mode": "deny_both",
824
824
  "scope": "credential"
825
+ },
826
+ {
827
+ "path": ".config/agentmoss/config.json",
828
+ "risk_level": "critical",
829
+ "desc": "AgentMoss 配置中的 LLM api_key(凭据),读=泄露,写=篡改,均不可",
830
+ "deny_mode": "deny_both",
831
+ "scope": "home"
832
+ },
833
+ {
834
+ "path": ".config/agentmoss",
835
+ "risk_level": "high",
836
+ "desc": "AgentMoss 自身配置目录(runtime 规则/token 用量),agent 可读但不可写(防篡改关闭安全检测)",
837
+ "deny_mode": "deny_write",
838
+ "scope": "home"
839
+ },
840
+ {
841
+ "path": ".agentmoss/brain",
842
+ "risk_level": "high",
843
+ "desc": "AgentMoss Brain 数据(rules.json/skill_meta.json 自学习成果),agent 可读自查但不可写(防投毒)",
844
+ "deny_mode": "deny_write",
845
+ "scope": "home"
825
846
  }
826
847
  ]
827
848
  }
@@ -78,5 +78,140 @@
78
78
  {
79
79
  "pattern": "\\blgroupdel\\s"
80
80
  }
81
- ]
81
+ ],
82
+ "combo_chains": {
83
+ "_comment": "L2 组合攻击链检测:单步操作可能无害,但按 kill chain 顺序组合即为后渗透攻击(参考 ChainWatch 六阶段 kill chain / 玄武实验室 Hermes 案例)。阶段 pattern 匹配 action_detail(含 tool call 参数),链定义要求至少 min_stages 个阶段按顺序出现才命中,防单步误报。A 会话内杀伤链 / D 提权-利用组合 / E 持久化-外联组合在 L2 实现(基于 action_history 子序列匹配);B 跨会话组合链 / C 敏感数据传播链 / F 跨设备组合依赖行为图与数据流,由 formal_engine 实现。",
84
+ "stages": {
85
+ "LISTEN": [
86
+ "\\bflask\\b",
87
+ "\\bfastapi\\b",
88
+ "httpserver",
89
+ "http\\.server",
90
+ "\\bnc\\s+-l",
91
+ "\\bsocat\\b",
92
+ "nohup\\s+.*\\bpython",
93
+ "create\\s+.*listener",
94
+ "listen\\("
95
+ ],
96
+ "EXEC": [
97
+ "subprocess",
98
+ "os\\.system",
99
+ "shell\\s*=\\s*True",
100
+ "\\beval\\(",
101
+ "\\bexec\\(",
102
+ "/exec",
103
+ "\\bbash\\s+-c",
104
+ "run.*command"
105
+ ],
106
+ "SCAN": [
107
+ "\\bnc\\s+-z",
108
+ "\\bnmap\\b",
109
+ "port\\s*scan",
110
+ "\\bseq\\b.*\\bnc\\b",
111
+ "192\\.168\\..*nc",
112
+ "scan.*\\bport"
113
+ ],
114
+ "CRED": [
115
+ "known_hosts",
116
+ "\\.ssh",
117
+ "id_rsa",
118
+ "/etc/shadow",
119
+ "/etc/passwd",
120
+ "credential",
121
+ "aws.*credentials"
122
+ ],
123
+ "EXFIL": [
124
+ "\\bcurl\\s+.*https?://",
125
+ "\\bwget\\s+.*https?://",
126
+ "\\bscp\\s+.*@",
127
+ "\\brsync\\s+.*@",
128
+ "POST.*https?://"
129
+ ],
130
+ "PERSIST": [
131
+ "\\bcrontab\\b",
132
+ "\\bsystemd\\b",
133
+ "systemctl\\s+enable",
134
+ "\\.bashrc",
135
+ "\\.profile",
136
+ "/etc/rc\\.local",
137
+ "\\bnohup\\b",
138
+ "\\bdaemon\\b",
139
+ "startup\\s+item",
140
+ "\\bat\\s+reboot"
141
+ ],
142
+ "PRIVESC": [
143
+ "\\bsudo\\b",
144
+ "\\bsu\\b",
145
+ "chmod\\s+u\\s*\\+\\s*s",
146
+ "\\bsetuid\\b",
147
+ "\\bpkexec\\b",
148
+ "\\bdoas\\b"
149
+ ]
150
+ },
151
+ "chains": [
152
+ {
153
+ "id": "listener-exec-scan",
154
+ "stages": ["LISTEN", "EXEC", "SCAN"],
155
+ "min_stages": 3,
156
+ "risk_level": "high",
157
+ "reason": "组合攻击链:建立监听服务 → 获取命令执行能力 → 网络侦察"
158
+ },
159
+ {
160
+ "id": "listener-exec-cred",
161
+ "stages": ["LISTEN", "EXEC", "CRED"],
162
+ "min_stages": 3,
163
+ "risk_level": "critical",
164
+ "reason": "组合攻击链:建立监听服务 → 获取命令执行能力 → 访问凭据文件"
165
+ },
166
+ {
167
+ "id": "listener-exec-exfil",
168
+ "stages": ["LISTEN", "EXEC", "EXFIL"],
169
+ "min_stages": 3,
170
+ "risk_level": "critical",
171
+ "reason": "组合攻击链:建立监听服务 → 获取命令执行能力 → 数据外传"
172
+ },
173
+ {
174
+ "id": "listener-exec-persist",
175
+ "stages": ["LISTEN", "EXEC", "PERSIST"],
176
+ "min_stages": 3,
177
+ "risk_level": "high",
178
+ "reason": "组合攻击链:建立监听服务 → 获取命令执行能力 → 持久化后门"
179
+ },
180
+ {
181
+ "id": "exec-scan-cred",
182
+ "stages": ["EXEC", "SCAN", "CRED"],
183
+ "min_stages": 3,
184
+ "risk_level": "high",
185
+ "reason": "组合攻击链:获取命令执行能力 → 网络侦察 → 访问凭据文件"
186
+ },
187
+ {
188
+ "id": "exec-privesc-cred",
189
+ "stages": ["EXEC", "PRIVESC", "CRED"],
190
+ "min_stages": 3,
191
+ "risk_level": "high",
192
+ "reason": "组合攻击链:获取命令执行能力 → 提权 → 访问凭据文件"
193
+ },
194
+ {
195
+ "id": "privesc-cred-exfil",
196
+ "stages": ["PRIVESC", "CRED", "EXFIL"],
197
+ "min_stages": 3,
198
+ "risk_level": "critical",
199
+ "reason": "组合攻击链:提权 → 访问凭据文件 → 数据外传"
200
+ },
201
+ {
202
+ "id": "persist-exfil",
203
+ "stages": ["PERSIST", "EXFIL"],
204
+ "min_stages": 2,
205
+ "risk_level": "high",
206
+ "reason": "组合攻击链:持久化后门 → 数据外传(C2 信标)"
207
+ },
208
+ {
209
+ "id": "exec-persist-exfil",
210
+ "stages": ["EXEC", "PERSIST", "EXFIL"],
211
+ "min_stages": 3,
212
+ "risk_level": "critical",
213
+ "reason": "组合攻击链:获取命令执行能力 → 持久化后门 → 数据外传"
214
+ }
215
+ ]
216
+ }
82
217
  }
@@ -0,0 +1,119 @@
1
+ """场景定制化规则注册中心(客户沙箱场景级规则)
2
+
3
+ 按 `scenario_id` 加载该场景的专属规则集(JSON 单一事实源)。
4
+ 规则文件位置:`agent_moss/scenarios/<scenario_id>/rules.json`
5
+
6
+ 与 agent 定制规则(agent_moss/agents/<id>/rules.json)**同构**——场景规则也是
7
+ `AgentExtensions` 结构(extra_command_patterns / extra_injection_keywords /
8
+ extra_sensitive_paths / builtin_safe_skills / builtin_safe_action_types),
9
+ 唯一区别是顶层 id 字段叫 `scenario_id` 而非 `agent_id`,由
10
+ `AgentExtensions.from_json(..., id_key="scenario_id")` 解析。
11
+
12
+ 设计动机:agent 规则靠调用方传 `agent_id`(真实 agent instance 身份)启用;
13
+ 场景规则是**部署级**约束(客户沙箱的目录访问边界、禁 ssh 等),没有天然调用方
14
+ 身份,启用判定为:
15
+ 1. 调用方显式传 `scenario_id`(请求级,与 agent_id 并列)
16
+ 2. 兜底环境变量 `AGENT_MOSS_SCENARIO`(部署级,客户沙箱设一次,所有请求生效)
17
+ 3. 两者都空 → 通用模式(不加载场景规则)
18
+
19
+ 使用:
20
+ from agent_moss.scenarios import get_scenario_extensions
21
+ ext = get_scenario_extensions("bank")
22
+ if ext:
23
+ # 把 ext.extra_command_patterns 追加到 agent_ext/通用规则之后
24
+ ...
25
+
26
+ Python 与 TypeScript 两端共享同一份 rules.json:
27
+ - py: agent_moss/scenarios/__init__.py:get_scenario_extensions
28
+ - ts: ts/src/scenarios/index.ts:getScenarioExtensions
29
+
30
+ 无 scenario_id 或 rules.json 不存在时返回 None(通用模式)。
31
+ """
32
+
33
+ from pathlib import Path
34
+ import threading
35
+ import logging
36
+
37
+ # 场景规则与 agent 规则同构:复用 AgentExtensions 数据载体(agents/base.py)
38
+ from ..agents.base import AgentExtensions, InjectionKeyword, CommandPattern, SensitivePath # noqa: F401
39
+
40
+ logger = logging.getLogger(__name__)
41
+
42
+ _SCENARIOS_DIR = Path(__file__).parent
43
+ _cache: dict[str, AgentExtensions | None] = {}
44
+ _lock = threading.Lock()
45
+
46
+
47
+ def get_scenario_extensions(scenario_id: str) -> AgentExtensions | None:
48
+ """按 scenario_id 加载该场景的专属规则集。
49
+
50
+ Args:
51
+ scenario_id: 场景标识,如 "bank"。空字符串返回 None(通用模式)。
52
+
53
+ Returns:
54
+ AgentExtensions 实例,或 None(无场景专属规则 / rules.json 不存在)。
55
+ """
56
+ if not scenario_id:
57
+ return None
58
+
59
+ # 双重检查锁定,避免并发重复 IO
60
+ if scenario_id in _cache:
61
+ return _cache[scenario_id]
62
+
63
+ with _lock:
64
+ if scenario_id in _cache:
65
+ return _cache[scenario_id]
66
+
67
+ # 统一存储:优先读 runtime_config 的 scenario_rules 分区(用户可在 console
68
+ # 查看/编辑/禁用/新增,与 agent_rules 同构,享受一致的合并升级)。回退
69
+ # rules.json(无 runtime_config 时,如直接起服务未 seed、或旧部署)。
70
+ ext = None
71
+ try:
72
+ from ..infra.runtime_config import (has_runtime_config, load_runtime_config,
73
+ get_enabled_scenario_extensions)
74
+ if has_runtime_config():
75
+ ext = get_enabled_scenario_extensions(load_runtime_config(), scenario_id)
76
+ except Exception:
77
+ pass # runtime_config 不可用时回退 rules.json
78
+
79
+ if ext is None:
80
+ rules_file = _SCENARIOS_DIR / scenario_id / "rules.json"
81
+ if not rules_file.exists():
82
+ logger.debug("场景 %s 无 rules.json,走通用模式", scenario_id)
83
+ _cache[scenario_id] = None
84
+ return None
85
+ try:
86
+ ext = AgentExtensions.from_json(rules_file, id_key="scenario_id")
87
+ except Exception as e:
88
+ logger.error("加载场景 %s rules.json 失败: %s", scenario_id, e)
89
+ _cache[scenario_id] = None
90
+ return None
91
+
92
+ _cache[scenario_id] = ext
93
+ logger.info(
94
+ "加载场景 %s 定制规则: %d cmd patterns, %d injection keywords, "
95
+ "%d sensitive paths, %d builtin safe skills, %d builtin safe action types",
96
+ scenario_id,
97
+ len(ext.extra_command_patterns),
98
+ len(ext.extra_injection_keywords),
99
+ len(ext.extra_sensitive_paths),
100
+ len(ext.builtin_safe_skills),
101
+ len(ext.builtin_safe_action_types),
102
+ )
103
+ return ext
104
+
105
+
106
+ def clear_scenario_extensions_cache() -> None:
107
+ """清空场景扩展缓存(用于测试或 rules.json 热加载)"""
108
+ with _lock:
109
+ _cache.clear()
110
+
111
+
112
+ __all__ = [
113
+ "AgentExtensions",
114
+ "InjectionKeyword",
115
+ "CommandPattern",
116
+ "SensitivePath",
117
+ "get_scenario_extensions",
118
+ "clear_scenario_extensions_cache",
119
+ ]
@@ -0,0 +1,23 @@
1
+ {
2
+ "scenario_id": "bank",
3
+ "description": "银行沙箱场景定制规则。客户约束:沙箱用户(sandbox)仅允许读写 /home/sandbox、/opt/sandbox、/tmp 三个目录,其他根目录一律禁止读写;禁止执行 ssh 命令(横向移动/远程访问)。",
4
+ "extra_command_patterns": [
5
+ {
6
+ "pattern": "(?:^|[;\\s>&|@'\"])(/(?!home/sandbox(?:\\s|/|$)|opt/sandbox(?:\\s|/|$)|tmp(?:\\s|/|$))[^\\s;&|>\"]+)",
7
+ "risk_level": "high",
8
+ "risk_type": "file_access",
9
+ "reason": "bank 场景:访问允许目录(/home/sandbox、/opt/sandbox、/tmp)之外的绝对路径"
10
+ },
11
+ {
12
+ "pattern": "(?:^|[;&|:]\\s*)ssh(?:\\s|$)",
13
+ "risk_level": "critical",
14
+ "risk_type": "network_access",
15
+ "reason": "bank 场景:禁止执行 ssh 命令(横向移动/远程访问)"
16
+ }
17
+ ],
18
+ "extra_injection_keywords": [],
19
+ "extra_sensitive_paths": [],
20
+ "builtin_safe_skills": [],
21
+ "builtin_safe_action_types": [],
22
+ "extra_skills": []
23
+ }