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 +21 -0
- package/README.en.md +4 -1
- package/README.md +18 -2
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/rules.js +10 -9
- package/lib/scanner.js +6 -3
- package/lib/tools.js +27 -1
- package/package.json +61 -62
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 # 构建 +
|
|
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: [/\.
|
|
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,
|
|
65
|
+
const group = ruleId.slice(4, 5);
|
|
65
66
|
const map = {
|
|
66
|
-
'
|
|
67
|
-
'
|
|
68
|
-
'
|
|
69
|
-
'
|
|
70
|
-
'
|
|
71
|
-
'
|
|
72
|
-
'
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
|
|
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
|
+
}
|