@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,173 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
基于 urllib 的轻量 HTTP 客户端,零第三方依赖。
|
|
4
|
+
提供 GET / POST / PUT 方法,支持 JSON 请求和响应。
|
|
5
|
+
当 ssl 模块不可用时(如 libssl 版本不匹配),自动回退到 curl 子进程。
|
|
6
|
+
所有脚本共用日志配置,写入 skill/logs/<日期>.log,自动清理 7 天前的日志。
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import datetime
|
|
10
|
+
import glob
|
|
11
|
+
import json
|
|
12
|
+
import logging
|
|
13
|
+
import os
|
|
14
|
+
import subprocess
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
import ssl
|
|
18
|
+
_SSL_AVAILABLE = True
|
|
19
|
+
except ImportError:
|
|
20
|
+
_SSL_AVAILABLE = False
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
from urllib.request import Request, urlopen
|
|
24
|
+
from urllib.error import HTTPError, URLError
|
|
25
|
+
except ImportError:
|
|
26
|
+
from urllib2 import Request, urlopen, HTTPError, URLError
|
|
27
|
+
|
|
28
|
+
# ---- 统一日志配置 ----
|
|
29
|
+
_scripts_dir = os.path.dirname(os.path.abspath(__file__))
|
|
30
|
+
_skill_dir = os.path.dirname(_scripts_dir)
|
|
31
|
+
_log_dir = os.path.join(_skill_dir, "logs")
|
|
32
|
+
os.makedirs(_log_dir, exist_ok=True)
|
|
33
|
+
|
|
34
|
+
# 清理超过 7 天的日志文件
|
|
35
|
+
_now = datetime.datetime.now()
|
|
36
|
+
for _f in glob.glob(os.path.join(_log_dir, "*.log")):
|
|
37
|
+
try:
|
|
38
|
+
_mtime = datetime.datetime.fromtimestamp(os.path.getmtime(_f))
|
|
39
|
+
if (_now - _mtime).days > 7:
|
|
40
|
+
os.remove(_f)
|
|
41
|
+
except OSError:
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
_log_file = os.path.join(_log_dir, _now.strftime("%Y-%m-%d") + ".log")
|
|
45
|
+
logging.basicConfig(
|
|
46
|
+
filename=_log_file,
|
|
47
|
+
level=logging.INFO,
|
|
48
|
+
format="%(asctime)s [%(name)s] %(levelname)s %(message)s",
|
|
49
|
+
)
|
|
50
|
+
logger = logging.getLogger("http")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _build_request(url, method="GET", headers=None, json_body=None):
|
|
54
|
+
"""构造 urllib Request 对象。"""
|
|
55
|
+
data = None
|
|
56
|
+
if json_body is not None:
|
|
57
|
+
data = json.dumps(json_body).encode("utf-8")
|
|
58
|
+
|
|
59
|
+
req = Request(url, data=data)
|
|
60
|
+
req.get_method = lambda: method
|
|
61
|
+
|
|
62
|
+
if headers:
|
|
63
|
+
for k, v in headers.items():
|
|
64
|
+
req.add_header(k, v)
|
|
65
|
+
|
|
66
|
+
if data is not None and not req.has_header("Content-type"):
|
|
67
|
+
req.add_header("Content-Type", "application/json; charset=utf-8")
|
|
68
|
+
|
|
69
|
+
return req
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _create_ssl_context():
|
|
73
|
+
"""创建 SSL 上下文。"""
|
|
74
|
+
ctx = ssl.create_default_context()
|
|
75
|
+
ctx.check_hostname = False
|
|
76
|
+
ctx.verify_mode = ssl.CERT_NONE
|
|
77
|
+
return ctx
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _log_safe_headers(headers):
|
|
81
|
+
"""提取可安全记录的请求头。"""
|
|
82
|
+
safe = {}
|
|
83
|
+
if headers:
|
|
84
|
+
for k in ("SAST-Chat-ID", "SAST-Request-ID", "Comate-User-Id", "Comate-Username"):
|
|
85
|
+
if k in headers:
|
|
86
|
+
safe[k] = headers[k]
|
|
87
|
+
return safe
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _request_curl(url, method="GET", headers=None, json_body=None, timeout=120):
|
|
91
|
+
"""ssl 不可用时,通过 curl 子进程发送请求。"""
|
|
92
|
+
cmd = ["curl", "-s", "-S", "--insecure", "-X", method, "--max-time", str(timeout)]
|
|
93
|
+
|
|
94
|
+
if headers:
|
|
95
|
+
for k, v in headers.items():
|
|
96
|
+
cmd += ["-H", "{}: {}".format(k, v)]
|
|
97
|
+
|
|
98
|
+
if json_body is not None:
|
|
99
|
+
cmd += ["-H", "Content-Type: application/json; charset=utf-8"]
|
|
100
|
+
cmd += ["-d", json.dumps(json_body)]
|
|
101
|
+
|
|
102
|
+
cmd.append(url)
|
|
103
|
+
|
|
104
|
+
logger.info("[curl] %s %s headers=%s", method, url, json.dumps(_log_safe_headers(headers)))
|
|
105
|
+
try:
|
|
106
|
+
proc = subprocess.run(cmd, capture_output=True, timeout=timeout + 10)
|
|
107
|
+
except FileNotFoundError:
|
|
108
|
+
raise RuntimeError(
|
|
109
|
+
"ssl 模块不可用且未找到 curl 命令。"
|
|
110
|
+
"请安装 curl 或修复 Python ssl 依赖(安装 libssl1.1 或重新编译 Python)。"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
if proc.returncode != 0:
|
|
114
|
+
stderr_text = proc.stderr.decode("utf-8", errors="replace").strip()
|
|
115
|
+
logger.error("[curl] %s %s -> curl exit %d: %s", method, url, proc.returncode, stderr_text)
|
|
116
|
+
raise RuntimeError("curl 请求失败 (exit {}): {}".format(proc.returncode, stderr_text))
|
|
117
|
+
|
|
118
|
+
body = proc.stdout.decode("utf-8")
|
|
119
|
+
logger.info("[curl] %s %s -> OK (%d bytes)", method, url, len(body))
|
|
120
|
+
return json.loads(body)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def request(url, method="GET", headers=None, json_body=None, timeout=120):
|
|
124
|
+
"""
|
|
125
|
+
发送 HTTP 请求,返回解析后的 JSON dict。
|
|
126
|
+
当 ssl 模块不可用时自动回退到 curl。
|
|
127
|
+
|
|
128
|
+
:param url: 请求 URL
|
|
129
|
+
:param method: HTTP 方法 (GET/POST/PUT)
|
|
130
|
+
:param headers: 请求头 dict
|
|
131
|
+
:param json_body: JSON 请求体 (自动序列化)
|
|
132
|
+
:param timeout: 超时秒数
|
|
133
|
+
:return: 响应 JSON dict
|
|
134
|
+
:raises: HTTPError, URLError, RuntimeError
|
|
135
|
+
"""
|
|
136
|
+
if not _SSL_AVAILABLE:
|
|
137
|
+
return _request_curl(url, method, headers, json_body, timeout)
|
|
138
|
+
|
|
139
|
+
# 记录请求(仅 URL 和关键 header,不记录 body 避免日志过大)
|
|
140
|
+
logger.info("%s %s headers=%s", method, url, json.dumps(_log_safe_headers(headers)))
|
|
141
|
+
|
|
142
|
+
req = _build_request(url, method, headers, json_body)
|
|
143
|
+
ctx = _create_ssl_context()
|
|
144
|
+
|
|
145
|
+
try:
|
|
146
|
+
resp = urlopen(req, timeout=timeout, context=ctx)
|
|
147
|
+
status = resp.getcode()
|
|
148
|
+
body = resp.read().decode("utf-8")
|
|
149
|
+
logger.info("%s %s -> %d (%d bytes)", method, url, status, len(body))
|
|
150
|
+
return json.loads(body)
|
|
151
|
+
except HTTPError as e:
|
|
152
|
+
error_body = ""
|
|
153
|
+
try:
|
|
154
|
+
error_body = e.read().decode("utf-8")[:500]
|
|
155
|
+
except Exception:
|
|
156
|
+
pass
|
|
157
|
+
logger.error("%s %s -> HTTP %d: %s", method, url, e.code, error_body)
|
|
158
|
+
raise
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def get(url, headers=None, timeout=120):
|
|
162
|
+
"""发送 GET 请求,返回 JSON。"""
|
|
163
|
+
return request(url, method="GET", headers=headers, timeout=timeout)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def post(url, headers=None, json_body=None, timeout=120):
|
|
167
|
+
"""发送 POST 请求,返回 JSON。"""
|
|
168
|
+
return request(url, method="POST", headers=headers, json_body=json_body, timeout=timeout)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def put(url, headers=None, json_body=None, timeout=120):
|
|
172
|
+
"""发送 PUT 请求,返回 JSON。"""
|
|
173
|
+
return request(url, method="PUT", headers=headers, json_body=json_body, timeout=timeout)
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
扫描结果解析与展示工具 - 解析 scan_result.json 文件,输出标准化漏洞报告。
|
|
4
|
+
|
|
5
|
+
用法:
|
|
6
|
+
python3 parse_scan_result.py --scan-result <扫描结果文件路径> [--output-dir <输出目录>]
|
|
7
|
+
|
|
8
|
+
输出:
|
|
9
|
+
1. 标准输出打印 Markdown 格式漏洞报告(供直接展示给用户)
|
|
10
|
+
2. 在输出目录生成 parsed_result.json,包含结构化漏洞数据(供后续修复脚本使用)
|
|
11
|
+
|
|
12
|
+
parsed_result.json 格式:
|
|
13
|
+
{
|
|
14
|
+
"total": 10,
|
|
15
|
+
"common_count": 7,
|
|
16
|
+
"sensitive_count": 3,
|
|
17
|
+
"false_positive_count": 2,
|
|
18
|
+
"analyzing_count": 1,
|
|
19
|
+
"bundle_hash": "xxx",
|
|
20
|
+
"common_vuls": [ ... ],
|
|
21
|
+
"sensitive_vuls": [ ... ],
|
|
22
|
+
"false_positive_vuls": [ ... ],
|
|
23
|
+
"analyzing_vuls": [ ... ]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
每个漏洞条目包含:
|
|
27
|
+
{
|
|
28
|
+
"ruleID": "codescan_java_mybatis-java_sqli",
|
|
29
|
+
"name": "Sql 注入漏洞",
|
|
30
|
+
"description": "...",
|
|
31
|
+
"suggestion": "...",
|
|
32
|
+
"level": "ERROR",
|
|
33
|
+
"level_cn": "严重",
|
|
34
|
+
"file": "src/main/java/...",
|
|
35
|
+
"startLine": 12,
|
|
36
|
+
"endLine": 12,
|
|
37
|
+
"hash": "abc123...",
|
|
38
|
+
"is_sensitive": false,
|
|
39
|
+
"aiAnalysisStatus": 0,
|
|
40
|
+
"aiAnalysisStatusText": "无需分析",
|
|
41
|
+
"codeFlows": [ {"file": "...", "line": 63, "message": "..."}, ... ]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
aiAnalysisStatus 说明:
|
|
45
|
+
0 - 无需分析
|
|
46
|
+
1 - 分析中
|
|
47
|
+
2 - 真实漏洞
|
|
48
|
+
3 - 误报(不进入修复流程)
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
import argparse
|
|
52
|
+
from collections import OrderedDict
|
|
53
|
+
import json
|
|
54
|
+
import os
|
|
55
|
+
import sys
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
LEVEL_MAP = {
|
|
59
|
+
"ERROR": "严重",
|
|
60
|
+
"WARNING": "高危",
|
|
61
|
+
"NOTE": "中危",
|
|
62
|
+
"NONE": "低危",
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# 等级排序优先级(越小越严重)
|
|
66
|
+
LEVEL_PRIORITY = {
|
|
67
|
+
"ERROR": 0,
|
|
68
|
+
"WARNING": 1,
|
|
69
|
+
"NOTE": 2,
|
|
70
|
+
"NONE": 3,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# AI 分析状态映射
|
|
74
|
+
AI_ANALYSIS_STATUS_MAP = {
|
|
75
|
+
0: "无需分析",
|
|
76
|
+
1: "分析中",
|
|
77
|
+
2: "真实漏洞",
|
|
78
|
+
3: "误报",
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def parse_scan_result(scan_result):
|
|
83
|
+
# type: (dict) -> dict
|
|
84
|
+
"""解析扫描结果,返回结构化漏洞数据。
|
|
85
|
+
|
|
86
|
+
aiAnalysisStatus=3 (误报) 和 aiAnalysisStatus=1 (分析中) 的漏洞会被单独归类,不进入修复流程。
|
|
87
|
+
"""
|
|
88
|
+
data = scan_result.get("data", {})
|
|
89
|
+
runs = data.get("sarif", {}).get("runs", []) or data.get("runs", [])
|
|
90
|
+
|
|
91
|
+
# 提取 bundleHash(由 scan_vulnerability.py 写入顶层)
|
|
92
|
+
bundle_hash = scan_result.get("bundleHash", "")
|
|
93
|
+
|
|
94
|
+
# 建立 ruleID -> rule 映射
|
|
95
|
+
rule_map = {}
|
|
96
|
+
for run in runs:
|
|
97
|
+
tool = run.get("tool", {})
|
|
98
|
+
# 兼容 rules 在 tool 或 tool.driver 下
|
|
99
|
+
rules = tool.get("rules", []) or tool.get("driver", {}).get("rules", [])
|
|
100
|
+
for rule in rules:
|
|
101
|
+
rule_id = rule.get("id", "")
|
|
102
|
+
if rule_id:
|
|
103
|
+
rule_map[rule_id] = rule
|
|
104
|
+
|
|
105
|
+
# 解析漏洞结果
|
|
106
|
+
common_vuls = []
|
|
107
|
+
sensitive_vuls = []
|
|
108
|
+
false_positive_vuls = [] # 误报漏洞单独存放
|
|
109
|
+
analyzing_vuls = [] # AI 分析中的漏洞单独存放(不进入修复流程)
|
|
110
|
+
|
|
111
|
+
for run in runs:
|
|
112
|
+
results = run.get("results", []) or run.get("result", [])
|
|
113
|
+
for result in results:
|
|
114
|
+
rule_id = result.get("ruleID", "") or result.get("ruleId", "")
|
|
115
|
+
rule = rule_map.get(rule_id, {})
|
|
116
|
+
|
|
117
|
+
# 基础信息
|
|
118
|
+
name = rule.get("name", rule_id)
|
|
119
|
+
description = rule.get("description", "") or result.get("message", {}).get("text", "")
|
|
120
|
+
suggestion = rule.get("suggestion", "")
|
|
121
|
+
level_config = rule.get("defaultConfiguration", {})
|
|
122
|
+
level = level_config.get("level", "NONE") if level_config else "NONE"
|
|
123
|
+
level_cn = LEVEL_MAP.get(level, "低危")
|
|
124
|
+
vul_hash = result.get("properties", {}).get("hash", "")
|
|
125
|
+
|
|
126
|
+
# AI 分析状态
|
|
127
|
+
ai_status = result.get("properties", {}).get("aiAnalysisStatus", 0)
|
|
128
|
+
ai_status_text = AI_ANALYSIS_STATUS_MAP.get(ai_status, "未知")
|
|
129
|
+
|
|
130
|
+
# 位置信息
|
|
131
|
+
locations = result.get("locations", [])
|
|
132
|
+
file_uri = ""
|
|
133
|
+
start_line = 0
|
|
134
|
+
end_line = 0
|
|
135
|
+
if locations:
|
|
136
|
+
phys = locations[0].get("physicalLocation", {})
|
|
137
|
+
artifact = phys.get("artifactLocation", {})
|
|
138
|
+
file_uri = artifact.get("uri", "")
|
|
139
|
+
region = artifact.get("region", {})
|
|
140
|
+
start_line = region.get("startLine", 0)
|
|
141
|
+
end_line = region.get("endLine", start_line)
|
|
142
|
+
|
|
143
|
+
# 数据流信息
|
|
144
|
+
code_flows_raw = result.get("codeFlows", [])
|
|
145
|
+
code_flows = []
|
|
146
|
+
if code_flows_raw:
|
|
147
|
+
thread_flows = code_flows_raw[0].get("threadFlows", [])
|
|
148
|
+
if thread_flows:
|
|
149
|
+
for loc_wrapper in thread_flows[0].get("locations", []):
|
|
150
|
+
loc = loc_wrapper.get("location", {})
|
|
151
|
+
loc_phys = loc.get("physicalLocation", {})
|
|
152
|
+
loc_file = loc_phys.get("artifactLocation", {}).get("uri", "")
|
|
153
|
+
loc_region = loc_phys.get("region", {})
|
|
154
|
+
loc_line = loc_region.get("startLine", 0)
|
|
155
|
+
loc_msg = loc.get("message", {}).get("text", "")
|
|
156
|
+
code_flows.append({
|
|
157
|
+
"file": loc_file,
|
|
158
|
+
"line": loc_line,
|
|
159
|
+
"message": loc_msg,
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
is_sensitive = "sensitive" in rule_id.lower()
|
|
163
|
+
|
|
164
|
+
vul_entry = {
|
|
165
|
+
"ruleID": rule_id,
|
|
166
|
+
"name": name,
|
|
167
|
+
"description": description,
|
|
168
|
+
"suggestion": suggestion,
|
|
169
|
+
"level": level,
|
|
170
|
+
"level_cn": level_cn,
|
|
171
|
+
"file": file_uri,
|
|
172
|
+
"startLine": start_line,
|
|
173
|
+
"endLine": end_line,
|
|
174
|
+
"hash": vul_hash,
|
|
175
|
+
"is_sensitive": is_sensitive,
|
|
176
|
+
"aiAnalysisStatus": ai_status,
|
|
177
|
+
"aiAnalysisStatusText": ai_status_text,
|
|
178
|
+
"codeFlows": code_flows,
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
# 根据 AI 分析状态分类
|
|
182
|
+
if ai_status == 3: # 误报
|
|
183
|
+
false_positive_vuls.append(vul_entry)
|
|
184
|
+
elif ai_status == 1: # 分析中,不进入修复流程
|
|
185
|
+
analyzing_vuls.append(vul_entry)
|
|
186
|
+
elif is_sensitive:
|
|
187
|
+
sensitive_vuls.append(vul_entry)
|
|
188
|
+
else:
|
|
189
|
+
common_vuls.append(vul_entry)
|
|
190
|
+
|
|
191
|
+
# 按严重程度排序
|
|
192
|
+
def sort_key(v):
|
|
193
|
+
return (LEVEL_PRIORITY.get(v["level"], 99), v["file"], v["startLine"])
|
|
194
|
+
|
|
195
|
+
common_vuls.sort(key=sort_key)
|
|
196
|
+
sensitive_vuls.sort(key=sort_key)
|
|
197
|
+
false_positive_vuls.sort(key=sort_key)
|
|
198
|
+
analyzing_vuls.sort(key=sort_key)
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
"total": len(common_vuls) + len(sensitive_vuls) + len(false_positive_vuls) + len(analyzing_vuls),
|
|
202
|
+
"common_count": len(common_vuls),
|
|
203
|
+
"sensitive_count": len(sensitive_vuls),
|
|
204
|
+
"false_positive_count": len(false_positive_vuls),
|
|
205
|
+
"analyzing_count": len(analyzing_vuls),
|
|
206
|
+
"bundle_hash": bundle_hash,
|
|
207
|
+
"common_vuls": common_vuls,
|
|
208
|
+
"sensitive_vuls": sensitive_vuls,
|
|
209
|
+
"false_positive_vuls": false_positive_vuls,
|
|
210
|
+
"analyzing_vuls": analyzing_vuls,
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _make_file_link(file_path, line, project_dir=""):
|
|
215
|
+
# type: (str, int, str) -> str
|
|
216
|
+
"""生成可点击的文件链接。"""
|
|
217
|
+
if not file_path:
|
|
218
|
+
return "未知位置"
|
|
219
|
+
basename = os.path.basename(file_path)
|
|
220
|
+
label = "{}:{}".format(basename, line)
|
|
221
|
+
if project_dir:
|
|
222
|
+
abs_path = os.path.join(project_dir, file_path)
|
|
223
|
+
else:
|
|
224
|
+
abs_path = file_path
|
|
225
|
+
return "[{}]({}#L{})".format(label, abs_path, line)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def format_vul_report(vuls, title="漏洞报告", project_dir=""):
|
|
229
|
+
# type: (list, str, str) -> str
|
|
230
|
+
"""将漏洞列表按类型分组格式化为 Markdown 报告。
|
|
231
|
+
|
|
232
|
+
同一 ruleID 的漏洞归为一组,描述/等级/修复建议只展示一次,
|
|
233
|
+
漏洞位置列表化展示,数据流用 <details> 折叠。
|
|
234
|
+
"""
|
|
235
|
+
if not vuls:
|
|
236
|
+
return ""
|
|
237
|
+
|
|
238
|
+
# 按 ruleID 分组,保持原有排序
|
|
239
|
+
groups = OrderedDict() # type: OrderedDict[str, list]
|
|
240
|
+
for vul in vuls:
|
|
241
|
+
rule_id = vul["ruleID"]
|
|
242
|
+
if rule_id not in groups:
|
|
243
|
+
groups[rule_id] = []
|
|
244
|
+
groups[rule_id].append(vul)
|
|
245
|
+
|
|
246
|
+
lines = ["**{}**\n".format(title)]
|
|
247
|
+
|
|
248
|
+
for group_idx, (rule_id, group_vuls) in enumerate(groups.items(), 1):
|
|
249
|
+
rep = group_vuls[0] # 取第一个作为代表获取公共信息
|
|
250
|
+
count = len(group_vuls)
|
|
251
|
+
count_suffix = "({} 处)".format(count) if count > 1 else ""
|
|
252
|
+
|
|
253
|
+
lines.append("{}. **{}**{}".format(group_idx, rep["name"], count_suffix))
|
|
254
|
+
if rep["description"]:
|
|
255
|
+
lines.append("- **漏洞描述**:{}".format(rep["description"]))
|
|
256
|
+
lines.append("- **漏洞等级**:{}".format(rep["level_cn"]))
|
|
257
|
+
if rep["suggestion"]:
|
|
258
|
+
lines.append("- **修复建议**:{}".format(rep["suggestion"]))
|
|
259
|
+
|
|
260
|
+
# 漏洞位置列表
|
|
261
|
+
lines.append("- **漏洞位置**:")
|
|
262
|
+
for vul in group_vuls:
|
|
263
|
+
loc_link = _make_file_link(vul["file"], vul["startLine"], project_dir)
|
|
264
|
+
lines.append(" - {}".format(loc_link))
|
|
265
|
+
|
|
266
|
+
# 数据流折叠展示
|
|
267
|
+
if vul["codeFlows"]:
|
|
268
|
+
lines.append(" <details><summary>数据流</summary>\n")
|
|
269
|
+
for hop_idx, hop in enumerate(vul["codeFlows"], 1):
|
|
270
|
+
hop_link = _make_file_link(hop["file"], hop["line"], project_dir)
|
|
271
|
+
hop_msg = hop.get("message", "")
|
|
272
|
+
if hop_msg:
|
|
273
|
+
lines.append(" {}. {} — {}".format(hop_idx, hop_link, hop_msg))
|
|
274
|
+
else:
|
|
275
|
+
lines.append(" {}. {}".format(hop_idx, hop_link))
|
|
276
|
+
lines.append("\n </details>")
|
|
277
|
+
lines.append("")
|
|
278
|
+
|
|
279
|
+
return "\n".join(lines)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def format_full_report(parsed, project_dir=""):
|
|
283
|
+
# type: (dict, str) -> str
|
|
284
|
+
"""生成完整的展示报告(包含各类漏洞)。"""
|
|
285
|
+
parts = []
|
|
286
|
+
|
|
287
|
+
common_count = parsed["common_count"]
|
|
288
|
+
sensitive_count = parsed["sensitive_count"]
|
|
289
|
+
false_positive_count = parsed.get("false_positive_count", 0)
|
|
290
|
+
analyzing_count = parsed.get("analyzing_count", 0)
|
|
291
|
+
real_vul_count = common_count + sensitive_count # 需要处理的真实漏洞数
|
|
292
|
+
|
|
293
|
+
# 汇总信息
|
|
294
|
+
summary_parts = []
|
|
295
|
+
if real_vul_count > 0:
|
|
296
|
+
if common_count > 0 and sensitive_count > 0:
|
|
297
|
+
summary_parts.append("**{}** 个普通漏洞和 **{}** 个硬编码漏洞".format(common_count, sensitive_count))
|
|
298
|
+
elif common_count > 0:
|
|
299
|
+
summary_parts.append("**{}** 个普通漏洞".format(common_count))
|
|
300
|
+
elif sensitive_count > 0:
|
|
301
|
+
summary_parts.append("**{}** 个硬编码漏洞".format(sensitive_count))
|
|
302
|
+
|
|
303
|
+
if false_positive_count > 0:
|
|
304
|
+
summary_parts.append("**{}** 个误报(已由 AI 分析确认,无需修复)".format(false_positive_count))
|
|
305
|
+
|
|
306
|
+
if summary_parts:
|
|
307
|
+
parts.append("扫描发现 {}。\n".format(",".join(summary_parts)))
|
|
308
|
+
else:
|
|
309
|
+
if analyzing_count > 0:
|
|
310
|
+
parts.append("扫描完成,**{}** 个漏洞正在 AI 分析中,暂未发现已确认的漏洞。\n".format(analyzing_count))
|
|
311
|
+
else:
|
|
312
|
+
parts.append("扫描完成,未发现漏洞。\n")
|
|
313
|
+
return "\n".join(parts)
|
|
314
|
+
|
|
315
|
+
if analyzing_count > 0:
|
|
316
|
+
parts.append("另有 **{}** 个漏洞正在 AI 分析中,分析完成后可能会被判定为误报而排除,分析期间暂不处理。\n".format(analyzing_count))
|
|
317
|
+
|
|
318
|
+
# 只有误报没有真实漏洞的情况
|
|
319
|
+
if real_vul_count == 0 and false_positive_count > 0:
|
|
320
|
+
parts.append("所有检测到的漏洞均已被 AI 分析确认为误报,无需进行修复。\n")
|
|
321
|
+
if parsed.get("false_positive_vuls"):
|
|
322
|
+
parts.append(format_vul_report(parsed["false_positive_vuls"], "误报漏洞列表(仅供参考)", project_dir))
|
|
323
|
+
return "\n".join(parts)
|
|
324
|
+
|
|
325
|
+
if parsed["common_vuls"]:
|
|
326
|
+
parts.append(format_vul_report(parsed["common_vuls"], "普通漏洞报告", project_dir))
|
|
327
|
+
|
|
328
|
+
if parsed["sensitive_vuls"]:
|
|
329
|
+
parts.append(format_vul_report(parsed["sensitive_vuls"], "硬编码漏洞报告", project_dir))
|
|
330
|
+
|
|
331
|
+
# 误报漏洞折叠展示
|
|
332
|
+
if false_positive_count > 0:
|
|
333
|
+
parts.append("\n<details><summary>误报漏洞({} 个,已由 AI 确认无需修复)</summary>\n".format(false_positive_count))
|
|
334
|
+
for vul in parsed.get("false_positive_vuls", []):
|
|
335
|
+
loc_link = _make_file_link(vul["file"], vul["startLine"], project_dir)
|
|
336
|
+
parts.append("- {} - {}".format(vul["name"], loc_link))
|
|
337
|
+
parts.append("\n</details>")
|
|
338
|
+
|
|
339
|
+
# AI 分析中漏洞折叠展示
|
|
340
|
+
if analyzing_count > 0:
|
|
341
|
+
parts.append("\n<details><summary>AI 分析中({} 个,分析完成前暂不处理)</summary>\n".format(analyzing_count))
|
|
342
|
+
for vul in parsed.get("analyzing_vuls", []):
|
|
343
|
+
loc_link = _make_file_link(vul["file"], vul["startLine"], project_dir)
|
|
344
|
+
parts.append("- {} - {}".format(vul["name"], loc_link))
|
|
345
|
+
parts.append("\n</details>")
|
|
346
|
+
|
|
347
|
+
return "\n".join(parts)
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def main():
|
|
351
|
+
"""
|
|
352
|
+
主函数入口,解析命令行参数并处理扫描结果
|
|
353
|
+
|
|
354
|
+
Args:
|
|
355
|
+
无(通过命令行参数 --scan-result 和 --output-dir 传入)
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
无(结果直接输出到标准输出和文件)
|
|
359
|
+
"""
|
|
360
|
+
parser = argparse.ArgumentParser(description="扫描结果解析与展示工具")
|
|
361
|
+
parser.add_argument("--scan-result", required=True, help="扫描结果文件路径 (scan_result.json)")
|
|
362
|
+
parser.add_argument("--output-dir", default=None, help="结构化结果输出目录,默认与输入文件同目录")
|
|
363
|
+
parser.add_argument("--project-dir", default="", help="项目根目录,用于生成可点击的绝对路径链接")
|
|
364
|
+
args = parser.parse_args()
|
|
365
|
+
|
|
366
|
+
# 读取扫描结果
|
|
367
|
+
try:
|
|
368
|
+
with open(args.scan_result, "r", encoding="utf-8") as f:
|
|
369
|
+
scan_result = json.load(f)
|
|
370
|
+
except Exception as e:
|
|
371
|
+
print("错误: 读取扫描结果失败 {}: {}".format(args.scan_result, e), file=sys.stderr)
|
|
372
|
+
sys.exit(1)
|
|
373
|
+
|
|
374
|
+
# 解析
|
|
375
|
+
parsed = parse_scan_result(scan_result)
|
|
376
|
+
|
|
377
|
+
# 输出 Markdown 报告到标准输出
|
|
378
|
+
report = format_full_report(parsed, args.project_dir)
|
|
379
|
+
print(report)
|
|
380
|
+
|
|
381
|
+
# 保存结构化 JSON
|
|
382
|
+
# 默认输出到输入文件同目录,自然跟随项目隔离
|
|
383
|
+
output_dir = args.output_dir or os.path.dirname(os.path.abspath(args.scan_result))
|
|
384
|
+
os.makedirs(output_dir, exist_ok=True)
|
|
385
|
+
output_file = os.path.join(output_dir, "parsed_result.json")
|
|
386
|
+
with open(output_file, "w", encoding="utf-8") as f:
|
|
387
|
+
json.dump(parsed, f, ensure_ascii=False, indent=2)
|
|
388
|
+
print(output_file, file=sys.stderr)
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
if __name__ == "__main__":
|
|
392
|
+
main()
|