Compare commits
17 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
0bf8159209 | 2 months ago |
|
|
960334f039 | 2 months ago |
|
|
6bc44294dc | 2 months ago |
|
|
c4b32439af | 2 months ago |
|
|
8c96cb3ca8 | 2 months ago |
|
|
56bd09304c | 2 months ago |
|
|
c84d29ed39 | 2 months ago |
|
|
28a448ba78 | 2 months ago |
|
|
c9e72ed447 | 3 months ago |
|
|
d526fe448d | 3 months ago |
|
|
092d74a47b | 3 months ago |
|
|
3d41771c4a | 3 months ago |
|
|
cd396d0f63 | 3 months ago |
|
|
df0eb0e075 | 3 months ago |
|
|
f6fdaf202b | 3 months ago |
|
|
edbf72b80f | 4 months ago |
|
|
8ff39a1941 | 4 months ago |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,45 @@
|
||||
"""
|
||||
数据模型和数据结构定义
|
||||
"""
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class IssueLocation:
|
||||
"""问题位置信息"""
|
||||
file_path: Path
|
||||
line: Optional[int]
|
||||
|
||||
|
||||
@dataclass
|
||||
class CppcheckIssue:
|
||||
"""Cppcheck问题信息"""
|
||||
id: str
|
||||
severity: str
|
||||
message: str
|
||||
locations: List[IssueLocation]
|
||||
|
||||
|
||||
@dataclass
|
||||
class CodeContext:
|
||||
"""代码上下文信息"""
|
||||
file_path: Path
|
||||
function_name: Optional[str] = None
|
||||
class_name: Optional[str] = None
|
||||
namespace: Optional[str] = None
|
||||
includes: List[str] = None
|
||||
dependencies: List[str] = None
|
||||
variable_context: List[str] = None
|
||||
control_flow_context: List[str] = None
|
||||
|
||||
def __post_init__(self):
|
||||
if self.includes is None:
|
||||
self.includes = []
|
||||
if self.dependencies is None:
|
||||
self.dependencies = []
|
||||
if self.variable_context is None:
|
||||
self.variable_context = []
|
||||
if self.control_flow_context is None:
|
||||
self.control_flow_context = []
|
||||
@ -0,0 +1,101 @@
|
||||
"""
|
||||
Cppcheck报告解析器模块
|
||||
"""
|
||||
import re
|
||||
import xml.etree.ElementTree as ET
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
from .models import CppcheckIssue, IssueLocation
|
||||
|
||||
|
||||
def parse_cppcheck_xml(xml_path: Path) -> List[CppcheckIssue]:
|
||||
"""解析cppcheck XML报告"""
|
||||
tree = ET.parse(xml_path)
|
||||
root = tree.getroot()
|
||||
|
||||
issues: List[CppcheckIssue] = []
|
||||
for error in root.findall("errors/error"):
|
||||
issue_id = error.get("id") or "unknown"
|
||||
severity = error.get("severity") or "unknown"
|
||||
msg = error.get("msg") or (error.get("verbose") or "")
|
||||
|
||||
locations: List[IssueLocation] = []
|
||||
for loc in error.findall("location"):
|
||||
file_attr = loc.get("file")
|
||||
line_attr = loc.get("line")
|
||||
if not file_attr:
|
||||
continue
|
||||
file_path = Path(file_attr).expanduser().resolve()
|
||||
line = int(line_attr) if line_attr and line_attr.isdigit() else None
|
||||
locations.append(IssueLocation(file_path=file_path, line=line))
|
||||
|
||||
if not locations:
|
||||
# 有些 error 只有一层 <error file= line=>
|
||||
file_attr = error.get("file")
|
||||
line_attr = error.get("line")
|
||||
if file_attr:
|
||||
locations.append(
|
||||
IssueLocation(
|
||||
file_path=Path(file_attr).expanduser().resolve(),
|
||||
line=int(line_attr) if line_attr and str(line_attr).isdigit() else None,
|
||||
)
|
||||
)
|
||||
|
||||
issues.append(CppcheckIssue(id=issue_id, severity=severity, message=msg, locations=locations))
|
||||
|
||||
return issues
|
||||
|
||||
|
||||
def parse_cppcheck_text(text_path: Path) -> List[CppcheckIssue]:
|
||||
"""解析 cppcheck 文本日志(常见行格式:
|
||||
/path/file.c:111:13: warning: Message [ruleId]
|
||||
也包含 note:/information:/error: 等等级
|
||||
"""
|
||||
content = text_path.read_text(encoding="utf-8", errors="replace")
|
||||
issues: List[CppcheckIssue] = []
|
||||
|
||||
# 常见匹配:路径:行:列: 等级: 消息 [规则]
|
||||
pattern = re.compile(r"^(?P<file>[^:\n]+?):(?P<line>\d+)(?::\d+)?\:\s*(?P<sev>warning|error|information|note)\:\s*(?P<msg>.*?)(?:\s*\[(?P<id>[^\]]+)\])?\s*$",
|
||||
re.IGNORECASE)
|
||||
|
||||
for raw_line in content.splitlines():
|
||||
m = pattern.match(raw_line.strip())
|
||||
if not m:
|
||||
continue
|
||||
file_path = Path(m.group("file")).expanduser()
|
||||
try:
|
||||
file_path = file_path.resolve()
|
||||
except Exception:
|
||||
pass
|
||||
line_num = int(m.group("line")) if m.group("line") else None
|
||||
sev = (m.group("sev") or "").lower()
|
||||
msg = m.group("msg") or ""
|
||||
rid = m.group("id") or "unknown"
|
||||
issues.append(
|
||||
CppcheckIssue(
|
||||
id=rid,
|
||||
severity=sev,
|
||||
message=msg,
|
||||
locations=[IssueLocation(file_path=file_path, line=line_num)],
|
||||
)
|
||||
)
|
||||
|
||||
return issues
|
||||
|
||||
|
||||
def read_code_snippet(file_path: Path, center_line: Optional[int], context: int = 30) -> str:
|
||||
"""读取代码片段"""
|
||||
try:
|
||||
lines = file_path.read_text(encoding="utf-8", errors="replace").splitlines()
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
if center_line is None:
|
||||
start = 0
|
||||
end = min(len(lines), 400)
|
||||
else:
|
||||
start = max(0, center_line - 1 - context)
|
||||
end = min(len(lines), center_line - 1 + context)
|
||||
snippet = "\n".join(lines[start:end])
|
||||
return snippet
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "platform-frontend",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
from openai import OpenAI
|
||||
|
||||
# 直接把你的 DeepSeek 密钥填在这里
|
||||
client = OpenAI(
|
||||
api_key="sk-0f12f1d7a48f4fb3b305a66f2948bfb9",
|
||||
base_url="https://api.deepseek.com/v1",
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
stream = client.chat.completions.create(
|
||||
model="deepseek-chat",
|
||||
messages=[{"role": "user", "content": "用三句话解释量子纠缠"}],
|
||||
stream=True,
|
||||
)
|
||||
|
||||
for chunk in stream:
|
||||
print(chunk.choices[0].delta.content or "", end="")
|
||||
print()
|
||||
@ -0,0 +1,437 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<results version="2">
|
||||
<cppcheck version="2.7"/>
|
||||
<errors>
|
||||
<error id="arrayIndexOutOfBounds" severity="error" msg="Array 'arr[3]' accessed at index 3, which is out of bounds." verbose="Array 'arr[3]' accessed at index 3, which is out of bounds." cwe="788" file0="/home/feng/test/math.c">
|
||||
<location file="/home/feng/test/math.c" line="1393" column="8" info="Array index out of bounds"/>
|
||||
<location file="/home/feng/test/math.c" line="1392" column="17" info="Assignment 'index=3', assigned value is 3"/>
|
||||
</error>
|
||||
<error id="zerodiv" severity="error" msg="Division by zero." verbose="Division by zero." cwe="369" file0="/home/feng/test/math.c">
|
||||
<location file="/home/feng/test/math.c" line="1053" column="14" info="Division by zero"/>
|
||||
<location file="/home/feng/test/math.c" line="1052" column="13" info="Assignment 'b=0', assigned value is 0"/>
|
||||
</error>
|
||||
<error id="shadowFunction" severity="style" msg="Local variable 'max' shadows outer function" verbose="Local variable 'max' shadows outer function" cwe="398" file0="/home/feng/test/math.c">
|
||||
<location file="/home/feng/test/math.c" line="656" column="12" info="Shadow variable"/>
|
||||
<location file="/home/feng/test/math.h" line="179" column="12" info="Shadowed declaration"/>
|
||||
<symbol>max</symbol>
|
||||
</error>
|
||||
<error id="shadowFunction" severity="style" msg="Local variable 'min' shadows outer function" verbose="Local variable 'min' shadows outer function" cwe="398" file0="/home/feng/test/math.c">
|
||||
<location file="/home/feng/test/math.c" line="670" column="12" info="Shadow variable"/>
|
||||
<location file="/home/feng/test/math.h" line="178" column="12" info="Shadowed declaration"/>
|
||||
<symbol>min</symbol>
|
||||
</error>
|
||||
<error id="constParameter" severity="style" msg="Parameter 'array' can be declared with const" verbose="Parameter 'array' can be declared with const" cwe="398" file0="/home/feng/test/math.c">
|
||||
<location file="/home/feng/test/math.c" line="445" column="27" info="Parameter 'array' can be declared with const"/>
|
||||
<symbol>array</symbol>
|
||||
</error>
|
||||
<error id="constParameter" severity="style" msg="Parameter 'array' can be declared with const" verbose="Parameter 'array' can be declared with const" cwe="398" file0="/home/feng/test/math.c">
|
||||
<location file="/home/feng/test/math.c" line="455" column="27" info="Parameter 'array' can be declared with const"/>
|
||||
<symbol>array</symbol>
|
||||
</error>
|
||||
<error id="constParameter" severity="style" msg="Parameter 'array' can be declared with const" verbose="Parameter 'array' can be declared with const" cwe="398" file0="/home/feng/test/math.c">
|
||||
<location file="/home/feng/test/math.c" line="652" column="25" info="Parameter 'array' can be declared with const"/>
|
||||
<symbol>array</symbol>
|
||||
</error>
|
||||
<error id="constParameter" severity="style" msg="Parameter 'array' can be declared with const" verbose="Parameter 'array' can be declared with const" cwe="398" file0="/home/feng/test/math.c">
|
||||
<location file="/home/feng/test/math.c" line="666" column="25" info="Parameter 'array' can be declared with const"/>
|
||||
<symbol>array</symbol>
|
||||
</error>
|
||||
<error id="constParameter" severity="style" msg="Parameter 'array' can be declared with const" verbose="Parameter 'array' can be declared with const" cwe="398" file0="/home/feng/test/math.c">
|
||||
<location file="/home/feng/test/math.c" line="677" column="34" info="Parameter 'array' can be declared with const"/>
|
||||
<symbol>array</symbol>
|
||||
</error>
|
||||
<error id="unreadVariable" severity="style" msg="Variable 'arr[index]' is assigned a value that is never used." verbose="Variable 'arr[index]' is assigned a value that is never used." cwe="563" file0="/home/feng/test/math.c">
|
||||
<location file="/home/feng/test/math.c" line="1393" column="16"/>
|
||||
<symbol>arr[index]</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'absolute_value' is never used." verbose="The function 'absolute_value' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="52" column="0"/>
|
||||
<symbol>absolute_value</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'add' is never used." verbose="The function 'add' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="11" column="0"/>
|
||||
<symbol>add</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'arccosine' is never used." verbose="The function 'arccosine' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="90" column="0"/>
|
||||
<symbol>arccosine</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'arcsine' is never used." verbose="The function 'arcsine' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="80" column="0"/>
|
||||
<symbol>arcsine</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'arctangent' is never used." verbose="The function 'arctangent' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="100" column="0"/>
|
||||
<symbol>arctangent</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'backward_difference' is never used." verbose="The function 'backward_difference' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="227" column="0"/>
|
||||
<symbol>backward_difference</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'binary_search' is never used." verbose="The function 'binary_search' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="455" column="0"/>
|
||||
<symbol>binary_search</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'bubble_sort' is never used." verbose="The function 'bubble_sort' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="322" column="0"/>
|
||||
<symbol>bubble_sort</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'calculate_all' is never used." verbose="The function 'calculate_all' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="925" column="0"/>
|
||||
<symbol>calculate_all</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'calculate_average' is never used." verbose="The function 'calculate_average' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="677" column="0"/>
|
||||
<symbol>calculate_average</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'central_difference' is never used." verbose="The function 'central_difference' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="232" column="0"/>
|
||||
<symbol>central_difference</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'character_type' is never used." verbose="The function 'character_type' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="494" column="0"/>
|
||||
<symbol>character_type</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'circle_area' is never used." verbose="The function 'circle_area' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="624" column="0"/>
|
||||
<symbol>circle_area</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'contains_substring' is never used." verbose="The function 'contains_substring' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="917" column="0"/>
|
||||
<symbol>contains_substring</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'cosine' is never used." verbose="The function 'cosine' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="70" column="0"/>
|
||||
<symbol>cosine</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'count_char_occurrences' is never used." verbose="The function 'count_char_occurrences' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="906" column="0"/>
|
||||
<symbol>count_char_occurrences</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'count_digits' is never used." verbose="The function 'count_digits' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="640" column="0"/>
|
||||
<symbol>count_digits</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'count_words' is never used." verbose="The function 'count_words' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="696" column="0"/>
|
||||
<symbol>count_words</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'create_polynomial' is never used." verbose="The function 'create_polynomial' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="117" column="0"/>
|
||||
<symbol>create_polynomial</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'create_queue' is never used." verbose="The function 'create_queue' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="238" column="0"/>
|
||||
<symbol>create_queue</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'cube' is never used." verbose="The function 'cube' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="845" column="0"/>
|
||||
<symbol>cube</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'divide' is never used." verbose="The function 'divide' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="26" column="0"/>
|
||||
<symbol>divide</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'ends_with' is never used." verbose="The function 'ends_with' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="897" column="0"/>
|
||||
<symbol>ends_with</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'evaluate_polynomial' is never used." verbose="The function 'evaluate_polynomial' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="145" column="0"/>
|
||||
<symbol>evaluate_polynomial</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'exponential' is never used." verbose="The function 'exponential' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="106" column="0"/>
|
||||
<symbol>exponential</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'find_max' is never used." verbose="The function 'find_max' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="652" column="0"/>
|
||||
<symbol>find_max</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'find_min' is never used." verbose="The function 'find_min' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="666" column="0"/>
|
||||
<symbol>find_min</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'find_polynomial_roots' is never used." verbose="The function 'find_polynomial_roots' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="161" column="0"/>
|
||||
<symbol>find_polynomial_roots</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'force_out_of_bound_access' is never used." verbose="The function 'force_out_of_bound_access' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1390" column="0"/>
|
||||
<symbol>force_out_of_bound_access</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'forward_difference' is never used." verbose="The function 'forward_difference' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="222" column="0"/>
|
||||
<symbol>forward_difference</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'free_polynomial' is never used." verbose="The function 'free_polynomial' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="137" column="0"/>
|
||||
<symbol>free_polynomial</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'free_queue' is never used." verbose="The function 'free_queue' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="250" column="0"/>
|
||||
<symbol>free_queue</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'insertion_sort' is never used." verbose="The function 'insertion_sort' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="350" column="0"/>
|
||||
<symbol>insertion_sort</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'integer_division_by_zero' is never used." verbose="The function 'integer_division_by_zero' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1051" column="0"/>
|
||||
<symbol>integer_division_by_zero</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_21st_century' is never used." verbose="The function 'is_21st_century' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="781" column="0"/>
|
||||
<symbol>is_21st_century</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_alphabetic' is never used." verbose="The function 'is_alphabetic' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="732" column="0"/>
|
||||
<symbol>is_alphabetic</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_digit' is never used." verbose="The function 'is_digit' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="747" column="0"/>
|
||||
<symbol>is_digit</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_finite' is never used." verbose="The function 'is_finite' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1032" column="0"/>
|
||||
<symbol>is_finite</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_infinite' is never used." verbose="The function 'is_infinite' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="992" column="0"/>
|
||||
<symbol>is_infinite</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_lowercase' is never used." verbose="The function 'is_lowercase' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="770" column="0"/>
|
||||
<symbol>is_lowercase</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_nan' is never used." verbose="The function 'is_nan' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="997" column="0"/>
|
||||
<symbol>is_nan</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_negative_infinite' is never used." verbose="The function 'is_negative_infinite' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1022" column="0"/>
|
||||
<symbol>is_negative_infinite</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_non_negative' is never used." verbose="The function 'is_non_negative' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1048" column="0"/>
|
||||
<symbol>is_non_negative</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_non_positive' is never used." verbose="The function 'is_non_positive' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1056" column="0"/>
|
||||
<symbol>is_non_positive</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_normal_natural_even' is never used." verbose="The function 'is_normal_natural_even' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1291" column="0"/>
|
||||
<symbol>is_normal_natural_even</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_normal_natural_odd' is never used." verbose="The function 'is_normal_natural_odd' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1301" column="0"/>
|
||||
<symbol>is_normal_natural_odd</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_normal_negative' is never used." verbose="The function 'is_normal_negative' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1231" column="0"/>
|
||||
<symbol>is_normal_negative</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_normal_negative_even' is never used." verbose="The function 'is_normal_negative_even' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1271" column="0"/>
|
||||
<symbol>is_normal_negative_even</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_normal_negative_float' is never used." verbose="The function 'is_normal_negative_float' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1321" column="0"/>
|
||||
<symbol>is_normal_negative_float</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_normal_negative_integer' is never used." verbose="The function 'is_normal_negative_integer' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1341" column="0"/>
|
||||
<symbol>is_normal_negative_integer</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_normal_negative_odd' is never used." verbose="The function 'is_normal_negative_odd' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1281" column="0"/>
|
||||
<symbol>is_normal_negative_odd</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_normal_negative_perfect_cube' is never used." verbose="The function 'is_normal_negative_perfect_cube' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1381" column="0"/>
|
||||
<symbol>is_normal_negative_perfect_cube</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_normal_positive' is never used." verbose="The function 'is_normal_positive' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1221" column="0"/>
|
||||
<symbol>is_normal_positive</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_normal_positive_even' is never used." verbose="The function 'is_normal_positive_even' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1251" column="0"/>
|
||||
<symbol>is_normal_positive_even</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_normal_positive_float' is never used." verbose="The function 'is_normal_positive_float' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1311" column="0"/>
|
||||
<symbol>is_normal_positive_float</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_normal_positive_integer' is never used." verbose="The function 'is_normal_positive_integer' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1331" column="0"/>
|
||||
<symbol>is_normal_positive_integer</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_normal_positive_odd' is never used." verbose="The function 'is_normal_positive_odd' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1261" column="0"/>
|
||||
<symbol>is_normal_positive_odd</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_normal_positive_perfect_cube' is never used." verbose="The function 'is_normal_positive_perfect_cube' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1371" column="0"/>
|
||||
<symbol>is_normal_positive_perfect_cube</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_normal_positive_perfect_square' is never used." verbose="The function 'is_normal_positive_perfect_square' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1361" column="0"/>
|
||||
<symbol>is_normal_positive_perfect_square</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_normal_positive_prime' is never used." verbose="The function 'is_normal_positive_prime' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1351" column="0"/>
|
||||
<symbol>is_normal_positive_prime</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_normal_zero' is never used." verbose="The function 'is_normal_zero' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1241" column="0"/>
|
||||
<symbol>is_normal_zero</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_palindrome' is never used." verbose="The function 'is_palindrome' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="629" column="0"/>
|
||||
<symbol>is_palindrome</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_positive_infinite' is never used." verbose="The function 'is_positive_infinite' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1012" column="0"/>
|
||||
<symbol>is_positive_infinite</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_positive_natural' is never used." verbose="The function 'is_positive_natural' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1171" column="0"/>
|
||||
<symbol>is_positive_natural</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_representable' is never used." verbose="The function 'is_representable' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1037" column="0"/>
|
||||
<symbol>is_representable</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_uppercase' is never used." verbose="The function 'is_uppercase' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="758" column="0"/>
|
||||
<symbol>is_uppercase</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_valid_date' is never used." verbose="The function 'is_valid_date' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="824" column="0"/>
|
||||
<symbol>is_valid_date</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_vowel' is never used." verbose="The function 'is_vowel' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="579" column="0"/>
|
||||
<symbol>is_vowel</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_within_range' is never used." verbose="The function 'is_within_range' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="954" column="0"/>
|
||||
<symbol>is_within_range</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_zero_or_negative' is never used." verbose="The function 'is_zero_or_negative' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1146" column="0"/>
|
||||
<symbol>is_zero_or_negative</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'is_zero_or_positive' is never used." verbose="The function 'is_zero_or_positive' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="1141" column="0"/>
|
||||
<symbol>is_zero_or_positive</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'linear_search' is never used." verbose="The function 'linear_search' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="445" column="0"/>
|
||||
<symbol>linear_search</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'max' is never used." verbose="The function 'max' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="879" column="0"/>
|
||||
<symbol>max</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'min' is never used." verbose="The function 'min' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="869" column="0"/>
|
||||
<symbol>min</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'multiply' is never used." verbose="The function 'multiply' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="21" column="0"/>
|
||||
<symbol>multiply</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'number_interval' is never used." verbose="The function 'number_interval' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="472" column="0"/>
|
||||
<symbol>number_interval</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'number_relationship' is never used." verbose="The function 'number_relationship' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="535" column="0"/>
|
||||
<symbol>number_relationship</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'piecewise_function' is never used." verbose="The function 'piecewise_function' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="517" column="0"/>
|
||||
<symbol>piecewise_function</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'power' is never used." verbose="The function 'power' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="36" column="0"/>
|
||||
<symbol>power</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'queue_dequeue' is never used." verbose="The function 'queue_dequeue' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="284" column="0"/>
|
||||
<symbol>queue_dequeue</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'queue_enqueue' is never used." verbose="The function 'queue_enqueue' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="268" column="0"/>
|
||||
<symbol>queue_enqueue</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'queue_front' is never used." verbose="The function 'queue_front' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="304" column="0"/>
|
||||
<symbol>queue_front</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'queue_rear' is never used." verbose="The function 'queue_rear' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="313" column="0"/>
|
||||
<symbol>queue_rear</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'rectangle_area' is never used." verbose="The function 'rectangle_area' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="614" column="0"/>
|
||||
<symbol>rectangle_area</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'round_to_nearest' is never used." verbose="The function 'round_to_nearest' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="57" column="0"/>
|
||||
<symbol>round_to_nearest</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'selection_sort' is never used." verbose="The function 'selection_sort' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="335" column="0"/>
|
||||
<symbol>selection_sort</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'simple_calculator' is never used." verbose="The function 'simple_calculator' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="712" column="0"/>
|
||||
<symbol>simple_calculator</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'simpson_rule' is never used." verbose="The function 'simpson_rule' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="204" column="0"/>
|
||||
<symbol>simpson_rule</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'sine' is never used." verbose="The function 'sine' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="65" column="0"/>
|
||||
<symbol>sine</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'square' is never used." verbose="The function 'square' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="588" column="0"/>
|
||||
<symbol>square</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'square_root' is never used." verbose="The function 'square_root' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="41" column="0"/>
|
||||
<symbol>square_root</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'starts_with' is never used." verbose="The function 'starts_with' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="889" column="0"/>
|
||||
<symbol>starts_with</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'subtract' is never used." verbose="The function 'subtract' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="16" column="0"/>
|
||||
<symbol>subtract</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'tangent' is never used." verbose="The function 'tangent' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="75" column="0"/>
|
||||
<symbol>tangent</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'traffic_light' is never used." verbose="The function 'traffic_light' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="548" column="0"/>
|
||||
<symbol>traffic_light</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'trapezoidal_rule' is never used." verbose="The function 'trapezoidal_rule' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="186" column="0"/>
|
||||
<symbol>trapezoidal_rule</symbol>
|
||||
</error>
|
||||
<error id="unusedFunction" severity="style" msg="The function 'triangle_area' is never used." verbose="The function 'triangle_area' is never used." cwe="561">
|
||||
<location file="/home/feng/test/math.c" line="619" column="0"/>
|
||||
<symbol>triangle_area</symbol>
|
||||
</error>
|
||||
<error id="missingIncludeSystem" severity="information" msg="Cppcheck cannot find all the include files (use --check-config for details)" verbose="Cppcheck cannot find all the include files. Cppcheck can check the code without the include files found. But the results will probably be more accurate if all the include files are found. Please check your project's include directories and add all of them as include directories for Cppcheck. To see what files Cppcheck cannot find use --check-config."/>
|
||||
</errors>
|
||||
</results>
|
||||
@ -0,0 +1,357 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Adapter that converts cppcheck_test_generator outputs into the unified Issue schema.
|
||||
|
||||
Typical usage:
|
||||
python3 cppcheck_adapter.py \
|
||||
--report /path/to/cppcheck_report.xml \
|
||||
--issues-dir /path/to/cppcheck_tests \
|
||||
--output unified_report/cppcheck_issues.json \
|
||||
--verification /path/to/cppcheck_tests/verification_results.json
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from dataclasses import dataclass, asdict
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Iterable, List, Optional, Tuple
|
||||
|
||||
# Ensure we can import the cppcheck_test_generator package.
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
if str(PROJECT_ROOT) not in sys.path:
|
||||
sys.path.append(str(PROJECT_ROOT))
|
||||
|
||||
from cppcheck_test_generator.models import CppcheckIssue # type: ignore # noqa: E402
|
||||
from cppcheck_test_generator.parsers import parse_cppcheck_xml # type: ignore # noqa: E402
|
||||
|
||||
|
||||
SEVERITY_MAP = {
|
||||
"error": "HIGH",
|
||||
"warning": "MEDIUM",
|
||||
"style": "LOW",
|
||||
"performance": "LOW",
|
||||
"portability": "LOW",
|
||||
"information": "INFO",
|
||||
"note": "INFO",
|
||||
}
|
||||
|
||||
CVSS_BASE = {
|
||||
"CRITICAL": 9.0,
|
||||
"HIGH": 8.0,
|
||||
"MEDIUM": 6.5,
|
||||
"LOW": 3.5,
|
||||
"INFO": 0.0,
|
||||
}
|
||||
|
||||
CATEGORY_MAP = {
|
||||
"memleak": "resource_management",
|
||||
"nullpointer": "memory_safety",
|
||||
"arrayindexoutofbounds": "memory_safety",
|
||||
"doublefree": "memory_safety",
|
||||
"useafterfree": "memory_safety",
|
||||
"uninitvar": "logic_bug",
|
||||
"zerodiv": "logic_bug",
|
||||
"mismatchallocdealloc": "resource_management",
|
||||
}
|
||||
|
||||
IMPACT_HINT = {
|
||||
"memleak": "持续泄漏会耗尽资源,导致服务性能下降或崩溃。",
|
||||
"nullpointer": "空指针解引用可能导致进程崩溃,可被用作拒绝服务。",
|
||||
"arrayindexoutofbounds": "数组越界访问可能破坏内存,造成未定义行为或远程代码执行。",
|
||||
"doublefree": "重复释放可能被利用进行堆喷射,从而执行任意代码。",
|
||||
"useafterfree": "释放后继续使用指针可能导致信息泄露或执行任意代码。",
|
||||
"uninitvar": "使用未初始化变量会导致不可预测行为或安全边界被绕过。",
|
||||
"zerodiv": "除零错误可能导致服务崩溃。",
|
||||
}
|
||||
|
||||
REFERENCE_MAP = {
|
||||
"memleak": ["https://cwe.mitre.org/data/definitions/401.html"],
|
||||
"nullpointer": ["https://cwe.mitre.org/data/definitions/476.html"],
|
||||
"arrayindexoutofbounds": ["https://cwe.mitre.org/data/definitions/119.html"],
|
||||
"doublefree": ["https://cwe.mitre.org/data/definitions/415.html"],
|
||||
"useafterfree": ["https://cwe.mitre.org/data/definitions/416.html"],
|
||||
"uninitvar": ["https://cwe.mitre.org/data/definitions/457.html"],
|
||||
"zerodiv": ["https://cwe.mitre.org/data/definitions/369.html"],
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class Issue:
|
||||
id: str
|
||||
source: Dict[str, Any]
|
||||
basic: Dict[str, Any]
|
||||
location: Dict[str, Any]
|
||||
severity: Dict[str, Any]
|
||||
status: Dict[str, Any]
|
||||
description: Dict[str, Any]
|
||||
reproduce: Dict[str, Any]
|
||||
root_cause: Dict[str, Any]
|
||||
impact: Dict[str, Any]
|
||||
fix: Dict[str, Any]
|
||||
|
||||
|
||||
def _normalize_severity(raw: str) -> str:
|
||||
if not raw:
|
||||
return "INFO"
|
||||
return SEVERITY_MAP.get(raw.lower(), raw.upper())
|
||||
|
||||
|
||||
def _cvss_for(severity: str) -> float:
|
||||
return CVSS_BASE.get(severity, 0.0)
|
||||
|
||||
|
||||
def _category_for(issue: CppcheckIssue) -> str:
|
||||
key = issue.id.lower()
|
||||
return CATEGORY_MAP.get(key, "logic_bug")
|
||||
|
||||
|
||||
def _impact_for(issue: CppcheckIssue) -> str:
|
||||
return IMPACT_HINT.get(issue.id.lower(), "可能影响系统稳定性与可用性。")
|
||||
|
||||
|
||||
def _references_for(issue: CppcheckIssue) -> List[str]:
|
||||
return REFERENCE_MAP.get(issue.id.lower(), [])
|
||||
|
||||
|
||||
def _parse_markdown_sections(text: str) -> Dict[str, str]:
|
||||
sections: Dict[str, str] = {}
|
||||
current = "body"
|
||||
buffer: List[str] = []
|
||||
heading_pattern = re.compile(r"^(#{1,6})\s+(.*)")
|
||||
|
||||
def flush():
|
||||
if buffer:
|
||||
sections[current] = "\n".join(buffer).strip()
|
||||
elif current not in sections:
|
||||
sections[current] = ""
|
||||
|
||||
for line in text.splitlines():
|
||||
match = heading_pattern.match(line.strip())
|
||||
if match:
|
||||
flush()
|
||||
current = match.group(2).strip().lower()
|
||||
buffer = []
|
||||
else:
|
||||
buffer.append(line)
|
||||
flush()
|
||||
return sections
|
||||
|
||||
|
||||
def _extract_section(sections: Dict[str, str], keywords: Iterable[str]) -> Optional[str]:
|
||||
for key in keywords:
|
||||
key_lower = key.lower()
|
||||
for section_key, value in sections.items():
|
||||
if key_lower in section_key:
|
||||
return value.strip()
|
||||
return None
|
||||
|
||||
|
||||
def _extract_list(text: Optional[str]) -> List[str]:
|
||||
if not text:
|
||||
return []
|
||||
items = []
|
||||
for line in text.splitlines():
|
||||
stripped = line.strip()
|
||||
if not stripped:
|
||||
continue
|
||||
if stripped[0] in ("-", "*"):
|
||||
stripped = stripped[1:].strip()
|
||||
items.append(stripped)
|
||||
return items
|
||||
|
||||
|
||||
def _load_verification_map(verification_path: Optional[Path], issues_dir: Path) -> Dict[str, Dict[str, Any]]:
|
||||
mapping: Dict[str, Dict[str, Any]] = {}
|
||||
|
||||
def record(entry: Dict[str, Any]) -> None:
|
||||
file_name = entry.get("file")
|
||||
if not file_name:
|
||||
return
|
||||
key = Path(file_name).stem # e.g., issue_001_memleak
|
||||
mapping[key] = entry
|
||||
|
||||
if verification_path and verification_path.exists():
|
||||
try:
|
||||
data = json.loads(verification_path.read_text(encoding="utf-8"))
|
||||
for entry in data.get("results", []):
|
||||
record(entry)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Also load per-issue verification JSON files if present
|
||||
for json_file in issues_dir.glob("verification_*.json"):
|
||||
try:
|
||||
entry = json.loads(json_file.read_text(encoding="utf-8"))
|
||||
record(entry)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
return mapping
|
||||
|
||||
|
||||
def _match_issue_files(issues_dir: Path) -> Dict[str, Tuple[Path, Optional[Path]]]:
|
||||
mapping: Dict[str, Tuple[Path, Optional[Path]]] = {}
|
||||
for md_file in sorted(issues_dir.glob("issue_*_*.md")):
|
||||
base = md_file.stem # issue_001_rule
|
||||
parts = base.split("_", 2)
|
||||
if len(parts) < 3:
|
||||
continue
|
||||
issue_id = parts[2]
|
||||
cpp_path = md_file.with_suffix(".cpp")
|
||||
mapping[issue_id.lower()] = (md_file, cpp_path if cpp_path.exists() else None)
|
||||
return mapping
|
||||
|
||||
|
||||
def _build_issue(
|
||||
cpp_issue: CppcheckIssue,
|
||||
md_path: Path,
|
||||
cpp_path: Optional[Path],
|
||||
verification_info: Optional[Dict[str, Any]],
|
||||
report_path: Path,
|
||||
) -> Issue:
|
||||
severity_level = _normalize_severity(cpp_issue.severity)
|
||||
issue_id = f"CPPC-{cpp_issue.id}-{md_path.stem.split('_')[1]}"
|
||||
location = cpp_issue.locations[0] if cpp_issue.locations else None
|
||||
|
||||
sections = _parse_markdown_sections(md_path.read_text(encoding="utf-8"))
|
||||
description_section = _extract_section(sections, ["漏洞描述", "问题描述", "description"])
|
||||
repro_section = _extract_section(sections, ["复现步骤", "重现步骤", "reproduction"])
|
||||
root_cause_section = _extract_section(sections, ["根本原因", "原因分析", "root cause"])
|
||||
impact_section = _extract_section(sections, ["潜在影响", "影响", "impact"])
|
||||
fix_section = _extract_section(sections, ["修复建议", "修复方案", "mitigation"])
|
||||
reference_section = _extract_section(sections, ["参考链接", "references"])
|
||||
|
||||
confirmed = False
|
||||
confirmed_by: List[str] = []
|
||||
if verification_info:
|
||||
confirmed = bool(
|
||||
verification_info.get("vulnerability_confirmed")
|
||||
or verification_info.get("triggers_cppcheck")
|
||||
)
|
||||
if confirmed:
|
||||
confirmed_by.append("generated_test")
|
||||
|
||||
reproduce_steps = _extract_list(repro_section) or [
|
||||
f"参阅 {md_path.name} 中的复现说明。",
|
||||
"编译并运行对应的测试用例以验证漏洞。",
|
||||
]
|
||||
if cpp_path:
|
||||
reproduce_steps.append(f"测试用例: {cpp_path}")
|
||||
|
||||
artifacts: Dict[str, Any] = {"analysis_markdown": str(md_path)}
|
||||
if cpp_path:
|
||||
artifacts["generated_test"] = str(cpp_path)
|
||||
if verification_info:
|
||||
artifacts["verification"] = verification_info
|
||||
|
||||
return Issue(
|
||||
id=issue_id,
|
||||
source={
|
||||
"engine": "cppcheck_ai",
|
||||
"sub_tool": "cppcheck",
|
||||
"raw_ids": [cpp_issue.id],
|
||||
"report_path": str(report_path),
|
||||
},
|
||||
basic={
|
||||
"title": f"{cpp_issue.id} - {location.file_path if location else '未知文件'}",
|
||||
"type": cpp_issue.id,
|
||||
"cwe": None,
|
||||
"category": _category_for(cpp_issue),
|
||||
},
|
||||
location={
|
||||
"file": str(location.file_path) if location else None,
|
||||
"function": None,
|
||||
"line": location.line if location else None,
|
||||
"column": None,
|
||||
"snippet": description_section or cpp_issue.message,
|
||||
},
|
||||
severity={
|
||||
"level": severity_level,
|
||||
"cvss": _cvss_for(severity_level),
|
||||
"cvss_vector": None,
|
||||
},
|
||||
status={
|
||||
"state": "confirmed" if confirmed else "new",
|
||||
"confirmed_by": confirmed_by,
|
||||
"first_seen": None,
|
||||
"last_seen": None,
|
||||
},
|
||||
description={
|
||||
"summary": cpp_issue.message,
|
||||
"details": description_section or sections.get("body", cpp_issue.message),
|
||||
},
|
||||
reproduce={
|
||||
"steps": reproduce_steps,
|
||||
"inputs": {},
|
||||
"artifacts": artifacts,
|
||||
},
|
||||
root_cause={
|
||||
"short": root_cause_section or cpp_issue.message,
|
||||
"technical_details": root_cause_section or "",
|
||||
},
|
||||
impact={
|
||||
"technical": impact_section or _impact_for(cpp_issue),
|
||||
"business": "可能影响系统稳定性与可用性。",
|
||||
},
|
||||
fix={
|
||||
"recommendation": _extract_list(fix_section) or ["参考安全开发规范修复该漏洞。"],
|
||||
"code_patch_hint": fix_section or "",
|
||||
"references": _extract_list(reference_section) or _references_for(cpp_issue),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def convert(report_path: Path, issues_dir: Path, output_path: Path, verification_path: Optional[Path]) -> None:
|
||||
cppcheck_issues = parse_cppcheck_xml(report_path)
|
||||
issue_map = {issue.id.lower(): issue for issue in cppcheck_issues}
|
||||
|
||||
files_map = _match_issue_files(issues_dir)
|
||||
verification_map = _load_verification_map(verification_path, issues_dir)
|
||||
|
||||
unified_issues: List[Issue] = []
|
||||
for issue_key, (md_path, cpp_path) in files_map.items():
|
||||
cpp_issue = issue_map.get(issue_key)
|
||||
if not cpp_issue:
|
||||
# 尝试更宽松匹配(移除非字母数字)
|
||||
normalized = re.sub(r"[^a-z0-9]", "", issue_key)
|
||||
cpp_issue = next(
|
||||
(iss for key, iss in issue_map.items() if re.sub(r"[^a-z0-9]", "", key) == normalized),
|
||||
None,
|
||||
)
|
||||
if not cpp_issue:
|
||||
print(f"[cppcheck_adapter] 跳过 {md_path.name}: 在报告中找不到对应的 issue id")
|
||||
continue
|
||||
|
||||
verification_info = None
|
||||
base_key = md_path.stem.replace(".md", "")
|
||||
if base_key in verification_map:
|
||||
verification_info = verification_map[base_key]
|
||||
|
||||
issue = _build_issue(cpp_issue, md_path, cpp_path, verification_info, report_path)
|
||||
unified_issues.append(issue)
|
||||
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with output_path.open("w", encoding="utf-8") as f:
|
||||
json.dump([asdict(issue) for issue in unified_issues], f, ensure_ascii=False, indent=2)
|
||||
|
||||
print(f"[cppcheck_adapter] Converted {len(unified_issues)} issues -> {output_path}")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Convert cppcheck_test_generator outputs to unified issues.")
|
||||
parser.add_argument("--report", type=Path, required=True, help="Path to cppcheck XML report.")
|
||||
parser.add_argument("--issues-dir", type=Path, required=True, help="Directory containing generated issue markdown/cpp files.")
|
||||
parser.add_argument("--output", type=Path, required=True, help="Path to write unified issues JSON.")
|
||||
parser.add_argument("--verification", type=Path, help="Optional verification_results.json path.")
|
||||
args = parser.parse_args()
|
||||
|
||||
convert(args.report, args.issues_dir, args.output, args.verification)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@ -0,0 +1,300 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Render a unified vulnerability report (Markdown + optional JSON summary)
|
||||
from previously normalized issue JSON files.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from collections import Counter
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Iterable, List, Optional
|
||||
|
||||
|
||||
SEVERITY_ORDER = ["CRITICAL", "HIGH", "MEDIUM", "LOW", "INFO"]
|
||||
SEVERITY_WEIGHT = {
|
||||
"CRITICAL": 5,
|
||||
"HIGH": 4,
|
||||
"MEDIUM": 3,
|
||||
"LOW": 2,
|
||||
"INFO": 1,
|
||||
}
|
||||
|
||||
|
||||
def load_issues(paths: Iterable[Path]) -> List[Dict[str, Any]]:
|
||||
issues: List[Dict[str, Any]] = []
|
||||
for path in paths:
|
||||
data = json.loads(path.read_text(encoding="utf-8"))
|
||||
issues.extend(data)
|
||||
return issues
|
||||
|
||||
|
||||
def severity_level(issue: Dict[str, Any]) -> str:
|
||||
level = issue.get("severity", {}).get("level")
|
||||
return level.upper() if isinstance(level, str) else "INFO"
|
||||
|
||||
|
||||
def severity_rank(level: str) -> int:
|
||||
try:
|
||||
return SEVERITY_ORDER.index(level)
|
||||
except ValueError:
|
||||
return len(SEVERITY_ORDER)
|
||||
|
||||
|
||||
def compute_stats(issues: List[Dict[str, Any]]) -> Dict[str, Any]:
|
||||
counts = Counter(severity_level(issue) for issue in issues)
|
||||
score = sum(SEVERITY_WEIGHT.get(level, 0) for level in counts for _ in range(counts[level]))
|
||||
if score >= 40:
|
||||
risk = "HIGH"
|
||||
elif score >= 20:
|
||||
risk = "MEDIUM"
|
||||
else:
|
||||
risk = "LOW"
|
||||
return {"counts": counts, "risk_score": score, "risk_level": risk}
|
||||
|
||||
|
||||
def get_top_issues(issues: List[Dict[str, Any]], limit: int = 5) -> List[Dict[str, Any]]:
|
||||
sorted_issues = sorted(
|
||||
issues,
|
||||
key=lambda issue: (
|
||||
severity_rank(severity_level(issue)),
|
||||
-1 * issue.get("severity", {}).get("cvss", 0),
|
||||
),
|
||||
)
|
||||
return sorted_issues[:limit]
|
||||
|
||||
|
||||
def render_counts(counts: Counter) -> str:
|
||||
lines = []
|
||||
for level in SEVERITY_ORDER:
|
||||
lines.append(f"- **{level}**: {counts.get(level, 0)}")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def render_table(issues: List[Dict[str, Any]]) -> str:
|
||||
header = "| 漏洞ID | 漏洞名称 | 风险等级 | 受影响资产 | CVSS |\n| :--- | :--- | :--- | :--- | :--- |\n"
|
||||
rows = []
|
||||
for issue in issues:
|
||||
loc = issue.get("location", {})
|
||||
file_path = loc.get("file") or "N/A"
|
||||
line = loc.get("line")
|
||||
asset = f"{file_path}:{line}" if line else file_path
|
||||
rows.append(
|
||||
f"| {issue.get('id')} | {issue.get('basic', {}).get('title', 'N/A')} | "
|
||||
f"{severity_level(issue)} | {asset} | {issue.get('severity', {}).get('cvss', 'N/A')} |"
|
||||
)
|
||||
return header + "\n".join(rows)
|
||||
|
||||
|
||||
def render_issue_detail(issue: Dict[str, Any]) -> str:
|
||||
loc = issue.get("location", {})
|
||||
file_path = loc.get("file") or "N/A"
|
||||
line = loc.get("line")
|
||||
asset = f"`{file_path}`"
|
||||
if line:
|
||||
asset += f":{line}"
|
||||
severity = severity_level(issue)
|
||||
cvss = issue.get("severity", {}).get("cvss", "N/A")
|
||||
source = issue.get("source", {}).get("engine", "unknown")
|
||||
status = issue.get("status", {}).get("state", "new")
|
||||
|
||||
desc = issue.get("description", {})
|
||||
summary = desc.get("summary") or ""
|
||||
details = desc.get("details") or ""
|
||||
|
||||
reproduce = issue.get("reproduce", {})
|
||||
steps = reproduce.get("steps") or []
|
||||
artifacts = reproduce.get("artifacts") or {}
|
||||
|
||||
root_cause = issue.get("root_cause", {})
|
||||
impact = issue.get("impact", {})
|
||||
fix = issue.get("fix", {})
|
||||
|
||||
references = fix.get("references") or []
|
||||
if isinstance(references, str):
|
||||
references = [references]
|
||||
|
||||
parts = [
|
||||
f"### {issue.get('id')}: {issue.get('basic', {}).get('title', '')}",
|
||||
f"- **风险等级**: {severity}",
|
||||
f"- **CVSS**: {cvss}",
|
||||
f"- **受影响资产**: {asset}",
|
||||
f"- **来源**: {source}",
|
||||
f"- **当前状态**: {status}",
|
||||
"",
|
||||
"**漏洞描述**:",
|
||||
summary,
|
||||
"",
|
||||
details,
|
||||
"",
|
||||
"**复现步骤**:",
|
||||
]
|
||||
if steps:
|
||||
parts.extend([f"{idx+1}. {step}" for idx, step in enumerate(steps)])
|
||||
else:
|
||||
parts.append("复现细节详见生成的测试用例。")
|
||||
|
||||
if artifacts:
|
||||
parts.append("")
|
||||
parts.append("**相关证据/文件**:")
|
||||
for key, value in artifacts.items():
|
||||
parts.append(f"- {key}: `{value}`" if isinstance(value, str) else f"- {key}: {value}")
|
||||
|
||||
parts.extend([
|
||||
"",
|
||||
"**根本原因**:",
|
||||
root_cause.get("short") or "待补充",
|
||||
"",
|
||||
root_cause.get("technical_details") or "",
|
||||
"",
|
||||
"**漏洞证明**:",
|
||||
"证据详见相关测试输出或 KLEE 生成的输入。",
|
||||
"",
|
||||
"**潜在影响**:",
|
||||
impact.get("technical") or "可能影响系统稳定性与可用性。",
|
||||
"",
|
||||
impact.get("business") or "",
|
||||
"",
|
||||
"**修复建议**:",
|
||||
])
|
||||
recommendations = fix.get("recommendation") or []
|
||||
if isinstance(recommendations, str):
|
||||
recommendations = [recommendations]
|
||||
if recommendations:
|
||||
parts.extend([f"- {rec}" for rec in recommendations])
|
||||
else:
|
||||
parts.append("- 参考安全开发规范修复该漏洞。")
|
||||
|
||||
if references:
|
||||
parts.extend(["", "**参考链接**:"])
|
||||
parts.extend([f"- {ref}" for ref in references])
|
||||
|
||||
parts.append("")
|
||||
return "\n".join(parts)
|
||||
|
||||
|
||||
def build_report(
|
||||
issues: List[Dict[str, Any]],
|
||||
metadata: Dict[str, Any],
|
||||
) -> str:
|
||||
now = metadata.get("generated_at") or datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
stats = compute_stats(issues)
|
||||
top_issues = get_top_issues(issues)
|
||||
|
||||
report_lines = [
|
||||
f"# 漏洞检测报告 - {metadata.get('report_title')}",
|
||||
"",
|
||||
"## 1. 报告摘要",
|
||||
f"- **检测目标**: {metadata.get('target', 'N/A')}",
|
||||
f"- **检测时间**: {metadata.get('time_range', 'N/A')}",
|
||||
f"- **报告版本**: {metadata.get('report_version', 'v1.0')}",
|
||||
f"- **报告生成日期**: {now}",
|
||||
f"- **检测方/负责人**: {metadata.get('scanner', 'N/A')}",
|
||||
"",
|
||||
"### 执行摘要",
|
||||
f"本次检测共发现 **{len(issues)}** 个漏洞,其中 **{stats['counts'].get('HIGH', 0)} 个高危**。整体风险评级为 **{stats['risk_level']}**。",
|
||||
"",
|
||||
"**漏洞统计**:",
|
||||
render_counts(stats["counts"]),
|
||||
"",
|
||||
"### 关键风险与建议",
|
||||
]
|
||||
|
||||
if top_issues:
|
||||
for idx, issue in enumerate(top_issues, start=1):
|
||||
report_lines.append(
|
||||
f"{idx}. **{issue.get('basic', {}).get('title', '')} ({severity_level(issue)})** "
|
||||
f"- {issue.get('description', {}).get('summary', '')}"
|
||||
)
|
||||
else:
|
||||
report_lines.append("当前无可展示的关键风险。")
|
||||
|
||||
report_lines.extend([
|
||||
"",
|
||||
"## 2. 漏洞详情",
|
||||
"",
|
||||
"### 漏洞列表",
|
||||
render_table(issues),
|
||||
"",
|
||||
])
|
||||
|
||||
for issue in issues:
|
||||
report_lines.append(render_issue_detail(issue))
|
||||
|
||||
report_lines.extend([
|
||||
"## 3. 附录与测试信息",
|
||||
"",
|
||||
f"- **测试范围**: {metadata.get('scope', '未提供')}",
|
||||
f"- **测试方法**: {metadata.get('methods', '符号执行与静态分析结合')} ",
|
||||
f"- **使用工具**: {metadata.get('tools', 'symbolic-engine, KLEE, cppcheck, clang-tidy')}",
|
||||
"",
|
||||
"**术语解释**:",
|
||||
"- **CVSS**: 通用漏洞评分系统。",
|
||||
"- **KLEE**: 基于 LLVM 的符号执行引擎。",
|
||||
"- **cppcheck**: C/C++ 代码静态分析工具。",
|
||||
"",
|
||||
"**免责声明**:",
|
||||
"本报告基于授权范围内的检测结果,仅反映指定时间点的安全状况。",
|
||||
])
|
||||
|
||||
return "\n".join(report_lines)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Render unified vulnerability report.")
|
||||
parser.add_argument("--issues", type=Path, nargs="+", required=True, help="Issue JSON files.")
|
||||
parser.add_argument("--output-md", type=Path, required=True, help="Output Markdown path.")
|
||||
parser.add_argument("--output-json", type=Path, help="Optional summary JSON path.")
|
||||
parser.add_argument("--report-title", default="未命名系统", help="Report title/name.")
|
||||
parser.add_argument("--target", default="未知目标", help="Detection target description.")
|
||||
parser.add_argument("--time-range", default="未提供", help="Detection time range.")
|
||||
parser.add_argument("--scanner", default="安全团队", help="Detection team / owner.")
|
||||
parser.add_argument("--report-version", default="v1.0", help="Report version.")
|
||||
parser.add_argument("--scope", default="核心服务端代码", help="Testing scope.")
|
||||
parser.add_argument(
|
||||
"--methods",
|
||||
default="符号执行 (KLEE) + 静态分析 (cppcheck, clang-tidy) + AI 测试生成",
|
||||
help="Testing methods description.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--tools",
|
||||
default="symbolic-engine, KLEE, cppcheck, clang-tidy, AI test generator",
|
||||
help="Tools used.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
issues = load_issues(args.issues)
|
||||
metadata = {
|
||||
"report_title": args.report_title,
|
||||
"target": args.target,
|
||||
"time_range": args.time_range,
|
||||
"report_version": args.report_version,
|
||||
"scanner": args.scanner,
|
||||
"scope": args.scope,
|
||||
"methods": args.methods,
|
||||
"tools": args.tools,
|
||||
"generated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
}
|
||||
|
||||
markdown = build_report(issues, metadata)
|
||||
args.output_md.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output_md.write_text(markdown, encoding="utf-8")
|
||||
print(f"[render_report] Markdown report generated: {args.output_md}")
|
||||
|
||||
if args.output_json:
|
||||
summary = {
|
||||
"metadata": metadata,
|
||||
"issues": issues,
|
||||
"stats": compute_stats(issues),
|
||||
}
|
||||
args.output_json.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output_json.write_text(json.dumps(summary, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
print(f"[render_report] JSON summary generated: {args.output_json}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@ -0,0 +1,266 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Adapter that converts symbolic-engine reports into the unified Issue schema.
|
||||
|
||||
Usage:
|
||||
python symbolic_adapter.py \
|
||||
--report /home/feng/klee-build/symbolic-engine/src/output/static_analysis_report.json \
|
||||
--output /home/feng/Report-Generation/unified_report/symbolic_issues.json \
|
||||
--klee-output /home/feng/klee-build/symbolic-engine/src/klee_output
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from dataclasses import dataclass, asdict
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
|
||||
SEVERITY_MAP = {
|
||||
"严重": "CRITICAL",
|
||||
"高": "HIGH",
|
||||
"中": "MEDIUM",
|
||||
"低": "LOW",
|
||||
"提示": "INFO",
|
||||
"信息": "INFO",
|
||||
}
|
||||
|
||||
CVSS_BASE = {
|
||||
"CRITICAL": 9.0,
|
||||
"HIGH": 8.0,
|
||||
"MEDIUM": 6.0,
|
||||
"LOW": 3.5,
|
||||
"INFO": 0.0,
|
||||
}
|
||||
|
||||
CATEGORY_MAP = {
|
||||
"缓冲区溢出": "memory_safety",
|
||||
"整数溢出": "memory_safety",
|
||||
"内存泄漏": "resource_management",
|
||||
"双重释放": "memory_safety",
|
||||
"使用已释放内存": "memory_safety",
|
||||
"空指针解引用": "memory_safety",
|
||||
"除零错误": "logic_bug",
|
||||
}
|
||||
|
||||
IMPACT_HINT = {
|
||||
"缓冲区溢出": "攻击者可能在目标进程中执行任意代码或导致服务崩溃。",
|
||||
"双重释放": "可能被利用构造堆风水,实现任意代码执行或程序崩溃。",
|
||||
"使用已释放内存": "可能触发未定义行为,造成崩溃或被构造为代码执行。",
|
||||
"内存泄漏": "持续泄漏会导致内存耗尽,导致服务性能下降或崩溃。",
|
||||
"整数溢出": "边界检查缺失可能绕过逻辑判断或触发缓冲区写入。",
|
||||
}
|
||||
|
||||
REFERENCE_MAP = {
|
||||
"缓冲区溢出": [
|
||||
"https://owasp.org/www-community/attacks/Buffer_overflow_attack",
|
||||
"https://cwe.mitre.org/data/definitions/119.html",
|
||||
],
|
||||
"双重释放": [
|
||||
"https://cwe.mitre.org/data/definitions/415.html",
|
||||
],
|
||||
"使用已释放内存": [
|
||||
"https://cwe.mitre.org/data/definitions/416.html",
|
||||
],
|
||||
"内存泄漏": [
|
||||
"https://cwe.mitre.org/data/definitions/401.html",
|
||||
],
|
||||
"整数溢出": [
|
||||
"https://cwe.mitre.org/data/definitions/190.html",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class Issue:
|
||||
id: str
|
||||
source: Dict[str, Any]
|
||||
basic: Dict[str, Any]
|
||||
location: Dict[str, Any]
|
||||
severity: Dict[str, Any]
|
||||
status: Dict[str, Any]
|
||||
description: Dict[str, Any]
|
||||
reproduce: Dict[str, Any]
|
||||
root_cause: Dict[str, Any]
|
||||
impact: Dict[str, Any]
|
||||
fix: Dict[str, Any]
|
||||
|
||||
|
||||
def _normalize_severity(raw: str) -> str:
|
||||
if not raw:
|
||||
return "INFO"
|
||||
raw = raw.strip()
|
||||
return SEVERITY_MAP.get(raw, raw.upper())
|
||||
|
||||
|
||||
def _cvss_for(severity: str, confidence: Optional[int]) -> float:
|
||||
base = CVSS_BASE.get(severity, 0.0)
|
||||
if confidence is None:
|
||||
return base
|
||||
# 简单地根据置信度拉伸 CVSS
|
||||
return round(min(10.0, base + (confidence / 100.0) * 1.5), 1)
|
||||
|
||||
|
||||
def _category_for(vuln_type: str) -> str:
|
||||
return CATEGORY_MAP.get(vuln_type, "logic_bug")
|
||||
|
||||
|
||||
def _impact_for(vuln_type: str) -> str:
|
||||
return IMPACT_HINT.get(vuln_type, "可能被利用造成稳定性下降或安全边界被绕过。")
|
||||
|
||||
|
||||
def _references_for(vuln_type: str) -> List[str]:
|
||||
return REFERENCE_MAP.get(vuln_type, [])
|
||||
|
||||
|
||||
def _build_issue(vuln: Dict[str, Any], report_path: Path, klee_output: Optional[Path]) -> Issue:
|
||||
severity_level = _normalize_severity(vuln.get("severity", ""))
|
||||
issue_id = f"KLEE-{int(vuln.get('id', 0)):04d}"
|
||||
file_path = vuln.get("file_path") or "UNKNOWN"
|
||||
line_number = vuln.get("line_number")
|
||||
snippet = (vuln.get("code_line") or "").strip()
|
||||
issue_type = vuln.get("type") or "未知漏洞"
|
||||
confidence = vuln.get("confidence_score")
|
||||
confirmed = bool(vuln.get("confirmed_by_klee"))
|
||||
|
||||
artifacts: Dict[str, Any] = {}
|
||||
if klee_output and confirmed:
|
||||
artifacts["klee_output_dir"] = str(klee_output)
|
||||
|
||||
reproduce_steps = [
|
||||
"切换到 symbolic-engine/src 目录。",
|
||||
f"运行 ./test_analyzer {file_path} 以重放符号执行。",
|
||||
]
|
||||
if confirmed:
|
||||
reproduce_steps.append("在 klee_output 目录中查看生成的 ktest 文件并使用 ktest-tool 复现。")
|
||||
|
||||
return Issue(
|
||||
id=issue_id,
|
||||
source={
|
||||
"engine": "symbolic-engine",
|
||||
"sub_tool": "KLEE",
|
||||
"raw_ids": [str(vuln.get("id"))],
|
||||
"report_path": str(report_path),
|
||||
},
|
||||
basic={
|
||||
"title": f"{issue_type} - {file_path}",
|
||||
"type": issue_type,
|
||||
"cwe": None,
|
||||
"category": _category_for(issue_type),
|
||||
},
|
||||
location={
|
||||
"file": file_path,
|
||||
"function": vuln.get("function"),
|
||||
"line": line_number,
|
||||
"column": vuln.get("column"),
|
||||
"snippet": snippet,
|
||||
},
|
||||
severity={
|
||||
"level": severity_level,
|
||||
"cvss": _cvss_for(severity_level, confidence),
|
||||
"cvss_vector": None,
|
||||
},
|
||||
status={
|
||||
"state": "confirmed" if confirmed else "new",
|
||||
"confirmed_by": ["klee"] if confirmed else [],
|
||||
"first_seen": None,
|
||||
"last_seen": None,
|
||||
},
|
||||
description={
|
||||
"summary": vuln.get("description") or "",
|
||||
"details": snippet or vuln.get("description") or "",
|
||||
},
|
||||
reproduce={
|
||||
"steps": reproduce_steps,
|
||||
"inputs": {},
|
||||
"artifacts": artifacts,
|
||||
},
|
||||
root_cause={
|
||||
"short": vuln.get("description") or "",
|
||||
"technical_details": snippet,
|
||||
},
|
||||
impact={
|
||||
"technical": _impact_for(issue_type),
|
||||
"business": "可能影响系统稳定性与可用性。",
|
||||
},
|
||||
fix={
|
||||
"recommendation": [vuln.get("fix_suggestion") or "参考安全开发规范修复该漏洞。"],
|
||||
"code_patch_hint": snippet,
|
||||
"references": _references_for(issue_type),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def _sanitize_report_text(text: str) -> str:
|
||||
"""Repair non-standard JSON emitted by symbolic-engine (code_line broken lines)."""
|
||||
lines = text.splitlines()
|
||||
fixed: List[str] = []
|
||||
i = 0
|
||||
while i < len(lines):
|
||||
line = lines[i]
|
||||
needs_join = (
|
||||
'"code_line"' in line
|
||||
and not line.rstrip().endswith('",')
|
||||
and i + 1 < len(lines)
|
||||
and lines[i + 1].strip() == '",'
|
||||
)
|
||||
if needs_join:
|
||||
fixed.append(line.rstrip("\n") + '",')
|
||||
i += 2
|
||||
continue
|
||||
fixed.append(line)
|
||||
i += 1
|
||||
return "\n".join(fixed)
|
||||
|
||||
|
||||
def _load_report(report_path: Path) -> Dict[str, Any]:
|
||||
raw_text = report_path.read_text(encoding="utf-8")
|
||||
try:
|
||||
return json.loads(raw_text)
|
||||
except json.JSONDecodeError:
|
||||
repaired = _sanitize_report_text(raw_text)
|
||||
return json.loads(repaired)
|
||||
|
||||
|
||||
def convert(report_path: Path, output_path: Path, klee_output: Optional[Path]) -> None:
|
||||
data = _load_report(report_path)
|
||||
|
||||
vulnerabilities = data.get("vulnerabilities", [])
|
||||
issues = [_build_issue(v, report_path, klee_output) for v in vulnerabilities]
|
||||
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with output_path.open("w", encoding="utf-8") as f:
|
||||
json.dump([asdict(issue) for issue in issues], f, ensure_ascii=False, indent=2)
|
||||
|
||||
print(f"[symbolic_adapter] Converted {len(issues)} issues -> {output_path}")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Convert symbolic-engine report to unified issues.")
|
||||
parser.add_argument(
|
||||
"--report",
|
||||
type=Path,
|
||||
required=True,
|
||||
help="Path to symbolic-engine static_analysis_report.json",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output",
|
||||
type=Path,
|
||||
required=True,
|
||||
help="Path to write unified issues JSON.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--klee-output",
|
||||
type=Path,
|
||||
help="Optional path to klee_output directory for evidence reference.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
convert(args.report, args.output, args.klee_output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue