XuanJian for OpenClaw
+玄鉴 · 攻击复现与策略实验
业务测试台
真实测试请求 · 玄鉴插件数据流实时观测
diff --git a/README.md b/README.md index fd18574..b3c73b2 100644 --- a/README.md +++ b/README.md @@ -80,8 +80,8 @@ applyEffects(state, effects); 2. 打开 `/agentsentry` 返回的认证 URL。裸 `http://127.0.0.1:8765/` 在全新浏览器中按设计返回 `401`;bootstrap token 写入安全 session cookie 后立即从地址栏移除。 3. 进入 `/command-lab`,先运行一个普通文件写入或健康检查,证明系统不是一刀切。 4. 再运行隐藏指令、敏感数据外发或记忆投毒样例,观察动作在执行前进入 `deny` 或 `ask`。 -5. 打开 `/security-screen` 的“因果”视图,查看 `intent -> capability -> data -> action -> sink` 路径和命中的边界。 -6. 回到首页查看 `tool_decision`、`guard_finding`、`alert`、`tool_result`,并导出 JSON/CSV 审计记录。 +5. 回到首页查看实时生长的 `intent -> capability -> data -> action -> sink -> guard -> decision` 语义动作图。 +6. 点击高亮攻击路径和右侧 Inspector,回放污染数据如何影响危险参数并在执行前被阻断;需要全局统计时再进入 `/security-screen`。 更完整的演示话术和复现顺序见 [演示与复现实验指南](reports/demo_and_reproduction.md)。 @@ -167,9 +167,9 @@ $env:AGENTSENTRY_API_KEY="YOUR_JUDGE_KEY" 认证后可使用三个主视图: -- `/`:实时记录、裁决详情、JSON/CSV 导出和运行配置。 +- `/`:主产品入口,按会话展示实时语义动作图、攻击路径、可读裁决依据和执行回放。 - `/command-lab`:受控业务工具与公开攻击样例的逐条复测入口。 -- `/security-screen`:安全态势、四域一环指标、告警时间线和 Live Causal Graph。 +- `/security-screen`:全局安全态势、四域一环指标、告警时间线和 3D 拓扑补充视图。 Dashboard 默认只允许 loopback。若要绑定 `0.0.0.0`,必须显式启用远程访问并配置认证 token;不要为了容器部署直接暴露未认证端口。 diff --git a/artifacts/design-qa/comparison-focused-context-final-20260808.jpg b/artifacts/design-qa/comparison-focused-context-final-20260808.jpg new file mode 100644 index 0000000..efa16c8 Binary files /dev/null and b/artifacts/design-qa/comparison-focused-context-final-20260808.jpg differ diff --git a/artifacts/design-qa/comparison-focused-context-final.png b/artifacts/design-qa/comparison-focused-context-final.png new file mode 100644 index 0000000..bb0466e Binary files /dev/null and b/artifacts/design-qa/comparison-focused-context-final.png differ diff --git a/artifacts/design-qa/comparison-focused-context.png b/artifacts/design-qa/comparison-focused-context.png new file mode 100644 index 0000000..b6de323 Binary files /dev/null and b/artifacts/design-qa/comparison-focused-context.png differ diff --git a/artifacts/design-qa/comparison-focused-graph-final-20260808.jpg b/artifacts/design-qa/comparison-focused-graph-final-20260808.jpg new file mode 100644 index 0000000..9979412 Binary files /dev/null and b/artifacts/design-qa/comparison-focused-graph-final-20260808.jpg differ diff --git a/artifacts/design-qa/comparison-focused-graph-final.png b/artifacts/design-qa/comparison-focused-graph-final.png new file mode 100644 index 0000000..40bcb65 Binary files /dev/null and b/artifacts/design-qa/comparison-focused-graph-final.png differ diff --git a/artifacts/design-qa/comparison-focused-graph.png b/artifacts/design-qa/comparison-focused-graph.png new file mode 100644 index 0000000..4712d77 Binary files /dev/null and b/artifacts/design-qa/comparison-focused-graph.png differ diff --git a/artifacts/design-qa/comparison-focused-inspector-final-20260808.jpg b/artifacts/design-qa/comparison-focused-inspector-final-20260808.jpg new file mode 100644 index 0000000..dbceb22 Binary files /dev/null and b/artifacts/design-qa/comparison-focused-inspector-final-20260808.jpg differ diff --git a/artifacts/design-qa/comparison-focused-inspector-final.png b/artifacts/design-qa/comparison-focused-inspector-final.png new file mode 100644 index 0000000..e159d0c Binary files /dev/null and b/artifacts/design-qa/comparison-focused-inspector-final.png differ diff --git a/artifacts/design-qa/comparison-focused-inspector.png b/artifacts/design-qa/comparison-focused-inspector.png new file mode 100644 index 0000000..26344b8 Binary files /dev/null and b/artifacts/design-qa/comparison-focused-inspector.png differ diff --git a/artifacts/design-qa/comparison-reference-left-implementation-right-final-20260808.jpg b/artifacts/design-qa/comparison-reference-left-implementation-right-final-20260808.jpg new file mode 100644 index 0000000..87710c8 Binary files /dev/null and b/artifacts/design-qa/comparison-reference-left-implementation-right-final-20260808.jpg differ diff --git a/artifacts/design-qa/comparison-reference-left-implementation-right-final.png b/artifacts/design-qa/comparison-reference-left-implementation-right-final.png new file mode 100644 index 0000000..073d2a7 Binary files /dev/null and b/artifacts/design-qa/comparison-reference-left-implementation-right-final.png differ diff --git a/artifacts/design-qa/comparison-reference-left-implementation-right.png b/artifacts/design-qa/comparison-reference-left-implementation-right.png new file mode 100644 index 0000000..7602f9a Binary files /dev/null and b/artifacts/design-qa/comparison-reference-left-implementation-right.png differ diff --git a/artifacts/design-qa/implementation-1366x768-final-20260808.jpg b/artifacts/design-qa/implementation-1366x768-final-20260808.jpg new file mode 100644 index 0000000..3dfc507 Binary files /dev/null and b/artifacts/design-qa/implementation-1366x768-final-20260808.jpg differ diff --git a/artifacts/design-qa/implementation-1366x768-final.png b/artifacts/design-qa/implementation-1366x768-final.png new file mode 100644 index 0000000..ed16d8d Binary files /dev/null and b/artifacts/design-qa/implementation-1366x768-final.png differ diff --git a/artifacts/design-qa/implementation-1366x768.png b/artifacts/design-qa/implementation-1366x768.png new file mode 100644 index 0000000..4269c3c Binary files /dev/null and b/artifacts/design-qa/implementation-1366x768.png differ diff --git a/artifacts/design-qa/implementation-1680x944-final-20260808.jpg b/artifacts/design-qa/implementation-1680x944-final-20260808.jpg new file mode 100644 index 0000000..f6aa274 Binary files /dev/null and b/artifacts/design-qa/implementation-1680x944-final-20260808.jpg differ diff --git a/artifacts/design-qa/implementation-1680x944-final.png b/artifacts/design-qa/implementation-1680x944-final.png new file mode 100644 index 0000000..66bbfac Binary files /dev/null and b/artifacts/design-qa/implementation-1680x944-final.png differ diff --git a/artifacts/design-qa/implementation-1680x944.png b/artifacts/design-qa/implementation-1680x944.png new file mode 100644 index 0000000..2a7049f Binary files /dev/null and b/artifacts/design-qa/implementation-1680x944.png differ diff --git a/artifacts/design-qa/implementation-390x844-final-20260808.jpg b/artifacts/design-qa/implementation-390x844-final-20260808.jpg new file mode 100644 index 0000000..8d901a8 Binary files /dev/null and b/artifacts/design-qa/implementation-390x844-final-20260808.jpg differ diff --git a/artifacts/design-qa/implementation-390x844-final.png b/artifacts/design-qa/implementation-390x844-final.png new file mode 100644 index 0000000..562599b Binary files /dev/null and b/artifacts/design-qa/implementation-390x844-final.png differ diff --git a/artifacts/design-qa/implementation-390x844.png b/artifacts/design-qa/implementation-390x844.png new file mode 100644 index 0000000..7095690 Binary files /dev/null and b/artifacts/design-qa/implementation-390x844.png differ diff --git a/artifacts/design-qa/theme-overview-graphite-1680.png b/artifacts/design-qa/theme-overview-graphite-1680.png new file mode 100644 index 0000000..67842f0 Binary files /dev/null and b/artifacts/design-qa/theme-overview-graphite-1680.png differ diff --git a/artifacts/design-qa/theme-overview-graphite-390.png b/artifacts/design-qa/theme-overview-graphite-390.png new file mode 100644 index 0000000..6a9d5ca Binary files /dev/null and b/artifacts/design-qa/theme-overview-graphite-390.png differ diff --git a/artifacts/design-qa/theme-overview-midnight-1680.png b/artifacts/design-qa/theme-overview-midnight-1680.png new file mode 100644 index 0000000..f91fd2d Binary files /dev/null and b/artifacts/design-qa/theme-overview-midnight-1680.png differ diff --git a/artifacts/design-qa/theme-root-graphite-1680.png b/artifacts/design-qa/theme-root-graphite-1680.png new file mode 100644 index 0000000..e90548a Binary files /dev/null and b/artifacts/design-qa/theme-root-graphite-1680.png differ diff --git a/artifacts/design-qa/workspace-agents-1680.png b/artifacts/design-qa/workspace-agents-1680.png new file mode 100644 index 0000000..839e5bf Binary files /dev/null and b/artifacts/design-qa/workspace-agents-1680.png differ diff --git a/artifacts/design-qa/workspace-agents-390.png b/artifacts/design-qa/workspace-agents-390.png new file mode 100644 index 0000000..ddb69e1 Binary files /dev/null and b/artifacts/design-qa/workspace-agents-390.png differ diff --git a/artifacts/design-qa/workspace-alerts-1680.png b/artifacts/design-qa/workspace-alerts-1680.png new file mode 100644 index 0000000..d566079 Binary files /dev/null and b/artifacts/design-qa/workspace-alerts-1680.png differ diff --git a/artifacts/design-qa/workspace-alerts-390.png b/artifacts/design-qa/workspace-alerts-390.png new file mode 100644 index 0000000..7b94bad Binary files /dev/null and b/artifacts/design-qa/workspace-alerts-390.png differ diff --git a/artifacts/design-qa/workspace-audit-1680.png b/artifacts/design-qa/workspace-audit-1680.png new file mode 100644 index 0000000..c5fb5b5 Binary files /dev/null and b/artifacts/design-qa/workspace-audit-1680.png differ diff --git a/artifacts/design-qa/workspace-audit-390.png b/artifacts/design-qa/workspace-audit-390.png new file mode 100644 index 0000000..efc4889 Binary files /dev/null and b/artifacts/design-qa/workspace-audit-390.png differ diff --git a/artifacts/design-qa/workspace-overview-1680.png b/artifacts/design-qa/workspace-overview-1680.png new file mode 100644 index 0000000..75003a5 Binary files /dev/null and b/artifacts/design-qa/workspace-overview-1680.png differ diff --git a/artifacts/design-qa/workspace-overview-390.png b/artifacts/design-qa/workspace-overview-390.png new file mode 100644 index 0000000..860b21d Binary files /dev/null and b/artifacts/design-qa/workspace-overview-390.png differ diff --git a/artifacts/design-qa/workspace-policies-1680.png b/artifacts/design-qa/workspace-policies-1680.png new file mode 100644 index 0000000..a2ad464 Binary files /dev/null and b/artifacts/design-qa/workspace-policies-1680.png differ diff --git a/artifacts/design-qa/workspace-policies-390.png b/artifacts/design-qa/workspace-policies-390.png new file mode 100644 index 0000000..3490b6f Binary files /dev/null and b/artifacts/design-qa/workspace-policies-390.png differ diff --git a/artifacts/design-qa/workspace-settings-1680.png b/artifacts/design-qa/workspace-settings-1680.png new file mode 100644 index 0000000..c59a3d0 Binary files /dev/null and b/artifacts/design-qa/workspace-settings-1680.png differ diff --git a/artifacts/design-qa/workspace-settings-390.png b/artifacts/design-qa/workspace-settings-390.png new file mode 100644 index 0000000..4595a69 Binary files /dev/null and b/artifacts/design-qa/workspace-settings-390.png differ diff --git a/artifacts/design-qa/workspace-tools-1680.png b/artifacts/design-qa/workspace-tools-1680.png new file mode 100644 index 0000000..facc9da Binary files /dev/null and b/artifacts/design-qa/workspace-tools-1680.png differ diff --git a/artifacts/design-qa/workspace-tools-390.png b/artifacts/design-qa/workspace-tools-390.png new file mode 100644 index 0000000..9438736 Binary files /dev/null and b/artifacts/design-qa/workspace-tools-390.png differ diff --git a/design-qa.md b/design-qa.md index 7a26476..34181ac 100644 --- a/design-qa.md +++ b/design-qa.md @@ -1,42 +1,131 @@ -# Security Screen DAG Design QA +# Semantic Action Graph Design QA -- source visual truth path: `.tmp/graph-v2-1920x1080.png` and `.tmp/graph-v2-1366x768.png` -- implementation URL: `http://127.0.0.1:8766/security-screen` -- implementation screenshot path: unavailable; the primary in-app Browser failed before capture because the shared automation runtime omitted its sandbox metadata -- viewport: intended checks at 1920x1080 and 1366x768 -- state: causal graph view with a real attack trace; authorized and enforcement-bypass traces covered by projection tests -- full-view comparison evidence: blocked because a current implementation screenshot could not be captured -- focused region comparison evidence: an independent Chromium pass verified the live 1366x768 layout and interactions, but did not retain a screenshot artifact for source-to-implementation comparison +- source visual truth path: `E:\weix\xwechat_files\wxid_qb9bpbg86nc222_5bd3\temp\RWTemp\2026-08\0aded3c3359cb22b61fecbb3abf4660c.png` +- implementation URL: `http://127.0.0.1:8766/` +- implementation screenshot path: `artifacts/design-qa/implementation-1680x944-final-20260808.jpg` +- responsive evidence: `artifacts/design-qa/implementation-1366x768-final-20260808.jpg`, `artifacts/design-qa/implementation-390x844-final-20260808.jpg` +- full-view comparison: `artifacts/design-qa/comparison-reference-left-implementation-right-final-20260808.jpg` +- focused comparisons: `artifacts/design-qa/comparison-focused-context-final-20260808.jpg`, `artifacts/design-qa/comparison-focused-graph-final-20260808.jpg`, `artifacts/design-qa/comparison-focused-inspector-final-20260808.jpg` +- primary viewport: `1680x944` +- state: deny session selected, attack primary path visible, decision node selected, timeline at LIVE +- browser-rendered evidence: Chrome local preview connected to the real dashboard endpoints +- primary interactions tested: node and edge selection, all timeline ticks, event-result shortcuts, full-graph toggle, draggable nodes with connected-edge redraw, layout persistence after reload, allow/ask/deny session switching, replay controls, more-actions menu, and allowlist confirmation dialog +- console errors checked: 0 errors, 0 warnings + +## Full-view Comparison Evidence + +The source and current implementation were opened together in the full-view comparison artifact. The implementation preserves the source hierarchy of navigation, request context, causal graph, evidence inspector, and event timeline while applying the requested product changes: + +- A dynamic core-conclusion bar is now the first content layer. +- Request context is compressed and Prompt Injection evidence is promoted. +- Red is limited to confirmed attack content; amber marks suspicious behavior, blue/gray marks normal behavior, and green marks successful enforcement. +- The default graph is the attack primary path; the complete dependency graph is opt-in. +- Event summary and timeline are merged into one dynamic incident story. + +No actionable P0, P1, or P2 full-view mismatch remains. + +## Focused Region Comparison Evidence + +- Request context: the focused comparison confirms the four large source cards were intentionally compressed into user request, model input, compact tool tags, and a stronger detection card. Long content wraps and the panel scrolls at short desktop heights. +- Causal graph: the focused comparison confirms semantic node types, labeled relations, primary-path emphasis, and a green enforcement outcome. The calmer palette preserves red for the Prompt Injection branch. +- Evidence inspector: the focused comparison confirms the new layered hierarchy of current selection, what happened, local facts, and collapsed technical details. Normal nodes and relations do not inherit irrelevant attack-wide fields. + +## Required Fidelity Surfaces + +- Fonts and typography: Segoe UI / Microsoft YaHei UI and Cascadia Code fallbacks keep Chinese labels, metadata, policy codes, and graph labels readable. Letter spacing is zero and primary labels do not clip. +- Spacing and layout rhythm: the 1680 view uses compact three-column investigation tracks plus a 220px result rail. At 1366, context and inspector remain independently scrollable. At 390, the conclusion and graph lead the vertical flow with no page-level horizontal overflow. +- Colors and visual tokens: blue/gray represent normal behavior, amber represents suspicious behavior, red represents confirmed attack content, and green represents effective safety control. The page no longer reads as a single red alarm state. +- Image and icon fidelity: the supplied shield asset and vendored Lucide icon set are used. No placeholder imagery, emoji, handcrafted SVG, or CSS illustration was introduced. +- Copy and content: visible incident IDs, timestamps, tools, targets, decisions, evidence, policies, and timeline events come from `/api/security/overview`, `/api/records`, and `/api/settings/enforcement`. Visible branding is `玄鉴`. +- Interaction and accessibility: graph nodes and edge hit areas are keyboard-focusable, selected items have visible emphasis, nodes are draggable, timeline ticks are buttons, summary results are buttons, dialogs are labeled, and reduced-motion behavior is supported. Compact metadata now uses contrast ratios above 4.5:1, and visible timeline/alert labels are included in their accessible names. + +## Comparison History + +1. Replaced the dashboard-first composition with the supplied incident-detail structure. +2. Added real backend projection for session, graph, inspector, request context, and timeline data. +3. Added selection-specific node and edge evidence instead of a static attack-wide JSON panel. +4. Added timeline-to-graph synchronization, result-summary shortcuts, draggable nodes, and persisted per-view layouts. +5. Added the dynamic core conclusion, compressed context, detection card, primary-path mode, layered inspector, and merged result rail. +6. Corrected the preview tool call from `graph_builder` to `send_email` and mapped that timeline step to `action-email`. +7. Corrected review-event detection time to prefer the matching audit record's full `created_at` value. +8. Removed narrow-screen graph-confidence clipping by hiding secondary graph metadata below 1480px. +9. Corrected the more-actions menu direction so the allowlist entry is visible and its confirmation dialog is reachable. +10. Raised compact metadata contrast and aligned brand, alert, and timeline accessible names with their visible labels. ## Findings -- P1: A source-to-implementation visual comparison is not available. The HTML module parses, the live endpoint serves the new markup, and the 1366x768 live pass found no overflow or console errors, but a retained current screenshot is still required for formal comparison at both target viewports. - -## Patches Made Since Previous QA - -- Preserved the existing security-screen palette, panel layout, and Three.js topology. -- Replaced the causal view's serpentine coordinates with a topological rank layout. -- Added connected display-only collapse nodes for long paths. -- Added fit, zoom, pan, keyboard tabs, node/edge inspection, directed focus, stable alert selection, dynamic evidence legend, and partial-graph counters. -- Added attack, authorized, and enforcement-bypass trace presentations. -- Added a six-step causal story in both the graph header and evidence bar: authorization actor, authorization result, data field, tool chain, destination, and verdict. -- Made the first real graph open automatically while preserving a user's manual topology/causal view choice across polling refreshes. -- Verified the rebuilt `8766` endpoint serves the six-step markup and currently exposes 4 real causal graphs across 66 overview alerts. -- Bound authorization evidence to the final Sink action so an earlier authorized read cannot be presented as authorization for a later unapproved external send. -- Separated graph-path verdict from the final merged enforcement decision; the sixth step now follows the actual alert action. -- Added two frontend regression tests for those security invariants. -- Consolidated the three crowded left-column cards and the attack-chain card into one four-tab `态势侧舱`; hidden panes continue receiving live render updates. -- Converted the attack chain to a vertical side-panel sequence and expanded the bottom row from three cramped columns to two wider evidence panels. -- Replaced the generic competition-ready banner with `玄鉴已启动 · 全域戒备` and live protection-layer wording. -- Added keyboard tab navigation (`ArrowLeft`, `ArrowRight`, `Home`, `End`) and stable per-tab live summaries. - -## Implementation Checklist - -- Capture both target viewports from the current `8766` build. -- Check node and edge-label collisions with 6, 12, and 16 projected nodes. -- Verify node selection, edge selection, inspector close, wheel zoom, drag pan, reset, Tab, and arrow-key view switching. -- Compare the current screenshots against the two source screenshots in one combined visual input. -- Reconfirm all six story cells at 1920x1080 and retain both current screenshots for comparison. -- Capture the new four-tab side console at 1920x1080 and 1366x768; verify every tab, focus state, vertical attack-chain layout, and two-column bottom row without overlap. +No actionable P0, P1, or P2 findings remain. + +## Follow-up Polish + +- P3: at 1366x768, request context and Inspector use internal scrolling to protect the graph and result rail; a future density preference could trade footer height for more vertical investigation space. +- P3: the default primary-path graph uses a deliberately quiet background compared with the source so relation labels remain legible during replay. + +final result: passed + +## Operations Workspace QA (2026-08-08) + +- implementation URL: `http://127.0.0.1:8767` +- routes verified: `/overview`, `/agents`, `/policies`, `/tools`, `/alerts`, `/audit`, `/settings` +- desktop evidence: `artifacts/design-qa/workspace-*-1680.png` +- mobile evidence: `artifacts/design-qa/workspace-*-390.png` +- data contracts verified: security overview, records, stats, policy config, tool manifests, alerts, health, enforcement, and checkpoints +- browser state: every route reached `LIVE` and rendered backend fixture data without a synchronization error +- interaction checks: tool detail and revoke-confirmation dialog, tool filtering, policy dirty state, alert detail, audit filtering and detail, agent detail, mode inventory, and security-stack inventory +- navigation check: overview and agent session links select the requested attack-monitor session through `?session=...` +- branding check: dynamic backend labels display `玄鉴`; internal API field names remain unchanged +- runtime errors observed during the interaction pass: 0 + +The workspace reuses the attack-monitor shell, density, color semantics, navigation, and responsive behavior. The QA pass corrected native button backgrounds in alert and identity-risk rows, removed danger color from zero-value attack metrics, and constrained long section metadata on mobile. + +No actionable P0, P1, or P2 workspace finding remains. + +## Theme Toggle QA (2026-08-08) + +- source visual truth path: existing semantic action graph and operations workspace screens +- implementation screenshots: `artifacts/design-qa/theme-overview-midnight-1680.png`, `artifacts/design-qa/theme-overview-graphite-1680.png`, `artifacts/design-qa/theme-root-graphite-1680.png`, `artifacts/design-qa/theme-overview-graphite-390.png` +- states: default midnight, graphite after click, graphite after reload, graphite on attack monitor, graphite on 390px mobile viewport +- primary interactions tested: theme button click, icon and accessible label update, localStorage persistence, cross-route persistence, and mobile viewport rendering +- console errors checked: 0 runtime errors during the theme interaction pass +- visual result: the graphite palette changes shell, panel, graph, and control surfaces while retaining red/amber/green risk semantics +- responsive result: no horizontal overflow on the verified 1680px and 390px states + +No actionable P0, P1, or P2 theme finding remains. + +## Production Console Restraint Pass (2026-08-09) + +- routes verified: `/overview`, `/`, `/tools` +- viewports verified: 1680x944 desktop and 390x844 mobile +- themes verified: 夜航 and 石墨, including persistence across routes +- data source: the live local dashboard contracts on port 8767; no backend response shape changed +- interaction checks: attack edge selection, normal-node selection, selection-specific Inspector content, theme switching, and internal table scrolling +- runtime checks: API state reached `实时`; no browser console errors or warnings were observed + +The overview now leads with current safety status, primary risk, and required attention instead of eight equal KPI cards. Secondary metrics use typography and spacing rather than individual frames. Tool trust and boolean properties render as plain attributes, while revocation and signature integrity remain explicit status badges. + +The causal graph now uses neutral gray for ordinary behavior, amber for sensitive or suspicious data, red for confirmed attack propagation, and green for effective enforcement. Visible relation labels use concise Chinese product language while raw relation kinds remain available in technical details. On mobile, the page has no document-level horizontal overflow; wide tool tables scroll only inside their table container. + +No actionable P0, P1, or P2 finding remains after this pass. + +## Navigation Brand Mark QA (2026-08-09) + +- source visual truth path: `E:\soft\qq记录\Tencent Files\2726640566\nt_qq\nt_data\Pic\2026-08\Ori\c0d3a891f491c1a42bf05aa6e57da686.png` +- implementation asset path: `openclaw-plugin/public/brand-mark.png` +- implementation screenshot path: unavailable because the configured in-app browser connection is not exposed in this session +- intended viewport and state: 1680x944 desktop, shared navigation header on `/` and `/overview` +- focused asset evidence: the supplied 1024x1024 lockup and the transparent 340x356 shield crop were opened at original resolution; the crop preserves the complete shield silhouette and blue gradient without duplicating the `玄鉴` wordmark +- runtime evidence: both routes return the new asset reference; `/brand-mark.png?v=20260809-1` returns `200`, `Content-Type: image/png`, and 115999 bytes +- responsive code evidence: the shared desktop slot is 42x44px and the existing mobile override constrains the mark to the compact header +- checks: build, typecheck, lint, focused unit tests, source/build asset hash, and `git diff --check` passed + +### Findings + +No P0/P1/P2 issue was found in the source asset, transparent crop, route integration, MIME response, or responsive sizing rules. A browser-rendered comparison of the final navigation header could not be captured in this session. + +### Comparison History + +1. Replaced the legacy navigation shield reference with the supplied raster brand mark. +2. Removed the legacy hue rotation so the supplied blue gradient renders unchanged. +3. Verified the same shared brand lockup on the attack monitor and operations workspace shells. final result: blocked diff --git a/openclaw-plugin/package.json b/openclaw-plugin/package.json index ff6e282..9bfb775 100644 --- a/openclaw-plugin/package.json +++ b/openclaw-plugin/package.json @@ -6,6 +6,7 @@ "type": "module", "scripts": { "build": "node ./scripts/build.mjs", + "preview:dashboard": "npm run build && node ./scripts/dashboard-ui-preview.mjs", "typecheck": "tsc --noEmit", "lint": "eslint --max-warnings=0 config.ts index.ts core server tests", "test": "vitest run && npm run test:policy && npm run test:security && npm run test:property", diff --git a/openclaw-plugin/public/README.md b/openclaw-plugin/public/README.md index b48d3a8..d24b3eb 100644 --- a/openclaw-plugin/public/README.md +++ b/openclaw-plugin/public/README.md @@ -4,8 +4,8 @@ 页面入口: -- `index.html`:运行记录与风险观测。 +- `index.html`:实时语义动作图、会话故事、裁决 Inspector 与执行回放。 - `command-lab.html`:业务测试台和公开 benchmark 单条复测。 -- `security-screen.html`:安全态势感知大屏。 +- `security-screen.html`:全局安全态势与 3D 拓扑补充视图。 所有样式和脚本都本地加载,不依赖外部 CDN。 diff --git a/openclaw-plugin/public/app.js b/openclaw-plugin/public/app.js index 9715fe3..ea4f32c 100644 --- a/openclaw-plugin/public/app.js +++ b/openclaw-plugin/public/app.js @@ -1,1108 +1,1115 @@ -const ALL_SESSIONS = "__all__"; +import { + buildDashboardModel, + buildIncidentConclusion, + buildSelectionEvidence, + decisionCode, + primaryPathGraph, +} from "/graph-adapter.js?v=20260809-4"; +import { SemanticGraph } from "/semantic-graph.js?v=20260809-3"; +import { normalizeVerdict, verdictMeta } from "/verdict.js?v=20260809-1"; + +const $ = (id) => document.getElementById(id); const state = { + model: { sessions: [], source: {} }, + overview: {}, records: [], - detailCache: new Map(), - stats: null, - selectedId: "", - selectedRecord: null, - selectedSession: "", - focusMode: "important", - autoRefresh: true, - timelinePage: 1, - timelinePageSize: 30, - timer: null, enforcement: null, + selectedSessionId: "", + selectedItem: null, + pathFocus: true, + playhead: 0, + live: true, + playing: false, + loading: false, + dataSignature: "", + refreshTimer: null, + playbackTimer: null, + search: "", }; -const layerNames = { - "Context Provenance": "上下文溯源域", - "State Integrity": "状态完整性域", - "Intent Authorization": "意图授权域", - "Tool Boundary": "工具边界域", - "Evidence Feedback": "证据回流环", - Runtime: "运行时", -}; - -const typeNames = { - finding_group: "发现聚合", - lab_command: "实验指令", - session_start: "会话开始", - provenance_scan: "溯源扫描", - llm_input: "模型输入", - message_write: "消息写入", - tool_decision: "工具裁决", - tool_result: "工具结果", - guard_finding: "防护发现", - alert: "告警", - approval_request: "审批请求", - approval_resolution: "审批结果", - approval_cache_hit: "审批缓存", - response_cover: "响应覆盖", - runtime: "运行时", -}; - -const severityNames = { - info: "信息", - success: "通过", - warning: "警告", - danger: "高危", -}; - -const decisionNames = { - allow: "允许", - ask: "待确认", - deny: "拒绝", - block: "阻断", -}; - -const enforcementModeText = { - observe: { - label: "观察", - title: "观察模式", - text: "只记录裁决、发现和告警,不改变 OpenClaw 原生工具执行结果。适合跑无防护基线、观察误报和做对比实验。", - }, - approval: { - label: "审批", - title: "审批模式", - text: "高风险 ask/deny 工具调用进入 OpenClaw 人工审批;选择 allow-always 后会按工具名和参数哈希缓存同一操作。", - }, - block: { - label: "阻断", - title: "阻断模式", - text: "确定为 deny 的高风险工具调用在执行前硬阻断。适合比赛演示、高风险工具调用和真实防护验证。", - }, -}; - -const roleNames = { - user: "用户", - assistant: "助手", - toolResult: "工具结果", - system: "系统", -}; - -const stopReasonNames = { - stop: "自然结束", - toolUse: "请求调用工具", - timeout: "超时", -}; - -const toolNames = { - agents_list: "列出 Agent", - read_webpage: "读取网页", - call_api: "调用 API", - read_file: "读取文件", - write_file: "写入文件", - send_email: "发送邮件", - shell_exec: "执行命令", - memory_read: "读取记忆", - memory_write: "写入记忆", -}; +const semanticGraph = new SemanticGraph({ + viewport: $("graphViewport"), + world: $("graphWorld"), + svg: $("graphEdges"), + nodes: $("graphNodes"), + empty: $("graphEmpty"), + onSelect: handleGraphSelection, +}); -const findingTypeNames = { - deterministic: "确定性规则", - heuristic: "启发式规则", - learned: "旧版哨兵判断", - behavioral: "统计行为基线", - semantic: "语义复核", - unknown: "未知类型", -}; +initialize(); -const textTranslations = [ - [/workspace file appears to contain embedded secrets/gi, "工作区文件疑似包含内嵌密钥"], - [/configuration appears to contain embedded secrets/gi, "配置文件疑似包含内嵌密钥"], - [/configuration contains hardcoded secret values/gi, "配置文件包含硬编码敏感值"], - [/workspace contains sensitive asset file/gi, "工作区包含敏感资产文件"], - [/AgentSentry dashboard started/gi, "玄鉴面板已启动"], - [/OpenClaw prompt build/gi, "OpenClaw 提示词构建"], - [/LLM input prepared/gi, "模型输入已准备"], - [/system prompt preview disabled/gi, "系统提示词预览已关闭"], - [/Message write: user/gi, "写入用户消息"], - [/Message write: assistant/gi, "写入助手消息"], - [/Message write: toolResult/gi, "写入工具结果"], - [/Tool call completed/gi, "工具调用完成"], - [/tool result returned/gi, "工具已返回结果"], - [/High-risk tool call: ([\w.-]+)/gi, (_, tool) => `高风险工具调用:${displayTool(tool)}`], - [/Tool call: ([\w.-]+)/gi, (_, tool) => `工具调用:${displayTool(tool)}`], - [/Business tool blocked: ([\w.-]+)/gi, (_, tool) => `业务工具已阻断:${displayTool(tool)}`], - [/Business tool decision: ([\w.-]+)/gi, (_, tool) => `业务工具裁决:${displayTool(tool)}`], - [/Business tool skipped: ([\w.-]+)/gi, (_, tool) => `业务工具未执行:${displayTool(tool)}`], - [/Lab policy deny: ([\w.-]+)/gi, (_, tool) => `实验策略拒绝:${displayTool(tool)}`], - [/Lab policy ask: ([\w.-]+)/gi, (_, tool) => `实验策略需确认:${displayTool(tool)}`], - [/tool ([\w.-]+) is outside TaskSpec/gi, (_, tool) => `工具 ${displayTool(tool)} 超出当前任务规范`], - [/tool ([\w.-]+) lacks explicit capability authorization/gi, (_, tool) => `工具 ${displayTool(tool)} 缺少显式能力授权`], - [/dynamic intent tracking detected drift from read-only task to high-risk action/gi, "动态意图追踪发现只读任务漂移到高风险动作"], - [/task intent does not allow email/gi, "当前任务未授权邮件外发"], - [/tool arguments match deterministic trust-risk policy/gi, "工具参数命中确定性信任风险策略"], - [/content contains prompt-injection or exfiltration indicators/gi, "内容包含提示注入或数据外传信号"], - [/content attempts to persist privileged instructions into memory or startup flow/gi, "内容试图把特权指令持久化到记忆或启动流程"], - [/high-risk action deviates from task intent/gi, "高风险动作偏离当前任务意图"], - [/read path references sensitive asset/gi, "读取路径指向敏感资产"], - [/TaskSpec/gi, "任务规范"], - [/Workspace provenance scan completed/gi, "工作区溯源扫描完成"], - [/Workspace provenance scan blocked workspace/gi, "工作区溯源扫描阻断"], - [/OpenClaw produced a tool trace for this Promptbeat case, but the provider timed out before the final assistant reply\./gi, "OpenClaw 已为该 PromptBeat 用例产生工具轨迹,但模型服务在最终回复前超时。"], - [/Observed tool calls: ([^.]+)\./gi, (_, tools) => `观察到的工具调用:${tools.split(",").map((tool) => displayTool(tool.trim())).join("、")}。`], - [/Check the AgentSentry dashboard for tool_decision, alert, and tool_result records\./gi, "请在玄鉴面板查看工具裁决、告警和工具结果记录。"], - [/Request timed out before a response was generated\. Please try again, or increase `agents\.defaults\.timeoutSeconds` in your config\./gi, "生成回复前请求超时。可以重试,或调高 OpenClaw 配置中的 agents.defaults.timeoutSeconds。"], - [/(\d+) findings; (\d+) files scanned; cached/gi, (_, findings, files) => `${findings} 个发现;已扫描 ${files} 个文件;使用缓存`], - [/(\d+) findings; (\d+) files scanned/gi, (_, findings, files) => `${findings} 个发现;已扫描 ${files} 个文件`], - [/Do not exfiltrate data/gi, "不要外传数据"], - [/Only answer the user/gi, "只回答用户"], -]; +async function initialize() { + bindInteractions(); + renderIcons(); + await refreshData({ keepSelection: false }); + state.refreshTimer = window.setInterval(() => { + if (state.live && !state.loading) void refreshData({ keepSelection: true, quiet: true }); + }, 5000); +} -const $ = (id) => document.getElementById(id); +async function refreshData({ keepSelection = true, quiet = false } = {}) { + if (state.loading) return; + state.loading = true; + if (!quiet) setConnection("connecting", "同步中"); + $("refreshBtn").classList.add("spinning"); -async function refresh({ keepSelection = true } = {}) { - setStatus("刷新中", "loading"); try { - const limit = $("limitSelect").value; - const [recordsResponse, statsResponse] = await Promise.all([ - fetch(`/api/records?compact=1&limit=${encodeURIComponent(limit)}`).then((res) => res.json()), - fetch(`/api/stats?limit=${encodeURIComponent(Math.min(Number(limit) || 500, 1000))}`).then((res) => res.json()), + const [overviewResult, recordsResult, enforcementResult] = await Promise.allSettled([ + fetchJson("/api/security/overview?limit=500"), + fetchJson("/api/records?limit=500"), + fetchJson("/api/settings/enforcement"), ]); + if (overviewResult.status === "rejected" && recordsResult.status === "rejected") { + throw overviewResult.reason || recordsResult.reason; + } - state.records = recordsResponse.records || []; - state.stats = { - ...(statsResponse || {}), - totalRecords: recordsResponse.totalRecords ?? statsResponse?.totalRecords ?? statsResponse?.total, - windowRecords: recordsResponse.windowRecords ?? state.records.length, - windowLimit: recordsResponse.windowLimit ?? Number(limit), - }; - normalizeSelection(keepSelection); - setStatus("已连接", "ok"); - render(); - } catch (error) { - setStatus("连接失败", "bad"); - $("timeline").innerHTML = `
${escapeHtml(verdict.code)}${escapeHtml(conclusion.result)}`;
+}
+
+function incidentVerdictTitle(session, conclusion) {
+ if (!session) return "等待行为进入裁决链路";
+ if (session.decision === "deny") return conclusion.target ? "攻击已在外发边界阻断" : "危险动作已在执行边界阻断";
+ if (session.decision === "ask") return "高风险动作已暂停并等待确认";
+ return "行为符合当前任务边界";
+}
+
+function selectionVerdict(evidence, session) {
+ const relation = String(evidence.kind || "").toLowerCase();
+ const stateValue = String(evidence.state || "").toLowerCase();
+ if (evidence.type === "edge" && ["blocked_by", "reviewed_by", "approved_by", "decides"].includes(relation)) return session.decision;
+ if (evidence.kind === "decision") return normalizeVerdict(stateValue);
+ if (evidence.kind === "guard") return session.decision;
+ if (["taint", "secret", "sink"].includes(evidence.kind) && session.decision !== "allow") return session.decision;
+ if (evidence.kind === "capability" && /unscoped|deny|block|未授权/i.test(`${evidence.state} ${evidence.title}`)) return session.decision === "deny" ? "deny" : "ask";
+ if (evidence.kind === "action" && /blocked|deny|reject|unscoped/i.test(stateValue)) return "deny";
+ return "";
+}
+
+function renderVerdictInline(value, rule) {
+ const meta = verdictMeta(value);
+ return ``;
}
function renderSessions() {
- const sessions = buildSessions();
- $("sessionCount").textContent = `${sessions.length}`;
-
- const allActive = state.selectedSession === ALL_SESSIONS ? "active" : "";
- const allItem = `
-
- `;
-
- const items = sessions
- .map((session) => {
- const active = session.key === state.selectedSession ? "active" : "";
- return `
-
- `;
- })
- .join("");
-
- $("sessionList").innerHTML = allItem + (items || '当前视图
-${escapeHtml(latest ? latest.session_key : "等待 OpenClaw 运行后产生记录。")}
-${escapeHtml(context.input || "未记录").replace(/\n/g, "
")}
${escapeHtml(context.originalInput || context.input || "未记录").replace(/\n/g, "
")}
${escapeHtml(tool)}`).join("") : `未记录`}${escapeHtml(context.adversarial || (detectionActive ? "检测到高风险行为,原始证据片段未单独记录。" : "当前请求未发现独立的攻击输入片段。")).replace(/\n/g, "+ ${detectionActive ? `
")}
${escapeHtml(code)}`).join("") : `等待更多证据`}${escapeHtml(evidence.description)}
+ ${localVerdict ? renderVerdictInline(localVerdict, evidence.policies?.find(isPolicyCode) || "") : ""}${escapeHtml(JSON.stringify({ selection: selection.value, records: evidence.records }, null, 2))}
+ ${escapeHtml(item.value).replace(/\n/g, "
")}
${escapeHtml(policy)}`).join("")}该图元素没有独立记录;其关系来自后端因果图投影。
+${escapeHtml(summaryText(fullRecord))}
-${escapeHtml(JSON.stringify(fullRecord, null, 2))}
- 另有 ${escapeHtml(payload.omitted_paths)} 个路径未展开。
` : ""; - return ` -${escapeHtml(translateText(payload.reason || record.summary || ""))} · ${escapeHtml(confidenceLabel(payload.confidence))}
-XuanJian for OpenClaw
+玄鉴 · 攻击复现与策略实验
真实测试请求 · 玄鉴插件数据流实时观测