P1-19.1: Arc 边界测试强制 - TaskNode 新增 test_required 字段 - _inject_boundary_tests() 为每个模块注入接口测试和单元测试任务 - Done When 验证必须包含"测试通过" P1-19.2: AirRvr 高风险审计 - 新增 HighRiskAudit, HighRiskFinding 数据类 - ReviewReport 新增 highRiskAudit 字段,含 lifecycle/nullPointer/danglingPointer/exceptionSafety/concurrency + overallRisk + deliveryVerdict - 序列化/反序列化支持 P1-19.3: block-release 集成 - dispatch_worker_group() 派发前扫描最新审查报告 - deliveryVerdict=block-release 时阻止所有后续派发 - 记录 eng.blocked 事件 P1-20: frontend-design Skill 集成 - is_ui_task() UI 任务检测 - ensure_frontend_design_skill() 自动安装 Skill - route_ui_task() UI 任务路由决策 - enter_worker() 集成 UI 检测,skill 不可用时阻止执行 - commands/do.md 更新 UI 处理说明 - SKILL.md 新增 INV-12/INV-13/INV-14 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
63 lines
2.5 KiB
Python
63 lines
2.5 KiB
Python
"""AirSec mode — V2 安全扫描器。"""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
from air_runtime.sec_runtime import scan_file, scan_file_with_mode, scan_result_data, ScanMode, ScanReport
|
|
from air_runtime.io import safe_json_load
|
|
from air_runtime.paths import airplan_root, event_log_path
|
|
from air_runtime.events import EventLog, SEC_SCAN
|
|
|
|
|
|
def main(args) -> None:
|
|
project_root = Path(args.project).expanduser().resolve()
|
|
tid = args.task_id or "unknown"
|
|
sub = args.sub or "scan"
|
|
mode = getattr(args, "sec_mode", "blocking") or "blocking"
|
|
|
|
if mode not in ("advisory", "blocking"):
|
|
print("error: mode must be advisory or blocking", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
if sub == "scan":
|
|
if args.scan_path:
|
|
scan_path = Path(args.scan_path).expanduser().resolve()
|
|
if scan_path.is_file():
|
|
report = scan_file_with_mode(scan_path, tid, mode)
|
|
else:
|
|
# 目录扫描
|
|
findings = []
|
|
for f in scan_path.rglob("*"):
|
|
if f.is_file() and not any(x in f.name for x in [".git", "node_modules", "__pycache__"]):
|
|
r = scan_file_with_mode(f, tid, mode)
|
|
findings.extend(r.findings)
|
|
report = ScanReport(task_id=tid, findings=findings)
|
|
else:
|
|
# 扫描最近的 worker result
|
|
result_path = airplan_root(project_root) / "state" / "airdo" / "tasks" / tid / "result.json"
|
|
data = safe_json_load(result_path) or {}
|
|
report = scan_result_data(data, tid)
|
|
|
|
log = EventLog(event_log_path(project_root))
|
|
log.emit(SEC_SCAN, {
|
|
"taskId": tid,
|
|
"clean": report.clean,
|
|
"findings": len(report.findings),
|
|
"whitelisted": report.whitelisted,
|
|
"mode": mode,
|
|
})
|
|
|
|
print("airplan_mode=sec")
|
|
print(f"task_id={tid}")
|
|
print(f"scan_path={getattr(args, 'scan_path', '')}")
|
|
print(f"mode={mode}")
|
|
print(f"clean={report.clean}")
|
|
print(f"findings={len(report.findings)}")
|
|
print(f"whitelisted={report.whitelisted}")
|
|
if report.findings:
|
|
for f in report.findings[:5]:
|
|
print(f" {f.file}:{f.line} [{f.severity}] {f.rule}: {f.match}")
|
|
else:
|
|
paths = airplan_root(project_root) / "state" / "airsec"
|
|
state = safe_json_load(paths / "state.json") or {}
|
|
print(f"airplan_mode=sec\nenabled={state.get('enabled', False)}")
|