diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 6278ceee0..86ac2094e 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -14758,6 +14758,50 @@ } } }, + "highValueConfigControlCoverage": { + "eyebrow": "高價值配置覆蓋矩陣", + "title": "14 類重要配置已進入同一張控管矩陣", + "subtitle": "這張卡顯示全域高價值配置控管成熟度,不是單次變更分類;Nginx、DNS / TLS、K8s、Secret、Workflow、Backup、AI provider、agent-bounty runtime 都已註冊,但 owner response 與執行期仍為 0。", + "coverageLabel": "只讀成熟度", + "boundaryTitle": "配置覆蓋矩陣邊界", + "boundaryIntro": "以下鍵值固定:矩陣只顯示分類、證據與下一步收件順序,不代表 live evidence 已取得,也不代表 reload、sync、scan、secret rotation、agent-bounty runtime 或主機操作已授權。", + "summary": { + "categories": { + "label": "註冊類別", + "detail": "14 類全部來自高價值配置 Gate。" + }, + "c0": { + "label": "C0 高風險", + "detail": "8 類會影響公開入口、secret、部署、備份或 agent runtime。" + }, + "coverage": { + "label": "平均成熟度", + "detail": "64% 只代表只讀框架成熟度。" + }, + "runtimeGate": { + "label": "執行期", + "detail": "仍為 0,不提供操作按鈕。" + } + }, + "items": { + "dockerSystemd": { + "title": "Docker / systemd 主機服務", + "body": "下一步補 110 / 188 compose、systemd、port / volume / env 只讀 inventory、restart window 與 rollback owner。" + }, + "sshNetwork": { + "title": "SSH / network / firewall", + "body": "下一步補 target whitelist、host key policy、ingress / egress matrix、network owner 與維護窗口。" + }, + "backupRestore": { + "title": "Backup / restore / escrow", + "body": "下一步補 restore drill approval package、offsite escrow owner、retention owner 與 no-secret-value evidence。" + }, + "monitoring": { + "title": "監控與告警設定", + "body": "下一步補 Prometheus / Alertmanager / Grafana / SigNoz / Sentry / Langfuse 的 rule diff、receiver diff 與 reload owner。" + } + } + }, "domainTlsCertbotInventory": { "eyebrow": "DNS / TLS / certbot 清冊", "title": "公開入口憑證與 ACME 關係已納入只讀控管", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 6278ceee0..86ac2094e 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -14758,6 +14758,50 @@ } } }, + "highValueConfigControlCoverage": { + "eyebrow": "高價值配置覆蓋矩陣", + "title": "14 類重要配置已進入同一張控管矩陣", + "subtitle": "這張卡顯示全域高價值配置控管成熟度,不是單次變更分類;Nginx、DNS / TLS、K8s、Secret、Workflow、Backup、AI provider、agent-bounty runtime 都已註冊,但 owner response 與執行期仍為 0。", + "coverageLabel": "只讀成熟度", + "boundaryTitle": "配置覆蓋矩陣邊界", + "boundaryIntro": "以下鍵值固定:矩陣只顯示分類、證據與下一步收件順序,不代表 live evidence 已取得,也不代表 reload、sync、scan、secret rotation、agent-bounty runtime 或主機操作已授權。", + "summary": { + "categories": { + "label": "註冊類別", + "detail": "14 類全部來自高價值配置 Gate。" + }, + "c0": { + "label": "C0 高風險", + "detail": "8 類會影響公開入口、secret、部署、備份或 agent runtime。" + }, + "coverage": { + "label": "平均成熟度", + "detail": "64% 只代表只讀框架成熟度。" + }, + "runtimeGate": { + "label": "執行期", + "detail": "仍為 0,不提供操作按鈕。" + } + }, + "items": { + "dockerSystemd": { + "title": "Docker / systemd 主機服務", + "body": "下一步補 110 / 188 compose、systemd、port / volume / env 只讀 inventory、restart window 與 rollback owner。" + }, + "sshNetwork": { + "title": "SSH / network / firewall", + "body": "下一步補 target whitelist、host key policy、ingress / egress matrix、network owner 與維護窗口。" + }, + "backupRestore": { + "title": "Backup / restore / escrow", + "body": "下一步補 restore drill approval package、offsite escrow owner、retention owner 與 no-secret-value evidence。" + }, + "monitoring": { + "title": "監控與告警設定", + "body": "下一步補 Prometheus / Alertmanager / Grafana / SigNoz / Sentry / Langfuse 的 rule diff、receiver diff 與 reload owner。" + } + } + }, "domainTlsCertbotInventory": { "eyebrow": "DNS / TLS / certbot 清冊", "title": "公開入口憑證與 ACME 關係已納入只讀控管", diff --git a/apps/web/src/app/[locale]/iwooos/page.tsx b/apps/web/src/app/[locale]/iwooos/page.tsx index 122307180..30e19e8b6 100644 --- a/apps/web/src/app/[locale]/iwooos/page.tsx +++ b/apps/web/src/app/[locale]/iwooos/page.tsx @@ -271,6 +271,14 @@ type HighValueConfigOwnerPacketItem = { tone: 'steady' | 'warn' | 'locked' } +type HighValueConfigControlCoverageItem = { + key: string + rank: string + value: string + icon: typeof ShieldCheck + tone: 'steady' | 'warn' | 'locked' +} + type DomainTlsCertbotInventoryItem = { key: string check: string @@ -2060,6 +2068,43 @@ const highValueConfigOwnerPacketSummary = [ { key: 'runtimeGate', value: '0', icon: Lock, tone: 'locked' }, ] as const +const highValueConfigControlCoverageSummary = [ + { key: 'categories', value: '14', icon: ListChecks, tone: 'steady' }, + { key: 'c0', value: '8', icon: AlertTriangle, tone: 'warn' }, + { key: 'coverage', value: '64%', icon: ShieldCheck, tone: 'warn' }, + { key: 'runtimeGate', value: '0', icon: Lock, tone: 'locked' }, +] as const + +const highValueConfigControlCoverageItems: HighValueConfigControlCoverageItem[] = [ + { key: 'dockerSystemd', rank: 'P1-1', value: '42%', icon: Server, tone: 'warn' }, + { key: 'sshNetwork', rank: 'P1-2', value: '48%', icon: Network, tone: 'warn' }, + { key: 'backupRestore', rank: 'P1-3', value: '52%', icon: Database, tone: 'warn' }, + { key: 'monitoring', rank: 'P1-4', value: '56%', icon: Radar, tone: 'warn' }, +] + +const highValueConfigControlCoverageBoundaries = [ + 'high_value_config_control_coverage_frontstage_summary_count=4', + 'high_value_config_control_coverage_frontstage_item_count=4', + 'high_value_config_control_coverage_category_count=14', + 'high_value_config_control_coverage_c0_category_count=8', + 'high_value_config_control_coverage_c1_category_count=4', + 'high_value_config_control_coverage_average_percent=64', + 'high_value_config_control_coverage_needs_live_evidence_count=6', + 'high_value_config_control_coverage_owner_response_required_count=14', + 'high_value_config_control_coverage_owner_response_received_count=0', + 'high_value_config_control_coverage_owner_response_accepted_count=0', + 'high_value_config_control_coverage_runtime_gate_count=0', + 'high_value_config_control_coverage_action_button_count=0', + 'runtime_execution_authorized=false', + 'host_write_authorized=false', + 'nginx_reload_authorized=false', + 'dns_tls_change_authorized=false', + 'certbot_renew_authorized=false', + 'workflow_modification_authorized=false', + 'active_scan_authorized=false', + 'agent_bounty_runtime_authorized=false', +] as const + const highValueConfigOwnerPacketItems: HighValueConfigOwnerPacketItem[] = [ { key: 'packetDraft', gate: 'HV1', state: '草案 1', icon: ClipboardCheck, tone: 'warn' }, { key: 'currentTier', gate: 'HV2', state: 'C3 / P3', icon: ListChecks, tone: 'warn' }, @@ -6671,6 +6716,134 @@ function IwoooSHighValueConfigOwnerPacketBoard() { ) } +function IwoooSHighValueConfigControlCoverageBoard() { + const t = useTranslations('iwooos.highValueConfigControlCoverage') + const textWrap = { overflowWrap: 'anywhere' as const, wordBreak: 'break-word' as const } + + return ( +
+
+
+
+
+ + {t('eyebrow')} +
+

{t('title')}

+

+ {t('subtitle')} +

+
+ +
+ {highValueConfigControlCoverageSummary.map(item => { + const Icon = item.icon + return ( +
+
+ {t(`summary.${item.key}.label` as never)} + +
+
+ {item.value} +
+

+ {t(`summary.${item.key}.detail` as never)} +

+
+ ) + })} +
+
+ +
+ {highValueConfigControlCoverageItems.map(item => { + const Icon = item.icon + return ( +
+
+ + {item.rank} + + +
+
+
+ {t(`items.${item.key}.title` as never)} +
+
+ {t('coverageLabel')}:{item.value} +
+
+

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

+
+ ) + })} +
+ +
+ + {t('boundaryTitle')} + +

+ {t('boundaryIntro')} +

+
+ {highValueConfigControlCoverageBoundaries.map(item => ( + + {item} + + ))} +
+
+
+
+ ) +} + function IwoooSDomainTlsCertbotInventoryBoard() { const t = useTranslations('iwooos.domainTlsCertbotInventory') const textWrap = { overflowWrap: 'anywhere' as const, wordBreak: 'break-word' as const } @@ -18682,6 +18855,7 @@ export default function IwoooSPage({ params }: { params: { locale: string } }) { + diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 091a7e1b5..7ef4df2be 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,42 @@ +## 2026-06-11|IwoooS 高價值配置控管覆蓋矩陣 + +**背景**:統帥要求所有重要配置都要先被資安控管,尤其 Nginx 常被手動變動;既有高價值配置 Gate 能判斷單次 diff,Owner Packet 能產生回覆草案,但仍缺一張可重跑、可被 guard 與前端消費的全域覆蓋矩陣。本段仍維持只讀證據、低摩擦框架與 `0 / false` 邊界;不 reload Nginx、不改 DNS / TLS、不改 workflow / secret、不 SSH、不 active scan、不啟動 `agent-bounty-protocol` runtime。 + +**完成**: + +- 新增 `high_value_config_control_coverage_v1` 產生器、schema、snapshot 與人讀文件,將 `14` 類高價值配置納入同一張控管覆蓋矩陣。 +- 覆蓋矩陣目前 `C0=8`、`C1=4`、`C2=1`、`C3=1`,平均只讀控管成熟度 `64%`,需補 live evidence 類別 `6`。 +- owner response required `14`,received / accepted 仍 `0 / 0`;runtime gate `0`,action button `0`。 +- 最低覆蓋四項已列入 P1 順序:`docker_compose_systemd_host_config` `42%`、`ssh_firewall_network_access` `48%`、`backup_restore_credential` `52%`、`monitoring_alerting_observability` `56%`。 +- `docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md`、IwoooS posture projection snapshot / schema 與 `security-mirror-progress-guard.py` 已同步新口徑。 +- `/zh-TW/iwooos` 新增「高價值配置覆蓋矩陣」只讀卡,顯示 `14` 類、`8` 個 C0、平均 `64%`、runtime gate `0`,並把最低覆蓋四項與固定邊界放在前台可見但不可執行的區塊。 +- `zh-TW` 與目前鏡像 messages 維持繁中;目標前端檔未命中工作對話外露片語。 + +**本地驗證**: + +- `python3 scripts/security/high-value-config-control-coverage.py --root . --output /tmp/high-value-config-control-coverage-check.json`:`HIGH_VALUE_CONFIG_CONTROL_COVERAGE_OK categories=14 c0=8 avg=64 runtime_gate=0`。 +- `python3 scripts/security/security-mirror-progress-guard.py --root .`:`SECURITY_MIRROR_PROGRESS_GUARD_OK`。 +- `python3 scripts/security/source-control-owner-response-guard.py --root .`:`SOURCE_CONTROL_OWNER_RESPONSE_GUARD_OK`。 +- `python3 -m py_compile scripts/security/high-value-config-control-coverage.py scripts/security/security-mirror-progress-guard.py`:通過。 +- JSON parse:coverage snapshot / schema、IwoooS posture projection snapshot / schema、`zh-TW.json`、`en.json` 通過。 +- `python3 scripts/ops/doc-secrets-sanity-check.py docs .gitea`:`DOC_SECRET_SANITY_OK scanned_files=674`。 +- `git diff --check`:通過。 +- 目標敏感片語掃描:IwoooS page、`zh-TW.json`、`en.json` 與本輪 security docs 未命中 `工作視窗` / `內部對話` / `對話內容` / `批准!繼續` / `In app browser`。 +- `tsc --noEmit -p apps/web/tsconfig.json` 未能在此乾淨臨時 worktree 形成有效型別結果,原因是缺少 `next` / `react` / workspace tsconfig 等依賴解析;此為本地依賴狀態限制,不是本段新增型別錯誤結論。後續以 Gitea code-review / CD build 與正式站 smoke 補驗。 + +**完成度同步**: + +- 高價值配置覆蓋矩陣框架:`100%`。 +- 全域配置類別註冊:`100%`。 +- 覆蓋 snapshot / schema / guard:`100%`。 +- 前台只讀可視化:本地 `100%`,正式站待 CD 後驗證。 +- owner response received / accepted:`0%`。 +- live evidence collection:`0%`。 +- runtime gate / action button:`0%`。 +- IwoooS 整體仍維持 `64%`;active runtime gate 仍 `0`。 + +**邊界**:本段只做只讀分類、snapshot、schema、guard、文件與前端可視化;未送 owner request、未標記 received / accepted、未修改 Nginx / systemd / Docker / firewall / DNS / TLS / certbot / workflow / secret / runner / K8s / ArgoCD、未收 secret value、未 SSH、未 active scan、未開 `agent-bounty-protocol` runtime、未處理 payout / withdrawal、未建立執行按鈕。 + ## 2026-06-11|IwoooS S4.10 Target Owner Response 擴充與正式站驗證 **背景**:`VibeWork` 與 `agent-bounty-protocol` 已納入 IwoooS 資安驗證與控管範圍;S4.10 GitHub target owner response 仍需只讀 owner gate,不得自動建立 repo、同步 refs、切 GitHub primary、修改 workflow、收 secret value 或開 runtime gate。本段把 owner response packet、approval board、primary readiness、rollback ADR、workflow secret-name inventory、rollup 與 IwoooS 前端口徑從舊 `8` 候選 / `7` 需批准 / `22` 回覆範本收斂到目前 `10` 候選 / `9` 需批准 / `24` 回覆範本。 diff --git a/docs/schemas/high_value_config_control_coverage_v1.schema.json b/docs/schemas/high_value_config_control_coverage_v1.schema.json new file mode 100644 index 000000000..004b958b0 --- /dev/null +++ b/docs/schemas/high_value_config_control_coverage_v1.schema.json @@ -0,0 +1,184 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://awoooi.wooo.work/schemas/high_value_config_control_coverage_v1.schema.json", + "title": "IwoooS 高價值配置控管覆蓋矩陣", + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "generated_at", + "git_commit", + "source_category_definition", + "status", + "summary", + "execution_boundaries", + "coverage_categories", + "lowest_coverage_categories", + "next_collection_order", + "operator_interpretation" + ], + "properties": { + "schema_version": { + "const": "high_value_config_control_coverage_v1" + }, + "generated_at": { + "type": "string" + }, + "git_commit": { + "type": "string" + }, + "source_category_definition": { + "const": "scripts/security/high-value-config-change-gate.py" + }, + "status": { + "const": "coverage_matrix_ready" + }, + "summary": { + "type": "object", + "additionalProperties": false, + "required": [ + "category_count", + "c0_category_count", + "c1_category_count", + "c2_category_count", + "c3_category_count", + "registered_control_count", + "owner_response_required_count", + "owner_response_received_count", + "owner_response_accepted_count", + "runtime_gate_count", + "action_button_count", + "average_coverage_percent", + "needs_live_evidence_count", + "lowest_coverage_category_count" + ], + "properties": { + "category_count": { "type": "integer", "minimum": 1 }, + "c0_category_count": { "type": "integer", "minimum": 0 }, + "c1_category_count": { "type": "integer", "minimum": 0 }, + "c2_category_count": { "type": "integer", "minimum": 0 }, + "c3_category_count": { "type": "integer", "minimum": 0 }, + "registered_control_count": { "type": "integer", "minimum": 1 }, + "owner_response_required_count": { "type": "integer", "minimum": 0 }, + "owner_response_received_count": { "const": 0 }, + "owner_response_accepted_count": { "const": 0 }, + "runtime_gate_count": { "const": 0 }, + "action_button_count": { "const": 0 }, + "average_coverage_percent": { "type": "integer", "minimum": 0, "maximum": 100 }, + "needs_live_evidence_count": { "type": "integer", "minimum": 0 }, + "lowest_coverage_category_count": { "type": "integer", "minimum": 0 } + } + }, + "execution_boundaries": { + "type": "object", + "additionalProperties": { "const": false }, + "required": [ + "runtime_execution_authorized", + "host_write_authorized", + "nginx_reload_authorized", + "dns_tls_change_authorized", + "certbot_renew_authorized", + "argocd_sync_authorized", + "kubectl_action_authorized", + "workflow_modification_authorized", + "runner_change_authorized", + "refs_sync_authorized", + "force_push_authorized", + "secret_value_collection_allowed", + "active_scan_authorized", + "agent_bounty_runtime_authorized", + "payout_or_withdrawal_authorized", + "action_buttons_allowed" + ] + }, + "coverage_categories": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/coverage_category" + } + }, + "lowest_coverage_categories": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "category_id", + "label", + "coverage_percent", + "current_gap", + "next_owner_action" + ], + "properties": { + "category_id": { "type": "string" }, + "label": { "type": "string" }, + "coverage_percent": { "type": "integer", "minimum": 0, "maximum": 100 }, + "current_gap": { "type": "string" }, + "next_owner_action": { "type": "string" } + } + } + }, + "next_collection_order": { + "type": "array", + "items": { "type": "string" } + }, + "operator_interpretation": { + "type": "array", + "items": { "type": "string" } + } + }, + "$defs": { + "coverage_category": { + "type": "object", + "additionalProperties": false, + "required": [ + "category_id", + "label", + "priority", + "control_tier", + "required_gate", + "coverage_status", + "coverage_percent", + "patterns", + "required_validation", + "evidence_refs", + "current_gap", + "next_owner_action", + "owner_response_required", + "owner_response_received", + "owner_response_accepted", + "runtime_gate_open", + "action_buttons_allowed" + ], + "properties": { + "category_id": { "type": "string" }, + "label": { "type": "string" }, + "priority": { "enum": ["P0", "P1", "P2", "P3"] }, + "control_tier": { "enum": ["C0", "C1", "C2", "C3"] }, + "required_gate": { "type": "string" }, + "coverage_status": { "type": "string" }, + "coverage_percent": { "type": "integer", "minimum": 0, "maximum": 100 }, + "patterns": { + "type": "array", + "items": { "type": "string" } + }, + "required_validation": { + "type": "array", + "items": { "type": "string" } + }, + "evidence_refs": { + "type": "array", + "items": { "type": "string" } + }, + "current_gap": { "type": "string" }, + "next_owner_action": { "type": "string" }, + "owner_response_required": { "const": true }, + "owner_response_received": { "const": false }, + "owner_response_accepted": { "const": false }, + "runtime_gate_open": { "const": false }, + "action_buttons_allowed": { "const": false } + } + } + } +} diff --git a/docs/schemas/iwooos_posture_projection_v1.schema.json b/docs/schemas/iwooos_posture_projection_v1.schema.json index 03dfb24b5..364eb2667 100644 --- a/docs/schemas/iwooos_posture_projection_v1.schema.json +++ b/docs/schemas/iwooos_posture_projection_v1.schema.json @@ -162,6 +162,20 @@ "rollout_risk_read_only_card_count", "rollout_risk_awoooi_rollout_risk", "rollout_risk_runtime_gate_count", + "high_value_config_control_coverage_first_layer", + "high_value_config_control_coverage_summary_count", + "high_value_config_control_coverage_item_count", + "high_value_config_control_coverage_category_count", + "high_value_config_control_coverage_c0_category_count", + "high_value_config_control_coverage_c1_category_count", + "high_value_config_control_coverage_average_percent", + "high_value_config_control_coverage_needs_live_evidence_count", + "high_value_config_control_coverage_owner_response_required_count", + "high_value_config_control_coverage_owner_response_received_count", + "high_value_config_control_coverage_owner_response_accepted_count", + "high_value_config_control_coverage_runtime_gate_count", + "high_value_config_control_coverage_action_button_count", + "high_value_config_control_coverage_lowest_category_count", "high_value_config_owner_packet_first_layer", "high_value_config_owner_packet_summary_count", "high_value_config_owner_packet_item_count", @@ -454,6 +468,62 @@ "type": "integer", "const": 0 }, + "high_value_config_control_coverage_first_layer": { + "type": "boolean", + "const": true + }, + "high_value_config_control_coverage_summary_count": { + "type": "integer", + "const": 4 + }, + "high_value_config_control_coverage_item_count": { + "type": "integer", + "const": 4 + }, + "high_value_config_control_coverage_category_count": { + "type": "integer", + "const": 14 + }, + "high_value_config_control_coverage_c0_category_count": { + "type": "integer", + "const": 8 + }, + "high_value_config_control_coverage_c1_category_count": { + "type": "integer", + "const": 4 + }, + "high_value_config_control_coverage_average_percent": { + "type": "integer", + "const": 64 + }, + "high_value_config_control_coverage_needs_live_evidence_count": { + "type": "integer", + "const": 6 + }, + "high_value_config_control_coverage_owner_response_required_count": { + "type": "integer", + "const": 14 + }, + "high_value_config_control_coverage_owner_response_received_count": { + "type": "integer", + "const": 0 + }, + "high_value_config_control_coverage_owner_response_accepted_count": { + "type": "integer", + "const": 0 + }, + "high_value_config_control_coverage_runtime_gate_count": { + "type": "integer", + "const": 0 + }, + "high_value_config_control_coverage_action_button_count": { + "type": "integer", + "const": 0 + }, + "high_value_config_control_coverage_lowest_category_count": { + "type": "integer", + "const": 4 + }, "high_value_config_owner_packet_first_layer": { "type": "boolean", "const": true diff --git a/docs/security/HIGH-VALUE-CONFIG-CONTROL-COVERAGE.md b/docs/security/HIGH-VALUE-CONFIG-CONTROL-COVERAGE.md new file mode 100644 index 000000000..885a48935 --- /dev/null +++ b/docs/security/HIGH-VALUE-CONFIG-CONTROL-COVERAGE.md @@ -0,0 +1,98 @@ +# IwoooS 高價值配置控管覆蓋矩陣 + +| 項目 | 內容 | +|------|------| +| 日期 | 2026-06-11 | +| 狀態 | `coverage_matrix_ready` | +| 工具 | `scripts/security/high-value-config-control-coverage.py` | +| Snapshot | `docs/security/high-value-config-control-coverage.snapshot.json` | +| Schema | `docs/schemas/high_value_config_control_coverage_v1.schema.json` | +| runtime gate | `0` | + +## 1. 目的 + +此矩陣把「所有重要配置都要被資安控管」從人讀清冊推進成可重跑 snapshot。它直接讀取 `high-value-config-change-gate.py` 的配置分類,避免變更 Gate 與長期覆蓋清冊各自漂移。 + +本階段仍是只讀覆蓋矩陣,不接 blocking CI、不 SSH、不讀 live host、不執行 `nginx -t`、不 reload Nginx、不做 DNS / TLS probe、不 renew cert、不同步 refs、不修改 workflow、不收 secret value、不啟動 agent-bounty runtime。 + +## 2. 覆蓋摘要 + +| 指標 | 目前值 | 說明 | +|------|--------|------| +| 註冊配置類別 | `14` | 全部來自高價值配置 Gate 的 CATEGORIES | +| C0 類別 | `8` | Nginx、DNS / TLS、K8s、secret、workflow / runner、runtime config、backup、agent-bounty runtime | +| C1 類別 | `4` | 監控、Docker / systemd、SSH / network、AI provider | +| C2 類別 | `1` | 產品 runtime route 與跨產品邊界 | +| C3 類別 | `1` | security evidence / snapshot / guard tooling | +| 平均只讀控管成熟度 | `64%` | 僅代表框架 / evidence / owner packet 準備度,不代表 runtime 可執行 | +| 需要 live evidence 的類別 | `6` | 只能等 owner-provided redacted evidence 或維護窗口,不主動修改主機 | +| owner response required | `14` | 每類都需要 owner response 才能往 accepted 前進 | +| owner response received / accepted | `0 / 0` | 不得假性提高 | +| runtime gate | `0` | 不得產生執行按鈕 | + +## 3. 最低覆蓋優先順序 + +| 優先 | 類別 | 目前成熟度 | 下一步 | +|------|------|------------|--------| +| P1-1 | Docker Compose / systemd / host service config | `42%` | 補 110 / 188 compose、systemd、port / volume / env 只讀 inventory、restart window、rollback owner | +| P1-2 | SSH / sudoers / known_hosts / firewall / WireGuard / NodePort | `48%` | 補 target whitelist、host key policy、ingress / egress matrix、network owner 與 rollback owner | +| P1-3 | Backup / restore / escrow / retention | `52%` | 補 restore drill approval package、offsite escrow owner、retention owner 與 no-secret-value evidence | +| P1-4 | Prometheus / Alertmanager / Grafana / SigNoz / Sentry / Langfuse | `56%` | 補 rule diff、receiver diff、reload owner、failure-only notification policy 與 route smoke | + +## 4. 固定 0 / false 邊界 + +以下旗標必須維持 `false`: + +```text +runtime_execution_authorized=false +host_write_authorized=false +nginx_reload_authorized=false +dns_tls_change_authorized=false +certbot_renew_authorized=false +argocd_sync_authorized=false +kubectl_action_authorized=false +workflow_modification_authorized=false +runner_change_authorized=false +refs_sync_authorized=false +force_push_authorized=false +secret_value_collection_allowed=false +active_scan_authorized=false +agent_bounty_runtime_authorized=false +payout_or_withdrawal_authorized=false +action_buttons_allowed=false +``` + +## 5. 判讀規則 + +1. `coverage_percent` 只代表只讀框架成熟度,不代表已收到 owner response。 +2. `coverage_status` 是下一步分流用語,不是 runtime approval state。 +3. C0 / C1 類別若缺 live evidence,只能等待 owner-provided redacted evidence、維護窗口與 rollback owner。 +4. `agent-bounty-protocol` 已是 C0 runtime / MCP / A2A / treasury boundary,但目前仍不得 claim / submit / payout / daemon / webhook / runtime execution。 +5. IwoooS 前端可顯示覆蓋矩陣,但不得提供可執行按鈕,也不得把可見狀態解讀成資安批准。 + +## 6. 指令 + +```bash +python3 scripts/security/high-value-config-control-coverage.py \ + --root . \ + --output docs/security/high-value-config-control-coverage.snapshot.json +``` + +固定 committed snapshot 時間: + +```bash +python3 scripts/security/high-value-config-control-coverage.py \ + --root . \ + --generated-at 2026-06-11T21:30:00+08:00 \ + --output docs/security/high-value-config-control-coverage.snapshot.json +``` + +## 7. 完成度 + +| 工作 | 完成度 | 說明 | +|------|--------|------| +| 全高價值配置類別註冊 | `100%` | 14 類全部來自既有 Gate 定義 | +| 覆蓋 snapshot / schema | `100%` | 已新增可重跑 snapshot 與 JSON schema | +| owner response 收件 | `0%` | 尚未收到或接受任何 owner response | +| live evidence collection | `0%` | 未 SSH、未 live probe、未 active scan | +| runtime gate | `0%` | 未開啟任何執行期閘門 | diff --git a/docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md b/docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md index 12c18340d..7f6c11816 100644 --- a/docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md +++ b/docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md @@ -6,6 +6,8 @@ | 狀態 | `inventory_and_classification_gate_ready` | | 範圍 | AWOOOI / IwoooS 全產品重要配置 | | 本階段模式 | source-control 修補 + 只讀盤點,不做 live reload / restart / sync | +| 覆蓋矩陣 | `docs/security/HIGH-VALUE-CONFIG-CONTROL-COVERAGE.md` | +| 覆蓋 snapshot | `docs/security/high-value-config-control-coverage.snapshot.json` | | runtime gate | `0` | ## 0. 核心結論 @@ -21,6 +23,22 @@ | C2 | 產品 runtime、admin、API、webhook、frontend build 或跨產品 route | 隨產品變更納管 | AWOOOI、AwoooP、IwoooS、VibeWork、agent-bounty-protocol、StockPlatform、Tsenyang、Bitan、VTuber | | C3 | 文件、runbook、template、snapshot 與證據索引 | 持續納管,避免範例變成可複製風險 | `SERVICE-ENDPOINTS.md`、DR runbook、owner response template | +### 0.1 2026-06-11 覆蓋矩陣狀態 + +`high_value_config_control_coverage_v1` 已把高價值配置控管從文字清冊推進成可重跑 snapshot。這份 snapshot 直接讀取 `scripts/security/high-value-config-change-gate.py` 的 `CATEGORIES`,避免長期清冊與變更 Gate 漂移。 + +| 指標 | 目前值 | 邊界 | +|------|--------|------| +| 註冊配置類別 | `14` | 代表已進 Gate 分類,不代表已批准 | +| C0 類別 | `8` | Nginx、DNS / TLS、K8s、secret、workflow / runner、runtime config、backup、agent-bounty runtime | +| C1 類別 | `4` | 監控、Docker / systemd、SSH / network、AI provider | +| 平均只讀控管成熟度 | `64%` | 只代表框架 / evidence / owner packet 準備度 | +| 需要 live evidence 類別 | `6` | 只能等 owner-provided redacted evidence 或維護窗口,不主動 SSH | +| owner response required | `14` | owner response received / accepted 仍 `0 / 0` | +| runtime gate | `0` | 不提供執行按鈕 | + +最低覆蓋優先順序為 Docker Compose / systemd、SSH / network、backup / restore、monitoring / alerting。這些是下一波 P1 只讀 inventory 的優先順序,不代表可以 restart、reload、scan 或收 secret value。 + ## 1. 目前已不符合新要求的項目 | 優先 | 項目 | 現況 | 風險 | 本階段處置 | @@ -132,6 +150,7 @@ Nginx 是目前必須最先資安控管的配置,原因是它同時控制公 | owner response evidence JSON 欄位檢查 | `70%` | Gate 可檢查必要欄位與 false flags;尚未接正式收件 API 或 AwoooP queue | | Gate → owner response packet 草案 | `100%` | 已新增 `scripts/security/high-value-config-owner-packet.py`,可將 impacted category 轉成 canonical owner response packet 草案 | | canonical owner 欄位對齊 | `100%` | 高價值配置 Gate 已對齊 S4.9 `owner_role_or_team`,並保留 `owner_role_team` 等 alias 支援 | +| 全域配置覆蓋矩陣 | `100%` | 已新增 `scripts/security/high-value-config-control-coverage.py`、snapshot 與 schema,14 類高價值配置可重跑檢查 | | owner packet 前台只讀接入 | `100%` | `/zh-TW/iwooos` 已顯示高價值配置 owner packet 草案、C0/C1 packet 數、request / received / accepted 仍為 0 與禁止執行邊界 | | owner response request / received / accepted | `0%` | Packet 只是草案;尚未送件、尚未收件、尚未 reviewer accepted | | CI blocking / workflow gate | `0%` | 本階段刻意不修改 `.gitea/workflows`,避免初期資安流程摩擦過大 | diff --git a/docs/security/IWOOOS-POSTURE-PROJECTION.md b/docs/security/IWOOOS-POSTURE-PROJECTION.md index 7bd0c19f4..54e50f7ce 100644 --- a/docs/security/IWOOOS-POSTURE-PROJECTION.md +++ b/docs/security/IWOOOS-POSTURE-PROJECTION.md @@ -89,6 +89,7 @@ IwoooS 首版只讀取或對齊以下已提交 evidence: 51. 10 個 frontend surface reverse bridge statuses,顯示既有資安入口目前是 embedded bridge、direct bridge 或 AwoooP read-only candidate;這只是連接狀態,不代表 owner response、runtime authorization、Code Review blocker、Gitea/GitHub action 或任何執行控制。 52. 6 個 source control primary readiness items,顯示 GitHub primary 前置缺口:candidate repo inventory、primary ready counter、owner response validation、refs truth、workflow / secret name inventory、rollback ADR;這只是 readiness,不代表 repo 建立、visibility 變更、refs mutation、secret value collection、primary switch 或 Gitea 停用。 53. 4 個 rollout risk read-only items,顯示風險來源部署 marker、`AWOOOI_ROLLOUT_RISK=1`、ArgoCD `Degraded` / `OutOfSync`、API health / smoke 已通過與執行期閘門仍為 0;這只是部署風險可見性,不代表 ArgoCD sync、kubectl、主機重啟、修復、部署或 runtime gate 已授權。 +54. 14 類 high-value config control coverage statuses,顯示 Nginx、DNS / TLS、K8s、secret、workflow、backup、agent-bounty runtime、monitoring、Docker / systemd、SSH / network、AI provider、產品 route 與 security evidence 的全域配置控管覆蓋矩陣;平均只讀成熟度 `64%`、C0 類別 `8`、需 live evidence 類別 `6`、owner response received / accepted 與 runtime gate 仍為 `0`,不代表 reload、sync、scan、secret rotation、payout 或主機操作授權。 ## 3.1 既有前端資安頁面整合 diff --git a/docs/security/high-value-config-control-coverage.snapshot.json b/docs/security/high-value-config-control-coverage.snapshot.json new file mode 100644 index 000000000..188422f61 --- /dev/null +++ b/docs/security/high-value-config-control-coverage.snapshot.json @@ -0,0 +1,588 @@ +{ + "coverage_categories": [ + { + "action_buttons_allowed": false, + "category_id": "nginx_public_gateway", + "control_tier": "C0", + "coverage_percent": 78, + "coverage_status": "repo_only_drift_detector_ready", + "current_gap": "缺 owner-provided live conf 匯出與 approved maintenance window;不得 SSH 擷取或 reload。", + "evidence_refs": [ + "docs/security/NGINX-CONFIG-DRIFT-DETECTOR.md", + "docs/security/nginx-config-drift-repo.snapshot.json", + "docs/security/DOMAIN-TLS-CERTBOT-INVENTORY.md" + ], + "label": "Nginx / reverse proxy / public route", + "next_owner_action": "提供脫敏 live conf 匯出檔或確認維護窗口、rollback owner 與 route smoke plan。", + "owner_response_accepted": false, + "owner_response_received": false, + "owner_response_required": true, + "patterns": [ + "infra/ansible/roles/nginx/templates/*.j2", + "infra/ansible/playbooks/nginx-sync.yml", + "ops/nginx/**", + "docs/runbooks/disaster-recovery/DR-Nginx.md" + ], + "priority": "P0", + "required_gate": "public_gateway_owner_response_required", + "required_validation": [ + "rendered_diff", + "nginx_t", + "affected_route_smoke", + "admin_route_smoke_if_affected", + "acme_path_smoke_if_affected", + "rollback_ref" + ], + "runtime_gate_open": false + }, + { + "action_buttons_allowed": false, + "category_id": "dns_tls_certbot", + "control_tier": "C0", + "coverage_percent": 74, + "coverage_status": "repo_only_inventory_ready", + "current_gap": "4 個 certificate path 關係需 owner 確認;live DNS / TLS probe 未執行。", + "evidence_refs": [ + "docs/security/DOMAIN-TLS-CERTBOT-INVENTORY.md", + "docs/security/domain-tls-certbot-inventory.snapshot.json" + ], + "label": "DNS / TLS / certbot / certificate path", + "next_owner_action": "確認 SAN / 共用憑證關係、renewal owner、ACME smoke 與 rollback owner。", + "owner_response_accepted": false, + "owner_response_received": false, + "owner_response_required": true, + "patterns": [ + "docs/runbooks/REGISTRY-CERTBOT-188.md", + "docs/runbooks/**/*CERTBOT*.md", + "docs/runbooks/**/*TLS*.md", + "ops/**/*cert*", + "ops/**/*tls*", + "infra/**/*cert*", + "infra/**/*tls*", + "k8s/**/*tls*" + ], + "priority": "P0", + "required_gate": "domain_tls_owner_response_required", + "required_validation": [ + "domain_inventory", + "certificate_path_check", + "renewal_window", + "acme_path_smoke", + "public_https_smoke", + "rollback_ref" + ], + "runtime_gate_open": false + }, + { + "action_buttons_allowed": false, + "category_id": "k8s_production_gitops", + "control_tier": "C0", + "coverage_percent": 58, + "coverage_status": "gate_defined_needs_runtime_evidence", + "current_gap": "尚未把 ArgoCD health / sync readback 與 rollback revision 收成 owner packet。", + "evidence_refs": [ + "docs/security/HIGH-VALUE-CONFIG-CHANGE-GATE.md", + "k8s/awoooi-prod", + "k8s/argocd" + ], + "label": "K8s / ArgoCD / production manifests", + "next_owner_action": "補 GitOps owner、rollback revision、health readback 與 post-deploy validation plan。", + "owner_response_accepted": false, + "owner_response_received": false, + "owner_response_required": true, + "patterns": [ + "k8s/awoooi-prod/**", + "k8s/argocd/**", + "k8s/velero/**", + "k8s/monitoring/**" + ], + "priority": "P0", + "required_gate": "gitops_owner_response_required", + "required_validation": [ + "gitops_diff", + "argocd_health_readback", + "sync_authorization_check", + "rollback_revision", + "post_deploy_health_if_executed" + ], + "runtime_gate_open": false + }, + { + "action_buttons_allowed": false, + "category_id": "secret_metadata", + "control_tier": "C0", + "coverage_percent": 66, + "coverage_status": "metadata_policy_ready", + "current_gap": "只允許 secret name / metadata;仍缺 owner response 與 parity acceptance。", + "evidence_refs": [ + "docs/security/SOURCE-CONTROL-WORKFLOW-SECRET-NAME-INVENTORY.md", + "docs/security/source-control-workflow-secret-name-inventory.snapshot.json", + "docs/security/SECRETS_REFERENCE.md" + ], + "label": "Secret metadata / injection / redaction", + "next_owner_action": "只回覆 secret name owner、rotation owner、injection owner 與 redacted evidence refs。", + "owner_response_accepted": false, + "owner_response_received": false, + "owner_response_required": true, + "patterns": [ + "k8s/**/*secret*", + "k8s/**/*Secret*", + ".gitea/workflows/*.yml", + ".gitea/workflows/*.yaml", + ".github/workflows/*.yml", + ".github/workflows/*.yaml", + "docs/runbooks/SECRETS-MANAGEMENT.md", + "docs/security/SECRETS_REFERENCE.md" + ], + "priority": "P0", + "required_gate": "secret_metadata_owner_response_required", + "required_validation": [ + "secret_name_parity", + "metadata_only_check", + "no_secret_value_check", + "rotation_owner", + "injection_readback_if_deployed" + ], + "runtime_gate_open": false + }, + { + "action_buttons_allowed": false, + "category_id": "gitea_workflow_runner_source_control", + "control_tier": "C0", + "coverage_percent": 70, + "coverage_status": "metadata_inventory_ready", + "current_gap": "workflow / runner / deploy key / webhook / branch protection 仍待 owner response;不得改 workflow。", + "evidence_refs": [ + "docs/security/SOURCE-CONTROL-WORKFLOW-SECRET-NAME-INVENTORY.md", + "docs/security/SOURCE-CONTROL-WORKFLOW-SECRET-NAME-OWNER-RESPONSE.md", + "docs/security/SOURCE-CONTROL-PRIMARY-READINESS-GATE.md" + ], + "label": "Gitea workflow / runner / deploy key / webhook / branch protection", + "next_owner_action": "補 runner label、webhook、deploy key、branch protection 與 workflow parity owner metadata。", + "owner_response_accepted": false, + "owner_response_received": false, + "owner_response_required": true, + "patterns": [ + ".gitea/workflows/**", + ".github/workflows/**", + "ops/runner/**", + "scripts/setup-runner*.sh", + "scripts/**/*runner*", + "docs/security/SOURCE-CONTROL-*", + "docs/security/GITEA-*", + "docs/security/GITHUB-*" + ], + "priority": "P0", + "required_gate": "workflow_source_control_owner_response_required", + "required_validation": [ + "workflow_diff", + "runner_label_owner", + "deploy_key_metadata_only", + "webhook_metadata_only", + "branch_protection_metadata", + "no_token_value_check" + ], + "runtime_gate_open": false + }, + { + "action_buttons_allowed": false, + "category_id": "public_admin_api_runtime_config", + "control_tier": "C0", + "coverage_percent": 62, + "coverage_status": "policy_ready_needs_change_scoped_smoke", + "current_gap": "每次產品 route / admin / API / frontend config 變更仍需逐次 smoke 與 owner gate。", + "evidence_refs": [ + "docs/HARD_RULES.md", + "docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md" + ], + "label": "Public / admin / API / frontend runtime config", + "next_owner_action": "補 affected route、admin/auth boundary、CORS/public URL 與 desktop/mobile smoke plan。", + "owner_response_accepted": false, + "owner_response_received": false, + "owner_response_required": true, + "patterns": [ + "apps/web/next.config.*", + "apps/web/src/lib/config.*", + "apps/api/src/core/config.py", + "apps/api/src/api/v1/monitoring.py", + "apps/api/src/middleware/**", + "apps/web/src/middleware.*" + ], + "priority": "P0", + "required_gate": "public_runtime_config_owner_response_required", + "required_validation": [ + "public_url_check", + "frontend_internal_ip_ban", + "cors_boundary_check", + "admin_auth_boundary_check", + "desktop_mobile_smoke_if_frontend" + ], + "runtime_gate_open": false + }, + { + "action_buttons_allowed": false, + "category_id": "backup_restore_credential", + "control_tier": "C0", + "coverage_percent": 52, + "coverage_status": "policy_defined_needs_restore_drill_owner", + "current_gap": "restore drill、retention、offsite escrow 與 credential owner 尚未形成 S4 owner response。", + "evidence_refs": [ + "docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md", + "docs/runbooks/disaster-recovery" + ], + "label": "Backup / restore / escrow / retention", + "next_owner_action": "補 restore drill approval package、escrow owner、retention owner 與 no-secret-value evidence。", + "owner_response_accepted": false, + "owner_response_received": false, + "owner_response_required": true, + "patterns": [ + "scripts/backup/**", + "k8s/velero/**", + "docs/runbooks/disaster-recovery/**", + "docs/runbooks/**/*RESTORE*.md", + "docs/runbooks/**/*BACKUP*.md" + ], + "priority": "P0", + "required_gate": "backup_restore_owner_response_required", + "required_validation": [ + "credential_absence_check", + "restore_drill_gate", + "retention_policy", + "escrow_owner", + "rollback_ref" + ], + "runtime_gate_open": false + }, + { + "action_buttons_allowed": false, + "category_id": "agent_bounty_protocol_runtime", + "control_tier": "C0", + "coverage_percent": 68, + "coverage_status": "onboarding_handoff_ready_needs_runtime_owner", + "current_gap": "尚未收到 runtime / MCP / A2A / treasury / payout owner response;runtime gate 必須維持 0。", + "evidence_refs": [ + "docs/security/AGENT-BOUNTY-IWOOOS-ONBOARDING-HANDOFF.md", + "docs/security/agent-bounty-iwooos-onboarding-handoff.snapshot.json", + "docs/security/GITHUB-TARGET-OWNER-DECISION-RESPONSE.md" + ], + "label": "agent-bounty-protocol runtime / MCP / A2A / treasury boundary", + "next_owner_action": "補 repo owner、external agent owner、treasury owner、runtime gate owner 與 validation plan。", + "owner_response_accepted": false, + "owner_response_received": false, + "owner_response_required": true, + "patterns": [ + "docs/security/AGENT-BOUNTY-IWOOOS-ONBOARDING-HANDOFF.md", + "docs/security/agent-bounty-iwooos-onboarding-handoff.snapshot.json", + "docs/schemas/agent_bounty_iwooos_onboarding_handoff_v1.schema.json", + "agent-bounty-protocol/**" + ], + "priority": "P0", + "required_gate": "agent_bounty_owner_response_required", + "required_validation": [ + "repo_owner_scope", + "runtime_gate_false", + "no_payout_or_treasury_execution", + "no_mcp_a2a_runtime_execution", + "redacted_evidence_refs_only" + ], + "runtime_gate_open": false + }, + { + "action_buttons_allowed": false, + "category_id": "monitoring_alerting_observability", + "control_tier": "C1", + "coverage_percent": 56, + "coverage_status": "policy_ready_needs_drift_evidence", + "current_gap": "Prometheus / Alertmanager / Grafana / SigNoz / Sentry / Langfuse 設定 drift 尚未集中收證。", + "evidence_refs": [ + "docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md", + "docs/security/SERVICE-HEALTH-FAILURE-NOTIFICATION-POLICY.md" + ], + "label": "Prometheus / Alertmanager / Grafana / SigNoz / Sentry / Langfuse", + "next_owner_action": "補 rule diff、receiver diff、reload owner、failure-only notification policy 與 route smoke。", + "owner_response_accepted": false, + "owner_response_received": false, + "owner_response_required": true, + "patterns": [ + "ops/monitoring/**", + "ops/alertmanager/**", + "ops/grafana/**", + "ops/signoz/**", + "ops/sentry-self-hosted/**", + "infra/langfuse/**", + "k8s/monitoring/**" + ], + "priority": "P1", + "required_gate": "monitoring_observability_owner_response_required", + "required_validation": [ + "rule_diff", + "receiver_diff", + "reload_gate", + "failure_notification_policy", + "public_route_smoke_if_affected" + ], + "runtime_gate_open": false + }, + { + "action_buttons_allowed": false, + "category_id": "docker_compose_systemd_host_config", + "control_tier": "C1", + "coverage_percent": 42, + "coverage_status": "inventory_needed", + "current_gap": "110 / 188 Docker Compose、systemd、port / volume / env 差異仍需只讀 inventory。", + "evidence_refs": [ + "docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md", + "docs/security/DEV-HOSTS-112-111-168-OBSERVE-ONLY-MAPPING.md" + ], + "label": "Docker Compose / systemd / host service config", + "next_owner_action": "補 compose / systemd owner、restart window、rollback owner 與 post-check 指標。", + "owner_response_accepted": false, + "owner_response_received": false, + "owner_response_required": true, + "patterns": [ + "docker-compose*.yml", + "docker-compose*.yaml", + "ops/**/docker-compose*.yml", + "ops/**/docker-compose*.yaml", + "scripts/reboot-recovery/**", + "scripts/**/*.service", + "ops/**/*.service" + ], + "priority": "P1", + "required_gate": "host_service_owner_response_required", + "required_validation": [ + "port_conflict_check", + "volume_diff", + "env_name_diff", + "restart_window", + "rollback_owner" + ], + "runtime_gate_open": false + }, + { + "action_buttons_allowed": false, + "category_id": "ssh_firewall_network_access", + "control_tier": "C1", + "coverage_percent": 48, + "coverage_status": "policy_ready_needs_network_matrix", + "current_gap": "尚未集中 ingress / egress、WireGuard、NodePort、firewall 與 known_hosts 矩陣。", + "evidence_refs": [ + "docs/HARD_RULES.md", + "docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md" + ], + "label": "SSH / sudoers / known_hosts / firewall / WireGuard / NodePort", + "next_owner_action": "補 target whitelist、host key policy、network owner、maintenance window 與 rollback owner。", + "owner_response_accepted": false, + "owner_response_received": false, + "owner_response_required": true, + "patterns": [ + "infra/ansible/inventory/**", + "infra/ansible/**/*known_hosts*", + "infra/ansible/**/*ssh*", + "scripts/**/*ssh*", + "scripts/**/*known_hosts*", + "ops/**/*wireguard*", + "ops/**/*firewall*", + "k8s/**/*network*", + "k8s/**/*Network*" + ], + "priority": "P1", + "required_gate": "network_access_owner_response_required", + "required_validation": [ + "target_whitelist", + "host_key_policy", + "ingress_egress_matrix", + "rollback_owner", + "maintenance_window" + ], + "runtime_gate_open": false + }, + { + "action_buttons_allowed": false, + "category_id": "ai_provider_model_routing", + "control_tier": "C1", + "coverage_percent": 60, + "coverage_status": "policy_ready_needs_dry_run_pack", + "current_gap": "模型 / provider / Ollama proxy 切換需 dry-run、benchmark、成本與 privacy review;目前不切 production。", + "evidence_refs": [ + "docs/HARD_RULES.md", + "docs/ai" + ], + "label": "AI provider / model routing / Ollama proxy / cost and privacy", + "next_owner_action": "補 provider owner、fallback order、cost review、privacy review、benchmark 與 rollback owner。", + "owner_response_accepted": false, + "owner_response_received": false, + "owner_response_required": true, + "patterns": [ + "apps/api/src/services/ai_providers/**", + "apps/api/src/services/**/*model*", + "apps/api/src/services/**/*provider*", + "infra/ansible/roles/nginx/templates/110-ollama-proxy.conf.j2", + "docs/ai/**", + "docs/**/*Ollama*" + ], + "priority": "P1", + "required_gate": "ai_provider_owner_response_required", + "required_validation": [ + "dry_run", + "benchmark", + "cost_review", + "privacy_review", + "fallback_order_check" + ], + "runtime_gate_open": false + }, + { + "action_buttons_allowed": false, + "category_id": "product_surface_runtime_routes", + "control_tier": "C2", + "coverage_percent": 72, + "coverage_status": "scope_inventory_ready", + "current_gap": "跨產品 owner response 尚未 accepted;產品 route / admin / webhook 仍需逐產品補證。", + "evidence_refs": [ + "docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md", + "docs/security/VIBEWORK-IWOOOS-ONBOARDING-HANDOFF.md", + "docs/security/AGENT-BOUNTY-IWOOOS-ONBOARDING-HANDOFF.md" + ], + "label": "AWOOOI / AwoooP / IwoooS / VibeWork / other product runtime routes", + "next_owner_action": "補 AWOOOI、AwoooP、IwoooS、VibeWork、agent-bounty-protocol 與公開網站 owner response。", + "owner_response_accepted": false, + "owner_response_received": false, + "owner_response_required": true, + "patterns": [ + "apps/web/src/app/**", + "apps/web/messages/*.json", + "docs/security/VIBEWORK-IWOOOS-ONBOARDING-HANDOFF.md", + "docs/security/vibework-iwooos-onboarding-handoff.snapshot.json" + ], + "priority": "P2", + "required_gate": "product_surface_owner_response_required", + "required_validation": [ + "product_boundary_check", + "i18n_traditional_chinese_check", + "no_internal_transcript_check", + "desktop_mobile_smoke_if_frontend" + ], + "runtime_gate_open": false + }, + { + "action_buttons_allowed": false, + "category_id": "security_evidence_tooling", + "control_tier": "C3", + "coverage_percent": 86, + "coverage_status": "guard_ready", + "current_gap": "guard 已可重跑,但尚未接 blocking CI;本階段刻意維持低摩擦。", + "evidence_refs": [ + "scripts/security/security-mirror-progress-guard.py", + "scripts/security/high-value-config-change-gate.py", + "scripts/security/high-value-config-owner-packet.py", + "docs/security/high-value-config-change-gate.snapshot.json" + ], + "label": "Security evidence / snapshot / guard tooling", + "next_owner_action": "維持 guard / doc secret sanity;若要 CI blocking 需另開人工批准與 rollout plan。", + "owner_response_accepted": false, + "owner_response_received": false, + "owner_response_required": true, + "patterns": [ + "docs/security/**", + "docs/schemas/**", + "scripts/security/**", + "docs/LOGBOOK.md" + ], + "priority": "P3", + "required_gate": "security_evidence_owner_review_required", + "required_validation": [ + "snapshot_parse", + "guard_smoke", + "doc_secret_sanity", + "no_runtime_gate_increase" + ], + "runtime_gate_open": false + } + ], + "execution_boundaries": { + "action_buttons_allowed": false, + "active_scan_authorized": false, + "agent_bounty_runtime_authorized": false, + "argocd_sync_authorized": false, + "certbot_renew_authorized": false, + "dns_tls_change_authorized": false, + "force_push_authorized": false, + "host_write_authorized": false, + "kubectl_action_authorized": false, + "nginx_reload_authorized": false, + "payout_or_withdrawal_authorized": false, + "refs_sync_authorized": false, + "runner_change_authorized": false, + "runtime_execution_authorized": false, + "secret_value_collection_allowed": false, + "workflow_modification_authorized": false + }, + "generated_at": "2026-06-11T21:30:00+08:00", + "git_commit": "d448ae36", + "lowest_coverage_categories": [ + { + "category_id": "docker_compose_systemd_host_config", + "coverage_percent": 42, + "current_gap": "110 / 188 Docker Compose、systemd、port / volume / env 差異仍需只讀 inventory。", + "label": "Docker Compose / systemd / host service config", + "next_owner_action": "補 compose / systemd owner、restart window、rollback owner 與 post-check 指標。" + }, + { + "category_id": "ssh_firewall_network_access", + "coverage_percent": 48, + "current_gap": "尚未集中 ingress / egress、WireGuard、NodePort、firewall 與 known_hosts 矩陣。", + "label": "SSH / sudoers / known_hosts / firewall / WireGuard / NodePort", + "next_owner_action": "補 target whitelist、host key policy、network owner、maintenance window 與 rollback owner。" + }, + { + "category_id": "backup_restore_credential", + "coverage_percent": 52, + "current_gap": "restore drill、retention、offsite escrow 與 credential owner 尚未形成 S4 owner response。", + "label": "Backup / restore / escrow / retention", + "next_owner_action": "補 restore drill approval package、escrow owner、retention owner 與 no-secret-value evidence。" + }, + { + "category_id": "monitoring_alerting_observability", + "coverage_percent": 56, + "current_gap": "Prometheus / Alertmanager / Grafana / SigNoz / Sentry / Langfuse 設定 drift 尚未集中收證。", + "label": "Prometheus / Alertmanager / Grafana / SigNoz / Sentry / Langfuse", + "next_owner_action": "補 rule diff、receiver diff、reload owner、failure-only notification policy 與 route smoke。" + } + ], + "next_collection_order": [ + "nginx_public_gateway", + "dns_tls_certbot", + "secret_metadata", + "gitea_workflow_runner_source_control", + "agent_bounty_protocol_runtime", + "docker_compose_systemd_host_config", + "monitoring_alerting_observability", + "ssh_firewall_network_access" + ], + "operator_interpretation": [ + "這是全域配置控管覆蓋矩陣,不是單次 git diff 變更分類。", + "所有 category 都已有高價值配置 Gate 註冊與 owner response 欄位,但 owner response received / accepted 仍為 0。", + "C0 / C1 coverage percent 只代表只讀框架成熟度,不代表 runtime 可執行。", + "缺 live evidence 的項目只能收 owner-provided redacted evidence,不得主動 SSH、reload、scan 或讀 secret value。" + ], + "schema_version": "high_value_config_control_coverage_v1", + "source_category_definition": "scripts/security/high-value-config-change-gate.py", + "status": "coverage_matrix_ready", + "summary": { + "action_button_count": 0, + "average_coverage_percent": 64, + "c0_category_count": 8, + "c1_category_count": 4, + "c2_category_count": 1, + "c3_category_count": 1, + "category_count": 14, + "lowest_coverage_category_count": 4, + "needs_live_evidence_count": 6, + "owner_response_accepted_count": 0, + "owner_response_received_count": 0, + "owner_response_required_count": 14, + "registered_control_count": 14, + "runtime_gate_count": 0 + } +} diff --git a/docs/security/iwooos-posture-projection.snapshot.json b/docs/security/iwooos-posture-projection.snapshot.json index b909cbd52..76584a29e 100644 --- a/docs/security/iwooos-posture-projection.snapshot.json +++ b/docs/security/iwooos-posture-projection.snapshot.json @@ -227,6 +227,20 @@ "rollout_risk_playwright_smoke_passed": true, "rollout_risk_requires_read_only_triage": true, "rollout_risk_runtime_gate_count": 0, + "high_value_config_control_coverage_first_layer": true, + "high_value_config_control_coverage_summary_count": 4, + "high_value_config_control_coverage_item_count": 4, + "high_value_config_control_coverage_category_count": 14, + "high_value_config_control_coverage_c0_category_count": 8, + "high_value_config_control_coverage_c1_category_count": 4, + "high_value_config_control_coverage_average_percent": 64, + "high_value_config_control_coverage_needs_live_evidence_count": 6, + "high_value_config_control_coverage_owner_response_required_count": 14, + "high_value_config_control_coverage_owner_response_received_count": 0, + "high_value_config_control_coverage_owner_response_accepted_count": 0, + "high_value_config_control_coverage_runtime_gate_count": 0, + "high_value_config_control_coverage_action_button_count": 0, + "high_value_config_control_coverage_lowest_category_count": 4, "high_value_config_owner_packet_first_layer": true, "high_value_config_owner_packet_summary_count": 4, "high_value_config_owner_packet_item_count": 6, diff --git a/scripts/security/high-value-config-control-coverage.py b/scripts/security/high-value-config-control-coverage.py new file mode 100644 index 000000000..a60f44372 --- /dev/null +++ b/scripts/security/high-value-config-control-coverage.py @@ -0,0 +1,348 @@ +#!/usr/bin/env python3 +""" +IwoooS 高價值配置控管覆蓋矩陣。 + +本工具讀取 high-value-config-change-gate.py 的 CATEGORIES 定義,產生一份 +全域配置控管覆蓋 snapshot。它只整理 source-of-truth、owner gate、 +evidence refs、缺口與 0/false 邊界,不讀 live host、不連外、不執行 +Nginx / DNS / K8s / workflow / agent runtime 動作。 +""" + +from __future__ import annotations + +import argparse +import json +import runpy +import subprocess +import sys +from datetime import datetime, timedelta, timezone +from pathlib import Path +from typing import Any + + +TAIPEI = timezone(timedelta(hours=8)) + +CONTROL_STATUS_BY_CATEGORY = { + "nginx_public_gateway": { + "coverage_status": "repo_only_drift_detector_ready", + "coverage_percent": 78, + "evidence_refs": [ + "docs/security/NGINX-CONFIG-DRIFT-DETECTOR.md", + "docs/security/nginx-config-drift-repo.snapshot.json", + "docs/security/DOMAIN-TLS-CERTBOT-INVENTORY.md", + ], + "current_gap": "缺 owner-provided live conf 匯出與 approved maintenance window;不得 SSH 擷取或 reload。", + "next_owner_action": "提供脫敏 live conf 匯出檔或確認維護窗口、rollback owner 與 route smoke plan。", + }, + "dns_tls_certbot": { + "coverage_status": "repo_only_inventory_ready", + "coverage_percent": 74, + "evidence_refs": [ + "docs/security/DOMAIN-TLS-CERTBOT-INVENTORY.md", + "docs/security/domain-tls-certbot-inventory.snapshot.json", + ], + "current_gap": "4 個 certificate path 關係需 owner 確認;live DNS / TLS probe 未執行。", + "next_owner_action": "確認 SAN / 共用憑證關係、renewal owner、ACME smoke 與 rollback owner。", + }, + "k8s_production_gitops": { + "coverage_status": "gate_defined_needs_runtime_evidence", + "coverage_percent": 58, + "evidence_refs": [ + "docs/security/HIGH-VALUE-CONFIG-CHANGE-GATE.md", + "k8s/awoooi-prod", + "k8s/argocd", + ], + "current_gap": "尚未把 ArgoCD health / sync readback 與 rollback revision 收成 owner packet。", + "next_owner_action": "補 GitOps owner、rollback revision、health readback 與 post-deploy validation plan。", + }, + "secret_metadata": { + "coverage_status": "metadata_policy_ready", + "coverage_percent": 66, + "evidence_refs": [ + "docs/security/SOURCE-CONTROL-WORKFLOW-SECRET-NAME-INVENTORY.md", + "docs/security/source-control-workflow-secret-name-inventory.snapshot.json", + "docs/security/SECRETS_REFERENCE.md", + ], + "current_gap": "只允許 secret name / metadata;仍缺 owner response 與 parity acceptance。", + "next_owner_action": "只回覆 secret name owner、rotation owner、injection owner 與 redacted evidence refs。", + }, + "gitea_workflow_runner_source_control": { + "coverage_status": "metadata_inventory_ready", + "coverage_percent": 70, + "evidence_refs": [ + "docs/security/SOURCE-CONTROL-WORKFLOW-SECRET-NAME-INVENTORY.md", + "docs/security/SOURCE-CONTROL-WORKFLOW-SECRET-NAME-OWNER-RESPONSE.md", + "docs/security/SOURCE-CONTROL-PRIMARY-READINESS-GATE.md", + ], + "current_gap": "workflow / runner / deploy key / webhook / branch protection 仍待 owner response;不得改 workflow。", + "next_owner_action": "補 runner label、webhook、deploy key、branch protection 與 workflow parity owner metadata。", + }, + "public_admin_api_runtime_config": { + "coverage_status": "policy_ready_needs_change_scoped_smoke", + "coverage_percent": 62, + "evidence_refs": [ + "docs/HARD_RULES.md", + "docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md", + ], + "current_gap": "每次產品 route / admin / API / frontend config 變更仍需逐次 smoke 與 owner gate。", + "next_owner_action": "補 affected route、admin/auth boundary、CORS/public URL 與 desktop/mobile smoke plan。", + }, + "backup_restore_credential": { + "coverage_status": "policy_defined_needs_restore_drill_owner", + "coverage_percent": 52, + "evidence_refs": [ + "docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md", + "docs/runbooks/disaster-recovery", + ], + "current_gap": "restore drill、retention、offsite escrow 與 credential owner 尚未形成 S4 owner response。", + "next_owner_action": "補 restore drill approval package、escrow owner、retention owner 與 no-secret-value evidence。", + }, + "agent_bounty_protocol_runtime": { + "coverage_status": "onboarding_handoff_ready_needs_runtime_owner", + "coverage_percent": 68, + "evidence_refs": [ + "docs/security/AGENT-BOUNTY-IWOOOS-ONBOARDING-HANDOFF.md", + "docs/security/agent-bounty-iwooos-onboarding-handoff.snapshot.json", + "docs/security/GITHUB-TARGET-OWNER-DECISION-RESPONSE.md", + ], + "current_gap": "尚未收到 runtime / MCP / A2A / treasury / payout owner response;runtime gate 必須維持 0。", + "next_owner_action": "補 repo owner、external agent owner、treasury owner、runtime gate owner 與 validation plan。", + }, + "monitoring_alerting_observability": { + "coverage_status": "policy_ready_needs_drift_evidence", + "coverage_percent": 56, + "evidence_refs": [ + "docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md", + "docs/security/SERVICE-HEALTH-FAILURE-NOTIFICATION-POLICY.md", + ], + "current_gap": "Prometheus / Alertmanager / Grafana / SigNoz / Sentry / Langfuse 設定 drift 尚未集中收證。", + "next_owner_action": "補 rule diff、receiver diff、reload owner、failure-only notification policy 與 route smoke。", + }, + "docker_compose_systemd_host_config": { + "coverage_status": "inventory_needed", + "coverage_percent": 42, + "evidence_refs": [ + "docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md", + "docs/security/DEV-HOSTS-112-111-168-OBSERVE-ONLY-MAPPING.md", + ], + "current_gap": "110 / 188 Docker Compose、systemd、port / volume / env 差異仍需只讀 inventory。", + "next_owner_action": "補 compose / systemd owner、restart window、rollback owner 與 post-check 指標。", + }, + "ssh_firewall_network_access": { + "coverage_status": "policy_ready_needs_network_matrix", + "coverage_percent": 48, + "evidence_refs": [ + "docs/HARD_RULES.md", + "docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md", + ], + "current_gap": "尚未集中 ingress / egress、WireGuard、NodePort、firewall 與 known_hosts 矩陣。", + "next_owner_action": "補 target whitelist、host key policy、network owner、maintenance window 與 rollback owner。", + }, + "ai_provider_model_routing": { + "coverage_status": "policy_ready_needs_dry_run_pack", + "coverage_percent": 60, + "evidence_refs": [ + "docs/HARD_RULES.md", + "docs/ai", + ], + "current_gap": "模型 / provider / Ollama proxy 切換需 dry-run、benchmark、成本與 privacy review;目前不切 production。", + "next_owner_action": "補 provider owner、fallback order、cost review、privacy review、benchmark 與 rollback owner。", + }, + "product_surface_runtime_routes": { + "coverage_status": "scope_inventory_ready", + "coverage_percent": 72, + "evidence_refs": [ + "docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md", + "docs/security/VIBEWORK-IWOOOS-ONBOARDING-HANDOFF.md", + "docs/security/AGENT-BOUNTY-IWOOOS-ONBOARDING-HANDOFF.md", + ], + "current_gap": "跨產品 owner response 尚未 accepted;產品 route / admin / webhook 仍需逐產品補證。", + "next_owner_action": "補 AWOOOI、AwoooP、IwoooS、VibeWork、agent-bounty-protocol 與公開網站 owner response。", + }, + "security_evidence_tooling": { + "coverage_status": "guard_ready", + "coverage_percent": 86, + "evidence_refs": [ + "scripts/security/security-mirror-progress-guard.py", + "scripts/security/high-value-config-change-gate.py", + "scripts/security/high-value-config-owner-packet.py", + "docs/security/high-value-config-change-gate.snapshot.json", + ], + "current_gap": "guard 已可重跑,但尚未接 blocking CI;本階段刻意維持低摩擦。", + "next_owner_action": "維持 guard / doc secret sanity;若要 CI blocking 需另開人工批准與 rollout plan。", + }, +} + +FALSE_BOUNDARIES = { + "runtime_execution_authorized": False, + "host_write_authorized": False, + "nginx_reload_authorized": False, + "dns_tls_change_authorized": False, + "certbot_renew_authorized": False, + "argocd_sync_authorized": False, + "kubectl_action_authorized": False, + "workflow_modification_authorized": False, + "runner_change_authorized": False, + "refs_sync_authorized": False, + "force_push_authorized": False, + "secret_value_collection_allowed": False, + "active_scan_authorized": False, + "agent_bounty_runtime_authorized": False, + "payout_or_withdrawal_authorized": False, + "action_buttons_allowed": False, +} + + +def git_short_sha(root: Path) -> str: + try: + result = subprocess.run( + ["git", "rev-parse", "--short", "HEAD"], + cwd=root, + check=True, + capture_output=True, + text=True, + ) + return result.stdout.strip() + except Exception: + return "unknown" + + +def load_gate_categories(root: Path) -> list[Any]: + gate_path = root / "scripts" / "security" / "high-value-config-change-gate.py" + namespace = runpy.run_path(str(gate_path)) + return list(namespace["CATEGORIES"]) + + +def category_to_dict(category: Any) -> dict[str, Any]: + status = CONTROL_STATUS_BY_CATEGORY[category.category_id] + return { + "category_id": category.category_id, + "label": category.label, + "priority": category.priority, + "control_tier": category.control_tier, + "required_gate": category.required_gate, + "coverage_status": status["coverage_status"], + "coverage_percent": status["coverage_percent"], + "patterns": list(category.patterns), + "required_validation": list(category.required_validation), + "evidence_refs": status["evidence_refs"], + "current_gap": status["current_gap"], + "next_owner_action": status["next_owner_action"], + "owner_response_required": True, + "owner_response_received": False, + "owner_response_accepted": False, + "runtime_gate_open": False, + "action_buttons_allowed": False, + } + + +def count_by(items: list[dict[str, Any]], key: str, value: Any) -> int: + return sum(1 for item in items if item[key] == value) + + +def build_report(root: Path, generated_at: str | None) -> dict[str, Any]: + report_time = generated_at or datetime.now(TAIPEI).isoformat(timespec="seconds") + categories = [category_to_dict(category) for category in load_gate_categories(root)] + c0_categories = [item for item in categories if item["control_tier"] == "C0"] + c1_categories = [item for item in categories if item["control_tier"] == "C1"] + needs_live_evidence = [ + item + for item in categories + if item["coverage_status"] + in { + "gate_defined_needs_runtime_evidence", + "policy_defined_needs_restore_drill_owner", + "policy_ready_needs_drift_evidence", + "inventory_needed", + "policy_ready_needs_network_matrix", + "policy_ready_needs_dry_run_pack", + } + ] + average_coverage = round(sum(item["coverage_percent"] for item in categories) / len(categories)) + lowest_categories = sorted(categories, key=lambda item: item["coverage_percent"])[:4] + + return { + "schema_version": "high_value_config_control_coverage_v1", + "generated_at": report_time, + "git_commit": git_short_sha(root), + "source_category_definition": "scripts/security/high-value-config-change-gate.py", + "status": "coverage_matrix_ready", + "summary": { + "category_count": len(categories), + "c0_category_count": len(c0_categories), + "c1_category_count": len(c1_categories), + "c2_category_count": count_by(categories, "control_tier", "C2"), + "c3_category_count": count_by(categories, "control_tier", "C3"), + "registered_control_count": len(categories), + "owner_response_required_count": len(categories), + "owner_response_received_count": 0, + "owner_response_accepted_count": 0, + "runtime_gate_count": 0, + "action_button_count": 0, + "average_coverage_percent": average_coverage, + "needs_live_evidence_count": len(needs_live_evidence), + "lowest_coverage_category_count": len(lowest_categories), + }, + "execution_boundaries": FALSE_BOUNDARIES, + "coverage_categories": categories, + "lowest_coverage_categories": [ + { + "category_id": item["category_id"], + "label": item["label"], + "coverage_percent": item["coverage_percent"], + "current_gap": item["current_gap"], + "next_owner_action": item["next_owner_action"], + } + for item in lowest_categories + ], + "next_collection_order": [ + "nginx_public_gateway", + "dns_tls_certbot", + "secret_metadata", + "gitea_workflow_runner_source_control", + "agent_bounty_protocol_runtime", + "docker_compose_systemd_host_config", + "monitoring_alerting_observability", + "ssh_firewall_network_access", + ], + "operator_interpretation": [ + "這是全域配置控管覆蓋矩陣,不是單次 git diff 變更分類。", + "所有 category 都已有高價值配置 Gate 註冊與 owner response 欄位,但 owner response received / accepted 仍為 0。", + "C0 / C1 coverage percent 只代表只讀框架成熟度,不代表 runtime 可執行。", + "缺 live evidence 的項目只能收 owner-provided redacted evidence,不得主動 SSH、reload、scan 或讀 secret value。", + ], + } + + +def main() -> int: + parser = argparse.ArgumentParser(description="IwoooS 高價值配置控管覆蓋矩陣") + parser.add_argument("--root", default=".", help="repo root") + parser.add_argument("--output", help="寫出 JSON 報告") + parser.add_argument("--generated-at", help="固定報告時間,供 committed snapshot 使用") + args = parser.parse_args() + + root = Path(args.root).resolve() + report = build_report(root, args.generated_at) + payload = json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True) + + if args.output: + output = Path(args.output) + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text(payload + "\n", encoding="utf-8") + else: + print(payload) + + summary = report["summary"] + print( + "HIGH_VALUE_CONFIG_CONTROL_COVERAGE_OK " + f"categories={summary['category_count']} " + f"c0={summary['c0_category_count']} " + f"avg={summary['average_coverage_percent']} " + f"runtime_gate={summary['runtime_gate_count']}", + file=sys.stderr, + ) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/security/security-mirror-progress-guard.py b/scripts/security/security-mirror-progress-guard.py index 96a35d119..12652ee79 100755 --- a/scripts/security/security-mirror-progress-guard.py +++ b/scripts/security/security-mirror-progress-guard.py @@ -87,6 +87,7 @@ def validate(root: Path) -> None: primary_gate = load_json(security_dir / "source-control-primary-readiness-gate.snapshot.json") rollout_policy = load_json(security_dir / "security-rollout-policy.snapshot.json") iwooos_projection = load_json(security_dir / "iwooos-posture-projection.snapshot.json") + high_value_config_coverage = load_json(security_dir / "high-value-config-control-coverage.snapshot.json") domain_tls_inventory = load_json(security_dir / "domain-tls-certbot-inventory.snapshot.json") s49_request_draft = load_json(security_dir / "gitea-inventory-owner-attestation-request-draft.snapshot.json") kali_status = load_json(security_dir / "kali-integration-status.snapshot.json") @@ -2393,6 +2394,97 @@ def validate(root: Path) -> None: iwooos_projection["summary"]["rollout_risk_runtime_gate_count"], 0, ) + assert_equal( + "high_value_config_coverage.schema", + high_value_config_coverage["schema_version"], + "high_value_config_control_coverage_v1", + ) + assert_equal( + "high_value_config_coverage.summary.category_count", + high_value_config_coverage["summary"]["category_count"], + 14, + ) + assert_equal( + "high_value_config_coverage.summary.c0_category_count", + high_value_config_coverage["summary"]["c0_category_count"], + 8, + ) + assert_equal( + "high_value_config_coverage.summary.c1_category_count", + high_value_config_coverage["summary"]["c1_category_count"], + 4, + ) + assert_equal( + "high_value_config_coverage.summary.average_coverage_percent", + high_value_config_coverage["summary"]["average_coverage_percent"], + 64, + ) + assert_equal( + "high_value_config_coverage.summary.needs_live_evidence_count", + high_value_config_coverage["summary"]["needs_live_evidence_count"], + 6, + ) + for key in [ + "owner_response_received_count", + "owner_response_accepted_count", + "runtime_gate_count", + "action_button_count", + ]: + assert_equal( + f"high_value_config_coverage.summary.{key}", + high_value_config_coverage["summary"][key], + 0, + ) + for key, value in high_value_config_coverage["execution_boundaries"].items(): + assert_false(f"high_value_config_coverage.execution_boundaries.{key}", value) + assert_equal( + "high_value_config_coverage.coverage_categories.count", + len(high_value_config_coverage["coverage_categories"]), + 14, + ) + assert_contains( + "high_value_config_coverage.coverage_categories.agent_bounty", + [item["category_id"] for item in high_value_config_coverage["coverage_categories"]], + "agent_bounty_protocol_runtime", + ) + assert_contains( + "high_value_config_coverage.lowest_coverage_categories.docker", + [item["category_id"] for item in high_value_config_coverage["lowest_coverage_categories"]], + "docker_compose_systemd_host_config", + ) + assert_true( + "iwooos_projection.summary.high_value_config_control_coverage_first_layer", + iwooos_projection["summary"]["high_value_config_control_coverage_first_layer"], + ) + assert_equal( + "iwooos_projection.summary.high_value_config_control_coverage_summary_count", + iwooos_projection["summary"]["high_value_config_control_coverage_summary_count"], + 4, + ) + assert_equal( + "iwooos_projection.summary.high_value_config_control_coverage_item_count", + iwooos_projection["summary"]["high_value_config_control_coverage_item_count"], + 4, + ) + expected_high_value_config_coverage_summary = { + "high_value_config_control_coverage_category_count": 14, + "high_value_config_control_coverage_c0_category_count": 8, + "high_value_config_control_coverage_c1_category_count": 4, + "high_value_config_control_coverage_average_percent": 64, + "high_value_config_control_coverage_needs_live_evidence_count": 6, + "high_value_config_control_coverage_owner_response_required_count": 14, + "high_value_config_control_coverage_owner_response_received_count": 0, + "high_value_config_control_coverage_owner_response_accepted_count": 0, + "high_value_config_control_coverage_runtime_gate_count": 0, + "high_value_config_control_coverage_action_button_count": 0, + "high_value_config_control_coverage_lowest_category_count": 4, + } + for key, expected in expected_high_value_config_coverage_summary.items(): + assert_equal( + f"iwooos_projection.summary.{key}", + iwooos_projection["summary"][key], + expected, + ) assert_true( "iwooos_projection.summary.high_value_config_owner_packet_first_layer", iwooos_projection["summary"]["high_value_config_owner_packet_first_layer"], @@ -11487,6 +11579,86 @@ def validate(root: Path) -> None: list(web_messages_en["iwooos"]["rolloutRiskReadOnly"]["items"].keys()), key, ) + assert_text_contains( + "iwooos_page.high_value_config_control_coverage_testid", + iwooos_projection_page, + 'data-testid="iwooos-high-value-config-control-coverage-board"', + ) + assert_text_contains( + "iwooos_page.high_value_config_control_coverage_boundary_testid", + iwooos_projection_page, + 'data-testid="iwooos-high-value-config-control-coverage-boundaries"', + ) + assert_text_contains( + "iwooos_page.high_value_config_control_coverage_component", + iwooos_projection_page, + "IwoooSHighValueConfigControlCoverageBoard", + ) + assert_text_before( + "iwooos_page.high_value_config_control_coverage_before_owner_packet", + iwooos_projection_page, + "", + "", + ) + for text in [ + "high_value_config_control_coverage_frontstage_summary_count=4", + "high_value_config_control_coverage_frontstage_item_count=4", + "high_value_config_control_coverage_category_count=14", + "high_value_config_control_coverage_c0_category_count=8", + "high_value_config_control_coverage_c1_category_count=4", + "high_value_config_control_coverage_average_percent=64", + "high_value_config_control_coverage_needs_live_evidence_count=6", + "high_value_config_control_coverage_owner_response_required_count=14", + "high_value_config_control_coverage_owner_response_received_count=0", + "high_value_config_control_coverage_owner_response_accepted_count=0", + "high_value_config_control_coverage_runtime_gate_count=0", + "high_value_config_control_coverage_action_button_count=0", + "runtime_execution_authorized=false", + "host_write_authorized=false", + "nginx_reload_authorized=false", + "dns_tls_change_authorized=false", + "certbot_renew_authorized=false", + "workflow_modification_authorized=false", + "active_scan_authorized=false", + "agent_bounty_runtime_authorized=false", + ]: + assert_text_contains( + "iwooos_page.high_value_config_control_coverage_boundary", + iwooos_projection_page, + text, + ) + assert_contains( + "web_messages.zh-TW.iwooos.highValueConfigControlCoverage", + list(web_messages_zh["iwooos"].keys()), + "highValueConfigControlCoverage", + ) + assert_contains( + "web_messages.en.iwooos.highValueConfigControlCoverage", + list(web_messages_en["iwooos"].keys()), + "highValueConfigControlCoverage", + ) + for key in ["categories", "c0", "coverage", "runtimeGate"]: + assert_contains( + "web_messages.zh-TW.iwooos.highValueConfigControlCoverage.summary", + list(web_messages_zh["iwooos"]["highValueConfigControlCoverage"]["summary"].keys()), + key, + ) + assert_contains( + "web_messages.en.iwooos.highValueConfigControlCoverage.summary", + list(web_messages_en["iwooos"]["highValueConfigControlCoverage"]["summary"].keys()), + key, + ) + for key in ["dockerSystemd", "sshNetwork", "backupRestore", "monitoring"]: + assert_contains( + "web_messages.zh-TW.iwooos.highValueConfigControlCoverage.items", + list(web_messages_zh["iwooos"]["highValueConfigControlCoverage"]["items"].keys()), + key, + ) + assert_contains( + "web_messages.en.iwooos.highValueConfigControlCoverage.items", + list(web_messages_en["iwooos"]["highValueConfigControlCoverage"]["items"].keys()), + key, + ) assert_text_contains( "iwooos_page.high_value_config_owner_packet_testid", iwooos_projection_page,