@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
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# 敏感信息硬编码漏洞修复流程
|
|
2
|
+
|
|
3
|
+
修复漏洞分为以下三个步骤:
|
|
4
|
+
1. 读取漏洞报告
|
|
5
|
+
2. 修复硬编码漏洞
|
|
6
|
+
3. 引入SDK(如果需要)
|
|
7
|
+
|
|
8
|
+
## 读取漏洞报告
|
|
9
|
+
|
|
10
|
+
基于凭证配置网页回传的数据获取漏洞报告,示例如下:
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"scanChatID": "从 credential_poll 返回的 chatUUID",
|
|
14
|
+
"repo": {
|
|
15
|
+
"language": "java",
|
|
16
|
+
"framework": "springboot"
|
|
17
|
+
},
|
|
18
|
+
"deployment": {
|
|
19
|
+
"platform": 2,
|
|
20
|
+
"platformName": "ipipe"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
{
|
|
24
|
+
"name": "src/main/resources/application.properties",
|
|
25
|
+
"hash": "文件SHA256",
|
|
26
|
+
"vulList": [
|
|
27
|
+
{
|
|
28
|
+
"ruleID": "codescan_generic_password-config_sensitive",
|
|
29
|
+
"line": 35,
|
|
30
|
+
"hash": "漏洞hash",
|
|
31
|
+
"extra": {
|
|
32
|
+
"secret": {
|
|
33
|
+
"credentialName": "KL_SPRING_DATASOURCE_PASSWORD",
|
|
34
|
+
"start": {"col": 28, "line": 35},
|
|
35
|
+
"end": {"col": 43, "line": 35}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"trigger": 1,
|
|
43
|
+
"type": 4
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
各字段说明如下:
|
|
47
|
+
* repo:代码库信息
|
|
48
|
+
* language:代码语言
|
|
49
|
+
* framework:代码框架
|
|
50
|
+
* deployment:部署信息
|
|
51
|
+
* platform:部署平台,1 表示 cnap,2 表示 ipipe,3 表示 opera,4 表示 datamanage
|
|
52
|
+
* platformName:部署平台名称(如 ipipe、cnap、opera、datamanage)
|
|
53
|
+
* credential_id:凭证 ID(platform 为 4 时使用,用于引入 keyless-sdk)
|
|
54
|
+
* files:文件信息
|
|
55
|
+
* name:漏洞文件的相对路径
|
|
56
|
+
* vulList:漏洞列表
|
|
57
|
+
* extra.secret.start:敏感信息的开始位置(行、列)
|
|
58
|
+
* extra.secret.end:敏感信息的结束位置(行、列)
|
|
59
|
+
* extra.secret.credentialName:建议使用的环境变量名称
|
|
60
|
+
|
|
61
|
+
## 修复漏洞
|
|
62
|
+
硬编码漏洞修复的本质就是改变原来在代码中的硬编码敏感信息写法,改为从环境变量中读取敏感信息。修复时遍历漏洞报告中的所有文件和漏洞,逐个修复,按以下流程进行处理。
|
|
63
|
+
|
|
64
|
+
### 代码文件
|
|
65
|
+
如果漏洞文件是代码文件,比如 java、go、php、py 等,则根据各语言的写法生成读取环境变量的代码,并替换掉原来的硬编码敏感信息,注意有以下几点要求:
|
|
66
|
+
1. 与环境变量读取无关的其他代码和字符不要做任何改动
|
|
67
|
+
2. 如果漏洞行是被引号包裹的纯字符串,输出时保留其结构不变,仍然可以直接执行字符串操作,但要避免空字符串的拼接
|
|
68
|
+
3. 不要给出环境变量默认值
|
|
69
|
+
4. 如果代码开头存在空格、tab等缩进,保留原始代码的缩进,不要清除两端的空白字符
|
|
70
|
+
5. 如果是 python 语言,使用 os.environ 的方式获取环境变量,而不是 os.getenv
|
|
71
|
+
6. 如果是 go 语言,不能在 const 中读取环境变量,在 const 下方重新定义读取代码,并删除 const 的硬编码的值
|
|
72
|
+
7. 如果是 java 语言,且 platform 为 4 时,则采用 System.getProperty 的语法读取变量
|
|
73
|
+
8. 如果原文件中缺失读取环境变量所需的第三方库,则自动引入,比如 python 的 os 库,go 的 os 库等
|
|
74
|
+
|
|
75
|
+
### 配置文件
|
|
76
|
+
如果漏洞文件是配置文件,比如 yml、properties、ini 等,则结合语言和框架共同决定如何修改代码,修改原则如下:
|
|
77
|
+
1. 如果语言为 go,框架为 gdp,toml 后缀的文件直接使用特定的占位符替换敏感信息,占位符的环境变量名称根据 credential_name 命名,示例如下:
|
|
78
|
+
```toml
|
|
79
|
+
// 修复前
|
|
80
|
+
password=123123123
|
|
81
|
+
// 修复后
|
|
82
|
+
password=${env.KL_PASSWORD}
|
|
83
|
+
```
|
|
84
|
+
2. 除第一种情况之外,其他语言、框架、配置文件类型,使用常规的环境变量占位符替换敏感信息,占位符的环境变量名称根据 credential_name 命名。
|
|
85
|
+
```toml
|
|
86
|
+
// 修复前
|
|
87
|
+
password=123123123
|
|
88
|
+
// 修复后
|
|
89
|
+
password=${KL_PASSWORD}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
漏洞报告可能涉及多个文件,修复时按文件进行聚合,优先将一个文件中的漏洞全部修复完成再修复其他漏洞。
|
|
93
|
+
|
|
94
|
+
## 引入SDK
|
|
95
|
+
从第一个漏洞报告读取数据,如果 platform 为 4 时需要引入 keyless-sdk 并添加初始化代码。通过该 sdk 将凭证注入到环境变量中,然后其他位置的代码才能读取到环境变量。注意,只有平台为 4 时才需要引入 sdk,其他平台不需要。不同语言 sdk 及初始化代码添加方式如下。
|
|
96
|
+
|
|
97
|
+
#### java 语言
|
|
98
|
+
pom.xml 文件中添加 keyless-sdk 依赖,其中的注释需要保留。
|
|
99
|
+
```xml
|
|
100
|
+
<!-- keyless-sdk dependency -->
|
|
101
|
+
<dependency>
|
|
102
|
+
<groupId>com.baidu.xbu-data</groupId>
|
|
103
|
+
<artifactId>keyless-sdk</artifactId>
|
|
104
|
+
<version>1.0.2</version>
|
|
105
|
+
</dependency>
|
|
106
|
+
```
|
|
107
|
+
项目启动的函数中添加以下初始化代码,注意其中的 credentialID 需要与漏洞报告中的 credential_id 保持一致。
|
|
108
|
+
```java
|
|
109
|
+
import com.baidu.keyless.KeylessClient;
|
|
110
|
+
|
|
111
|
+
public class KeylessAppCredentialDemo {
|
|
112
|
+
public static void main(String[] args) {
|
|
113
|
+
// 初始化 keyless-sdk 开始
|
|
114
|
+
// step1: 设置 credentialID 构建 client,详情可参考 https://ku.baidu-int.com/d/BRnvQROwD8akQY
|
|
115
|
+
String credentialID = "appDemo_prod";
|
|
116
|
+
KeylessClient keylessClient = KeylessClient.builder()
|
|
117
|
+
.credentialId(credentialID)
|
|
118
|
+
.build();
|
|
119
|
+
// step2: 获取凭证信息并注入到系统环境变量中
|
|
120
|
+
try {
|
|
121
|
+
keylessClient.load();
|
|
122
|
+
} catch (Exception e) {
|
|
123
|
+
System.out.println("load keyless client error: " + e.getMessage());
|
|
124
|
+
}
|
|
125
|
+
// 初始化 keyless-sdk 结束
|
|
126
|
+
// 以下为其他原始代码
|
|
127
|
+
... ...
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
#### go 语言
|
|
133
|
+
go.mod 文件中添加 keyless-sdk 依赖,其中的注释需要保留。
|
|
134
|
+
```mod
|
|
135
|
+
require (
|
|
136
|
+
icode.baidu.com/baidu/xbu-data/things-go keyless-sdk-2.2.0 // keyless-sdk dependency
|
|
137
|
+
)
|
|
138
|
+
```
|
|
139
|
+
项目启动的函数中添加以下初始化代码,注意其中的 CredentialID 需要与漏洞报告中的 credential_id 保持一致。
|
|
140
|
+
```go
|
|
141
|
+
package main
|
|
142
|
+
|
|
143
|
+
import (
|
|
144
|
+
"icode.baidu.com/baidu/xbu-data/things-go/pkg/keyless"
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
func main() {
|
|
148
|
+
// 初始化 keyless-sdk 开始
|
|
149
|
+
// step1: 设置 CredentialID 构建 client,详情可参考 https://ku.baidu-int.com/d/G_QqzMQrQR9_K_
|
|
150
|
+
req := &keyless.GetCredentialReq{
|
|
151
|
+
CredentialID: "appDemo_prod", // 凭证标识
|
|
152
|
+
}
|
|
153
|
+
// step2: 获取凭证信息并注入到系统环境变量中
|
|
154
|
+
client := keyless.NewClient(req)
|
|
155
|
+
err := client.Load()
|
|
156
|
+
if err != nil {
|
|
157
|
+
fmt.Println(err.Error())
|
|
158
|
+
return
|
|
159
|
+
}
|
|
160
|
+
// 初始化 keyless-sdk 结束
|
|
161
|
+
// 以下为其他原始代码
|
|
162
|
+
... ...
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### python 语言
|
|
167
|
+
requirements.txt 文件中添加 keyless-sdk 依赖,其中的注释需要保留。
|
|
168
|
+
```txt
|
|
169
|
+
// keyless-sdk dependency
|
|
170
|
+
credential-vault-sdk>=0.1.12(Python3 版本添加此依赖)
|
|
171
|
+
credential-vault-sdk-py2>=0.1.9(Python2 版本添加此依赖)
|
|
172
|
+
```
|
|
173
|
+
初始化代码如下,注意
|
|
174
|
+
* 其中的 credential_id 需要与漏洞报告中的 credential_id 保持一致;
|
|
175
|
+
* 如果是完整的服务或项目,初始化代码要添加在项目启动的函数中;
|
|
176
|
+
* 如果是独立的 Python 脚本文件,每个独立的文件都要添加 keyless-sdk 依赖和初始化代码。
|
|
177
|
+
```python
|
|
178
|
+
from credential_vault_sdk.keyless_client import KeylessClient
|
|
179
|
+
|
|
180
|
+
def main():
|
|
181
|
+
# 初始化 keyless-sdk 开始
|
|
182
|
+
# step1: 设置 credential_id 构建 client,详情可参考 https://ku.baidu-int.com/d/HWaSIaQSThk88b
|
|
183
|
+
client = KeylessClient(credential_id="appDemo_prod")
|
|
184
|
+
# step2: 获取凭证信息并注入到系统环境变量中
|
|
185
|
+
client.load()
|
|
186
|
+
# 初始化 keyless-sdk 结束
|
|
187
|
+
# 以下为其他原始代码
|
|
188
|
+
... ...
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
#### C++ 语言
|
|
192
|
+
在文件开始引入头文件和命名空间,其中的注释需要保留:
|
|
193
|
+
```C++
|
|
194
|
+
// keyless-sdk dependency
|
|
195
|
+
#include "keyless/keyless_client.h"
|
|
196
|
+
|
|
197
|
+
using namespace baidu::credentialvault::cppsdk;
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
项目启动的函数中添加以下初始化代码,注意其中的 credentialID 需要与漏洞报告中的 credential_id 保持一致。
|
|
201
|
+
```C++
|
|
202
|
+
#include "keyless/keyless_client.h"
|
|
203
|
+
|
|
204
|
+
using namespace baidu::credentialvault::cppsdk;
|
|
205
|
+
|
|
206
|
+
int main(int argc, char *argv[]) {
|
|
207
|
+
// 初始化 keyless-sdk 开始
|
|
208
|
+
// step1: 设置 credential_id 构建 client,详情可参考 https://ku.baidu-int.com/d/x5s_A3YHll0PvR
|
|
209
|
+
KeylessClient client = KeylessClient("appDemo_prod");
|
|
210
|
+
// step2: 获取凭证信息并注入到系统环境变量中
|
|
211
|
+
KeylessStatus load_result = client.load();
|
|
212
|
+
if (!load_result.ok()) {
|
|
213
|
+
return 0;
|
|
214
|
+
}
|
|
215
|
+
// 初始化 keyless-sdk 结束
|
|
216
|
+
// 以下为其他原始代码
|
|
217
|
+
... ...
|
|
218
|
+
}
|
|
219
|
+
```
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
凭证托管工具 - 将凭证托管到第三方平台(iPipe/CNAP/datamanage)。
|
|
4
|
+
|
|
5
|
+
用法:
|
|
6
|
+
python3 credential_hosting.py --poll-result <poll_result.json路径> --username <用户名>
|
|
7
|
+
|
|
8
|
+
也可手动指定各参数(不推荐,仅向后兼容):
|
|
9
|
+
python3 credential_hosting.py --chat-id <chatID> --platform <platformName> --username <用户名> --credentials '<JSON>'
|
|
10
|
+
|
|
11
|
+
使用 --poll-result 时,自动从 credential_poll.py 的输出文件中提取 chatUUID、deployment.platformName、data.credentials。
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import argparse
|
|
15
|
+
import json
|
|
16
|
+
import logging
|
|
17
|
+
import sys
|
|
18
|
+
|
|
19
|
+
import http_client # noqa: F401 (triggers shared logging config)
|
|
20
|
+
import utils
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger("credential_hosting")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def hosting_credentials(chat_id, platform, credentials, username, user_id):
|
|
26
|
+
"""将凭证托管到指定平台。"""
|
|
27
|
+
url = "{}/api/v1/deployments/{}/credentials".format(utils.HOST, platform)
|
|
28
|
+
payload = {
|
|
29
|
+
"configs": credentials,
|
|
30
|
+
"scanChatID": chat_id,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
logger.info("credential_hosting: chat_id=%s, platform=%s, credentials_count=%d",
|
|
34
|
+
chat_id, platform, len(credentials))
|
|
35
|
+
print("正在托管凭证到 {} 平台...".format(platform), file=sys.stderr)
|
|
36
|
+
return http_client.post(
|
|
37
|
+
url,
|
|
38
|
+
headers=utils.build_headers(username, user_id, chat_id),
|
|
39
|
+
json_body=payload,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def main():
|
|
44
|
+
parser = argparse.ArgumentParser(description="凭证托管工具")
|
|
45
|
+
parser.add_argument("--poll-result", default=None, help="credential_poll.py 输出的结果文件路径,自动提取所需参数")
|
|
46
|
+
parser.add_argument("--chat-id", default=None, help="scanChatID(使用 --poll-result 时可省略)")
|
|
47
|
+
parser.add_argument("--platform", default=None, help="托管平台名称(使用 --poll-result 时可省略)")
|
|
48
|
+
parser.add_argument("--username", required=True, help="Comate 用户名")
|
|
49
|
+
parser.add_argument("--credentials", default=None, help="凭证配置 JSON(使用 --poll-result 时可省略)")
|
|
50
|
+
args = parser.parse_args()
|
|
51
|
+
|
|
52
|
+
user_id = utils.make_user_id(args.username)
|
|
53
|
+
|
|
54
|
+
chat_id = args.chat_id
|
|
55
|
+
platform = args.platform
|
|
56
|
+
credentials = None
|
|
57
|
+
|
|
58
|
+
if args.poll_result:
|
|
59
|
+
# 从 poll_result 文件提取所有参数
|
|
60
|
+
try:
|
|
61
|
+
with open(args.poll_result, "r", encoding="utf-8") as f:
|
|
62
|
+
poll_data = json.load(f)
|
|
63
|
+
except Exception as e:
|
|
64
|
+
print("错误: 读取 poll-result 失败 {}: {}".format(args.poll_result, e), file=sys.stderr)
|
|
65
|
+
sys.exit(1)
|
|
66
|
+
|
|
67
|
+
chat_id = chat_id or poll_data.get("chatUUID", "")
|
|
68
|
+
data = poll_data.get("data", {})
|
|
69
|
+
platform = platform or data.get("deployment", {}).get("platformName", "")
|
|
70
|
+
credentials = data.get("credentials")
|
|
71
|
+
elif args.credentials:
|
|
72
|
+
try:
|
|
73
|
+
credentials = json.loads(args.credentials)
|
|
74
|
+
except json.JSONDecodeError as e:
|
|
75
|
+
print("错误: 凭证 JSON 解析失败: {}".format(e), file=sys.stderr)
|
|
76
|
+
sys.exit(1)
|
|
77
|
+
|
|
78
|
+
if not chat_id or not platform or credentials is None:
|
|
79
|
+
print("错误: 缺少必要参数。使用 --poll-result 或同时提供 --chat-id、--platform、--credentials", file=sys.stderr)
|
|
80
|
+
sys.exit(1)
|
|
81
|
+
|
|
82
|
+
result = hosting_credentials(chat_id, platform, credentials, args.username, user_id)
|
|
83
|
+
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
if __name__ == "__main__":
|
|
87
|
+
main()
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
凭证配置轮询工具 - 通过 WebSocket 监听凭证托管助手网页的配置结果。
|
|
4
|
+
|
|
5
|
+
用法:
|
|
6
|
+
python3 credential_poll.py --chat-id <chatID> --username <用户名>
|
|
7
|
+
|
|
8
|
+
流程:
|
|
9
|
+
1. 连接 WebSocket 端点
|
|
10
|
+
2. 等待用户在网页完成凭证配置并点击「生成代码」
|
|
11
|
+
3. 收到 msgType=repair 消息后输出配置数据
|
|
12
|
+
|
|
13
|
+
注意: 使用 Python 标准库实现 WebSocket 客户端,无需第三方依赖。
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import argparse
|
|
17
|
+
import base64
|
|
18
|
+
import json
|
|
19
|
+
import logging
|
|
20
|
+
import os
|
|
21
|
+
import socket
|
|
22
|
+
import struct
|
|
23
|
+
import sys
|
|
24
|
+
import time
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
import ssl
|
|
28
|
+
_SSL_AVAILABLE = True
|
|
29
|
+
except ImportError:
|
|
30
|
+
_SSL_AVAILABLE = False
|
|
31
|
+
|
|
32
|
+
# 触发共享日志配置
|
|
33
|
+
import http_client # noqa: F401
|
|
34
|
+
import utils
|
|
35
|
+
|
|
36
|
+
logger = logging.getLogger("credential_poll")
|
|
37
|
+
|
|
38
|
+
WS_TIMEOUT = 30 * 60 # 30 分钟超时
|
|
39
|
+
RECV_TIMEOUT = 5 # 每次 recv 超时秒数
|
|
40
|
+
MAX_RECONNECT = 10
|
|
41
|
+
RECONNECT_BASE_INTERVAL = 1 # 秒
|
|
42
|
+
|
|
43
|
+
# WebSocket opcodes
|
|
44
|
+
OP_TEXT = 0x1
|
|
45
|
+
OP_CLOSE = 0x8
|
|
46
|
+
OP_PING = 0x9
|
|
47
|
+
OP_PONG = 0xA
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _generate_ws_key():
|
|
51
|
+
"""生成随机的 Sec-WebSocket-Key。"""
|
|
52
|
+
return base64.b64encode(os.urandom(16)).decode("ascii")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _ws_connect(host, port, path, headers, use_ssl=True, timeout=10):
|
|
56
|
+
"""
|
|
57
|
+
建立 WebSocket 连接,返回 socket 对象。
|
|
58
|
+
执行 HTTP Upgrade 握手,验证 101 响应。
|
|
59
|
+
"""
|
|
60
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
61
|
+
sock.settimeout(timeout)
|
|
62
|
+
|
|
63
|
+
if use_ssl:
|
|
64
|
+
if not _SSL_AVAILABLE:
|
|
65
|
+
raise RuntimeError(
|
|
66
|
+
"WebSocket 需要 SSL 但 ssl 模块不可用。"
|
|
67
|
+
"请安装 libssl1.1 或重新编译 Python 以支持 ssl。"
|
|
68
|
+
)
|
|
69
|
+
ctx = ssl.create_default_context()
|
|
70
|
+
ctx.check_hostname = False
|
|
71
|
+
ctx.verify_mode = ssl.CERT_NONE
|
|
72
|
+
sock = ctx.wrap_socket(sock, server_hostname=host)
|
|
73
|
+
|
|
74
|
+
sock.connect((host, port))
|
|
75
|
+
|
|
76
|
+
# 构造握手请求
|
|
77
|
+
ws_key = _generate_ws_key()
|
|
78
|
+
lines = [
|
|
79
|
+
"GET {} HTTP/1.1".format(path),
|
|
80
|
+
"Host: {}".format(host),
|
|
81
|
+
"Upgrade: websocket",
|
|
82
|
+
"Connection: Upgrade",
|
|
83
|
+
"Sec-WebSocket-Version: 13",
|
|
84
|
+
"Sec-WebSocket-Key: {}".format(ws_key),
|
|
85
|
+
]
|
|
86
|
+
for k, v in headers.items():
|
|
87
|
+
lines.append("{}: {}".format(k, v))
|
|
88
|
+
lines.append("")
|
|
89
|
+
lines.append("")
|
|
90
|
+
|
|
91
|
+
request = "\r\n".join(lines)
|
|
92
|
+
sock.sendall(request.encode("utf-8"))
|
|
93
|
+
|
|
94
|
+
# 读取响应头
|
|
95
|
+
response = b""
|
|
96
|
+
while b"\r\n\r\n" not in response:
|
|
97
|
+
chunk = sock.recv(4096)
|
|
98
|
+
if not chunk:
|
|
99
|
+
raise ConnectionError("连接在握手期间被关闭")
|
|
100
|
+
response += chunk
|
|
101
|
+
|
|
102
|
+
header_end = response.index(b"\r\n\r\n")
|
|
103
|
+
header_part = response[:header_end].decode("utf-8")
|
|
104
|
+
status_line = header_part.split("\r\n")[0]
|
|
105
|
+
|
|
106
|
+
# 验证 101 状态码
|
|
107
|
+
parts = status_line.split(" ", 2)
|
|
108
|
+
status_code = int(parts[1])
|
|
109
|
+
if status_code != 101:
|
|
110
|
+
# 尝试读取 body 获取错误信息
|
|
111
|
+
body_part = response[header_end + 4:]
|
|
112
|
+
error_msg = body_part.decode("utf-8", errors="replace") if body_part else ""
|
|
113
|
+
raise ConnectionError("WebSocket 握手失败: HTTP {} - {}".format(status_code, error_msg))
|
|
114
|
+
|
|
115
|
+
return sock
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _ws_recv_frame(sock):
|
|
119
|
+
"""
|
|
120
|
+
接收一个 WebSocket 帧,返回 (opcode, payload_bytes)。
|
|
121
|
+
"""
|
|
122
|
+
def _recv_exact(n):
|
|
123
|
+
data = b""
|
|
124
|
+
while len(data) < n:
|
|
125
|
+
chunk = sock.recv(n - len(data))
|
|
126
|
+
if not chunk:
|
|
127
|
+
raise ConnectionError("连接被关闭")
|
|
128
|
+
data += chunk
|
|
129
|
+
return data
|
|
130
|
+
|
|
131
|
+
# 读取 2 字节头
|
|
132
|
+
head = _recv_exact(2)
|
|
133
|
+
opcode = head[0] & 0x0F
|
|
134
|
+
masked = (head[1] & 0x80) != 0
|
|
135
|
+
payload_len = head[1] & 0x7F
|
|
136
|
+
|
|
137
|
+
# 扩展长度
|
|
138
|
+
if payload_len == 126:
|
|
139
|
+
payload_len = struct.unpack("!H", _recv_exact(2))[0]
|
|
140
|
+
elif payload_len == 127:
|
|
141
|
+
payload_len = struct.unpack("!Q", _recv_exact(8))[0]
|
|
142
|
+
|
|
143
|
+
# mask (服务端发给客户端通常不 mask,但保留处理)
|
|
144
|
+
mask_key = None
|
|
145
|
+
if masked:
|
|
146
|
+
mask_key = _recv_exact(4)
|
|
147
|
+
|
|
148
|
+
# payload
|
|
149
|
+
payload = _recv_exact(payload_len)
|
|
150
|
+
|
|
151
|
+
if mask_key:
|
|
152
|
+
payload = bytes(b ^ mask_key[i % 4] for i, b in enumerate(payload))
|
|
153
|
+
|
|
154
|
+
return opcode, payload
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _ws_send_frame(sock, opcode, payload=b""):
|
|
158
|
+
"""
|
|
159
|
+
发送一个 WebSocket 帧(客户端发送需要 mask)。
|
|
160
|
+
"""
|
|
161
|
+
mask_key = os.urandom(4)
|
|
162
|
+
masked_payload = bytes(b ^ mask_key[i % 4] for i, b in enumerate(payload))
|
|
163
|
+
|
|
164
|
+
frame = bytearray()
|
|
165
|
+
frame.append(0x80 | opcode) # FIN + opcode
|
|
166
|
+
|
|
167
|
+
length = len(payload)
|
|
168
|
+
if length < 126:
|
|
169
|
+
frame.append(0x80 | length) # MASK bit + length
|
|
170
|
+
elif length < 65536:
|
|
171
|
+
frame.append(0x80 | 126)
|
|
172
|
+
frame.extend(struct.pack("!H", length))
|
|
173
|
+
else:
|
|
174
|
+
frame.append(0x80 | 127)
|
|
175
|
+
frame.extend(struct.pack("!Q", length))
|
|
176
|
+
|
|
177
|
+
frame.extend(mask_key)
|
|
178
|
+
frame.extend(masked_payload)
|
|
179
|
+
|
|
180
|
+
sock.sendall(bytes(frame))
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _ws_send_pong(sock, payload=b""):
|
|
184
|
+
"""回复 pong 帧。"""
|
|
185
|
+
_ws_send_frame(sock, OP_PONG, payload)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _ws_send_close(sock, code=1000, reason=""):
|
|
189
|
+
"""发送 close 帧。"""
|
|
190
|
+
payload = struct.pack("!H", code) + reason.encode("utf-8")
|
|
191
|
+
_ws_send_frame(sock, OP_CLOSE, payload)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _parse_ws_url(url):
|
|
195
|
+
"""解析 wss://host/path,返回 (host, port, path, use_ssl)。"""
|
|
196
|
+
if url.startswith("wss://"):
|
|
197
|
+
use_ssl = True
|
|
198
|
+
default_port = 443
|
|
199
|
+
rest = url[6:]
|
|
200
|
+
elif url.startswith("ws://"):
|
|
201
|
+
use_ssl = False
|
|
202
|
+
default_port = 80
|
|
203
|
+
rest = url[5:]
|
|
204
|
+
else:
|
|
205
|
+
raise ValueError("不支持的 URL scheme: {}".format(url))
|
|
206
|
+
|
|
207
|
+
if "/" in rest:
|
|
208
|
+
host_port, path = rest.split("/", 1)
|
|
209
|
+
path = "/" + path
|
|
210
|
+
else:
|
|
211
|
+
host_port = rest
|
|
212
|
+
path = "/"
|
|
213
|
+
|
|
214
|
+
if ":" in host_port:
|
|
215
|
+
host, port_str = host_port.split(":", 1)
|
|
216
|
+
port = int(port_str)
|
|
217
|
+
else:
|
|
218
|
+
host = host_port
|
|
219
|
+
port = default_port
|
|
220
|
+
|
|
221
|
+
return host, port, path, use_ssl
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def poll_credential_config(chat_id, user_id):
|
|
225
|
+
"""通过 WebSocket 监听凭证配置,返回配置数据 dict 或 None。"""
|
|
226
|
+
url = "{}/api/v1/ws/credential/events".format(utils.WS_HOST)
|
|
227
|
+
host, port, path, use_ssl = _parse_ws_url(url)
|
|
228
|
+
logger.info("credential_poll start: chat_id=%s", chat_id)
|
|
229
|
+
|
|
230
|
+
ws_headers = {
|
|
231
|
+
"SAST-Chat-ID": chat_id,
|
|
232
|
+
"Comate-User-Id": user_id,
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
reconnect_attempts = 0
|
|
236
|
+
start_time = time.time()
|
|
237
|
+
|
|
238
|
+
while time.time() - start_time < WS_TIMEOUT:
|
|
239
|
+
sock = None
|
|
240
|
+
try:
|
|
241
|
+
sock = _ws_connect(host, port, path, ws_headers, use_ssl, timeout=RECV_TIMEOUT)
|
|
242
|
+
print("WebSocket 已连接,等待凭证配置...", file=sys.stderr)
|
|
243
|
+
reconnect_attempts = 0 # 连接成功,重置重连计数
|
|
244
|
+
|
|
245
|
+
while time.time() - start_time < WS_TIMEOUT:
|
|
246
|
+
try:
|
|
247
|
+
opcode, payload = _ws_recv_frame(sock)
|
|
248
|
+
|
|
249
|
+
if opcode == OP_TEXT:
|
|
250
|
+
msg = payload.decode("utf-8")
|
|
251
|
+
if not msg:
|
|
252
|
+
continue
|
|
253
|
+
data = json.loads(msg)
|
|
254
|
+
msg_type = data.get("msgType", "")
|
|
255
|
+
|
|
256
|
+
if msg_type == "repair":
|
|
257
|
+
print("收到凭证配置数据", file=sys.stderr)
|
|
258
|
+
logger.info("credential_poll received repair data")
|
|
259
|
+
try:
|
|
260
|
+
_ws_send_close(sock)
|
|
261
|
+
except Exception:
|
|
262
|
+
pass
|
|
263
|
+
return data
|
|
264
|
+
elif msg_type == "openFile":
|
|
265
|
+
continue
|
|
266
|
+
|
|
267
|
+
elif opcode == OP_PING:
|
|
268
|
+
_ws_send_pong(sock, payload)
|
|
269
|
+
|
|
270
|
+
elif opcode == OP_CLOSE:
|
|
271
|
+
print("服务端关闭连接,尝试重连...", file=sys.stderr)
|
|
272
|
+
break
|
|
273
|
+
|
|
274
|
+
except socket.timeout:
|
|
275
|
+
# recv 超时,继续等待
|
|
276
|
+
continue
|
|
277
|
+
except (ConnectionError, OSError):
|
|
278
|
+
print("WebSocket 连接断开,尝试重连...", file=sys.stderr)
|
|
279
|
+
break
|
|
280
|
+
except ValueError as e:
|
|
281
|
+
print("JSON 解析失败: {}".format(e), file=sys.stderr)
|
|
282
|
+
continue
|
|
283
|
+
|
|
284
|
+
except ConnectionError as e:
|
|
285
|
+
err_msg = str(e)
|
|
286
|
+
print("WebSocket 连接失败: {}".format(err_msg), file=sys.stderr)
|
|
287
|
+
# 400004 表示会话不存在(用户还未打开网页),继续重试等待用户打开
|
|
288
|
+
# 其他业务错误直接退出
|
|
289
|
+
if "400004" in err_msg:
|
|
290
|
+
print("会话不存在,等待用户打开凭证托管网页...", file=sys.stderr)
|
|
291
|
+
elif "HTTP 200" in err_msg or "400" in err_msg:
|
|
292
|
+
print("服务端拒绝连接,请检查 chatID 是否有效", file=sys.stderr)
|
|
293
|
+
return None
|
|
294
|
+
|
|
295
|
+
except Exception as e:
|
|
296
|
+
print("WebSocket 连接失败: {}".format(e), file=sys.stderr)
|
|
297
|
+
|
|
298
|
+
finally:
|
|
299
|
+
if sock:
|
|
300
|
+
try:
|
|
301
|
+
sock.close()
|
|
302
|
+
except Exception:
|
|
303
|
+
pass
|
|
304
|
+
|
|
305
|
+
# 重连逻辑:指数退避
|
|
306
|
+
reconnect_attempts += 1
|
|
307
|
+
if reconnect_attempts > MAX_RECONNECT:
|
|
308
|
+
print("达到最大重连次数,退出", file=sys.stderr)
|
|
309
|
+
return None
|
|
310
|
+
|
|
311
|
+
delay = RECONNECT_BASE_INTERVAL * (2 ** (reconnect_attempts - 1))
|
|
312
|
+
print("{}秒后重连 (第{}/{})次...".format(delay, reconnect_attempts, MAX_RECONNECT), file=sys.stderr)
|
|
313
|
+
time.sleep(delay)
|
|
314
|
+
|
|
315
|
+
print("超时,未收到凭证配置", file=sys.stderr)
|
|
316
|
+
logger.warning("credential_poll timeout: chat_id=%s", chat_id)
|
|
317
|
+
return None
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def main():
|
|
321
|
+
parser = argparse.ArgumentParser(description="凭证配置轮询工具")
|
|
322
|
+
parser.add_argument("--chat-id", required=True, help="scanChatID,用于关联扫描会话")
|
|
323
|
+
parser.add_argument("--username", required=True, help="Comate 用户名")
|
|
324
|
+
parser.add_argument("--output", default=None, help="将结果保存到指定文件路径(同时仍输出到标准输出)")
|
|
325
|
+
args = parser.parse_args()
|
|
326
|
+
|
|
327
|
+
user_id = utils.make_user_id(args.username)
|
|
328
|
+
|
|
329
|
+
result = poll_credential_config(args.chat_id, user_id)
|
|
330
|
+
if result:
|
|
331
|
+
output_json = json.dumps(result, ensure_ascii=False, indent=2)
|
|
332
|
+
print(output_json)
|
|
333
|
+
if args.output:
|
|
334
|
+
output_dir = os.path.dirname(os.path.abspath(args.output))
|
|
335
|
+
os.makedirs(output_dir, exist_ok=True)
|
|
336
|
+
with open(args.output, "w", encoding="utf-8") as f:
|
|
337
|
+
f.write(output_json)
|
|
338
|
+
print("已保存: {}".format(args.output), file=sys.stderr)
|
|
339
|
+
else:
|
|
340
|
+
print(json.dumps({"error": "未收到凭证配置数据"}, ensure_ascii=False))
|
|
341
|
+
sys.exit(1)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
if __name__ == "__main__":
|
|
345
|
+
main()
|