dsh-code-security 0.3.7 → 0.4.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/CHANGELOG.md CHANGED
@@ -4,6 +4,23 @@
4
4
 
5
5
  [历史英文记录](docs/CHANGELOG.en.md)
6
6
 
7
+ ## 0.4.0 (2026-10-15)
8
+
9
+ - 增加 DSH 中英文设置工作台:项目选择、扫描/增量/复查、覆盖情况、问题筛选、脱敏证据、复制位置与报告下载。
10
+ - 可选接入 Betterleaks 1.9.0、Semgrep CE 1.181.0 和 OSV-Scanner 2.7.0。扫描使用隔离快照,不执行项目安装/构建脚本;密钥在线验证关闭,OSV 联网查询需要明确选择。
11
+ - 增加自有 MIT 数据流规则和可靠的 DOMPurify 绑定判断,区分模式提示与数据流证据。增强差异扫描读取暂存区/工作树实际内容,仅报告涉及新增行的问题。
12
+ - 统一脱敏输出、历史、基线和导出,迁移旧记录时保留稳定指纹。修复 PEP 621 等 Python 依赖解析,错误/动态声明不再被当成空的安全清单。
13
+ - 可按项目开启每轮结束后的安静增量检查,包含未跟踪文件、取消过期任务,仅为后续对话提供新增高风险提示;手动结果独立保存。
14
+ - 复查保留上次范围与引擎,检查不完整时不判定关闭;成功复查更新当前报告。增加 Windows/Linux、Node 22/24 与固定原生引擎的 CI 检查。
15
+
16
+ ## 0.3.8 (2026-10-12)
17
+
18
+ - 修复增量扫描遗漏中文/转义文件名、私钥、`.env` 敏感项和禁用依赖审计配置的问题,报告真实证据行号。
19
+ - 结合有限上下文识别已有豁免和跨行配置,仅报告涉及新增行的问题;校验差异块行数,格式错误或不完整时明确报错。
20
+ - 全量、单文件与增量扫描统一排除策略和工作区相对路径,兼容旧记录中 `./` 前缀的基线指纹。
21
+ - 固定 Git 输出格式与当前工作区范围,禁用外部 diff/textconv,拒绝被误当成 Git 选项的基线参数和错误类型的 `staged` 参数。
22
+ - 补充隔离 Git 回归场景;范围、兼容注意事项及后续计划见[本轮迭代记录](docs/ITERATION-2026-10-12.md)。
23
+
7
24
  ## 0.3.7 (2026-10-05)
8
25
 
9
26
  - 扫描、策略、报告和 git diff 使用当前会话工作区;支持取消并跳过目录联接。锁定合并历史与基线写入,损坏记录和无效策略明确报错并保留原文件。
package/README.en.md CHANGED
@@ -8,11 +8,14 @@ Run local static security checks on code or Git changes and verify fixes.
8
8
 
9
9
  [![npm](https://img.shields.io/npm/v/dsh-code-security)](https://www.npmjs.com/package/dsh-code-security) [![downloads](https://raw.githubusercontent.com/STARDUSTLC666/dsh-suite/npm-downloads/assets/dsh-code-security-downloads.svg)](https://www.npmjs.com/package/dsh-code-security)
10
10
 
11
+ Feedback and contributions are welcome: report [issues](https://github.com/STARDUSTLC666/dsh-code-security/issues) or submit [pull requests](https://github.com/STARDUSTLC666/dsh-code-security/pulls).
12
+
11
13
  ## What it does
12
14
 
13
15
  - Scan files, directories or Git diffs with locations and code evidence.
14
- - Recheck fixes and manage baselines or scan policy.
15
- - Export Markdown or SARIF reports and inspect dependency manifests.
16
+ - Inspect new findings, coverage and redacted evidence in DSH Settings, then recheck fixes.
17
+ - Optionally use Betterleaks for secrets, Semgrep CE for data flow and OSV for dependency advisories.
18
+ - Export Markdown/SARIF, maintain baselines, and opt into quiet per-project change checks.
16
19
 
17
20
  ## Install
18
21
 
@@ -26,11 +29,11 @@ For the web version, replace `desktop` with `web`. Restart DSH after installatio
26
29
 
27
30
  ## Start using it
28
31
 
29
- Ask: “Review the current Git diff for security findings, show evidence and recheck the fixes.”
32
+ Ask: “Review the current Git diff for security findings, show evidence and recheck the fixes.” Then open **Settings → Code security** to view the project and results.
30
33
 
31
34
  ## Requirements and configuration
32
35
 
33
- Deterministic rules run locally. See the usage guide for rule coverage, baselines and policy configuration.
36
+ Built-in rules run locally by default. Optional programs are installed by the user; basic checks work without them. Online dependency audits require an explicit mode choice, and automatic checks are off by default. Complete detected credentials are redacted before return, persistence and export.
34
37
 
35
38
  Detailed configuration, tool arguments and troubleshooting are in the [usage guide](docs/USAGE.en.md). For standalone development, follow the Node requirement in [package.json](package.json).
36
39
 
package/README.md CHANGED
@@ -8,11 +8,14 @@
8
8
 
9
9
  [![npm](https://img.shields.io/npm/v/dsh-code-security)](https://www.npmjs.com/package/dsh-code-security) [![downloads](https://raw.githubusercontent.com/STARDUSTLC666/dsh-suite/npm-downloads/assets/dsh-code-security-downloads.svg)](https://www.npmjs.com/package/dsh-code-security)
10
10
 
11
+ 欢迎使用,遇到问题或有改进建议,请提交 [issues](https://github.com/STARDUSTLC666/dsh-code-security/issues) 和 [PR](https://github.com/STARDUSTLC666/dsh-code-security/pulls)。
12
+
11
13
  ## 功能
12
14
 
13
15
  - 扫描文件、目录或 Git diff,报告规则、位置与代码证据。
14
- - 复扫修复结果,维护问题基线和检查策略。
15
- - 导出 Markdown / SARIF 报告,检查依赖清单。
16
+ - 在 DSH 设置中查看新增问题、检查覆盖、脱敏证据,并复查修复结果。
17
+ - 可选 Betterleaks 密钥检测、Semgrep CE 数据流规则、OSV 依赖漏洞审计。
18
+ - 导出 Markdown / SARIF,维护基线;可按项目开启安静的自动改动检查。
16
19
 
17
20
  ## 安装
18
21
 
@@ -26,11 +29,11 @@ dsh plugin --profile desktop add dsh-code-security
26
29
 
27
30
  ## 开始使用
28
31
 
29
- 安装后可说:“审查当前 Git 改动的安全问题,给出位置和证据,再复查修复结果。”
32
+ 安装后可说:“审查当前 Git 改动的安全问题,给出位置和证据,再复查修复结果。”之后可在「设置 → 代码安全」查看项目与检查结果。
30
33
 
31
34
  ## 依赖与配置
32
35
 
33
- 默认在本机执行确定性规则检查。规则覆盖与基线、策略配置见使用说明。
36
+ 默认仅运行本地内置规则。增强程序由用户自行安装,未安装也可使用基础检查。联网依赖审计需明确选择;自动检查默认关闭。完整检测到的密钥会在返回、保存和导出前脱敏。
34
37
 
35
38
  详细配置、工具参数与排错见[使用说明](docs/USAGE.md)。从源码独立开发时,Node 要求以 [package.json](package.json) 为准。
36
39
 
@@ -0,0 +1,23 @@
1
+ # Third-party components
2
+
3
+ The plugin and the original rules in rules/semgrep.yml are MIT licensed.
4
+
5
+ Runtime dependencies are installed as separate npm packages and retain their own notices:
6
+
7
+ | Component | Purpose | License |
8
+ | :-- | :-- | :-- |
9
+ | @babel/parser, @babel/traverse, @babel/types 7.29.x | Parse JavaScript/TypeScript and verify sanitizer bindings | MIT |
10
+ | smol-toml 1.9.1 | Parse Python/Rust TOML manifests | BSD-3-Clause |
11
+ | proper-lockfile 4.1.2 | Serialize state and workspace-index updates | MIT |
12
+
13
+ Optional scanner executables are not bundled, copied, downloaded or installed by the plugin. The operator installs them separately:
14
+
15
+ | Program tested | Upstream license |
16
+ | :-- | :-- |
17
+ | [Betterleaks 1.9.0](https://github.com/betterleaks/betterleaks/tree/v1.9.0) | MIT |
18
+ | [OSV-Scanner 2.7.0](https://github.com/google/osv-scanner/tree/v2.7.0) | Apache-2.0 |
19
+ | [Semgrep CE 1.181.0](https://github.com/semgrep/semgrep) | LGPL-2.1 for the CE engine; other components may have separate notices |
20
+
21
+ Semgrep-maintained registry rules have separate terms. This plugin does not bundle or automatically load those rules. Its four original taint rules are supplied under the plugin's MIT license. See [Semgrep licensing](https://docs.semgrep.dev/licensing).
22
+
23
+ The CI-only installer downloads checksum-pinned public scanner releases and installs Semgrep in an ephemeral virtual environment. These test programs are not included in the npm artifact. fflate (MIT) is a development dependency used only to create a synthetic offline advisory fixture.
@@ -0,0 +1,53 @@
1
+ # dsh-code-security 第一轮迭代
2
+
3
+ 日期:2026-10-12。基于 `91d4d8c770cfaa796d07b7b770a627c3bce8f49e`,本轮版本为 `0.3.8`。此页记录源码迭代和本地验证;发布 CI、npm 完整性及隔离安装结果在发布后独立核验。
4
+
5
+ 这轮先解决增量审查的漏报、范围和证据一致性。原有 37 项测试全部通过,但新增回归测试在旧实现上复现了中文路径、文件级规则等问题被误判为通过。
6
+
7
+ ## 已实现
8
+
9
+ | 场景 | 原行为 | 当前行为 |
10
+ | :-- | :-- | :-- |
11
+ | 中文/转义文件名 | Git 引号与八进制路径解析失败,可能返回零发现并通过 | 解码 UTF-8 与 Git 的 C 转义,保留正确文件和行号 |
12
+ | 新增私钥、`.env` 敏感项、`audit=false` | 增量扫描跳过文件级规则 | 全量和增量共用匹配,定位实际证据;保留跨行配置检测 |
13
+ | 未改动的豁免注释、危险代码 | 缺少上下文,已有 next-line 豁免失效 | 读取少量上下文,只报告证据涉及新增行的问题 |
14
+ | 仓库子目录作为会话工作区 | 带入兄弟目录改动,路径基准偏离工作区 | Git 范围和证据路径均相对当前会话工作区 |
15
+ | 策略排除 | 增量扫描未应用排除,直接选择文件也绕过排除 | 三种扫描入口统一排除;报告实际扫描和跳过数量 |
16
+ | 基线一致性 | 全量的 `./src/a.js` 与增量的 `src/a.js` 指纹不同 | 规范相对路径,并从保存的证据重算旧基线指纹 |
17
+ | Git 个性化配置/参数 | 颜色、前缀影响解析;`--quiet` 可误当基线 | 固定输出格式,禁用外部 diff/textconv,校验参数类型 |
18
+ | 差异块缺失行 | 接受不完整差异并给出部分结果 | 校验差异块行数,异常时保留原扫描状态并报错 |
19
+
20
+ 主要代码:[diff.ts](../src/diff.ts)、[scanner.ts](../src/scanner.ts)、[tools.ts](../src/tools.ts)、[state.ts](../src/state.ts)。已同步仓库跟踪的 `lib/` 构建产物。
21
+
22
+ ## 验证
23
+
24
+ - `pnpm test`:53/53 通过,0 失败,0 跳过;包含 TypeScript 构建。
25
+ - 新增 16 项回归测试,其中 9 项使用本地隔离 Git 仓库。覆盖中文路径、暂存区与工作树不一致、Git 输出偏好、子工作区、基线兼容、外部转换器与排除计数等。
26
+ - 工具调用链覆盖 `secure_scan` / `secure_diff` / `secure_baseline` / `secure_fix_verify` / `secure_report` / `secure_export`,验证写入状态、结果渲染文本和 SARIF 路径。
27
+ - 环境:Windows,Node `24.16.0`,pnpm `11.7.0`,Git `2.54.0.windows.1`。
28
+ - 本页所列测试未在真实 DSH Web/桌面界面执行点击、输入或视觉验收;本地运行环境为 Windows/Node 24,Linux CI 与 npm 发布验收另行记录。
29
+
30
+ 回归入口:[diff.test.mjs](../test/diff.test.mjs)、[git-diff.test.mjs](../test/git-diff.test.mjs)。测试仓库位于 E 盘项目内的 `test/.tmp/`,测试结束后仅清理经过路径校验的自建子目录。
31
+
32
+ ## 兼容注意
33
+
34
+ 未新增依赖;继续沿用项目已有的 MIT 许可与 `proper-lockfile@4.1.2`(MIT),Node 声明保持 `^22.19.0 || >=24.0.0`。原工具名和参数结构保留,增量输出增加明确的扫描/跳过计数。
35
+
36
+ 旧记录的 `./` 前缀兼容已验证。`SEC-303`、`SEC-305`、`SEC-404` 的证据由“整个文件开头”改成“实际匹配片段”;旧基线若使用前一种片段,可能被视为新增。应复扫并人工确认后重新接受,不能自动把新证据全部豁免。
37
+
38
+ 增量检查只涵盖 Git 显示的文本差异,仍不包含未跟踪文件。上下文为前后各 3 行;超出上下文的跨行结构、通用数据流和复杂语言语义未由本轮解决。差异块校验可以发现块内截断,但不代表已解决所有收集上限下的完整性判断。
39
+
40
+ ## 下一轮优先项
41
+
42
+ 以下来自当前代码静态检查,尚未在本轮实现或验收。
43
+
44
+ 1. **扫描完整性门禁。** `scanPath` 遇到不存在目标、读取失败或文件数上限时,应返回明确的完整性状态;`passed` 不能仅因发现为空而为真。还应检查宿主 stdout 收集上限带来的整体截断。
45
+ 2. **复扫范围校验。** `secure_fix_verify` 应核对上次与本次目标/覆盖范围,避免把没有重新扫描的问题误报为关闭;部分复扫应输出“未验证”。
46
+ 3. **规则准确性与隐私。** 补充安全/危险样例集,优先处理普通字符串、块注释、已使用安全 Loader 等误报;为密钥证据增加默认脱敏,并设计不依赖明文片段的稳定指纹。
47
+ 4. **依赖清单解析。** 为标准 `pyproject.toml`、Cargo 依赖区段和损坏清单补测试,再评估采用 MIT/兼容许可的 TOML 解析器;当前 SBOM-lite 不能当作漏洞数据库扫描。
48
+
49
+ 后续功能迭代仍需补单插件真实 DSH 界面验收和更广的跨平台 CI;本版发布验证独立进行。
50
+
51
+ ## Git 行为依据
52
+
53
+ 输出格式、工作区相对范围和外部转换器开关按 [Git diff 官方文档](https://git-scm.com/docs/git-diff) 实现;文件名转义依据 [core.quotePath 官方说明](https://git-scm.com/docs/git-config#Documentation/git-config.txt-corequotePath)。
package/docs/USAGE.en.md CHANGED
@@ -1,31 +1,94 @@
1
- # dsh-code-security usage guide
1
+ # Code security guide
2
2
 
3
3
  [Overview](../README.en.md) · [Changelog](../CHANGELOG.md) · [Validation](VALIDATION.md)
4
4
 
5
- ## Current improvements
5
+ ## Start with a change
6
6
 
7
- Relative stateDir defaults to .code-security in the session workspace. Concurrent scans retain both histories. Damaged records must be inspected after backup rather than silently recreated. Scans skip symlinks and junctions; static rules do not replace a full security audit.
7
+ Ask in a project chat: “Check the current changes for security issues, show evidence, and recheck the repairs.” Or open **Settings → Code security**, select the project and choose **Scan code** or **Check changes**.
8
+
9
+ Projects come from host sessions or completed security tools; the browser cannot submit arbitrary filesystem roots. If a project is missing, run a check in its chat and refresh the list. Jobs continue when the panel is closed. Cancellation waits for the scanner and preserves the previous report.
10
+
11
+ Findings show severity, confidence, rule, location, source engines and redacted evidence. Filter new/high-risk findings, copy locations, download SARIF/Markdown, or open a file while its corresponding project chat is active.
12
+
13
+ ## Optional engines
14
+
15
+ | Engine | Coverage | Default |
16
+ | :-- | :-- | :-- |
17
+ | Built-in rules | 40+ injection, deserialization, crypto, secrets, configuration and path/network patterns | Enabled; local |
18
+ | Betterleaks 1.9.0 | Platform-specific and generic secret patterns | Optional; live credential validation disabled |
19
+ | Semgrep CE 1.181.0 | Original JS/TS command, HTML and SQL taint rules; Python command taint | Optional; local; metrics/version checks disabled |
20
+ | OSV-Scanner 2.7.0 | Resolved dependency versions and known advisories | Chosen separately in dependency checks |
21
+
22
+ Install optional programs yourself, then set their executable paths or add them to PATH. Save settings and click **Check tools**. Engine settings apply to the current DSH profile; automatic checks are enabled per project. The plugin never installs scanners automatically.
23
+
24
+ A selected engine that is missing, fails, times out or returns incomplete output cannot produce a pass. No eligible files is explicitly shown as not run. Findings at the same location with the same CWE are merged while retaining source engines.
25
+
26
+ Built-in HTML findings are low-confidence sink patterns. Only a verified lexical dompurify import and a default sanitize call suppress them. Shadowed parameters, changed methods, custom configuration or parse failure are not silently treated as safe. Semgrep CE provides single-file data-flow evidence, not cross-file analysis or proof of exploitability.
27
+
28
+ ## Dependency modes
29
+
30
+ - **Inventory (offline)** parses npm, PEP 621, Poetry, Python dependency groups, Cargo, Go and other declarations. Parse errors and dynamic declarations are explicit. Version-risk flags are not a CVE audit.
31
+ - **Local database** uses OSV's fully offline mode. Missing databases and malformed lockfiles are incomplete checks.
32
+ - **Online** explicitly permits querying OSV with package names, resolved versions and dependency metadata. Source code is not uploaded. No package manager, install script or project build is run.
33
+
34
+ Use package-lock.json, pnpm-lock.yaml, uv.lock or another supported resolved lockfile. A declared version range cannot stand in for an installed version. Local-source packages are not counted as checked against public advisories. Advisory IDs and upstream fixed versions are returned; compatibility still needs testing.
35
+
36
+ The current OSV 2.7.0 implementation uses `cache/osv-scalibr/npm/all.zip` (PyPI for Python). Enter **cache** in the panel. Download the ecosystem archive from OSV's official export bucket, or use OSV's own database download command beforehand. The 2.7.0 implementation/Windows test differs from its older documentation that still names osv-scanner. Custom cache paths are forwarded using both old and new environment-variable names; 2.7.0 is the version tested here.
37
+
38
+ Health shows cache file modification times, not advisory freshness guarantees. See the [offline guide](https://google.github.io/osv-scanner/usage/offline-mode/) and [current cache implementation](https://github.com/google/osv-scalibr/blob/5be78a00e3c2/enricher/vulnmatch/osvlocal/matcher.go).
39
+
40
+ ## Quiet automatic checks
41
+
42
+ Disabled by default. Enable **Check changes after each turn** in the project's enhanced settings:
43
+
44
+ 1. After a main-session turn, debounce for about 1.8 seconds; cancel when a new turn starts.
45
+ 2. Run only local built-in rules over Git changes and untracked files.
46
+ 3. Supply new, unaccepted high-risk patterns as untrusted evidence for a later conversation reminder. No popup, new model turn, automatic edit or baseline acceptance.
47
+ 4. Save automatic results separately, preserving manual enhanced scans and rechecks. Non-Git projects and failures are shown as incomplete.
48
+
49
+ This is not scheduled monitoring and does not run after DSH exits.
8
50
 
9
51
  ## Tools
10
52
 
11
- | Tool | Purpose | Write |
53
+ | Tool | Main arguments and purpose | Writes |
12
54
  | :-- | :-- | :-- |
13
- | `secure_scan` | Scan files with CWE/severity/line/snippet evidence | state |
14
- | `secure_diff` | Review only added lines of git diff | state |
15
- | `secure_fix_verify` | Compare with baseline: closed / remaining / fresh | state |
16
- | `secure_report` | Aggregate by rule/file with gate verdict | no |
17
- | `secure_export` | Export SARIF 2.1.0 / Markdown | file write approval |
18
- | `secure_baseline` | Accept current findings as baseline; gate on new issues only | approval |
19
- | `secure_deps` | SBOM-lite: parse dependency manifests and version-risk flags | no |
20
- | `secure_policy_show` | Show .code-security.json | no |
21
- | `secure_policy_set` | Replace policy JSON | approval |
22
-
23
- 40+ deterministic rules: injection, deserialization, weak crypto (including shell TLS bypass flags), secrets, dangerous config, sensitive logging, path traversal, SSRF.
24
-
25
- ```bash
26
- dsh plugin --profile web add dsh-code-security
55
+ | secure_scan | Optional target and engines array | State |
56
+ | secure_diff | base (HEAD), staged, target, includeUntracked (false), engines | State |
57
+ | secure_fix_verify | Reuse the previous scope/engines; narrowing is rejected | State only after a complete recheck |
58
+ | secure_report | Latest findings, baseline split and coverage | None |
59
+ | secure_export | format: sarif/markdown; optional path | Approval when writing a path |
60
+ | secure_baseline | Required reason; accept all current findings | Approval |
61
+ | secure_deps | target; audit: none/offline/online | Temporary snapshot |
62
+ | secure_policy_show | Show project policy | None |
63
+ | secure_policy_set | Complete policy JSON | Approval |
64
+ | secure_health | Engine versions, settings and cache metadata | None |
65
+
66
+ Example secure_scan arguments:
67
+
68
+ ```json
69
+ {"target":"src","engines":["builtin","betterleaks","semgrep"]}
70
+ ```
71
+
72
+ Rechecks retain the previous target, Git base, staging mode and engine selection. An incomplete check closes no previous findings. A rule ceasing to match is separate from application tests passing.
73
+
74
+ Legacy diff records lack the full scope metadata. Rerun secure_diff once after upgrading before rechecking such a record.
75
+
76
+ ## Scope and state
77
+
78
+ Defaults: 5,000 files, 2 MiB per file, at most 64 MiB per enhanced snapshot. Build directories, node_modules, .git and .code-security are excluded; symlinks/junctions are not followed. Unreadable/oversized files and parse/output limits are explicitly incomplete. Current scans cover a workspace or Git diff, not every historical commit.
79
+
80
+ Relative stateDir defaults to the session project's .code-security. Locks and atomic replacement protect concurrent history/baseline writes. Corrupt records are preserved and reported. Legacy findings, history and baselines are migrated to redacted evidence while retaining fingerprints computed from original evidence. Complete detected credentials are not returned to the model or reports.
81
+
82
+ Policy file: `.code-security.json`.
83
+
84
+ ```json
85
+ {"version":1,"exclude":["vendor/**","generated/**"],"ignore":[{"ruleId":"SEC-206","file":"test/**","reason":"Not security-sensitive"}],"failOn":"high"}
27
86
  ```
28
87
 
29
- ## License
88
+ Git runs with external diff/textconv disabled. Paths are relative to the session workspace. Staged scans read the index and only added-line findings are reported. Invalid arguments, Git refs and incomplete hunks preserve previous state.
89
+
90
+ ## Development and licenses
91
+
92
+ Run `pnpm test`. Without native executable paths, the three native integration tests are explicitly skipped. Separate CI jobs install pinned, checksum-verified engines on Windows/Linux and use a synthetic offline OSV database.
30
93
 
31
- MIT (see [LICENSE](../LICENSE))
94
+ The plugin and its original rules are [MIT](../LICENSE). Optional programs are not redistributed. See [third-party notices](../THIRD_PARTY_NOTICES.md). Static checks provide reviewable evidence and do not replace application tests or a full security audit.
package/docs/USAGE.md CHANGED
@@ -1,77 +1,97 @@
1
- # dsh-code-security 使用说明
1
+ # 代码安全检查使用说明
2
2
 
3
- [返回简介](../README.md) · [更新记录](../CHANGELOG.md) · [验证记录](VALIDATION.md)
3
+ [返回简介](../README.md) · [更新记录](../CHANGELOG.md) · [验证范围](VALIDATION.md)
4
4
 
5
- ## 本次改进
5
+ ## 从一次修改开始
6
6
 
7
- 相对 stateDir 默认在会话工作区的 .code-security。并发扫描保留双方历史;记录损坏时先备份排查,不会默默重建。扫描不跟随符号链接或目录联接;静态规则不能代替完整安全审计。
7
+ 在项目聊天中说:“检查当前改动的安全问题,给出证据,修好后再复查。”也可以打开 DSH「设置 → 代码安全」,选择项目后点击「检查代码」或「检查改动」。
8
8
 
9
- ## 工具
9
+ 工作台登记当前会话的项目目录,不接受浏览器传入任意磁盘路径。项目尚未出现时,先在该项目聊天中运行一次安全检查,再刷新项目。检查可以在关闭面板后继续;取消检查会等待进程退出,不覆盖之前的结果。
10
10
 
11
- | 工具 | 作用 | 写操作 |
11
+ 结果区区分新增和已接受的问题,并显示严重度、置信度、规则、位置、来源及脱敏证据。可筛选高风险问题、复制位置、导出 SARIF/Markdown。打开文件需要先打开对应的项目聊天;否则复制位置即可。
12
+
13
+ ## 选择检查深度
14
+
15
+ | 引擎 | 检查内容 | 默认行为 |
12
16
  | :-- | :-- | :-- |
13
- | `secure_scan` | 扫描文件/目录,输出 CWE/严重度/行号/代码片段证据 | 写状态 |
14
- | `secure_diff` | 只审查 git diff 新增行 | 写状态 |
15
- | `secure_fix_verify` | 修复后复扫:关闭 / 仍存在 / 新引入 | 写状态 |
16
- | `secure_report` | 按规则/文件聚合 + 门禁结论 | 否 |
17
- | `secure_export` | 导出 SARIF 2.1.0 / Markdown 报告 | 写文件审批 |
18
- | `secure_baseline` | 接受当前已知问题为基线,之后只按新增判定 | 审批门 |
19
- | `secure_deps` | SBOM-lite:解析依赖清单与版本约束风险 | 否 |
20
- | `secure_policy_show` | 查看 .code-security.json 策略 | 否 |
21
- | `secure_policy_set` | 写入策略(排除/忽略/阈值) | 审批门 |
22
-
23
- ## 规则覆盖(40+)
24
-
25
- - 注入:eval / exec / shell=True / SQL 拼接 / innerHTML
26
- - 反序列化:pickle / yaml.load / ObjectInputStream / Marshal / unserialize
27
- - 加密:弱哈希、ECB、硬编码 IV/密钥、JWT none、TLS 校验关闭、Shell TLS 绕过(curl -k / wget --no-check-certificate / git sslVerify=false)
28
- - 凭据:硬编码密码、令牌、私钥、高熵密钥
29
- - 配置:chmod 777、Docker privileged / latest、npm audit=false、CORS *
30
- - 泄露:敏感日志、错误堆栈外发、路径穿越、SSRF
31
-
32
- 发现只包含客观证据与 CWE 编号,不附修复建议——修复方案由 agent 基于证据生成。
33
-
34
- ## 安装
35
-
36
- ```bash
37
- dsh plugin --profile web add dsh-code-security
38
- ```
17
+ | 内置规则 | 40+ 条注入、反序列化、弱加密、凭据、危险配置、路径与网络模式 | 启用,本地运行 |
18
+ | Betterleaks 1.9.0 | 平台格式与通用密钥规则 | 可选,关闭在线密钥有效性验证 |
19
+ | Semgrep CE 1.181.0 | 本插件自有的 JS/TS 命令、HTML、SQL 与 Python 命令输入传播规则 | 可选,本地运行,关闭指标与版本查询 |
20
+ | OSV-Scanner 2.7.0 | 锁文件中的实际依赖版本与已知漏洞 | 在依赖检查中单独选择 |
39
21
 
40
- ## 卸载
22
+ 增强程序由用户安装,不随插件自动下载。在「增强工具与自动检查」填写可执行文件路径,或将程序加入 PATH,保存后点击「检查工具」。设置对当前 DSH profile 生效;自动检查开关按项目保存。只选择需要的引擎,未安装时可继续使用内置规则。
41
23
 
42
- ```bash
43
- dsh plugin --profile web remove dsh-code-security
44
- ```
24
+ 扫描状态包含覆盖情况:执行完成、部分完成、未安装、超时、失败、没有可检查文件。选中但失败的引擎不会被当成通过;没有符合该引擎的文件会明确标注未运行。同一位置、同类问题合并展示并保留来源。
25
+
26
+ 内置 HTML 规则是低置信度的写入点提示。只有可确认词法绑定来自 dompurify 的默认 sanitize 调用才可抑制提示;同名参数、重写方法、额外配置或语法解析失败不会被简单当作安全。Semgrep CE 提供单文件数据流证据,不承诺跨文件分析或漏洞可利用性。
27
+
28
+ ## 依赖清单与漏洞审计
29
+
30
+ - **仅列出依赖**:默认离线;解析 npm、PEP 621、Poetry、Python 依赖组、Cargo、Go 等声明,显示固定/范围/浮动版本。清单不是漏洞审计,解析失败和动态依赖会说明。
31
+ - **本地数据库**:通过 OSV 的完全离线模式扫描锁文件。数据库未找到或锁文件无效时显示未完成。
32
+ - **联网审计**:明确选择后,向 OSV 查询包名、实际版本等依赖信息;不上传源代码,不执行包管理器、项目安装脚本或构建脚本。显示漏洞编号、受影响版本和上游列出的修复版本。
33
+
34
+ 推荐提供 package-lock.json、pnpm-lock.yaml、uv.lock 等锁文件;只有版本范围时不能冒充实际安装版本。本地路径包不能当作已查询公共漏洞库。修复版本来自公告,升级前仍需检查兼容性。
35
+
36
+ OSV 2.7.0 当前实现使用 `缓存目录/osv-scalibr/npm/all.zip`(Python 使用 PyPI)。在工作台填写的是**缓存目录**。可从 OSV 官方漏洞导出桶下载相应生态的 all.zip,或者自行使用 OSV 的数据库下载命令。注意:2.7.0 的源码/Windows 实测路径与其仍写 osv-scanner 的旧离线文档存在差异。自定义目录会同时传给新旧环境变量;本轮实测的是 2.7.0。
37
+
38
+ 「检查工具」显示找到的缓存和文件修改时间。文件时间不是每条漏洞的更新时间;损坏、不完整、过旧数据库的风险不会由一个日期消除。参考 [OSV 离线说明](https://google.github.io/osv-scanner/usage/offline-mode/)和[当前缓存实现](https://github.com/google/osv-scalibr/blob/5be78a00e3c2/enricher/vulnmatch/osvlocal/matcher.go)。
39
+
40
+ ## 安静的自动检查
45
41
 
46
- 卸载后重启 Web 服务。如需彻底清理,可再手动删除自己 profile `cordis.patch.yml` 中覆盖的插件行。
42
+ 默认关闭。在项目的增强设置中启用「每轮完成后检查改动」并保存:
47
43
 
48
- ## 使用
44
+ 1. 主会话一轮结束后,合并等待约 1.8 秒再检查;新一轮开始会取消旧检查。
45
+ 2. 只用内置规则检查本地 Git 改动和未跟踪的新文件,不运行模型或外部扫描程序。
46
+ 3. 只把未被手动基线接受的新增高风险模式,作为证据提供给后续对话提示。没有弹窗,也不会自动修改代码或接受基线。
47
+ 4. 结果放在独立的 automatic 目录,保留手动增强扫描和复查记录。非 Git 项目或检查失败会在设置中提示手动核对。
49
48
 
50
- ```text
51
- secure_scan { target: src }
52
- secure_diff { base: HEAD }
53
- secure_fix_verify { target: src }
54
- secure_baseline { reason: 历史遗留 }
55
- secure_deps { target: . }
49
+ 它不是定时监控,也不会在 DSH 关闭后运行。提醒发生在后续对话中,不是主动发起一轮新对话。
50
+
51
+ ## 工具参数
52
+
53
+ | 工具 | 主要参数与用途 | 写入 |
54
+ | :-- | :-- | :-- |
55
+ | secure_scan | target 可选;engines 可选数组 | 本地状态 |
56
+ | secure_diff | base 默认 HEAD;staged;target;includeUntracked 默认 false;engines | 本地状态 |
57
+ | secure_fix_verify | 沿用上次范围与引擎,不能用缩小范围伪称修复 | 完整复查后更新状态 |
58
+ | secure_report | 最近结果、新增/已接受、覆盖情况 | 无 |
59
+ | secure_export | format 为 sarif/markdown;path 可选 | 提供 path 时需审批 |
60
+ | secure_baseline | reason 必填,接受当前全部问题 | 需审批 |
61
+ | secure_deps | target;audit 为 none/offline/online | 临时快照 |
62
+ | secure_policy_show | 查看项目策略 | 无 |
63
+ | secure_policy_set | policy 为完整 JSON | 需审批 |
64
+ | secure_health | 工具版本、检查设置和缓存信息 | 无 |
65
+
66
+ ```json
67
+ {"target":"src","engines":["builtin","betterleaks","semgrep"]}
56
68
  ```
57
69
 
58
- 策略示例(`.code-security.json`):
70
+ 以上是 secure_scan 的参数。secure_fix_verify 默认使用上次的 target、Git base、暂存模式和引擎;检查失败时不判定旧问题已修复。规则不再命中与实际功能测试通过是两项不同证据。
71
+
72
+ 升级前的增量记录未保存完整范围,需先重新执行 secure_diff,之后才能按原范围复查。
73
+
74
+ ## 范围、策略和记录
75
+
76
+ 默认跳过 node_modules、.git、构建目录和 .code-security,也不跟随符号链接/目录联接。默认文件上限 5000、每文件 2 MiB;增强快照总量最多 64 MiB。超过限制、无法读取或格式无效时明确显示不完整。当前检查工作区或指定 Git 差异,不遍历所有 Git 历史提交。
77
+
78
+ 相对 stateDir 默认位于当前会话项目的 .code-security。并发写入使用锁与原子替换;记录损坏时保留原文件并报错。旧记录、历史和基线读取时会迁移为脱敏证据,并保留基于原始证据计算的指纹。不会向模型或报告返回完整检测到的密钥。
79
+
80
+ 策略文件 `.code-security.json`:
59
81
 
60
82
  ```json
61
83
  {
62
84
  "version": 1,
63
85
  "exclude": ["vendor/**", "generated/**"],
64
- "ignore": [{ "ruleId": "SEC-206", "file": "test/**", "reason": "非安全敏感" }],
86
+ "ignore": [{"ruleId":"SEC-206","file":"test/**","reason":"非安全敏感"}],
65
87
  "failOn": "high"
66
88
  }
67
89
  ```
68
90
 
69
- ## 工程
91
+ Git 输出禁用外部 diff/textconv,路径相对当前会话工作区。暂存检查读取 Git index;新增行才计入差异发现。错写参数、无效 Git ref 或不完整 diff 不会覆盖已有记录。
70
92
 
71
- ```bash
72
- pnpm test # 构建并执行测试
73
- ```
93
+ ## 开发与许可
74
94
 
75
- ## License
95
+ `pnpm test` 构建并测试;未指定原生程序路径时,三项原生引擎集成测试明确跳过。CI 另有 Windows/Linux 原生引擎任务,使用固定版本、校验下载摘要和合成离线漏洞库。
76
96
 
77
- MIT(见 [LICENSE](../LICENSE))
97
+ 插件和自有规则采用 [MIT](../LICENSE)。第三方程序未打包进插件,其许可和规则限制见 [第三方说明](../THIRD_PARTY_NOTICES.md)。静态检查提供可复核线索,不能替代项目测试或完整安全审计。
@@ -2,6 +2,20 @@
2
2
 
3
3
  本页整理原 README 的历史验证说明,保留当时的版本、日期与范围。自动测试、启动检查、浏览器操作和真实服务验收分别记录,不能相互替代。更详细的版本验收文件仍保留在仓库中。
4
4
 
5
+ ## 0.4.0(2026-10-15)
6
+
7
+ 本地环境:Windows、Node 24.16.0、官方 npm DSH 0.2.1-alpha.3。全部使用 E 盘隔离目录与合成数据,没有复制用户密钥或修改前台桌面窗口。
8
+
9
+ - **78 项 Windows 测试通过,无跳过**,包含真实 Betterleaks 1.9.0、Semgrep CE 1.181.0、OSV-Scanner 2.7.0。普通未安装原生引擎的测试运行会明确跳过这三项,不能等同于原生验收。
10
+ - **真实原生引擎**:合成令牌/占位符和脱敏、局部变量传播、参数化 SQL、DOMPurify 导入与同名参数、传递依赖的离线公告/修复版本/缺库/损坏锁文件。OSV 另覆盖 Git 忽略了临时目录的真实仓库,避免跳过快照后产生空结果。
11
+ - **官方 DSH 网页端实际操作**:项目登记与空态、检查工具、增强扫描、脱敏证据、SARIF 下载、离线漏洞编号/修复版本、缺失程序、取消、修复复查、中文/英文切换、390px 窄屏。浏览器控制插件不可用,使用独立的无头系统 Chrome 与 Playwright;控制台/页面错误为 0,无横向溢出。
12
+ - **自动检查回归**:默认关闭、未跟踪文件、手动结果隔离、已接受基线过滤、新一轮取消、过期事件竞态及被动上下文注册;不启动模型或擅自接受基线。
13
+ - **发布包安装**:用官方 CLI 将最终本地 tarball 安装到新的隔离 profile,核对版本、十个工具、PEP 621、脱敏、SARIF、客户端及三份规则文件。公开 registry 与 CI 产物的一致性由独立发布流程核对。
14
+
15
+ **验收边界**:本轮未操作原生桌面窗口;网页版结果不记作桌面端验收。文件预览仅验证了未打开对应聊天时的明确提示,没有完成活动聊天中的文件跳转。已知漏洞在线查询曾在真实 OSV 程序中检查样例依赖;离线匹配使用合成数据库,不代表完整公共漏洞库覆盖或准确率测评。未调用用户的真实模型完成一轮任务。
16
+
17
+ CI 新增 Windows/Linux × Node 22/24,以及两种系统上的固定原生引擎任务;每个运行结果可在 [Actions](https://github.com/STARDUSTLC666/dsh-code-security/actions) 独立查看。扫描通过、相关功能测试通过、包已发布和桌面操作通过是不同结论。
18
+
5
19
  ## 原中文记录
6
20
 
7
21
  2026-09-13 修复:保留宿主 `subprocess.spawn` 的服务对象,避免方法独立传递后因 `this` 丢失而报错。该路径已在隔离宿主进程服务中实测通过。`secure_diff` 已通过真实宿主进程服务读取隔离 Git 仓库差异,正确识别变更文件和新增行。
package/lib/audit.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ import type { ResolvedSecureConfig, SourceEngine } from './config.js';
2
+ import type { ProcessRunner } from './runner.js';
3
+ import type { SecurePolicy } from './policy.js';
4
+ import { type EngineCoverage } from './engines.js';
5
+ export type ScanScope = {
6
+ kind: 'scan';
7
+ target?: string;
8
+ } | {
9
+ kind: 'diff';
10
+ target?: string;
11
+ base: string;
12
+ staged: boolean;
13
+ includeUntracked?: boolean;
14
+ };
15
+ export declare function sourceEngines(value: unknown, defaults: SourceEngine[]): SourceEngine[];
16
+ export declare function runSourceCheck(scope: ScanScope, cfg: ResolvedSecureConfig, cwd: string, runner: ProcessRunner, engines: SourceEngine[], policy: SecurePolicy, signal?: AbortSignal): Promise<{
17
+ findings: import("./scanner.js").Finding[];
18
+ engines: SourceEngine[];
19
+ coverage: EngineCoverage[];
20
+ complete: boolean;
21
+ scope: ScanScope;
22
+ warnings: string[];
23
+ root: string;
24
+ filesScanned: number;
25
+ filesSkipped: number;
26
+ durationMs: number;
27
+ }>;
package/lib/audit.js ADDED
@@ -0,0 +1,109 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { policyIgnores } from './policy.js';
4
+ import { parseDiff, runGitDiff, scanDiff } from './diff.js';
5
+ import { isExcludedPath, scanPath } from './scanner.js';
6
+ import { mergeFindings, scanExternal } from './engines.js';
7
+ export function sourceEngines(value, defaults) {
8
+ if (value === undefined)
9
+ return [...defaults];
10
+ if (!Array.isArray(value) || !value.length || value.length > 3 || value.some(e => !['builtin', 'betterleaks', 'semgrep'].includes(e)) || new Set(value).size !== value.length)
11
+ throw new Error('engines 必须从 builtin、betterleaks、semgrep 中选择,不能重复或为空。');
12
+ return value;
13
+ }
14
+ export async function runSourceCheck(scope, cfg, cwd, runner, engines, policy, signal) {
15
+ const documents = [];
16
+ let documentBytes = 0, truncated = false;
17
+ const collect = (doc) => { const size = Buffer.byteLength(doc.text); if (documentBytes + size > 64 * 1024 * 1024) {
18
+ truncated = true;
19
+ return;
20
+ } ; documentBytes += size; documents.push(doc); };
21
+ let result;
22
+ if (scope.kind === 'scan')
23
+ result = await scanPath({ cwd, target: scope.target, maxFiles: cfg.maxFiles, maxFileBytes: cfg.maxFileBytes, policy, signal, ...(engines.some(e => e !== 'builtin') ? { onDocument: collect } : {}) });
24
+ else {
25
+ const diff = await runGitDiff(runner, cwd, scope.base, scope.target, 30000, scope.staged);
26
+ const scanned = scanDiff(diff, cwd, policy);
27
+ result = { root: scope.base, ...scanned, durationMs: 0, complete: true, warnings: [] };
28
+ if (engines.some(e => e !== 'builtin')) {
29
+ for (const file of parseDiff(diff, cwd)) {
30
+ signal?.throwIfAborted();
31
+ if (isExcludedPath(file.file, policy) || !file.added.length)
32
+ continue;
33
+ if (documents.length >= cfg.maxFiles) {
34
+ truncated = true;
35
+ break;
36
+ }
37
+ const absolute = path.resolve(cwd, file.file), rel = path.relative(cwd, absolute);
38
+ if (rel.startsWith('..') || path.isAbsolute(rel))
39
+ throw new Error('增量文件超出当前工作区');
40
+ let text;
41
+ if (scope.staged) {
42
+ const shown = await runner.run(['git', 'show', ':./' + file.file], { cwd, timeoutMs: 30000, signal });
43
+ if (shown.exitCode !== 0)
44
+ throw new Error('无法读取暂存区文件,请刷新后重试');
45
+ text = shown.stdout;
46
+ }
47
+ else {
48
+ const stat = await fs.lstat(absolute);
49
+ if (!stat.isFile() || stat.isSymbolicLink() || await fs.realpath(absolute) !== absolute)
50
+ throw new Error('增量文件不是工作区内的普通文件');
51
+ if (stat.size > cfg.maxFileBytes) {
52
+ truncated = true;
53
+ continue;
54
+ }
55
+ text = await fs.readFile(absolute, { encoding: 'utf8', signal });
56
+ }
57
+ if (Buffer.byteLength(text) > cfg.maxFileBytes) {
58
+ truncated = true;
59
+ continue;
60
+ }
61
+ const lines = text.split(/\r?\n/);
62
+ if (file.added.some(line => lines[line.line - 1] !== line.text))
63
+ throw new Error('扫描期间文件已变化,请重新扫描;已有记录未覆盖');
64
+ collect({ file: file.file, text, selectedLines: new Set(file.added.map(line => line.line)) });
65
+ }
66
+ if (await runGitDiff(runner, cwd, scope.base, scope.target, 30000, scope.staged) !== diff)
67
+ throw new Error('扫描期间 Git 改动已变化,请重试');
68
+ }
69
+ if (scope.includeUntracked && !scope.staged) {
70
+ const untracked = await runner.run(['git', 'ls-files', '--others', '--exclude-standard', '-z', '--', scope.target ?? '.'], { cwd, signal, timeoutMs: 30000 });
71
+ if (untracked.exitCode !== 0)
72
+ throw new Error('无法读取未跟踪文件列表');
73
+ if (untracked.stdout && !untracked.stdout.endsWith('\0'))
74
+ throw new Error('未跟踪文件列表不完整,已有记录未覆盖');
75
+ const names = untracked.stdout.split('\0').filter(Boolean);
76
+ for (const name of names) {
77
+ signal?.throwIfAborted();
78
+ if (result.filesScanned >= cfg.maxFiles) {
79
+ result.complete = false;
80
+ result.warnings.push('已达到文件数量上限');
81
+ break;
82
+ }
83
+ if (path.isAbsolute(name) || name.split(/[\\/]/).includes('..'))
84
+ throw new Error('未跟踪文件超出当前工作区');
85
+ let added = 0;
86
+ const extra = await scanPath({ cwd, target: name, maxFiles: 1, maxFileBytes: cfg.maxFileBytes, policy, signal, onDocument(doc) { added = doc.text.split(/\r?\n/).length; if (engines.some(e => e !== 'builtin'))
87
+ collect(doc); } });
88
+ result.filesChanged++;
89
+ result.filesScanned += extra.filesScanned;
90
+ result.filesSkipped += extra.filesSkipped;
91
+ result.addedLines += added;
92
+ result.findings.push(...extra.findings);
93
+ result.complete &&= extra.complete;
94
+ result.warnings.push(...extra.warnings);
95
+ }
96
+ }
97
+ }
98
+ const coverage = engines.includes('builtin') ? [{ engine: 'builtin', status: result.complete ? 'completed' : 'partial', findingsCount: result.findings.length, ...(result.complete ? {} : { reason: 'incomplete-coverage' }) }] : [];
99
+ let findings = engines.includes('builtin') ? result.findings : [];
100
+ const external = await Promise.all(engines.filter((e) => e !== 'builtin').map(e => scanExternal(e, documents, cfg, runner, signal)));
101
+ for (const checked of external) {
102
+ findings.push(...checked.findings.filter(f => !policyIgnores(policy, f.ruleId, f.file)));
103
+ coverage.push(truncated && checked.coverage.status === 'completed' ? { ...checked.coverage, status: 'partial', reason: 'snapshot-size-limit' } : checked.coverage);
104
+ }
105
+ if (engines.length > 1)
106
+ findings = mergeFindings(findings);
107
+ const { diff: _privateDiff, base: _base, ...publicResult } = result;
108
+ return { ...publicResult, findings, engines, coverage, complete: result.complete && !truncated && coverage.every(c => c.status === 'completed' || c.status === 'not-run' && c.reason === 'no-eligible-files'), scope };
109
+ }
@@ -0,0 +1,18 @@
1
+ import type { SecurityWorkbench } from './workbench.js';
2
+ export declare class AutomaticSecurity {
3
+ private readonly workbench;
4
+ private readonly idleMs;
5
+ private readonly pending;
6
+ private readonly tasks;
7
+ private readonly revisions;
8
+ private readonly aliases;
9
+ private readonly alerts;
10
+ private closed;
11
+ constructor(workbench: SecurityWorkbench, idleMs?: number);
12
+ attach(host: any): () => void;
13
+ observe(session: any, event: any): Promise<void>;
14
+ context(agent: any): string;
15
+ private cancel;
16
+ whenIdle(): Promise<void>;
17
+ dispose(): void;
18
+ }