增强系统级实体覆盖摘要与工作台索引

这个提交包含在:
hao
2026-03-20 08:47:16 -07:00
父节点 5e1ea395ef
当前提交 c3a853d2cf
修改 160 个文件,包含 26943 行新增5119 行删除

查看文件

@@ -65,6 +65,7 @@ const DATA_HUB_ITEMS = [
{ title: "entity-queues.json", href: "/data/entity-queues.json", description: "discovery/history/latest/workflow 四类队列摘要。", badge: "json" },
{ title: "runs.json", href: "/runs.json", description: "最近运行的结构化详情,可用于 UI 和调试。", badge: "json" },
{ title: "systems.json", href: "/systems.json", description: "系统级覆盖、分类、更新时间和浏览器证据统计。", badge: "json" },
{ title: "entities.json", href: "/entities.json", description: "分层实体索引、实体状态和系统归属。", badge: "json" },
{ title: "advisories.json", href: "/advisories.json", description: "漏洞条目元数据、来源和 secure-code 主题。", badge: "json" },
{ title: "profiles.json", href: "/profiles.json", description: "复现档案元数据、成功判据和 browser assertions。", badge: "json" },
{ title: "architecture.json", href: "/architecture.json", description: "当前架构库的结构化真值。", badge: "json" },
@@ -94,6 +95,7 @@ const state = {
summary: null,
runs: [],
systems: [],
entities: [],
advisories: {},
profiles: {},
architecture: null,
@@ -599,6 +601,9 @@ function renderSystemCards(items, compact = false) {
const total = Math.max(Number(system.total || 0), 1);
const verified = Number(system.verified_real || 0) + Number(system.verified_synthetic || 0);
const coverage = Math.round((verified / total) * 100);
const entitySummary = system.entity_summary || {};
const topEntities = system.top_entities || [];
const backlogPreview = system.backlog_preview || [];
return `
<article class="system-card ${compact ? "system-card-compact" : ""}">
<div class="timeline-head">
@@ -610,8 +615,28 @@ function renderSystemCards(items, compact = false) {
<span class="tag">真实 ${escapeHtml(system.verified_real || 0)}</span>
<span class="tag">合成 ${escapeHtml(system.verified_synthetic || 0)}</span>
<span class="tag">阻塞 ${escapeHtml(system.blocked || 0)}</span>
<span class="tag">实体 ${escapeHtml(entitySummary.cataloged_entity_total || 0)}</span>
<span class="tag">backlog ${escapeHtml(entitySummary.candidate_entity_total || 0)}</span>
</div>
<div class="meter"><span style="--fill:${coverage}%"></span></div>
${compact ? "" : `
<div class="plan-grid" style="margin-top:12px;">
<article class="plan-card">
<span class="plan-label">实体覆盖</span>
<div class="plan-copy">cataloged ${escapeHtml(entitySummary.cataloged_entity_total || 0)} · child ${escapeHtml(entitySummary.child_entity_total || 0)} · plugins ${escapeHtml(entitySummary.plugin_total || 0)}</div>
</article>
<article class="plan-card">
<span class="plan-label">队列与缺口</span>
<div class="plan-copy">history complete ${escapeHtml(entitySummary.history_full_complete_count || 0)} · latest green ${escapeHtml(entitySummary.latest_green_count || 0)} · version gap ${escapeHtml(entitySummary.version_gap_entity_count || 0)}</div>
</article>
</div>
${(topEntities.length || backlogPreview.length) ? `
<div class="tag-row" style="margin-top:10px;">
${topEntities.map((item) => `<span class="tag">${escapeHtml(item.entity_type)} · ${escapeHtml(item.display_name)} · ${escapeHtml(item.advisory_count || 0)}</span>`).join("")}
${backlogPreview.map((item) => `<span class="tag">${escapeHtml(item.entity_type)} backlog · ${escapeHtml(item.display_name)}</span>`).join("")}
</div>
` : ""}
`}
<div class="detail-actions" style="margin-top:12px;">
<button class="button button-secondary button-small" type="button" data-filter-key="system" data-filter-value="${escapeHtml(system.system_id)}">锁定系统</button>
<a class="button button-secondary button-small" href="${escapeHtml(buildUrl("runs", { system: system.system_id, run: null }))}">查看运行</a>
@@ -1609,10 +1634,11 @@ async function loadData(preserveSelection = true) {
renderSyncState("loading", "刷新中", `本地时间 ${new Date().toLocaleTimeString("zh-CN", { hour12: false })}`);
try {
const [summary, runs, systems, advisories, profiles, architecture, completeness, entityCompleteness, sourceHealth, alerts, monitorSummary] = await Promise.all([
const [summary, runs, systems, entities, advisories, profiles, architecture, completeness, entityCompleteness, sourceHealth, alerts, monitorSummary] = await Promise.all([
fetchJson("/summary.json"),
fetchJson("/runs.json"),
fetchJson("/systems.json"),
fetchJson("/entities.json"),
fetchJson("/advisories.json"),
fetchJson("/profiles.json"),
fetchJson("/architecture.json"),
@@ -1626,6 +1652,7 @@ async function loadData(preserveSelection = true) {
state.summary = summary;
state.runs = runs;
state.systems = systems;
state.entities = entities;
state.advisories = advisories;
state.profiles = profiles;
state.architecture = architecture;

查看文件

@@ -18,6 +18,7 @@ LEGACY_TEMPLATE_DIR = TEMPLATES_DIR / "legacy"
LOVART_VENDOR_MANIFEST = LOVART_TEMPLATE_DIR / "vendor" / "source-manifest.json"
ROOT_JSON_FILES = ["summary.json", "runs.json", "systems.json", "advisories.json", "profiles.json"]
ROOT_JSON_FILES.append("architecture.json")
ROOT_JSON_FILES.append("entities.json")
SECTION_ROUTE_DIRS = ["overview", "runs", "systems", "architecture", "data"]
CATEGORY_LABELS = {
@@ -570,6 +571,7 @@ def _build_architecture_data(summary: Dict[str, Any], source_map: Dict[str, Any]
_link("entity-queues.json", "/data/entity-queues.json", "discovery/history/latest/workflow 四类队列摘要。"),
_link("runs.json", "/runs.json", "最近 run 的结构化详情。"),
_link("systems.json", "/systems.json", "系统级覆盖与浏览器证据摘要。"),
_link("entities.json", "/entities.json", "分层实体索引、实体状态和系统归属。"),
_link("advisories.json", "/advisories.json", "漏洞条目元数据与来源。"),
_link("profiles.json", "/profiles.json", "复现档案元数据。"),
_link("architecture.json", "/architecture.json", "当前架构库结构化 JSON。"),
@@ -1363,12 +1365,17 @@ def render_dashboard(
entity_completeness = read_json(ROOT / "08-threat-intel" / "generated" / "entity-completeness.json", default={}) or {}
entity_backlog = read_json(ROOT / "08-threat-intel" / "generated" / "entity-discovery-backlog.json", default=[]) or []
entity_queues = read_json(ROOT / "08-threat-intel" / "generated" / "entity-queues.json", default={}) or {}
entity_records = load_json_dir(ROOT / "08-threat-intel" / "registry" / "entities")
source_map = source_map_data if source_map_data is not None else (read_yaml(SOURCE_MAP_PATH, default={}) or {})
repro_map = repro_map_data if repro_map_data is not None else (read_yaml(REPRO_MAP_PATH, default={}) or {})
source_system_map = {item["system_id"]: item for item in source_map.get("systems", []) if item.get("system_id")}
merged_advisories = _merge_latest_advisories(advisory_records, runs, source_system_map)
advisory_map = {item["canonical_id"]: item for item in merged_advisories if item.get("canonical_id")}
profile_map = load_profiles()
entity_summary_map = {item.get("system_id"): item for item in (entity_completeness.get("systems") or []) if item.get("system_id")}
entities_by_system: Dict[str, List[Dict[str, Any]]] = {}
for item in sorted(entity_records, key=lambda value: (value.get("root_system_id") or "", value.get("entity_type") or "", value.get("display_name") or "")):
entities_by_system.setdefault(item.get("root_system_id") or "", []).append(item)
_sync_run_bundles(runs)
@@ -1419,6 +1426,15 @@ def render_dashboard(
elif status not in {"verified-synthetic"}:
family_entry["manual"] += 1
for system_id, system in systems.items():
entity_summary = entity_summary_map.get(system_id, {})
system["entity_summary"] = entity_summary
system["top_entities"] = entity_summary.get("top_entities", [])
system["backlog_preview"] = entity_summary.get("backlog_preview", [])
system["entity_total"] = entity_summary.get("cataloged_entity_total", 0)
system["entity_backlog"] = entity_summary.get("candidate_entity_total", 0)
system["entity_type_counts"] = entity_summary.get("entity_type_counts", {})
recent_runs = sorted(runs, key=lambda item: item.get("finished_at") or "", reverse=True)[:100]
decorated_runs: List[Dict[str, Any]] = []
for item in recent_runs:
@@ -1532,6 +1548,7 @@ def render_dashboard(
write_json(DASHBOARD_DIR / "summary.json", summary)
write_json(DASHBOARD_DIR / "runs.json", decorated_runs)
write_json(DASHBOARD_DIR / "systems.json", summary["systems"])
write_json(DASHBOARD_DIR / "entities.json", entity_records)
write_json(DASHBOARD_DIR / "advisories.json", {key: _advisory_meta(value) for key, value in advisory_map.items()})
write_json(DASHBOARD_DIR / "profiles.json", {key: _profile_meta(value) for key, value in profile_map.items()})
write_json(DASHBOARD_DIR / "data" / "completeness.json", completeness)