diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 5541d5490..8b7532748 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -11031,6 +11031,68 @@ } } }, + "commandMap": { + "eyebrow": "資安工作地圖", + "title": "六條線看資安網", + "subtitle": "第一層只保留工作線、證據、下一步與鎖定邊界;主機、版本來源、AwoooP 與 runtime 都仍維持只讀。", + "tabsLabel": "IwoooS 資安工作線", + "boundaryTitle": "工作地圖邊界", + "panelLabels": { + "evidence": "證據", + "next": "下一步", + "locked": "鎖定" + }, + "items": { + "unlock": { + "title": "61% 解鎖線", + "state": "S4.9 owner evidence", + "detail": "目前真正能推動整體進度的第一條線仍是 S4.9;必須先有可追溯、已脫敏、可審查的負責人回覆。", + "evidence": "已建立首頁首層路徑與五步驟。", + "next": "收 S4.9 回覆、脫敏 evidence refs、預檢與 reviewer 接受。", + "locked": "未收到 / 未接受前不提高 61%。" + }, + "scope": { + "title": "全產品範圍", + "state": "八類只讀納管", + "detail": "AwoooI、AwoooP、IwoooS、公開網站、VibeWork、Kali、開發主機與版本來源都放進同一張資安範圍圖。", + "evidence": "全域矩陣與全產品快照已可見。", + "next": "補各產品 owner、資料分級與 evidence pointer。", + "locked": "不把範圍可見視為阻擋或執行授權。" + }, + "hosts": { + "title": "主機與工具", + "state": "112 / 111 / 168", + "detail": "Kali 192.168.0.112 與兩台開發主機已納入視野;監控、MCP、Ansible、KM 與版本來源也在同一條證據鏈。", + "evidence": "主機與工具證據鏈已前台化。", + "next": "補維護窗口、host owner decision、脫敏主機 evidence。", + "locked": "不 SSH 變更、不更新主機、不執行 Kali /execute。" + }, + "sourceControl": { + "title": "版本來源", + "state": "GitHub primary 待證據", + "detail": "Gitea 轉 GitHub 的長期方向保留,但 repo、refs、workflow 與 secret 名稱仍等待負責人證據。", + "evidence": "source-control readiness 與 S4.9 路徑已可見。", + "next": "先收 Gitea 清冊 owner response,再整理 GitHub target 與 refs truth。", + "locked": "不建立 repo、不同步 refs、不改 workflow / secret、不切 primary。" + }, + "awooop": { + "title": "AwoooP 真相鏈", + "state": "跨 Session 已接線", + "detail": "AwoooP、Alerts、Monitoring、Work Items、Runs、Approvals 與 Telegram truth-chain 已能連到同一筆 Incident 證據。", + "evidence": "正式環境已有 truth-chain drill-down 與 deploy marker。", + "next": "把資安 owner response 與 reviewer queue 維持只讀同步。", + "locked": "AwoooP approval 不自動等於資安 runtime 批准。" + }, + "boundary": { + "title": "執行邊界", + "state": "Gate 0", + "detail": "目前 IwoooS 是低摩擦框架與只讀證據網;掃描、修復、部署、主機更新與版本來源變更都要獨立人工閘門。", + "evidence": "guard、snapshot、LOGBOOK 與 boundary keys 都已鎖住。", + "next": "等 owner evidence 或人工決策後,再另開 follow-up runtime gate。", + "locked": "runtime_execution_authorized=false;active_runtime_gate_count=0。" + } + } + }, "firstUnlockEvidencePacket": { "title": "第一解鎖證據包", "subtitle": "S2.115 把 S4.9 要讓 61% 真正前進所需的證據收斂成五個欄位:負責人判定 metadata、範圍與來源參照、脫敏聲明、收件預檢軌跡、審查接受摘要。這裡只顯示要補什麼,不收 raw 載荷、不收機密明文、不開 重點 審查。", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 5541d5490..8b7532748 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -11031,6 +11031,68 @@ } } }, + "commandMap": { + "eyebrow": "資安工作地圖", + "title": "六條線看資安網", + "subtitle": "第一層只保留工作線、證據、下一步與鎖定邊界;主機、版本來源、AwoooP 與 runtime 都仍維持只讀。", + "tabsLabel": "IwoooS 資安工作線", + "boundaryTitle": "工作地圖邊界", + "panelLabels": { + "evidence": "證據", + "next": "下一步", + "locked": "鎖定" + }, + "items": { + "unlock": { + "title": "61% 解鎖線", + "state": "S4.9 owner evidence", + "detail": "目前真正能推動整體進度的第一條線仍是 S4.9;必須先有可追溯、已脫敏、可審查的負責人回覆。", + "evidence": "已建立首頁首層路徑與五步驟。", + "next": "收 S4.9 回覆、脫敏 evidence refs、預檢與 reviewer 接受。", + "locked": "未收到 / 未接受前不提高 61%。" + }, + "scope": { + "title": "全產品範圍", + "state": "八類只讀納管", + "detail": "AwoooI、AwoooP、IwoooS、公開網站、VibeWork、Kali、開發主機與版本來源都放進同一張資安範圍圖。", + "evidence": "全域矩陣與全產品快照已可見。", + "next": "補各產品 owner、資料分級與 evidence pointer。", + "locked": "不把範圍可見視為阻擋或執行授權。" + }, + "hosts": { + "title": "主機與工具", + "state": "112 / 111 / 168", + "detail": "Kali 192.168.0.112 與兩台開發主機已納入視野;監控、MCP、Ansible、KM 與版本來源也在同一條證據鏈。", + "evidence": "主機與工具證據鏈已前台化。", + "next": "補維護窗口、host owner decision、脫敏主機 evidence。", + "locked": "不 SSH 變更、不更新主機、不執行 Kali /execute。" + }, + "sourceControl": { + "title": "版本來源", + "state": "GitHub primary 待證據", + "detail": "Gitea 轉 GitHub 的長期方向保留,但 repo、refs、workflow 與 secret 名稱仍等待負責人證據。", + "evidence": "source-control readiness 與 S4.9 路徑已可見。", + "next": "先收 Gitea 清冊 owner response,再整理 GitHub target 與 refs truth。", + "locked": "不建立 repo、不同步 refs、不改 workflow / secret、不切 primary。" + }, + "awooop": { + "title": "AwoooP 真相鏈", + "state": "跨 Session 已接線", + "detail": "AwoooP、Alerts、Monitoring、Work Items、Runs、Approvals 與 Telegram truth-chain 已能連到同一筆 Incident 證據。", + "evidence": "正式環境已有 truth-chain drill-down 與 deploy marker。", + "next": "把資安 owner response 與 reviewer queue 維持只讀同步。", + "locked": "AwoooP approval 不自動等於資安 runtime 批准。" + }, + "boundary": { + "title": "執行邊界", + "state": "Gate 0", + "detail": "目前 IwoooS 是低摩擦框架與只讀證據網;掃描、修復、部署、主機更新與版本來源變更都要獨立人工閘門。", + "evidence": "guard、snapshot、LOGBOOK 與 boundary keys 都已鎖住。", + "next": "等 owner evidence 或人工決策後,再另開 follow-up runtime gate。", + "locked": "runtime_execution_authorized=false;active_runtime_gate_count=0。" + } + } + }, "firstUnlockEvidencePacket": { "title": "第一解鎖證據包", "subtitle": "S2.115 把 S4.9 要讓 61% 真正前進所需的證據收斂成五個欄位:負責人判定 metadata、範圍與來源參照、脫敏聲明、收件預檢軌跡、審查接受摘要。這裡只顯示要補什麼,不收 raw 載荷、不收機密明文、不開 重點 審查。", diff --git a/apps/web/src/app/[locale]/iwooos/page.tsx b/apps/web/src/app/[locale]/iwooos/page.tsx index e0afe7923..1d349fc1c 100644 --- a/apps/web/src/app/[locale]/iwooos/page.tsx +++ b/apps/web/src/app/[locale]/iwooos/page.tsx @@ -63,6 +63,7 @@ type VisualDashboardGate = { } type VisualExperienceMode = 'attackPath' | 'assetHeat' | 'responseFlow' +type IwoooSCommandMapMode = 'unlock' | 'scope' | 'hosts' | 'sourceControl' | 'awooop' | 'boundary' type VisualExperienceTab = { key: VisualExperienceMode @@ -165,6 +166,13 @@ type VibeWorkSecurityOnboardingItem = { tone: 'steady' | 'warn' | 'locked' } +type IwoooSCommandMapItem = { + key: IwoooSCommandMapMode + metric: string + icon: typeof ShieldCheck + tone: 'steady' | 'warn' | 'locked' +} + type AwoooPReadOnlyLandingReadinessItem = { key: string item: string @@ -2251,6 +2259,32 @@ const iwooosFirstProgressUnlockPathBoundaries = [ 'gitea_disablement_authorized=false', ] as const +const iwooosCommandMapItems: IwoooSCommandMapItem[] = [ + { key: 'unlock', metric: 'S4.9', icon: ListChecks, tone: 'warn' }, + { key: 'scope', metric: '8/8', icon: Radar, tone: 'steady' }, + { key: 'hosts', metric: '3', icon: Activity, tone: 'warn' }, + { key: 'sourceControl', metric: '0', icon: GitBranch, tone: 'locked' }, + { key: 'awooop', metric: '已接線', icon: Bell, tone: 'steady' }, + { key: 'boundary', metric: 'Gate 0', icon: Lock, tone: 'locked' }, +] + +const iwooosCommandMapBoundaries = [ + 'iwooos_command_map_first_layer=true', + 'iwooos_command_map_mode_count=6', + 'iwooos_command_map_default_mode=unlock', + 'iwooos_command_map_above_first_progress_unlock_path=true', + 'iwooos_command_map_navigation_controls_allowed=true', + 'iwooos_command_map_execution_action_buttons_allowed=false', + 'iwooos_command_map_runtime_gate_count=0', + 'iwooos_command_map_kali_execute_authorized=false', + 'iwooos_command_map_host_mutation_authorized=false', + 'iwooos_command_map_source_control_mutation_authorized=false', + 'iwooos_command_map_github_primary_switch_authorized=false', + 'runtime_execution_authorized=false', + 'active_runtime_gate_count=0', + 'not_authorization=true', +] as const + const iwooosFirstUnlockEvidencePacketSlots = [ { key: 'ownerDecisionMetadata', slot: '01', state: '待補', icon: ClipboardCheck, tone: 'warn' }, { key: 'scopeEvidenceRefs', slot: '02', state: '待補', icon: GitBranch, tone: 'warn' }, @@ -9000,6 +9034,182 @@ function IwoooSFirstProgressUnlockPathBoard() { ) } +function IwoooSCommandMapBoard() { + const t = useTranslations('iwooos.commandMap') + const [activeKey, setActiveKey] = useState('unlock') + const activeItem = iwooosCommandMapItems.find(item => item.key === activeKey) ?? iwooosCommandMapItems[0] + const ActiveIcon = activeItem.icon + const textWrap = { overflowWrap: 'anywhere' as const, wordBreak: 'break-word' as const } + + return ( +
+
+
+
+
+ + {t('eyebrow')} +
+

{t('title')}

+

+ {t('subtitle')} +

+
+ +
+ {iwooosCommandMapItems.map(item => { + const Icon = item.icon + const selected = activeKey === item.key + return ( + + ) + })} +
+
+ +
+
+ +
+
+
+

+ {t(`items.${activeItem.key}.title` as never)} +

+ + {activeItem.metric} + +
+

+ {t(`items.${activeItem.key}.detail` as never)} +

+
+ {['evidence', 'next', 'locked'].map(key => ( +
+
{t(`panelLabels.${key}` as never)}
+
+ {t(`items.${activeItem.key}.${key}` as never)} +
+
+ ))} +
+
+
+ +
+ + {t('boundaryTitle')} + +
+ {iwooosCommandMapBoundaries.map(item => ( + + {item} + + ))} +
+
+
+
+ ) +} + function IwoooSFirstUnlockEvidencePacketBoard() { const t = useTranslations('iwooos.firstUnlockEvidencePacket') const summaryItems = [ @@ -13719,6 +13929,7 @@ export default function IwoooSPage({ params }: { params: { locale: string } }) { + diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index e177ca5d8..636189e50 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,86 @@ +## 2026-05-31|IwoooS 資安工作地圖首層化 + +**背景**: + +- 使用者要求 IwoooS 不要只是大量文字,需更符合主流資安產品的圖表、狀態與互動式呈現。 +- 前一輪已把第一個 61% 解鎖路徑推到首層;本輪再把整體資安網濃縮成六條可切換工作線,讓使用者先看範圍、證據、下一步與鎖定邊界,再往下看詳細證據。 + +**本次調整**: + +- `apps/web/src/app/[locale]/iwooos/page.tsx`: + - 新增 `IwoooSCommandMapBoard`,位於標題區下方、第一解鎖路徑之前。 + - 六條工作線:`61% 解鎖線`、`全產品範圍`、`主機與工具`、`版本來源`、`AwoooP 真相鏈`、`執行邊界`。 + - 每條工作線以 icon、metric、狀態與 active panel 呈現 evidence / next / locked,不提供掃描、修復、主機變更或版本來源操作。 +- `apps/web/messages/zh-TW.json` / `apps/web/messages/en.json`: + - 補齊資安工作地圖繁中文案;`en.json` 維持繁中鏡像。 +- `docs/security/iwooos-posture-projection.snapshot.json`: + - 新增 `command_map_items` 六條只讀導覽線,以及 navigation control / execution action button 分離欄位。 +- `docs/security/security-mirror-status-rollup.snapshot.json`: + - 新增 `s2_143_iwooos_command_map_first_layer`;明確標示 headline 不提升、runtime 不開閘。 +- `scripts/security/security-mirror-progress-guard.py`: + - 新增 guard:工作地圖必須在第一解鎖路徑之前、視覺指揮板之前;六條線只能 navigation,不能變成 execution action。 + +**驗證**: + +```text +python3 -m json.tool apps/web/messages/zh-TW.json / en.json -> pass +python3 -m json.tool docs/security/iwooos-posture-projection.snapshot.json -> pass +python3 -m json.tool docs/security/security-mirror-status-rollup.snapshot.json -> pass +cmp -s apps/web/messages/zh-TW.json apps/web/messages/en.json -> pass +python3 scripts/security/security-mirror-progress-guard.py -> SECURITY_MIRROR_PROGRESS_GUARD_OK +python3 scripts/security/source-control-owner-response-guard.py -> SOURCE_CONTROL_OWNER_RESPONSE_GUARD_OK +git diff --check -> pass +pnpm --dir apps/web exec tsc --noEmit --tsBuildInfoFile /tmp/awoooi-iwooos-command-map-20260531-r2.tsbuildinfo -> pass +NEXT_PUBLIC_API_URL=https://awoooi.wooo.work NEXT_PRIVATE_BUILD_WORKER_COUNT=1 pnpm --dir apps/web run build -> pass +``` + +**本機瀏覽器檢查**: + +```text +http://localhost:3022/zh-TW/iwooos?_v=command-map-local-r2 + +desktop 1440x1000: + hasCommandMap=true + buttonCount=6 + commandMapTop=245 + mapBeforeUnlock=true + mapBeforeVisual=true + horizontalOverflow=false + screenshot=/tmp/iwooos-command-map-r2-desktop.png + +mobile 390x844: + hasCommandMap=true + buttonCount=6 + commandMapTop=449 + mapBeforeUnlock=true + mapBeforeVisual=true + horizontalOverflow=false + screenshot=/tmp/iwooos-command-map-r2-mobile.png + +interaction: + sourceSelected=true + hostSelected=true + horizontalOverflow=false +``` + +**進度邊界**: + +```text +overall_percent=61 +framework_detail += s2_143_iwooos_command_map_first_layer +iwooos_command_map_mode_count=6 +iwooos_command_map_navigation_controls_allowed=true +iwooos_command_map_execution_action_buttons_allowed=false +owner_response_received_count=0 +owner_response_accepted_count=0 +runtime_execution_authorized=false +active_runtime_gate_count=0 +kali_execute_authorized=false +host_mutation_authorized=false +source_control_mutation_authorized=false +github_primary_switch_authorized=false +``` + ## 2026-05-31|Telegram 詳情/歷史接上 Incident 真相鏈 **背景**: diff --git a/docs/security/iwooos-posture-projection.snapshot.json b/docs/security/iwooos-posture-projection.snapshot.json index a6216ebc8..bd5eff732 100644 --- a/docs/security/iwooos-posture-projection.snapshot.json +++ b/docs/security/iwooos-posture-projection.snapshot.json @@ -49,6 +49,13 @@ "first_progress_unlock_path_owner_response_received_count": 0, "first_progress_unlock_path_owner_response_accepted_count": 0, "first_progress_unlock_path_runtime_gate_count": 0, + "command_map_first_layer": true, + "command_map_mode_count": 6, + "command_map_default_mode": "unlock", + "command_map_above_first_progress_unlock_path": true, + "command_map_navigation_controls_allowed": true, + "command_map_execution_action_buttons_allowed": false, + "command_map_runtime_gate_count": 0, "long_form_sections_default_collapsed": true, "owner_response_validation_received_count": 0, "owner_response_validation_accepted_count": 0, @@ -210,6 +217,84 @@ "not_authorization": true } ], + "command_map_items": [ + { + "item_id": "unlock", + "display_order": 1, + "display_state": "s4_9_owner_evidence_waiting", + "target_surface": "first_progress_unlock_path", + "display_mode": "first_layer_command_map", + "navigation_control_allowed": true, + "execution_action_button_allowed": false, + "runtime_gate_opened": false, + "runtime_execution_authorized": false, + "not_authorization": true + }, + { + "item_id": "scope", + "display_order": 2, + "display_state": "eight_scopes_read_only", + "target_surface": "global_security_mesh_matrix", + "display_mode": "first_layer_command_map", + "navigation_control_allowed": true, + "execution_action_button_allowed": false, + "runtime_gate_opened": false, + "runtime_execution_authorized": false, + "not_authorization": true + }, + { + "item_id": "hosts", + "display_order": 3, + "display_state": "hosts_112_111_168_observe_only", + "target_surface": "host_tool_evidence_chain", + "display_mode": "first_layer_command_map", + "navigation_control_allowed": true, + "execution_action_button_allowed": false, + "runtime_gate_opened": false, + "runtime_execution_authorized": false, + "not_authorization": true + }, + { + "item_id": "sourceControl", + "display_order": 4, + "display_state": "github_primary_waiting_owner_evidence", + "target_surface": "source_control_readiness", + "display_mode": "first_layer_command_map", + "navigation_control_allowed": true, + "execution_action_button_allowed": false, + "runtime_gate_opened": false, + "runtime_execution_authorized": false, + "source_control_mutation_authorized": false, + "github_primary_switch_authorized": false, + "not_authorization": true + }, + { + "item_id": "awooop", + "display_order": 5, + "display_state": "truth_chain_connected_read_only", + "target_surface": "awooop_incident_truth_chain", + "display_mode": "first_layer_command_map", + "navigation_control_allowed": true, + "execution_action_button_allowed": false, + "runtime_gate_opened": false, + "runtime_execution_authorized": false, + "not_authorization": true + }, + { + "item_id": "boundary", + "display_order": 6, + "display_state": "gate_zero_locked", + "target_surface": "execution_boundary", + "display_mode": "first_layer_command_map", + "navigation_control_allowed": true, + "execution_action_button_allowed": false, + "runtime_gate_opened": false, + "runtime_execution_authorized": false, + "kali_execute_authorized": false, + "host_mutation_authorized": false, + "not_authorization": true + } + ], "posture_pillars": [ { "pillar_id": "exposure_posture", diff --git a/docs/security/security-mirror-status-rollup.snapshot.json b/docs/security/security-mirror-status-rollup.snapshot.json index 13bda3d43..e3cbfe035 100644 --- a/docs/security/security-mirror-status-rollup.snapshot.json +++ b/docs/security/security-mirror-status-rollup.snapshot.json @@ -2185,6 +2185,18 @@ "runtime_delta": false, "execution_authorized": false, "not_authorization": true + }, + { + "delta_id": "s2_143_iwooos_command_map_first_layer", + "display_order": 172, + "completed_stage": "S2.143 IwoooS 資安工作地圖首層化", + "progress_axis": "framework_detail", + "headline_percent_delta": 0, + "framework_delta_visible": true, + "why_headline_unchanged": "IwoooS 只新增首層資安工作地圖,把 61% 解鎖線、全產品範圍、主機與工具、版本來源、AwoooP 真相鏈與執行邊界濃縮為六個 read-only navigation modes,並放在第一解鎖路徑之前;iwooos_command_map_mode_count=6、iwooos_command_map_default_mode=unlock、iwooos_command_map_above_first_progress_unlock_path=true、iwooos_command_map_navigation_controls_allowed=true、iwooos_command_map_execution_action_buttons_allowed=false、iwooos_command_map_runtime_gate_count=0、runtime_execution_authorized=false、active_runtime_gate_count=0,不把導覽切換當 owner response 已收到、runtime gate、Kali /execute、主機變更、repo/refs/workflow/secret 操作、GitHub primary 切換、Gitea 停用或正式部署。", + "runtime_delta": false, + "execution_authorized": false, + "not_authorization": true } ], "next_safe_actions": [ diff --git a/scripts/security/security-mirror-progress-guard.py b/scripts/security/security-mirror-progress-guard.py index 92727f86c..804b8690d 100755 --- a/scripts/security/security-mirror-progress-guard.py +++ b/scripts/security/security-mirror-progress-guard.py @@ -599,6 +599,7 @@ def validate(root: Path) -> None: "s2_140_iwooos_product_evidence_wiring_preflight_retry_review_candidate_preflight_recovery_retry_gates", "s2_141_iwooos_all_product_coverage_snapshot", "s2_142_iwooos_first_unlock_path_first_layer", + "s2_143_iwooos_command_map_first_layer", ] assert_equal( "progress_delta_ledger.delta_ids", @@ -1459,6 +1460,37 @@ def validate(root: Path) -> None: iwooos_projection["summary"]["first_progress_unlock_path_runtime_gate_count"], 0, ) + assert_true( + "iwooos_projection.summary.command_map_first_layer", + iwooos_projection["summary"]["command_map_first_layer"], + ) + assert_equal( + "iwooos_projection.summary.command_map_mode_count", + iwooos_projection["summary"]["command_map_mode_count"], + 6, + ) + assert_equal( + "iwooos_projection.summary.command_map_default_mode", + iwooos_projection["summary"]["command_map_default_mode"], + "unlock", + ) + assert_true( + "iwooos_projection.summary.command_map_above_first_progress_unlock_path", + iwooos_projection["summary"]["command_map_above_first_progress_unlock_path"], + ) + assert_true( + "iwooos_projection.summary.command_map_navigation_controls_allowed", + iwooos_projection["summary"]["command_map_navigation_controls_allowed"], + ) + assert_false( + "iwooos_projection.summary.command_map_execution_action_buttons_allowed", + iwooos_projection["summary"]["command_map_execution_action_buttons_allowed"], + ) + assert_equal( + "iwooos_projection.summary.command_map_runtime_gate_count", + iwooos_projection["summary"]["command_map_runtime_gate_count"], + 0, + ) assert_true( "iwooos_projection.summary.all_product_coverage_snapshot_detail_ledger_collapsed", iwooos_projection["summary"]["all_product_coverage_snapshot_detail_ledger_collapsed"], @@ -11732,6 +11764,117 @@ def validate(root: Path) -> None: f"iwooos_projection.first_progress_unlock_path_steps.{item['step_id']}.not_authorization", item["not_authorization"], ) + assert_text_contains( + "iwooos_page.command_map_testid", + iwooos_projection_page, + 'data-testid="iwooos-command-map-board"', + ) + assert_text_contains( + "iwooos_page.command_map_component", + iwooos_projection_page, + "IwoooSCommandMapBoard", + ) + assert_text_before( + "iwooos_page.command_map_before_first_unlock_path", + iwooos_projection_page, + "", + "", + ) + assert_text_before( + "iwooos_page.command_map_before_visual_dashboard", + iwooos_projection_page, + "", + "", + ) + assert_text_contains( + "iwooos_page.command_map_boundaries", + iwooos_projection_page, + 'data-testid="iwooos-command-map-boundaries"', + ) + for text in [ + "iwooos_command_map_first_layer=true", + "iwooos_command_map_mode_count=6", + "iwooos_command_map_default_mode=unlock", + "iwooos_command_map_above_first_progress_unlock_path=true", + "iwooos_command_map_navigation_controls_allowed=true", + "iwooos_command_map_execution_action_buttons_allowed=false", + "iwooos_command_map_runtime_gate_count=0", + "iwooos_command_map_kali_execute_authorized=false", + "iwooos_command_map_host_mutation_authorized=false", + "iwooos_command_map_source_control_mutation_authorized=false", + "iwooos_command_map_github_primary_switch_authorized=false", + "runtime_execution_authorized=false", + "active_runtime_gate_count=0", + "not_authorization=true", + ]: + assert_text_contains( + "iwooos_page.command_map_boundary", + iwooos_projection_page, + text, + ) + expected_command_map_item_ids = [ + "unlock", + "scope", + "hosts", + "sourceControl", + "awooop", + "boundary", + ] + command_map_items = iwooos_projection["command_map_items"] + assert_equal( + "iwooos_projection.command_map_items.ids", + [item["item_id"] for item in command_map_items], + expected_command_map_item_ids, + ) + assert_equal( + "iwooos_projection.command_map_items.display_order", + [item["display_order"] for item in command_map_items], + list(range(1, len(expected_command_map_item_ids) + 1)), + ) + for item in command_map_items: + assert_equal( + f"iwooos_projection.command_map_items.{item['item_id']}.display_mode", + item["display_mode"], + "first_layer_command_map", + ) + assert_true( + f"iwooos_projection.command_map_items.{item['item_id']}.navigation_control_allowed", + item["navigation_control_allowed"], + ) + assert_false( + f"iwooos_projection.command_map_items.{item['item_id']}.execution_action_button_allowed", + item["execution_action_button_allowed"], + ) + assert_false( + f"iwooos_projection.command_map_items.{item['item_id']}.runtime_gate_opened", + item["runtime_gate_opened"], + ) + assert_false( + f"iwooos_projection.command_map_items.{item['item_id']}.runtime_execution_authorized", + item["runtime_execution_authorized"], + ) + assert_true( + f"iwooos_projection.command_map_items.{item['item_id']}.not_authorization", + item["not_authorization"], + ) + source_control_item = next(item for item in command_map_items if item["item_id"] == "sourceControl") + assert_false( + "iwooos_projection.command_map_items.sourceControl.source_control_mutation_authorized", + source_control_item["source_control_mutation_authorized"], + ) + assert_false( + "iwooos_projection.command_map_items.sourceControl.github_primary_switch_authorized", + source_control_item["github_primary_switch_authorized"], + ) + boundary_item = next(item for item in command_map_items if item["item_id"] == "boundary") + assert_false( + "iwooos_projection.command_map_items.boundary.kali_execute_authorized", + boundary_item["kali_execute_authorized"], + ) + assert_false( + "iwooos_projection.command_map_items.boundary.host_mutation_authorized", + boundary_item["host_mutation_authorized"], + ) for text in [ "iwooos_first_unlock_path_step_count=5", "iwooos_first_unlock_path_current_focus=s4_9_owner_response", @@ -11806,6 +11949,60 @@ def validate(root: Path) -> None: list(web_messages_en["iwooos"]["firstProgressUnlockPath"]["items"].keys()), key, ) + assert_contains( + "web_messages.zh-TW.iwooos.commandMap", + list(web_messages_zh["iwooos"].keys()), + "commandMap", + ) + assert_contains( + "web_messages.en.iwooos.commandMap", + list(web_messages_en["iwooos"].keys()), + "commandMap", + ) + for key in ["eyebrow", "title", "subtitle", "tabsLabel", "boundaryTitle", "panelLabels", "items"]: + assert_contains( + "web_messages.zh-TW.iwooos.commandMap.keys", + list(web_messages_zh["iwooos"]["commandMap"].keys()), + key, + ) + assert_contains( + "web_messages.en.iwooos.commandMap.keys", + list(web_messages_en["iwooos"]["commandMap"].keys()), + key, + ) + for key in ["evidence", "next", "locked"]: + assert_contains( + "web_messages.zh-TW.iwooos.commandMap.panelLabels", + list(web_messages_zh["iwooos"]["commandMap"]["panelLabels"].keys()), + key, + ) + assert_contains( + "web_messages.en.iwooos.commandMap.panelLabels", + list(web_messages_en["iwooos"]["commandMap"]["panelLabels"].keys()), + key, + ) + for key in expected_command_map_item_ids: + assert_contains( + "web_messages.zh-TW.iwooos.commandMap.items", + list(web_messages_zh["iwooos"]["commandMap"]["items"].keys()), + key, + ) + assert_contains( + "web_messages.en.iwooos.commandMap.items", + list(web_messages_en["iwooos"]["commandMap"]["items"].keys()), + key, + ) + for field in ["title", "state", "detail", "evidence", "next", "locked"]: + assert_contains( + f"web_messages.zh-TW.iwooos.commandMap.items.{key}", + list(web_messages_zh["iwooos"]["commandMap"]["items"][key].keys()), + field, + ) + assert_contains( + f"web_messages.en.iwooos.commandMap.items.{key}", + list(web_messages_en["iwooos"]["commandMap"]["items"][key].keys()), + field, + ) assert_text_contains( "iwooos_page.first_unlock_evidence_packet_testid", iwooos_projection_page,