dsh-code-security 0.3.8 → 0.4.1
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 +14 -0
- package/README.en.md +5 -4
- package/README.md +5 -4
- package/THIRD_PARTY_NOTICES.md +23 -0
- package/docs/USAGE.en.md +80 -23
- package/docs/USAGE.md +68 -56
- package/docs/VALIDATION.md +18 -0
- package/lib/audit.d.ts +27 -0
- package/lib/audit.js +109 -0
- package/lib/automatic.d.ts +18 -0
- package/lib/automatic.js +103 -0
- package/lib/client.js +63 -0
- package/lib/config.d.ts +9 -0
- package/lib/config.js +12 -1
- package/lib/deps.d.ts +14 -8
- package/lib/deps.js +170 -143
- package/lib/engines.d.ts +50 -0
- package/lib/engines.js +255 -0
- package/lib/extra.d.ts +2 -1
- package/lib/extra.js +29 -4
- package/lib/index.d.ts +8 -2
- package/lib/index.js +45 -5
- package/lib/privacy.d.ts +7 -0
- package/lib/privacy.js +54 -0
- package/lib/rules.js +1 -1
- package/lib/runner.d.ts +4 -0
- package/lib/runner.js +8 -3
- package/lib/scanner.d.ts +12 -1
- package/lib/scanner.js +23 -25
- package/lib/state.d.ts +7 -0
- package/lib/state.js +28 -7
- package/lib/syntax.d.ts +1 -0
- package/lib/syntax.js +43 -0
- package/lib/tools.d.ts +2 -0
- package/lib/tools.js +53 -20
- package/lib/workbench.d.ts +30 -0
- package/lib/workbench.js +242 -0
- package/package.json +16 -3
- package/rules/betterleaks.toml +3 -0
- package/rules/osv.toml +1 -0
- package/rules/semgrep.yml +72 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,20 @@
|
|
|
4
4
|
|
|
5
5
|
[历史英文记录](docs/CHANGELOG.en.md)
|
|
6
6
|
|
|
7
|
+
## 0.4.1 (2026-10-15)
|
|
8
|
+
|
|
9
|
+
- 修复同一行同时命中高熵密钥和其他安全规则时,其他规则的证据仍带出原始字符串的问题;所有证据统一使用同一套高熵脱敏判断,也覆盖截断的字符串。
|
|
10
|
+
- 回归检查模型返回值、历史、已接受基线、SARIF 与 Markdown,保留原始证据指纹用于跟踪。
|
|
11
|
+
|
|
12
|
+
## 0.4.0 (2026-10-15)
|
|
13
|
+
|
|
14
|
+
- 增加 DSH 中英文设置工作台:项目选择、扫描/增量/复查、覆盖情况、问题筛选、脱敏证据、复制位置与报告下载。
|
|
15
|
+
- 可选接入 Betterleaks 1.9.0、Semgrep CE 1.181.0 和 OSV-Scanner 2.7.0。扫描使用隔离快照,不执行项目安装/构建脚本;密钥在线验证关闭,OSV 联网查询需要明确选择。
|
|
16
|
+
- 增加自有 MIT 数据流规则和可靠的 DOMPurify 绑定判断,区分模式提示与数据流证据。增强差异扫描读取暂存区/工作树实际内容,仅报告涉及新增行的问题。
|
|
17
|
+
- 统一脱敏输出、历史、基线和导出,迁移旧记录时保留稳定指纹。修复 PEP 621 等 Python 依赖解析,错误/动态声明不再被当成空的安全清单。
|
|
18
|
+
- 可按项目开启每轮结束后的安静增量检查,包含未跟踪文件、取消过期任务,仅为后续对话提供新增高风险提示;手动结果独立保存。
|
|
19
|
+
- 复查保留上次范围与引擎,检查不完整时不判定关闭;成功复查更新当前报告。增加 Windows/Linux、Node 22/24 与固定原生引擎的 CI 检查。
|
|
20
|
+
|
|
7
21
|
## 0.3.8 (2026-10-12)
|
|
8
22
|
|
|
9
23
|
- 修复增量扫描遗漏中文/转义文件名、私钥、`.env` 敏感项和禁用依赖审计配置的问题,报告真实证据行号。
|
package/README.en.md
CHANGED
|
@@ -13,8 +13,9 @@ Feedback and contributions are welcome: report [issues](https://github.com/STARD
|
|
|
13
13
|
## What it does
|
|
14
14
|
|
|
15
15
|
- Scan files, directories or Git diffs with locations and code evidence.
|
|
16
|
-
-
|
|
17
|
-
-
|
|
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.
|
|
18
19
|
|
|
19
20
|
## Install
|
|
20
21
|
|
|
@@ -28,11 +29,11 @@ For the web version, replace `desktop` with `web`. Restart DSH after installatio
|
|
|
28
29
|
|
|
29
30
|
## Start using it
|
|
30
31
|
|
|
31
|
-
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.
|
|
32
33
|
|
|
33
34
|
## Requirements and configuration
|
|
34
35
|
|
|
35
|
-
|
|
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.
|
|
36
37
|
|
|
37
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).
|
|
38
39
|
|
package/README.md
CHANGED
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
## 功能
|
|
14
14
|
|
|
15
15
|
- 扫描文件、目录或 Git diff,报告规则、位置与代码证据。
|
|
16
|
-
-
|
|
17
|
-
-
|
|
16
|
+
- 在 DSH 设置中查看新增问题、检查覆盖、脱敏证据,并复查修复结果。
|
|
17
|
+
- 可选 Betterleaks 密钥检测、Semgrep CE 数据流规则、OSV 依赖漏洞审计。
|
|
18
|
+
- 导出 Markdown / SARIF,维护基线;可按项目开启安静的自动改动检查。
|
|
18
19
|
|
|
19
20
|
## 安装
|
|
20
21
|
|
|
@@ -28,11 +29,11 @@ dsh plugin --profile desktop add dsh-code-security
|
|
|
28
29
|
|
|
29
30
|
## 开始使用
|
|
30
31
|
|
|
31
|
-
安装后可说:“审查当前 Git
|
|
32
|
+
安装后可说:“审查当前 Git 改动的安全问题,给出位置和证据,再复查修复结果。”之后可在「设置 → 代码安全」查看项目与检查结果。
|
|
32
33
|
|
|
33
34
|
## 依赖与配置
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
默认仅运行本地内置规则。增强程序由用户自行安装,未安装也可使用基础检查。联网依赖审计需明确选择;自动检查默认关闭。完整检测到的密钥会在返回、保存和导出前脱敏。
|
|
36
37
|
|
|
37
38
|
详细配置、工具参数与排错见[使用说明](docs/USAGE.md)。从源码独立开发时,Node 要求以 [package.json](package.json) 为准。
|
|
38
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.
|
package/docs/USAGE.en.md
CHANGED
|
@@ -1,37 +1,94 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Code security guide
|
|
2
2
|
|
|
3
3
|
[Overview](../README.en.md) · [Changelog](../CHANGELOG.md) · [Validation](VALIDATION.md)
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Start with a change
|
|
6
6
|
|
|
7
|
-
|
|
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
8
|
|
|
9
|
-
|
|
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
10
|
|
|
11
|
-
|
|
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
12
|
|
|
13
|
-
|
|
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.
|
|
14
50
|
|
|
15
51
|
## Tools
|
|
16
52
|
|
|
17
|
-
| Tool |
|
|
53
|
+
| Tool | Main arguments and purpose | Writes |
|
|
18
54
|
| :-- | :-- | :-- |
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
|
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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"]}
|
|
33
70
|
```
|
|
34
71
|
|
|
35
|
-
|
|
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"}
|
|
86
|
+
```
|
|
87
|
+
|
|
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.
|
|
36
93
|
|
|
37
|
-
|
|
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,85 +1,97 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 代码安全检查使用说明
|
|
2
2
|
|
|
3
|
-
[返回简介](../README.md) · [更新记录](../CHANGELOG.md) · [
|
|
3
|
+
[返回简介](../README.md) · [更新记录](../CHANGELOG.md) · [验证范围](VALIDATION.md)
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 从一次修改开始
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
在项目聊天中说:“检查当前改动的安全问题,给出证据,修好后再复查。”也可以打开 DSH「设置 → 代码安全」,选择项目后点击「检查代码」或「检查改动」。
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
工作台登记当前会话的项目目录,不接受浏览器传入任意磁盘路径。项目尚未出现时,先在该项目聊天中运行一次安全检查,再刷新项目。检查可以在关闭面板后继续;取消检查会等待进程退出,不覆盖之前的结果。
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
结果区区分新增和已接受的问题,并显示严重度、置信度、规则、位置、来源及脱敏证据。可筛选高风险问题、复制位置、导出 SARIF/Markdown。打开文件需要先打开对应的项目聊天;否则复制位置即可。
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
## 选择检查深度
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
| 引擎 | 检查内容 | 默认行为 |
|
|
16
|
+
| :-- | :-- | :-- |
|
|
17
|
+
| 内置规则 | 40+ 条注入、反序列化、弱加密、凭据、危险配置、路径与网络模式 | 启用,本地运行 |
|
|
18
|
+
| Betterleaks 1.9.0 | 平台格式与通用密钥规则 | 可选,关闭在线密钥有效性验证 |
|
|
19
|
+
| Semgrep CE 1.181.0 | 本插件自有的 JS/TS 命令、HTML、SQL 与 Python 命令输入传播规则 | 可选,本地运行,关闭指标与版本查询 |
|
|
20
|
+
| OSV-Scanner 2.7.0 | 锁文件中的实际依赖版本与已知漏洞 | 在依赖检查中单独选择 |
|
|
21
|
+
|
|
22
|
+
增强程序由用户安装,不随插件自动下载。在「增强工具与自动检查」填写可执行文件路径,或将程序加入 PATH,保存后点击「检查工具」。设置对当前 DSH profile 生效;自动检查开关按项目保存。只选择需要的引擎,未安装时可继续使用内置规则。
|
|
23
|
+
|
|
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)。
|
|
16
39
|
|
|
17
|
-
##
|
|
40
|
+
## 安静的自动检查
|
|
18
41
|
|
|
19
|
-
|
|
42
|
+
默认关闭。在项目的增强设置中启用「每轮完成后检查改动」并保存:
|
|
43
|
+
|
|
44
|
+
1. 主会话一轮结束后,合并等待约 1.8 秒再检查;新一轮开始会取消旧检查。
|
|
45
|
+
2. 只用内置规则检查本地 Git 改动和未跟踪的新文件,不运行模型或外部扫描程序。
|
|
46
|
+
3. 只把未被手动基线接受的新增高风险模式,作为证据提供给后续对话提示。没有弹窗,也不会自动修改代码或接受基线。
|
|
47
|
+
4. 结果放在独立的 automatic 目录,保留手动增强扫描和复查记录。非 Git 项目或检查失败会在设置中提示手动核对。
|
|
48
|
+
|
|
49
|
+
它不是定时监控,也不会在 DSH 关闭后运行。提醒发生在后续对话中,不是主动发起一轮新对话。
|
|
50
|
+
|
|
51
|
+
## 工具参数
|
|
52
|
+
|
|
53
|
+
| 工具 | 主要参数与用途 | 写入 |
|
|
20
54
|
| :-- | :-- | :-- |
|
|
21
|
-
|
|
|
22
|
-
|
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
- 反序列化:pickle / yaml.load / ObjectInputStream / Marshal / unserialize
|
|
35
|
-
- 加密:弱哈希、ECB、硬编码 IV/密钥、JWT none、TLS 校验关闭、Shell TLS 绕过(curl -k / wget --no-check-certificate / git sslVerify=false)
|
|
36
|
-
- 凭据:硬编码密码、令牌、私钥、高熵密钥
|
|
37
|
-
- 配置:chmod 777、Docker privileged / latest、npm audit=false、CORS *
|
|
38
|
-
- 泄露:敏感日志、错误堆栈外发、路径穿越、SSRF
|
|
39
|
-
|
|
40
|
-
发现只包含客观证据与 CWE 编号,不附修复建议——修复方案由 agent 基于证据生成。
|
|
41
|
-
|
|
42
|
-
## 安装
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
dsh plugin --profile web add dsh-code-security
|
|
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"]}
|
|
46
68
|
```
|
|
47
69
|
|
|
48
|
-
|
|
70
|
+
以上是 secure_scan 的参数。secure_fix_verify 默认使用上次的 target、Git base、暂存模式和引擎;检查失败时不判定旧问题已修复。规则不再命中与实际功能测试通过是两项不同证据。
|
|
49
71
|
|
|
50
|
-
|
|
51
|
-
dsh plugin --profile web remove dsh-code-security
|
|
52
|
-
```
|
|
72
|
+
升级前的增量记录未保存完整范围,需先重新执行 secure_diff,之后才能按原范围复查。
|
|
53
73
|
|
|
54
|
-
|
|
74
|
+
## 范围、策略和记录
|
|
55
75
|
|
|
56
|
-
|
|
76
|
+
默认跳过 node_modules、.git、构建目录和 .code-security,也不跟随符号链接/目录联接。默认文件上限 5000、每文件 2 MiB;增强快照总量最多 64 MiB。超过限制、无法读取或格式无效时明确显示不完整。当前检查工作区或指定 Git 差异,不遍历所有 Git 历史提交。
|
|
57
77
|
|
|
58
|
-
|
|
59
|
-
secure_scan { target: src }
|
|
60
|
-
secure_diff { base: HEAD }
|
|
61
|
-
secure_fix_verify { target: src }
|
|
62
|
-
secure_baseline { reason: 历史遗留 }
|
|
63
|
-
secure_deps { target: . }
|
|
64
|
-
```
|
|
78
|
+
相对 stateDir 默认位于当前会话项目的 .code-security。并发写入使用锁与原子替换;记录损坏时保留原文件并报错。旧记录、历史和基线读取时会迁移为脱敏证据,并保留基于原始证据计算的指纹。不会向模型或报告返回完整检测到的密钥。
|
|
65
79
|
|
|
66
|
-
|
|
80
|
+
策略文件 `.code-security.json`:
|
|
67
81
|
|
|
68
82
|
```json
|
|
69
83
|
{
|
|
70
84
|
"version": 1,
|
|
71
85
|
"exclude": ["vendor/**", "generated/**"],
|
|
72
|
-
"ignore": [{
|
|
86
|
+
"ignore": [{"ruleId":"SEC-206","file":"test/**","reason":"非安全敏感"}],
|
|
73
87
|
"failOn": "high"
|
|
74
88
|
}
|
|
75
89
|
```
|
|
76
90
|
|
|
77
|
-
|
|
91
|
+
Git 输出禁用外部 diff/textconv,路径相对当前会话工作区。暂存检查读取 Git index;新增行才计入差异发现。错写参数、无效 Git ref 或不完整 diff 不会覆盖已有记录。
|
|
78
92
|
|
|
79
|
-
|
|
80
|
-
pnpm test # 构建并执行测试
|
|
81
|
-
```
|
|
93
|
+
## 开发与许可
|
|
82
94
|
|
|
83
|
-
|
|
95
|
+
`pnpm test` 构建并测试;未指定原生程序路径时,三项原生引擎集成测试明确跳过。CI 另有 Windows/Linux 原生引擎任务,使用固定版本、校验下载摘要和合成离线漏洞库。
|
|
84
96
|
|
|
85
|
-
|
|
97
|
+
插件和自有规则采用 [MIT](../LICENSE)。第三方程序未打包进插件,其许可和规则限制见 [第三方说明](../THIRD_PARTY_NOTICES.md)。静态检查提供可复核线索,不能替代项目测试或完整安全审计。
|
package/docs/VALIDATION.md
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
本页整理原 README 的历史验证说明,保留当时的版本、日期与范围。自动测试、启动检查、浏览器操作和真实服务验收分别记录,不能相互替代。更详细的版本验收文件仍保留在仓库中。
|
|
4
4
|
|
|
5
|
+
## 0.4.1(2026-10-15)
|
|
6
|
+
|
|
7
|
+
79 项 Windows 测试全部通过,包含下列三个真实原生引擎。新增同一行同时命中注入与高熵密钥的回归,确认其他规则的证据、返回值、历史、基线和导出也不再带出原值;截断的字符串使用相同判断。其余功能与客户端沿用 0.4.0 的验收范围。
|
|
8
|
+
|
|
9
|
+
## 0.4.0(2026-10-15)
|
|
10
|
+
|
|
11
|
+
本地环境:Windows、Node 24.16.0、官方 npm DSH 0.2.1-alpha.3。全部使用 E 盘隔离目录与合成数据,没有复制用户密钥或修改前台桌面窗口。
|
|
12
|
+
|
|
13
|
+
- **78 项 Windows 测试通过,无跳过**,包含真实 Betterleaks 1.9.0、Semgrep CE 1.181.0、OSV-Scanner 2.7.0。普通未安装原生引擎的测试运行会明确跳过这三项,不能等同于原生验收。
|
|
14
|
+
- **真实原生引擎**:合成令牌/占位符和脱敏、局部变量传播、参数化 SQL、DOMPurify 导入与同名参数、传递依赖的离线公告/修复版本/缺库/损坏锁文件。OSV 另覆盖 Git 忽略了临时目录的真实仓库,避免跳过快照后产生空结果。
|
|
15
|
+
- **官方 DSH 网页端实际操作**:项目登记与空态、检查工具、增强扫描、脱敏证据、SARIF 下载、离线漏洞编号/修复版本、缺失程序、取消、修复复查、中文/英文切换、390px 窄屏。浏览器控制插件不可用,使用独立的无头系统 Chrome 与 Playwright;控制台/页面错误为 0,无横向溢出。
|
|
16
|
+
- **自动检查回归**:默认关闭、未跟踪文件、手动结果隔离、已接受基线过滤、新一轮取消、过期事件竞态及被动上下文注册;不启动模型或擅自接受基线。
|
|
17
|
+
- **发布包安装**:用官方 CLI 将最终本地 tarball 安装到新的隔离 profile,核对版本、十个工具、PEP 621、脱敏、SARIF、客户端及三份规则文件。公开 registry 与 CI 产物的一致性由独立发布流程核对。
|
|
18
|
+
|
|
19
|
+
**验收边界**:本轮未操作原生桌面窗口;网页版结果不记作桌面端验收。文件预览仅验证了未打开对应聊天时的明确提示,没有完成活动聊天中的文件跳转。已知漏洞在线查询曾在真实 OSV 程序中检查样例依赖;离线匹配使用合成数据库,不代表完整公共漏洞库覆盖或准确率测评。未调用用户的真实模型完成一轮任务。
|
|
20
|
+
|
|
21
|
+
CI 新增 Windows/Linux × Node 22/24,以及两种系统上的固定原生引擎任务;每个运行结果可在 [Actions](https://github.com/STARDUSTLC666/dsh-code-security/actions) 独立查看。扫描通过、相关功能测试通过、包已发布和桌面操作通过是不同结论。
|
|
22
|
+
|
|
5
23
|
## 原中文记录
|
|
6
24
|
|
|
7
25
|
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
|
+
}
|