v4-pro 2.0.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.
v4_pro/__init__.py ADDED
@@ -0,0 +1,8 @@
1
+ """
2
+ V4 Pro — Vibe Verification & Validation Pipeline for Professionals.
3
+
4
+ 将 "Vibe Coding" 从随意的 AI 生成升级为结构化、质量可控的工程流水线。
5
+ """
6
+
7
+ __version__ = "2.0.0"
8
+ __author__ = "V4 Pro Team"
@@ -0,0 +1,5 @@
1
+ """安全审计模块。"""
2
+
3
+ from v4_pro.audit.scanner import SecurityAuditor
4
+
5
+ __all__ = ["SecurityAuditor"]
@@ -0,0 +1,137 @@
1
+ """
2
+ 独立安全审计模块 (SecurityAuditor)。
3
+
4
+ v4-pro audit 命令的实现 — 与 verify 的安全扫描共用同一套检测引擎
5
+ (v4_pro.verify.security_scan.SecurityScanner),保证两个入口的结果一致。
6
+
7
+ v2.0 变化:
8
+ - 检测逻辑统一到 SecurityScanner(此前是两套正则,规则漂移、误报翻倍)
9
+ - 保留审计报告 schema: audit_metadata / summary / findings / remediation_priority
10
+ - OWASP/CWE 映射由规则表统一维护
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import logging
16
+ from datetime import datetime, timezone
17
+ from pathlib import Path
18
+ from typing import Any
19
+
20
+ from v4_pro.verify.security_scan import SecurityScanner
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ # verify 严重度 → 审计严重度
25
+ _SEV_MAP = {"P0": "critical", "P1": "high", "P2": "medium", "P3": "low"}
26
+
27
+
28
+ class SecurityAuditor:
29
+ """
30
+ 独立安全审计器。
31
+
32
+ 用法:
33
+ auditor = SecurityAuditor()
34
+ report = auditor.audit(Path("./generated/"))
35
+ """
36
+
37
+ def __init__(self):
38
+ self._scanned_files = 0
39
+
40
+ def audit(self, code_dir: Path, extra_test_paths: list[str] | None = None) -> dict[str, Any]:
41
+ """
42
+ 执行全面安全审计。
43
+
44
+ Args:
45
+ code_dir: 代码目录
46
+
47
+ Returns:
48
+ 审计报告字典(schema 与 1.x 兼容)
49
+ """
50
+ code_dir = Path(code_dir)
51
+
52
+ scanner = SecurityScanner()
53
+ scan_result = scanner.scan(code_dir, extra_test_paths=extra_test_paths)
54
+ self._scanned_files = scan_result["summary"].get("files_scanned", 0)
55
+
56
+ findings = []
57
+ for issue in scan_result["issues"]:
58
+ findings.append({
59
+ "file": issue.get("file", ""),
60
+ "line": issue.get("line", 0),
61
+ "title": issue.get("title", ""),
62
+ "severity": _SEV_MAP.get(issue.get("severity", "P3"), "low"),
63
+ "rule_id": issue.get("rule_id", ""),
64
+ "owasp_category": issue.get("owasp_category", ""),
65
+ "cwe": issue.get("cwe", ""),
66
+ "code_snippet": issue.get("code_snippet", ""),
67
+ "recommendation": issue.get("suggestion", ""),
68
+ "confidence": "high" if issue.get("severity") in ("P0", "P1") else "medium",
69
+ })
70
+
71
+ for i, f in enumerate(findings):
72
+ f["id"] = f"AUDIT-{i+1:04d}"
73
+
74
+ severity_counts = {"critical": 0, "high": 0, "medium": 0, "low": 0, "info": 0}
75
+ for f in findings:
76
+ sev = f.get("severity", "info")
77
+ severity_counts[sev] = severity_counts.get(sev, 0) + 1
78
+
79
+ owasp_covered = sorted({f["owasp_category"] for f in findings if f.get("owasp_category")})
80
+
81
+ return {
82
+ "audit_metadata": {
83
+ "timestamp": datetime.now(timezone.utc).isoformat(),
84
+ "code_directory": str(code_dir),
85
+ "files_scanned": self._scanned_files,
86
+ "owasp_categories_covered": owasp_covered,
87
+ "tool": "V4 Pro Security Auditor v2.0",
88
+ },
89
+ "summary": {
90
+ "total_findings": len(findings),
91
+ "critical": severity_counts["critical"],
92
+ "high": severity_counts["high"],
93
+ "medium": severity_counts["medium"],
94
+ "low": severity_counts["low"],
95
+ "info": severity_counts["info"],
96
+ "risk_score": self._calculate_risk_score(severity_counts),
97
+ },
98
+ "findings": findings,
99
+ "remediation_priority": self._build_remediation_plan(findings),
100
+ }
101
+
102
+ @staticmethod
103
+ def _calculate_risk_score(counts: dict[str, int]) -> float:
104
+ """综合风险评分 (0-100): critical=10, high=5, medium=2, low=1, info=0.2。"""
105
+ weights = {"critical": 10, "high": 5, "medium": 2, "low": 1, "info": 0.2}
106
+ raw = sum(counts.get(sev, 0) * weight for sev, weight in weights.items())
107
+ return round(min(raw, 100), 1)
108
+
109
+ @staticmethod
110
+ def _build_remediation_plan(findings: list[dict]) -> list[dict]:
111
+ plan = []
112
+ critical = [f for f in findings if f["severity"] == "critical"]
113
+ high = [f for f in findings if f["severity"] == "high"]
114
+ medium = [f for f in findings if f["severity"] == "medium"]
115
+
116
+ if critical:
117
+ plan.append({
118
+ "priority": 1,
119
+ "action": f"立即修复 {len(critical)} 个严重漏洞",
120
+ "findings": [f["id"] for f in critical],
121
+ "deadline": "24 小时内",
122
+ })
123
+ if high:
124
+ plan.append({
125
+ "priority": 2,
126
+ "action": f"尽快修复 {len(high)} 个高危漏洞",
127
+ "findings": [f["id"] for f in high],
128
+ "deadline": "本周内",
129
+ })
130
+ if medium:
131
+ plan.append({
132
+ "priority": 3,
133
+ "action": f"计划修复 {len(medium)} 个中危漏洞",
134
+ "findings": [f["id"] for f in medium],
135
+ "deadline": "下个迭代",
136
+ })
137
+ return plan
v4_pro/ci_check.py ADDED
@@ -0,0 +1,79 @@
1
+ """CI/CD 集成 — 将 audit JSON 转为 GitHub Actions 行内注解。
2
+
3
+ 用法:
4
+ v4-pro audit --code ./src/ --format json > report.json
5
+ python -m v4_pro.ci_check report.json
6
+ """
7
+
8
+ import json
9
+ import re
10
+ import sys
11
+ from pathlib import Path
12
+
13
+
14
+ def _sanitize(text: str) -> str:
15
+ """清理控制字符和不可见字符。"""
16
+ if not text:
17
+ return ""
18
+ text = re.sub(r"[\x00-\x08\x0b\x0c\x0e-\x1f\ufffd]", "", text)
19
+ return text.strip()[:200]
20
+
21
+
22
+ def main():
23
+ # Force UTF-8 for GitHub Actions output
24
+ try:
25
+ sys.stdout.reconfigure(encoding="utf-8")
26
+ except AttributeError:
27
+ pass # Python < 3.7
28
+
29
+ args = sys.argv[1:]
30
+ if not args:
31
+ print("Usage: python -m v4_pro.ci_check <audit-report.json>")
32
+ sys.exit(1)
33
+
34
+ report_path = Path(args[0])
35
+ if not report_path.exists():
36
+ print(f"File not found: {report_path}")
37
+ sys.exit(1)
38
+
39
+ report = json.loads(report_path.read_bytes())
40
+ findings = report.get("findings", [])
41
+
42
+ if not findings:
43
+ print("✅ v4-pro: No issues found")
44
+ return
45
+
46
+ critical_findings = [f for f in findings if f.get("severity") == "critical"]
47
+ high_findings = [f for f in findings if f.get("severity") == "high"]
48
+
49
+ for finding in findings:
50
+ severity = finding.get("severity", "warning")
51
+ level = "error" if severity in ("critical", "high") else "warning"
52
+ title = _sanitize(finding.get("title", "Unknown issue"))
53
+ file_path = _sanitize(finding.get("file", ""))
54
+ line = finding.get("line", 1)
55
+
56
+ # GitHub Actions annotation
57
+ msg = f"::{level} file={file_path},line={line},title={title}::{title}"
58
+ print(msg)
59
+
60
+ rec = _sanitize(finding.get("recommendation", ""))
61
+ if rec:
62
+ print(f"::notice file={file_path},line={line}::Suggestion: {rec}")
63
+
64
+ # Summary
65
+ print("::group::V4 Pro Audit Summary")
66
+ total = len(findings)
67
+ critical = len(critical_findings)
68
+ high = len(high_findings)
69
+ print(f"Critical: {critical} | High: {high} | Total: {total}")
70
+ print("::endgroup::")
71
+
72
+ if critical > 0:
73
+ print(f"❌ {critical} critical issue(s) found — PR blocked")
74
+ sys.exit(1)
75
+ print("✅ V4 Pro quality gate passed")
76
+
77
+
78
+ if __name__ == "__main__":
79
+ main()