dsh-weiwen-law 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -11
- package/package.json +103 -101
- package/src/core/engine.mjs +224 -22
- package/src/index.js +106 -26
- package/test/alignment.test.mjs +64 -0
- package/test/anchor-channel.test.mjs +367 -0
- package/test/attribution.test.mjs +95 -0
- package/test/blind-spot-20260830.test.mjs +286 -0
- package/test/broken-window-heal.test.mjs +78 -0
- package/test/bugstop.test.mjs +90 -0
- package/test/cases.test.mjs +196 -0
- package/test/criterion-wiring.test.mjs +196 -0
- package/test/deduce_risk.test.mjs +157 -0
- package/test/dict-as-s-gate.test.mjs +204 -0
- package/test/docs-engine-sync.test.mjs +73 -0
- package/test/engine.test.mjs +211 -0
- package/test/fail-closed-gate.test.mjs +65 -0
- package/test/inner-h-parked.test.mjs +220 -0
- package/test/ledger-api-integration.test.mjs +74 -0
- package/test/m-court.test.mjs +171 -0
- package/test/mutation-gate.test.mjs +92 -0
- package/test/nested-wiring.test.mjs +383 -0
- package/test/opaque_exec.test.mjs +213 -0
- package/test/police-gate.test.mjs +81 -0
- package/test/projection-export.test.mjs +41 -0
- package/test/rcapacity-wiring.test.mjs +248 -0
- package/test/receipt-gate.test.mjs +185 -0
- package/test/residual-rdomain-fractal.test.mjs +142 -0
- package/test/s-account-ledger.test.mjs +126 -0
- package/test/scar-anchor-gate.test.mjs +150 -0
- package/test/sd-effect-fusion.test.mjs +127 -0
- package/test/sd-type-gate.test.mjs +123 -0
- package/test/session-ledger-buckets.test.mjs +55 -0
- package/test/speech-act-publish.test.mjs +82 -0
- package/test/strata-wiring.test.mjs +136 -0
- package/test/structure-integration.test.mjs +78 -0
- package/test/verb-wiring.test.mjs +1235 -0
package/README.md
CHANGED
|
@@ -82,16 +82,16 @@ node examples/demo-tool-loop.mjs
|
|
|
82
82
|
|
|
83
83
|
## 挂到 DSH(生产挂载)
|
|
84
84
|
|
|
85
|
-
把 `weiwen-law.patch.yml` 作为 overlay 接入你的 DSH profile(具体路径以你的 DSH 版本为准,详见 [`DESIGN.md`](./DESIGN.md) 的挂载章节)。接入后,运行在该 profile 的 Agent 自动获得
|
|
85
|
+
把 `weiwen-law.patch.yml` 作为 overlay 接入你的 DSH profile(具体路径以你的 DSH 版本为准,详见 [`DESIGN.md`](./DESIGN.md) 的挂载章节)。接入后,运行在该 profile 的 Agent 自动获得 11 个白箱工具。
|
|
86
86
|
|
|
87
|
-
> 注:原生挂载的精确 profile 路径随 DSH 版本变化;本仓库已通过实跑验证插件可被 DSH 加载、
|
|
87
|
+
> 注:原生挂载的精确 profile 路径随 DSH 版本变化;本仓库已通过实跑验证插件可被 DSH 加载、11 工具全部注册。如官方 API 有变更,以官方 docs 当前版本为准核对。
|
|
88
88
|
|
|
89
89
|
## 模型怎么调用(给 AI 工程师)
|
|
90
90
|
|
|
91
|
-
> **白话版**:插件向 DSH 注册
|
|
91
|
+
> **白话版**:插件向 DSH 注册 11 个白箱工具,模型像调普通函数一样调用它们来**自查边界**;同时挂了 3 道硬性闸门(2 道拦动作/步、1 道拦回执)+ 1 个只读审计钩子。
|
|
92
92
|
> **专业版**:节选自 `src/index.js`(完整代码见仓库),见下方代码块。
|
|
93
93
|
|
|
94
|
-
###
|
|
94
|
+
### 11 个白箱工具(真实注册名)
|
|
95
95
|
|
|
96
96
|
| 工具 | 模型调它做什么 |
|
|
97
97
|
|---|---|
|
|
@@ -101,6 +101,11 @@ node examples/demo-tool-loop.mjs
|
|
|
101
101
|
| `query_conduction_chain` | 拿传导链与框架结构 |
|
|
102
102
|
| `query_boundary` | 查内 H 边界(本插件不读不写主体性黑箱) |
|
|
103
103
|
| `query_bugstop` | 查第一 Bug 停机闭环状态:哪些故障环节已停未修复、缺失步骤(反推/溯源/修复),白箱观测闭环是否闭合 |
|
|
104
|
+
| `query_anchor_channel` | 锚源自报:结构入口(宿主声明的任务范围)是否接通、锚池里有什么;窗口面只是观察面,**永不授权** |
|
|
105
|
+
| `query_criterion_wiring` | 判据接线自查:每条判据走的是哪条路(进计算 / 只读面 / 零引用),把「在不在场」与「被不被调用」分开 |
|
|
106
|
+
| `query_strata` | 分层读数:层别定位(同一形态在不同层不能串判) |
|
|
107
|
+
| `query_nested` | 四层嵌套读数(R/S/D/H 的宏观与微观形态、服从关系、展开终止判据) |
|
|
108
|
+
| `query_tension` | S↔D 张力读数:三档路由(有容差算跨距 / 只有条款出缺口清单 / 都拿不到则不判)、H 在张力上的位置形态、H 的四个面向与公式准入律 |
|
|
104
109
|
|
|
105
110
|
### 3 道硬性闸门(hooks)+ 1 个只读审计钩子
|
|
106
111
|
|
|
@@ -160,7 +165,7 @@ export function apply(ctx) {
|
|
|
160
165
|
// 契约是 (exec, result) —— 从第一个参数上读 error 会让失败入账静默失效。
|
|
161
166
|
ctx.on('tools/result', (exec, result) => { if (result?.error) engine.onFailure(); });
|
|
162
167
|
|
|
163
|
-
// ⑤
|
|
168
|
+
// ⑤ 11 个白箱自查工具(节选其一,其余同构)
|
|
164
169
|
ctx.tools.register(defineTool({
|
|
165
170
|
name: 'query_iron_laws',
|
|
166
171
|
description: '返回三大铁律的定稿文本(不可变)',
|
|
@@ -172,14 +177,14 @@ export function apply(ctx) {
|
|
|
172
177
|
}
|
|
173
178
|
```
|
|
174
179
|
|
|
175
|
-
> 完整实现(含
|
|
180
|
+
> 完整实现(含 11 个工具的 `execute` 细节、运行日志、引擎裁决逻辑)见仓库 `src/index.js`。
|
|
176
181
|
|
|
177
182
|
## 结构
|
|
178
183
|
|
|
179
184
|
```
|
|
180
185
|
package.json # dsh 字段声明 bundle
|
|
181
186
|
weiwen-law.patch.yml # 挂载补丁(headless profile overlay)
|
|
182
|
-
src/index.js # 插件入口:钩子 +
|
|
187
|
+
src/index.js # 插件入口:钩子 + 11 个白箱自查工具
|
|
183
188
|
src/core/law.mjs # 框架定义常量(详见基础版仓库,本仓不展开推导)
|
|
184
189
|
src/core/engine.mjs # 纯逻辑裁决引擎(零 DSH 依赖,可单测)
|
|
185
190
|
test/ # 单元测试 + 真实案例测试 + 对齐回归(`npm test` 全绿)
|
|
@@ -201,7 +206,7 @@ DESIGN.md # 架构设计(映射表 / 风险 / 使用流程 / 挂
|
|
|
201
206
|
- Node.js `^22.19 || >=24`(dsh 硬性要求,奇数版本不支持)
|
|
202
207
|
- DeepSeek API Key(或其他 OpenAI 兼容端点的 Key)
|
|
203
208
|
- dsh 当前为开发者预览版(v0.1.x),官方提示后续存在破坏性 API 变更;生产环境请锁定具体版本
|
|
204
|
-
- **兼容声明**:本插件验证于 DSH v0.1.x —— 真机实测(2026-08-19 与 2026-09-21
|
|
209
|
+
- **兼容声明**:本插件验证于 DSH v0.1.x —— 真机实测(2026-08-19 与 2026-09-21:当时注册 6 个白箱工具;2026-10 增补至 11 个,新增 `query_anchor_channel` / `query_criterion_wiring` / `query_strata` / `query_nested` / `query_tension`;3 道硬性闸门 + 1 个只读审计钩子端到端工作,回执闸门在一次真实失败调用上实测拦下,并把回执断成纠错回执);mainline 快速演进中,接入前请以官方文档当前版本复核(挂载细节见 DESIGN.md)。
|
|
205
210
|
|
|
206
211
|
### 方式一:npx 快速启动(推荐先体验)
|
|
207
212
|
|
|
@@ -231,7 +236,7 @@ export DEEPSEEK_API_KEY=sk-xxxx # Linux/macOS
|
|
|
231
236
|
# $env:DEEPSEEK_API_KEY="sk-xxxx" # Windows PowerShell
|
|
232
237
|
```
|
|
233
238
|
|
|
234
|
-
挂载后,运行在该 profile 的 Agent 自动获得
|
|
239
|
+
挂载后,运行在该 profile 的 Agent 自动获得 11 个白箱自查工具(`query_iron_laws` / `query_steady_state` / `list_rigid_anchors` / `query_conduction_chain` / `query_boundary` / `query_bugstop` / `query_anchor_channel` / `query_criterion_wiring` / `query_strata` / `query_nested` / `query_tension`),并在工具调用前经过 `tools/pre-execute` 硬性护栏闸门(R/D/S/H/M 总裁决)与 `agent/pre-step` 内 H 不可侵闸门;工具失败的回执还要过 `tools/post-execute` 回执闸门(D 破窗止损:偏离累积达阈值时把故障回执就地断成纠错回执,断点落在回执上)。
|
|
235
240
|
|
|
236
241
|
### 方式三:一行命令安装(官方 dsh plugin 机制,推荐)
|
|
237
242
|
|
|
@@ -248,13 +253,13 @@ dsh plugin --profile web add "github:Shaky77/weiwen-law-dsh"
|
|
|
248
253
|
dsh --profile web
|
|
249
254
|
```
|
|
250
255
|
|
|
251
|
-
装完后在 `设置 → 插件 → 插件列表` 可见 `weiwen-law` 状态为"已启用";Agent 自动获得
|
|
256
|
+
装完后在 `设置 → 插件 → 插件列表` 可见 `weiwen-law` 状态为"已启用";Agent 自动获得 11 个白箱自查工具 + 3 道硬性闸门(`tools/pre-execute` / `agent/pre-step` / `tools/post-execute`)+ 1 个只读审计钩子(`tools/result`)。
|
|
252
257
|
|
|
253
258
|
### 卸载
|
|
254
259
|
|
|
255
260
|
- **方式三安装的**(官方 plugin 机制):`dsh plugin --profile web remove dsh-weiwen-law`,重启生效。
|
|
256
261
|
- **方式二 overlay 挂载的**:从 dsh 启动配置(cordis.yml 的 plugins 列表或 `--patch` 参数)移除 `weiwen-law.patch.yml` 引用,重启生效。
|
|
257
|
-
- 移除后 Agent 不再获得
|
|
262
|
+
- 移除后 Agent 不再获得 11 个白箱自查工具,也不再有 3 道硬性闸门与审计钩子;插件本身不写持久状态,卸载即干净。
|
|
258
263
|
|
|
259
264
|
### 日常使用 vs 压测
|
|
260
265
|
|
package/package.json
CHANGED
|
@@ -1,101 +1,103 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "dsh-weiwen-law",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "唯稳律(Weiwen's Law)通用因果引擎(白箱呈现)—— DeepSeek Harness (Cordis) 插件实现",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "./src/index.js",
|
|
7
|
-
"dsh": {
|
|
8
|
-
"bundle": {
|
|
9
|
-
"patch": "./weiwen-law.patch.yml"
|
|
10
|
-
}
|
|
11
|
-
},
|
|
12
|
-
"files": [
|
|
13
|
-
"src",
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
"causal
|
|
47
|
-
"
|
|
48
|
-
"causal-
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-weiwen-law",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "唯稳律(Weiwen's Law)通用因果引擎(白箱呈现)—— DeepSeek Harness (Cordis) 插件实现",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.js",
|
|
7
|
+
"dsh": {
|
|
8
|
+
"bundle": {
|
|
9
|
+
"patch": "./weiwen-law.patch.yml"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"src",
|
|
14
|
+
"!src/runtime.log",
|
|
15
|
+
"!src/core/_engine_base_*",
|
|
16
|
+
"weiwen-law.patch.yml",
|
|
17
|
+
"README.md",
|
|
18
|
+
"DESIGN.md",
|
|
19
|
+
"LICENSE",
|
|
20
|
+
"examples",
|
|
21
|
+
"test"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"test": "node --test \"test/*.test.mjs\"",
|
|
25
|
+
"build:docs": "node scripts/build-docs-engine.mjs",
|
|
26
|
+
"check:docs": "node scripts/build-docs-engine.mjs --check"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=22"
|
|
30
|
+
},
|
|
31
|
+
"license": "AGPL-3.0",
|
|
32
|
+
"author": {
|
|
33
|
+
"name": "Shaky77",
|
|
34
|
+
"email": "563003@qq.com"
|
|
35
|
+
},
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/Shaky77/weiwen-law-dsh.git"
|
|
39
|
+
},
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://github.com/Shaky77/weiwen-law-dsh/issues"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/Shaky77/weiwen-law-dsh#readme",
|
|
44
|
+
"keywords": [
|
|
45
|
+
"ai",
|
|
46
|
+
"causal",
|
|
47
|
+
"causality",
|
|
48
|
+
"causal-inference",
|
|
49
|
+
"causal-reasoning",
|
|
50
|
+
"causal-engine",
|
|
51
|
+
"guardrail",
|
|
52
|
+
"guardrails",
|
|
53
|
+
"white-box",
|
|
54
|
+
"whitebox",
|
|
55
|
+
"deepseek",
|
|
56
|
+
"harness",
|
|
57
|
+
"agent",
|
|
58
|
+
"safety",
|
|
59
|
+
"ai-safety",
|
|
60
|
+
"ai-security",
|
|
61
|
+
"agi-alignment",
|
|
62
|
+
"audit",
|
|
63
|
+
"auditable",
|
|
64
|
+
"interpretability",
|
|
65
|
+
"endogenous-risk-control",
|
|
66
|
+
"token-efficient",
|
|
67
|
+
"weiwens-law",
|
|
68
|
+
"weiwen-law",
|
|
69
|
+
"kiss-law",
|
|
70
|
+
"steady-state",
|
|
71
|
+
"rigid-anchor",
|
|
72
|
+
"agent-safety",
|
|
73
|
+
"llm-guardrail",
|
|
74
|
+
"dsh-plugin",
|
|
75
|
+
"causal-logic",
|
|
76
|
+
"ai-alignment",
|
|
77
|
+
"因果律",
|
|
78
|
+
"因果推理",
|
|
79
|
+
"因果引擎",
|
|
80
|
+
"白箱",
|
|
81
|
+
"护栏",
|
|
82
|
+
"人工智能安全",
|
|
83
|
+
"AI安全",
|
|
84
|
+
"人工智能对齐",
|
|
85
|
+
"可审计",
|
|
86
|
+
"稳态",
|
|
87
|
+
"刚性锚点",
|
|
88
|
+
"内生风险控制",
|
|
89
|
+
"智能体安全",
|
|
90
|
+
"大模型护栏",
|
|
91
|
+
"令牌高效",
|
|
92
|
+
"唯稳律",
|
|
93
|
+
"守真稳态"
|
|
94
|
+
],
|
|
95
|
+
"peerDependencies": {
|
|
96
|
+
"@deepseek-ai/dsh-tools": "*"
|
|
97
|
+
},
|
|
98
|
+
"peerDependenciesMeta": {
|
|
99
|
+
"@deepseek-ai/dsh-tools": {
|
|
100
|
+
"optional": true
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
package/src/core/engine.mjs
CHANGED
|
@@ -156,7 +156,14 @@ function isCredWriteSignal(category, path, sh) {
|
|
|
156
156
|
if (/(>>?|tee)\s+[^\s'"]+/.test(sh) && (CREDENTIAL_PATH.test(sh) || CREDENTIAL_SH.test(sh))) return true;
|
|
157
157
|
return false;
|
|
158
158
|
}
|
|
159
|
+
// 🔴 [2026-10-26] 系统路径判据:补 OS 保留区(Windows 侧原为**完全空白**)。
|
|
160
|
+
// 实测:`C:/Windows/System32/x.dll` 判 allow,而 `C:/Windows/System32/drivers/etc/hosts` 判 review ——
|
|
161
|
+
// 两者同为系统保留区,差别只是路径里**恰好含 `/etc/`** 这个 Unix 目录名 ⇒ 判据读的是"字符串含哪个目录名",
|
|
162
|
+
// 不是"是否落在 OS 保留区"。此处补齐 Windows 保留区(来源=OS 自身定义,非我们列举危险文件)。
|
|
163
|
+
// ⚠️ Unix 侧暂只保留原有四目录:加 bin/sbin/lib/usr 会命中用户自建的 `.../usr/...` 目录(误伤),
|
|
164
|
+
// 留作待裁(扩表=覆盖更全但误伤面更大,需对照实测后才可动)。
|
|
159
165
|
const SYSTEM_PATH = /\/(etc|sys|proc|boot)\//i;
|
|
166
|
+
const SYSTEM_PATH_WIN = /(?:^|[^a-z0-9])[a-z]:[\/\\](?:windows|program files(?: \(x86\))?|programdata|\$recycle\.bin|system volume information)(?:[\/\\]|$)/i;
|
|
160
167
|
const EXTERNAL_TARGET = /https?:\/\/(?!localhost|127\.0\.0\.1|0\.0\.0\.0)[^\s'"]+/i;
|
|
161
168
|
// [A3 · 2026-10-02 · 文本层止血·不自称完备] 外联判据从「HTTP 词形」升为「网络命名空间」:
|
|
162
169
|
// DNS 盲区根因=解析命令(dig/nslookup/host/getaddrinfo…)含域名/IP 却非 http(s),被放行。
|
|
@@ -248,15 +255,72 @@ function opaqueExecSignal({ name, category, path, sh, packed }) {
|
|
|
248
255
|
return false;
|
|
249
256
|
}
|
|
250
257
|
|
|
258
|
+
// 🔴 [2026-10-26 · 结构统一] 位置判据**单一来源**:本函数与 `_sh12LocKeyShape` 共用 `_SH12_LOC_KEY`。
|
|
259
|
+
// 旧版只认 `path` / `file` 两个**字段名**(白名单)⇒ 宿主用 `file_path` 时抽不到
|
|
260
|
+
// ⇒ 落到「无扰动 ⇒ allow」出口 ⇒ **写操作恒放行**(2026-10-26 外部 agent 实跑取证)。
|
|
261
|
+
// 而同一次调用的 `_sh12LocKeyShape` 用**词素形态**就认得出 `file_path` ⇒ 两套判据对同一事实相反:
|
|
262
|
+
// 「形态判据说够得着」∧「抽取器说抽不到」⇒ **中间这一格没人兜 ⇒ 黑洞**。
|
|
263
|
+
// ⚠️ 根因不是"漏了 file_path 这个名字"(那是枚举,宿主任意改名即复发),
|
|
264
|
+
// 是**同一个问题存在两套判据源**。⇒ 修法=合并判据源,不是补白名单。
|
|
265
|
+
// ⚠️ 也不新造正则:直接引用 `_SH12_LOC_KEY`,将来改一处两处同时变 —— 判据分裂的成因就此断掉。
|
|
266
|
+
function _pathByKeyShape(args) {
|
|
267
|
+
let found = '';
|
|
268
|
+
const walk = (o, d) => {
|
|
269
|
+
if (found || d > 3 || o == null) return;
|
|
270
|
+
if (Array.isArray(o)) { o.forEach((x) => walk(x, d + 1)); return; }
|
|
271
|
+
if (typeof o === 'object') {
|
|
272
|
+
for (const k of Object.keys(o)) {
|
|
273
|
+
const v = o[k];
|
|
274
|
+
if (_SH12_LOC_KEY.test(k) && typeof v === 'string' && v.trim()) { found = v.trim(); return; }
|
|
275
|
+
walk(v, d + 1);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
walk(args, 0);
|
|
280
|
+
return found;
|
|
281
|
+
}
|
|
282
|
+
// 🔴 [2026-10-26 · 判据读真实落点,不读字符串形态] 路径先规范化再交给任何判据。
|
|
283
|
+
// 实测病灶:`write{file_path:'C:/work/lab/../../Windows/System32/x.dll'}` 判 allow,
|
|
284
|
+
// 而同落点的 `C:/Windows/System32/drivers/etc/hosts` 判 review —— 差别只在**字符串开头**,
|
|
285
|
+
// 不在真实落点。⇒ "系统路径"判据读到的是字符串前缀,不是它最终写到哪里。
|
|
286
|
+
// 归一放在**唯一出口**(extractPath 返回处),下游 SYSTEM_PATH / CREDENTIAL_PATH /
|
|
287
|
+
// isCredWrite / 锚匹配 / 日志全部同步 —— 不在各判据处各写一份(那又会分裂成两套)。
|
|
288
|
+
function normalizeLoc(p) {
|
|
289
|
+
if (typeof p !== 'string' || !p) return p;
|
|
290
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(p)) return p; // URL 不是文件落点,不做文件系统归一
|
|
291
|
+
let s = p.replace(/\\/g, '/');
|
|
292
|
+
const tilde = s.startsWith('~') ? '~' : '';
|
|
293
|
+
if (tilde) s = s.slice(1);
|
|
294
|
+
const drive = (s.match(/^[A-Za-z]:/) || [''])[0];
|
|
295
|
+
if (drive) s = s.slice(drive.length);
|
|
296
|
+
const abs = s.startsWith('/');
|
|
297
|
+
const out = [];
|
|
298
|
+
for (const seg of s.split('/')) {
|
|
299
|
+
if (!seg || seg === '.') continue;
|
|
300
|
+
// `..` 上溯:能消则消;已到根外则**保留**(超根上溯无法确定真实落点,不得当成普通路径放过去)
|
|
301
|
+
if (seg === '..') { if (out.length) out.pop(); else out.push('..'); continue; }
|
|
302
|
+
out.push(seg);
|
|
303
|
+
}
|
|
304
|
+
return tilde + drive + (abs ? '/' : '') + out.join('/');
|
|
305
|
+
}
|
|
251
306
|
function extractPath(call) {
|
|
307
|
+
return normalizeLoc(_extractPathRaw(call));
|
|
308
|
+
}
|
|
309
|
+
function _extractPathRaw(call) {
|
|
252
310
|
const c = call?.args ?? {};
|
|
253
|
-
if (typeof c.path === 'string') return c.path;
|
|
254
|
-
if (typeof c.file === 'string') return c.file;
|
|
311
|
+
if (typeof c.path === 'string' && c.path.trim()) return c.path;
|
|
312
|
+
if (typeof c.file === 'string' && c.file.trim()) return c.file;
|
|
313
|
+
const byShape = _pathByKeyShape(c); // ← 形态兜底(与 _sh12LocKeyShape 同源)
|
|
314
|
+
if (byShape) return byShape;
|
|
255
315
|
const sh = extractShell(call);
|
|
256
316
|
const m = sh.match(/(?:cat|head|tail|read|less|more|vi|vim|nano|cp|mv|rm|echo|write)\s+([^\s]+)/i);
|
|
257
317
|
return m ? m[1] : '';
|
|
258
318
|
}
|
|
259
|
-
|
|
319
|
+
// 🔴 [2026-10-26] 导出(不改逻辑):适配层「窗口警察」要判"这个动作能不能造成不可逆改变",
|
|
320
|
+
// 而该判据在本函数里已经存在。**必须复用,不得在适配层另写一份** ——
|
|
321
|
+
// 同日已实证一次判据分裂的代价:`extractPath`(字段白名单)与 `_sh12LocKeyShape`(词素形态)
|
|
322
|
+
// 对同一调用给出相反结论 ⇒ 写操作恒放行。⇒ 判据源只许一处,改动此处即全员同步。
|
|
323
|
+
export function inferCategory(call) {
|
|
260
324
|
const name = call?.name ?? '';
|
|
261
325
|
if (TOOL_CATEGORY[name]) return TOOL_CATEGORY[name];
|
|
262
326
|
const a = call?.args ?? {};
|
|
@@ -274,6 +338,26 @@ function inferCategory(call) {
|
|
|
274
338
|
}
|
|
275
339
|
return 'other';
|
|
276
340
|
}
|
|
341
|
+
// 🔴 [2026-10-26 · 结构统一] 「不可逆性」是**一个判据**,不是各门各写一份。
|
|
342
|
+
// 同日实测已见判据分裂的代价:`extractPath`(字段白名单)与 `_sh12LocKeyShape`(词素形态)
|
|
343
|
+
// 对同一调用给出相反结论 ⇒ 写操作恒放行。⇒ 本判据定义在此、**唯一来源**:
|
|
344
|
+
// 适配层「窗口警察」与引擎各门(R 归因 / 推演落点 / 无扰动出口)全部共用。
|
|
345
|
+
// 用途:决定「审查强度」。不可逆才要求归因与物证;可逆/只读不要求 ——
|
|
346
|
+
// 既不把"没看"当"没事"(fail-open),也不把只读翻成拦截(误伤)。
|
|
347
|
+
const IRREVERSIBLE_CATS = new Set(['write', 'exec-destructive', 'exfil', 'exfil-net']);
|
|
348
|
+
// 破坏语义词素(例示,非完备清单):认**语素**不认完整名,宿主任意命名都命中。
|
|
349
|
+
// 刻意不含 exec|shell|bash|pwsh|cmd|run|spawn —— 那些是**执行通道名**(能跑命令),不是**动作意图**(要破坏);
|
|
350
|
+
// 混进来会把「用 pwsh 列个目录」判成不可逆 ⇒ 只读也被要求归因/物证(实跑误伤)。
|
|
351
|
+
// 通道类由 inferCategory 读**命令内容**去判(exec vs exec-destructive),不靠工具名。
|
|
352
|
+
const IRREVERSIBLE_VERB = /(delete|remove|write|edit|create|drop|destroy|truncate|purge|wipe|overwrite|rename|move|chmod|chown|send|upload|post|put|patch)/i;
|
|
353
|
+
export function isIrreversible(call) {
|
|
354
|
+
const cat = inferCategory(call);
|
|
355
|
+
if (IRREVERSIBLE_CATS.has(cat)) return true;
|
|
356
|
+
if (cat === 'read') return false; // 明确只读 ⇒ 可逆
|
|
357
|
+
const name = String(call?.name ?? '');
|
|
358
|
+
const action = typeof call?.args?.action === 'string' ? call.args.action : '';
|
|
359
|
+
return IRREVERSIBLE_VERB.test(name) || IRREVERSIBLE_VERB.test(action);
|
|
360
|
+
}
|
|
277
361
|
function inferCallSemantics(call, ctx = {}) {
|
|
278
362
|
const category = inferCategory(call);
|
|
279
363
|
const path = extractPath(call);
|
|
@@ -309,7 +393,7 @@ function inferCallSemantics(call, ctx = {}) {
|
|
|
309
393
|
// write_file 的 args.path 属写类工具,由 isCredWrite 判写方向,不受此过滤影响(sh 为空)。
|
|
310
394
|
isCred: (CREDENTIAL_PATH.test(path) && !credNonReadHead(sh)) || (CREDENTIAL_SH.test(sh) && !credNonReadHead(sh)) || hasEnvSecretRef(sh) || hasEnvSecretRef(packed),
|
|
311
395
|
isCredWrite,
|
|
312
|
-
isSystem: SYSTEM_PATH.test(path) || SYSTEM_PATH.test(sh),
|
|
396
|
+
isSystem: SYSTEM_PATH.test(path) || SYSTEM_PATH.test(sh) || SYSTEM_PATH_WIN.test(path) || SYSTEM_PATH_WIN.test(sh),
|
|
313
397
|
isExternal,
|
|
314
398
|
hitsInnerH: hitsInnerH(packed),
|
|
315
399
|
// 方向信号(非内容信号):执行内容在执行前是否可审计。
|
|
@@ -332,8 +416,65 @@ function inferCallSemantics(call, ctx = {}) {
|
|
|
332
416
|
// H₀ → S₀(S₀+1) ← S 增路径(稳态增量,纯白箱)
|
|
333
417
|
// H₀ → D₀(D₀+1) ← D 增路径(=|S-1| 稳态绝对侵蚀)
|
|
334
418
|
// 两路同时跑、都汇入 M₀(M+1)(独立事件沉淀),终态 S 再分叉 {S₀(S₀+1)/S₀(S₀-1)} 对比区分。
|
|
419
|
+
// ────────────────────────────────────────────────────────────────────
|
|
420
|
+
// 🔴 推演前提(2026-10-26 · 接 M_TWO_TENSES/FORWARD_INFERENCE 的锚在场判据)
|
|
421
|
+
//
|
|
422
|
+
// 安定(law.mjs `M_TWO_TENSES.adjudicationVsUse`):
|
|
423
|
+
// 「前提 = 参照锚必须在场(R / 恒定项在位…)—— 锚不在则参照线不存在、Δ 无从测。」
|
|
424
|
+
//
|
|
425
|
+
// 实测病灶(`_tmp/dia-premise-20261026.mjs`):`category === 'other'`(动作类别归不出)
|
|
426
|
+
// 时,本层所有信号恒为 false ⇒
|
|
427
|
+
// · `simulateBranch(s,'S+1')` 走 `purelyBenign` 默认真值 ⇒ `finalS: +1`
|
|
428
|
+
// · `simulateBranch(s,'D-1')` 落末尾无 note 的 `finalS: 0`
|
|
429
|
+
// ⇒ `deduceRisk` 读成「S 增路径成立、D 路径无实际侵蚀」⇒ 输出 allow。
|
|
430
|
+
// 但这两句**都不是推演结论**,是空转的默认真值 ⇒ 四象格「真的假话」:
|
|
431
|
+
// 逐字核得真(确实没命中任何信号)、整体是假(它并没有推过)。
|
|
432
|
+
// ⇒ 病根不是"判错",是**"我推不出"被写成了"我推过、没风险"**(与「未测 ≠ 零」同族)。
|
|
433
|
+
//
|
|
434
|
+
// 🔴 三次修正(同日):第一版写成**两态**(锚在场 / 不在场)⇒ 影子实验把"真空"一起打成 review,
|
|
435
|
+
// 误伤 3 条正常样本(查询类空 args / 写操作缺省 / 委托人授权删除)⇒ 当场还原。
|
|
436
|
+
// 病根是**一桶两义**(本轮第三次撞见同一个形状):`!path && !sh` 既可能是「真空」
|
|
437
|
+
// 也可能是「有载荷但本层抽不到」,两者缺省方向相反,不能共用一个桶。
|
|
438
|
+
// ⇒ 定三态,且**真空不需要锚**(安 09-26:「空没有任何上下浮动…没有风险」⇒ 结论独立成立):
|
|
439
|
+
// VACUUM 无载荷 ⇒ 无需推演,S 持平、M 未变(判决合法,理由不得写成"证明了增益")
|
|
440
|
+
// ANCHORED 载荷在 ∧ 类别可归 ⇒ 推演可跑
|
|
441
|
+
// NO_ANCHOR 载荷在 ∧ 类别归不出 ⇒ 无参照线 ⇒ 本层未产生判据(未测 ≠ 零)
|
|
442
|
+
const PREMISE = { VACUUM: 'vacuum', ANCHORED: 'anchored', NO_ANCHOR: 'no-anchor' };
|
|
443
|
+
/** 载荷是否为空(真空判据)。`bash{command:''}` 属真空:有键、内容为空 —— 只看键数会判错。 */
|
|
444
|
+
function isVacuous(call) {
|
|
445
|
+
const isEmpty = (v) => v === null || v === undefined || v === ''
|
|
446
|
+
|| (Array.isArray(v) && v.length === 0)
|
|
447
|
+
|| (typeof v === 'object' && !Array.isArray(v) && Object.keys(v).length === 0);
|
|
448
|
+
return Object.values(call?.args ?? {}).every(isEmpty);
|
|
449
|
+
}
|
|
450
|
+
function inferencePremise(s, call) {
|
|
451
|
+
if (call && isVacuous(call)) {
|
|
452
|
+
return { ok: true, code: PREMISE.VACUUM,
|
|
453
|
+
note: '真空(无载荷):无作用对象 ⇒ 无需推演,S 持平、M 未变' };
|
|
454
|
+
}
|
|
455
|
+
// 🔴 [2026-10-26 · 路径2 接路径1 的锚] 锚的判据源 = **归因层 `attributeCall`**,不是 `inferCategory`。
|
|
456
|
+
// 来由(attribution.mjs 文件头原文):
|
|
457
|
+
// 「路径1(分形属性查归因):先把"这是什么动作"归因清楚,以暴露的归因为**锚点**;
|
|
458
|
+
// 路径2(以锚点推演预测):**拿锚点**跑推演(引擎既有 deduceRisk / simulateBranch)。」
|
|
459
|
+
// 实测(2026-10-26):两条路径各有一套归因源,对同一工具结论相反 ——
|
|
460
|
+
// `fs_write` / `delete_file`:inferCategory ⇒ 'other'(推演层空转)
|
|
461
|
+
// attributeCall ⇒ file-write / file-delete(锚在场)
|
|
462
|
+
// ⇒ 推演层一直在用**自己那张表**当锚,而不是路径1 给出的锚 ⇒ 这是"判据分裂"的第三处
|
|
463
|
+
// (前两处:extractPath 白名单 vs 词素;inferCategory vs attributeCall 此处)。
|
|
464
|
+
// 为何必须接归因层:孤立的 `inferCategory='other'` 既可能是"名不在表里"、也可能是"真中性",
|
|
465
|
+
// 两者缺省方向相反;而归因层已经把这件事判完了(`ok` / `layer`)⇒ 单一来源。
|
|
466
|
+
const anchored = call
|
|
467
|
+
? (() => { try { return attributeCall(call).ok === true; } catch { return false; } })()
|
|
468
|
+
: !!(s?.category && s.category !== 'other');
|
|
469
|
+
if (!anchored) {
|
|
470
|
+
return { ok: false, code: PREMISE.NO_ANCHOR,
|
|
471
|
+
note: '锚不在场(路径1 归不出动作类别)⇒ 推演无参照线:本层未产生判据,S/M 变化未测(≠ 无风险)' };
|
|
472
|
+
}
|
|
473
|
+
return { ok: true, code: PREMISE.ANCHORED, note: null };
|
|
474
|
+
}
|
|
475
|
+
|
|
335
476
|
// simulateBranch:给定语义,模拟"单条分支"的终态 S。kind='S+1' 即 S 增路径,'D-1' 即 D 增(蚀)路径。
|
|
336
|
-
function simulateBranch(s, kind) {
|
|
477
|
+
function simulateBranch(s, kind, premise = inferencePremise(s)) {
|
|
337
478
|
if (kind === 'S+1') {
|
|
338
479
|
// S 增路径:假定 call 作为稳态增量被接受。仅当 call 纯稳态贡献(无任何风险信号)才真 +1。
|
|
339
480
|
// 前提:可审计性是 S 成立的前提——
|
|
@@ -344,8 +485,11 @@ function simulateBranch(s, kind) {
|
|
|
344
485
|
const auditable = !opaque;
|
|
345
486
|
const purelyBenign = auditable && !s.isCred && !s.isSystem && !s.isExternal && !s.hitsInnerH && !s.isAuthSink;
|
|
346
487
|
return {
|
|
347
|
-
|
|
488
|
+
// 🔴 前提三态:真空(无载荷)⇒ 结论独立成立,可给 +1;锚不在场 ⇒ 空转,不得给 +1
|
|
489
|
+
path: 'S+1', finalS: (purelyBenign && premise.ok) ? +1 : 0,
|
|
490
|
+
premise,
|
|
348
491
|
...(auditable ? {} : { note: '执行内容不可审计(不透明源/未知变量/引用本会话写入文件/跨调用敏感源→汇组合)→ 无法证明稳态增量,S+1 不成立' }),
|
|
492
|
+
...(premise.ok ? {} : { note: `${premise.note} ⇒ S+1 的 +1 未成立(空转,非增益)` }),
|
|
349
493
|
};
|
|
350
494
|
}
|
|
351
495
|
// D-1 路径(=|S-1|):假定 call 触发损伤。侵蚀量按风险信号严重度分级(客观结构,非枚举具体值)。
|
|
@@ -369,7 +513,16 @@ function simulateBranch(s, kind) {
|
|
|
369
513
|
if (s.isCredWrite) return { path: 'D-1', finalS: -2, note: '写凭据存放位(篡改/植入身份凭据),需人工确认' };
|
|
370
514
|
if (s.isSystem && ['read', 'write', 'exec', 'exec-destructive'].includes(s.category)) return { path: 'D-1', finalS: -1, note: '系统信息泄露/完整性受损' };
|
|
371
515
|
if (s.hasOpaqueVar) return { path: 'D-1', finalS: -1, note: '执行内容含未知变量引用,值不可审计(无法证明无风险)' };
|
|
372
|
-
|
|
516
|
+
// 🔴 [2026-10-26] 末尾这一格是「假定有损伤」分支的**缺省**。原来写 `finalS: 0` 且无 note
|
|
517
|
+
// ⇒ 与"推演过了、确认无侵蚀"在读数上完全同形(阿澄 81 的判据精确命中此格,只是他猜的位置在
|
|
518
|
+
// 另一个出口)。缺省值本身没错(0=未命中任何侵蚀条目),错在**没交代它是不是推出来的**:
|
|
519
|
+
// · 锚在场(category 可归)⇒ 0 是结论:查过侵蚀条目、无一命中 ⇒ 无侵蚀。
|
|
520
|
+
// · 锚不在场(category='other')⇒ 0 是空转:侵蚀条目一条都没被问过 ⇒ **未测**。
|
|
521
|
+
return {
|
|
522
|
+
path: 'D-1', finalS: 0,
|
|
523
|
+
premise,
|
|
524
|
+
...(premise.ok ? { note: '侵蚀条目已逐条比对,无一命中 ⇒ 无侵蚀(结论)' } : { note: `${premise.note} ⇒ 0 是空转缺省,非"无侵蚀"结论` }),
|
|
525
|
+
};
|
|
373
526
|
}
|
|
374
527
|
|
|
375
528
|
// ---------------- 默认 R 刚性锚点策略(具象判据示例) ----------------
|
|
@@ -1605,8 +1758,10 @@ export class WeiwenLawEngine {
|
|
|
1605
1758
|
// ⇒ 判据自否定:越敏感 ⇒ 越早退 ⇒ 越到不了登记点 ⇒ refsSessionSensitiveRead「源→汇」通道结构上不可达。
|
|
1606
1759
|
// 为何测试全绿却未暴露:既有用例直调 deduceRisk(绕过判定层)⇒ 只测了后半段,前段断路未被覆盖。
|
|
1607
1760
|
// H 分叉:S 增路径 + D 增(蚀)路径 同时模拟(并行,非二选一)
|
|
1608
|
-
|
|
1609
|
-
const
|
|
1761
|
+
// 🔴 [2026-10-26] 前提先算、再喂两路:锚不在场时两路的缺省值都不是结论(见 inferencePremise)。
|
|
1762
|
+
const premise = inferencePremise(s, call);
|
|
1763
|
+
const bS = simulateBranch(s, 'S+1', premise);
|
|
1764
|
+
const bD = simulateBranch(s, 'D-1', premise);
|
|
1610
1765
|
// 两路都汇入 M(独立事件沉淀,手稿:M₀(M₀+1))
|
|
1611
1766
|
const m = { m: 1, branches: { bS, bD } };
|
|
1612
1767
|
// 终态 S 对比区分(手稿:{S₀(S₀+1) / S₀(S₀-1)})。
|
|
@@ -1620,8 +1775,13 @@ export class WeiwenLawEngine {
|
|
|
1620
1775
|
reason: `推演判定高风险(D 路径确定性侵蚀 S):${bD.note || '凭据/内H/外传'}` };
|
|
1621
1776
|
}
|
|
1622
1777
|
if (erosion < 0 || !sOk) {
|
|
1623
|
-
|
|
1624
|
-
|
|
1778
|
+
// 🔴 [2026-10-26] 前提缺失时**点名是前提缺失**,不再笼统说"S+1 不成立":
|
|
1779
|
+
// 后者会让外部读成"推演跑了、结论是中等风险",而真相是"推演没跑起来"。
|
|
1780
|
+
// (实测反馈:外部审计 agent 无法从这类回执反推规则 ⇒ 读数必须可归因到具体一层。)
|
|
1781
|
+
return { verdict: 'review', m, branches: { bS, bD }, deduced: true, premise,
|
|
1782
|
+
reason: !premise.ok
|
|
1783
|
+
? `推演未产生判据(${premise.note}):交还人工,建议限权/二次确认`
|
|
1784
|
+
: `推演判定中风险(${!sOk ? 'S+1 不成立:无法证明稳态增量' : `D 路径轻度侵蚀 S:${bD.note || '灰区'}`}):建议限权/二次确认` };
|
|
1625
1785
|
}
|
|
1626
1786
|
// [2026-09-26 · reason 正位(安 空输入判据 · 根因级)] 无动作文本 ⇒ **如实陈述**,不套"S 增路径成立"。
|
|
1627
1787
|
// 安的原话:「空,没有上下文,仅仅只是空,按照唯稳律推演,空没有任何上下浮动,持平稳定,
|
|
@@ -1644,12 +1804,32 @@ export class WeiwenLawEngine {
|
|
|
1644
1804
|
// 🔴 **根因(一桶两义)**:该收口把 `null`(=**本层不接**)也改成了 review。⇒ 正确修法必须**带门**:
|
|
1645
1805
|
// 先判"该对象是否落在本层职责域内"(够不着 vs 不涉及),**只有"受理事由成立且够不着"才 review**;
|
|
1646
1806
|
// 不能只做"穷尽 ⇒ review"。⇒ 已在 `deployment.md` 更正 `XSUB-10` 登记。
|
|
1647
|
-
|
|
1648
|
-
|
|
1807
|
+
// ── 🔴 [2026-10-26 · 推演前提收口] 本出口必须交代"这一格是推出来的、还是空转的"。──
|
|
1808
|
+
// 见 simulateBranch 上方 premise 注释与实测 `_tmp/dia-premise-20261026.mjs`:
|
|
1809
|
+
// `glob{pattern}` / `grep{pattern,path}` 都落在这里或其下一格,原文案一律给
|
|
1810
|
+
// 「无扰动 ⇒ 无风险」/「S 增路径成立」,而实际是 **category='other' ⇒ 信号恒 false**。
|
|
1811
|
+
// ⚠️ 仍**不改 verdict**:锚在场的终局判定在归因层(2100+ `!attrib.ok ∧ hasPayload ⇒ review`),
|
|
1812
|
+
// 此处重复扣会误伤真空样本;只改**读数**,让「未测」不再显示成「零风险」(铁律:未测 ≠ 零)。
|
|
1813
|
+
// 「真空」的判据是**载荷为空**,不是"键数为零":`bash{command:''}` 有一个键、内容为空,
|
|
1814
|
+
// 安 09-26 那次的样本正是它(原话「空,没有上下文,仅仅只是空」)⇒ 只看键数会把这类判成"有对象"。
|
|
1815
|
+
const isEmptyPayload = (v) => v === null || v === undefined || v === ''
|
|
1816
|
+
|| (Array.isArray(v) && v.length === 0)
|
|
1817
|
+
|| (typeof v === 'object' && !Array.isArray(v) && Object.keys(v).length === 0);
|
|
1818
|
+
const noObject = Object.values(call?.args ?? {}).every(isEmptyPayload);
|
|
1819
|
+
if (noObject) {
|
|
1820
|
+
return { verdict: 'allow', m, branches: { bS, bD }, deduced: true, premise,
|
|
1821
|
+
reason: '无扰动入基线(真空:无作用对象可审):S 持平、M 未变 ⇒ 无风险,放行' };
|
|
1822
|
+
}
|
|
1823
|
+
return { verdict: 'allow', m, branches: { bS, bD }, deduced: true, premise,
|
|
1824
|
+
reason: '本层未取得作用对象(未抽到 shell 命令 / 文件落点)⇒ 推演未产生判据:S/M 变化未测(≠ 无风险)' };
|
|
1649
1825
|
}
|
|
1650
1826
|
// 双成立(S+1=+1 且 D 侵蚀=0)→ 风险=0 < 唯稳律 < 稳态 严格成立 → allow
|
|
1651
|
-
|
|
1652
|
-
|
|
1827
|
+
// 🔴 同上:`premise.ok === false` 时,S+1 的 +1 与 D-1 的 0 都是空转缺省,
|
|
1828
|
+
// 不得陈述为"S 增路径成立" —— 那句话是**真的假话**(核得真、整体假)。
|
|
1829
|
+
return { verdict: 'allow', m, branches: { bS, bD }, deduced: true, premise,
|
|
1830
|
+
reason: premise.ok
|
|
1831
|
+
? '推演判定低风险(S 增路径成立、D 路径无实际侵蚀):放行并累积 S'
|
|
1832
|
+
: `推演未产生判据(${premise.note})⇒ 本格放行依据不来自推演层` };
|
|
1653
1833
|
}
|
|
1654
1834
|
|
|
1655
1835
|
// ---------- M 沉淀:推演层两分支都汇入 M(独立事件,append-only 历史刻痕) ----------
|
|
@@ -2016,13 +2196,35 @@ export class WeiwenLawEngine {
|
|
|
2016
2196
|
if (!attrib.ok) {
|
|
2017
2197
|
// 分形子项 m 果 = 归不出:动作类别不可判 → 铁律「判不出来就 review,不猜」。
|
|
2018
2198
|
// 挂 attrib + fractalSubM:true 标注此为「分形子项级」触发,非整体推演结论。
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2199
|
+
//
|
|
2200
|
+
// 🔴 [2026-10-26 · 结构修法 · 第二次才对] 「归不出」仍分两种出口,但**门不是不可逆性**。
|
|
2201
|
+
// ⚠️ 我先写成「不可逆 ⇒ review / 可逆 ⇒ 免归因」,被回归测试当场打回(3 条安全用例转 allow):
|
|
2202
|
+
// `deploy_keylogger{target:HOST}` 归不出类别 ⇒ 我的 `isIrreversible` 内部也归不出 ⇒ 'other'
|
|
2203
|
+
// ⇒ 被当"可逆"放行 ⇒ **漏判**。⇒ 这正是原病灶的同构翻版:**把"我不知道"当成"没有风险"**
|
|
2204
|
+
// (原来在"抽不到目标 ⇒ 无扰动 ⇒ allow"那一格)。换位置不等于修好。
|
|
2205
|
+
// ⇒ 正确的门是**有没有可审之物**(结构,与窗口警察「真空不要求物证」同源):
|
|
2206
|
+
// · **真空**(无参数)⇒ 没有作用对象可审 ⇒ 免归因,继续常规推演。
|
|
2207
|
+
// 实测样本:`list_rigid_anchors{}` / `query_bugstop{}`(自省工具被自己拒=越读越瞎)。
|
|
2208
|
+
// · **有参数但归不出** ⇒ 我知道它要动东西、却不知道动什么 ⇒ **review(不猜)**。
|
|
2209
|
+
// 实测样本:`deploy_keylogger{target:HOST}` / `{config:{target:'HOST'}}` ⇒ 必须仍 review。
|
|
2210
|
+
// ⇒ 判据:**"归不出"只能往 review 走,不能往 allow 走;能往 allow 走的只有"没东西可归"。**
|
|
2211
|
+
const hasPayload = Object.keys(call?.args ?? {}).length > 0;
|
|
2212
|
+
if (hasPayload) {
|
|
2213
|
+
return {
|
|
2214
|
+
kind: 'review',
|
|
2215
|
+
law: 'R',
|
|
2216
|
+
reason: `路径1 归因锚定失败(名=${call?.name ?? ''} 中性或无可观测行为):动作类别不可判,交还人工,不猜`,
|
|
2217
|
+
attrib,
|
|
2218
|
+
fractalSubM: true,
|
|
2219
|
+
};
|
|
2220
|
+
}
|
|
2221
|
+
// 真空 ⇒ 无可审之物(不是"跳过审查",是"没有对象需要归因")
|
|
2222
|
+
this._mark('R', {
|
|
2223
|
+
step: '归因锚定(真空:无可审之物)',
|
|
2224
|
+
verdict: null,
|
|
2225
|
+
cuts: false,
|
|
2226
|
+
note: '该调用无参数、无作用对象 ⇒ 无可归因之物 ⇒ 继续常规推演(不因"名中性"而拒)',
|
|
2227
|
+
});
|
|
2026
2228
|
}
|
|
2027
2229
|
// 【2026-09-15 铁律8 补强 · 接线】破坏性/删除类动作「目标(物证)未外化」→ 证据不足 → review
|
|
2028
2230
|
// 接 destructiveTargetMissing(L682):仅标记「破坏性动作 + 目标缺失」,命中即交还人工。
|