dsh-code-security 0.3.0 → 0.3.2

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.en.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # dsh-code-security
2
2
 
3
+ ![npm](https://img.shields.io/npm/v/dsh-code-security) ![downloads](https://img.shields.io/npm/dm/dsh-code-security) ![license](https://img.shields.io/github/license/STARDUSTLC666/dsh-code-security) ![stars](https://img.shields.io/github/stars/STARDUSTLC666/dsh-code-security?style=social)
4
+
3
5
  > Every agent code change passes a local security scan before delivery.
4
6
 
5
7
  A DeepSeek Harness plugin for AI code security review: deterministic rule engine, git-diff incremental review, fix-verify loop, and policy gate. Methodology borrows from Codex security skills (evidence-first findings, severity ordering, supply-chain layers). Zero runtime dependencies.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # dsh-code-security
2
2
 
3
+ ![npm](https://img.shields.io/npm/v/dsh-code-security) ![downloads](https://img.shields.io/npm/dm/dsh-code-security) ![license](https://img.shields.io/github/license/STARDUSTLC666/dsh-code-security) ![stars](https://img.shields.io/github/stars/STARDUSTLC666/dsh-code-security?style=social)
4
+
3
5
  > 让 agent 每次改完代码,先过一道本地安全扫描,修复完了才准交付。
4
6
 
5
7
  DeepSeek Harness AI 代码安全审查插件:确定性规则引擎 + git diff 增量审查 + 修复闭环 + 策略门禁。借鉴 Codex 安全技能的方法论(漏洞分级、证据优先、供应链分层、Agent 安全),实现为可复现工具。零运行时依赖。
@@ -31,7 +33,7 @@ DeepSeek Harness AI 代码安全审查插件:确定性规则引擎 + git diff
31
33
 
32
34
  ## 兼容性
33
35
 
34
- 在 `@deepseek-ai/dsh@0.1.1-rc.2` 上验证(2026-08-26)。遵循 cordis 组合包补丁模型(`cordis.patch.yml` + `dsh.bundle.patch`),运行时不 import 任何 `@deepseek-ai/*` 内部模块。
36
+ 在 `@deepseek-ai/dsh@0.1.2-alpha.4` 源码模式下验证(2026-09-02)。遵循 cordis 组合包补丁模型(`cordis.patch.yml` + `dsh.bundle.patch`),运行时不 import 任何 `@deepseek-ai/*` 内部模块。
35
37
 
36
38
  ## 安装
37
39
 
package/lib/extra.js CHANGED
@@ -92,17 +92,5 @@ export function buildExtraTools(cfg, cwd) {
92
92
  },
93
93
  timeoutMs: 30000,
94
94
  };
95
- secureBaseline.gate = async (exec, next) => {
96
- const record = (typeof exec === 'object' && exec !== null ? exec : {});
97
- const approval = record.approval;
98
- if (approval === undefined)
99
- return { kind: 'deny', reason: 'secure_baseline 需要确认,但当前环境没有审批通道。' };
100
- const outcome = await approval.request({ reason: '把当前全部安全问题接受为基线' });
101
- if (outcome === 'allowed-once')
102
- return next();
103
- if (outcome === 'cancelled')
104
- return { kind: 'deny', reason: '基线接受被取消,未执行。' };
105
- return { kind: 'deny', reason: '基线接受未获批准。' };
106
- };
107
95
  return [secureBaseline, secureDeps];
108
96
  }
package/lib/index.d.ts CHANGED
@@ -17,12 +17,11 @@ export interface SecurePluginContext {
17
17
  spawn: SubprocessSpawnLike;
18
18
  };
19
19
  tools: {
20
- register(definition: SecureToolDefinition, options?: {
21
- prepend?: boolean;
22
- }): () => void;
20
+ register(definition: SecureToolDefinition): () => void;
23
21
  };
24
- get?(name: 'approval'): unknown;
25
- on?(event: string, listener: () => void): () => void;
22
+ on?(event: string, listener: (...args: any[]) => unknown, options?: {
23
+ prepend?: boolean;
24
+ }): (() => void) | void;
26
25
  }
27
26
  export declare function apply(ctx: SecurePluginContext, config?: SecureConfig | null): void;
28
27
  export * from './config.js';
package/lib/index.js CHANGED
@@ -25,17 +25,22 @@ export function apply(ctx, config) {
25
25
  const tools = buildSecureTools(cfg, process.cwd(), runner);
26
26
  const disposers = [];
27
27
  for (const definition of tools) {
28
- const wrapped = { ...definition };
29
- if (wrapped.gate !== undefined) {
30
- const original = wrapped.gate.bind(wrapped);
31
- wrapped.gate = async (exec, next) => {
32
- const record = (typeof exec === 'object' && exec !== null ? exec : {});
33
- return original({ ...record, approval: ctx.get?.('approval') }, next);
34
- };
35
- }
36
- disposers.push(ctx.tools.register(wrapped, { prepend: true }));
28
+ disposers.push(ctx.tools.register(definition));
37
29
  }
38
30
  if (typeof ctx.on === 'function') {
31
+ ctx.on('tools/pre-execute', async (exec, next) => {
32
+ const args = (typeof exec.arguments === 'object' && exec.arguments !== null ? exec.arguments : {});
33
+ let reason;
34
+ if (exec.name === 'secure_policy_set')
35
+ reason = '覆盖写入项目 .code-security.json 安全策略';
36
+ if (exec.name === 'secure_baseline')
37
+ reason = '把当前全部安全问题接受为基线';
38
+ if (exec.name === 'secure_export' && typeof args.path === 'string' && args.path !== '')
39
+ reason = '导出安全报告到 ' + args.path;
40
+ if (reason === undefined)
41
+ return next();
42
+ return { kind: 'ask', reason };
43
+ }, { prepend: true });
39
44
  ctx.on('dispose', () => { for (const dispose of disposers)
40
45
  dispose(); });
41
46
  }
package/lib/runner.js CHANGED
@@ -10,26 +10,25 @@ export function createSubprocessRunner(spawn, graceMs, defaultTimeoutMs) {
10
10
  const timeoutMs = options?.timeoutMs ?? defaultTimeoutMs;
11
11
  const controller = new AbortController();
12
12
  const timer = setTimeout(() => controller.abort(new Error('git diff timed out')), timeoutMs);
13
- let handle;
14
13
  try {
15
- handle = spawn({
14
+ const handle = spawn({
16
15
  argv,
17
16
  cwd: process.cwd(),
18
17
  stdio: { stdin: 'ignore', stdout: { maxBytes: COLLECT_BYTES }, stderr: { maxBytes: COLLECT_BYTES } },
19
18
  graceMs,
20
19
  signal: controller.signal,
21
20
  });
21
+ const outcome = await handle.done;
22
+ return {
23
+ exitCode: outcome.exitCode,
24
+ signal: outcome.signal,
25
+ stdout: handle.collected.stdout?.readFrom(0).text ?? '',
26
+ stderr: handle.collected.stderr?.readFrom(0).text ?? '',
27
+ };
22
28
  }
23
29
  finally {
24
30
  clearTimeout(timer);
25
31
  }
26
- const outcome = await handle.done;
27
- return {
28
- exitCode: outcome.exitCode,
29
- signal: outcome.signal,
30
- stdout: handle.collected.stdout?.readFrom(0).text ?? '',
31
- stderr: handle.collected.stderr?.readFrom(0).text ?? '',
32
- };
33
32
  },
34
33
  };
35
34
  }
package/lib/tools.d.ts CHANGED
@@ -24,7 +24,6 @@ export interface SecureToolDefinition {
24
24
  render(args: unknown, value: unknown): ContentBlock[];
25
25
  };
26
26
  execute(args: unknown, exec: unknown): Promise<unknown>;
27
- gate?(exec: unknown, next: () => Promise<unknown>): Promise<unknown>;
28
27
  timeoutMs?: number;
29
28
  }
30
29
  export declare function buildSecureTools(cfg: ResolvedSecureConfig, cwd: string, runner: ProcessRunner): SecureToolDefinition[];
package/lib/tools.js CHANGED
@@ -295,18 +295,6 @@ export function buildSecureTools(cfg, cwd, runner) {
295
295
  },
296
296
  timeoutMs: 10000,
297
297
  };
298
- policySet.gate = async (exec, next) => {
299
- const record = (typeof exec === 'object' && exec !== null ? exec : {});
300
- const approval = record.approval;
301
- if (approval === undefined)
302
- return { kind: 'deny', reason: 'secure_policy_set 需要确认,但当前环境没有审批通道。如确需直接写入,请在受控终端手动编辑 .code-security.json。' };
303
- const outcome = await approval.request({ reason: '覆盖写入项目 .code-security.json 安全策略' });
304
- if (outcome === 'allowed-once')
305
- return next();
306
- if (outcome === 'cancelled')
307
- return { kind: 'deny', reason: '策略写入被取消,未执行。' };
308
- return { kind: 'deny', reason: '策略写入未获批准。' };
309
- };
310
298
  const secureExport = {
311
299
  name: 'secure_export',
312
300
  description: '把最近一次扫描结果导出为 SARIF 2.1.0 或 Markdown。path 可选:提供时写入文件(写操作需要审批)。',
@@ -339,22 +327,6 @@ export function buildSecureTools(cfg, cwd, runner) {
339
327
  },
340
328
  timeoutMs: 30000,
341
329
  };
342
- secureExport.gate = async (exec, next) => {
343
- const record = (typeof exec === 'object' && exec !== null ? exec : {});
344
- const args = (typeof record.args === 'object' && record.args !== null ? record.args : {});
345
- const target = typeof args.path === 'string' && args.path !== '' ? args.path : '';
346
- if (target === '')
347
- return next();
348
- const approval = record.approval;
349
- if (approval === undefined)
350
- return { kind: 'deny', reason: 'secure_export 写入文件需要确认,但当前环境没有审批通道。' };
351
- const outcome = await approval.request({ reason: '导出安全报告到 ' + target });
352
- if (outcome === 'allowed-once')
353
- return next();
354
- if (outcome === 'cancelled')
355
- return { kind: 'deny', reason: '导出被取消,未执行。' };
356
- return { kind: 'deny', reason: '导出未获批准。' };
357
- };
358
330
  const secureHealth = {
359
331
  name: 'secure_health',
360
332
  description: 'dsh-code-security 自检:汇总扫描配置(文件数/体积上限、严重度门禁、状态目录)并确认工作目录可用。遇到问题时先运行本工具定位。',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-code-security",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "DSH AI 代码安全审查插件:secure_scan/secure_diff/secure_fix_verify/secure_report/secure_export/secure_baseline/secure_deps/secure_policy_show/secure_policy_set 九工具,40+ 确定性规则、密钥熵检测、git diff/staged 审查、SARIF 导出、基线接受与 SBOM-lite,零运行时依赖。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",