@moonquake2004/dsh-security 0.1.3 → 0.1.4

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 CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
4
4
  [![npm](https://img.shields.io/npm/v/@moonquake2004/dsh-security)](https://www.npmjs.com/package/@moonquake2004/dsh-security)
5
- [![Tests](https://img.shields.io/badge/tests-80%2F80%20passing-brightgreen)](#)
5
+ [![Tests](https://img.shields.io/badge/tests-86%2F86%20passing-brightgreen)](#)
6
6
 
7
- **DSH 生态统一安全检查框架** — 覆盖插件全生命周期(发现→安装→运行→更新→退役),19 个内置检查 + 4 个外部工具集成。
7
+ **DSH 生态统一安全检查框架** — 覆盖插件全生命周期(发现→安装→运行→更新→退役),20 个内置检查 + 4 个外部工具集成。
8
8
 
9
9
  > Community security tool. Not an official DeepSeek project.
10
10
 
@@ -76,6 +76,7 @@ dsh-security 的目标:**建立 DSH 生态的统一安全检查层**,让任
76
76
  │ SP6: 已知漏洞匹配 (OSV/CVE/GHSA) │
77
77
  │ SP7: client 产物语法预检 (#2752 白屏源 boot 前拦截) │
78
78
  │ SP8: dist-tag 异常检测 (#2763 broken latest) │
79
+ │ SP9: CLI 核心包泄漏检测 (#4640 symbol 分裂) │
79
80
  │ SS1: 凭据泄露检测 (会话日志) │
80
81
  │ SS2: PII 数据暴露检测 │
81
82
  │ SS3: 插件输出敏感数据检测 │
@@ -251,7 +252,7 @@ node --test test/*.mjs
251
252
  dsh-security/
252
253
  ├── src/
253
254
  │ ├── protocol/ # Layer 0: Check Protocol
254
- │ ├── checks/ # 19 个内置检查
255
+ │ ├── checks/ # 20 个内置检查
255
256
  │ ├── integrations/ # 4 个外部工具集成
256
257
  │ ├── session-reader.mjs # 会话日志读取(明文 + zstd)
257
258
  │ ├── registry.mjs # Check Registry(支持 setConfig 注入配置)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moonquake2004/dsh-security",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Unified security check framework for DeepSeek Harness ecosystem — covers the full plugin lifecycle (discovery → install → runtime → update → retirement)",
5
5
  "type": "module",
6
6
  "main": "src/index.mjs",
@@ -7,6 +7,7 @@ export { sp5Check } from './sp5-permission-model.mjs';
7
7
  export { sp6Check } from './sp6-vuln-match.mjs';
8
8
  export { sp7Check } from './sp7-client-syntax.mjs';
9
9
  export { sp8Check } from './sp8-dist-tag-health.mjs';
10
+ export { sp9Check } from './sp9-dual-instance-guard.mjs';
10
11
 
11
12
  // Layer 2: Runtime Checks
12
13
  export { sr1Check } from './sr1-sandbox-violation.mjs';
@@ -0,0 +1,105 @@
1
+ /**
2
+ * SP9: Dual-Instance Guard — CLI 核心包泄漏检测
3
+ *
4
+ * 出处:#4640 + zoahdev/dsh-ecosystem 家族 4——profile 的 node_modules
5
+ * 中若出现 @deepseek-ai/dsh-* 包(尤其是 dsh-tools),会导致同一进程
6
+ * 两份副本 → TOOL_RUNTIME_SCHEDULER 唯一 symbol 分裂 →
7
+ * undefined.prepare → 所有工具调用失败 → 会话不可恢复。
8
+ *
9
+ * 核心规则:profile 只承载 surfaces 和 plugins;core packages 由 CLI
10
+ * 自身的依赖树提供。profile 中出现任何 @deepseek-ai/dsh-* 包即为异常。
11
+ *
12
+ * 做法:扫描 profile/node_modules/@deepseek-ai/ 下的目录,
13
+ * 检查是否存在 dsh-* 包(排除纯客户端包如 dsh-client-*,
14
+ * 因为它们可能被插件合法引用;只报 dsh-tools/dsh-agent-loop
15
+ * 等核心运行时包)。
16
+ *
17
+ * Severity: CRITICAL(直接导致工具调用全灭)
18
+ * Phase: POST_INSTALL
19
+ */
20
+
21
+ import { readFileSync, readdirSync, existsSync } from 'node:fs';
22
+ import { join } from 'node:path';
23
+ import { Severity } from '../protocol/severity.mjs';
24
+ import { CheckPhase } from '../protocol/phase.mjs';
25
+ import { pass, fail, skip } from '../protocol/check.mjs';
26
+
27
+ /**
28
+ * CLI 核心运行时包——出现在 profile 中即为异常。
29
+ * 客户端包(dsh-client-*)被插件合法引用,不在此列。
30
+ */
31
+ const CORE_RUNTIME_PKGS = new Set([
32
+ 'dsh-tools',
33
+ 'dsh-agent-loop',
34
+ 'dsh-sandbox-local',
35
+ 'dsh-subprocess-local',
36
+ ]);
37
+
38
+ export async function run(profileDir) {
39
+ const id = 'SP9';
40
+ const nmDir = join(profileDir, 'node_modules');
41
+ if (!existsSync(nmDir)) return pass(id, Severity.CRITICAL, '无 node_modules,跳过核心包泄漏检测');
42
+
43
+ const dsDir = join(nmDir, '@deepseek-ai');
44
+ if (!existsSync(dsDir)) return pass(id, Severity.CRITICAL, 'profile 中无 @deepseek-ai 包,无泄漏风险');
45
+
46
+ // 枚举 @deepseek-ai/* 下的包
47
+ let entries;
48
+ try { entries = readdirSync(dsDir, { withFileTypes: true }); } catch { entries = []; }
49
+
50
+ const leaked = [];
51
+ for (const entry of entries) {
52
+ if (!entry.isDirectory()) continue;
53
+ const pkgName = `@deepseek-ai/${entry.name}`;
54
+ const pjPath = join(dsDir, entry.name, 'package.json');
55
+ if (!existsSync(pjPath)) continue;
56
+
57
+ let version = '?';
58
+ try {
59
+ const pkg = JSON.parse(readFileSync(pjPath, 'utf8'));
60
+ version = pkg.version || '?';
61
+ } catch { /* read error */ }
62
+
63
+ if (entry.name.startsWith('dsh-')) {
64
+ leaked.push({ name: pkgName, version, isCore: CORE_RUNTIME_PKGS.has(entry.name) });
65
+ }
66
+ }
67
+
68
+ if (leaked.length === 0) {
69
+ return pass(id, Severity.CRITICAL, 'profile 中无 @deepseek-ai/dsh-* 泄漏,核心包仅由 CLI 提供');
70
+ }
71
+
72
+ const coreLeaks = leaked.filter(l => l.isCore);
73
+ const otherLeaks = leaked.filter(l => !l.isCore);
74
+
75
+ // 核心运行时包泄漏 = CRITICAL(直接导致 Symbol 分裂 + 工具调用全灭)
76
+ if (coreLeaks.length > 0) {
77
+ const details = coreLeaks.map(l => ` ${l.name}@${l.version}`).join('\n');
78
+ const otherNote = otherLeaks.length > 0
79
+ ? `\n另有 ${otherLeaks.length} 个非核心 @deepseek-ai/dsh-* 包在 profile 中:${otherLeaks.map(l => l.name).join(', ')}`
80
+ : '';
81
+ return fail(id, Severity.CRITICAL,
82
+ `检测到 ${coreLeaks.length} 个 CLI 核心运行时包泄漏到 profile(#4640:双实例 symbol 分裂 → 工具调用全灭):\n${details}${otherNote}\n修复: dsh plugin --profile <name> remove <泄漏包名>(或检查安装来源是否误将 CLI 依赖写入 profile)`,
83
+ '核心包(dsh-tools/dsh-agent-loop 等)不应出现在 profile 的 node_modules 中;它们由 CLI 自身依赖树提供',
84
+ ['#4640']
85
+ );
86
+ }
87
+
88
+ // 只有非核心包泄漏 = HIGH(潜在风险但不一定立即崩溃)
89
+ const details = otherLeaks.map(l => ` ${l.name}@${l.version}`).join('\n');
90
+ return fail(id, Severity.HIGH,
91
+ `检测到 ${otherLeaks.length} 个 @deepseek-ai/dsh-* 包泄漏到 profile(非核心包,暂未触发 symbol 分裂,但增加未来冲突风险):\n${details}`,
92
+ '清理 profile 中不需要的 @deepseek-ai/dsh-* 包;若为客户端包且被插件依赖,确认无版本冲突',
93
+ ['#4640']
94
+ );
95
+ }
96
+
97
+ export const sp9Check = {
98
+ id: 'SP9',
99
+ name: 'dual-instance-guard',
100
+ severity: Severity.CRITICAL,
101
+ phase: CheckPhase.POST_INSTALL,
102
+ description: 'CLI 核心包泄漏检测——profile 中不应出现 @deepseek-ai/dsh-tools 等核心包(#4640 symbol 分裂)',
103
+ src: 'builtin',
104
+ runner: (profileDir) => run(profileDir),
105
+ };
package/src/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * DSH Security Framework — 统一安全检查框架
3
3
  *
4
- * 为 DSH 生态提供全生命周期安全检查(19 个检查项,4 层架构)。
4
+ * 为 DSH 生态提供全生命周期安全检查(20 个检查项,4 层架构)。
5
5
  *
6
6
  * @example
7
7
  * import { createDefaultRegistry } from '@moonquake2004/dsh-security';
@@ -24,6 +24,7 @@ export { sp5Check } from './checks/sp5-permission-model.mjs';
24
24
  export { sp6Check } from './checks/sp6-vuln-match.mjs';
25
25
  export { sp7Check } from './checks/sp7-client-syntax.mjs';
26
26
  export { sp8Check } from './checks/sp8-dist-tag-health.mjs';
27
+ export { sp9Check } from './checks/sp9-dual-instance-guard.mjs';
27
28
 
28
29
  // Layer 2: Runtime Checks
29
30
  export { sr1Check } from './checks/sr1-sandbox-violation.mjs';
package/src/registry.mjs CHANGED
@@ -149,6 +149,7 @@ export async function createDefaultRegistry(loadExternal = true, options = {}) {
149
149
  import('./checks/ss3-sensitive-output.mjs'),
150
150
  import('./checks/sp7-client-syntax.mjs'),
151
151
  import('./checks/sp8-dist-tag-health.mjs'),
152
+ import('./checks/sp9-dual-instance-guard.mjs'),
152
153
  ]);
153
154
  for (const mod of modules) {
154
155
  for (const val of Object.values(mod)) {