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.
@@ -0,0 +1,6 @@
1
+ Metadata-Version: 2.1
2
+ Name: aike-code-sentinel
3
+ Version: 0.1.0
4
+ Summary: AI-driven Code Security Sentinel
5
+ Author: aike-autonomy
6
+ License: MIT
File without changes
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: manual
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -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
+ ]
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()