@comate/zulu 1.2.1-beta.2 → 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 +3 -3
- 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,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()
|