@comate/zulu 1.4.0-beta.4 → 1.4.0-beta.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/comate-engine/assets/skills/auto-commit/SKILL.md +2 -0
- package/comate-engine/assets/skills/auto-commit-sandbox-comate/SKILL.md +2 -2
- package/comate-engine/assets/skills/code-review/SKILL.md +6 -5
- package/comate-engine/assets/skills/code-review/agents/custom-reviewer.md +2 -2
- package/comate-engine/assets/skills/code-review/agents/meta-reviewer.md +2 -2
- package/comate-engine/assets/skills/code-review/agents/style-reviewer.md +72 -10
- package/comate-engine/assets/skills/code-review/references/dispatch-template.md +12 -12
- package/comate-engine/assets/skills/code-review/references/rules/Java/JAVA_STYLE_RULES.md +11 -5
- package/comate-engine/assets/skills/code-security/SKILL.md +110 -41
- package/comate-engine/assets/skills/code-security/references/credential_hosting.md +190 -28
- package/comate-engine/assets/skills/code-security/references/vul_analysis-go_sql_injection.md +149 -0
- package/comate-engine/assets/skills/code-security/references/vul_analysis-java_sql_injection.md +185 -0
- package/comate-engine/assets/skills/code-security/references/vul_analysis-php_sql_injection.md +147 -0
- package/comate-engine/assets/skills/code-security/references/vul_analysis-python_sql_injection.md +143 -0
- package/comate-engine/assets/skills/code-security/references/vul_repair-go_sql_injection.md +2 -2
- package/comate-engine/assets/skills/code-security/references/vul_repair-sca.md +225 -0
- package/comate-engine/assets/skills/code-security/scripts/credential_hosting.py +12 -10
- package/comate-engine/assets/skills/code-security/scripts/credential_open_page.py +125 -0
- package/comate-engine/assets/skills/code-security/scripts/credential_poll.py +12 -9
- package/comate-engine/assets/skills/code-security/scripts/credential_url.py +81 -0
- package/comate-engine/assets/skills/code-security/scripts/ducc/get_claude_session_id.sh +33 -0
- package/comate-engine/assets/skills/code-security/scripts/ducc/open_browser.py +191 -0
- package/comate-engine/assets/skills/code-security/scripts/parse_scan_result.py +99 -16
- package/comate-engine/assets/skills/code-security/scripts/repair_vulnerability.py +66 -13
- package/comate-engine/assets/skills/code-security/scripts/scan_vulnerability.py +44 -12
- package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/SKILL.md +8 -8
- package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/references/long_running_task.md +0 -15
- package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/references/testing_strategy.md +1 -1
- package/comate-engine/assets/skills/create-image/SKILL.md +197 -206
- package/comate-engine/assets/skills/create-image/scripts/generate-image.ps1 +213 -0
- package/comate-engine/assets/skills/create-image/scripts/generate-image.sh +322 -0
- package/comate-engine/assets/skills/create-subagent/SKILL.md +23 -5
- package/comate-engine/fallbackServer.js +1 -1
- package/comate-engine/node_modules/@comate/plugin-shared-internals/dist/index.js +1 -1
- package/comate-engine/server.js +89 -66
- package/dist/bundle/index.js +3 -3
- package/package.json +1 -1
- package/scripts/postinstall.js +4 -3
- package/comate-engine/assets/skills/code-review/evals/SKILL.md +0 -334
- package/comate-engine/assets/skills/code-review/evals/agents/gt-generator.md +0 -76
- package/comate-engine/assets/skills/code-review/evals/agents/miner.md +0 -87
- package/comate-engine/assets/skills/code-review/evals/agents/score-judge.md +0 -168
- package/comate-engine/assets/skills/code-review/evals/references/cli-query-template.md +0 -114
- package/comate-engine/assets/skills/code-review/evals/references/gt-schema.md +0 -77
- package/comate-engine/assets/skills/code-review/references/custom-rules/RULE_TEMPLATE.md +0 -141
- /package/comate-engine/assets/commands/{code-review-comate.md → code-review.md} +0 -0
- /package/comate-engine/assets/commands/{debug-comate.md → debug.md} +0 -0
- /package/comate-engine/assets/commands/{unit-test-comate.md → unit-test.md} +0 -0
- /package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/references/backend_dev.md +0 -0
- /package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/references/env_setup.md +0 -0
- /package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/references/frontend_dev.md +0 -0
- /package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/references/git_operations.md +0 -0
- /package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/scripts/check_config.py +0 -0
|
@@ -1,30 +1,77 @@
|
|
|
1
1
|
# 硬编码漏洞修复与凭证托管
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**【强制执行总则】** 以下步骤必须严格按顺序执行,不得跳过任何步骤,不得调换顺序。每个步骤完成后才能进入下一步。
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 流程概览
|
|
6
6
|
|
|
7
|
-
输出可点击的链接让用户打开凭证托管助手网页,网页 URL 格式及输出示例如下:
|
|
8
7
|
```
|
|
9
|
-
|
|
8
|
+
步骤一:打开硬编码风险治理网页
|
|
9
|
+
↓
|
|
10
|
+
步骤二:等待凭证配置完成(立即执行轮询脚本)
|
|
11
|
+
↓
|
|
12
|
+
步骤三:修复硬编码代码(本地修复,按 vul_repair_sensitive.md 规则)
|
|
13
|
+
↓
|
|
14
|
+
步骤四:托管凭证(用户选择托管或跳过)
|
|
15
|
+
├→ 托管凭证 → 执行托管脚本 → 步骤五
|
|
16
|
+
└→ 跳过托管 → 步骤五
|
|
17
|
+
↓
|
|
18
|
+
步骤五:提交代码并合入
|
|
19
|
+
├→ 选择1:代码提交且合入 → 步骤六(清理历史 commit)→ [申请结单?是] → 清理临时文件 → 结束
|
|
20
|
+
│ └→ [申请结单?否] → 复测扫描 → [申请结单?是] → 清理临时文件 → 结束
|
|
21
|
+
│ └→ [申请结单?否] → 清理临时文件 → 结束
|
|
22
|
+
├→ 选择2:未提交合入,复测扫描 → 复测扫描 → [申请结单?是] → 清理临时文件 → 结束
|
|
23
|
+
│ └→ [申请结单?否] → 清理临时文件 → 结束
|
|
24
|
+
├→ 选择3:未提交合入,单元测试 → 单元测试 → 复测扫描 → [申请结单?是] → 清理临时文件 → 结束
|
|
25
|
+
│ └→ [申请结单?否] → 清理临时文件 → 结束
|
|
26
|
+
└→ 选择4:未提交合入,流程结束 → 清理临时文件 → 结束
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 步骤一:打开硬编码风险治理网页
|
|
32
|
+
|
|
33
|
+
**【强制执行】** 必须根据当前环境执行对应脚本,不得跳过此步骤。
|
|
34
|
+
|
|
35
|
+
### Comate 环境
|
|
36
|
+
|
|
37
|
+
脚本会自动构建凭证配置网页 URL,并在 IDE 内嵌浏览器中自动打开:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
python3 scripts/credential_open_page.py \
|
|
41
|
+
--chat-id "${_CHAT_ID}" \
|
|
42
|
+
--username "${_USERNAME}" \
|
|
43
|
+
--ide-name "${_IDE}" \
|
|
44
|
+
--project-dir "<项目目录绝对路径>"
|
|
45
|
+
```
|
|
10
46
|
|
|
11
|
-
|
|
47
|
+
脚本说明:
|
|
48
|
+
- `--project-dir`:当前扫描的项目目录,用于自动从 git remote 获取 `repo` 标识;若无 git 信息则使用目录名
|
|
49
|
+
- 脚本通过 comate-kernel socket 在 IDE 内嵌浏览器中自动打开网页
|
|
50
|
+
- 若 kernel socket 不可用,自动降级为 `--open-url` 触发 `simpleBrowser.api.open`
|
|
12
51
|
|
|
13
|
-
|
|
52
|
+
### DUCC 环境
|
|
53
|
+
|
|
54
|
+
`credential_open_page.py` 负责输出可点击链接,`scripts/ducc/open_browser.py` 负责在内嵌浏览器中打开,两者接收相同参数,共用公共模块 `credential_url.py` 构建 URL:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# 在内嵌浏览器中打开
|
|
58
|
+
python3 scripts/ducc/open_browser.py \
|
|
59
|
+
--chat-id "${_CHAT_ID}" \
|
|
60
|
+
--username "${_USERNAME}" \
|
|
61
|
+
--ide-name "${_IDE}" \
|
|
62
|
+
--project-dir "<项目目录绝对路径>"
|
|
14
63
|
```
|
|
15
64
|
|
|
16
|
-
|
|
17
|
-
- `COMATE_UID` 为 `${COMATE_USERNAME}` 的 MD5 字符串的前 12 位,使用 `python3 -c "import hashlib; print(hashlib.md5('${COMATE_USERNAME}'.encode()).hexdigest()[:12])"` 计算
|
|
18
|
-
- `REPO` 是代码库标识,可以从项目目录的 git remote 获取(如 `baidu/scan/cnap-test`),如果没有 git 信息则使用项目目录名
|
|
65
|
+
---
|
|
19
66
|
|
|
20
|
-
|
|
67
|
+
**【强制执行】** 无论哪种环境,无论网页是否成功自动打开,都必须将脚本的 stdout 输出(含可点击链接)完整展示在聊天框中,不得省略。
|
|
21
68
|
|
|
22
69
|
## 步骤二:等待凭证配置完成
|
|
23
70
|
|
|
24
|
-
|
|
71
|
+
**【强制执行】** 必须等到聊天框中已成功输出可点击链接后,才能开始执行以下轮询脚本,不得提前执行、不得跳过、不得等待用户回复后再执行。此步骤是后续修复流程的前置依赖,未执行将导致整个硬编码修复流程中断。
|
|
25
72
|
|
|
26
73
|
```bash
|
|
27
|
-
python3 scripts/credential_poll.py --chat-id ${
|
|
74
|
+
python3 scripts/credential_poll.py --chat-id ${_CHAT_ID} --username ${_USERNAME} --output <临时目录>/poll_result.json
|
|
28
75
|
```
|
|
29
76
|
|
|
30
77
|
`<临时目录>` 使用与 `scan_result.json` 同一目录(即 `.tmp/<项目名>_<哈希>/`)。
|
|
@@ -35,13 +82,13 @@ python3 scripts/credential_poll.py --chat-id ${COMATE_SESSION_ID} --username ${C
|
|
|
35
82
|
- `data.credentials`:凭证名值对列表
|
|
36
83
|
- `data.deployment`:托管平台信息(platform、platformName)
|
|
37
84
|
- `data.repo`:代码库语言和框架信息
|
|
38
|
-
- `data.succMsg
|
|
39
|
-
- `data.errorMsg
|
|
85
|
+
- `data.succMsg`:配置成功提示信息
|
|
86
|
+
- `data.errorMsg`:配置失败提示信息
|
|
40
87
|
- `chatUUID`:此次会话 ID
|
|
41
88
|
|
|
42
89
|
## 步骤三:修复硬编码代码
|
|
43
90
|
|
|
44
|
-
|
|
91
|
+
**【强制执行】** 收到凭证配置数据后,必须在本地直接修复硬编码漏洞,不调用后端修复服务。必须先读取 `references/vul_repair_sensitive.md` 文档中的修复规则再执行修复。
|
|
45
92
|
|
|
46
93
|
修复输入数据来自 `poll_result.json` 文件(由步骤二保存),其中 `data.files`、`data.repo`、`data.deployment` 包含了修复所需的全部信息。
|
|
47
94
|
|
|
@@ -51,52 +98,167 @@ python3 scripts/credential_poll.py --chat-id ${COMATE_SESSION_ID} --username ${C
|
|
|
51
98
|
3. 根据每个漏洞的 `extra.secret.credentialName`、`extra.secret.start`、`extra.secret.end` 定位敏感信息并替换为环境变量读取方式
|
|
52
99
|
4. 如果 `data.deployment.platform` 为 4,还需引入 keyless-sdk(参考 `references/vul_repair_sensitive.md` 的「引入SDK」章节)
|
|
53
100
|
|
|
54
|
-
##
|
|
101
|
+
## 步骤四:托管凭证
|
|
55
102
|
|
|
56
|
-
|
|
103
|
+
**【强制执行】** 代码修复完成后,以表格方式总结展示修复变化(文件路径、代码行位置、内容变化),然后必须使用 Questions 组件向用户提供以下选项,等待用户确认后再继续,不得自动跳过:
|
|
104
|
+
|
|
105
|
+
Questions 组件使用规范:
|
|
106
|
+
- **不得展示「Other」选项**
|
|
107
|
+
- **选项为必选**,用户未做选择时 Continue 按钮不可点击
|
|
57
108
|
|
|
58
109
|
```
|
|
59
|
-
代码修复已完成,请 Review
|
|
110
|
+
代码修复已完成,请 Review 修复后的代码。确认无误后,选择后续操作:
|
|
60
111
|
1. 托管凭证 - 将凭证托管到 {platformName} 平台
|
|
61
112
|
2. 跳过托管 - 仅完成代码修复,不托管凭证
|
|
62
113
|
```
|
|
63
114
|
|
|
64
|
-
|
|
115
|
+
用户必须选择其中一项后才能继续。
|
|
116
|
+
|
|
117
|
+
**用户选择「托管凭证」时**,执行:
|
|
65
118
|
|
|
66
119
|
```bash
|
|
67
|
-
python3 scripts/credential_hosting.py --poll-result <poll_result.json路径> --username ${
|
|
120
|
+
python3 scripts/credential_hosting.py --poll-result <poll_result.json路径> --username ${_USERNAME}
|
|
68
121
|
```
|
|
69
122
|
|
|
70
123
|
脚本自动从 `poll_result.json` 中提取 `chatUUID`、`deployment.platformName`、`data.credentials`,无需手动传递这些参数。
|
|
124
|
+
托管完成后,从 `poll_result.json` 中读取 `data.succMsg`, 展示给用户:
|
|
125
|
+
```
|
|
126
|
+
{succMsg}
|
|
127
|
+
```
|
|
128
|
+
如果失败,从 `poll_result.json` 中读取 `data.errorMsg`, 展示给用户:
|
|
129
|
+
```
|
|
130
|
+
{errorMsg}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
**用户选择「跳过托管」时**,直接进入步骤五。
|
|
135
|
+
|
|
136
|
+
完成步骤四后,进入步骤五。
|
|
137
|
+
|
|
138
|
+
## 步骤五:提交代码并合入
|
|
71
139
|
|
|
72
|
-
|
|
140
|
+
**【强制执行】** 使用 Questions 组件向用户确认代码是否提交并推送合入远程仓库,不得自动跳过:
|
|
73
141
|
|
|
74
|
-
|
|
142
|
+
Questions 组件使用规范:
|
|
143
|
+
- **不得展示「Other」选项**
|
|
144
|
+
- **选项为必选**,用户未做选择时 Continue 按钮不可点击
|
|
75
145
|
|
|
76
146
|
```
|
|
77
|
-
|
|
147
|
+
请确认代码是否提交并合入,选择后续操作:
|
|
148
|
+
1. 代码提交且合入 - 继续清理历史 git commit 记录
|
|
149
|
+
2. 代码未提交合入 - 进行复测扫描
|
|
150
|
+
3. 代码未提交合入 - 进行单元测试
|
|
151
|
+
4. 代码未提交合入 - 流程结束,后续由用户自行处理
|
|
152
|
+
```
|
|
78
153
|
|
|
79
|
-
|
|
154
|
+
根据用户选择执行对应分支:
|
|
155
|
+
|
|
156
|
+
- **选择 1(代码已合入)**→ 进入步骤六(清理历史 commit),清理完成后进入「复测扫描」→「单元测试」→「清理临时文件」
|
|
157
|
+
- **选择 2(复测扫描)**→ 进入「复测扫描」→「清理临时文件」
|
|
158
|
+
- **选择 3(单元测试)**→ 进入「单元测试」→「复测扫描」→「清理临时文件」
|
|
159
|
+
- **选择 4(流程结束)**→ 进入「清理临时文件」后结束
|
|
160
|
+
|
|
161
|
+
## 步骤六:清理历史 git commit 记录
|
|
162
|
+
|
|
163
|
+
**【强制执行】** 仅当用户在步骤五选择「代码已合入」时执行此步骤。
|
|
164
|
+
|
|
165
|
+
由于历史 commit 中可能仍残留硬编码凭证,需要提示用户清理 Git 历史记录。
|
|
166
|
+
|
|
167
|
+
`REPO` 从项目目录的 git remote URL 中提取三级路径(格式为 `一级/二级/三级`,如 `baidu/scan/cnap-test`):
|
|
168
|
+
- SSH 格式(`git@icode.baidu.com:baidu/scan/cnap-test.git`):取 `:` 后的部分,去掉 `.git` 后缀
|
|
169
|
+
- HTTP 格式(`https://icode.baidu.com/baidu/scan/cnap-test`):取域名后的路径部分,去掉 `.git` 后缀
|
|
170
|
+
- 若无 git 信息则使用项目目录名;`/` 不需要编码,直接拼接
|
|
171
|
+
|
|
172
|
+
1. 在聊天框输出可点击链接:
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
代码已合入,建议立即清理 Git 历史记录,防止历史 commit 中残留的硬编码凭证泄露。
|
|
176
|
+
|
|
177
|
+
请点击以下链接进入清理平台:[历史 Commit 清理平台](https://commit-clean.baidu-int.com/task?repo=<REPO>)
|
|
178
|
+
```
|
|
80
179
|
|
|
180
|
+
2. 执行以下命令在外部浏览器中自动打开清理平台:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
python3 -c "import webbrowser; webbrowser.open('https://commit-clean.baidu-int.com/task?repo=<REPO>')"
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
3. 通过 Questions 组件让用户确认(**不得展示「Other」选项,选项为必选,未选择时 Continue 按钮不可点击**):
|
|
187
|
+
|
|
188
|
+
```
|
|
81
189
|
请确认:
|
|
82
190
|
1. 已完成清理 - 我已清理历史 commit,继续后续流程
|
|
83
191
|
2. 稍后处理 - 跳过清理,继续后续流程
|
|
84
192
|
```
|
|
85
193
|
|
|
86
|
-
|
|
194
|
+
用户选择后,使用 Questions 组件询问是否申请结单(**不得展示「Other」选项,选项为必选**):
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
是否申请结单?
|
|
198
|
+
1. 是,立即申请结单
|
|
199
|
+
2. 否,稍后手动处理
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
根据用户选择:
|
|
203
|
+
- **选择「是,立即申请结单」**→ 执行「申请结单」→ 清理临时文件 → **流程结束,不再执行复测扫描**
|
|
204
|
+
- **选择「否,稍后手动处理」**→ 进入「复测扫描」
|
|
205
|
+
|
|
206
|
+
## 单元测试
|
|
207
|
+
|
|
208
|
+
进入单元测试阶段(流程见 SKILL.md「单元测试」章节)。「单元测试」完成后进入「复测扫描」。
|
|
87
209
|
|
|
88
210
|
## 复测扫描
|
|
89
211
|
|
|
90
|
-
|
|
212
|
+
**【强制执行】** 必须自动执行一次复测扫描(流程与 SKILL.md「漏洞扫描」章节一致),不得跳过。对比修复前后的硬编码漏洞数量,向用户说明修复效果。复测结果同样必须执行数据上报。展示格式:
|
|
91
213
|
|
|
92
214
|
```
|
|
93
|
-
|
|
215
|
+
正在执行复测扫描验证修复效果...
|
|
94
216
|
|
|
95
217
|
**复测结果**:修复前共 N 个硬编码漏洞,修复后剩余 M 个,本次修复 X 个漏洞。
|
|
96
218
|
```
|
|
97
219
|
|
|
98
220
|
如果复测仍存在未修复的硬编码漏洞,展示剩余漏洞列表(格式同漏洞报告),并提示用户可以选择继续修复或忽略。
|
|
99
221
|
|
|
222
|
+
复测扫描结束后,使用 Questions 组件询问是否申请结单(**不得展示「Other」选项,选项为必选**):
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
是否申请结单?
|
|
226
|
+
1. 是,立即申请结单
|
|
227
|
+
2. 否,稍后手动处理
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
根据用户选择:
|
|
231
|
+
- **选择「是,立即申请结单」**→ 执行「申请结单」→ 清理临时文件 → **流程结束**
|
|
232
|
+
- **选择「否,稍后手动处理」**→ 进入清理临时文件
|
|
233
|
+
|
|
100
234
|
## 清理临时文件
|
|
101
235
|
|
|
102
|
-
|
|
236
|
+
所有流程结束后,清理临时文件:`python3 -c "import shutil; shutil.rmtree('<临时目录绝对路径>')"` 删除该项目的临时目录(即 `.tmp/<项目名>_<哈希>/` 整个目录)。
|
|
237
|
+
|
|
238
|
+
## 申请结单
|
|
239
|
+
|
|
240
|
+
用户选择「是,立即申请结单」时:
|
|
241
|
+
|
|
242
|
+
1. 在聊天框输出以下内容,提示用户前往网站申请结单:
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
请点击以下链接,在工单系统中申请结单:
|
|
246
|
+
|
|
247
|
+
[申请结单](https://security.baidu.com/ticket/pending/list)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
2. 执行以下命令在外部浏览器中自动打开申请结单页面:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
python3 -c "import webbrowser; webbrowser.open('https://security.baidu.com/ticket/pending/list')"
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
3. 输出提示:
|
|
257
|
+
|
|
258
|
+
```
|
|
259
|
+
已为您打开申请结单页面,请在工单列表中找到对应工单并申请结单。流程至此结束。
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
流程结束。
|
|
263
|
+
|
|
264
|
+
用户选择「否,稍后手动处理」时,跳过此步骤,继续后续流程。
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# 漏洞复核流程
|
|
2
|
+
|
|
3
|
+
你是一个资深的安全审计专家,请对 Go 代码的 SQL 注入漏洞扫描结果进行复核,判断是否为误报。
|
|
4
|
+
|
|
5
|
+
漏洞复核可分为三个步骤:
|
|
6
|
+
|
|
7
|
+
1. 数据读取:仔细阅读污点追踪信息(trace)及相关的代码文件。
|
|
8
|
+
2. 漏洞分析:根据「漏洞分析」的要求判断漏洞是否为误报。
|
|
9
|
+
3. 结果输出:按照格式要求输出复核结果。
|
|
10
|
+
|
|
11
|
+
## 数据读取
|
|
12
|
+
|
|
13
|
+
阅读扫描报告 parsed_result.json,其中包含了详情的漏洞位置、数据流信息以及漏洞类型说明。
|
|
14
|
+
|
|
15
|
+
## 漏洞分析
|
|
16
|
+
|
|
17
|
+
### 分析步骤(请严格按顺序执行,满足终止条件时立即停止)
|
|
18
|
+
**Step 1 - 追踪校验逻辑**
|
|
19
|
+
从污点来源到危险函数,逐行检查是否存在对该参数的过滤/校验代码。
|
|
20
|
+
参考「误报特征」中列举的保护类型进行识别。
|
|
21
|
+
**重要**:校验逻辑可能是间接的,以下两种模式都属于有效校验:
|
|
22
|
+
- **map 查找**:如果污点变量被用作 map 的 key 来查找值,且查找失败会中断执行,则该查找本身就是对污点变量的白名单校验——因为只有 key 恰好等于白名单中某个固定值时才能继续执行。
|
|
23
|
+
- **参数化查询存在性校验**:如果污点变量通过参数化查询(GORM 等 ORM 的 Where 占位符)去数据库查询是否存在匹配记录,且查询无结果会中断执行(return error),则该查询等价于隐式白名单校验。推理链条为:参数化查询本身不会产生 SQL 注入 → 查询要求数据库中存在与污点变量精确匹配的记录 → SQL 注入 payload(如 `' OR 1=1--`)不可能作为合法业务数据存在于数据库中 → payload 无法通过校验 → 后续即使拼接 SQL 也是安全的。**这种模式应判定为 false_positive**。
|
|
24
|
+
|
|
25
|
+
**边界条件**:如果被查询字段是用户可写入的(如 username、title、备注等任意字符串字段)且无字符集限制,攻击者可能预先注册 `admin' OR 1=1--` 这样的合法记录,此时存在性校验不再可靠,**不能据此判定为误报**。仅当被查询字段是系统受控、字符集受限(如纯字母数字 ID、枚举码、内部字典表)时,该模式才成立。
|
|
26
|
+
|
|
27
|
+
如果 Step 1 未发现任何校验逻辑,跳过 Step 2,直接进入 Step 3。
|
|
28
|
+
|
|
29
|
+
**Step 2 - 验证中断有效性**
|
|
30
|
+
如果 Step 1 发现校验逻辑,检查校验失败时是否会中断后续执行。
|
|
31
|
+
需要找到对应的 return 语句(可能在被调用函数内部)。
|
|
32
|
+
**特别注意**:中断判断要考虑间接中断。例如 `value, ok := fixedMap[userInput]; if !ok { return error }`,校验的是 `ok`(查找结果)而非 `userInput`(污点变量本身),但如果 `userInput` 不在 `fixedMap` 的 key 中则 `ok` 必为 false,从而触发中断——这等价于对 `userInput` 的白名单校验。同理,`err := db.Where("name = ?", input).First(&record).Error; if err != nil { return err }` 校验的是 `err`(查询结果),但如果 `input` 不在数据库中则 `err` 不为 nil,从而触发中断——这同样等价于对 `input` 的白名单校验。
|
|
33
|
+
|
|
34
|
+
**提前终止决策(Step 2 完成后必须执行)**
|
|
35
|
+
如果同时满足以下两个条件,执行「终止验证」后立即输出结论,**禁止继续分析**:
|
|
36
|
+
- 条件 A:Step 1 发现了覆盖全部污点参数的校验逻辑
|
|
37
|
+
- 条件 B:Step 2 确认校验失败时会中断执行
|
|
38
|
+
|
|
39
|
+
「终止验证」:选取一个 SQL 注入 payload,
|
|
40
|
+
逐步写出代入校验函数的每一步输入和输出值,确认该 payload 确实无法通过校验。
|
|
41
|
+
验证通过后,判定为 **false_positive**,输出结论。
|
|
42
|
+
|
|
43
|
+
**Step 3 - 验证防御有效性**(仅当未触发「提前终止决策」时执行,即条件 A 或条件 B 任一不成立)
|
|
44
|
+
检查通过校验的值,其格式/范围是否能排除 SQL 注入所需的特殊字符(如引号、注释符 `--` 等)。
|
|
45
|
+
|
|
46
|
+
**Step 4 - 覆盖完整性检查**(仅当未触发「提前终止决策」时执行)
|
|
47
|
+
确认保护措施是否覆盖了进入危险函数的所有污点参数,任一参数未被保护则判定为 true_positive。
|
|
48
|
+
|
|
49
|
+
### true_positive 推演示例(参考)
|
|
50
|
+
扫描结果指出某 Go 文件中如下代码存在 SQL 注入:
|
|
51
|
+
|
|
52
|
+
```go
|
|
53
|
+
func ListUsers(c *gin.Context) {
|
|
54
|
+
orderBy := c.Query("order_by") // 污点来源
|
|
55
|
+
if orderBy == "" {
|
|
56
|
+
orderBy = "id"
|
|
57
|
+
}
|
|
58
|
+
db.Raw("SELECT * FROM users ORDER BY " + orderBy).Scan(&users) // 危险点
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
- Step 1:仅有空字符串校验,未发现白名单/类型转换/正则等任何能限定取值范围的逻辑("误报特征注意 #1" 明确空校验不算)。
|
|
63
|
+
- Step 2:跳过(Step 1 未发现校验)。
|
|
64
|
+
- 提前终止决策:条件 A 不成立,不能终止。
|
|
65
|
+
- Step 3:`orderBy` 直接拼入 SQL 结构(ORDER BY 子句),可包含任意字符(如 `id; DROP TABLE users--`)。
|
|
66
|
+
- Step 4:唯一污点参数未被保护。
|
|
67
|
+
- 结论:**true_positive**,原因记录为"`order_by` 经空校验后直接拼入 ORDER BY 子句,未做白名单限制,可注入 `id; DROP TABLE users--` 等 payload"。
|
|
68
|
+
|
|
69
|
+
### 误报判断逻辑
|
|
70
|
+
1. 如果代码中存在有效的过滤、校验、清理逻辑,则为误报 (false_positive),有效的定义是指能够防御当前漏洞场景,具体可参考「误报特征」。
|
|
71
|
+
2. 只要存在能够防御漏洞的代码就算误报,比如强制类型转换、数字类型校验、字符串格式校验等,**不要求一定使用预编译或参数化查询等最佳实践**
|
|
72
|
+
3. 该路径在实际运行中无法触发(如死代码、测试代码等),则为误报 (false_positive)。
|
|
73
|
+
4. 其他的情况,则认为是真实存在的漏洞(true_positive)。
|
|
74
|
+
|
|
75
|
+
### 分析纪律
|
|
76
|
+
|
|
77
|
+
1. **禁止使用未验证假设**:对任何"可能存在绕过"的判断,必须附上具体 payload 代入每一步函数的输入/输出推演过程,否则该判断无效。
|
|
78
|
+
2. **找到误报证据即止步**:完成「终止验证」后,禁止继续寻找理论上的绕过方式。
|
|
79
|
+
3. **摇摆结论以推演为准**:分析过程中如结论出现反复,以最后一次有具体值代入推演支撑的结论为准,无推演支撑的推测性结论一律作废。
|
|
80
|
+
4. **禁止运行代码或编写测试**:但允许查阅标准库或第三方库的官方文档以确认函数行为(例如 `strconv.Atoi` 对带特殊字符的输入返回什么),不得通过实际执行代码来验证。
|
|
81
|
+
5. **证据不足时保守判定**:如果数据流不完整、关键代码缺失(如外部闭源依赖)、无法对所有污点参数进行完整推演,**判定为 true_positive**,并在 `reason` 中说明缺失的证据。误报需要正面证据,而真漏洞是默认假设。
|
|
82
|
+
|
|
83
|
+
### 误报特征
|
|
84
|
+
代码中存在以下特征可认为是安全的,可以判定为误报:
|
|
85
|
+
|
|
86
|
+
1. 输入数据在进入危险函数前,经过了强制类型转换,参数被转换为数字类型(如 `strconv.Atoi()`、`strconv.ParseInt()`、类型断言为数字类型)。
|
|
87
|
+
2. 输入数据在进入危险函数前,经过了白名单过滤,包括但不限于以下形式:
|
|
88
|
+
- **显式白名单**:通过 map 查找、slice 遍历等方式直接校验输入是否在允许列表中。
|
|
89
|
+
- **隐式白名单(map/slice 等值映射查找)**:污点变量作为 key 去查找固定 map,然后对查找结果做校验,失败则中断执行。此时污点变量的值域被限定为 map 中的固定 key,不可能包含 SQL 注入 payload。
|
|
90
|
+
```go
|
|
91
|
+
// 典型模式:input 作为 key 查找固定 map,映射失败则返回错误
|
|
92
|
+
value, ok := fixedMap[input]
|
|
93
|
+
if !ok {
|
|
94
|
+
return fmt.Errorf("invalid input")
|
|
95
|
+
}
|
|
96
|
+
// 此处 input 的值已被限定为 fixedMap 的 key,是安全的
|
|
97
|
+
db.Exec("... WHERE col = '" + input + "'")
|
|
98
|
+
```
|
|
99
|
+
- **参数化查询存在性校验**:污点变量通过参数化查询去数据库中查询是否存在匹配记录,查询无结果则中断执行。此时污点变量的值被限定为数据库表中已有的合法记录,SQL 注入 payload 不可能作为合法记录存在。
|
|
100
|
+
```go
|
|
101
|
+
// 典型模式:使用参数化查询校验 input 是否存在于数据库
|
|
102
|
+
var record Model
|
|
103
|
+
err := db.Where("name = ?", input).First(&record).Error
|
|
104
|
+
if err != nil {
|
|
105
|
+
return errors.New("记录不存在")
|
|
106
|
+
}
|
|
107
|
+
// 此处 input 的值已被限定为数据库中已有的合法记录值,是安全的
|
|
108
|
+
```
|
|
109
|
+
- **switch/case 匹配**:`switch input { case "a": ... case "b": ... default: return error }` 模式。
|
|
110
|
+
- 上述白名单校验的核心判定标准是:**通过校验后,污点变量的可能取值是否被限定为有限的、已知的安全字符串集合**。
|
|
111
|
+
3. 代码中使用了正则表达式进行输入验证,且表达式能够防御当前场景,比如仅允许数字类型的值。
|
|
112
|
+
4. 输入数据在进入危险函数前,被预编译为安全的代码,比如使用 ORM 框架。
|
|
113
|
+
|
|
114
|
+
### 证据优先级(用于减少误判)
|
|
115
|
+
- **强证据**:参数化查询绑定参数(`db.Where("name = ?", input)`、`db.Raw(sql, args...)`)且未拼接用户输入到 SQL 结构中。
|
|
116
|
+
- **中证据**:严格白名单/映射查找并在失败时中断执行。
|
|
117
|
+
- **弱证据**:仅依赖一般性清理(如仅移除空格)、过于宽松的正则、长度限制等**无法排除注入特殊字符**的措施。弱证据不能单独判定为 `false_positive`。
|
|
118
|
+
|
|
119
|
+
### 常见危险反例(命中则倾向 true_positive)
|
|
120
|
+
- 使用 `fmt.Sprintf("%s", input)` 或字符串拼接 `"..." + input` 将用户输入拼入 SQL 语句结构(列名、表名、ORDER BY 等)。
|
|
121
|
+
- 在 `Order`/`Select`/`Table` 等位置直接拼接用户输入。
|
|
122
|
+
- `Where` 使用 map 且 key 可被用户控制(即使 value 做了参数化)——这是 GORM 的常见陷阱:使用了参数化绑定不代表整条语句安全,key 仍会被拼到 SQL 结构中。
|
|
123
|
+
|
|
124
|
+
**注意**:仅校验参数是否为空(如 `if param == "" { return err }` 或 `if param == nil`),没有其他校验逻辑,不能认为是误报。
|
|
125
|
+
|
|
126
|
+
## 结果输出
|
|
127
|
+
|
|
128
|
+
将结果写入到 `analyze_report.json` 文件中,与 `parsed_result.json` 同目录。**输出必须是合法 JSON**(不允许 `//` 注释、不允许末尾逗号),可使用 `json.Valid` 或本地 `python -m json.tool` 自查。
|
|
129
|
+
|
|
130
|
+
字段说明:
|
|
131
|
+
- `vul_hash`:从 `parsed_result.json` 中拷贝的漏洞 hash,用于回写关联。
|
|
132
|
+
- `result`:取值仅限 `"true_positive"` 或 `"false_positive"`。
|
|
133
|
+
- `reason`:判定依据。误报必须给出"起到过滤作用的具体代码 + 推演链条";真漏洞必须给出"可达 payload + 拼接位置"。
|
|
134
|
+
|
|
135
|
+
文件示例(合法 JSON,可直接保存):
|
|
136
|
+
```json
|
|
137
|
+
[
|
|
138
|
+
{
|
|
139
|
+
"vul_hash": "7c3ecd91ff42281bd862b400f5b955b1",
|
|
140
|
+
"result": "false_positive",
|
|
141
|
+
"reason": "代码在第 45 行使用 strconv.Atoi 将 input 强制转换为 int,转换失败立即 return;payload \"1' OR 1=1--\" 经 strconv.Atoi 返回 err != nil,无法到达 db.Exec。清洗函数:strconv.Atoi"
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"vul_hash": "a1b2c3d4e5f6...",
|
|
145
|
+
"result": "true_positive",
|
|
146
|
+
"reason": "order_by 参数仅做空校验后直接拼入 ORDER BY 子句(services/user.go:78),可注入 payload \"id; DROP TABLE users--\",未发现任何白名单/类型转换/参数化保护。"
|
|
147
|
+
}
|
|
148
|
+
]
|
|
149
|
+
```
|
package/comate-engine/assets/skills/code-security/references/vul_analysis-java_sql_injection.md
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# 漏洞复核流程
|
|
2
|
+
|
|
3
|
+
你是一个资深的安全审计专家,请对 Java 代码的 SQL 注入漏洞扫描结果进行复核,判断是否为误报。
|
|
4
|
+
|
|
5
|
+
漏洞复核可分为三个步骤:
|
|
6
|
+
|
|
7
|
+
1. 数据读取:仔细阅读污点追踪信息(trace)及相关的代码文件。
|
|
8
|
+
2. 漏洞分析:根据「漏洞分析」的要求判断漏洞是否为误报。
|
|
9
|
+
3. 结果输出:按照格式要求输出复核结果。
|
|
10
|
+
|
|
11
|
+
## 数据读取
|
|
12
|
+
|
|
13
|
+
阅读扫描报告 parsed_result.json,其中包含了详细的漏洞位置、数据流信息以及漏洞类型说明。
|
|
14
|
+
|
|
15
|
+
## 漏洞分析
|
|
16
|
+
|
|
17
|
+
### 分析步骤(请严格按顺序执行,满足终止条件时立即停止)
|
|
18
|
+
**Step 1 - 追踪校验逻辑**
|
|
19
|
+
从污点来源到危险函数,逐行检查是否存在对该参数的过滤/校验代码。
|
|
20
|
+
参考「误报特征」中列举的保护类型进行识别。
|
|
21
|
+
**重要**:校验逻辑可能是间接的,以下几种模式都属于有效校验:
|
|
22
|
+
- **字典/Map 查找**:如果污点变量被用作 Map 的 key 来查找值,且查找失败会中断执行,则该查找本身就是对污点变量的白名单校验——因为只有 key 恰好等于白名单中某个固定值时才能继续执行。
|
|
23
|
+
- **参数化查询存在性校验**:如果污点变量通过参数化查询(预编译/JPA/MyBatis 等 ORM)去数据库查询是否存在匹配记录,且查询无结果会中断执行(throw/return),则该查询等价于隐式白名单校验。推理链条为:参数化查询本身不会产生 SQL 注入 → 查询要求数据库中存在与污点变量精确匹配的记录 → SQL 注入 payload(如 `' OR 1=1--`)不可能作为合法业务数据存在于数据库中 → payload 无法通过校验 → 后续即使拼接 SQL 也是安全的。**这种模式应判定为 false_positive**。
|
|
24
|
+
|
|
25
|
+
**边界条件**:如果被查询字段是用户可写入的(如 username、title、备注等任意字符串字段)且无字符集限制,攻击者可能预先注册 `admin' OR 1=1--` 这样的合法记录,此时存在性校验不再可靠,**不能据此判定为误报**。仅当被查询字段是系统受控、字符集受限(如纯数字 ID、枚举码、内部字典表)时,该模式才成立。
|
|
26
|
+
- **复合变量的组成成分校验**:如果 SQL 中实际使用的变量是由多个污点字段拼接而成(如 `configId = field1 + field2 + CONST + field3`),且每个污点字段在拼接之前已分别通过参数化查询存在性校验(验证失败会中断执行),则整个复合变量等同于已被校验——合法业务 ID 不包含 SQL 注入所需的特殊字符,因此后续对复合变量的 SQL 拼接也应判定为 **false_positive**。注意:常量部分(如 `Const.FINANCE = "FINANCE"`)本身是安全的,不需要额外校验。
|
|
27
|
+
|
|
28
|
+
如果 Step 1 未发现任何校验逻辑,跳过 Step 2,直接进入 Step 3。
|
|
29
|
+
|
|
30
|
+
**Step 2 - 验证中断有效性**
|
|
31
|
+
如果 Step 1 发现校验逻辑,检查校验失败时是否会中断后续执行。
|
|
32
|
+
需要找到对应的 return / throw 语句(可能在被调用函数内部)。
|
|
33
|
+
**特别注意**:中断判断要考虑间接中断。例如 `String value = map.get(userInput); if (value == null) throw new Exception();`,校验的是 `value`(映射结果)而非 `userInput`(污点变量本身),但如果 `userInput` 不在 `map` 的 key 中则 `value` 必为 null,从而触发中断——这等价于对 `userInput` 的白名单校验。同理,`Entity record = repo.findByName(input); if (record == null) throw ...;` 校验的是 `record`(查询结果),但如果 `input` 不在数据库中则 `record` 必为 null,从而触发中断——这同样等价于对 `input` 的白名单校验。
|
|
34
|
+
|
|
35
|
+
**以下几种中断形式都应视为有效的流程终止:**
|
|
36
|
+
- 直接 `throw` 异常或 `return` 终止:`if (result == null) throw new NotFoundException()`
|
|
37
|
+
- `CollectionUtils.isEmpty(list)` 判断:`if (CollectionUtils.isEmpty(results)) return null;`(等价于对空集合的 null 检查)
|
|
38
|
+
- **两级间接中断**:方法 A 因查询为空返回 `null`,调用方方法 B 收到 `null` 后也 return 或 throw——这整个链条等价于直接中断,只要能追踪到最终必然终止执行即可。
|
|
39
|
+
**提前终止决策(Step 2 完成后必须执行)**
|
|
40
|
+
如果同时满足以下两个条件,执行「终止验证」后立即输出结论,**禁止继续分析**:
|
|
41
|
+
- 条件 A:Step 1 发现了覆盖全部污点参数的校验逻辑
|
|
42
|
+
- 条件 B:Step 2 确认校验失败时会中断执行
|
|
43
|
+
|
|
44
|
+
「终止验证」:选取一个 SQL 注入 payload,
|
|
45
|
+
逐步写出代入校验函数的每一步输入和输出值,确认该 payload 确实无法通过校验。
|
|
46
|
+
验证通过后,判定为 **false_positive**,输出结论。
|
|
47
|
+
|
|
48
|
+
**Step 3 - 验证防御有效性**(仅当未触发「提前终止决策」时执行,即条件 A 或条件 B 任一不成立)
|
|
49
|
+
检查通过校验的值,其格式/范围是否能排除 SQL 注入所需的特殊字符(如引号、注释符 `--` 等)。
|
|
50
|
+
|
|
51
|
+
**Step 4 - 覆盖完整性检查**(仅当未触发「提前终止决策」时执行)
|
|
52
|
+
确认保护措施是否覆盖了进入危险函数的所有污点参数,任一参数未被保护则判定为 true_positive。
|
|
53
|
+
|
|
54
|
+
### true_positive 推演示例(参考)
|
|
55
|
+
扫描结果指出某 Java 文件中如下代码存在 SQL 注入:
|
|
56
|
+
|
|
57
|
+
```java
|
|
58
|
+
public List<User> listUsers(String orderBy) {
|
|
59
|
+
if (orderBy == null) orderBy = "id";
|
|
60
|
+
String sql = "SELECT * FROM users ORDER BY " + orderBy;
|
|
61
|
+
return jdbcTemplate.queryForList(sql, User.class);
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
- Step 1:仅有 null 校验,未发现白名单/类型转换/正则等任何能限定取值范围的逻辑("误报特征注意 #1" 明确空校验不算)。
|
|
66
|
+
- Step 2:跳过(Step 1 未发现校验)。
|
|
67
|
+
- 提前终止决策:条件 A 不成立,不能终止。
|
|
68
|
+
- Step 3:`orderBy` 直接拼入 SQL 结构(ORDER BY 子句),可包含任意字符(如 `id; DROP TABLE users--`)。
|
|
69
|
+
- Step 4:唯一污点参数未被保护。
|
|
70
|
+
- 结论:**true_positive**,原因记录为"`orderBy` 经 null 校验后直接拼入 ORDER BY 子句,未做白名单限制,可注入 `id; DROP TABLE users--` 等 payload"。
|
|
71
|
+
|
|
72
|
+
### 误报判断逻辑
|
|
73
|
+
1. 如果代码中存在有效的过滤、校验、清理逻辑,则为误报 (false_positive),有效的定义是指能够防御当前漏洞场景,具体可参考「误报特征」。
|
|
74
|
+
2. 只要存在能够防御漏洞的代码就算误报,比如强制类型转换、数字类型校验、字符串格式校验等,**不要求一定使用预编译或参数化查询等最佳实践**
|
|
75
|
+
3. 该路径在实际运行中无法触发(如死代码、测试代码等),则为误报 (false_positive)。
|
|
76
|
+
4. 其他的情况,则认为是真实存在的漏洞(true_positive)。
|
|
77
|
+
|
|
78
|
+
### 分析纪律
|
|
79
|
+
|
|
80
|
+
1. **禁止使用未验证假设**:对任何"可能存在绕过"的判断,必须附上具体 payload 代入每一步函数的输入/输出推演过程,否则该判断无效。
|
|
81
|
+
2. **找到误报证据即止步**:完成「终止验证」后,禁止继续寻找理论上的绕过方式。
|
|
82
|
+
3. **摇摆结论以推演为准**:分析过程中如结论出现反复,以最后一次有具体值代入推演支撑的结论为准,无推演支撑的推测性结论一律作废。
|
|
83
|
+
4. **禁止运行代码或编写测试**:但允许查阅 JDK / 第三方库的官方文档以确认函数行为(例如 `Integer.parseInt` 对带特殊字符输入的行为),不得通过实际执行代码来验证。
|
|
84
|
+
5. **证据不足时保守判定**:如果数据流不完整、关键代码缺失(如外部闭源依赖、自定义 ORM 内部行为不明)、无法对所有污点参数进行完整推演,**判定为 true_positive**,并在 `reason` 中说明缺失的证据。误报需要正面证据,而真漏洞是默认假设。
|
|
85
|
+
|
|
86
|
+
### 误报特征
|
|
87
|
+
代码中存在以下特征可认为是安全的,可以判定为误报:
|
|
88
|
+
|
|
89
|
+
1. 输入数据在进入危险函数前,经过了强制类型转换,参数被转换为数字类型(如 `Integer.parseInt()`、`Long.parseLong()`、`BigDecimal` 后再做范围校验)。
|
|
90
|
+
2. 输入数据在进入危险函数前,经过了白名单过滤,包括但不限于以下形式:
|
|
91
|
+
- **显式白名单**:`whitelist.contains(input)` 或 `map.containsKey(input)` 等直接校验。
|
|
92
|
+
- **隐式白名单(数组/字典等值映射查找)**:污点变量作为 key 去查找固定 Map/数组,然后对查找结果做非空校验,失败则中断执行。此时污点变量的值域被限定为 Map 中的固定 key,不可能包含 SQL 注入 payload。
|
|
93
|
+
```java
|
|
94
|
+
// 典型模式:input 作为 key 查找固定映射,映射失败则抛出异常
|
|
95
|
+
String value = fixedMap.get(input);
|
|
96
|
+
if (value == null) throw new IllegalArgumentException("invalid");
|
|
97
|
+
// 此处 input 的值已被限定为 fixedMap 的 key,是安全的
|
|
98
|
+
jdbcTemplate.execute("... WHERE col = '" + input + "'");
|
|
99
|
+
```
|
|
100
|
+
- **参数化查询存在性校验**:污点变量通过参数化查询(预编译/ORM)去数据库中查询是否存在匹配记录,查询无结果则中断执行。此时污点变量的值被限定为数据库表中已有的合法记录,SQL 注入 payload 不可能作为合法记录存在。
|
|
101
|
+
```java
|
|
102
|
+
// 典型模式:使用参数化查询校验 input 是否存在于数据库
|
|
103
|
+
Entity record = repository.findByName(input); // JPA 参数化查询
|
|
104
|
+
if (record == null) {
|
|
105
|
+
throw new NotFoundException("记录不存在");
|
|
106
|
+
}
|
|
107
|
+
// 此处 input 的值已被限定为数据库中已有的合法记录值,是安全的
|
|
108
|
+
```
|
|
109
|
+
- **多参数 DTO 复合 ID 模式**:DTO 对象的多个污点字段(如 `sceneId`、`reportVersionId`、`dataVersionId`)作为命名参数传入参数化查询做存在性验证(SQL 使用 `:sceneId`、`:reportVersionId` 等占位符),验证失败(结果为 null 或 `CollectionUtils.isEmpty()` 为 true)则中断执行,之后这些字段被拼接为复合 ID 再用于 SQL 字符串拼接。应判定为 **false_positive**:每个字段已被隐式限定为数据库中的合法业务值,合法 ID 不含 SQL 注入特殊字符;注入 payload 无法通过存在性校验,因此永远无法到达 SQL 拼接点。
|
|
110
|
+
```java
|
|
111
|
+
// 典型模式:DTO 多字段通过命名参数参数化查询做存在性验证
|
|
112
|
+
// Service 层
|
|
113
|
+
List<TableQuart> quarts = dao.queryByVersion(action); // 内部使用 :sceneId/:reportVersionId/:dataVersionId
|
|
114
|
+
if (CollectionUtils.isEmpty(quarts)) {
|
|
115
|
+
return null; // 查询无结果 → 流程中断(调用方收到 null 也会终止)
|
|
116
|
+
}
|
|
117
|
+
// 后续(同一调用链的下游方法)
|
|
118
|
+
// 将已通过验证的 DTO 字段拼接为复合 ID
|
|
119
|
+
String configId = action.getSceneId() + action.getReportVersionId()
|
|
120
|
+
+ Const.FINANCE + action.getDataVersionId(); // Const.FINANCE 是安全常量
|
|
121
|
+
String sql = "ALTER TABLE ... ADD PARTITION " + configId + " VALUES ('" + configId + "')";
|
|
122
|
+
// 虽然此处是字符串拼接,但 configId 由已验证的合法业务字段组成 → false_positive
|
|
123
|
+
```
|
|
124
|
+
- **switch/case 匹配**:`switch(input) { case "a": ... case "b": ... default: throw new Exception(); }` 模式。
|
|
125
|
+
- **枚举白名单转换(Enum Whitelist Conversion)**:污点变量经过枚举静态工厂方法(如 `fromAlias`、`fromString`、`valueOf`、`of` 等)查找,再调用 `.name()` 获取枚举常量名。由于 `.name()` 返回的是 Java 枚举常量的名称(由代码预先定义),完全不受用户输入控制,因此该模式是有效的白名单净化。若工厂方法匹配失败返回 `null`,后续 `.name()` 调用将抛出 NPE,同样阻断 SQL 注入。
|
|
126
|
+
```java
|
|
127
|
+
// 典型模式:将用户输入映射为枚举常量名,输出值只能是预定义的枚举名之一
|
|
128
|
+
String privStr = privileges.stream()
|
|
129
|
+
.map(p -> Privilege.fromAlias(p).name()) // 输出固定为 SELECT_PRIV / GRANT_PRIV 等
|
|
130
|
+
.collect(Collectors.joining(", "));
|
|
131
|
+
// 此处 privStr 只包含预定义枚举名,不含 SQL 特殊字符,是安全的
|
|
132
|
+
```
|
|
133
|
+
- 上述白名单校验的核心判定标准是:**通过校验后,污点变量的可能取值是否被限定为有限的、已知的安全字符串集合**。
|
|
134
|
+
3. 代码中使用了正则表达式进行输入验证,且表达式能够防御当前场景,比如仅允许数字类型的值。典型形式包括:
|
|
135
|
+
- 调用 `Pattern.matcher(input).matches()` 或 `input.matches(regex)` 并在不匹配时中断执行。
|
|
136
|
+
- 调用自定义断言方法(如 `assertPattern(input, pattern, msg)`),该方法内部对输入做正则校验,不匹配则抛出异常。判定要点:必须确认正则本身能排除 SQL 注入所需的特殊字符(引号、分号、注释符等),例如 `^[a-zA-Z][a-zA-Z_]*$` 只允许字母和下划线,可有效阻断注入;而宽松的 `^.+$` 不能阻断注入,不算有效防御。
|
|
137
|
+
```java
|
|
138
|
+
// 典型模式:断言方法内做正则白名单校验,不通过则抛异常
|
|
139
|
+
assertPattern(input, VALID_PATTERN, "Invalid input: " + input);
|
|
140
|
+
// VALID_PATTERN = "^[a-zA-Z][a-zA-Z_]*$" — 只允许字母和下划线,不含 SQL 特殊字符
|
|
141
|
+
// 后续对 input 的 SQL 拼接是安全的
|
|
142
|
+
```
|
|
143
|
+
4. 输入数据在进入危险函数前,被预编译为安全的代码,比如使用 ORM 框架。
|
|
144
|
+
5. 数据流经过了以下框架或者方法的处理,则认为是安全的:
|
|
145
|
+
- 自定义ORM框架:包名 `baidu.crowdtest.mybatisorm`
|
|
146
|
+
- 自定义ORM框架:包名 `com.baidu.eop.framework.mybatis`,其中包含大量注解 `@EopSqlSharding` 可认为是安全的
|
|
147
|
+
- 自定义过滤方法:包名 `com.baidu.bce.crm.lib.utils`, 类名 `CheckUtil`,方法名 `checkOrderBySqlInject`
|
|
148
|
+
|
|
149
|
+
### 证据优先级(用于减少误判)
|
|
150
|
+
- **强证据**:参数化查询绑定参数(`PreparedStatement`、`JdbcTemplate query(sql, args)`、MyBatis `#{}`)且 SQL 结构未由用户输入控制。
|
|
151
|
+
- **中证据**:严格白名单/映射查找并在失败时中断执行。
|
|
152
|
+
- **弱证据**:仅依赖一般性转义、长度限制、宽松正则。弱证据不能单独判定为 `false_positive`。
|
|
153
|
+
|
|
154
|
+
### 常见危险反例(命中则倾向 true_positive)
|
|
155
|
+
- 通过字符串拼接构造 SQL:`"..." + input`。
|
|
156
|
+
- MyBatis 使用 `${}` 直接拼接用户输入。
|
|
157
|
+
- 在 `ORDER BY`、列名、表名等 SQL 结构位置直接使用用户输入。
|
|
158
|
+
|
|
159
|
+
**注意**:
|
|
160
|
+
1. 仅校验参数是否为空(如 `if (param != null)` 或仅做非空判断),没有其他校验逻辑,不能认为是误报。
|
|
161
|
+
|
|
162
|
+
## 结果输出
|
|
163
|
+
|
|
164
|
+
将结果写入到 `analyze_report.json` 文件中,与 `parsed_result.json` 同目录。**输出必须是合法 JSON**(不允许 `//` 注释、不允许末尾逗号),可使用 `python -m json.tool` 自查。
|
|
165
|
+
|
|
166
|
+
字段说明:
|
|
167
|
+
- `vul_hash`:从 `parsed_result.json` 中拷贝的漏洞 hash,用于回写关联。
|
|
168
|
+
- `result`:取值仅限 `"true_positive"` 或 `"false_positive"`。
|
|
169
|
+
- `reason`:判定依据。误报必须给出"起到过滤作用的具体代码 + 推演链条";真漏洞必须给出"可达 payload + 拼接位置"。
|
|
170
|
+
|
|
171
|
+
文件示例(合法 JSON,可直接保存):
|
|
172
|
+
```json
|
|
173
|
+
[
|
|
174
|
+
{
|
|
175
|
+
"vul_hash": "7c3ecd91ff42281bd862b400f5b955b1",
|
|
176
|
+
"result": "false_positive",
|
|
177
|
+
"reason": "代码在第 45 行使用 Integer.parseInt 将 input 强制转换为 int,转换失败立即抛 NumberFormatException;payload \"1' OR 1=1--\" 经 Integer.parseInt 抛异常,无法到达 jdbcTemplate.execute。清洗函数:Integer.parseInt"
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
"vul_hash": "a1b2c3d4e5f6...",
|
|
181
|
+
"result": "true_positive",
|
|
182
|
+
"reason": "orderBy 参数仅做 null 校验后直接拼入 ORDER BY 子句(service/UserService.java:78),可注入 payload \"id; DROP TABLE users--\",未发现任何白名单/类型转换/参数化保护。"
|
|
183
|
+
}
|
|
184
|
+
]
|
|
185
|
+
```
|