|
|
|
|
@@ -8,6 +8,8 @@ from typing import Any, Dict, Iterable, List
|
|
|
|
|
from intel.config import FRAMEWORK_ROOT, GENERATED_DIR, REGISTRY_ROOT, ROOT, SECURE_CODE_ROOT, SYSTEMS_DIR, TRIAGE_DIR
|
|
|
|
|
from intel.models import AdvisoryRecord
|
|
|
|
|
from intel.utils import ensure_dir, isoformat, now_utc, write_json, write_text
|
|
|
|
|
from lab.render import render_dashboard as render_lab_dashboard
|
|
|
|
|
from lab.repro import annotate_with_latest_run, latest_runs_by_advisory
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UTC = timezone.utc
|
|
|
|
|
@@ -109,6 +111,25 @@ FORBIDDEN_SCENARIOS = [
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _merged_item(item: AdvisoryRecord, run_map: Dict[str, Dict[str, Any]]) -> Dict[str, Any]:
|
|
|
|
|
return annotate_with_latest_run(item.to_dict(), run_map.get(item.canonical_id))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _status_counts(items: List[Dict[str, Any]]) -> Dict[str, int]:
|
|
|
|
|
counts = {"verified_real": 0, "verified_synthetic": 0, "blocked": 0, "manual": 0}
|
|
|
|
|
for item in items:
|
|
|
|
|
status = item.get("verification_status")
|
|
|
|
|
if status == "verified-real":
|
|
|
|
|
counts["verified_real"] += 1
|
|
|
|
|
elif status == "verified-synthetic":
|
|
|
|
|
counts["verified_synthetic"] += 1
|
|
|
|
|
elif status and status.startswith("blocked-"):
|
|
|
|
|
counts["blocked"] += 1
|
|
|
|
|
else:
|
|
|
|
|
counts["manual"] += 1
|
|
|
|
|
return counts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _recent_count(items: Iterable[AdvisoryRecord], days: int = 30) -> int:
|
|
|
|
|
cutoff = now_utc() - timedelta(days=days)
|
|
|
|
|
total = 0
|
|
|
|
|
@@ -161,6 +182,7 @@ def _clear_json_dir(path: Path) -> None:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def render_system_scaffolding(source_map: Dict[str, Any], advisories: List[AdvisoryRecord]) -> None:
|
|
|
|
|
run_map = latest_runs_by_advisory()
|
|
|
|
|
grouped: Dict[str, List[AdvisoryRecord]] = defaultdict(list)
|
|
|
|
|
for advisory in advisories:
|
|
|
|
|
grouped[advisory.system_id].append(advisory)
|
|
|
|
|
@@ -172,7 +194,9 @@ def render_system_scaffolding(source_map: Dict[str, Any], advisories: List[Advis
|
|
|
|
|
ensure_dir(system_dir / "cases")
|
|
|
|
|
|
|
|
|
|
items = sorted(grouped.get(system["system_id"], []), key=lambda item: item.published_at or "", reverse=True)
|
|
|
|
|
merged_items = [_merged_item(item, run_map) for item in items]
|
|
|
|
|
markdown_count = len([item for item in items if item.render_markdown and item.case_path])
|
|
|
|
|
counts = _status_counts(merged_items)
|
|
|
|
|
index_lines = [
|
|
|
|
|
f"# {system['display_name']}",
|
|
|
|
|
"",
|
|
|
|
|
@@ -184,6 +208,10 @@ def render_system_scaffolding(source_map: Dict[str, Any], advisories: List[Advis
|
|
|
|
|
f"- 总案例数: `{len(items)}`",
|
|
|
|
|
f"- 近 30 天新增/更新: `{_recent_count(items)}`",
|
|
|
|
|
f"- 重点 Markdown 案例数: `{markdown_count}`",
|
|
|
|
|
f"- 已实证(真实版本): `{counts['verified_real']}`",
|
|
|
|
|
f"- 已实证(synthetic): `{counts['verified_synthetic']}`",
|
|
|
|
|
f"- 阻塞数: `{counts['blocked']}`",
|
|
|
|
|
f"- 待人工/缺浏览器证据: `{counts['manual']}`",
|
|
|
|
|
f"- 最近渲染时间: `{isoformat(now_utc())}`",
|
|
|
|
|
"",
|
|
|
|
|
"## 目标约束",
|
|
|
|
|
@@ -205,19 +233,19 @@ def render_system_scaffolding(source_map: Dict[str, Any], advisories: List[Advis
|
|
|
|
|
"",
|
|
|
|
|
"## 案例列表",
|
|
|
|
|
"",
|
|
|
|
|
"| 标题 | 严重度 | 状态 | 来源置信度 | 更新时间 | 案例页 |",
|
|
|
|
|
"|------|--------|------|------------|----------|--------|",
|
|
|
|
|
"| 标题 | 严重度 | 案例状态 | 实证状态 | 实证方式 | 来源置信度 | 更新时间 | 案例页 |",
|
|
|
|
|
"|------|--------|----------|----------|----------|------------|----------|--------|",
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
if items:
|
|
|
|
|
for item in items:
|
|
|
|
|
case_link = f"[link]({_abs_repo_path(item.case_path)})" if item.case_path else "-"
|
|
|
|
|
timestamp = item.updated_at or item.published_at or ""
|
|
|
|
|
if merged_items:
|
|
|
|
|
for item in merged_items:
|
|
|
|
|
case_link = f"[link]({_abs_repo_path(item['case_path'])})" if item.get("case_path") else "-"
|
|
|
|
|
timestamp = item.get("updated_at") or item.get("published_at") or ""
|
|
|
|
|
index_lines.append(
|
|
|
|
|
f"| {item.title} | `{item.severity}` | `{item.status}` | `{item.source_confidence}` | `{timestamp}` | {case_link} |"
|
|
|
|
|
f"| {item['title']} | `{item['severity']}` | `{item['status']}` | `{item.get('verification_status', 'triage-manual')}` | `{item.get('verification_mode', '-')}` | `{item['source_confidence']}` | `{timestamp}` | {case_link} |"
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
index_lines.append("| No advisories yet | `n/a` | `empty` | `n/a` | `n/a` | - |")
|
|
|
|
|
index_lines.append("| No advisories yet | `n/a` | `empty` | `n/a` | `n/a` | `n/a` | `n/a` | - |")
|
|
|
|
|
write_text(system_dir / "INDEX.md", "\n".join(index_lines))
|
|
|
|
|
|
|
|
|
|
system_registry_path = _abs_repo_path("08-threat-intel", "registry", "systems", f"{system['system_id']}.json")
|
|
|
|
|
@@ -274,9 +302,11 @@ def render_system_scaffolding(source_map: Dict[str, Any], advisories: List[Advis
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def render_case_pages(advisories: List[AdvisoryRecord]) -> None:
|
|
|
|
|
run_map = latest_runs_by_advisory()
|
|
|
|
|
for item in advisories:
|
|
|
|
|
if not item.render_markdown or not item.case_path:
|
|
|
|
|
continue
|
|
|
|
|
merged = _merged_item(item, run_map)
|
|
|
|
|
lines = [
|
|
|
|
|
"---",
|
|
|
|
|
f'title: "{item.title.replace(chr(34), chr(39))}"',
|
|
|
|
|
@@ -288,6 +318,10 @@ def render_case_pages(advisories: List[AdvisoryRecord]) -> None:
|
|
|
|
|
f'severity: "{item.severity}"',
|
|
|
|
|
f'exploit_status: "{item.exploit_status}"',
|
|
|
|
|
f'source_confidence: "{item.source_confidence}"',
|
|
|
|
|
f'verification_status: "{merged.get("verification_status", "triage-manual")}"',
|
|
|
|
|
f'verification_mode: "{merged.get("verification_mode", "synthetic")}"',
|
|
|
|
|
f'artifact_mode: "{merged.get("artifact_mode") or ""}"',
|
|
|
|
|
f'last_run_id: "{merged.get("last_run_id") or ""}"',
|
|
|
|
|
'target_types:',
|
|
|
|
|
' - "lab-local"',
|
|
|
|
|
' - "lab-public"',
|
|
|
|
|
@@ -315,6 +349,15 @@ def render_case_pages(advisories: List[AdvisoryRecord]) -> None:
|
|
|
|
|
"",
|
|
|
|
|
f"# {item.title}",
|
|
|
|
|
"",
|
|
|
|
|
"## 本地实证状态",
|
|
|
|
|
"",
|
|
|
|
|
f"- 实证状态: `{merged.get('verification_status', 'triage-manual')}`",
|
|
|
|
|
f"- 实证方式: `{merged.get('verification_mode', 'synthetic')}`",
|
|
|
|
|
f"- Artifact 模式: `{merged.get('artifact_mode') or 'unknown'}`",
|
|
|
|
|
f"- 最近运行: `{merged.get('last_run_id') or '-'}`",
|
|
|
|
|
f"- 浏览器证据: `{'present' if merged.get('browser_evidence', {}).get('present') else 'missing'}`",
|
|
|
|
|
f"- Run Bundle: `{merged.get('evidence_bundle') or '-'}`",
|
|
|
|
|
"",
|
|
|
|
|
"## 事件层",
|
|
|
|
|
"",
|
|
|
|
|
f"- Canonical ID: `{item.canonical_id}`",
|
|
|
|
|
@@ -362,9 +405,10 @@ def render_registry(source_map: Dict[str, Any], advisories: List[AdvisoryRecord]
|
|
|
|
|
_clear_json_dir(REGISTRY_ROOT / "systems")
|
|
|
|
|
_clear_json_dir(TRIAGE_DIR)
|
|
|
|
|
|
|
|
|
|
run_map = latest_runs_by_advisory()
|
|
|
|
|
grouped: Dict[str, List[AdvisoryRecord]] = defaultdict(list)
|
|
|
|
|
for advisory in advisories:
|
|
|
|
|
write_json(REGISTRY_ROOT / "advisories" / f"{advisory.canonical_id}.json", advisory.to_dict())
|
|
|
|
|
write_json(REGISTRY_ROOT / "advisories" / f"{advisory.canonical_id}.json", _merged_item(advisory, run_map))
|
|
|
|
|
grouped[advisory.system_id].append(advisory)
|
|
|
|
|
|
|
|
|
|
triage_by_system: Dict[str, List[Dict[str, Any]]] = defaultdict(list)
|
|
|
|
|
@@ -375,6 +419,8 @@ def render_registry(source_map: Dict[str, Any], advisories: List[AdvisoryRecord]
|
|
|
|
|
for system in source_map["systems"]:
|
|
|
|
|
system_id = system["system_id"]
|
|
|
|
|
items = grouped.get(system_id, [])
|
|
|
|
|
merged_items = [_merged_item(item, run_map) for item in items]
|
|
|
|
|
counts = _status_counts(merged_items)
|
|
|
|
|
payload = {
|
|
|
|
|
"system_id": system_id,
|
|
|
|
|
"display_name": system["display_name"],
|
|
|
|
|
@@ -386,6 +432,10 @@ def render_registry(source_map: Dict[str, Any], advisories: List[AdvisoryRecord]
|
|
|
|
|
"latest_update": max((item.updated_at or item.published_at or "" for item in items), default=""),
|
|
|
|
|
"output_dir": system["output_dir"],
|
|
|
|
|
"secure_code_topics": system.get("secure_code_topics", []),
|
|
|
|
|
"verified_real": counts["verified_real"],
|
|
|
|
|
"verified_synthetic": counts["verified_synthetic"],
|
|
|
|
|
"blocked_count": counts["blocked"],
|
|
|
|
|
"manual_count": counts["manual"],
|
|
|
|
|
"items": [item.canonical_id for item in sorted(items, key=lambda item: item.published_at or "", reverse=True)],
|
|
|
|
|
}
|
|
|
|
|
write_json(SYSTEMS_DIR / f"{system_id}.json", payload)
|
|
|
|
|
@@ -400,6 +450,7 @@ def render_generated(
|
|
|
|
|
) -> None:
|
|
|
|
|
ensure_dir(GENERATED_DIR)
|
|
|
|
|
systems = {item["system_id"]: item for item in source_map["systems"]}
|
|
|
|
|
run_map = latest_runs_by_advisory()
|
|
|
|
|
change_summary = change_summary or {}
|
|
|
|
|
triage_by_system: Dict[str, List[Dict[str, Any]]] = defaultdict(list)
|
|
|
|
|
for item in triage:
|
|
|
|
|
@@ -408,19 +459,24 @@ def render_generated(
|
|
|
|
|
coverage_lines = [
|
|
|
|
|
"# 覆盖矩阵",
|
|
|
|
|
"",
|
|
|
|
|
"| 系统 | 分类 | 覆盖策略 | 历史全量 | 近两年全量 | 全量 registry | 重点案例 Markdown | secure-code 关联 | 自动同步状态 | triage | 最近更新 |",
|
|
|
|
|
"|------|------|----------|----------|------------|--------------|--------------------|------------------|--------------|--------|----------|",
|
|
|
|
|
"| 系统 | 分类 | 覆盖策略 | 历史全量 | 近两年全量 | 全量 registry | 重点案例 Markdown | secure-code 关联 | 自动同步状态 | 本地实证状态 | 浏览器证据 | run bundle | triage | 最近更新 |",
|
|
|
|
|
"|------|------|----------|----------|------------|--------------|--------------------|------------------|--------------|--------------|------------|-----------|--------|----------|",
|
|
|
|
|
]
|
|
|
|
|
by_system: Dict[str, List[AdvisoryRecord]] = defaultdict(list)
|
|
|
|
|
for advisory in advisories:
|
|
|
|
|
by_system[advisory.system_id].append(advisory)
|
|
|
|
|
for system_id, system in sorted(systems.items()):
|
|
|
|
|
items = by_system.get(system_id, [])
|
|
|
|
|
merged_items = [_merged_item(item, run_map) for item in items]
|
|
|
|
|
counts = _status_counts(merged_items)
|
|
|
|
|
markdown_count = len([item for item in items if item.case_path])
|
|
|
|
|
sync_state = "seeded" if items else "scaffolded"
|
|
|
|
|
recent = max((item.updated_at or item.published_at or "" for item in items), default="")
|
|
|
|
|
browser_present = len([item for item in merged_items if item.get("browser_evidence", {}).get("present")])
|
|
|
|
|
run_bundle_count = len([item for item in merged_items if item.get("last_run_id")])
|
|
|
|
|
proof_state = f"real:{counts['verified_real']}/synthetic:{counts['verified_synthetic']}/blocked:{counts['blocked']}"
|
|
|
|
|
coverage_lines.append(
|
|
|
|
|
f"| {system['display_name']} | `{system['category']}` | `{system['tier']}` | `{'yes' if system['tier'] == 'history-full' else '-'}` | `yes` | `{len(items)}` | `{markdown_count}` | `{len(system.get('secure_code_topics', []))}` | `{sync_state}` | `{len(triage_by_system.get(system_id, []))}` | `{recent}` |"
|
|
|
|
|
f"| {system['display_name']} | `{system['category']}` | `{system['tier']}` | `{'yes' if system['tier'] == 'history-full' else '-'}` | `yes` | `{len(items)}` | `{markdown_count}` | `{len(system.get('secure_code_topics', []))}` | `{sync_state}` | `{proof_state}` | `{browser_present}` | `{run_bundle_count}` | `{len(triage_by_system.get(system_id, []))}` | `{recent}` |"
|
|
|
|
|
)
|
|
|
|
|
write_text(GENERATED_DIR / "coverage-matrix.md", "\n".join(coverage_lines))
|
|
|
|
|
|
|
|
|
|
@@ -432,6 +488,7 @@ def render_generated(
|
|
|
|
|
f"- 系统数量: `{len(source_map['systems'])}`",
|
|
|
|
|
f"- Advisory 数量: `{len(advisories)}`",
|
|
|
|
|
f"- 重点 Markdown 数量: `{markdown_total}`",
|
|
|
|
|
f"- Run Bundle 数量: `{len(run_map)}`",
|
|
|
|
|
f"- 新增记录: `{change_summary.get('new_count', 0)}`",
|
|
|
|
|
f"- 更新记录: `{change_summary.get('updated_count', 0)}`",
|
|
|
|
|
f"- Triage 数量: `{len(triage)}`",
|
|
|
|
|
@@ -454,9 +511,11 @@ def render_generated(
|
|
|
|
|
"updated_count": change_summary.get("updated_count", 0),
|
|
|
|
|
"systems_touched": change_summary.get("systems_touched", []),
|
|
|
|
|
"triage_count": len(triage),
|
|
|
|
|
"run_bundle_count": len(run_map),
|
|
|
|
|
"failures": failures,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
render_lab_dashboard()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def render_secure_code(source_map: Dict[str, Any]) -> None:
|
|
|
|
|
|