# 红线扫描器使用文档
零依赖、启发式、只读。它把 P0 红线里最容易在生成阶段踩中的反例变成提交前可执行的一条命令;正式门禁仍以 P3C / PMD / ESLint / 人工 review 为准。
## 1. 入口
作为 DSH 插件安装时,脚本在技能资源目录下:
```bash
node "<资源目录>/scan-redlines.mjs" --profile java --root .
```
如果项目把本包装进了 devDependencies(`npm i -D dsh-agent-prompts`),也可以用 bin:
```bash
npx dsh-agent-prompts-scan --profile java --root .
```
`profile` 只有两个:`java`(`.java` / `.xml`)与 `frontend`(`.js/.mjs/.cjs/.jsx/.ts/.tsx/.vue`)。
## 2. 退出码与阈值
| 退出码 | 含义 |
|---|---|
| 0 | 未达到 `--fail-on` 阈值 |
| 1 | 达到阈值 |
| 2 | 参数 / IO / 配置 / baseline 错误 |
`--fail-on P0`(默认)/ `P1` / `P2` / `none`。`P1` 会同时拦 P0+P1,`P2` 会拦全部。
## 3. 常用选项
| 选项 | 说明 |
|---|---|
| `--root
` | 扫描根目录,默认当前目录 |
| `--changed-only` | 只扫 git 工作区 + 暂存 + 未跟踪文件 |
| `--base [` | `--changed-only` 基准,默认 `HEAD` |
| `--baseline ` | 过滤 baseline 中已记录的问题,只对新问题失败 |
| `--write-baseline ` | 把当前问题写入 baseline,退出 0 |
| `--ignore r1,r2` | 临时忽略规则(可重复) |
| `--config ` | 配置文件,默认 `/.dsh/redlines.json` |
| `--format text\|json\|sarif\|github` | 输出格式;`--json` 等价 `json` |
| `--max ` | 最多输出条数,默认 200 |
## 4. 基线工作流(老仓库)
```bash
# 1. 第一次:记录存量问题(退出 0)
npx dsh-agent-prompts-scan --profile java --root . --write-baseline .dsh/redlines-baseline.json
# 2. 之后:只对新增问题失败
npx dsh-agent-prompts-scan --profile java --root . --baseline .dsh/redlines-baseline.json
# 3. 修复一批存量后,重建基线
npx dsh-agent-prompts-scan --profile java --root . --write-baseline .dsh/redlines-baseline.json
```
baseline 用 `rule + 文件 + 规范化片段` 的 SHA-256 指纹匹配,不依赖行号;改行号不会让旧问题重新出现。baseline 文件建议提交到仓库,团队共享。
## 5. 配置文件
`/.dsh/redlines.json`:
```json
{
"ignoreRules": ["F2E_CONSOLE"],
"ignorePaths": ["**/generated/**", "**/*.d.ts", "**/legacy/**"],
"severity": {
"JAVA_SYSTEM_OUT": "P2",
"F2E_ANY": "P0"
},
"failOn": "P0"
}
```
- `ignoreRules`:规则 ID 数组。
- `ignorePaths`:简单 glob(`*` / `**` / `?`),相对 `--root`。
- `severity`:覆盖单条规则的级别(`P0` / `P1` / `P2`)。
- `failOn`:配置级阈值;命令行显式 `--fail-on` 优先。
- 也支持按 profile 覆盖:`{ "java": { ... }, "frontend": { ... } }`,或 `{ "profiles": { "java": { ... } } }`。
配置文件支持 UTF-8 BOM(Windows 编辑器常见)。
## 6. CI 接入
### SARIF(GitHub Code Scanning)
```bash
npx dsh-agent-prompts-scan --profile java --root . --format sarif > redlines.sarif
```
```yaml
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: redlines.sarif
```
### GitHub 注解
```bash
npx dsh-agent-prompts-scan --profile java --root . --format github
```
输出 `::error file=...,line=...,title=RULE::message`,GitHub Actions 会直接标在 diff 上。
### PR 只扫改动
```bash
npx dsh-agent-prompts-scan --profile java --root . --changed-only --base origin/main
```
## 7. 规则清单
### Java
| 规则 | 级别 | 说明 |
|---|---|---|
| `JAVA_THREADPOOL_FACTORY` | P0 | `Executors.newFixedThreadPool` 等工厂 |
| `JAVA_RAW_THREAD` | P0 | 裸 `new Thread()` |
| `JAVA_SIMPLEDATEFORMAT` | P0 | `SimpleDateFormat` |
| `JAVA_UTIL_DATE` | P0 | `java.util.Date` |
| `JAVA_CALENDAR` | P0 | `Calendar.getInstance()` |
| `JAVA_EMPTY_CATCH` | P0 | 空 `catch` |
| `JAVA_SQL_DOLLAR` | P0 | Java 字符串/注解里的 `${}` |
| `JAVA_SQL_NO_WHERE` | P0 | `@Update` / `@Delete` 缺 where |
| `XML_SQL_DOLLAR` | P0 | MyBatis `${}` |
| `XML_SQL_NO_WHERE` | P0 | Mapper update/delete 缺 where |
| `JAVA_COLLECTION_RETURN_NULL` | P0 | 集合方法 `return null`(启发式) |
| `JAVA_THREADLOCAL_NO_REMOVE` | P0 | 有 ThreadLocal 但无 `remove()` |
| `JAVA_BIGDECIMAL_DOUBLE` | P1 | `new BigDecimal(double)` |
| `JAVA_PRINT_STACKTRACE` | P1 | `printStackTrace()` |
| `JAVA_SYSTEM_OUT` | P1 | `System.out` / `System.err` |
### 前端
| 规则 | 级别 | 说明 |
|---|---|---|
| `F2E_EVAL` | P0 | `eval` / `new Function` |
| `F2E_DANGEROUS_HTML` | P0 | `dangerouslySetInnerHTML`(检测到消毒调用时 P2) |
| `F2E_V_HTML` | P0 | `v-html`(检测到消毒调用时 P2) |
| `F2E_TARGET_BLANK` | P0 | `target=_blank` 缺 `noopener` |
| `F2E_VAR` | P1 | `var` |
| `F2E_EQEQ` | P1 | `==` / `!=` |
| `F2E_ANY` | P1 | `any` |
| `F2E_INDEX_KEY` | P1 | 数组索引作为 key |
| `F2E_V_FOR_NO_KEY` | P1 | `v-for` 缺 key |
| `F2E_V_IF_FOR` | P1 | `v-if` 与 `v-for` 同元素 |
| `F2E_INNER_HTML` | P1 | `.innerHTML =` |
| `F2E_DEBUGGER` | P1 | `debugger` |
| `F2E_TS_IGNORE` | P2 | `@ts-ignore` |
| `F2E_CONSOLE` | P2 | `console.log/debug/info` |
## 8. 准确性边界
- 扫描前去掉注释;字符串内容遮罩后再跑语法规则,SQL/HTML 规则使用保留字符串的副本。因此注释/字符串里的 `Executors`、`SimpleDateFormat` 不会误报。
- `v-html="DOMPurify.sanitize(x)"` 会降为 P2,但仍需人工确认消毒范围。
- 仍是正则级:跨文件数据流、动态拼接、复杂表达式可能漏报;`JAVA_COLLECTION_RETURN_NULL` 是方法签名启发式。
- 不自动修复;不访问网络;不执行目标代码。
## 9. 建议的提交前门禁
```bash
npx dsh-agent-prompts-scan --profile java --changed-only --root .
npx dsh-agent-prompts-scan --profile frontend --changed-only --root .
npx eslint . --max-warnings=0
npm run typecheck
```
扫描器是提示层,ESLint/PMD/P3C 是规则层,人工 review 是语义层;三层都不要省。
]