dsh-rule-engine 0.5.4 → 0.5.6
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 +8 -0
- package/lib/core/authorization.js +243 -14
- package/lib/core/baseline.js +43 -0
- package/lib/core/config.js +7 -0
- package/lib/core/contract.js +54 -0
- package/lib/core/guard-core.js +133 -28
- package/lib/core/intent.js +267 -0
- package/lib/core/llm-intent.js +148 -0
- package/lib/core/llm-understander.js +2 -2
- package/lib/core/overengineering.js +6 -4
- package/lib/core/paths.js +5 -0
- package/lib/core/patterns.js +56 -4
- package/lib/core/state.js +61 -7
- package/lib/core/text-detect.js +61 -3
- package/lib/core/tool-catalog.js +85 -0
- package/lib/core/understander.js +1 -1
- package/lib/index.js +468 -49
- package/lib/service.js +35 -1
- package/package.json +1 -1
- package/scripts/rules-health.mjs +55 -0
package/lib/service.js
CHANGED
|
@@ -16,7 +16,8 @@ const REMOTE_METHODS = [
|
|
|
16
16
|
"getAuditLog",
|
|
17
17
|
"getUnderstanding",
|
|
18
18
|
"getTaskContractConfig",
|
|
19
|
-
"setTaskContractConfig"
|
|
19
|
+
"setTaskContractConfig",
|
|
20
|
+
"getRuleStats"
|
|
20
21
|
];
|
|
21
22
|
|
|
22
23
|
function currentVersion() {
|
|
@@ -210,6 +211,39 @@ class RuleEngineService extends TypertRemoteService {
|
|
|
210
211
|
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
211
212
|
}
|
|
212
213
|
}
|
|
214
|
+
|
|
215
|
+
/** C2 轻量版:规则统计(detected/suppressed/injected,会话聚合;只读) */
|
|
216
|
+
async getRuleStats() {
|
|
217
|
+
try {
|
|
218
|
+
const rows = aggregateRuleStats(state.sessions.values());
|
|
219
|
+
return {
|
|
220
|
+
ok: true,
|
|
221
|
+
stats: rows,
|
|
222
|
+
totals: rows.reduce((t, r) => ({
|
|
223
|
+
detected: t.detected + r.detected,
|
|
224
|
+
suppressed: t.suppressed + r.suppressed,
|
|
225
|
+
injected: t.injected + r.injected
|
|
226
|
+
}), { detected: 0, suppressed: 0, injected: 0 }),
|
|
227
|
+
sessions: state.sessions.size
|
|
228
|
+
};
|
|
229
|
+
} catch (error) {
|
|
230
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** 纯函数:聚合各会话 ruleStats(C2,可独立测试) */
|
|
236
|
+
export function aggregateRuleStats(sessions) {
|
|
237
|
+
const merged = {};
|
|
238
|
+
for (const s of sessions) {
|
|
239
|
+
for (const [ruleId, st] of Object.entries(s.ruleStats || {})) {
|
|
240
|
+
const m = (merged[ruleId] ||= { ruleId, detected: 0, suppressed: 0, injected: 0 });
|
|
241
|
+
m.detected += st.detected || 0;
|
|
242
|
+
m.suppressed += st.suppressed || 0;
|
|
243
|
+
m.injected += st.injected || 0;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return Object.values(merged).sort((a, b) => b.detected - a.detected);
|
|
213
247
|
}
|
|
214
248
|
|
|
215
249
|
export { RuleEngineService, RuleEngineService as default };
|
package/package.json
CHANGED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// rules-health.mjs - 规则触发率统计(阶段 2"数据驱动精简"的基础工具,2026-08-24)
|
|
2
|
+
// 用法:node scripts/rules-health.mjs [--top 10]
|
|
3
|
+
// 数据源:DSH_HOME/rule-engine.log.jsonl(引擎审计日志——四类审计自 2026-08-24 起)
|
|
4
|
+
// 输出:① 各规则触发统计(按 total 排序)② 零命中清单(对照 rule-understanding.json 规则全集)
|
|
5
|
+
// ③ 样本标注:总量 < MIN_SAMPLE 时提示"样本不足,暂不据此删/并/浓缩规则"。
|
|
6
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import os from "node:os";
|
|
9
|
+
|
|
10
|
+
const DSH_HOME = process.env.DSH_HOME || join(os.homedir(), ".dsh");
|
|
11
|
+
const LOG = join(DSH_HOME, "rule-engine.log.jsonl");
|
|
12
|
+
const UNDERSTANDING = join(DSH_HOME, "rule-understanding.json");
|
|
13
|
+
const MIN_SAMPLE = 200;
|
|
14
|
+
const TOP = Number((process.argv.find((a) => a.startsWith("--top=")) || "").split("=")[1]) || 10;
|
|
15
|
+
|
|
16
|
+
if (!existsSync(LOG)) {
|
|
17
|
+
console.log(`NO LOG: ${LOG}`);
|
|
18
|
+
process.exit(0);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const stats = new Map(); // ruleId -> Map(kind -> count)
|
|
22
|
+
let total = 0;
|
|
23
|
+
for (const line of readFileSync(LOG, "utf8").split("\n")) {
|
|
24
|
+
if (!line.trim()) continue;
|
|
25
|
+
let e;
|
|
26
|
+
try { e = JSON.parse(line); } catch { continue; }
|
|
27
|
+
if (!e || !e.kind || !e.rule) continue;
|
|
28
|
+
total++;
|
|
29
|
+
const byKind = stats.get(String(e.rule)) || new Map();
|
|
30
|
+
byKind.set(e.kind, (byKind.get(e.kind) || 0) + 1);
|
|
31
|
+
stats.set(String(e.rule), byKind);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 规则全集(对照零命中)
|
|
35
|
+
let ruleIds = new Set();
|
|
36
|
+
try {
|
|
37
|
+
const u = JSON.parse(readFileSync(UNDERSTANDING, "utf8"));
|
|
38
|
+
ruleIds = new Set((u.rules || []).map((r) => String(r.ruleId)));
|
|
39
|
+
} catch { /* 理解产物缺失时只统计日志内规则 */ }
|
|
40
|
+
|
|
41
|
+
const rows = [...stats.entries()]
|
|
42
|
+
.map(([rule, byKind]) => ({ rule, total: [...byKind.values()].reduce((a, b) => a + b, 0), byKind }))
|
|
43
|
+
.sort((a, b) => b.total - a.total);
|
|
44
|
+
|
|
45
|
+
console.log(`规则触发率统计(样本总量 ${total}${total < MIN_SAMPLE ? `,⚠ 样本不足 ${MIN_SAMPLE}——暂不据此删/并规则` : ""})`);
|
|
46
|
+
console.log("规则 触发数 分类明细");
|
|
47
|
+
for (const { rule, total, byKind } of rows.slice(0, TOP)) {
|
|
48
|
+
const detail = [...byKind.entries()].map(([k, n]) => `${k}:${n}`).join(" ");
|
|
49
|
+
console.log(`${String(rule).padEnd(10)} ${String(total).padEnd(7)} ${detail}`);
|
|
50
|
+
}
|
|
51
|
+
const zero = [...ruleIds].filter((id) => !stats.has(id));
|
|
52
|
+
if (zero.length > 0) {
|
|
53
|
+
console.log(`\n零命中规则(${zero.length}):${zero.join(", ")}`);
|
|
54
|
+
console.log("注:零命中 ≠ 无用(条件触发型低频规则正常);仅作体检参考,勿据单次样本删规则。");
|
|
55
|
+
}
|