@comate/zulu 1.2.1-beta.2 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/comate-engine/assets/skills/auto-commit-comate/SKILL.md +260 -0
  2. package/comate-engine/assets/skills/auto-commit-comate/references/data_structures.md +189 -0
  3. package/comate-engine/assets/skills/auto-commit-comate/references/new_version_instruction.md +209 -0
  4. package/comate-engine/assets/skills/auto-commit-comate/references/old_version_instruction.md +208 -0
  5. package/comate-engine/assets/skills/auto-commit-comate/scripts/git_diff_cli.py +196 -0
  6. package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/git_utils.py +20 -10
  7. package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/icafe/client.py +69 -40
  8. package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/icafe/farseer.py +8 -9
  9. package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/icafe/matching.py +65 -9
  10. package/comate-engine/assets/skills/auto-commit-comate/scripts/match_card_cli.py +37 -0
  11. package/comate-engine/assets/skills/cnap-comate/SKILL.md +157 -0
  12. package/comate-engine/assets/skills/cnap-comate/references/cases.md +198 -0
  13. package/comate-engine/assets/skills/cnap-comate/references/deploy-troubleshoot.md +15 -0
  14. package/comate-engine/assets/skills/cnap-comate/references/install.md +43 -0
  15. package/comate-engine/assets/skills/cnap-comate/references/kubectl.md +55 -0
  16. package/comate-engine/assets/skills/cnap-comate/references/login.md +125 -0
  17. package/comate-engine/assets/skills/cnap-comate/references/oncall.md +24 -0
  18. package/comate-engine/assets/skills/cnap-comate/scripts/install_cnap_cli.sh +36 -0
  19. package/comate-engine/assets/skills/code-security/SKILL.md +176 -0
  20. package/comate-engine/assets/skills/code-security/references/credential_hosting.md +102 -0
  21. package/comate-engine/assets/skills/code-security/references/vul_repair_sensitive.md +219 -0
  22. package/comate-engine/assets/skills/code-security/scripts/build_repair_info.py +0 -0
  23. package/comate-engine/assets/skills/code-security/scripts/credential_hosting.py +99 -0
  24. package/comate-engine/assets/skills/code-security/scripts/credential_poll.py +350 -0
  25. package/comate-engine/assets/skills/code-security/scripts/http_client.py +173 -0
  26. package/comate-engine/assets/skills/code-security/scripts/parse_scan_result.py +301 -0
  27. package/comate-engine/assets/skills/code-security/scripts/repair_vulnerability.py +261 -0
  28. package/comate-engine/assets/skills/code-security/scripts/report_chat.py +198 -0
  29. package/comate-engine/assets/skills/code-security/scripts/scan_vulnerability.py +316 -0
  30. package/comate-engine/assets/skills/code-security-comate/SKILL.md +219 -0
  31. package/comate-engine/assets/skills/code-security-comate/references/credential_hosting.md +102 -0
  32. package/comate-engine/assets/skills/code-security-comate/references/vul_repair-go_sql_injection.md +399 -0
  33. package/comate-engine/assets/skills/code-security-comate/references/vul_repair-java_sql_injection.md +591 -0
  34. package/comate-engine/assets/skills/code-security-comate/references/vul_repair-php_sql_injection.md +318 -0
  35. package/comate-engine/assets/skills/code-security-comate/references/vul_repair-python_sql_injection.md +198 -0
  36. package/comate-engine/assets/skills/code-security-comate/references/vul_repair_sensitive.md +219 -0
  37. package/comate-engine/assets/skills/code-security-comate/scripts/credential_hosting.py +87 -0
  38. package/comate-engine/assets/skills/code-security-comate/scripts/credential_poll.py +345 -0
  39. package/comate-engine/assets/skills/code-security-comate/scripts/http_client.py +173 -0
  40. package/comate-engine/assets/skills/code-security-comate/scripts/parse_scan_result.py +392 -0
  41. package/comate-engine/assets/skills/code-security-comate/scripts/repair_vulnerability.py +245 -0
  42. package/comate-engine/assets/skills/code-security-comate/scripts/report_chat.py +145 -0
  43. package/comate-engine/assets/skills/code-security-comate/scripts/scan_vulnerability.py +444 -0
  44. package/comate-engine/assets/skills/code-security-comate/scripts/utils.py +153 -0
  45. package/comate-engine/assets/skills/comate-docs-comate/SKILL.md +148 -0
  46. package/comate-engine/assets/skills/comate-docs-comate/references/doc-map-extended.md +78 -0
  47. package/comate-engine/assets/skills/comate-docs-comate/references/models-and-billing.md +51 -0
  48. package/comate-engine/assets/skills/comate-docs-comate/references/product-overview.md +73 -0
  49. package/comate-engine/assets/skills/comate-docs-comate/references/query_content.md +83 -0
  50. package/comate-engine/assets/skills/comate-docs-comate/references/query_repo.md +57 -0
  51. package/comate-engine/assets/skills/comate-docs-comate/scripts/ku_operator.py +1575 -0
  52. package/comate-engine/assets/skills/create-image-comate/SKILL.md +278 -0
  53. package/comate-engine/assets/skills/create-skill-comate/SKILL.md +308 -217
  54. package/comate-engine/assets/skills/create-skill-comate/agents/analyzer.md +274 -0
  55. package/comate-engine/assets/skills/create-skill-comate/agents/comparator.md +202 -0
  56. package/comate-engine/assets/skills/create-skill-comate/agents/grader.md +223 -0
  57. package/comate-engine/assets/skills/create-skill-comate/assets/eval_review.html +146 -0
  58. package/comate-engine/assets/skills/create-skill-comate/eval-viewer/generate_review.py +489 -0
  59. package/comate-engine/assets/skills/create-skill-comate/eval-viewer/viewer.html +1325 -0
  60. package/comate-engine/assets/skills/create-skill-comate/references/schemas.md +430 -0
  61. package/comate-engine/assets/skills/create-skill-comate/scripts/__init__.py +0 -0
  62. package/comate-engine/assets/skills/create-skill-comate/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  63. package/comate-engine/assets/skills/create-skill-comate/scripts/__pycache__/aggregate_benchmark.cpython-311.pyc +0 -0
  64. package/comate-engine/assets/skills/create-skill-comate/scripts/aggregate_benchmark.py +412 -0
  65. package/comate-engine/assets/skills/create-skill-comate/scripts/generate_report.py +334 -0
  66. package/comate-engine/assets/skills/create-skill-comate/scripts/package_skill.py +140 -0
  67. package/comate-engine/assets/skills/create-skill-comate/scripts/utils.py +53 -0
  68. package/comate-engine/assets/skills/find-skills-comate/SKILL.md +15 -12
  69. package/comate-engine/assets/skills/find-skills-comate/scripts/fetch_skills.py +32 -3
  70. package/comate-engine/assets/skills/get-ugate-token-comate/SKILL.md +159 -0
  71. package/comate-engine/assets/skills/get-ugate-token-comate/getUgateToken.py +150 -0
  72. package/comate-engine/assets/skills/icafe-comate/SKILL.md +240 -0
  73. package/comate-engine/assets/skills/icafe-comate/references/ai-workflows.md +233 -0
  74. package/comate-engine/assets/skills/icafe-comate/references/commands.md +1147 -0
  75. package/comate-engine/assets/skills/icafe-comate/references/error-handling.md +164 -0
  76. package/comate-engine/assets/skills/icafe-comate/references/git-auto-bindcard-workflow.md +201 -0
  77. package/comate-engine/assets/skills/icafe-comate/references/git-bindcard-workflow.md +327 -0
  78. package/comate-engine/assets/skills/icafe-comate/references/iql-syntax.md +327 -0
  79. package/comate-engine/assets/skills/icafe-comate/references/platform-concepts.md +317 -0
  80. package/comate-engine/assets/skills/icafe-comate/references/smart-create-workflow.md +171 -0
  81. package/comate-engine/assets/skills/icafe-comate/references/smart-find-workflow.md +127 -0
  82. package/comate-engine/assets/skills/icafe-comate/references/smart-update-workflow.md +118 -0
  83. package/comate-engine/assets/skills/icode-comate/SKILL.md +366 -0
  84. package/comate-engine/assets/skills/icode-comate/references/api/add_reviewers.md +44 -0
  85. package/comate-engine/assets/skills/icode-comate/references/api/build_fetch_command.md +89 -0
  86. package/comate-engine/assets/skills/icode-comate/references/api/check_repo_permission.md +89 -0
  87. package/comate-engine/assets/skills/icode-comate/references/api/create_branch.md +79 -0
  88. package/comate-engine/assets/skills/icode-comate/references/api/create_draft_comment.md +109 -0
  89. package/comate-engine/assets/skills/icode-comate/references/api/get_ai_cr_result.md +190 -0
  90. package/comate-engine/assets/skills/icode-comate/references/api/get_ai_review.md +97 -0
  91. package/comate-engine/assets/skills/icode-comate/references/api/get_diff_content.md +92 -0
  92. package/comate-engine/assets/skills/icode-comate/references/api/get_diff_file.md +88 -0
  93. package/comate-engine/assets/skills/icode-comate/references/api/get_machine_check.md +73 -0
  94. package/comate-engine/assets/skills/icode-comate/references/api/get_my_reviews.md +115 -0
  95. package/comate-engine/assets/skills/icode-comate/references/api/get_person_commit.md +89 -0
  96. package/comate-engine/assets/skills/icode-comate/references/api/get_person_repo.md +63 -0
  97. package/comate-engine/assets/skills/icode-comate/references/api/get_repo_branch.md +62 -0
  98. package/comate-engine/assets/skills/icode-comate/references/api/get_repo_config.md +91 -0
  99. package/comate-engine/assets/skills/icode-comate/references/api/get_repo_members.md +118 -0
  100. package/comate-engine/assets/skills/icode-comate/references/api/get_repo_reviews.md +91 -0
  101. package/comate-engine/assets/skills/icode-comate/references/api/get_review_comments.md +87 -0
  102. package/comate-engine/assets/skills/icode-comate/references/api/get_review_info.md +81 -0
  103. package/comate-engine/assets/skills/icode-comate/references/api/get_submit_settings.md +105 -0
  104. package/comate-engine/assets/skills/icode-comate/references/api/icode-api.md +86 -0
  105. package/comate-engine/assets/skills/icode-comate/references/api/publish_comments.md +72 -0
  106. package/comate-engine/assets/skills/icode-comate/references/api/set_review_score.md +58 -0
  107. package/comate-engine/assets/skills/icode-comate/references/api/start_ai_review.md +77 -0
  108. package/comate-engine/assets/skills/icode-comate/references/api/submit_review.md +50 -0
  109. package/comate-engine/assets/skills/icode-comate/references/api/trigger_ai_cr.md +63 -0
  110. package/comate-engine/assets/skills/icode-comate/references/feature/add-reviewer.md +92 -0
  111. package/comate-engine/assets/skills/icode-comate/references/feature/fix-machine-check.md +144 -0
  112. package/comate-engine/assets/skills/icode-comate/references/feature/merge-cr.md +100 -0
  113. package/comate-engine/assets/skills/icode-comate/references/feature/ssh-setup.md +106 -0
  114. package/comate-engine/assets/skills/icode-comate/references/feature/submit-acr.md +135 -0
  115. package/comate-engine/assets/skills/icode-comate/references/feature/submit-cr.md +123 -0
  116. package/comate-engine/assets/skills/icode-comate/references/git/clone.md +67 -0
  117. package/comate-engine/assets/skills/icode-comate/references/git/icode-git.md +68 -0
  118. package/comate-engine/assets/skills/icode-comate/references/git/push.md +64 -0
  119. package/comate-engine/assets/skills/icode-comate/references/git/push_cr.md +103 -0
  120. package/comate-engine/assets/skills/icode-comate/references/install.md +144 -0
  121. package/comate-engine/assets/skills/icode-comate/references/login.md +111 -0
  122. package/comate-engine/assets/skills/icode-comate/scripts/add-reviewer.sh +154 -0
  123. package/comate-engine/assets/skills/icode-comate/scripts/common.sh +145 -0
  124. package/comate-engine/assets/skills/icode-comate/scripts/fix-machine-check.sh +131 -0
  125. package/comate-engine/assets/skills/icode-comate/scripts/merge-cr.sh +105 -0
  126. package/comate-engine/assets/skills/icode-comate/scripts/ssh-setup.sh +159 -0
  127. package/comate-engine/assets/skills/icode-comate/scripts/submit-acr.sh +236 -0
  128. package/comate-engine/assets/skills/icode-comate/scripts/submit-cr.sh +104 -0
  129. package/comate-engine/assets/skills/icode-comate/scripts/test-preflight.sh +89 -0
  130. package/comate-engine/assets/skills/ku-operator-comate/SKILL.md +121 -0
  131. package/comate-engine/assets/skills/ku-operator-comate/examples.md +190 -0
  132. package/comate-engine/assets/skills/ku-operator-comate/references/add_member.md +49 -0
  133. package/comate-engine/assets/skills/ku-operator-comate/references/change_scope.md +38 -0
  134. package/comate-engine/assets/skills/ku-operator-comate/references/copy_doc.md +50 -0
  135. package/comate-engine/assets/skills/ku-operator-comate/references/create_doc.md +61 -0
  136. package/comate-engine/assets/skills/ku-operator-comate/references/delete_doc.md +31 -0
  137. package/comate-engine/assets/skills/ku-operator-comate/references/edit_content.md +568 -0
  138. package/comate-engine/assets/skills/ku-operator-comate/references/move_doc.md +45 -0
  139. package/comate-engine/assets/skills/ku-operator-comate/references/query_comment.md +79 -0
  140. package/comate-engine/assets/skills/ku-operator-comate/references/query_content.md +83 -0
  141. package/comate-engine/assets/skills/ku-operator-comate/references/query_flowchart.md +84 -0
  142. package/comate-engine/assets/skills/ku-operator-comate/references/query_permission.md +38 -0
  143. package/comate-engine/assets/skills/ku-operator-comate/references/query_recent_view.md +67 -0
  144. package/comate-engine/assets/skills/ku-operator-comate/references/query_repo.md +57 -0
  145. package/comate-engine/assets/skills/ku-operator-comate/references/query_user_info.md +37 -0
  146. package/comate-engine/assets/skills/ku-operator-comate/references/update_member.md +41 -0
  147. package/comate-engine/assets/skills/ku-operator-comate/references/upload_attachment.md +52 -0
  148. package/comate-engine/assets/skills/ku-operator-comate/scripts/ku_operator.py +1575 -0
  149. package/comate-engine/node_modules/better-sqlite3/node_modules/.bin/prebuild-install +2 -2
  150. package/comate-engine/node_modules/tree-sitter-bash/node_modules/.bin/node-gyp-build +2 -2
  151. package/comate-engine/node_modules/tree-sitter-bash/node_modules/.bin/node-gyp-build-optional +2 -2
  152. package/comate-engine/node_modules/tree-sitter-bash/node_modules/.bin/node-gyp-build-test +2 -2
  153. package/comate-engine/package.json +2 -0
  154. package/comate-engine/server.js +170 -46
  155. package/dist/bundle/index.js +8 -8
  156. package/package.json +1 -1
  157. package/comate-engine/assets/skills/figma2code-comate/codeConnect.md +0 -37
  158. package/comate-engine/assets/skills/figma2code-comate/designToken.md +0 -3
  159. package/comate-engine/assets/skills/figma2code-comate/f2cMcp.md +0 -59
  160. package/comate-engine/assets/skills/smart-commit/SKILL.md +0 -646
  161. package/comate-engine/node_modules/@comate/plugin-host/dist/index-AZIho4HV.js +0 -1
  162. package/comate-engine/node_modules/@comate/plugin-host/dist/user-BIpzRUfb.js +0 -44
  163. package/comate-engine/node_modules/better-sqlite3/build/Release/better_sqlite3.node +0 -0
  164. /package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/references/issue_type_mapping.json +0 -0
  165. /package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/references/query_reference.md +0 -0
  166. /package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/compat.py +0 -0
  167. /package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/create_card_cli.py +0 -0
  168. /package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/icafe/__init__.py +0 -0
  169. /package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/logger.py +0 -0
  170. /package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/recognize_card_cli.py +0 -0
@@ -0,0 +1,198 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 对话结果上报工具 - 扫描完成后向服务端上报本次对话信息。
4
+
5
+ 用法:
6
+ python3 report_chat.py --username <用户名> --chat-id <对话ID> --scan-result <扫描结果JSON文件> [--status <状态码>] [--err-message <错误信息>] [--git-url <仓库URL>] [--git-branch <分支>] [--ide <IDE名称>] [--query <用户输入>]
7
+
8
+ 接口: POST /api/v1/chats
9
+ """
10
+
11
+ import argparse
12
+ import hashlib
13
+ import json
14
+ import logging
15
+ import os
16
+ import subprocess
17
+ import sys
18
+ import uuid
19
+
20
+ import http_client # noqa: F401 (triggers shared logging config)
21
+
22
+ HOST = "https://comate-sec.baidu-int.com"
23
+
24
+ USERNAME = ""
25
+ USER_ID = ""
26
+
27
+ logger = logging.getLogger("report")
28
+
29
+
30
+ def build_headers(chat_id):
31
+ # type: (str) -> dict
32
+ return {
33
+ "Comate-Username": USERNAME,
34
+ "Comate-User-Id": USER_ID,
35
+ "SAST-Request-ID": str(uuid.uuid4()),
36
+ "SAST-Chat-ID": chat_id,
37
+ }
38
+
39
+
40
+ def calc_file_sha256(file_path):
41
+ # type: (str) -> str
42
+ """计算文件 SHA256。"""
43
+ sha256_hash = hashlib.sha256()
44
+ try:
45
+ with open(file_path, "rb") as f:
46
+ for chunk in iter(lambda: f.read(4096), b""):
47
+ sha256_hash.update(chunk)
48
+ return sha256_hash.hexdigest()
49
+ except Exception:
50
+ return ""
51
+
52
+
53
+ def build_vuls_from_scan_result(scan_result, root_path=""):
54
+ # type: (dict, str) -> list
55
+ """从扫描结果中提取漏洞信息,构造 vuls 数组。"""
56
+ vuls = []
57
+ data = scan_result.get("data", {})
58
+ # 兼容两种结构: data.sarif.runs 或 data.runs
59
+ runs = data.get("sarif", {}).get("runs", []) or data.get("runs", [])
60
+ if not runs:
61
+ return vuls
62
+
63
+ # 按文件聚合漏洞
64
+ file_vuls = {} # type: dict
65
+ for run in runs:
66
+ # 兼容 "results"(标准SARIF) 和 "result"(服务端实际返回)
67
+ results = run.get("results", []) or run.get("result", [])
68
+ for result in results:
69
+ locations = result.get("locations", [])
70
+ vul_hash = result.get("properties", {}).get("hash", "")
71
+ if not vul_hash:
72
+ continue
73
+ for loc in locations:
74
+ file_path = loc.get("physicalLocation", {}).get("artifactLocation", {}).get("uri", "")
75
+ if not file_path:
76
+ continue
77
+ if file_path not in file_vuls:
78
+ file_vuls[file_path] = {
79
+ "fileName": file_path,
80
+ "fileHash": "",
81
+ "vuls": [],
82
+ }
83
+ file_vuls[file_path]["vuls"].append({"vulHash": vul_hash})
84
+
85
+ # 填充 fileHash
86
+ bundle_files = scan_result.get("data", {}).get("bundleFiles", {})
87
+ for file_path, file_info in file_vuls.items():
88
+ fh = bundle_files.get(file_path, "")
89
+ if not fh and root_path:
90
+ fh = calc_file_sha256(os.path.join(root_path, file_path))
91
+ file_info["fileHash"] = fh
92
+
93
+ vuls = list(file_vuls.values())
94
+ return vuls
95
+
96
+
97
+ def get_git_info(root_path):
98
+ # type: (str) -> dict
99
+ """从项目目录获取 git URL 和分支。"""
100
+ info = {"url": "", "branch": ""}
101
+ if not root_path:
102
+ return info
103
+ try:
104
+ info["url"] = subprocess.check_output(
105
+ ["git", "remote", "get-url", "origin"], cwd=root_path, stderr=subprocess.DEVNULL
106
+ ).decode("utf-8").strip()
107
+ except Exception:
108
+ pass
109
+ try:
110
+ info["branch"] = subprocess.check_output(
111
+ ["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=root_path, stderr=subprocess.DEVNULL
112
+ ).decode("utf-8").strip()
113
+ except Exception:
114
+ pass
115
+ return info
116
+
117
+
118
+ def report_chat(chat_id, scan_result, status, err_message, git_url, git_branch, ide, query, root_path=""):
119
+ # type: (str, dict, int, str, str, str, str, str, str) -> dict
120
+ """上报对话结果到服务端。"""
121
+ # git_url/git_branch 为空时,自动从 root_path 获取
122
+ if root_path and (not git_url or not git_branch):
123
+ git_info = get_git_info(root_path)
124
+ if not git_url:
125
+ git_url = git_info["url"]
126
+ if not git_branch:
127
+ git_branch = git_info["branch"]
128
+
129
+ vuls = build_vuls_from_scan_result(scan_result, root_path)
130
+
131
+ body = {
132
+ "type": 1,
133
+ "gitInfo": {
134
+ "url": git_url,
135
+ "branch": git_branch,
136
+ },
137
+ "ide": ide,
138
+ "query": query,
139
+ "status": status,
140
+ "errMessage": err_message,
141
+ "vuls": vuls,
142
+ }
143
+
144
+ headers = build_headers(chat_id)
145
+ logger.info("report_chat: chat_id=%s, vuls_count=%d, git_url=%s, git_branch=%s",
146
+ chat_id, len(vuls), git_url, git_branch)
147
+ return http_client.post("{}/api/v1/chats".format(HOST), headers=headers, json_body=body)
148
+
149
+
150
+ def main():
151
+ global USERNAME, USER_ID
152
+ parser = argparse.ArgumentParser(description="对话结果上报工具")
153
+ parser.add_argument("--username", required=True, help="Comate 用户名")
154
+ parser.add_argument("--chat-id", required=True, help="对话唯一标识 (COMATE_SESSION_ID)")
155
+ parser.add_argument("--scan-result", required=True, help="扫描结果 JSON 文件路径")
156
+ parser.add_argument("--status", type=int, default=0, help="执行状态码,0=成功,1=失败 (默认 0)")
157
+ parser.add_argument("--err-message", default="", help="错误信息 (默认空)")
158
+ parser.add_argument("--root-path", default="", help="项目根目录,用于计算文件哈希")
159
+ parser.add_argument("--git-url", default="", help="Git 仓库 URL")
160
+ parser.add_argument("--git-branch", default="", help="Git 分支")
161
+ parser.add_argument("--ide", default="", help="IDE 名称")
162
+ parser.add_argument("--query", default="", help="用户输入的查询文本")
163
+ args = parser.parse_args()
164
+
165
+ USERNAME = args.username
166
+ USER_ID = hashlib.md5(USERNAME.encode("utf-8")).hexdigest()[:12]
167
+
168
+ logger.info("report_chat start: chat_id=%s, username=%s, scan_result=%s",
169
+ args.chat_id, USERNAME, args.scan_result)
170
+
171
+ # 读取扫描结果
172
+ scan_result = {}
173
+ try:
174
+ with open(args.scan_result, "r", encoding="utf-8") as f:
175
+ scan_result = json.load(f)
176
+ except Exception as e:
177
+ print("警告: 读取扫描结果失败 {}: {}".format(args.scan_result, e), file=sys.stderr)
178
+
179
+ try:
180
+ result = report_chat(
181
+ chat_id=args.chat_id,
182
+ scan_result=scan_result,
183
+ status=args.status,
184
+ err_message=args.err_message,
185
+ git_url=args.git_url,
186
+ git_branch=args.git_branch,
187
+ ide=args.ide,
188
+ query=args.query,
189
+ root_path=args.root_path,
190
+ )
191
+ print(json.dumps(result, ensure_ascii=False))
192
+ except Exception as e:
193
+ print("上报失败: {}".format(e), file=sys.stderr)
194
+ sys.exit(1)
195
+
196
+
197
+ if __name__ == "__main__":
198
+ main()
@@ -0,0 +1,316 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 漏洞扫描工具 - 将项目代码上传至 Comate 安全服务端进行 SAST 扫描。
4
+
5
+ 用法:
6
+ python3 scan_vulnerability.py --root-path <项目目录> --username <用户名>
7
+
8
+ 流程:
9
+ 1. 获取扫描配置(支持的文件类型)
10
+ 2. 遍历项目目录,收集文件哈希
11
+ 3. 创建 bundle 并上传缺失文件
12
+ 4. 发起扫描并轮询结果
13
+ 5. 输出 SARIF 格式漏洞报告
14
+ """
15
+
16
+ import argparse
17
+ import base64
18
+ import hashlib
19
+ import json
20
+ import logging
21
+ import os
22
+ import re
23
+ import sys
24
+ import time
25
+ import uuid
26
+ from typing import Any, Dict, List, Optional, Tuple
27
+
28
+ import http_client # noqa: F401 (triggers shared logging config)
29
+
30
+ HOST = "https://comate-sec.baidu-int.com"
31
+
32
+ USERNAME = ""
33
+ USER_ID = ""
34
+
35
+ logger = logging.getLogger("scan")
36
+
37
+
38
+ def build_headers(chat_id=""):
39
+ # type: (str) -> Dict[str, str]
40
+ headers = {
41
+ "Comate-Username": USERNAME,
42
+ "Comate-User-Id": USER_ID,
43
+ "SAST-Request-ID": str(uuid.uuid4()),
44
+ }
45
+ if chat_id:
46
+ headers["SAST-Chat-ID"] = chat_id
47
+ return headers
48
+
49
+
50
+ def calc_sha256(file_path):
51
+ # type: (str) -> str
52
+ sha256_hash = hashlib.sha256()
53
+ with open(file_path, "rb") as f:
54
+ for chunk in iter(lambda: f.read(4096), b""):
55
+ sha256_hash.update(chunk)
56
+ return sha256_hash.hexdigest()
57
+
58
+
59
+ def parse_gitignore(gitignore_path):
60
+ # type: (str) -> List[Any]
61
+ """解析 .gitignore 文件,返回正则表达式列表。"""
62
+ patterns = []
63
+ try:
64
+ with open(gitignore_path, "r", encoding="utf-8") as f:
65
+ for line in f:
66
+ line = line.strip()
67
+ if not line or line.startswith("#"):
68
+ continue
69
+ regex = line
70
+ regex = regex.replace(".", r"\.")
71
+ regex = regex.replace("*", ".*")
72
+ regex = regex.replace("?", ".")
73
+ if line.startswith("/"):
74
+ regex = "^" + regex[1:]
75
+ if line.endswith("/"):
76
+ regex = regex[:-1] + r"($|/.*)"
77
+ patterns.append(re.compile(regex))
78
+ except FileNotFoundError:
79
+ pass
80
+ return patterns
81
+
82
+
83
+ def is_ignored(path, gitignore_patterns, root_dir):
84
+ # type: (str, List[Any], str) -> bool
85
+ rel_path = os.path.relpath(path, root_dir)
86
+ for pattern in gitignore_patterns:
87
+ if pattern.search(rel_path) or pattern.search(path):
88
+ return True
89
+ return False
90
+
91
+
92
+ def walk_dir(directory, extensions=None, gitignore_patterns=None):
93
+ # type: (str, Optional[List[str]], Optional[List[Any]]) -> List[str]
94
+ """递归遍历目录,按扩展名过滤并排除 .gitignore 中的文件。"""
95
+ files = []
96
+ resolved_dir = os.path.realpath(directory)
97
+ for root, dirs, filenames in os.walk(resolved_dir):
98
+ if gitignore_patterns:
99
+ dirs[:] = [
100
+ d for d in dirs
101
+ if not is_ignored(os.path.join(root, d), gitignore_patterns, resolved_dir)
102
+ ]
103
+ for name in filenames:
104
+ if name.lower().endswith(".json"):
105
+ continue
106
+ full_path = os.path.join(root, name)
107
+ if gitignore_patterns and is_ignored(full_path, gitignore_patterns, resolved_dir):
108
+ continue
109
+ if extensions:
110
+ if not any(name.lower().endswith(ext.lower()) for ext in extensions):
111
+ continue
112
+ files.append(full_path)
113
+ return files
114
+
115
+
116
+ def read_file_content(file_path):
117
+ # type: (str) -> str
118
+ """读取文件内容,二进制文件用 base64 编码。"""
119
+ try:
120
+ with open(file_path, "r", encoding="utf-8") as f:
121
+ return f.read()
122
+ except UnicodeDecodeError:
123
+ with open(file_path, "rb") as f:
124
+ return base64.b64encode(f.read()).decode("ascii")
125
+
126
+
127
+ def get_settings():
128
+ # type: () -> Dict
129
+ """获取扫描配置。"""
130
+ return http_client.get("{}/api/v2/analysis/settings".format(HOST), headers=build_headers())
131
+
132
+
133
+ def create_bundle(file_hashes):
134
+ # type: (Dict[str, str]) -> Tuple[str, List[str]]
135
+ """创建扫描 bundle,返回 (bundle_hash, missing_files)。"""
136
+ data = http_client.post("{}/api/v1/bundle".format(HOST), headers=build_headers(), json_body=file_hashes)
137
+
138
+ if isinstance(data.get("data"), list):
139
+ return data.get("bundleHash", ""), data["data"]
140
+ return (
141
+ data.get("data", {}).get("bundleHash", ""),
142
+ data.get("data", {}).get("missingFiles", []),
143
+ )
144
+
145
+
146
+ def upload_files(bundle_hash, root_path, missing_files, file_hashes, upload_type="scan"):
147
+ # type: (str, str, List[str], Dict[str, str], str) -> Tuple[str, List[str]]
148
+ """上传缺失文件,返回 (bundle_hash, remaining_missing_files)。"""
149
+ payload = {"files": {}}
150
+ for name in missing_files:
151
+ file_path = os.path.join(root_path, name)
152
+ try:
153
+ content = read_file_content(file_path)
154
+ payload["files"][name] = {"hash": file_hashes.get(name, ""), "content": content}
155
+ except Exception as e:
156
+ print("警告: 读取文件失败 {}: {}".format(file_path, e), file=sys.stderr)
157
+
158
+ if upload_type == "scan":
159
+ url = "{}/api/v1/bundle/{}".format(HOST, bundle_hash)
160
+ else:
161
+ url = "{}/api/v1/upload".format(HOST)
162
+
163
+ data = http_client.put(url, headers=build_headers(), json_body=payload)
164
+
165
+ if isinstance(data.get("data"), list):
166
+ return data.get("bundleHash", bundle_hash), data["data"]
167
+ return (
168
+ data.get("data", {}).get("bundleHash", bundle_hash),
169
+ data.get("data", {}).get("missingFiles", []),
170
+ )
171
+
172
+
173
+ def scan_init(root_path):
174
+ # type: (str) -> Tuple[str, Dict[str, str]]
175
+ """初始化扫描:获取配置、收集文件、创建 bundle、上传文件。返回 (bundle_hash, file_hashes)。"""
176
+ # 获取配置
177
+ cfg = get_settings()
178
+ scan_config = cfg.get("data", {}).get("scanConfiguration", {})
179
+ extensions = list(
180
+ set(scan_config.get("sca", {}).get("supportedLanguages", []))
181
+ | set(scan_config.get("sast", {}).get("supportedLanguages", []))
182
+ )
183
+
184
+ # 解析 .gitignore
185
+ gitignore_path = os.path.join(root_path, ".gitignore")
186
+ gitignore_patterns = parse_gitignore(gitignore_path)
187
+
188
+ # 遍历目录,收集文件哈希
189
+ all_files = walk_dir(root_path, extensions, gitignore_patterns)
190
+ file_hashes = {} # type: Dict[str, str]
191
+ for f in all_files:
192
+ rel = os.path.relpath(f, root_path)
193
+ try:
194
+ file_hashes[rel] = calc_sha256(f)
195
+ except Exception as e:
196
+ print("警告: 计算哈希失败 {}: {}".format(f, e), file=sys.stderr)
197
+
198
+ print("收集文件: {} 个".format(len(file_hashes)), file=sys.stderr)
199
+
200
+ # 创建 bundle
201
+ bundle_hash, missing_files = create_bundle(file_hashes)
202
+ print("Bundle: {}, 待上传: {} 个文件".format(bundle_hash, len(missing_files)), file=sys.stderr)
203
+
204
+ # 循环上传缺失文件
205
+ while missing_files:
206
+ bundle_hash, missing_files = upload_files(
207
+ bundle_hash, root_path, missing_files, file_hashes, "scan"
208
+ )
209
+ if missing_files:
210
+ print("剩余待上传: {} 个文件".format(len(missing_files)), file=sys.stderr)
211
+
212
+ return bundle_hash, file_hashes
213
+
214
+
215
+ def get_git_info(root_path):
216
+ # type: (str) -> Dict[str, str]
217
+ """从项目目录获取 git 信息(URL、分支、commitID)。"""
218
+ import subprocess
219
+ info = {"gitURL": "", "gitBranch": "", "gitCommitID": ""}
220
+ try:
221
+ info["gitURL"] = subprocess.check_output(
222
+ ["git", "remote", "get-url", "origin"], cwd=root_path, stderr=subprocess.DEVNULL
223
+ ).decode("utf-8").strip()
224
+ except Exception:
225
+ pass
226
+ try:
227
+ info["gitBranch"] = subprocess.check_output(
228
+ ["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=root_path, stderr=subprocess.DEVNULL
229
+ ).decode("utf-8").strip()
230
+ except Exception:
231
+ pass
232
+ try:
233
+ info["gitCommitID"] = subprocess.check_output(
234
+ ["git", "rev-parse", "HEAD"], cwd=root_path, stderr=subprocess.DEVNULL
235
+ ).decode("utf-8").strip()
236
+ except Exception:
237
+ pass
238
+ return info
239
+
240
+
241
+ def scan_vulnerability(root_path, chat_id=""):
242
+ # type: (str, str) -> Tuple[Dict, str]
243
+ """执行漏洞扫描,返回 (SARIF 格式结果, bundleHash)。"""
244
+ bundle_hash, file_hashes = scan_init(root_path)
245
+
246
+ git_info = get_git_info(root_path)
247
+ scan_info = {
248
+ "key": {"hash": bundle_hash, "type": "file"},
249
+ "scan": 3,
250
+ "analysisContext": {
251
+ "initiator": "",
252
+ "trigger": "manual",
253
+ "workspaceName": "",
254
+ "workspacePath": "",
255
+ "gitInfo": [{
256
+ "path": "",
257
+ "gitURL": git_info["gitURL"],
258
+ "gitBranch": git_info["gitBranch"],
259
+ "gitCommitID": git_info["gitCommitID"],
260
+ }],
261
+ },
262
+ }
263
+
264
+ print("开始扫描...", file=sys.stderr)
265
+ logger.info("scan start: chat_id=%s, bundle_hash=%s", chat_id, bundle_hash)
266
+ while True:
267
+ result = http_client.post(
268
+ "{}/api/v2/analysis".format(HOST), headers=build_headers(chat_id), json_body=scan_info
269
+ )
270
+ if result.get("status") != 1:
271
+ return result, bundle_hash
272
+ print("扫描中,等待结果...", file=sys.stderr)
273
+ time.sleep(3)
274
+
275
+
276
+ def main():
277
+ global USERNAME, USER_ID
278
+ parser = argparse.ArgumentParser(description="代码安全漏洞扫描工具")
279
+ parser.add_argument("--root-path", required=True, help="待扫描项目根目录")
280
+ parser.add_argument("--username", required=True, help="Comate 用户名")
281
+ parser.add_argument("--chat-id", default="", help="对话唯一标识 (COMATE_SESSION_ID)")
282
+ parser.add_argument("--output-dir", default=None, help="结果输出目录,默认为 skill 临时目录")
283
+ args = parser.parse_args()
284
+
285
+ USERNAME = args.username
286
+ USER_ID = hashlib.md5(USERNAME.encode("utf-8")).hexdigest()[:12]
287
+
288
+ logger.info("scan_vulnerability start: chat_id=%s, username=%s, root_path=%s",
289
+ args.chat_id, USERNAME, args.root_path)
290
+
291
+ root_path = os.path.realpath(args.root_path)
292
+ if not os.path.isdir(root_path):
293
+ print("错误: 目录不存在 {}".format(root_path), file=sys.stderr)
294
+ sys.exit(1)
295
+
296
+ # 默认输出到 skill 临时目录,按项目路径隔离子目录避免并发冲突
297
+ skill_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
298
+ project_name = os.path.basename(root_path)
299
+ path_hash = hashlib.md5(root_path.encode("utf-8")).hexdigest()[:8]
300
+ default_output = os.path.join(skill_dir, ".tmp", "{}_{}".format(project_name, path_hash))
301
+ output_dir = os.path.realpath(args.output_dir) if args.output_dir else default_output
302
+ os.makedirs(output_dir, exist_ok=True)
303
+
304
+ result, bundle_hash = scan_vulnerability(root_path, chat_id=args.chat_id)
305
+
306
+ # 将 bundleHash 写入结果,供后续解析和修复使用
307
+ result["bundleHash"] = bundle_hash
308
+
309
+ output_file = os.path.join(output_dir, "scan_result.json")
310
+ with open(output_file, "w", encoding="utf-8") as f:
311
+ json.dump(result, f, ensure_ascii=False, indent=2)
312
+ print(output_file)
313
+
314
+
315
+ if __name__ == "__main__":
316
+ main()