From 9de8469e8f6254522eecf044d2d36a0e1af9ec2b Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 19 May 2026 18:04:07 +0800 Subject: [PATCH] feat(web): add IwoooS coverage matrix --- apps/web/messages/en.json | 46 +++++++ apps/web/messages/zh-TW.json | 46 +++++++ apps/web/src/app/[locale]/iwooos/page.tsx | 129 ++++++++++++++++++ docs/LOGBOOK.md | 15 ++ .../iwooos_posture_projection_v1.schema.json | 116 ++++++++++++++++ docs/security/IWOOOS-POSTURE-PROJECTION.md | 20 +++ .../security/SECURITY-MIRROR-STATUS-ROLLUP.md | 3 +- .../SECURITY-SUPPLY-CHAIN-PROGRESS.md | 4 +- .../iwooos-posture-projection.snapshot.json | 118 +++++++++++++++- ...ecurity-mirror-status-rollup.snapshot.json | 34 ++++- .../security-mirror-progress-guard.py | 86 ++++++++++++ 11 files changed, 612 insertions(+), 5 deletions(-) diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 90932d31..b3b4d270 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -1135,6 +1135,52 @@ } } }, + "coverage": { + "title": "Coverage and Boundary Matrix", + "subtitle": "Groups the 10 existing security surfaces into four responsibility planes so IwoooS can show where to read signals, human control, governance audit, and engineering review.", + "groups": { + "signals": { + "title": "Signals and Exposure", + "body": "Collects security, compliance, alert, error, and UX audit signals; observations stay visible without becoming blockers." + }, + "humanControl": { + "title": "Human Control Boundary", + "body": "Keeps HITL, multi-sig, and AwoooP approvals visible; runtime gates still require human decisions." + }, + "governanceAudit": { + "title": "Governance and Audit", + "body": "Governance events, SLOs, remediation queues, and operation logs are evidence surfaces, not execution authorization." + }, + "engineeringReview": { + "title": "Engineering Review", + "body": "Code Review remains a non-blocking review pipeline for risk grading and coding follow-up, not deploy approval." + } + }, + "conflicts": { + "title": "Overlap and Conflict Controls", + "subtitle": "The same security signal can appear on multiple pages. IwoooS only organizes entrypoints and does not change ownership or authority.", + "preserveOwnership": { + "title": "Preserve Route Ownership", + "body": "Each route remains owned by its original page and API contract; IwoooS does not move write authority." + }, + "noRuntimeLift": { + "title": "No Runtime Lift", + "body": "The coverage matrix can show coverage and gaps, but cannot create scan, execute, repair, or blocking gates." + }, + "codeReviewNotDeployGate": { + "title": "Code Review Is Not Deploy Approval", + "body": "AI Code Review can grade risk and propose coding follow-up, but cannot become deploy approval by itself." + }, + "awooopNotSecurityApproval": { + "title": "AwoooP Approval Is Not Security Approval", + "body": "The AwoooP approval queue can show human gate state, but security gates still require decision records and follow-up runtime gates." + }, + "kaliNotCalled": { + "title": "Frontend Index Does Not Call Kali", + "body": "Kali 112 remains observe-only; active scan or /execute must go through human approval and follow-up gates." + } + } + }, "nextGate": { "title": "Next High-level Gate", "body": "S4.9 Gitea owner attestation response is the recommended next owner evidence. Headline progress should only increase after owner responses, redacted payload ingestion, active runtime gates, or GitHub primary readiness actually change." diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 1404e62e..a5723aa5 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -1136,6 +1136,52 @@ } } }, + "coverage": { + "title": "覆蓋與邊界矩陣", + "subtitle": "把 10 個既有資安頁面分成四個責任面,讓 IwoooS 能說清楚哪裡看訊號、哪裡做人工控制、哪裡看治理稽核、哪裡看工程審查。", + "groups": { + "signals": { + "title": "訊號與暴露面", + "body": "集中安全、合規、告警、錯誤與 UX 稽核訊號;只顯示風險,不把觀察結果直接升成阻擋。" + }, + "humanControl": { + "title": "人工控制邊界", + "body": "保留 HITL、multi-sig 與 AwoooP approvals 的人控位置;沒有人工決策就不啟動 runtime gate。" + }, + "governanceAudit": { + "title": "治理與稽核", + "body": "治理事件、SLO、補救佇列與操作日誌用來看流程證據,不把 audit event 當執行授權。" + }, + "engineeringReview": { + "title": "工程審查", + "body": "Code Review 維持 non-blocking review pipeline,用於風險分級與後續修復建議,不直接等同 deploy approval。" + } + }, + "conflicts": { + "title": "重疊與衝突控制", + "subtitle": "同一個資安訊號可能在多個頁面出現,IwoooS 只做入口整理,不改變原始頁面的責任與權限。", + "preserveOwnership": { + "title": "保留原頁 owner", + "body": "每個 route 繼續由原本頁面與 API contract 負責,IwoooS 不搬移資料寫入權。" + }, + "noRuntimeLift": { + "title": "不把只讀索引升成 runtime", + "body": "coverage matrix 只能顯示覆蓋與缺口,不建立 scan、execute、repair 或 blocking gate。" + }, + "codeReviewNotDeployGate": { + "title": "Code Review 不等於部署批准", + "body": "AI Code Review 可以提供風險分級與 coding follow-up,但不能直接變成 deploy approval。" + }, + "awooopNotSecurityApproval": { + "title": "AwoooP approvals 不等於資安批准", + "body": "AwoooP 審批佇列可顯示人控狀態,但資安 gate 仍需對應決策紀錄與 follow-up runtime gate。" + }, + "kaliNotCalled": { + "title": "前端索引不呼叫 Kali", + "body": "Kali 112 維持 observe-only;任何 active scan 或 /execute 都必須走人工批准與後續 gate。" + } + } + }, "nextGate": { "title": "下一個高層 Gate", "body": "S4.9 Gitea owner attestation response 是目前建議先收的 owner evidence。任何 headline 提升都要等 owner response、redacted payload ingestion、active runtime gate 或 GitHub primary readiness 有真實變化。" diff --git a/apps/web/src/app/[locale]/iwooos/page.tsx b/apps/web/src/app/[locale]/iwooos/page.tsx index e135ecf8..32dfa0af 100644 --- a/apps/web/src/app/[locale]/iwooos/page.tsx +++ b/apps/web/src/app/[locale]/iwooos/page.tsx @@ -48,6 +48,19 @@ type SecuritySurface = { tone: 'steady' | 'warn' | 'locked' } +type CoverageGroup = { + key: string + count: string + icon: typeof ShieldCheck + tone: 'steady' | 'warn' | 'locked' + surfaces: string +} + +type ConflictControl = { + key: string + tone: 'steady' | 'warn' | 'locked' +} + const postureMetrics: PostureMetric[] = [ { key: 'overall', value: '58%', tone: 'warn' }, { key: 'framework', value: '80-85%', tone: 'steady' }, @@ -86,6 +99,45 @@ const existingSecuritySurfaces: SecuritySurface[] = [ { key: 'codeReview', href: '/code-review', icon: SearchCheck, tone: 'warn' }, ] +const coverageGroups: CoverageGroup[] = [ + { + key: 'signals', + count: '5', + icon: Radar, + tone: 'warn', + surfaces: '/security-compliance, /security, /compliance, /alerts, /errors', + }, + { + key: 'humanControl', + count: '2', + icon: Lock, + tone: 'locked', + surfaces: '/authorizations, /awooop/approvals', + }, + { + key: 'governanceAudit', + count: '2', + icon: ListChecks, + tone: 'steady', + surfaces: '/governance, /alert-operation-logs', + }, + { + key: 'engineeringReview', + count: '1', + icon: SearchCheck, + tone: 'warn', + surfaces: '/code-review', + }, +] + +const conflictControls: ConflictControl[] = [ + { key: 'preserveOwnership', tone: 'steady' }, + { key: 'noRuntimeLift', tone: 'locked' }, + { key: 'codeReviewNotDeployGate', tone: 'warn' }, + { key: 'awooopNotSecurityApproval', tone: 'locked' }, + { key: 'kaliNotCalled', tone: 'locked' }, +] + const evidenceItems = [ 'iwooos-posture-projection.snapshot.json', 'security-rollout-policy.snapshot.json', @@ -239,6 +291,48 @@ function SurfaceCard({ item, locale }: { item: SecuritySurface; locale: string } ) } +function CoverageCard({ item }: { item: CoverageGroup }) { + const t = useTranslations('iwooos.coverage') + const Icon = item.icon + return ( +
+
+ + {item.count} +
+

{t(`groups.${item.key}.title` as never)}

+

+ {t(`groups.${item.key}.body` as never)} +

+
{item.surfaces}
+
+ ) +} + +function ConflictRow({ item, index }: { item: ConflictControl; index: number }) { + const t = useTranslations('iwooos.coverage.conflicts') + return ( +
+ +
+
{t(`${item.key}.title` as never)}
+
+ {t(`${item.key}.body` as never)} +
+
+
+ ) +} + export default function IwoooSPage({ params }: { params: { locale: string } }) { const t = useTranslations('iwooos') @@ -273,6 +367,41 @@ export default function IwoooSPage({ params }: { params: { locale: string } }) { +
+
+
+

{t('coverage.title')}

+

+ {t('coverage.subtitle')} +

+
+
+ {coverageGroups.map(item => )} +
+
+ +
+

{t('coverage.conflicts.title')}

+

+ {t('coverage.conflicts.subtitle')} +

+ {conflictControls.map((item, index) => )} +
+
+
None: "s2_8_iwooos_frontend_posture_entry", "s2_9_iwooos_posture_projection_contract", "s2_10_iwooos_existing_frontend_surface_integration", + "s2_11_iwooos_surface_coverage_boundary_matrix", ] assert_equal( "progress_delta_ledger.delta_ids", @@ -300,6 +301,29 @@ def validate(root: Path) -> None: iwooos_projection["summary"]["existing_frontend_surface_count"], len(expected_iwooos_surface_ids), ) + expected_iwooos_coverage_group_ids = [ + "signals_and_exposure", + "human_control_boundary", + "governance_and_audit", + "engineering_review", + ] + expected_iwooos_conflict_control_ids = [ + "preserve_original_route_ownership", + "no_runtime_lift_from_index", + "code_review_not_deploy_gate", + "awooop_approval_not_security_approval", + "frontend_index_does_not_call_kali", + ] + assert_equal( + "iwooos_projection.summary.frontend_surface_coverage_group_count", + iwooos_projection["summary"]["frontend_surface_coverage_group_count"], + len(expected_iwooos_coverage_group_ids), + ) + assert_equal( + "iwooos_projection.summary.frontend_surface_conflict_control_count", + iwooos_projection["summary"]["frontend_surface_conflict_control_count"], + len(expected_iwooos_conflict_control_ids), + ) iwooos_progress = iwooos_projection["progress"] assert_equal("iwooos_projection.progress.overall_percent", iwooos_progress["overall_percent"], progress["overall_percent"]) assert_equal( @@ -374,6 +398,66 @@ def validate(root: Path) -> None: f"iwooos_projection.existing_frontend_surfaces.{item['surface_id']}.not_authorization", item["not_authorization"], ) + iwooos_coverage_groups = iwooos_projection["frontend_surface_coverage_groups"] + assert_equal( + "iwooos_projection.frontend_surface_coverage_groups.ids", + [item["group_id"] for item in iwooos_coverage_groups], + expected_iwooos_coverage_group_ids, + ) + assert_equal( + "iwooos_projection.frontend_surface_coverage_groups.display_order", + [item["display_order"] for item in iwooos_coverage_groups], + list(range(1, len(expected_iwooos_coverage_group_ids) + 1)), + ) + covered_surface_ids = sorted({surface_id for item in iwooos_coverage_groups for surface_id in item["surface_ids"]}) + assert_equal("iwooos_projection.frontend_surface_coverage_groups.coverage", covered_surface_ids, sorted(expected_iwooos_surface_ids)) + for item in iwooos_coverage_groups: + assert_equal( + f"iwooos_projection.frontend_surface_coverage_groups.{item['group_id']}.display_mode", + item["display_mode"], + "coverage_only", + ) + assert_false( + f"iwooos_projection.frontend_surface_coverage_groups.{item['group_id']}.runtime_execution_authorized", + item["runtime_execution_authorized"], + ) + assert_false( + f"iwooos_projection.frontend_surface_coverage_groups.{item['group_id']}.action_buttons_allowed", + item["action_buttons_allowed"], + ) + assert_true( + f"iwooos_projection.frontend_surface_coverage_groups.{item['group_id']}.not_authorization", + item["not_authorization"], + ) + iwooos_conflict_controls = iwooos_projection["frontend_surface_conflict_controls"] + assert_equal( + "iwooos_projection.frontend_surface_conflict_controls.ids", + [item["control_id"] for item in iwooos_conflict_controls], + expected_iwooos_conflict_control_ids, + ) + assert_equal( + "iwooos_projection.frontend_surface_conflict_controls.display_order", + [item["display_order"] for item in iwooos_conflict_controls], + list(range(1, len(expected_iwooos_conflict_control_ids) + 1)), + ) + for item in iwooos_conflict_controls: + assert_equal( + f"iwooos_projection.frontend_surface_conflict_controls.{item['control_id']}.display_mode", + item["display_mode"], + "conflict_control_only", + ) + assert_false( + f"iwooos_projection.frontend_surface_conflict_controls.{item['control_id']}.runtime_execution_authorized", + item["runtime_execution_authorized"], + ) + assert_false( + f"iwooos_projection.frontend_surface_conflict_controls.{item['control_id']}.action_buttons_allowed", + item["action_buttons_allowed"], + ) + assert_true( + f"iwooos_projection.frontend_surface_conflict_controls.{item['control_id']}.not_authorization", + item["not_authorization"], + ) assert_equal( "iwooos_projection.non_blocking_lane_ids", iwooos_projection["non_blocking_lane_ids"], @@ -392,6 +476,8 @@ def validate(root: Path) -> None: "display_progress_estimate", "display_non_blocking_lanes", "display_existing_frontend_security_surfaces", + "display_frontend_surface_coverage_matrix", + "display_frontend_surface_conflict_controls", "display_evidence_refs", "display_forbidden_actions", ]: