dsh-code-security 0.3.3 → 0.3.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.en.md +6 -0
- package/README.md +6 -0
- package/lib/tools.d.ts +1 -0
- package/lib/tools.js +38 -22
- package/package.json +2 -2
package/README.en.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# dsh-code-security
|
|
2
2
|
|
|
3
|
+
## 0.3.5 update (2026-09-28)
|
|
4
|
+
|
|
5
|
+
Rejects unknown scan arguments and empty targets before scanning or replacing state, preventing misspelled scopes from expanding to the whole workspace. Fixes standard-mode output incorrectly displaying zero files and a failed verdict: counts, verdicts and findings now come from the actual scan result.
|
|
6
|
+
|
|
7
|
+
Validation host: Harness `0.2.0-rc.1` built from official sources (commit `407e65c8`) with Node `24.16.0` on 2026-09-28. All 31 plugin tests pass in an isolated environment; all 18 plugins mount together in one host registering 10 tools, with tool schemas and health-check contracts passing. No live ports or external services were exercised in this round.
|
|
8
|
+
|
|
3
9
|
   
|
|
4
10
|
|
|
5
11
|
> Every agent code change passes a local security scan before delivery.
|
package/README.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# dsh-code-security
|
|
2
2
|
|
|
3
|
+
## 0.3.5 更新(2026-09-28)
|
|
4
|
+
|
|
5
|
+
扫描参数写错时明确报错,避免把 `paths`、`files` 或空目标误当成扫描整个工作区。修复标准模式中结果卡片误显示“0 个文件、未通过”的问题;文件数量、结论与问题列表现在使用实际扫描结果。
|
|
6
|
+
|
|
7
|
+
验证宿主:官方源码构建的 Harness `0.2.0-rc.1`(commit `407e65c8`)+ Node `24.16.0`(2026-09-28)。31 项插件测试在隔离环境全部通过;同一个宿主里 18 个插件共同加载,注册 10 个工具,工具 schema 与健康检查契约通过。本轮未启用真实端口与外部服务。
|
|
8
|
+
|
|
3
9
|
   
|
|
4
10
|
|
|
5
11
|
> 让 agent 每次改完代码,先过一道本地安全扫描,修复完了才准交付。
|
package/lib/tools.d.ts
CHANGED
package/lib/tools.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { writeFile } from 'node:fs/promises';
|
|
9
9
|
import path from 'node:path';
|
|
10
|
+
import { pathToFileURL } from 'node:url';
|
|
10
11
|
import { optionalString, requiredString } from './config.js';
|
|
11
12
|
import { buildExtraTools } from './extra.js';
|
|
12
13
|
import { runGitDiff, scanDiff } from './diff.js';
|
|
@@ -32,6 +33,21 @@ function compileParameters(spec) {
|
|
|
32
33
|
}
|
|
33
34
|
return { type: 'object', properties, ...(required.length > 0 ? { required } : {}) };
|
|
34
35
|
}
|
|
36
|
+
/** Reject ambiguous scopes before reading files, running git, or replacing scan state. */
|
|
37
|
+
function scanArguments(raw, allowed = ['target']) {
|
|
38
|
+
if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
39
|
+
throw new Error('扫描参数必须是对象。请用 { target: "文件或目录" } 限定范围;扫描整个工作区请传 {}。');
|
|
40
|
+
}
|
|
41
|
+
const args = raw;
|
|
42
|
+
const unknown = Object.keys(args).filter(key => !allowed.includes(key));
|
|
43
|
+
if (unknown.length > 0) {
|
|
44
|
+
throw new Error('不支持的扫描参数:' + unknown.join('、') + '。请使用 target 指定一个文件或目录;仅省略 target 或使用 target: "." 才扫描整个工作区。');
|
|
45
|
+
}
|
|
46
|
+
if (Object.hasOwn(args, 'target') && (typeof args.target !== 'string' || args.target.trim() === '')) {
|
|
47
|
+
throw new Error('扫描参数 target 必须是非空的文件或目录字符串。扫描整个工作区请省略 target 或使用 "."。');
|
|
48
|
+
}
|
|
49
|
+
return args;
|
|
50
|
+
}
|
|
35
51
|
const findingSchema = {
|
|
36
52
|
type: 'object',
|
|
37
53
|
properties: {
|
|
@@ -54,7 +70,7 @@ const scanSchema = {
|
|
|
54
70
|
failOn: { type: 'string' }, durationMs: { type: 'integer' },
|
|
55
71
|
newFindings: { type: 'array', items: findingSchema }, newCounts: countsSchema,
|
|
56
72
|
acceptedFindings: { type: 'array', items: findingSchema },
|
|
57
|
-
baseline: { type: 'object', additionalProperties: true },
|
|
73
|
+
baseline: { oneOf: [{ type: 'object', additionalProperties: true }, { type: 'null' }] },
|
|
58
74
|
},
|
|
59
75
|
additionalProperties: true,
|
|
60
76
|
};
|
|
@@ -69,7 +85,7 @@ function baselineSummary(value) {
|
|
|
69
85
|
accepted: Array.isArray(rec.acceptedFindings) ? rec.acceptedFindings : [],
|
|
70
86
|
};
|
|
71
87
|
}
|
|
72
|
-
function renderFindings(value) {
|
|
88
|
+
function renderFindings(_args, value) {
|
|
73
89
|
const rec = (value ?? {});
|
|
74
90
|
const findings = Array.isArray(rec.findings) ? rec.findings : [];
|
|
75
91
|
const counts = rec.counts;
|
|
@@ -87,13 +103,13 @@ export function buildSecureTools(cfg, cwd, runner) {
|
|
|
87
103
|
const stateDir = cfg.stateDir;
|
|
88
104
|
const secureScan = {
|
|
89
105
|
name: 'secure_scan',
|
|
90
|
-
description: '
|
|
91
|
-
parameters: compileParameters({
|
|
92
|
-
|
|
93
|
-
|
|
106
|
+
description: '扫描工作区(或指定文件/目录),用确定性规则检测注入、弱加密、硬编码密钥、危险配置等问题。使用 target 字符串限定范围,不接受 paths/files 等参数;{} 表示整个工作区。结果写入 .code-security 状态供 secure_fix_verify 对比。',
|
|
107
|
+
parameters: { ...compileParameters({
|
|
108
|
+
target: { type: 'string', description: '要扫描的文件或目录(可选,缺省扫描整个工作区)。' },
|
|
109
|
+
}), additionalProperties: false },
|
|
94
110
|
output: { schema: scanSchema, render: renderFindings },
|
|
95
111
|
async execute(rawArgs) {
|
|
96
|
-
const args = (rawArgs
|
|
112
|
+
const args = scanArguments(rawArgs);
|
|
97
113
|
const target = optionalString(args, 'target');
|
|
98
114
|
const policy = await loadPolicy(cwd);
|
|
99
115
|
const failOn = policy.failOn ?? cfg.failOn;
|
|
@@ -118,14 +134,14 @@ export function buildSecureTools(cfg, cwd, runner) {
|
|
|
118
134
|
const secureDiff = {
|
|
119
135
|
name: 'secure_diff',
|
|
120
136
|
description: '只审查 git diff 的新增行(默认 HEAD,即未提交改动)。base 可用任意 git ref;staged=true 审查已暂存改动;target 可限定文件。结果同样写入状态。',
|
|
121
|
-
parameters: compileParameters({
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
137
|
+
parameters: { ...compileParameters({
|
|
138
|
+
base: { type: 'string', description: 'git 基线(默认 HEAD)。' },
|
|
139
|
+
target: { type: 'string', description: '限定文件路径(可选)。' },
|
|
140
|
+
staged: { type: 'boolean', description: '审查已暂存改动(git diff --cached,默认 false)。' },
|
|
141
|
+
}), additionalProperties: false },
|
|
126
142
|
output: { schema: scanSchema, render: renderFindings },
|
|
127
143
|
async execute(rawArgs) {
|
|
128
|
-
const args = (rawArgs
|
|
144
|
+
const args = scanArguments(rawArgs, ['base', 'target', 'staged']);
|
|
129
145
|
const base = optionalString(args, 'base') ?? 'HEAD';
|
|
130
146
|
const target = optionalString(args, 'target');
|
|
131
147
|
const policy = await loadPolicy(cwd);
|
|
@@ -152,9 +168,9 @@ export function buildSecureTools(cfg, cwd, runner) {
|
|
|
152
168
|
const secureFixVerify = {
|
|
153
169
|
name: 'secure_fix_verify',
|
|
154
170
|
description: '复扫并与上次扫描基线对比,输出已关闭、仍存在、新引入的问题指纹。用于修复后确认没有按下葫芦浮起瓢。',
|
|
155
|
-
parameters: compileParameters({
|
|
156
|
-
|
|
157
|
-
|
|
171
|
+
parameters: { ...compileParameters({
|
|
172
|
+
target: { type: 'string', description: '复扫文件或目录(可选,缺省扫描整个工作区)。' },
|
|
173
|
+
}), additionalProperties: false },
|
|
158
174
|
output: {
|
|
159
175
|
schema: {
|
|
160
176
|
type: 'object',
|
|
@@ -172,7 +188,7 @@ export function buildSecureTools(cfg, cwd, runner) {
|
|
|
172
188
|
},
|
|
173
189
|
},
|
|
174
190
|
async execute(rawArgs) {
|
|
175
|
-
const args = (rawArgs
|
|
191
|
+
const args = scanArguments(rawArgs);
|
|
176
192
|
const target = optionalString(args, 'target');
|
|
177
193
|
const policy = await loadPolicy(cwd);
|
|
178
194
|
const state = await loadState(stateDir);
|
|
@@ -320,7 +336,7 @@ export function buildSecureTools(cfg, cwd, runner) {
|
|
|
320
336
|
const state = await loadState(stateDir);
|
|
321
337
|
if (state.last === null)
|
|
322
338
|
throw new Error('尚无扫描结果,请先执行 secure_scan 或 secure_diff。');
|
|
323
|
-
const text = format === 'sarif' ? buildSarif(state.last.findings,
|
|
339
|
+
const text = format === 'sarif' ? buildSarif(state.last.findings, cwd) : buildMarkdown(state.last.findings, state.last.target);
|
|
324
340
|
if (target !== undefined)
|
|
325
341
|
await writeFile(target, text, 'utf8');
|
|
326
342
|
return { format, path: target ?? '', text, findingCount: state.last.findings.length };
|
|
@@ -362,15 +378,15 @@ function buildMarkdown(findings, target) {
|
|
|
362
378
|
}
|
|
363
379
|
return lines.join('\n') + '\n';
|
|
364
380
|
}
|
|
365
|
-
function buildSarif(findings,
|
|
381
|
+
function buildSarif(findings, cwd) {
|
|
366
382
|
const rules = new Map();
|
|
367
383
|
const results = findings.map((finding, index) => {
|
|
368
|
-
rules.set(finding.ruleId, { id: finding.ruleId, name: finding.title, shortDescription: finding.title });
|
|
384
|
+
rules.set(finding.ruleId, { id: finding.ruleId, name: finding.title, shortDescription: { text: finding.title } });
|
|
369
385
|
return {
|
|
370
386
|
ruleId: finding.ruleId,
|
|
371
387
|
level: finding.severity === 'critical' || finding.severity === 'high' ? 'error' : finding.severity === 'medium' ? 'warning' : 'note',
|
|
372
388
|
message: { text: finding.message },
|
|
373
|
-
locations: [{ physicalLocation: { artifactLocation: { uri: finding.file.replace(/\\/g, '/') }, region: { startLine: finding.line } } }],
|
|
389
|
+
locations: [{ physicalLocation: { artifactLocation: { uri: finding.file.replace(/\\/g, '/').split('/').map(encodeURIComponent).join('/'), uriBaseId: 'ROOTPATH' }, region: { startLine: finding.line } } }],
|
|
374
390
|
partialFingerprints: { primaryLocationLineHash: finding.id + ':' + String(index) },
|
|
375
391
|
};
|
|
376
392
|
});
|
|
@@ -379,7 +395,7 @@ function buildSarif(findings, target) {
|
|
|
379
395
|
$schema: 'https://json.schemastore.org/sarif-2.1.0.json',
|
|
380
396
|
runs: [{
|
|
381
397
|
tool: { driver: { name: 'dsh-code-security', version: '0.1.0', informationUri: 'https://github.com/STARDUSTLC666/dsh-code-security', rules: [...rules.values()] } },
|
|
382
|
-
originalUriBaseIds: { ROOTPATH: { uri:
|
|
398
|
+
originalUriBaseIds: { ROOTPATH: { uri: pathToFileURL(path.resolve(cwd) + path.sep).href } },
|
|
383
399
|
results,
|
|
384
400
|
}],
|
|
385
401
|
}, null, 2);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-code-security",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "DSH
|
|
3
|
+
"version": "0.3.6",
|
|
4
|
+
"description": "DSH 代码安全审查插件:40+ 确定性规则与密钥熵检测,支持 git diff/staged 审查、SARIF 导出、基线接受与 SBOM-lite。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/index.d.ts",
|