@comate/zulu 1.2.1-beta.1 → 1.3.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/comate-engine/assets/skills/auto-commit-comate/SKILL.md +260 -0
- package/comate-engine/assets/skills/auto-commit-comate/references/data_structures.md +189 -0
- package/comate-engine/assets/skills/auto-commit-comate/references/new_version_instruction.md +209 -0
- package/comate-engine/assets/skills/auto-commit-comate/references/old_version_instruction.md +208 -0
- package/comate-engine/assets/skills/auto-commit-comate/scripts/git_diff_cli.py +196 -0
- package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/git_utils.py +20 -10
- package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/icafe/client.py +69 -40
- package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/icafe/farseer.py +8 -9
- package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/icafe/matching.py +65 -9
- package/comate-engine/assets/skills/auto-commit-comate/scripts/match_card_cli.py +37 -0
- package/comate-engine/assets/skills/cnap-comate/SKILL.md +157 -0
- package/comate-engine/assets/skills/cnap-comate/references/cases.md +198 -0
- package/comate-engine/assets/skills/cnap-comate/references/deploy-troubleshoot.md +15 -0
- package/comate-engine/assets/skills/cnap-comate/references/install.md +43 -0
- package/comate-engine/assets/skills/cnap-comate/references/kubectl.md +55 -0
- package/comate-engine/assets/skills/cnap-comate/references/login.md +125 -0
- package/comate-engine/assets/skills/cnap-comate/references/oncall.md +24 -0
- package/comate-engine/assets/skills/cnap-comate/scripts/install_cnap_cli.sh +36 -0
- package/comate-engine/assets/skills/code-security/SKILL.md +176 -0
- package/comate-engine/assets/skills/code-security/references/credential_hosting.md +102 -0
- package/comate-engine/assets/skills/code-security/references/vul_repair_sensitive.md +219 -0
- package/comate-engine/assets/skills/code-security/scripts/build_repair_info.py +0 -0
- package/comate-engine/assets/skills/code-security/scripts/credential_hosting.py +99 -0
- package/comate-engine/assets/skills/code-security/scripts/credential_poll.py +350 -0
- package/comate-engine/assets/skills/code-security/scripts/http_client.py +173 -0
- package/comate-engine/assets/skills/code-security/scripts/parse_scan_result.py +301 -0
- package/comate-engine/assets/skills/code-security/scripts/repair_vulnerability.py +261 -0
- package/comate-engine/assets/skills/code-security/scripts/report_chat.py +198 -0
- package/comate-engine/assets/skills/code-security/scripts/scan_vulnerability.py +316 -0
- package/comate-engine/assets/skills/code-security-comate/SKILL.md +219 -0
- package/comate-engine/assets/skills/code-security-comate/references/credential_hosting.md +102 -0
- package/comate-engine/assets/skills/code-security-comate/references/vul_repair-go_sql_injection.md +399 -0
- package/comate-engine/assets/skills/code-security-comate/references/vul_repair-java_sql_injection.md +591 -0
- package/comate-engine/assets/skills/code-security-comate/references/vul_repair-php_sql_injection.md +318 -0
- package/comate-engine/assets/skills/code-security-comate/references/vul_repair-python_sql_injection.md +198 -0
- package/comate-engine/assets/skills/code-security-comate/references/vul_repair_sensitive.md +219 -0
- package/comate-engine/assets/skills/code-security-comate/scripts/credential_hosting.py +87 -0
- package/comate-engine/assets/skills/code-security-comate/scripts/credential_poll.py +345 -0
- package/comate-engine/assets/skills/code-security-comate/scripts/http_client.py +173 -0
- package/comate-engine/assets/skills/code-security-comate/scripts/parse_scan_result.py +392 -0
- package/comate-engine/assets/skills/code-security-comate/scripts/repair_vulnerability.py +245 -0
- package/comate-engine/assets/skills/code-security-comate/scripts/report_chat.py +145 -0
- package/comate-engine/assets/skills/code-security-comate/scripts/scan_vulnerability.py +444 -0
- package/comate-engine/assets/skills/code-security-comate/scripts/utils.py +153 -0
- package/comate-engine/assets/skills/comate-docs-comate/SKILL.md +148 -0
- package/comate-engine/assets/skills/comate-docs-comate/references/doc-map-extended.md +78 -0
- package/comate-engine/assets/skills/comate-docs-comate/references/models-and-billing.md +51 -0
- package/comate-engine/assets/skills/comate-docs-comate/references/product-overview.md +73 -0
- package/comate-engine/assets/skills/comate-docs-comate/references/query_content.md +83 -0
- package/comate-engine/assets/skills/comate-docs-comate/references/query_repo.md +57 -0
- package/comate-engine/assets/skills/comate-docs-comate/scripts/ku_operator.py +1575 -0
- package/comate-engine/assets/skills/create-image-comate/SKILL.md +278 -0
- package/comate-engine/assets/skills/create-skill-comate/SKILL.md +308 -217
- package/comate-engine/assets/skills/create-skill-comate/agents/analyzer.md +274 -0
- package/comate-engine/assets/skills/create-skill-comate/agents/comparator.md +202 -0
- package/comate-engine/assets/skills/create-skill-comate/agents/grader.md +223 -0
- package/comate-engine/assets/skills/create-skill-comate/assets/eval_review.html +146 -0
- package/comate-engine/assets/skills/create-skill-comate/eval-viewer/generate_review.py +489 -0
- package/comate-engine/assets/skills/create-skill-comate/eval-viewer/viewer.html +1325 -0
- package/comate-engine/assets/skills/create-skill-comate/references/schemas.md +430 -0
- package/comate-engine/assets/skills/create-skill-comate/scripts/__init__.py +0 -0
- package/comate-engine/assets/skills/create-skill-comate/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- package/comate-engine/assets/skills/create-skill-comate/scripts/__pycache__/aggregate_benchmark.cpython-311.pyc +0 -0
- package/comate-engine/assets/skills/create-skill-comate/scripts/aggregate_benchmark.py +412 -0
- package/comate-engine/assets/skills/create-skill-comate/scripts/generate_report.py +334 -0
- package/comate-engine/assets/skills/create-skill-comate/scripts/package_skill.py +140 -0
- package/comate-engine/assets/skills/create-skill-comate/scripts/utils.py +53 -0
- package/comate-engine/assets/skills/find-skills-comate/SKILL.md +15 -12
- package/comate-engine/assets/skills/find-skills-comate/scripts/fetch_skills.py +32 -3
- package/comate-engine/assets/skills/get-ugate-token-comate/SKILL.md +159 -0
- package/comate-engine/assets/skills/get-ugate-token-comate/getUgateToken.py +150 -0
- package/comate-engine/assets/skills/icafe-comate/SKILL.md +240 -0
- package/comate-engine/assets/skills/icafe-comate/references/ai-workflows.md +233 -0
- package/comate-engine/assets/skills/icafe-comate/references/commands.md +1147 -0
- package/comate-engine/assets/skills/icafe-comate/references/error-handling.md +164 -0
- package/comate-engine/assets/skills/icafe-comate/references/git-auto-bindcard-workflow.md +201 -0
- package/comate-engine/assets/skills/icafe-comate/references/git-bindcard-workflow.md +327 -0
- package/comate-engine/assets/skills/icafe-comate/references/iql-syntax.md +327 -0
- package/comate-engine/assets/skills/icafe-comate/references/platform-concepts.md +317 -0
- package/comate-engine/assets/skills/icafe-comate/references/smart-create-workflow.md +171 -0
- package/comate-engine/assets/skills/icafe-comate/references/smart-find-workflow.md +127 -0
- package/comate-engine/assets/skills/icafe-comate/references/smart-update-workflow.md +118 -0
- package/comate-engine/assets/skills/icode-comate/SKILL.md +366 -0
- package/comate-engine/assets/skills/icode-comate/references/api/add_reviewers.md +44 -0
- package/comate-engine/assets/skills/icode-comate/references/api/build_fetch_command.md +89 -0
- package/comate-engine/assets/skills/icode-comate/references/api/check_repo_permission.md +89 -0
- package/comate-engine/assets/skills/icode-comate/references/api/create_branch.md +79 -0
- package/comate-engine/assets/skills/icode-comate/references/api/create_draft_comment.md +109 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_ai_cr_result.md +190 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_ai_review.md +97 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_diff_content.md +92 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_diff_file.md +88 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_machine_check.md +73 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_my_reviews.md +115 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_person_commit.md +89 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_person_repo.md +63 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_repo_branch.md +62 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_repo_config.md +91 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_repo_members.md +118 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_repo_reviews.md +91 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_review_comments.md +87 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_review_info.md +81 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_submit_settings.md +105 -0
- package/comate-engine/assets/skills/icode-comate/references/api/icode-api.md +86 -0
- package/comate-engine/assets/skills/icode-comate/references/api/publish_comments.md +72 -0
- package/comate-engine/assets/skills/icode-comate/references/api/set_review_score.md +58 -0
- package/comate-engine/assets/skills/icode-comate/references/api/start_ai_review.md +77 -0
- package/comate-engine/assets/skills/icode-comate/references/api/submit_review.md +50 -0
- package/comate-engine/assets/skills/icode-comate/references/api/trigger_ai_cr.md +63 -0
- package/comate-engine/assets/skills/icode-comate/references/feature/add-reviewer.md +92 -0
- package/comate-engine/assets/skills/icode-comate/references/feature/fix-machine-check.md +144 -0
- package/comate-engine/assets/skills/icode-comate/references/feature/merge-cr.md +100 -0
- package/comate-engine/assets/skills/icode-comate/references/feature/ssh-setup.md +106 -0
- package/comate-engine/assets/skills/icode-comate/references/feature/submit-acr.md +135 -0
- package/comate-engine/assets/skills/icode-comate/references/feature/submit-cr.md +123 -0
- package/comate-engine/assets/skills/icode-comate/references/git/clone.md +67 -0
- package/comate-engine/assets/skills/icode-comate/references/git/icode-git.md +68 -0
- package/comate-engine/assets/skills/icode-comate/references/git/push.md +64 -0
- package/comate-engine/assets/skills/icode-comate/references/git/push_cr.md +103 -0
- package/comate-engine/assets/skills/icode-comate/references/install.md +144 -0
- package/comate-engine/assets/skills/icode-comate/references/login.md +111 -0
- package/comate-engine/assets/skills/icode-comate/scripts/add-reviewer.sh +154 -0
- package/comate-engine/assets/skills/icode-comate/scripts/common.sh +145 -0
- package/comate-engine/assets/skills/icode-comate/scripts/fix-machine-check.sh +131 -0
- package/comate-engine/assets/skills/icode-comate/scripts/merge-cr.sh +105 -0
- package/comate-engine/assets/skills/icode-comate/scripts/ssh-setup.sh +159 -0
- package/comate-engine/assets/skills/icode-comate/scripts/submit-acr.sh +236 -0
- package/comate-engine/assets/skills/icode-comate/scripts/submit-cr.sh +104 -0
- package/comate-engine/assets/skills/icode-comate/scripts/test-preflight.sh +89 -0
- package/comate-engine/assets/skills/ku-operator-comate/SKILL.md +121 -0
- package/comate-engine/assets/skills/ku-operator-comate/examples.md +190 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/add_member.md +49 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/change_scope.md +38 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/copy_doc.md +50 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/create_doc.md +61 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/delete_doc.md +31 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/edit_content.md +568 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/move_doc.md +45 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/query_comment.md +79 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/query_content.md +83 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/query_flowchart.md +84 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/query_permission.md +38 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/query_recent_view.md +67 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/query_repo.md +57 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/query_user_info.md +37 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/update_member.md +41 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/upload_attachment.md +52 -0
- package/comate-engine/assets/skills/ku-operator-comate/scripts/ku_operator.py +1575 -0
- package/comate-engine/node_modules/better-sqlite3/node_modules/.bin/prebuild-install +2 -2
- package/comate-engine/node_modules/tree-sitter-bash/node_modules/.bin/node-gyp-build +2 -2
- package/comate-engine/node_modules/tree-sitter-bash/node_modules/.bin/node-gyp-build-optional +2 -2
- package/comate-engine/node_modules/tree-sitter-bash/node_modules/.bin/node-gyp-build-test +2 -2
- package/comate-engine/package.json +2 -0
- package/comate-engine/server.js +263 -79
- package/dist/bundle/index.js +8 -8
- package/package.json +1 -1
- package/comate-engine/assets/skills/figma2code-comate/codeConnect.md +0 -37
- package/comate-engine/assets/skills/figma2code-comate/designToken.md +0 -3
- package/comate-engine/assets/skills/figma2code-comate/f2cMcp.md +0 -59
- package/comate-engine/assets/skills/smart-commit/SKILL.md +0 -646
- package/comate-engine/node_modules/@comate/plugin-host/dist/index-AZIho4HV.js +0 -1
- package/comate-engine/node_modules/@comate/plugin-host/dist/user-BIpzRUfb.js +0 -44
- /package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/references/issue_type_mapping.json +0 -0
- /package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/references/query_reference.md +0 -0
- /package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/compat.py +0 -0
- /package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/create_card_cli.py +0 -0
- /package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/icafe/__init__.py +0 -0
- /package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/logger.py +0 -0
- /package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/recognize_card_cli.py +0 -0
package/comate-engine/assets/skills/code-security-comate/references/vul_repair-java_sql_injection.md
ADDED
|
@@ -0,0 +1,591 @@
|
|
|
1
|
+
JAVA SQL 注入漏洞修复知识
|
|
2
|
+
|
|
3
|
+
# Java sqli 漏洞修复最佳实践
|
|
4
|
+
采用先分析漏洞报告->分析数据传递链路->判断是否存在依赖->分析修复方式(判断是否可以修复、使用哪种修复方式)->修复漏洞->验证是否存在语法问题和依赖问题(注意:**不要做与漏洞修复无关的代码改动**)。
|
|
5
|
+
|
|
6
|
+
## 漏洞分析
|
|
7
|
+
### 不可修复场景
|
|
8
|
+
1. sql 语句为外部包生成或者传入(例如:request 参数、用户文件上传后读取等),无法通过当前数据流链路的函数或者源码中找到定义或者生成方法,无法确定 sql 语句的信息,这里作为不可修复场景,不进行漏洞修复。
|
|
9
|
+
2. 污点数据以select、update、delete 等子句拼接到 sql 语句时,且无法确定污点数据子句具体信息,为不可修复场景,不进行漏洞修复。
|
|
10
|
+
3. 明确的知道传入的数据为表名、列名或列名列表等,表示可修复,参考可修复场景进行修复。
|
|
11
|
+
|
|
12
|
+
示例:
|
|
13
|
+
|
|
14
|
+
```java
|
|
15
|
+
//sql 从外部 form requestbody 或者 request 请求数据 中直接输入
|
|
16
|
+
public StreamingResponseBody queryDataBySql(@RequestBody MetaDataSqlForm form){
|
|
17
|
+
String sql = form.sql; //sql 语句直接通过外部参数传递进来,无法知道 sql 具体内容,所以不能简单的进行过滤修复,返回不可修复
|
|
18
|
+
PreparedStatement stmt = conn.prepareStatement(sql);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
//Condition 从外部 form requestbody 或者 request 请求数据 中直接输入
|
|
22
|
+
public StreamingResponseBody queryDataBySql(@RequestBody MetaDataSqlForm form){
|
|
23
|
+
String whereCondition = form.condition;
|
|
24
|
+
String sql = "select * from user " + whereCondition; //外部输入了新的子句,但是子句无法知道具体内容,无法通过过滤修复,返回不可修复
|
|
25
|
+
PreparedStatement stmt = conn.prepareStatement(sql);
|
|
26
|
+
}
|
|
27
|
+
// 从外部文件输入
|
|
28
|
+
public void processFileToSql(MultipartFile file, int recordId) throws Exception {
|
|
29
|
+
String fileContent;
|
|
30
|
+
try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream()))) {
|
|
31
|
+
fileContent = reader.lines().collect(Collectors.joining("\n"));
|
|
32
|
+
}
|
|
33
|
+
String sql = fileContent; //sql 语句直接通过外部参数传递进来,无法知道 sql 具体内容,所以不能简单的进行过滤修复,返回不可修复
|
|
34
|
+
PreparedStatement stmt = conn.prepareStatement(sql);
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
### 可修复场景
|
|
38
|
+
1. 基于当前数据流代码文件中进行漏洞修复,不要创建新的用于代码过滤的工具类文件,例如:在需要过滤表名列名的情况下,直接在当前 java 文件中建立通用的过滤函数,并在需要过滤的地方使用。(过滤方式参考过滤方式规则)
|
|
39
|
+
2. 同个文件进行参数过滤时,可以在文件头定义过滤正则或者过滤函数,如果定义已经存在,直接复用。
|
|
40
|
+
3. 当修复需要对**函数签名修改时,需要对函数进行重载**,避免修改函数签名导致原业务编译使用异常。
|
|
41
|
+
4. 污点表名、列名、视图名称、DataBase 名称、order 信息、orderby 信息等拼接进入SQL 语句时需要进行过滤的方式修复漏洞,过滤使用的正则:"^[\\w\\s\\[\\]\"`$.,*]+$"。(XML/JSON配置中可能为"^[\\w\\s\\[\\]\\\"`$.,*]+$", 注意这里的引号转义问题,避免转义失败导致语法错误)
|
|
42
|
+
5. show、create 、drop、grant 语句场景不支持预编译参数方式修复漏洞,需要使用对污点参数进行过滤的方法进行修复,过滤使用的正则:"^[\\w\\s\\[\\]\"`$.,*]+$"。(XML/JSON配置中可能为"^[\\w\\s\\[\\]\\\"`$.,*]+$", 注意这里的引号转义问题,避免转义失败导致语法错误)
|
|
43
|
+
6. SQL 语句拼接 in 的参数时,需要对其中的每个子项进行过滤或者预编译,如果适合修改成预编译,则**按照参数个数的对 in () 中的内容进行 ? 替换**(**即每个子项均进行参数预编译,NamedParameterJdbcTemplate和**`JPQL`**支持 in (:ids) 的除外**),将每个参数放入查询参数中,优先进行预编译参数绑定。
|
|
44
|
+
7. 在 where 语句场景 field 操作符 value,如果 field 或者 操作符为污点信息,则进行过滤,如果 value 为污点数据,则进行预编译操作。
|
|
45
|
+
8. 当污点拼接进入 sql 语句时,在分析确认需要通过预编译修复的时候,需要考虑 sql 语句的所在位置,例如有时 sql 语句为全局变量赋值到局部变量,改成预编译执行时,需要将当前函数变更为预编译方式,且需要变更全局 sql 语句为预编译方式,避免遗漏导致用户执行出错。
|
|
46
|
+
9. mybatis 场景下,漏洞触发为 xml 文件时,需要通过参数传递链路,从 java 代码文件层面找到合适的位置,对污点参数进行过滤。例如表名、列名、orderby 和 in 等参数场景,无法直接通过将 $ 改为 # 绑定参数或预编译。
|
|
47
|
+
10. 对于同一个参数进行漏洞修复时,只需要采取一种修复方式即可,不要同时对一个参数进行预编译、参数绑定和过滤等多种方式。
|
|
48
|
+
11. 当污点数据拼接进入到 sql ,需要详细分析当前函数是否还存在其他地方使用了该 sql,避免因为采用预编译修复方案,导致其他使用该 sql 的场景存在异常;如果其他使用的场景在当前文件中,可以统一进行预编译方式修改,得对函数需要重载,保障其他文件调用正常。
|
|
49
|
+
|
|
50
|
+
#### 过滤方式规则
|
|
51
|
+
1、对于需要过滤或者正则校验进行防护的参数在后续使用时为直接拼接或者传递,可以直接通过正则过滤;如果使用的是某个对象的成员或者可迭代对象时,后续直接使用的是『对象.成员函数()』的,**使用临时变量承接过滤函数过滤后的结果, 函数起名以 safeFilter 或者safeValidate 开头(如果不存在需要在当前文件中定义,只有存在的函数才能使用),过滤后使用新的对象进行传递(保障后续数据流检测可以识别到该防护,即用安全的函数截断数据流)**。例如:
|
|
52
|
+
|
|
53
|
+
```java
|
|
54
|
+
//原始代码
|
|
55
|
+
if (CollectionUtils.isNotEmpty(param.getOrgIdList())) {
|
|
56
|
+
sb.append(" AND txf.org_id IN " + SqlUtils.getInSql(param.getOrgIdList())); // 原始代码,param.getOrgIdList() 外部传入的误点参数
|
|
57
|
+
}
|
|
58
|
+
....
|
|
59
|
+
// 定义过滤函数,返回一个新的 list 对象
|
|
60
|
+
public static List<String> safeFilterOrgIdList(List<String> input) {
|
|
61
|
+
if (input == null) return null;
|
|
62
|
+
Pattern ID_PATTERN = Pattern.compile("^\\d+$");
|
|
63
|
+
return input.stream()
|
|
64
|
+
.filter(item -> item != null && !item.trim().isEmpty()) // 判空
|
|
65
|
+
.filter(item -> ID_PATTERN.matcher(item).matches()) // 正则匹配
|
|
66
|
+
.collect(Collectors.toList());
|
|
67
|
+
}
|
|
68
|
+
// 修复后的代码
|
|
69
|
+
if (CollectionUtils.isNotEmpty(param.getOrgIdList())) {
|
|
70
|
+
// 1. 先过滤非法参数
|
|
71
|
+
List<String> cleanList = safeFilterOrgIdList(param.getOrgIdList());
|
|
72
|
+
|
|
73
|
+
// 2. 只有过滤后仍有值,才拼接 SQL,其他地方使用 param.getOrgIdList(),也需要使用 cleanList
|
|
74
|
+
if (CollectionUtils.isNotEmpty(cleanList)) {
|
|
75
|
+
// 使用清洗后的 cleanList,避免注入风险
|
|
76
|
+
sb.append(" AND txf.org_id IN " + SqlUtils.getInSql(cleanList)); //使用过滤后的参数传递,确保orgid 的传递是通过了清洗函数的,这里的清洗函数为 safeFilterOrgIdList
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
2、如果传入下层函数的为一个对象,有污点的是其中一个属性,使用过滤函数过滤整个对象,**函数起名以 safeFilter 或者safeValidate 开头,并通过临时变量承接过滤函数产生的过滤后的对象,使用临时对象向下层调用传递**。例如:
|
|
82
|
+
|
|
83
|
+
```java
|
|
84
|
+
//原始代码
|
|
85
|
+
if (CollectionUtils.isNotEmpty(param.getOrgIdList())) {
|
|
86
|
+
return xxx.selectOrg(param);
|
|
87
|
+
}
|
|
88
|
+
....
|
|
89
|
+
// 定义过滤函数,返回一个新的 CourseQuery 对象,用安全的函数截断数据流
|
|
90
|
+
public static CourseQuery safeFilterOrgIdList(CourseQuery param) {
|
|
91
|
+
if (param == null) return null;
|
|
92
|
+
Pattern ID_PATTERN = Pattern.compile("^\\d+$");
|
|
93
|
+
for (String item : param.getOrgIdList()) {
|
|
94
|
+
if (item != null && !ID_PATTERN.matcher(item).matches()) {
|
|
95
|
+
throw new IllegalArgumentException("Invalid OrgIdList contains invalid item: " + item);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return param;
|
|
99
|
+
}
|
|
100
|
+
// 修复后的代码
|
|
101
|
+
if (CollectionUtils.isNotEmpty(param.getOrgIdList())) {
|
|
102
|
+
CourseQuery tmpParam = safeFilterOrgIdList(param); //通过 safeFilterOrgIdList 函数过滤
|
|
103
|
+
return xxx.selectOrg(tmpParam); // 向下传递过滤后的
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
3、**MyBatis/ORM 场景下对象属性过滤的特殊处理**:当污点数据通过对象属性传递到 MyBatis XML(如 `${page.orderBy}`、`${entity.tableName}` 等)时,需要根据赋值和读取方式选择正确的过滤位置:
|
|
108
|
+
|
|
109
|
+
| 赋值方式 | 读取方式 | 过滤位置 |
|
|
110
|
+
|---------|---------|---------|
|
|
111
|
+
| 调用 setter | MyBatis `${obj.field}` 隐式访问 | **setter 中过滤** |
|
|
112
|
+
| 调用 setter | 代码显式调用 getter | **setter 和 getter 都过滤** |
|
|
113
|
+
| 构造函数/反射赋值 | 代码显式调用 getter | **getter 中过滤**(构造函数也建议加) |
|
|
114
|
+
| 构造函数/反射赋值 | MyBatis `${obj.field}` 隐式访问 | **构造函数中过滤** |
|
|
115
|
+
|
|
116
|
+
**原理说明**:静态分析器存在两个追踪限制:
|
|
117
|
+
1. **隐式调用不可见**:无法追踪 MyBatis OGNL 表达式对 getter 的隐式调用(如 `${obj.field}`),因此 getter 中的过滤对 MyBatis 隐式访问无效,必须在 setter/构造函数等赋值入口过滤。
|
|
118
|
+
2. **对象级污点追踪**:当整个对象被标记为污点(如 Spring Controller 的 `@RequestBody` 参数),即使 setter 中已对某个字段做了过滤,后续通过显式 getter 调用获取该字段时,分析器仍可能因对象级污点而认为返回值是污点。此时需要在 getter 中也加过滤,让分析器在追踪 getter 方法体时命中清洗规则。
|
|
119
|
+
|
|
120
|
+
**最佳实践:在 setter 和 getter 中同时过滤**,确保无论是 MyBatis 隐式访问还是代码显式调用 getter,都能被静态分析器识别到防护措施。构造函数中如有直接赋值也需过滤。
|
|
121
|
+
|
|
122
|
+
**示例1:setter 赋值 + MyBatis 隐式访问 / 显式 getter 调用(setter 和 getter 都过滤)**
|
|
123
|
+
```java
|
|
124
|
+
// 原始代码(有漏洞)
|
|
125
|
+
public class Page<T> {
|
|
126
|
+
private String orderBy;
|
|
127
|
+
|
|
128
|
+
public void setOrderBy(String orderBy) {
|
|
129
|
+
this.orderBy = orderBy; // 未过滤,MyBatis ${page.orderBy} 访问时有注入风险
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
public String getOrderBy() {
|
|
133
|
+
return orderBy; // 未过滤,显式调用时对象级污点会穿透
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// 修复后(setter 和 getter 都过滤)
|
|
138
|
+
public class Page<T> {
|
|
139
|
+
private String orderBy;
|
|
140
|
+
private static final Pattern SAFE_PATTERN = Pattern.compile("^[\\w\\s\\[\\]\"`$.,*]+$");
|
|
141
|
+
|
|
142
|
+
private static String safeFilterOrderBy(String input) {
|
|
143
|
+
if (input == null || input.isEmpty()) return "";
|
|
144
|
+
if (!SAFE_PATTERN.matcher(input).matches()) return "";
|
|
145
|
+
return input;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
public void setOrderBy(String orderBy) {
|
|
149
|
+
this.orderBy = safeFilterOrderBy(orderBy); // setter 过滤:覆盖 MyBatis 隐式访问场景
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
public String getOrderBy() {
|
|
153
|
+
return safeFilterOrderBy(orderBy); // getter 过滤:覆盖代码显式调用 getter 时对象级污点场景
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**示例2:构造函数赋值 + 显式 getter 调用(构造函数、setter、getter 都过滤)**
|
|
159
|
+
```java
|
|
160
|
+
// 原始代码(有漏洞)
|
|
161
|
+
public class Page<T> {
|
|
162
|
+
private String orderBy;
|
|
163
|
+
|
|
164
|
+
public Page(HttpServletRequest request) {
|
|
165
|
+
this.orderBy = request.getParameter("orderBy"); // 构造函数直接赋值,未过滤
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// 修复后(所有入口和出口都过滤)
|
|
170
|
+
public class Page<T> {
|
|
171
|
+
private String orderBy;
|
|
172
|
+
private static final Pattern SAFE_PATTERN = Pattern.compile("^[\\w\\s\\[\\]\"`$.,*]+$");
|
|
173
|
+
|
|
174
|
+
private static String safeFilterOrderBy(String input) {
|
|
175
|
+
if (input == null || input.isEmpty()) return "";
|
|
176
|
+
if (!SAFE_PATTERN.matcher(input).matches()) return "";
|
|
177
|
+
return input;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
public Page(HttpServletRequest request) {
|
|
181
|
+
this.orderBy = safeFilterOrderBy(request.getParameter("orderBy")); // 构造函数中过滤
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
public void setOrderBy(String orderBy) {
|
|
185
|
+
this.orderBy = safeFilterOrderBy(orderBy); // setter 过滤:覆盖 MyBatis 隐式访问场景
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
public String getOrderBy() {
|
|
189
|
+
return safeFilterOrderBy(orderBy); // getter 过滤:覆盖显式调用时对象级污点场景
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**注意**:这种模式适用于所有通过 ORM/模板引擎(MyBatis、Hibernate、Freemarker 等)访问的对象属性。setter 和 getter 都加过滤虽然看似冗余,但因为静态分析器的两种限制(隐式调用不可见 + 对象级污点追踪),这是确保所有场景都能被识别到防护的最可靠方式。
|
|
195
|
+
|
|
196
|
+
### 修复后的检查点
|
|
197
|
+
1. 检查补丁代码其中参数是否在当前函数范围。
|
|
198
|
+
2. 检查补丁代码是否存在语法异常问题,例如正则中引号转义失败,导致非预期字符串闭合问题。
|
|
199
|
+
3. 检查补丁代码是否影响其他调用场景使用。
|
|
200
|
+
|
|
201
|
+
## 修复示例
|
|
202
|
+
以下是针对 SQL 注入漏洞在不同场景下的详细整理,包括常见 Java 框架(如 MyBatis、JDBC、Hibernate 等)中的注入风险及其示例:
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
### 1. **JDBC 拼接 SQL 语句**
|
|
207
|
+
* **场景**:动态拼接条件时未使用参数化查询,且原始 sql 为全局定义。
|
|
208
|
+
* **示例**:
|
|
209
|
+
|
|
210
|
+
```java
|
|
211
|
+
public class TestServiceImp implements TestService {
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* 周期营销数据,总和表
|
|
215
|
+
*/
|
|
216
|
+
private static final String QUERY_SQL =
|
|
217
|
+
"select id,uid,name from user_table where uid = '%s' and tid in (%s)";
|
|
218
|
+
private static final String QUERY_SQL_ALL =
|
|
219
|
+
"select * from user_table where uid = '%s' and tid in (%s)";
|
|
220
|
+
|
|
221
|
+
private List<ScheduleReportDTO> getData(String uId, List<String> tId) {
|
|
222
|
+
String queryReady;
|
|
223
|
+
if (isAll) {
|
|
224
|
+
queryReady = QUERY_SQL_ALL;
|
|
225
|
+
else{
|
|
226
|
+
queryReady = QUERY_SQL;
|
|
227
|
+
}
|
|
228
|
+
String queryReadySQL = String.format(queryReady, uId, StringUtils.join(tId, ","));
|
|
229
|
+
resultSet = statement.executeQuery(queryReadySQL);
|
|
230
|
+
List<ScheduleReportDTO> remindReportDataList = new ArrayList<>();
|
|
231
|
+
//获取返回结果
|
|
232
|
+
return remindReportDataList;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
* **风险**:用户输入的 uId 参数会导致 SQL 注入,tId 也是用户输入的,是List<String> 类型,可能也会导致 SQL 注入,如果这里 tId 为List<long>等情况,则tId不存在注入风险,不用修复。另外, sql 语句在全局变量定义,修复时需要考虑修改全局变量定义的 sql 语句。
|
|
237
|
+
* **修复**:条件直使用 PreparedStatement 动态绑定参数,同时需要对 sql 语句的源头QUERY_SQL_ALL 和 QUERY_SQL 两个全局变量进行修改,使其支持预编译:
|
|
238
|
+
|
|
239
|
+
```java
|
|
240
|
+
public class TestServiceImp implements TestService {
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* 周期营销数据,总和表
|
|
244
|
+
*/
|
|
245
|
+
private static final String QUERY_SQL =
|
|
246
|
+
"select id,uid,name from user_table where uid = ? and tid in (%s)";
|
|
247
|
+
private static final String QUERY_SQL_ALL =
|
|
248
|
+
"select * from user_table where uid = ? and tid in (%s)";
|
|
249
|
+
|
|
250
|
+
private List<ScheduleReportDTO> getData(String uid, List<String> tId) {
|
|
251
|
+
String queryReady;
|
|
252
|
+
if (isAll) {
|
|
253
|
+
queryReady = QUERY_SQL_ALL;
|
|
254
|
+
} else {
|
|
255
|
+
queryReady = QUERY_SQL;
|
|
256
|
+
}
|
|
257
|
+
String queryReadySQL = String.format(queryReady, String.join(",", Collections.nCopies(tId.size(), "?")));
|
|
258
|
+
PreparedStatement pstmt = connection.prepareStatement(queryReadySQL);
|
|
259
|
+
pstmt.setString(1, uid);
|
|
260
|
+
for (int i = 0; i < tId.size(); i++) {
|
|
261
|
+
pstmt.setString(i + 2, tId.get(i));
|
|
262
|
+
}
|
|
263
|
+
resultSet = pstmt.executeQuery();
|
|
264
|
+
List<ScheduleReportDTO> remindReportDataList = new ArrayList<>();
|
|
265
|
+
//获取返回结果
|
|
266
|
+
return remindReportDataList;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
### 2. **MyBatis 使用 **`$`** 传递动态表名、列名、orderby 等**
|
|
273
|
+
* **场景**:MyBatis 中使用 `$` 符号直接拼接用户输入到 SQL 语句中。
|
|
274
|
+
* **示例**:
|
|
275
|
+
|
|
276
|
+
```xml
|
|
277
|
+
<select id="getUserByName" resultType="User">
|
|
278
|
+
SELECT * FROM ${tableName} WHERE username = ${username} order by ${order}
|
|
279
|
+
</select>
|
|
280
|
+
```
|
|
281
|
+
* 若用户传入 `tableName = users; DROP TABLE users;`、`username = zhangsan; DROP TABLE users;`或者 `order = order; DROP TABLE users;`,会导致 SQL 注入。
|
|
282
|
+
* **风险**:`$` 符号直接拼接字符串,不进行参数化处理。
|
|
283
|
+
* **修复**:
|
|
284
|
+
* 避免使用 `$` 传递动态表名、列名或者排序条件等。
|
|
285
|
+
* 在 java 代码层面进行参数过滤,例如:
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
```java
|
|
289
|
+
if (!tableName.matches("^[\\w\\s\\[\\]\"`$.,*]+$")) {
|
|
290
|
+
throw new IllegalArgumentException("Invalid tableName");
|
|
291
|
+
}
|
|
292
|
+
if (!order.matches("^[\\w\\s\\[\\]\"`$.,*]+$")) {
|
|
293
|
+
throw new IllegalArgumentException("Invalid order");
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
* 使用 `#` 绑定参数传递普通值, 例如,这里的${username} 改为 #{username} 。
|
|
297
|
+
|
|
298
|
+
```java
|
|
299
|
+
<select id="getUserByName" resultType="User">
|
|
300
|
+
SELECT * FROM ${tableName} WHERE username = #{username} order by ${order}
|
|
301
|
+
</select>
|
|
302
|
+
```
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
### 3. **Hibernate 拼接 HQL 查询**
|
|
306
|
+
* **场景**:在 Hibernate 中直接拼接用户输入到 HQL 查询中。
|
|
307
|
+
* **示例**:
|
|
308
|
+
|
|
309
|
+
```java
|
|
310
|
+
String username = request.getParameter("username");
|
|
311
|
+
String hql = "FROM User WHERE username = '" + username + "'";
|
|
312
|
+
Query query = session.createQuery(hql);
|
|
313
|
+
List<User> users = query.list();
|
|
314
|
+
```
|
|
315
|
+
* **风险**:用户输入 `' OR '1'='1` 会导致 SQL 注入(HQL 注入)。
|
|
316
|
+
* **修复**:使用命名参数绑定:
|
|
317
|
+
|
|
318
|
+
```java
|
|
319
|
+
String hql = "FROM User WHERE username = :username";
|
|
320
|
+
Query query = session.createQuery(hql);
|
|
321
|
+
query.setParameter("username", username);
|
|
322
|
+
List<User> users = query.list();
|
|
323
|
+
```
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
### 4. **Spring Data JPA 动态查询**
|
|
327
|
+
* **场景**:使用 `@Query` 注解或原生 SQL 查询时,直接拼接用户输入。
|
|
328
|
+
* **示例**:
|
|
329
|
+
|
|
330
|
+
```java
|
|
331
|
+
@Query(value = "SELECT * FROM users WHERE username = '" + username + "'", nativeQuery = true)
|
|
332
|
+
List<User> findByUsernameNative(String username);
|
|
333
|
+
|
|
334
|
+
```
|
|
335
|
+
* **风险**:使用原生 SQL 拼接则存在SQL 注入。
|
|
336
|
+
* **修复**:使用命名参数绑定:
|
|
337
|
+
|
|
338
|
+
```java
|
|
339
|
+
@Query("SELECT u FROM User u WHERE u.username = :username")
|
|
340
|
+
List<User> findByUsername(@Param("username") String username);
|
|
341
|
+
```
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
### 5. **存储过程调用传递动态参数**
|
|
345
|
+
* **场景**:在调用存储过程时,直接拼接用户输入到参数中。
|
|
346
|
+
* **示例**:
|
|
347
|
+
|
|
348
|
+
```java
|
|
349
|
+
String username = request.getParameter("username");
|
|
350
|
+
CallableStatement cstmt = connection.prepareCall("{CALL getUser(?)}");
|
|
351
|
+
cstmt.setString(1, "'" + username + "'"); // 直接拼接用户输入
|
|
352
|
+
ResultSet rs = cstmt.executeQuery();
|
|
353
|
+
```
|
|
354
|
+
* **风险**:用户输入 `'; DROP TABLE users; --` 会导致 SQL 注入。
|
|
355
|
+
* **修复**:使用参数化调用:
|
|
356
|
+
|
|
357
|
+
```java
|
|
358
|
+
CallableStatement cstmt = connection.prepareCall("{CALL getUser(?)}");
|
|
359
|
+
cstmt.setString(1, username); // 参数化传递
|
|
360
|
+
ResultSet rs = cstmt.executeQuery();
|
|
361
|
+
```
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
### 6. **ORM 框架的动态查询(如 JPA Criteria API)**
|
|
365
|
+
* **场景**:在 ORM 框架中使用动态查询时,未正确验证用户输入。
|
|
366
|
+
* **示例**:
|
|
367
|
+
|
|
368
|
+
```java
|
|
369
|
+
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
|
370
|
+
CriteriaQuery<User> cq = cb.createQuery(User.class);
|
|
371
|
+
Root<User> user = cq.from(User.class);
|
|
372
|
+
cq.where(cb.equal(user.get("username"), username)); // 假设 username 未验证
|
|
373
|
+
List<User> users = entityManager.createQuery(cq).getResultList();
|
|
374
|
+
```
|
|
375
|
+
* **风险**:若 `username` 未验证,可能被恶意构造。
|
|
376
|
+
* **修复**:确保用户输入经过验证,避免直接使用未经验证的输入。
|
|
377
|
+
|
|
378
|
+
```java
|
|
379
|
+
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
|
380
|
+
CriteriaQuery<User> cq = cb.createQuery(User.class);
|
|
381
|
+
Root<User> user = cq.from(User.class);
|
|
382
|
+
if (!username.matches("^[\\w\\s\\[\\]\"`$.,*]+$")) {
|
|
383
|
+
throw new IllegalArgumentException("Invalid username");
|
|
384
|
+
}
|
|
385
|
+
cq.where(cb.equal(user.get("username"), username));
|
|
386
|
+
List<User> users = entityManager.createQuery(cq).getResultList();
|
|
387
|
+
```
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
### 7. **组合条件查询中的动态拼接1**
|
|
391
|
+
* **场景**:在复杂查询中,动态拼接多个条件时未使用参数化查询。
|
|
392
|
+
* **示例**:
|
|
393
|
+
|
|
394
|
+
```java
|
|
395
|
+
String baseQuery = "SELECT * FROM users WHERE 1=1";
|
|
396
|
+
if (username != null) {
|
|
397
|
+
baseQuery += " AND username = '" + username + "'";
|
|
398
|
+
}
|
|
399
|
+
if (email != null) {
|
|
400
|
+
baseQuery += " AND email = '" + email + "'";
|
|
401
|
+
}
|
|
402
|
+
Statement stmt = connection.createStatement();
|
|
403
|
+
ResultSet rs = stmt.executeQuery(baseQuery);
|
|
404
|
+
```
|
|
405
|
+
* **风险**:用户输入 `'; DROP TABLE users; --` 会导致 SQL 注入。
|
|
406
|
+
* **修复**:使用 `PreparedStatement` 动态绑定参数:
|
|
407
|
+
|
|
408
|
+
```java
|
|
409
|
+
StringBuilder sb = new StringBuilder("SELECT * FROM users WHERE 1=1");
|
|
410
|
+
List<Object> params = new ArrayList<>();
|
|
411
|
+
if (username != null) {
|
|
412
|
+
sb.append(" AND username = ?");
|
|
413
|
+
params.add(username);
|
|
414
|
+
}
|
|
415
|
+
if (email != null) {
|
|
416
|
+
sb.append(" AND email = ?");
|
|
417
|
+
params.add(email);
|
|
418
|
+
}
|
|
419
|
+
PreparedStatement pstmt = connection.prepareStatement(sb.toString());
|
|
420
|
+
for (int i = 0; i < params.size(); i++) {
|
|
421
|
+
pstmt.setObject(i + 1, params.get(i));
|
|
422
|
+
}
|
|
423
|
+
ResultSet rs = pstmt.executeQuery();
|
|
424
|
+
```
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
### 8. **组合条件查询中的动态拼接2**
|
|
428
|
+
* **场景**:在复杂查询中,动态拼接多个条件时未使用参数化查询,动态拼接表名列名未进行过滤。
|
|
429
|
+
* **示例**:
|
|
430
|
+
|
|
431
|
+
```java
|
|
432
|
+
String baseQuery = "SELECT id,username,email," + addr + " FROM " + userTable + " WHERE 1=1";
|
|
433
|
+
if (username != null) {
|
|
434
|
+
baseQuery += " AND username = '" + username + "'";
|
|
435
|
+
}
|
|
436
|
+
if (email != null) {
|
|
437
|
+
baseQuery += " AND email = '" + email + "'";
|
|
438
|
+
}
|
|
439
|
+
Statement stmt = connection.createStatement();
|
|
440
|
+
ResultSet rs = stmt.executeQuery(baseQuery);
|
|
441
|
+
```
|
|
442
|
+
* **风险**:用户输入 `'; DROP TABLE users; --` 会导致 SQL 注入。
|
|
443
|
+
* **修复**:对表名、列名进行正则过滤,对条件直使用 `PreparedStatement` 动态绑定参数:
|
|
444
|
+
|
|
445
|
+
```java
|
|
446
|
+
if (!addr.matches("^[\\w\\s\\[\\]\"`$.,*]+$")) {
|
|
447
|
+
throw new IllegalArgumentException("Invalid column addr");
|
|
448
|
+
}
|
|
449
|
+
if (!userTable.matches("^[\\w\\s\\[\\]\"`$.,*]+$")) {
|
|
450
|
+
throw new IllegalArgumentException("Invalid userTable");
|
|
451
|
+
}
|
|
452
|
+
StringBuilder sb = new StringBuilder("SELECT id,username,email," + addr + " FROM " + userTable + " WHERE 1=1");
|
|
453
|
+
List<Object> params = new ArrayList<>();
|
|
454
|
+
if (username != null) {
|
|
455
|
+
sb.append(" AND username = ?");
|
|
456
|
+
params.add(username);
|
|
457
|
+
}
|
|
458
|
+
if (email != null) {
|
|
459
|
+
sb.append(" AND email = ?");
|
|
460
|
+
params.add(email);
|
|
461
|
+
}
|
|
462
|
+
PreparedStatement pstmt = connection.prepareStatement(sb.toString());
|
|
463
|
+
for (int i = 0; i < params.size(); i++) {
|
|
464
|
+
pstmt.setObject(i + 1, params.get(i));
|
|
465
|
+
}
|
|
466
|
+
ResultSet rs = pstmt.executeQuery();
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
### 9. **组合条件查询中的动态拼接3**
|
|
471
|
+
* **场景**:在复杂查询中,动态拼接条件时未使用参数化查询,且被其他函数使用。
|
|
472
|
+
* **示例**:
|
|
473
|
+
|
|
474
|
+
```java
|
|
475
|
+
@Override
|
|
476
|
+
public Page<Object> queryPageFieldData(String schema, String tableName, String fieldName, String keyword,
|
|
477
|
+
OrderType orderType, int currentPage, int pageSize) {
|
|
478
|
+
StringBuilder sqlSb = new StringBuilder();
|
|
479
|
+
sqlSb.append("select distinct ").append(fieldName)
|
|
480
|
+
.append(" from ")
|
|
481
|
+
.append(quoteName(schema)).append(".").append(quoteName(tableName));
|
|
482
|
+
if (StringUtils.isNotEmpty(keyword)) {
|
|
483
|
+
sqlSb.append(" where ").append(quoteName(fieldName)).append(" like '%").append(keyword).append("%'");
|
|
484
|
+
}
|
|
485
|
+
sqlSb.append(" order by ").append(quoteName(fieldName)).append(" ").append(orderType != null ?
|
|
486
|
+
orderType.toString() : "asc");
|
|
487
|
+
String sql = sqlSb.toString();
|
|
488
|
+
log.info("[{}]. queryPageFieldData. sql=[{}]", ThreadLocalUtil.getTraceId(), sql);
|
|
489
|
+
|
|
490
|
+
List<Object> result = new ArrayList<>();
|
|
491
|
+
String pageSql = genPageSql(sql, currentPage, pageSize);
|
|
492
|
+
try (Statement stmt = connection.createStatement();
|
|
493
|
+
ResultSet rs = stmt.executeQuery(pageSql)) {
|
|
494
|
+
ResultSetMetaData rsmd = rs.getMetaData();
|
|
495
|
+
while (rs.next()) {
|
|
496
|
+
result.add(rs.getObject(fieldName));
|
|
497
|
+
}
|
|
498
|
+
} catch (SQLException e) {
|
|
499
|
+
throw new ShowException(e);
|
|
500
|
+
}
|
|
501
|
+
int count = queryCount(sql);
|
|
502
|
+
Page page = new Page(count, pageSize, currentPage);
|
|
503
|
+
page.setData(result);
|
|
504
|
+
return page;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
@Override
|
|
508
|
+
public int queryCount(String sql) {
|
|
509
|
+
StringBuilder countSql = new StringBuilder();
|
|
510
|
+
countSql.append("select count(*) as count from (").append(sql).append(") count");
|
|
511
|
+
List<Integer> result = new ArrayList<>();
|
|
512
|
+
try (Statement stmt = connection.createStatement();
|
|
513
|
+
ResultSet rs = stmt.executeQuery(countSql.toString())) {
|
|
514
|
+
while (rs.next()) {
|
|
515
|
+
result.add(rs.getInt("count"));
|
|
516
|
+
}
|
|
517
|
+
return result.get(0);
|
|
518
|
+
} catch (SQLException e) {
|
|
519
|
+
throw new ShowException(e.getMessage());
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
```
|
|
523
|
+
* **风险**:用户输入的 like 参数会导致 SQL 注入,同时sqlSb 在当前函数使用,且被引入到sql变量中,导致风险传递到 queryCount 函数,此时需要考虑queryPageFieldData和queryCount两个函数的风险修复。
|
|
524
|
+
* **修复**:条件直使用 `PreparedStatement` 动态绑定参数,同时对queryCount情况也修复,因为使用预编译,所以参数也需要通过函数参数传递,此时通过重载queryCount函数,加入需要传递的参数,并对queryCount中的查询进行预编译改造:
|
|
525
|
+
|
|
526
|
+
```java
|
|
527
|
+
@Override
|
|
528
|
+
public Page<Object> queryPageFieldData(String schema, String tableName, String fieldName, String keyword,
|
|
529
|
+
OrderType orderType, int currentPage, int pageSize) {
|
|
530
|
+
StringBuilder sqlSb = new StringBuilder();
|
|
531
|
+
sqlSb.append("select distinct ").append(fieldName)
|
|
532
|
+
.append(" from ")
|
|
533
|
+
.append(quoteName(schema)).append(".").append(quoteName(tableName));
|
|
534
|
+
String sql;
|
|
535
|
+
List<Object> params = new ArrayList<>();
|
|
536
|
+
if (StringUtils.isNotEmpty(keyword)) {
|
|
537
|
+
sqlSb.append(" where ").append(quoteName(fieldName)).append(" like ?");
|
|
538
|
+
params.add("%" + keyword + "%");
|
|
539
|
+
}
|
|
540
|
+
sqlSb.append(" order by ").append(quoteName(fieldName)).append(" ").append(orderType != null ?
|
|
541
|
+
orderType.toString() : "asc");
|
|
542
|
+
sql = sqlSb.toString();
|
|
543
|
+
log.info("[{}]. queryPageFieldData. sql=[{}]", ThreadLocalUtil.getTraceId(), sql);
|
|
544
|
+
|
|
545
|
+
List<Object> result = new ArrayList<>();
|
|
546
|
+
String pageSql = genPageSql(sql, currentPage, pageSize);
|
|
547
|
+
try (PreparedStatement stmt = connection.prepareStatement(pageSql)) {
|
|
548
|
+
for (int i = 0; i < params.size(); i++) {
|
|
549
|
+
stmt.setObject(i + 1, params.get(i));
|
|
550
|
+
}
|
|
551
|
+
try (ResultSet rs = stmt.executeQuery()) {
|
|
552
|
+
ResultSetMetaData rsmd = rs.getMetaData();
|
|
553
|
+
while (rs.next()) {
|
|
554
|
+
result.add(rs.getObject(fieldName));
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
} catch (SQLException e) {
|
|
558
|
+
throw new ShowException(e);
|
|
559
|
+
}
|
|
560
|
+
int count = queryCount(sql, params);
|
|
561
|
+
Page page = new Page(count, pageSize, currentPage);
|
|
562
|
+
page.setData(result);
|
|
563
|
+
return page;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
@Override
|
|
567
|
+
public int queryCount(String sql, List<Object> params) {
|
|
568
|
+
StringBuilder countSql = new StringBuilder();
|
|
569
|
+
countSql.append("select count(*) as count from (").append(sql).append(") count");
|
|
570
|
+
List<Integer> result = new ArrayList<>();
|
|
571
|
+
try (PreparedStatement stmt = connection.prepareStatement(countSql.toString())) {
|
|
572
|
+
for (int i = 0; i < params.size(); i++) {
|
|
573
|
+
stmt.setObject(i + 1, params.get(i));
|
|
574
|
+
}
|
|
575
|
+
try (ResultSet rs = stmt.executeQuery()) {
|
|
576
|
+
while (rs.next()) {
|
|
577
|
+
result.add(rs.getInt("count"));
|
|
578
|
+
}
|
|
579
|
+
return result.get(0);
|
|
580
|
+
}
|
|
581
|
+
} catch (SQLException e) {
|
|
582
|
+
throw new ShowException(e.getMessage());
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
@Override
|
|
587
|
+
public int queryCount(String sql) {
|
|
588
|
+
return queryCount(sql, Collections.emptyList());
|
|
589
|
+
}
|
|
590
|
+
```
|
|
591
|
+
---
|