dsh-code-security 0.2.2 → 0.3.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 stardustlc
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md CHANGED
@@ -18,10 +18,13 @@ A DeepSeek Harness plugin for AI code security review: deterministic rule engine
18
18
  | `secure_policy_show` | Show .code-security.json | no |
19
19
  | `secure_policy_set` | Replace policy JSON | approval |
20
20
 
21
- 40+ deterministic rules: injection, deserialization, weak crypto, secrets, dangerous config, sensitive logging, path traversal, SSRF.
21
+ 40+ deterministic rules: injection, deserialization, weak crypto (including shell TLS bypass flags), secrets, dangerous config, sensitive logging, path traversal, SSRF.
22
22
 
23
23
  ```bash
24
24
  dsh plugin --profile web add dsh-code-security
25
25
  ```
26
26
 
27
27
  MIT
28
+ ## License
29
+
30
+ MIT (see [LICENSE](LICENSE))
package/README.md CHANGED
@@ -22,19 +22,32 @@ DeepSeek Harness AI 代码安全审查插件:确定性规则引擎 + git diff
22
22
 
23
23
  - 注入:eval / exec / shell=True / SQL 拼接 / innerHTML
24
24
  - 反序列化:pickle / yaml.load / ObjectInputStream / Marshal / unserialize
25
- - 加密:弱哈希、ECB、硬编码 IV/密钥、JWT none、TLS 校验关闭
25
+ - 加密:弱哈希、ECB、硬编码 IV/密钥、JWT none、TLS 校验关闭、Shell TLS 绕过(curl -k / wget --no-check-certificate / git sslVerify=false)
26
26
  - 凭据:硬编码密码、令牌、私钥、高熵密钥
27
27
  - 配置:chmod 777、Docker privileged / latest、npm audit=false、CORS *
28
28
  - 泄露:敏感日志、错误堆栈外发、路径穿越、SSRF
29
29
 
30
30
  发现只包含客观证据与 CWE 编号,不附修复建议——修复方案由 agent 基于证据生成。
31
31
 
32
+ ## 兼容性
33
+
34
+ 在 `@deepseek-ai/dsh@0.1.1-rc.2` 上验证(2026-08-26)。遵循 cordis 组合包补丁模型(`cordis.patch.yml` + `dsh.bundle.patch`),运行时不 import 任何 `@deepseek-ai/*` 内部模块。
35
+
32
36
  ## 安装
33
37
 
34
38
  ```bash
35
39
  dsh plugin --profile web add dsh-code-security
36
40
  ```
37
41
 
42
+ ## 卸载
43
+
44
+ ```bash
45
+ dsh plugin --profile web remove dsh-code-security
46
+ ```
47
+
48
+ 卸载后重启 Web 服务。如需彻底清理,可再手动删除自己 profile `cordis.patch.yml` 中覆盖的插件行。
49
+
50
+
38
51
  ## 使用
39
52
 
40
53
  ```text
@@ -59,7 +72,10 @@ secure_deps { target: . }
59
72
  ## 工程
60
73
 
61
74
  ```bash
62
- pnpm test # 构建 + 23 个测试
75
+ pnpm test # 构建 + 24 个测试
63
76
  ```
64
77
 
65
78
  MIT
79
+ ## License
80
+
81
+ MIT(见 [LICENSE](LICENSE))
package/lib/index.d.ts CHANGED
@@ -10,6 +10,7 @@
10
10
  import { type SecureConfig } from './config.js';
11
11
  import { type SubprocessSpawnLike } from './runner.js';
12
12
  import { type SecureToolDefinition } from './tools.js';
13
+ export declare const name = "code-security";
13
14
  export declare const inject: string[];
14
15
  export interface SecurePluginContext {
15
16
  subprocess: {
package/lib/index.js CHANGED
@@ -10,6 +10,7 @@
10
10
  import { resolveConfig } from './config.js';
11
11
  import { createSubprocessRunner } from './runner.js';
12
12
  import { buildSecureTools } from './tools.js';
13
+ export const name = 'code-security';
13
14
  export const inject = ['tools', 'subprocess'];
14
15
  export function apply(ctx, config) {
15
16
  let cfg;
package/lib/rules.js CHANGED
@@ -28,10 +28,11 @@ export const RULES = Object.freeze([
28
28
  { id: 'SEC-105', title: 'PHP 反序列化', cwe: 'CWE-502', severity: 'high', confidence: 'medium', languages: ['php'], patterns: [/\bunserialize\s*\(/], message: 'unserialize 处理不可信输入。' },
29
29
  { id: 'SEC-201', title: '弱哈希用于安全场景', cwe: 'CWE-328', severity: 'high', confidence: 'medium', languages: ALL, patterns: [/\bcreateHash\s*\(\s*['"]?(?:md5|sha1)['"]?/i, /\bhashlib\.(?:md5|sha1)\s*\(/i, /\b(?:md5|sha1)(?:sum)?\s+/i], message: '使用 MD5 / SHA1 弱哈希。' },
30
30
  { id: 'SEC-202', title: 'ECB 分组加密模式', cwe: 'CWE-327', severity: 'high', confidence: 'medium', languages: ALL, patterns: [/aes[-_/]?\d*[-_/]?ecb/i, /['"]AES\/ECB/i], message: '使用 ECB 加密模式。' },
31
- { id: 'SEC-203', title: '硬编码 IV / 密钥', cwe: 'CWE-321', severity: 'high', confidence: 'medium', languages: ALL, patterns: [/\.createCipheriv?\s*\([^)]*['"][A-Za-z0-9+/=]{12,}['"]/, /\b(?:iv|key|secret)\s*[:=]\s*['"][A-Za-z0-9+/=]{16,}['"]/i], message: '加密密钥或 IV 以字面量硬编码。' },
31
+ { id: 'SEC-203', title: '硬编码 IV / 密钥', cwe: 'CWE-321', severity: 'high', confidence: 'medium', languages: ALL, patterns: [/\.createCipher(?:iv)?\s*\([^)]*['"][A-Za-z0-9+/=]{12,}['"]/, /\b(?:iv|key|secret)\s*[:=]\s*['"][A-Za-z0-9+/=]{16,}['"]/i], message: '加密密钥或 IV 以字面量硬编码。' },
32
32
  { id: 'SEC-204', title: 'JWT alg none / 不校验签名', cwe: 'CWE-347', severity: 'critical', confidence: 'high', languages: JS, patterns: [/['"]?alg['"]?\s*[:=]\s*['"]?none['"]?/i, /\bjwt\.verify\s*\([^)]*\{\s*algorithms\s*:\s*\[\s*['"]none/i], message: 'JWT 允许 none 算法或未校验签名。' },
33
33
  { id: 'SEC-205', title: 'TLS 校验被禁用', cwe: 'CWE-295', severity: 'high', confidence: 'high', languages: ALL, patterns: [/rejectUnauthorized\s*:\s*false/, /\bverify\s*=\s*False/, /InsecureSkipVerify\s*:\s*true/], message: 'TLS 证书校验被显式关闭。' },
34
34
  { id: 'SEC-206', title: 'Math.random 用于安全敏感逻辑', cwe: 'CWE-330', severity: 'low', confidence: 'low', languages: JS, patterns: [/\bMath\.random\s*\(/], message: '使用非密码学安全的 Math.random。' },
35
+ { id: 'SEC-207', title: 'Shell 关闭 TLS 证书校验', cwe: 'CWE-295', severity: 'high', confidence: 'high', languages: SHELL, patterns: [/\bcurl\b[^\n]*(?:-k\b|--insecure\b)/, /\bwget\b[^\n]*--no-check-certificate\b/, /\bgit\b[^\n]*-c\s+http\.sslVerify\s*=\s*false\b/i, /\bnpm\b[^\n]*--strict-ssl\s*=\s*false\b/i], message: '命令行下载/访问工具关闭了 TLS 证书校验。' },
35
36
  { id: 'SEC-301', title: '硬编码密码', cwe: 'CWE-798', severity: 'critical', confidence: 'high', languages: ALL, patterns: [/(?:password|passwd|pwd)\s*[:=]\s*['"][^'"]{4,}['"]/i], message: '密码以明文硬编码在源码中。' },
36
37
  { id: 'SEC-302', title: '硬编码令牌 / API 密钥', cwe: 'CWE-798', severity: 'high', confidence: 'medium', languages: ALL, patterns: [/(?:api[_-]?key|secret[_-]?key|access[_-]?key|auth[_-]?token)\s*[:=]\s*['"][A-Za-z0-9_\-]{16,}['"]/i], message: '访问令牌或 API 密钥以字面量硬编码。' },
37
38
  { id: 'SEC-303', title: '私钥材料', cwe: 'CWE-798', severity: 'critical', confidence: 'high', languages: ALL, patterns: [/-----BEGIN (?:RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----/], message: '检测到私钥文件内容。', fileLevel: true },
@@ -61,15 +62,15 @@ export const RULES = Object.freeze([
61
62
  ]);
62
63
  /** 规则类别:供报告聚合与排序使用。 */
63
64
  export function ruleCategory(ruleId) {
64
- const group = ruleId.slice(4, 6);
65
+ const group = ruleId.slice(4, 5);
65
66
  const map = {
66
- '00': '注入',
67
- '01': '反序列化',
68
- '02': '加密与密钥',
69
- '03': '凭据泄露',
70
- '04': '配置与权限',
71
- '05': '日志与信息泄露',
72
- '06': '路径与网络',
67
+ '0': '注入',
68
+ '1': '反序列化',
69
+ '2': '加密与密钥',
70
+ '3': '凭据泄露',
71
+ '4': '配置与权限',
72
+ '5': '日志与信息泄露',
73
+ '6': '路径与网络',
73
74
  };
74
75
  return map[group] ?? '其他';
75
76
  }
package/lib/scanner.js CHANGED
@@ -37,7 +37,7 @@ function fileHasBinary(buffer) {
37
37
  return true;
38
38
  return false;
39
39
  }
40
- const TEXT_EXTENSIONS = /\.(?:js|mjs|cjs|jsx|ts|tsx|py|go|java|rb|php|sh|bash|zsh|ya?ml|json|env|dockerfile|txt|md|html|css|vue|svelte)$/i;
40
+ const TEXT_EXTENSIONS = /\.(?:js|mjs|cjs|jsx|ts|tsx|py|go|java|rb|php|sh|bash|zsh|ya?ml|json|env|dockerfile|txt|md|html|css|vue|svelte|pem|key|crt|cer|p12|der|p8)$/i;
41
41
  function shouldScanFile(file) {
42
42
  const base = path.basename(file).toLowerCase();
43
43
  if (base === 'dockerfile')
@@ -105,7 +105,8 @@ export function scanLineSet(entries, rel, policy) {
105
105
  prevDisable.set(entry.line + 1, disabledRules(entry.text, DISABLE_NEXT));
106
106
  }
107
107
  for (const entry of entries) {
108
- const currentDisable = isComment(entry.text) ? disabledRules(entry.text, DISABLE_LINE) : null;
108
+ const isCommentLine = isComment(entry.text);
109
+ const currentDisable = isCommentLine ? disabledRules(entry.text, DISABLE_LINE) : null;
109
110
  for (const rule of applied) {
110
111
  if (rule.id === 'SEC-304')
111
112
  continue;
@@ -115,6 +116,8 @@ export function scanLineSet(entries, rel, policy) {
115
116
  continue;
116
117
  if (rule.fileLevel === true)
117
118
  continue;
119
+ if (isCommentLine)
120
+ continue;
118
121
  if (!rule.patterns.some((pattern) => pattern.test(entry.text)))
119
122
  continue;
120
123
  findings.push({
@@ -131,7 +134,7 @@ export function scanLineSet(entries, rel, policy) {
131
134
  });
132
135
  }
133
136
  const entropyRule = RULES.find((rule) => rule.id === 'SEC-304');
134
- if (entropyRule !== undefined && !policyIgnores(policy, entropyRule.id, rel)) {
137
+ if (entropyRule !== undefined && !policyIgnores(policy, entropyRule.id, rel) && !isCommentLine && !shouldSkipLine(entropyRule, currentDisable) && !shouldSkipLine(entropyRule, prevDisable.get(entry.line))) {
135
138
  findings.push(...detectHighEntropy(entry.text, entry.line, rel, rel, entropyRule));
136
139
  }
137
140
  }
package/lib/tools.js CHANGED
@@ -355,7 +355,33 @@ export function buildSecureTools(cfg, cwd, runner) {
355
355
  return { kind: 'deny', reason: '导出被取消,未执行。' };
356
356
  return { kind: 'deny', reason: '导出未获批准。' };
357
357
  };
358
- return [secureScan, secureDiff, secureFixVerify, secureReport, secureExport, ...buildExtraTools(cfg, cwd), policyShow, policySet];
358
+ const secureHealth = {
359
+ name: 'secure_health',
360
+ description: 'dsh-code-security 自检:汇总扫描配置(文件数/体积上限、严重度门禁、状态目录)并确认工作目录可用。遇到问题时先运行本工具定位。',
361
+ parameters: compileParameters({}),
362
+ output: {
363
+ schema: { type: 'object', additionalProperties: true },
364
+ render: (_args, value) => {
365
+ const rec = (value ?? {});
366
+ const rawChecks = Array.isArray(rec.checks) ? rec.checks : [];
367
+ const lines = ['dsh-code-security 自检' + (rec.ok === true ? ':正常。' : ':发现问题。')];
368
+ for (const item of rawChecks) {
369
+ const c = (item ?? {});
370
+ lines.push('- ' + String(c.name) + ':' + (c.ok === true ? '✅ ' + String(c.detail ?? '') : '❌ ' + String(c.detail ?? '')));
371
+ }
372
+ return [{ type: 'text', text: lines.join('\n') }];
373
+ },
374
+ },
375
+ async execute(rawArgs) {
376
+ const checks = [];
377
+ checks.push({ name: '扫描上限', ok: true, detail: 'maxFiles=' + cfg.maxFiles + ',maxFileBytes=' + cfg.maxFileBytes });
378
+ checks.push({ name: '严重度门禁', ok: true, detail: 'failOn=' + cfg.failOn });
379
+ checks.push({ name: '状态目录', ok: true, detail: cfg.stateDir });
380
+ checks.push({ name: '工作目录', ok: cwd !== '', detail: cwd });
381
+ return { ok: true, plugin: 'dsh-code-security', checks };
382
+ },
383
+ };
384
+ return [secureScan, secureDiff, secureFixVerify, secureReport, secureExport, ...buildExtraTools(cfg, cwd), policyShow, policySet, secureHealth];
359
385
  }
360
386
  function buildMarkdown(findings, target) {
361
387
  const lines = ['# 安全审查报告', '', '目标:' + target, '', '| 严重度 | 规则 | CWE | 文件:行 | 说明 |', '| :-- | :-- | :-- | :-- | :-- |'];
package/package.json CHANGED
@@ -1,62 +1,61 @@
1
- {
2
- "name": "dsh-code-security",
3
- "version": "0.2.2",
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
- "type": "module",
6
- "main": "lib/index.js",
7
- "types": "lib/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./lib/index.d.ts",
11
- "default": "./lib/index.js"
12
- },
13
- "./cordis.patch.yml": "./cordis.patch.yml",
14
- "./package.json": "./package.json"
15
- },
16
- "files": [
17
- "lib",
18
- "cordis.patch.yml",
19
- "README.md",
20
- "README.en.md"
21
- ],
22
- "scripts": {
23
- "build": "tsc -p tsconfig.json",
24
- "prepare": "tsc -p tsconfig.json",
25
- "typecheck": "tsc -p tsconfig.json --noEmit",
26
- "test": "pnpm run build && node --test \"test/*.test.mjs\"",
27
- "prepublishOnly": "pnpm run build"
28
- },
29
- "dsh": {
30
- "bundle": {
31
- "patch": "./cordis.patch.yml"
32
- }
33
- },
34
- "keywords": [
35
- "dsh",
36
- "deepseek-harness",
37
- "plugin",
38
- "security",
39
- "code-review",
40
- "sast",
41
- "secrets",
42
- "owasp"
43
- ],
44
- "license": "MIT",
45
- "engines": {
46
- "node": ">=22.13"
47
- },
48
- "devDependencies": {
49
- "@types/node": "^24.0.0",
50
- "typescript": "^5.6.0"
51
- },
52
- "repository": {
53
- "type": "git",
54
- "url": "git+https://github.com/STARDUSTLC666/dsh-code-security.git"
55
- },
56
- "bugs": {
57
- "url": "https://github.com/STARDUSTLC666/dsh-code-security/issues"
58
- },
59
- "homepage": "https://github.com/STARDUSTLC666/dsh-code-security#readme",
60
- "author": "stardustlc",
61
- "packageManager": "pnpm@11.7.0"
62
- }
1
+ {
2
+ "name": "dsh-code-security",
3
+ "version": "0.3.0",
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
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "types": "lib/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/index.d.ts",
11
+ "default": "./lib/index.js"
12
+ },
13
+ "./cordis.patch.yml": "./cordis.patch.yml",
14
+ "./package.json": "./package.json"
15
+ },
16
+ "files": [
17
+ "lib",
18
+ "cordis.patch.yml",
19
+ "README.md",
20
+ "README.en.md"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsc -p tsconfig.json",
24
+ "typecheck": "tsc -p tsconfig.json --noEmit",
25
+ "test": "pnpm run build && node --test \"test/*.test.mjs\"",
26
+ "prepublishOnly": "pnpm run build"
27
+ },
28
+ "dsh": {
29
+ "bundle": {
30
+ "patch": "./cordis.patch.yml"
31
+ }
32
+ },
33
+ "keywords": [
34
+ "dsh",
35
+ "deepseek-harness",
36
+ "plugin",
37
+ "security",
38
+ "code-review",
39
+ "sast",
40
+ "secrets",
41
+ "owasp"
42
+ ],
43
+ "license": "MIT",
44
+ "engines": {
45
+ "node": ">=22"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^24.0.0",
49
+ "typescript": "^5.6.0"
50
+ },
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "git+https://github.com/STARDUSTLC666/dsh-code-security.git"
54
+ },
55
+ "bugs": {
56
+ "url": "https://github.com/STARDUSTLC666/dsh-code-security/issues"
57
+ },
58
+ "homepage": "https://github.com/STARDUSTLC666/dsh-code-security#readme",
59
+ "author": "stardustlc",
60
+ "packageManager": "pnpm@11.7.0"
61
+ }