aike-code-sentinel 0.1.0__py3-none-any.whl
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.
- aike_code_sentinel-0.1.0.dist-info/METADATA +6 -0
- aike_code_sentinel-0.1.0.dist-info/RECORD +0 -0
- aike_code_sentinel-0.1.0.dist-info/WHEEL +4 -0
- code_sentinel/__init__.py +30 -0
- code_sentinel/__pycache__/__init__.cpython-311.pyc +0 -0
- code_sentinel/__pycache__/cli.cpython-311.pyc +0 -0
- code_sentinel/__pycache__/fixer.cpython-311.pyc +0 -0
- code_sentinel/__pycache__/reporter.cpython-311.pyc +0 -0
- code_sentinel/__pycache__/scanner.cpython-311.pyc +0 -0
- code_sentinel/cli.py +382 -0
- code_sentinel/fixer.py +329 -0
- code_sentinel/reporter.py +438 -0
- code_sentinel/scanner.py +817 -0
|
File without changes
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Code Sentinel — 代码哨兵 MVP
|
|
3
|
+
==============================
|
|
4
|
+
基于 Genome Core 四层安全审查框架的 AST 静态分析工具。
|
|
5
|
+
|
|
6
|
+
检测维度:
|
|
7
|
+
- 安全风险:eval/exec/os.system/subprocess 无过滤调用
|
|
8
|
+
- 性能问题:嵌套循环 O(n²)、大文件一次性读取
|
|
9
|
+
- 代码异味:过长函数(>50行)、过多参数(>5个)、裸 except
|
|
10
|
+
- 依赖风险:requirements.txt 中已知漏洞包
|
|
11
|
+
|
|
12
|
+
集成 Genome Core 四层防灭绝:
|
|
13
|
+
1. discipline — 纪律检查(危险模式匹配)
|
|
14
|
+
2. guard — 格式/内容/社区三层防线
|
|
15
|
+
3. review — 可信度评估
|
|
16
|
+
4. self_review — 语法/逻辑/沙箱/边界四层自审
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from .scanner import CodeScanner, SecurityIssue, PerformanceIssue, SmellIssue, DependencyIssue
|
|
20
|
+
from .fixer import CodeFixer, FixResult
|
|
21
|
+
from .reporter import ReportGenerator
|
|
22
|
+
from .cli import main
|
|
23
|
+
|
|
24
|
+
__version__ = "0.1.0"
|
|
25
|
+
__all__ = [
|
|
26
|
+
"CodeScanner", "SecurityIssue", "PerformanceIssue", "SmellIssue", "DependencyIssue",
|
|
27
|
+
"CodeFixer", "FixResult",
|
|
28
|
+
"ReportGenerator",
|
|
29
|
+
"main",
|
|
30
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
code_sentinel/cli.py
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
"""
|
|
2
|
+
命令行入口 — Code Sentinel CLI
|
|
3
|
+
==============================
|
|
4
|
+
支持三个子命令:
|
|
5
|
+
scan — 扫描代码并输出问题列表
|
|
6
|
+
diff — 扫描 git diff 变更引入的问题
|
|
7
|
+
report — 生成 HTML/Markdown/JSON 报告
|
|
8
|
+
fix — 生成修复建议和 diff
|
|
9
|
+
|
|
10
|
+
集成 Genome Core 四层安全审查:
|
|
11
|
+
discipline → guard → review → self_review
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import argparse
|
|
15
|
+
import json
|
|
16
|
+
import sys
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import List, Optional
|
|
19
|
+
|
|
20
|
+
from .scanner import CodeScanner
|
|
21
|
+
from .fixer import CodeFixer
|
|
22
|
+
from .reporter import ReportGenerator
|
|
23
|
+
|
|
24
|
+
# ── Genome Core 集成 ──
|
|
25
|
+
try:
|
|
26
|
+
from genome_core.security import (
|
|
27
|
+
verify_config, check_dangerous, full_guard, full_review, LAWS,
|
|
28
|
+
)
|
|
29
|
+
from genome_core.governance import route_to_ministry, audit_log, check_accountability
|
|
30
|
+
from genome_core.tools import timestamp
|
|
31
|
+
HAS_GENOME_CORE = True
|
|
32
|
+
except ImportError:
|
|
33
|
+
HAS_GENOME_CORE = False
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _print_banner():
|
|
37
|
+
"""打印启动横幅"""
|
|
38
|
+
banner = r"""
|
|
39
|
+
____ _ _ ____ _ _ _
|
|
40
|
+
/ ___|___ __| | | / ___| ___| |_ _ __(_) |_ ___
|
|
41
|
+
| | / _ \ / _` | | \___ \ / _ \ __| '__| | __/ _ \
|
|
42
|
+
| |__| (_) | (_| | | ___) | __/ |_| | | | || __/
|
|
43
|
+
\____\___/ \__,_|_| |____/ \___|\__|_| |_|\__\___|
|
|
44
|
+
|
|
45
|
+
▸ Code Sentinel MVP — 代码哨兵静态分析工具
|
|
46
|
+
▸ 基于 Genome Core 四层防灭绝安全框架
|
|
47
|
+
"""
|
|
48
|
+
print(banner)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _print_genome_status():
|
|
52
|
+
"""打印 Genome Core 集成状态"""
|
|
53
|
+
if HAS_GENOME_CORE:
|
|
54
|
+
print(" ✓ Genome Core 已集成 — 四层安全审查已启用")
|
|
55
|
+
print(f" ✓ {len(LAWS)} 条法律已加载")
|
|
56
|
+
print()
|
|
57
|
+
else:
|
|
58
|
+
print(" ⚠ Genome Core 未安装 — 安全审查受限")
|
|
59
|
+
print()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _validate_path(path_str: str) -> Path:
|
|
63
|
+
"""验证路径是否存在,不存在时输出友好错误并退出"""
|
|
64
|
+
p = Path(path_str)
|
|
65
|
+
if not p.exists():
|
|
66
|
+
print(f"❌ 错误: 路径不存在: {path_str}", file=sys.stderr)
|
|
67
|
+
print(f" 请检查路径是否正确", file=sys.stderr)
|
|
68
|
+
sys.exit(1)
|
|
69
|
+
return p
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _print_scan_results(results: dict):
|
|
73
|
+
"""打印扫描结果摘要到控制台"""
|
|
74
|
+
summary = results["summary"]
|
|
75
|
+
print(f"\n{'='*60}")
|
|
76
|
+
print(f" 扫描完成: {summary['target']}")
|
|
77
|
+
print(f" 扫描文件: {summary['scanned_files']} 个")
|
|
78
|
+
print(f"{'='*60}")
|
|
79
|
+
print(f" 总问题数: {summary['total']}")
|
|
80
|
+
print(f" 🔴 高危: {summary['high']}")
|
|
81
|
+
print(f" 🟡 中危: {summary['medium']}")
|
|
82
|
+
print(f" 🟢 低危: {summary['low']}")
|
|
83
|
+
print(f"{'='*60}")
|
|
84
|
+
|
|
85
|
+
categories = [
|
|
86
|
+
("security", "安全风险", "🔒"),
|
|
87
|
+
("performance", "性能问题", "⚡"),
|
|
88
|
+
("smell", "代码异味", "👃"),
|
|
89
|
+
("dependency", "依赖风险", "📦"),
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
for key, label, icon in categories:
|
|
93
|
+
issues = results.get(key, [])
|
|
94
|
+
if not issues:
|
|
95
|
+
continue
|
|
96
|
+
print(f"\n {icon} {label} ({len(issues)} 个):")
|
|
97
|
+
for iss in issues[:10]: # 最多显示前10个
|
|
98
|
+
file = iss.get("file", "?")
|
|
99
|
+
line = iss.get("line", 0)
|
|
100
|
+
desc = iss.get("description", "?")
|
|
101
|
+
sev = iss.get("severity", "?")
|
|
102
|
+
print(f" [{sev}] {file}:{line} — {desc}")
|
|
103
|
+
if len(issues) > 10:
|
|
104
|
+
print(f" ... 还有 {len(issues) - 10} 个问题(使用 --report 查看完整报告)")
|
|
105
|
+
|
|
106
|
+
if results.get("genome_core_integrated"):
|
|
107
|
+
print(f"\n ✅ 已通过 Genome Core 安全审查验证")
|
|
108
|
+
print()
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def cmd_scan(args: argparse.Namespace):
|
|
112
|
+
"""scan 子命令:扫描代码"""
|
|
113
|
+
target = _validate_path(args.path)
|
|
114
|
+
scanner = CodeScanner(str(target))
|
|
115
|
+
results = scanner.scan()
|
|
116
|
+
|
|
117
|
+
if args.json:
|
|
118
|
+
print(json.dumps(results, indent=2, ensure_ascii=False))
|
|
119
|
+
elif not args.quiet:
|
|
120
|
+
_print_scan_results(results)
|
|
121
|
+
|
|
122
|
+
if args.output:
|
|
123
|
+
output_path = Path(args.output)
|
|
124
|
+
output_path.write_text(
|
|
125
|
+
json.dumps(results, indent=2, ensure_ascii=False), encoding="utf-8"
|
|
126
|
+
)
|
|
127
|
+
if not args.quiet and not args.json:
|
|
128
|
+
print(f" 📄 扫描结果已保存: {output_path}")
|
|
129
|
+
|
|
130
|
+
# Genome Core 审计
|
|
131
|
+
if HAS_GENOME_CORE:
|
|
132
|
+
audit_log("scan", route_to_ministry("scan"),
|
|
133
|
+
f"扫描 {target},发现 {results['summary']['total']} 个问题")
|
|
134
|
+
|
|
135
|
+
return results
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def cmd_report(args: argparse.Namespace):
|
|
139
|
+
"""report 子命令:生成报告"""
|
|
140
|
+
# 先执行扫描
|
|
141
|
+
target = _validate_path(args.path)
|
|
142
|
+
scanner = CodeScanner(str(target))
|
|
143
|
+
results = scanner.scan()
|
|
144
|
+
|
|
145
|
+
if args.json:
|
|
146
|
+
print(json.dumps(results, indent=2, ensure_ascii=False))
|
|
147
|
+
return results
|
|
148
|
+
|
|
149
|
+
output_dir = args.output_dir or "."
|
|
150
|
+
generator = ReportGenerator(results, output_dir)
|
|
151
|
+
|
|
152
|
+
formats = args.format or ["html", "md"]
|
|
153
|
+
|
|
154
|
+
generated = []
|
|
155
|
+
for fmt in formats:
|
|
156
|
+
if fmt == "html":
|
|
157
|
+
path = generator.generate_html()
|
|
158
|
+
generated.append(("HTML", path))
|
|
159
|
+
elif fmt == "md":
|
|
160
|
+
path = generator.generate_markdown()
|
|
161
|
+
generated.append(("Markdown", path))
|
|
162
|
+
elif fmt == "json":
|
|
163
|
+
path = generator.generate_json()
|
|
164
|
+
generated.append(("JSON", path))
|
|
165
|
+
elif fmt == "json-schema":
|
|
166
|
+
path = generator.generate_json_schema()
|
|
167
|
+
generated.append(("JSON Schema", path))
|
|
168
|
+
|
|
169
|
+
if not args.quiet:
|
|
170
|
+
print(f"\n 📊 报告生成完成:")
|
|
171
|
+
for name, path in generated:
|
|
172
|
+
print(f" • {name}: {path}")
|
|
173
|
+
|
|
174
|
+
# Genome Core 审计
|
|
175
|
+
if HAS_GENOME_CORE:
|
|
176
|
+
audit_log("report", "户部",
|
|
177
|
+
f"生成 {len(generated)} 份报告,目标: {target}")
|
|
178
|
+
|
|
179
|
+
return results
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def cmd_fix(args: argparse.Namespace):
|
|
183
|
+
"""fix 子命令:生成修复建议"""
|
|
184
|
+
target = _validate_path(args.path)
|
|
185
|
+
scanner = CodeScanner(str(target))
|
|
186
|
+
results = scanner.scan()
|
|
187
|
+
|
|
188
|
+
fixer = CodeFixer()
|
|
189
|
+
fix_results = fixer.generate_fixes(results)
|
|
190
|
+
|
|
191
|
+
if args.json:
|
|
192
|
+
print(json.dumps([fr.to_dict() for fr in fix_results],
|
|
193
|
+
indent=2, ensure_ascii=False))
|
|
194
|
+
return fix_results
|
|
195
|
+
|
|
196
|
+
if not fix_results:
|
|
197
|
+
if not args.quiet:
|
|
198
|
+
print(" ✅ 未检测到可自动修复的问题。" if args.verbose else "No fixable issues found.")
|
|
199
|
+
return
|
|
200
|
+
|
|
201
|
+
if not args.quiet:
|
|
202
|
+
print(f"\n 🔧 修复建议 ({len(fix_results)} 个):")
|
|
203
|
+
for fr in fix_results:
|
|
204
|
+
for action in fr.actions:
|
|
205
|
+
print(f"\n ── {action.description}")
|
|
206
|
+
print(f" 文件: {action.file}:{action.line}")
|
|
207
|
+
print(f" 类别: {action.category}")
|
|
208
|
+
print(f" 可信度: {action.confidence:.0%}")
|
|
209
|
+
if args.verbose:
|
|
210
|
+
print(f"\n Diff:")
|
|
211
|
+
print(f" {'─'*40}")
|
|
212
|
+
for line in action.to_diff().split("\n"):
|
|
213
|
+
if line:
|
|
214
|
+
print(f" {line}")
|
|
215
|
+
if fr.genome_review_passed:
|
|
216
|
+
print(f" ✅ 已通过 Genome Core 四层自审")
|
|
217
|
+
else:
|
|
218
|
+
print(f" ⚠ 未通过 Genome Core 自审")
|
|
219
|
+
|
|
220
|
+
if args.output:
|
|
221
|
+
output_path = Path(args.output)
|
|
222
|
+
output_path.write_text(
|
|
223
|
+
json.dumps([fr.to_dict() for fr in fix_results],
|
|
224
|
+
indent=2, ensure_ascii=False),
|
|
225
|
+
encoding="utf-8"
|
|
226
|
+
)
|
|
227
|
+
if not args.quiet and not args.json:
|
|
228
|
+
print(f"\n 📄 修复结果已保存: {output_path}")
|
|
229
|
+
|
|
230
|
+
# Genome Core 审计
|
|
231
|
+
if HAS_GENOME_CORE:
|
|
232
|
+
audit_log("fix", "工部",
|
|
233
|
+
f"为 {target} 生成 {len(fix_results)} 个修复建议")
|
|
234
|
+
|
|
235
|
+
return fix_results
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def cmd_diff(args: argparse.Namespace):
|
|
239
|
+
"""diff 子命令:扫描 git diff 变更引入的问题"""
|
|
240
|
+
import subprocess
|
|
241
|
+
|
|
242
|
+
try:
|
|
243
|
+
cmd = ["git", "diff"]
|
|
244
|
+
if args.staged:
|
|
245
|
+
cmd.append("--staged")
|
|
246
|
+
proc = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
|
247
|
+
diff_text = proc.stdout
|
|
248
|
+
except subprocess.CalledProcessError as e:
|
|
249
|
+
print(f"❌ git diff 执行失败: {e}", file=sys.stderr)
|
|
250
|
+
sys.exit(1)
|
|
251
|
+
except FileNotFoundError:
|
|
252
|
+
print(f"❌ 未找到 git 命令,请确保已安装 git", file=sys.stderr)
|
|
253
|
+
sys.exit(1)
|
|
254
|
+
|
|
255
|
+
if not diff_text.strip():
|
|
256
|
+
if not args.quiet:
|
|
257
|
+
print(" ℹ 没有检测到变更")
|
|
258
|
+
return {}
|
|
259
|
+
|
|
260
|
+
scanner = CodeScanner(".") # 路径无关,scan_diff 会按 diff 中的路径扫描
|
|
261
|
+
results = scanner.scan_diff(diff_text)
|
|
262
|
+
|
|
263
|
+
if args.json:
|
|
264
|
+
print(json.dumps(results, indent=2, ensure_ascii=False))
|
|
265
|
+
elif not args.quiet:
|
|
266
|
+
_print_scan_results(results)
|
|
267
|
+
|
|
268
|
+
if args.output:
|
|
269
|
+
output_path = Path(args.output)
|
|
270
|
+
output_path.write_text(
|
|
271
|
+
json.dumps(results, indent=2, ensure_ascii=False), encoding="utf-8"
|
|
272
|
+
)
|
|
273
|
+
if not args.quiet and not args.json:
|
|
274
|
+
print(f" 📄 扫描结果已保存: {output_path}")
|
|
275
|
+
|
|
276
|
+
# Genome Core 审计
|
|
277
|
+
if HAS_GENOME_CORE:
|
|
278
|
+
audit_log("diff", route_to_ministry("scan"),
|
|
279
|
+
f"扫描 diff ({len(results.get('summary', {}))} 文件变更)")
|
|
280
|
+
|
|
281
|
+
return results
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
285
|
+
"""构建 CLI 参数解析器"""
|
|
286
|
+
parser = argparse.ArgumentParser(
|
|
287
|
+
prog="code-sentinel",
|
|
288
|
+
description="Code Sentinel — 基于 Genome Core 的代码静态分析工具",
|
|
289
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
290
|
+
epilog="""
|
|
291
|
+
示例:
|
|
292
|
+
code-sentinel scan ./src # 扫描 src 目录
|
|
293
|
+
code-sentinel scan ./src -o results.json # 扫描并保存结果
|
|
294
|
+
code-sentinel diff --staged # 扫描已暂存的变更
|
|
295
|
+
code-sentinel diff # 扫描未暂存的变更
|
|
296
|
+
code-sentinel report ./src # 生成 HTML+Markdown 报告
|
|
297
|
+
code-sentinel report ./src -f html md json # 指定格式
|
|
298
|
+
code-sentinel fix ./src # 生成修复建议
|
|
299
|
+
code-sentinel fix ./src --verbose # 显示详细 diff
|
|
300
|
+
""",
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
subparsers = parser.add_subparsers(dest="command", help="子命令")
|
|
304
|
+
|
|
305
|
+
# scan
|
|
306
|
+
scan_parser = subparsers.add_parser("scan", help="扫描代码并输出问题列表")
|
|
307
|
+
scan_parser.add_argument("path", type=str, help="目标文件或目录路径")
|
|
308
|
+
scan_parser.add_argument("-o", "--output", type=str, help="保存扫描结果到 JSON 文件")
|
|
309
|
+
scan_parser.add_argument("--json", action="store_true", help="以 JSON 格式输出扫描结果")
|
|
310
|
+
scan_parser.add_argument("-q", "--quiet", action="store_true", help="静默模式,仅输出关键结果")
|
|
311
|
+
scan_parser.add_argument("-v", "--verbose", action="store_true", help="详细输出")
|
|
312
|
+
|
|
313
|
+
# report
|
|
314
|
+
report_parser = subparsers.add_parser("report", help="生成 HTML/Markdown/JSON 报告")
|
|
315
|
+
report_parser.add_argument("path", type=str, help="目标文件或目录路径")
|
|
316
|
+
report_parser.add_argument("-o", "--output-dir", type=str, default=".",
|
|
317
|
+
help="报告输出目录(默认当前目录)")
|
|
318
|
+
report_parser.add_argument("-f", "--format", type=str, nargs="+",
|
|
319
|
+
choices=["html", "md", "json", "json-schema"],
|
|
320
|
+
default=["html", "md"], help="报告格式(默认 html md)")
|
|
321
|
+
report_parser.add_argument("--json", action="store_true", help="以 JSON 格式输出报告")
|
|
322
|
+
report_parser.add_argument("-q", "--quiet", action="store_true", help="静默模式,仅输出关键结果")
|
|
323
|
+
report_parser.add_argument("-v", "--verbose", action="store_true", help="详细输出")
|
|
324
|
+
|
|
325
|
+
# fix
|
|
326
|
+
fix_parser = subparsers.add_parser("fix", help="生成修复建议和 diff")
|
|
327
|
+
fix_parser.add_argument("path", type=str, help="目标文件或目录路径")
|
|
328
|
+
fix_parser.add_argument("-o", "--output", type=str, help="保存修复结果到 JSON 文件")
|
|
329
|
+
fix_parser.add_argument("--json", action="store_true", help="以 JSON 格式输出修复结果")
|
|
330
|
+
fix_parser.add_argument("-q", "--quiet", action="store_true", help="静默模式,仅输出关键结果")
|
|
331
|
+
fix_parser.add_argument("-v", "--verbose", action="store_true", help="显示详细 diff")
|
|
332
|
+
|
|
333
|
+
# diff
|
|
334
|
+
diff_parser = subparsers.add_parser("diff", help="扫描 git diff 变更引入的问题")
|
|
335
|
+
diff_parser.add_argument("--staged", action="store_true",
|
|
336
|
+
help="扫描已暂存(staged)的变更(即 git diff --staged)")
|
|
337
|
+
diff_parser.add_argument("-o", "--output", type=str, help="保存扫描结果到 JSON 文件")
|
|
338
|
+
diff_parser.add_argument("--json", action="store_true", help="以 JSON 格式输出扫描结果")
|
|
339
|
+
diff_parser.add_argument("-q", "--quiet", action="store_true", help="静默模式,仅输出关键结果")
|
|
340
|
+
diff_parser.add_argument("-v", "--verbose", action="store_true", help="详细输出")
|
|
341
|
+
|
|
342
|
+
return parser
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def main():
|
|
346
|
+
"""CLI 主入口"""
|
|
347
|
+
parser = build_parser()
|
|
348
|
+
args = parser.parse_args()
|
|
349
|
+
|
|
350
|
+
if not args.command:
|
|
351
|
+
parser.print_help()
|
|
352
|
+
sys.exit(1)
|
|
353
|
+
|
|
354
|
+
# 打印横幅
|
|
355
|
+
if args.verbose:
|
|
356
|
+
_print_banner()
|
|
357
|
+
_print_genome_status()
|
|
358
|
+
|
|
359
|
+
# 路由到对应子命令
|
|
360
|
+
commands = {
|
|
361
|
+
"scan": cmd_scan,
|
|
362
|
+
"diff": cmd_diff,
|
|
363
|
+
"report": cmd_report,
|
|
364
|
+
"fix": cmd_fix,
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
cmd_fn = commands.get(args.command)
|
|
368
|
+
if cmd_fn:
|
|
369
|
+
try:
|
|
370
|
+
cmd_fn(args)
|
|
371
|
+
except FileNotFoundError as e:
|
|
372
|
+
print(f"❌ 错误: {e}", file=sys.stderr)
|
|
373
|
+
sys.exit(1)
|
|
374
|
+
except Exception as e:
|
|
375
|
+
print(f"❌ 执行失败: {e}", file=sys.stderr)
|
|
376
|
+
if HAS_GENOME_CORE:
|
|
377
|
+
audit_log("error", "工部", f"{args.command} 失败: {e}")
|
|
378
|
+
sys.exit(1)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
if __name__ == "__main__":
|
|
382
|
+
main()
|