From 5f9a11e6b25387975ed451dfb45a63eba02fbba3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 15 Jun 2026 04:29:54 +0800 Subject: [PATCH] =?UTF-8?q?fix(iwooos):=20=E6=96=B0=E5=A2=9E=20public=20ru?= =?UTF-8?q?ntime=20config=20=E9=A9=97=E6=94=B6=E8=88=87=20tenants=20?= =?UTF-8?q?=E9=98=B2=E6=B4=A9=E6=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/messages/en.json | 2 +- apps/web/messages/zh-TW.json | 2 +- .../src/app/[locale]/awooop/tenants/page.tsx | 13 +- apps/web/src/app/[locale]/iwooos/page.tsx | 87 +- .../HIGH-VALUE-CONFIG-CONTROL-COVERAGE.md | 32 +- .../IWOOOS-CONFIG-CONTROL-INVENTORY.md | 19 +- ...NTIME-CONFIG-CHANGE-EVIDENCE-ACCEPTANCE.md | 136 ++ .../SECURITY-SUPPLY-CHAIN-PROGRESS.md | 10 +- ...alue-config-control-coverage.snapshot.json | 48 +- .../iwooos-posture-projection.snapshot.json | 31 +- ...g-change-evidence-acceptance.snapshot.json | 1521 +++++++++++++++++ ...-04-15-MASTER-ai-autonomous-flywheel-v2.md | 12 + ...026-06-04-iwooos-security-governance-p0.md | 2 + .../high-value-config-control-coverage.py | 33 +- ...ntime-config-change-evidence-acceptance.py | 525 ++++++ .../security-mirror-progress-guard.py | 416 ++++- 16 files changed, 2853 insertions(+), 36 deletions(-) create mode 100644 docs/security/PUBLIC-RUNTIME-CONFIG-CHANGE-EVIDENCE-ACCEPTANCE.md create mode 100644 docs/security/public-runtime-config-change-evidence-acceptance.snapshot.json create mode 100644 scripts/security/public-runtime-config-change-evidence-acceptance.py diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 9b4c0f7e6..de70cdd99 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -17756,7 +17756,7 @@ }, "publicRuntime": { "title": "Public / admin / API runtime config", - "body": "AWOOOI、AwoooP、IwoooS、任務媒合產品、股票研究平台、官方形象網站、藥局網站、直播角色網站 等公開、後台、API、callback、webhook 與 env runtime config 必須進 owner gate。" + "body": "AWOOOI、AwoooP、IwoooS、任務媒合產品、股票研究平台、官方形象網站、藥局網站、直播角色網站 等公開、後台、API、callback、webhook 與 env runtime config 已新增變更證據驗收;目前只收 affected route、auth boundary、API readback、CORS diff、frontend env diff、i18n redaction、desktop / mobile smoke、sensitive scan 與 rollback owner,不改 CORS、不改 route、不部署,也不得暴露 raw namespace、內部狀態碼或內部協作內容。" }, "agentBounty": { "title": "agent-bounty runtime / treasury", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 9b4c0f7e6..de70cdd99 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -17756,7 +17756,7 @@ }, "publicRuntime": { "title": "Public / admin / API runtime config", - "body": "AWOOOI、AwoooP、IwoooS、任務媒合產品、股票研究平台、官方形象網站、藥局網站、直播角色網站 等公開、後台、API、callback、webhook 與 env runtime config 必須進 owner gate。" + "body": "AWOOOI、AwoooP、IwoooS、任務媒合產品、股票研究平台、官方形象網站、藥局網站、直播角色網站 等公開、後台、API、callback、webhook 與 env runtime config 已新增變更證據驗收;目前只收 affected route、auth boundary、API readback、CORS diff、frontend env diff、i18n redaction、desktop / mobile smoke、sensitive scan 與 rollback owner,不改 CORS、不改 route、不部署,也不得暴露 raw namespace、內部狀態碼或內部協作內容。" }, "agentBounty": { "title": "agent-bounty runtime / treasury", diff --git a/apps/web/src/app/[locale]/awooop/tenants/page.tsx b/apps/web/src/app/[locale]/awooop/tenants/page.tsx index 96ed4399a..292bb2b2a 100644 --- a/apps/web/src/app/[locale]/awooop/tenants/page.tsx +++ b/apps/web/src/app/[locale]/awooop/tenants/page.tsx @@ -348,6 +348,10 @@ function sourceActionLabel(readiness: string, t: ReturnType - {sourceRepos.map((repo) => ( - + {sourceRepos.map((repo, index) => ( + -
{repo.source_scope_id}
+
{sourcePublicScopeCode(index)}
{t("labels.redactedScope")}
+
+ {assetCategoryLabel(repo.category, t)} +
{repo.product_name} diff --git a/apps/web/src/app/[locale]/iwooos/page.tsx b/apps/web/src/app/[locale]/iwooos/page.tsx index fead1f0ea..8ec0160b7 100644 --- a/apps/web/src/app/[locale]/iwooos/page.tsx +++ b/apps/web/src/app/[locale]/iwooos/page.tsx @@ -2105,7 +2105,7 @@ const highValueConfigControlCoverageBoundaries = [ '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=68', - 'high_value_config_control_coverage_needs_live_evidence_count=8', + 'high_value_config_control_coverage_needs_live_evidence_count=9', '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', @@ -2115,6 +2115,29 @@ const highValueConfigControlCoverageBoundaries = [ 'k8s_production_gitops_coverage_percent=64', 'secret_metadata_coverage_percent=68', 'gitea_workflow_runner_source_control_coverage_percent=72', + 'public_admin_api_runtime_config_coverage_percent=64', + 'public_runtime_config_change_evidence_acceptance_candidate_count=6', + 'public_runtime_config_change_evidence_acceptance_c0_candidate_count=5', + 'public_runtime_config_change_evidence_acceptance_c1_candidate_count=1', + 'public_runtime_config_change_evidence_acceptance_write_capable_candidate_count=6', + 'public_runtime_config_change_evidence_acceptance_source_ref_count=20', + 'public_runtime_config_change_evidence_acceptance_required_evidence_field_count=21', + 'public_runtime_config_change_evidence_acceptance_reviewer_check_count=21', + 'public_runtime_config_change_evidence_acceptance_outcome_lane_count=8', + 'public_runtime_config_change_evidence_acceptance_blocked_action_count=32', + 'public_runtime_config_change_evidence_acceptance_received_count=0', + 'public_runtime_config_change_evidence_acceptance_accepted_count=0', + 'public_runtime_config_change_evidence_acceptance_route_scope_accepted_count=0', + 'public_runtime_config_change_evidence_acceptance_admin_auth_boundary_accepted_count=0', + 'public_runtime_config_change_evidence_acceptance_api_contract_readback_accepted_count=0', + 'public_runtime_config_change_evidence_acceptance_cors_origin_diff_accepted_count=0', + 'public_runtime_config_change_evidence_acceptance_frontend_env_diff_accepted_count=0', + 'public_runtime_config_change_evidence_acceptance_i18n_redaction_review_accepted_count=0', + 'public_runtime_config_change_evidence_acceptance_desktop_mobile_smoke_accepted_count=0', + 'public_runtime_config_change_evidence_acceptance_sensitive_string_scan_accepted_count=0', + 'public_runtime_config_change_evidence_acceptance_postcheck_evidence_accepted_count=0', + 'public_runtime_config_change_evidence_acceptance_runtime_approval_package_ready_count=0', + 'public_runtime_config_change_evidence_acceptance_runtime_gate_count=0', 'cd_runner_secret_injection_change_evidence_acceptance_candidate_count=5', 'cd_runner_secret_injection_change_evidence_acceptance_c0_candidate_count=4', 'cd_runner_secret_injection_change_evidence_acceptance_c1_candidate_count=1', @@ -2261,6 +2284,27 @@ const highValueConfigControlCoverageBoundaries = [ 'exporter_deploy_authorized=false', 'live_alert_fire_authorized=false', 'alert_chain_smoke_authorized=false', + 'runtime_config_change_authorized=false', + 'api_route_change_authorized=false', + 'cors_change_authorized=false', + 'frontend_env_change_authorized=false', + 'middleware_auth_change_authorized=false', + 'callback_url_change_authorized=false', + 'webhook_secret_change_authorized=false', + 'security_header_change_authorized=false', + 'cookie_policy_change_authorized=false', + 'csrf_disable_authorized=false', + 'rate_limit_disable_authorized=false', + 'api_contract_change_authorized=false', + 'i18n_public_text_internal_identity_allowed=false', + 'internal_ip_exposure_allowed=false', + 'repo_namespace_exposure_allowed=false', + 'owner_namespace_exposure_allowed=false', + 'internal_status_code_exposure_allowed=false', + 'internal_transcript_exposure_allowed=false', + 'raw_payload_storage_allowed=false', + 'desktop_mobile_smoke_authorized=false', + 'database_migration_authorized=false', 'workflow_modification_authorized=false', 'webhook_modification_authorized=false', 'runner_change_authorized=false', @@ -2299,7 +2343,7 @@ const criticalConfigPriorityItems: CriticalConfigPriorityItem[] = [ { key: 'dnsTls', rank: 'P0-2', state: '78% / probe 0', icon: Route, tone: 'warn' }, { key: 'k8sArgocd', rank: 'P0-3', state: '64% / sync 0', icon: Cloud, tone: 'warn' }, { key: 'workflowSecret', rank: 'P0-4', state: '68% / 72%', icon: GitBranch, tone: 'warn' }, - { key: 'publicRuntime', rank: 'P0-5', state: 'route gate 0', icon: Network, tone: 'warn' }, + { key: 'publicRuntime', rank: 'P0-5', state: '64% / route 0', icon: Network, tone: 'warn' }, { key: 'agentBounty', rank: 'P0-6', state: 'runtime 0', icon: Workflow, tone: 'locked' }, ] @@ -2317,6 +2361,26 @@ const criticalConfigPriorityBoundaries = [ 'nginx_public_gateway_rendered_diff_accepted=false', 'nginx_public_gateway_rendered_diff_acceptance_candidate_count=3', 'nginx_public_gateway_nginx_test_evidence_count=0', + 'public_admin_api_runtime_config_coverage_percent=64', + 'public_runtime_config_change_evidence_acceptance_candidate_count=6', + 'public_runtime_config_change_evidence_acceptance_c0_candidate_count=5', + 'public_runtime_config_change_evidence_acceptance_write_capable_candidate_count=6', + 'public_runtime_config_change_evidence_acceptance_required_evidence_field_count=21', + 'public_runtime_config_change_evidence_acceptance_reviewer_check_count=21', + 'public_runtime_config_change_evidence_acceptance_outcome_lane_count=8', + 'public_runtime_config_change_evidence_acceptance_blocked_action_count=32', + 'public_runtime_config_change_evidence_acceptance_received_count=0', + 'public_runtime_config_change_evidence_acceptance_accepted_count=0', + 'public_runtime_config_change_evidence_acceptance_route_scope_accepted_count=0', + 'public_runtime_config_change_evidence_acceptance_admin_auth_boundary_accepted_count=0', + 'public_runtime_config_change_evidence_acceptance_api_contract_readback_accepted_count=0', + 'public_runtime_config_change_evidence_acceptance_cors_origin_diff_accepted_count=0', + 'public_runtime_config_change_evidence_acceptance_frontend_env_diff_accepted_count=0', + 'public_runtime_config_change_evidence_acceptance_i18n_redaction_review_accepted_count=0', + 'public_runtime_config_change_evidence_acceptance_desktop_mobile_smoke_accepted_count=0', + 'public_runtime_config_change_evidence_acceptance_sensitive_string_scan_accepted_count=0', + 'public_runtime_config_change_evidence_acceptance_runtime_approval_package_ready_count=0', + 'public_runtime_config_change_evidence_acceptance_runtime_gate_count=0', 'secret_metadata_coverage_percent=68', 'gitea_workflow_runner_source_control_coverage_percent=72', 'cd_runner_secret_injection_change_evidence_acceptance_candidate_count=5', @@ -2363,6 +2427,25 @@ const criticalConfigPriorityBoundaries = [ 'production_deploy_authorized=false', 'force_push_authorized=false', 'github_primary_switch_authorized=false', + 'runtime_config_change_authorized=false', + 'api_route_change_authorized=false', + 'cors_change_authorized=false', + 'frontend_env_change_authorized=false', + 'middleware_auth_change_authorized=false', + 'callback_url_change_authorized=false', + 'webhook_secret_change_authorized=false', + 'security_header_change_authorized=false', + 'cookie_policy_change_authorized=false', + 'csrf_disable_authorized=false', + 'rate_limit_disable_authorized=false', + 'api_contract_change_authorized=false', + 'internal_ip_exposure_allowed=false', + 'repo_namespace_exposure_allowed=false', + 'owner_namespace_exposure_allowed=false', + 'internal_status_code_exposure_allowed=false', + 'internal_transcript_exposure_allowed=false', + 'raw_payload_storage_allowed=false', + 'database_migration_authorized=false', 'secret_value_collection_allowed=false', 'public_route_change_authorized=false', 'agent_bounty_runtime_authorized=false', diff --git a/docs/security/HIGH-VALUE-CONFIG-CONTROL-COVERAGE.md b/docs/security/HIGH-VALUE-CONFIG-CONTROL-COVERAGE.md index 314ceab9a..235020f33 100644 --- a/docs/security/HIGH-VALUE-CONFIG-CONTROL-COVERAGE.md +++ b/docs/security/HIGH-VALUE-CONFIG-CONTROL-COVERAGE.md @@ -44,6 +44,14 @@ 固定數字為 `change_evidence_candidate_count=5`、`c0_change_evidence_candidate_count=4`、`c1_change_evidence_candidate_count=1`、`write_capable_candidate_count=5`、`local_workflow_file_count=33`、`local_referenced_secret_name_count=42`、`runner_label_count=5`、`required_evidence_field_count=19`、`reviewer_check_count=19`、`outcome_lane_count=8`、`blocked_action_count=32`。此更新讓 `secret_metadata` 從 `66%` 推進到 `68%`,讓 `gitea_workflow_runner_source_control` 從 `70%` 推進到 `72%`;workflow diff、runner attestation、secret name parity、secret injection route、deploy marker readback、guard result、post-check evidence、runtime approval package、workflow 修改、runner 啟用、secret rotation、repo secret change、Gitea action dispatch、production deploy 與 runtime gate 仍全部為 `0 / false`。 +## 1.2b 2026-06-15 Public / Admin / API runtime config 變更證據驗收 + +已新增 `docs/security/PUBLIC-RUNTIME-CONFIG-CHANGE-EVIDENCE-ACCEPTANCE.md` 與 `docs/security/public-runtime-config-change-evidence-acceptance.snapshot.json`,將公開產品頁、AwoooP 後台、API / CORS、frontend env、Sentry tunnel、webhook / callback 與跨產品 runtime route 轉成 metadata-only 變更證據驗收帳本。 + +固定數字為 `change_evidence_candidate_count=6`、`c0_change_evidence_candidate_count=5`、`c1_change_evidence_candidate_count=1`、`write_capable_candidate_count=6`、`source_ref_count=20`、`required_evidence_field_count=21`、`reviewer_check_count=21`、`outcome_lane_count=8`、`blocked_action_count=32`。此更新讓 `public_admin_api_runtime_config` 從 `62%` 推進到 `64%`,但 affected route、admin/auth boundary、API contract readback、CORS diff、frontend env diff、i18n redaction review、webhook / callback owner、desktop / mobile smoke、sensitive string scan、post-check evidence、runtime approval package、route / CORS / env / auth / webhook 變更、production deploy 與 runtime gate 仍全部為 `0 / false`。 + +此帳本明確把 raw owner namespace、repo slug、內部狀態碼、內部協作文字、cookie、token、secret value、DSN value、raw payload 與未脫敏截圖列為拒收或隔離條件。前台可以顯示產品 / 專案脫敏名稱與控管狀態,但不得顯示個人 namespace、內部狀態碼、內部協作內容或抱怨語句。 + ## 1.3 2026-06-14 agent-bounty-protocol owner request draft 已新增 `docs/security/AGENT-BOUNTY-OWNER-REQUEST-DRAFT.md` 與 `docs/security/agent-bounty-owner-request-draft.snapshot.json`,將 `agent-bounty-protocol` onboarding handoff 轉成 11 份 owner request draft。 @@ -88,7 +96,7 @@ | C2 類別 | `1` | 產品 runtime route 與跨產品邊界 | | C3 類別 | `1` | security evidence / snapshot / guard tooling | | 平均只讀控管成熟度 | `68%` | 僅代表框架 / evidence / owner packet / acceptance ledger 準備度,不代表 runtime 可執行 | -| 需要 live / owner evidence 的類別 | `8` | 只能等 owner-provided redacted evidence 或維護窗口,不主動修改 workflow、secret、runner 或主機 | +| 需要 live / owner evidence 的類別 | `9` | 只能等 owner-provided redacted evidence 或維護窗口,不主動修改 workflow、secret、runner、route、CORS、env 或主機 | | owner response required | `14` | 每類都需要 owner response 才能往 accepted 前進 | | owner response received / accepted | `0 / 0` | 不得假性提高 | | runtime gate | `0` | 不得產生執行按鈕 | @@ -147,6 +155,27 @@ secret_value_collection_allowed=false secret_hash_collection_allowed=false partial_token_collection_allowed=false secret_rotation_authorized=false +runtime_config_change_authorized=false +api_route_change_authorized=false +cors_change_authorized=false +frontend_env_change_authorized=false +middleware_auth_change_authorized=false +callback_url_change_authorized=false +webhook_secret_change_authorized=false +security_header_change_authorized=false +cookie_policy_change_authorized=false +csrf_disable_authorized=false +rate_limit_disable_authorized=false +api_contract_change_authorized=false +i18n_public_text_internal_identity_allowed=false +internal_ip_exposure_allowed=false +repo_namespace_exposure_allowed=false +owner_namespace_exposure_allowed=false +internal_status_code_exposure_allowed=false +internal_transcript_exposure_allowed=false +raw_payload_storage_allowed=false +desktop_mobile_smoke_authorized=false +database_migration_authorized=false active_scan_authorized=false agent_bounty_runtime_authorized=false payout_or_withdrawal_authorized=false @@ -203,6 +232,7 @@ python3 scripts/security/high-value-config-control-coverage.py \ | DNS / TLS / certbot owner response acceptance | `100%` | 已新增 `domain_tls_certbot_owner_response_acceptance_v1`,4 個 C0 candidate、13 個 owner 必填欄位、13 個 reviewer checks、7 條 outcome lanes、20 類 blocked action;成熟度 `74% -> 78%` | | Docker / systemd / host service owner response acceptance | `100%` | 已新增 `host_service_owner_response_acceptance_v1`,9 個 candidate、3 個 write-capable、14 個 reviewer checks、7 條 outcome lanes、20 類 blocked action;成熟度 `50% -> 54%` | | CD / Runner / Secret injection change evidence acceptance | `100%` | 已新增 `cd_runner_secret_injection_change_evidence_acceptance_v1`,5 個 candidate、4 個 C0、19 個 reviewer checks、8 條 outcome lanes、32 類 blocked action;secret metadata 成熟度 `66% -> 68%`,workflow / runner 成熟度 `70% -> 72%` | +| Public / Admin / API runtime config 變更證據驗收 | `100%` | 已新增 `public_runtime_config_change_evidence_acceptance_v1`,6 個 candidate、5 個 C0、21 個 reviewer checks、8 條 outcome lanes、32 類 blocked action;public/admin/API runtime config 成熟度 `62% -> 64%`,raw namespace / repo slug / 內部狀態碼 / 內部協作內容外洩列為拒收或隔離條件 | | 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 0c476b53b..b33c4c289 100644 --- a/docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md +++ b/docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md @@ -33,7 +33,7 @@ | C0 類別 | `8` | Nginx、DNS / TLS、K8s、secret、workflow / runner、runtime config、backup、agent-bounty runtime | | C1 類別 | `4` | 監控、Docker / systemd、SSH / network、AI provider | | 平均只讀控管成熟度 | `68%` | 只代表框架 / evidence / owner packet / acceptance ledger 準備度 | -| 需要 live evidence 類別 | `6` | 只能等 owner-provided redacted evidence 或維護窗口,不主動 SSH | +| 需要 live evidence 類別 | `9` | 只能等 owner-provided redacted evidence 或維護窗口,不主動 SSH、不改 route / CORS / env | | owner response required | `14` | owner response received / accepted 仍 `0 / 0` | | runtime gate | `0` | 不提供執行按鈕 | @@ -69,6 +69,14 @@ 此更新讓 `secret_metadata` 類別成熟度從 `66%` 推進到 `68%`,讓 `gitea_workflow_runner_source_control` 類別成熟度從 `70%` 推進到 `72%`。它只表示 workflow diff、runner owner attestation、secret name parity、secret injection route、Gitea run readback、guard result、rollback owner 與 post-check evidence 已有收件驗收規則;workflow 修改、workflow dispatch、runner 啟用 / 重啟、GitHub hosted runner、secret value / hash / partial token 收集、secret store read、secret 建立 / 更新 / rotate / 刪除、webhook 修改、deploy key 修改、branch protection / CODEOWNERS 修改、refs sync、force push、GitHub primary switch、Gitea 停用、production deploy、runtime gate 仍全部為 `0 / false`。 +### 0.3c 2026-06-15 Public / Admin / API runtime config 變更證據驗收 + +`public_runtime_config_change_evidence_acceptance_v1` 已把公開產品頁、AwoooP 後台、API / CORS、frontend env、Sentry tunnel、webhook / callback 與跨產品 runtime route 轉成 metadata-only 變更證據驗收只讀帳本。固定 `candidates=6`、`c0=5`、`c1=1`、`write_capable=6`、`source_refs=20`、`required_evidence_fields=21`、`reviewer_checks=21`、`outcome_lanes=8`、`blocked_actions=32`。 + +此更新讓 Public / admin / API / frontend runtime config 類別成熟度從 `62%` 推進到 `64%`。它只表示 affected route refs、admin/auth boundary、API contract readback、CORS origin diff、frontend env diff、i18n redaction review、webhook / callback owner、desktop / mobile smoke、sensitive string scan、console error scan、rollback owner 與 post-check evidence 已有收件驗收規則;route 變更、CORS 變更、NEXT_PUBLIC env 變更、middleware auth 變更、callback / webhook 變更、security header / cookie / CSRF / rate limit 變更、database migration、frontend / API deploy、production deploy、runtime gate 仍全部為 `0 / false`。 + +此帳本同時把 raw owner namespace、repo slug、內部狀態碼、內部協作文字、cookie、token、secret value、DSN value、raw payload 與未脫敏截圖列為拒收或隔離條件。AwoooP Tenants、IwoooS、Code Review 或其他公開頁只能顯示脫敏產品 / 專案名稱與控管狀態,不得顯示個人 namespace、內部狀態碼、內部協作內容或抱怨語句。 + `backup_restore_owner_request_draft_v1` 已把 backup、restore、offsite、credential escrow、retention、Velero、alert / health 與 DR runbook 的 38 個 surface 轉成人工送件前 owner request draft。固定 `drafts=38`、`write_capable=27`、`live_evidence_required=38`、`owner_fields=14`、`blocked_actions=18`,但 request sent、owner response received / accepted、live backup evidence、backup run、restore run、offsite sync、remote delete、escrow marker write、retention change、host write、runtime gate 仍全部為 `0 / false`。 此更新仍不是 live network truth:live firewall、sudoers、known_hosts、NetworkPolicy、NodePort、WireGuard evidence、network owner、maintenance window、rollback owner 與 owner response received / accepted 全部仍為 `0`,也不得執行 SSH、keyscan、sudo、firewall change、NetworkPolicy apply、NodePort change 或 WireGuard cutover。 @@ -170,7 +178,7 @@ Nginx 是目前必須最先資安控管的配置,原因是它同時控制公 | P0 | K8s Secret metadata | `k8s/awoooi-prod/03-secrets.example.yaml`、secret templates、workflow injection | Gitea Secrets / K8s Secret names | secret name parity only、no value collection、rotation owner | | P0 | Gitea workflows | `.gitea/workflows/*.yaml` | Gitea Actions | self-hosted runner, secret reference guard, deployment verification, no write action without owner | | P0 | Runner / deploy key / webhook / branch protection | `ops/runner/*`、source-control snapshots | Gitea / GitHub owner metadata | labels、key names、webhook names、ruleset metadata only;no token / key value | -| P0 | Public admin / API route config | Nginx templates、`apps/web/src/lib/config.ts`、`apps/api/src/core/config.py` | Product owner + runtime owner | auth boundary、CORS、public URL、admin path smoke、frontend internal IP ban | +| P0 | Public admin / API route config | Nginx templates、`apps/web/src/lib/config.ts`、`apps/api/src/core/config.py`、`docs/security/PUBLIC-RUNTIME-CONFIG-CHANGE-EVIDENCE-ACCEPTANCE.md`、`docs/security/public-runtime-config-change-evidence-acceptance.snapshot.json` | Product owner + runtime owner | auth boundary、CORS、public URL、admin path smoke、frontend internal IP ban、i18n redaction、raw namespace / repo slug / 內部狀態碼 / 內部協作內容外洩阻擋、desktop / mobile smoke | | P0 | Backup / restore credential | `scripts/backup/*`、`k8s/velero/*`、DR runbooks、`docs/security/BACKUP-RESTORE-ESCROW-INVENTORY.md`、`docs/security/BACKUP-RESTORE-OWNER-REQUEST-DRAFT.md` | MinIO / restic / offsite escrow | owner request draft 已固定;credential value absent、restore drill gate、offsite owner、escrow owner、retention policy、rollback owner | | P0 | agent-bounty-protocol treasury / MCP / A2A | `docs/security/AGENT-BOUNTY-IWOOOS-ONBOARDING-HANDOFF.md`、`docs/security/AGENT-BOUNTY-OWNER-REQUEST-DRAFT.md` | agent-bounty owner response | owner request draft 已固定;no payout / claim / submit / daemon / webhook until explicit runtime approval | | P1 | Prometheus / Alertmanager | `k8s/monitoring/*`、`ops/alertmanager/alertmanager.yml`、`ops/monitoring/*`、`docs/security/MONITORING-ALERTING-OBSERVABILITY-INVENTORY.md`、`docs/security/MONITORING-OWNER-REQUEST-DRAFT.md` | 110 monitoring stack | repo-only 清冊與 owner request draft 已固定;仍缺 rule diff、receiver diff、reload owner、receipt proof 與 live evidence | @@ -201,6 +209,7 @@ Nginx 是目前必須最先資安控管的配置,原因是它同時控制公 7. agent-bounty-protocol、VibeWork 與其他產品的 route / admin / webhook / payout / deploy config 必須放入 IwoooS 控管,但不能混用 AWOOOI runtime approval。 8. Backup / restore / offsite / escrow / retention 清冊可見只代表需被控管;不得把 runbook 命令、snapshot、AwoooP approval 或 IwoooS UI 當作 backup run、restore drill、rclone sync、remote delete、restic prune、escrow marker write 或 Velero restore 授權。 9. DNS / TLS / certbot owner response 只能收脫敏 metadata ref、coverage basis、expiry metadata、renewal owner、ACME route owner、maintenance window、rollback owner 與 validation plan;不得因 owner 回覆而自動做 DNS query、live TLS probe、certbot renew、Nginx reload、route smoke、DNS record 變更、certificate path 變更或 ACME route 變更。 +10. Public / admin / API / frontend runtime config 變更必須先通過 affected route、auth boundary、API readback、CORS diff、frontend env diff、i18n redaction、desktop / mobile smoke、sensitive string scan、rollback owner 與 post-check evidence;前台不得顯示 raw owner namespace、repo slug、內部狀態碼、內部協作內容或未脫敏截圖。 ## 5. 需要調整的既有規範 @@ -242,6 +251,7 @@ Nginx 是目前必須最先資安控管的配置,原因是它同時控制公 | SSH / firewall / network owner response acceptance | `100%` | 已新增 `ssh_network_owner_response_acceptance_v1`,16 個 candidate、6 個 write-capable、15 個 reviewer checks、7 條 outcome lanes、22 類 blocked action;成熟度 `54% -> 58%` | | 端口 / 防火牆變更證據驗收 | `100%` | 已新增 `port_firewall_change_evidence_acceptance_v1`,14 個 candidate、6 個 write-capable、16 個 reviewer checks、8 條 outcome lanes、24 類 blocked action;成熟度 `58% -> 60%` | | K8s / ArgoCD GitOps 變更證據驗收 | `100%` | 已新增 `k8s_argocd_change_evidence_acceptance_v1`,4 個 candidate、3 個 C0、4 個 write-capable、18 個 reviewer checks、8 條 outcome lanes、28 類 blocked action;成熟度 `62% -> 64%` | +| Public / Admin / API runtime config 變更證據驗收 | `100%` | 已新增 `public_runtime_config_change_evidence_acceptance_v1`,6 個 candidate、5 個 C0、21 個 reviewer checks、8 條 outcome lanes、32 類 blocked action;成熟度 `62% -> 64%`;raw namespace / repo slug / 內部狀態碼 / 內部協作內容外洩列為拒收或隔離條件 | | Backup / restore / escrow owner request draft | `100%` | 已將 38 個 backup / restore / escrow surface 轉成 owner request draft;request sent / received / accepted、backup run、restore run、offsite sync、remote delete、escrow marker write、retention change 仍為 0 | | CI blocking / workflow gate | `0%` | 本階段刻意不修改 `.gitea/workflows`,避免初期資安流程摩擦過大 | | owner-provided live Nginx file compare | `70%` | 工具可吃 owner 匯出的 live conf 檔比較;本階段不主動 SSH 取得 | @@ -256,8 +266,9 @@ Nginx 是目前必須最先資安控管的配置,原因是它同時控制公 2. P0:由 owner 提供脫敏 live Nginx conf 匯出檔,重跑 compare mode;不自動覆寫、不 reload。 3. P0:向 owner 收 DNS / TLS / certbot 脫敏 coverage metadata ref、expiry metadata ref、renewal owner、ACME route owner、maintenance window、rollback owner 與 validation plan;不做 DNS query、TLS probe、certbot renew 或 reload。 4. P0:把 workflow / runner / secret name owner response 與高價值配置 C0 gate 串成同一個 IwoooS 狀態。 -5. P0:把 agent-bounty-protocol compose / MCP / A2A / treasury 高價值配置欄位接入同一個 owner packet queue;不啟用 runtime。 -6. P1:向 owner 收 110 / 188 Docker Compose 與 systemd 脫敏 live hash metadata、maintenance / restart window、rollback owner、post-check plan、disable switch 與 no-secret-value evidence;不主動 SSH、不重啟、不跑 repair-bot。 +5. P0:收 public / admin / API runtime config 的脫敏變更證據,先補 affected route、auth boundary、API readback、CORS diff、frontend env diff、i18n redaction、desktop / mobile smoke、sensitive string scan、rollback owner 與 post-check evidence;不改 route / CORS / env。 +6. P0:把 agent-bounty-protocol compose / MCP / A2A / treasury 高價值配置欄位接入同一個 owner packet queue;不啟用 runtime。 +7. P1:向 owner 收 110 / 188 Docker Compose 與 systemd 脫敏 live hash metadata、maintenance / restart window、rollback owner、post-check plan、disable switch 與 no-secret-value evidence;不主動 SSH、不重啟、不跑 repair-bot。 7. P1:向 owner 收 SSH / firewall / WireGuard / NodePort 脫敏 live access state、allowed source CIDR、change / incident ref、actor、before / after state、cross-project sync、maintenance window、rollback owner 與 validation plan;不主動 keyscan、不改 firewall、不開關端口。 8. P1:向 owner 收 backup / restore / offsite / escrow 非敏感 evidence id、最新備份狀態、restore drill plan、maintenance window、rollback owner 與 validation plan;驗收前 backup run、restore drill、offsite sync、remote delete、escrow marker write、retention change 全部維持 `0 / false`。 9. P1:把 Prometheus / Alertmanager / Grafana / SigNoz / Sentry / Langfuse owner request draft 接入 AwoooP 只讀狀態;驗收前 reload、receiver route change、silence change、Telegram send 與 alert chain smoke 全部維持 `0 / false`。 diff --git a/docs/security/PUBLIC-RUNTIME-CONFIG-CHANGE-EVIDENCE-ACCEPTANCE.md b/docs/security/PUBLIC-RUNTIME-CONFIG-CHANGE-EVIDENCE-ACCEPTANCE.md new file mode 100644 index 000000000..3ff89d90a --- /dev/null +++ b/docs/security/PUBLIC-RUNTIME-CONFIG-CHANGE-EVIDENCE-ACCEPTANCE.md @@ -0,0 +1,136 @@ +# Public / Admin / API Runtime Config 變更證據驗收 + +> 本文件是 IwoooS 高價值配置控管的只讀驗收帳本。它定義未來 public route、admin/auth boundary、API / CORS、frontend env、i18n、callback 與 webhook runtime config 變更要如何收件、補件、拒收或進 reviewer acceptance。它不是 route 變更、CORS 變更、env 變更、auth 變更、webhook 變更、部署或 runtime gate 授權。 + +## 1. Snapshot + +| 欄位 | 值 | 說明 | +|---|---:|---| +| `schema_version` | `public_runtime_config_change_evidence_acceptance_v1` | Public runtime config 變更證據驗收 | +| `change_evidence_candidate_count` | `6` | 六類 public / admin / API runtime config 候選 | +| `c0_change_evidence_candidate_count` | `5` | public route、admin/auth、API/CORS、frontend env、webhook/callback 為 C0 | +| `c1_change_evidence_candidate_count` | `1` | cross-product route scope 為 C1 | +| `write_capable_candidate_count` | `6` | 六類未來都可能引發 route、auth、CORS、env 或 callback 寫入 | +| `source_ref_count` | `20` | source refs 全部存在於 repo 內 | +| `required_evidence_field_count` | `21` | reviewer 前必填 evidence 欄位 | +| `reviewer_check_count` | `21` | reviewer 必檢條件 | +| `outcome_lane_count` | `8` | 收件後分流結果 | +| `blocked_action_count` | `32` | 本帳本明確禁止的 runtime / route / secret / deploy 動作 | +| `runtime_gate_count` | `0` | 沒有開啟 runtime gate | + +來源 snapshot:`docs/security/public-runtime-config-change-evidence-acceptance.snapshot.json` + +## 2. 變更候選 + +| 候選 | Tier | 風險 | 範圍 | +|---|---|---|---| +| `public_runtime_config_change_evidence:public_product_route_and_i18n_redaction` | C0 | HIGH | 公開產品頁、IwoooS / AwoooP / Tenants / Code Review 前台文案、raw identity 與內部協作文字防外洩 | +| `public_runtime_config_change_evidence:admin_auth_and_operator_console_boundary` | C0 | HIGH | AwoooP operator console、approvals、work-items、runs、admin auth / CSRF / owner guard | +| `public_runtime_config_change_evidence:api_cors_and_public_url_runtime_config` | C0 | HIGH | API base URL、CORS origins、NEXT_PUBLIC build-time config、public domain / internal IP boundary | +| `public_runtime_config_change_evidence:frontend_env_and_sentry_tunnel_runtime_config` | C0 | HIGH | Next.js middleware、Sentry tunnel、browser-facing env、health route 與 console error boundary | +| `public_runtime_config_change_evidence:webhook_callback_and_notification_runtime_config` | C0 | HIGH | webhook callback、proposal route、deep link、notification route 與 external send boundary | +| `public_runtime_config_change_evidence:cross_product_runtime_route_scope` | C1 | MEDIUM | VibeWork、agent-bounty-protocol、StockPlatform、官方形象網站、藥局網站與其他產品 runtime route scope | + +## 3. 必收 Evidence + +每筆候選進 reviewer acceptance 前,至少要有: + +| Evidence | 必要性 | +|---|---| +| `proposed_runtime_config_change_ref` | 變更 ref,不能只有口頭同意 | +| `affected_route_refs` | public / admin / API / callback / webhook / frontend route 範圍 | +| `public_url_or_domain_ref` | public URL / domain 依據,禁止內網 IP 暴露 | +| `admin_auth_boundary_ref` | admin / operator / approval route 的 auth boundary | +| `api_contract_readback_ref` | API contract / public payload readback | +| `cors_origin_diff_ref` | CORS origin diff 或 owner-provided ref | +| `frontend_env_diff_ref` | NEXT_PUBLIC / browser-facing env diff | +| `i18n_redaction_review_ref` | 全繁中、無 raw identity、無內部對話的文案審查 | +| `webhook_callback_owner_ref` | callback / webhook / notification route owner | +| `desktop_mobile_smoke_ref` | desktop / mobile smoke、overflow 與必要文案 | +| `api_health_readback_ref` | health / API readback | +| `sensitive_string_scan_ref` | raw namespace、internal state code、內部協作語句、secret value 掃描 | +| `console_error_scan_ref` | console / page error 結果 | +| `blast_radius` | 產品、route、API、admin/auth、public domain、callback、webhook 與使用者影響 | +| `maintenance_window` | 未來 runtime config 變更窗口或不適用理由 | +| `rollback_owner` | 回復負責人 | +| `rollback_plan_ref` | 回復方式 | +| `postcheck_evidence_ref` | API readback、browser smoke、bundle scan 或 alert silence review | +| `redacted_evidence_refs` | 只允許脫敏 evidence refs | +| `reviewer_outcome` | reviewer 結果 | +| `not_approval` | 明確標示不是 runtime 授權 | + +## 4. Reviewer Checks + +Reviewer 必須確認: + +1. 有 proposed runtime config change ref。 +2. affected route refs 明確。 +3. public URL 不使用內網 IP。 +4. admin auth boundary 與 owner 明確。 +5. public API 不暴露 raw owner namespace、repo slug 或內部狀態碼。 +6. CORS 只收 diff / owner ref,不直接改白名單。 +7. frontend env 有 diff 與 bundle sensitive scan。 +8. i18n 文案全繁中,無內部對話、抱怨語句或 raw identity。 +9. webhook / callback route 有 owner 與回復方式。 +10. desktop / mobile smoke 含 overflow 結果。 +11. API / backend runtime config 有 health 或 contract readback。 +12. sensitive string scan 至少檢查 raw namespace、internal state code、internal transcript、secret value。 +13. console / page error 結果已標明。 +14. 沒有 cookie、token、secret value、hash、partial token 或 raw payload。 +15. security header、cookie、CSRF、rate limit 或 middleware 影響有說明。 +16. blast radius 明確。 +17. maintenance window 明確。 +18. rollback owner 明確。 +19. post-check evidence 明確。 +20. 不把本帳本、UI 可見、CD success、AwoooP approval 或 smoke pass 當資安批准。 +21. 影響跨專案時有同步 ref。 + +## 5. Outcome Lanes + +| Lane | 意義 | +|---|---| +| `waiting_change_evidence` | 尚未收到 runtime config 變更證據 | +| `quarantine_sensitive_payload` | 收到敏感 payload,只能隔離 | +| `reject_unredacted_or_runtime_claim` | 未脫敏或誤稱已批准,直接拒收 | +| `request_supplement` | 缺 route scope、auth、CORS、smoke、rollback 或 post-check,要求補件 | +| `ready_for_reviewer_acceptance` | metadata 合格後進 reviewer acceptance | +| `ready_for_runtime_approval_package` | reviewer 接受後形成 runtime approval package | +| `waiting_maintenance_window` | 未來 runtime config 變更仍需維護窗口 | +| `waiting_runtime_gate` | runtime gate 仍等待獨立人工批准 | + +## 6. 明確禁止 + +本帳本不得被解讀為以下動作的授權: + +- 改 public route、admin route、API route、CORS、NEXT_PUBLIC env、callback URL 或 webhook secret。 +- 改 middleware auth、關閉 CSRF、關閉 rate limit、改 cookie policy 或 security headers。 +- 在前台、public API、HTML、bundle 或 messages 放入 raw owner namespace、repo slug、內部狀態碼、內部對話、內部協作語句或 secret value。 +- 部署 frontend / API、改 Nginx route、改 OpenAPI contract、跑 migration、發送 webhook、active scan、force push 或切 GitHub primary。 + +## 7. 目前邊界 + +| 欄位 | 值 | +|---|---:| +| `change_evidence_received_count` | `0` | +| `change_evidence_accepted_count` | `0` | +| `route_scope_accepted_count` | `0` | +| `admin_auth_boundary_accepted_count` | `0` | +| `api_contract_readback_accepted_count` | `0` | +| `cors_origin_diff_accepted_count` | `0` | +| `frontend_env_diff_accepted_count` | `0` | +| `i18n_redaction_review_accepted_count` | `0` | +| `webhook_callback_owner_accepted_count` | `0` | +| `desktop_mobile_smoke_accepted_count` | `0` | +| `sensitive_string_scan_accepted_count` | `0` | +| `postcheck_evidence_accepted_count` | `0` | +| `runtime_approval_package_ready_count` | `0` | +| `runtime_gate_count` | `0` | +| `action_button_count` | `0` | + +## 8. 完成度 + +- Public / admin / API runtime config 變更證據驗收 artifact:`100%`。 +- `public_admin_api_runtime_config` 只讀治理成熟度:`62% -> 64%`。 +- Active runtime gate:`0`。 + +此完成度只代表規範、snapshot、guard 與前台 marker 可驗證;不代表任何 route、CORS、env、auth、callback、webhook、部署或 runtime 動作已授權。 diff --git a/docs/security/SECURITY-SUPPLY-CHAIN-PROGRESS.md b/docs/security/SECURITY-SUPPLY-CHAIN-PROGRESS.md index 9a01b3cf0..9dc75c041 100644 --- a/docs/security/SECURITY-SUPPLY-CHAIN-PROGRESS.md +++ b/docs/security/SECURITY-SUPPLY-CHAIN-PROGRESS.md @@ -3,16 +3,22 @@ | 項目 | 內容 | |------|------| | 日期 | 2026-06-15 | -| 狀態 | IwoooS 64% 只讀治理推進中;CD / Runner / Secret 注入變更證據驗收只讀帳本已本地完成;端口 / 防火牆變更證據驗收與 K8s / ArgoCD GitOps 變更證據驗收已正式部署驗證;S4.9 owner response gate 仍是第一優先 | +| 狀態 | IwoooS 64% 只讀治理推進中;CD / Runner / Secret 注入變更證據驗收與 Public / Admin / API runtime config 變更證據驗收只讀帳本已本地完成;端口 / 防火牆變更證據驗收與 K8s / ArgoCD GitOps 變更證據驗收已正式部署驗證;S4.9 owner response gate 仍是第一優先 | | 本階段完成 | 資安供應鏈 contract manifest + Source Control Approval Board + Draft Reconcile Plan + Ref Detail Diff + Ref Truth Classification + Source Control Ref Truth Owner Response 收件包 + GitHub Primary Readiness Gate + GitHub Primary Rollback ADR + GitHub Target Owner Decision Response 收件包 + Gitea 認證清冊匯出請求 + Gitea 認證清冊匯入驗收契約 + Gitea 清冊覆蓋 Owner Attestation + Gitea Owner Attestation Approval Lane 對齊 + Gitea Owner Attestation Response 收件包 + Workflow / Secret Name Inventory + Workflow / Secret Name Local Evidence + Workflow / Secret Name Redacted Export Request + Workflow / Secret Name Owner Response 收件包 + Source Control Owner Response Validation Rollup + Kali 112 live integration status + Security Finding contract + Kali scan scope approval package + Security Approval Queue + S3 人工批准 Gate + S3 人工決策紀錄 + S3 人工審查封包 + S3 人工決策狀態轉移 + S3 後續 runtime gate 準備契約 + 鏡像 readiness index + 鏡像接收計畫 + 鏡像事件信封 + 鏡像路由矩陣 + 鏡像驗收契約 + 鏡像隔離契約 + 鏡像 dry-run 報告契約 + 鏡像狀態彙整契約 + IwoooS 前端態勢入口 + IwoooS posture projection contract + IwoooS 既有前端資安頁面整合 + IwoooS 覆蓋與邊界矩陣 + IwoooS 只讀資安處理旅程 + IwoooS owner evidence readiness board + IwoooS host coverage view + IwoooS host action gate matrix + IwoooS host evidence readiness board + IwoooS host evidence collection order + IwoooS host evidence intake preflight + IwoooS host evidence review outcome lanes + IwoooS host evidence review handoff packets + IwoooS host evidence reviewer checklist + IwoooS host evidence reviewer outcome lanes + IwoooS host owner decision candidate packets + IwoooS host owner decision review checklist + IwoooS host owner decision review outcome lanes + IwoooS host owner decision record draft packets + IwoooS host owner decision record draft review checklist + IwoooS host owner decision record draft review outcome lanes + IwoooS host owner decision record write-up packets + IwoooS host owner decision record write-up review checklist + IwoooS host owner decision record write-up review outcome lanes + IwoooS host owner decision record formal candidate packets + IwoooS host owner decision record formal candidate review checklist + IwoooS host owner decision record formal candidate review outcome lanes + IwoooS host owner decision record formal record queue packets + IwoooS host owner decision record formal record queue review checklist + IwoooS host owner decision record formal record queue review outcome lanes + IwoooS host owner decision record human handoff readiness packets + IwoooS host owner decision record human handoff readiness review checklist + IwoooS host owner decision record human handoff readiness review outcome lanes + IwoooS host owner decision record human record owner review candidate packets + IwoooS host owner decision record human record owner review candidate checklist + IwoooS host owner decision record human record owner review candidate outcome lanes + IwoooS host owner decision record human record owner review preparation packets + IwoooS host owner decision record human record owner review preparation checklist + IwoooS progress acceleration lanes + IwoooS owner response next-action focus + IwoooS S4.9 owner response preflight + IwoooS S4.9 owner response request templates + IwoooS progress hold movement gates + IwoooS AwoooP read-only landing readiness + IwoooS AwoooP cross-session handoff packets + AwoooP 首頁 IwoooS 資安鏡像候選 + AwoooP 工作鏈路 IwoooS 資安鏡像候選 + AwoooP 審批佇列 IwoooS owner response 只讀焦點 | | 本階段追加 | AwoooP 合約儀表板 IwoooS 資安契約只讀候選 + AwoooP 租戶管理 IwoooS 資安租戶範圍只讀候選 + AwoooP 執行監控 IwoooS 執行狀態只讀候選 + 既有安全 / 合規頁面 IwoooS 只讀反向橋接 + 告警 / 錯誤 / 授權 / 治理頁面 IwoooS 只讀反向橋接 + 稽核 / 工程審查頁面 IwoooS 深色只讀反向橋接 + IwoooS 前端資安頁面連接狀態板 + IwoooS GitHub 主要來源就緒度只讀狀態板 + AwoooP 工作鏈路 GitHub 主要來源就緒度只讀工作項 + AwoooP 合約儀表板 GitHub 主要來源就緒度合約只讀候選 + AwoooP 審批佇列 GitHub 主要來源就緒度審批邊界 + AwoooP 首頁 GitHub 主要來源就緒度只讀摘要 + AwoooP 租戶管理 GitHub 主要來源就緒度租戶範圍 + AwoooP 執行監控 GitHub 主要來源就緒度執行邊界 + IwoooS / AwoooP 資安可視區塊繁體中文呈現防護檢查 + AwoooP 執行詳情 / 審批詳情繁體中文呈現防護檢查 + AwoooP 首頁負責人回覆驗收總覽 + AwoooP 工作鏈路負責人回覆驗收只讀工作項 + AwoooP 合約儀表板負責人回覆驗收契約只讀候選 + AwoooP 審批佇列負責人回覆驗收只讀審查邊界 + AwoooP 租戶管理負責人回覆驗收租戶範圍 + AwoooP 執行監控負責人回覆驗收執行邊界 + AwoooP 執行詳情負責人回覆驗收詳情邊界 + AwoooP 審批決策負責人回覆驗收審批邊界 + IwoooS AwoooP 資安入口覆蓋狀態板 + IwoooS 階段式資安收斂節奏圖 + IwoooS 下一步人工收件作戰板 + IwoooS 人工回覆安全驗收閘道 + IwoooS 人工回覆審查結果分流 + IwoooS 人工決策準備佇列 + IwoooS 人工決策紀錄草稿防誤用 + IwoooS 人工決策正式紀錄負責人指派確認準備包 + IwoooS 人工決策正式紀錄負責人指派確認清單 + IwoooS 人工決策正式紀錄負責人指派確認結果分流 + IwoooS 人工決策正式紀錄負責人指派決策準備包 + IwoooS 人工決策正式紀錄負責人指派決策檢查清單 + IwoooS S4.9 負責人回覆封套欄位 + IwoooS S4.9 負責人回覆封套送件前檢查 + IwoooS S4.9 負責人回覆封套送件前結果分流 + IwoooS S4.9 負責人回覆送件請求草稿 + IwoooS S4.9 負責人回覆送件鏈路摘要 + IwoooS 低摩擦分階段收斂主控 + IwoooS 低摩擦下一步行動邊界 + IwoooS 64% 進度移動訊號驗收條 + IwoooS 第一個進度解鎖路徑 + IwoooS 第一解鎖證據包 + IwoooS 第一解鎖證據包預檢分流 + IwoooS 第一解鎖證據包補件路徑 + IwoooS 第一解鎖證據包補件送審前檢查 + IwoooS 第一解鎖證據包補件送審結果分流 + IwoooS 第一解鎖證據包 reviewer 指派準備包 + IwoooS 第一解鎖證據包 reviewer 指派前檢查 + IwoooS 第一解鎖證據包 reviewer 指派前檢查結果分流 + IwoooS 正式只讀 landing 與 Kali 112 只讀證據進度重估 | | 本階段追加補充 | IwoooS 目前具體工作地圖 + IwoooS 目前具體交付清單 + IwoooS 目前阻塞與解除條件 + IwoooS 三軸進度與全產品套用範圍 + IwoooS 全產品分階段套用台帳 + IwoooS 全產品 rollout 波次驗收門檻 + IwoooS 全產品 rollout 驗收結果分流 + IwoooS 全產品證據接線地圖 + IwoooS 全產品證據接線預檢 + IwoooS 全產品證據接線預檢結果分流 + IwoooS 全產品預檢補件回收台帳 + IwoooS 全產品補件重試門檻 + IwoooS 全產品重試結果分流 + IwoooS 全產品人工審查候選準備 + IwoooS 全產品人工審查候選預檢 + IwoooS 全產品人工審查候選預檢結果分流 + IwoooS 全產品人工審查候選預檢補件回收台帳 + IwoooS 全產品人工審查候選預檢補件重試門檻 + IwoooS 全產品只讀套用快照 + P2-145 owner response acceptance gate 正式驗證完成 | -| P0 追加 | IwoooS P0 配置控管優先序前台正式驗證完成;Nginx public gateway、DNS / TLS / certbot、K8s / ArgoCD / production manifests、Workflow / runner / secret metadata、Public / admin / API runtime config、agent-bounty runtime / treasury 六類先列為即時風險配置;高價值配置 Gate 已補上 `k8s/nginx/**`、`scripts/ops/**/*cert*`、`scripts/ops/**/*tls*`,sample 從 `matched=0 / C0=0` 收斂到 `matched=3 / C0=2`;Gate 預設工作樹 preflight 已可讀取 staged / unstaged / untracked,本地 smoke 對臨時 `k8s/nginx/*` 檔命中 C0;Owner Packet snapshot 已同步為 `packets=3 / c0=2`,Coverage snapshot 已同步最新 patterns;IwoooS / AwoooP 前台 Owner Packet 摘要已正式驗證 `packet=3 / c0=2`,feature commit `e999c16b`、deploy marker `16c6b983`、Gitea code-review `2973` / CD `2972` success;IwoooS posture projection snapshot / schema / guard 已同步 `packet=3 / c0=2`,不再保留舊 `1 / 0` 口徑;高價值配置 Owner Packet 收件預檢已新增 `checks=9 / lanes=5 / required_fields=27 / blocked_requests=16`;高價值配置 Owner Request 草稿包已新增 `drafts=3 / handoff_fields=11 / forbidden_payloads=12 / sent=0`;Public Gateway live conf 匯出請求包已新增 `requests=3 / c0=2 / redaction_rules=8 / received=0`;Public Gateway redacted export 收件預檢已新增 `candidates=3 / c0=2 / checks=10 / rejection_guards=12 / received=0 / accepted=0`;Public Gateway rendered diff / nginx gate 草稿已新增 `candidates=3 / c0=2 / stages=7 / blocked=14 / rendered_diff=0 / runtime=0`;Public Gateway owner response acceptance 只讀帳本已新增 `candidates=3 / c0=2 / fields=12 / checks=12 / lanes=7 / blocked=18 / accepted=0 / runtime=0`;DNS / TLS / certbot Owner Confirmation Request 已新增 `requests=4 / c0=4 / fields=9 / questions=5 / guards=12 / received=0 / accepted=0`;K8s / ArgoCD manifest repo-only 清冊已新增 `files=49 / c0=36 / yaml=45 / kinds=20 / blocked=13 / runtime=0`;K8s / ArgoCD Owner Request Draft 已新增 `drafts=4 / c0=3 / fields=11 / sent=0 / runtime=0`;K8s / ArgoCD owner response acceptance 只讀帳本已新增 `candidates=4 / c0=3 / fields=11 / checks=12 / lanes=7 / blocked=18 / accepted=0 / runtime=0`;K8s / ArgoCD GitOps 變更證據驗收已新增 `candidates=4 / c0=3 / write_capable=4 / evidence_fields=18 / checks=18 / lanes=8 / blocked=28 / accepted=0 / runtime=0`;CD / Runner / Secret 注入變更證據驗收已新增 `candidates=5 / c0=4 / write_capable=5 / workflow_files=33 / secret_names=42 / runner_labels=5 / evidence_fields=19 / checks=19 / lanes=8 / blocked=32 / accepted=0 / runtime=0`;Backup / Restore / Escrow owner response acceptance 只讀帳本已新增 `candidates=38 / write_capable=27 / fields=14 / checks=13 / lanes=7 / blocked=22 / accepted=0 / runtime=0`;SSH / Firewall / Network Access owner response acceptance 只讀帳本已新增 `candidates=16 / write_capable=6 / fields=13 / checks=15 / lanes=7 / blocked=22 / accepted=0 / runtime=0`;端口 / 防火牆變更證據驗收只讀帳本已新增 `candidates=14 / write_capable=6 / policy_or_exposure=5 / evidence_fields=16 / checks=16 / lanes=8 / blocked=24 / accepted=0 / runtime=0`;owner response / live evidence / runtime gate / action buttons 仍全部為 0 | +| P0 追加 | IwoooS P0 配置控管優先序前台正式驗證完成;Nginx public gateway、DNS / TLS / certbot、K8s / ArgoCD / production manifests、Workflow / runner / secret metadata、Public / admin / API runtime config、agent-bounty runtime / treasury 六類先列為即時風險配置;高價值配置 Gate 已補上 `k8s/nginx/**`、`scripts/ops/**/*cert*`、`scripts/ops/**/*tls*`,sample 從 `matched=0 / C0=0` 收斂到 `matched=3 / C0=2`;Gate 預設工作樹 preflight 已可讀取 staged / unstaged / untracked,本地 smoke 對臨時 `k8s/nginx/*` 檔命中 C0;Owner Packet snapshot 已同步為 `packets=3 / c0=2`,Coverage snapshot 已同步最新 patterns;IwoooS / AwoooP 前台 Owner Packet 摘要已正式驗證 `packet=3 / c0=2`,feature commit `e999c16b`、deploy marker `16c6b983`、Gitea code-review `2973` / CD `2972` success;IwoooS posture projection snapshot / schema / guard 已同步 `packet=3 / c0=2`,不再保留舊 `1 / 0` 口徑;高價值配置 Owner Packet 收件預檢已新增 `checks=9 / lanes=5 / required_fields=27 / blocked_requests=16`;高價值配置 Owner Request 草稿包已新增 `drafts=3 / handoff_fields=11 / forbidden_payloads=12 / sent=0`;Public Gateway live conf 匯出請求包已新增 `requests=3 / c0=2 / redaction_rules=8 / received=0`;Public Gateway redacted export 收件預檢已新增 `candidates=3 / c0=2 / checks=10 / rejection_guards=12 / received=0 / accepted=0`;Public Gateway rendered diff / nginx gate 草稿已新增 `candidates=3 / c0=2 / stages=7 / blocked=14 / rendered_diff=0 / runtime=0`;Public Gateway owner response acceptance 只讀帳本已新增 `candidates=3 / c0=2 / fields=12 / checks=12 / lanes=7 / blocked=18 / accepted=0 / runtime=0`;DNS / TLS / certbot Owner Confirmation Request 已新增 `requests=4 / c0=4 / fields=9 / questions=5 / guards=12 / received=0 / accepted=0`;K8s / ArgoCD manifest repo-only 清冊已新增 `files=49 / c0=36 / yaml=45 / kinds=20 / blocked=13 / runtime=0`;K8s / ArgoCD Owner Request Draft 已新增 `drafts=4 / c0=3 / fields=11 / sent=0 / runtime=0`;K8s / ArgoCD owner response acceptance 只讀帳本已新增 `candidates=4 / c0=3 / fields=11 / checks=12 / lanes=7 / blocked=18 / accepted=0 / runtime=0`;K8s / ArgoCD GitOps 變更證據驗收已新增 `candidates=4 / c0=3 / write_capable=4 / evidence_fields=18 / checks=18 / lanes=8 / blocked=28 / accepted=0 / runtime=0`;CD / Runner / Secret 注入變更證據驗收已新增 `candidates=5 / c0=4 / write_capable=5 / workflow_files=33 / secret_names=42 / runner_labels=5 / evidence_fields=19 / checks=19 / lanes=8 / blocked=32 / accepted=0 / runtime=0`;Public / Admin / API runtime config 變更證據驗收已新增 `candidates=6 / c0=5 / write_capable=6 / source_refs=20 / evidence_fields=21 / checks=21 / lanes=8 / blocked=32 / accepted=0 / runtime=0`,並把 raw namespace、repo slug、內部狀態碼與內部協作內容外洩列為拒收 / 隔離;Backup / Restore / Escrow owner response acceptance 只讀帳本已新增 `candidates=38 / write_capable=27 / fields=14 / checks=13 / lanes=7 / blocked=22 / accepted=0 / runtime=0`;SSH / Firewall / Network Access owner response acceptance 只讀帳本已新增 `candidates=16 / write_capable=6 / fields=13 / checks=15 / lanes=7 / blocked=22 / accepted=0 / runtime=0`;端口 / 防火牆變更證據驗收只讀帳本已新增 `candidates=14 / write_capable=6 / policy_or_exposure=5 / evidence_fields=16 / checks=16 / lanes=8 / blocked=24 / accepted=0 / runtime=0`;owner response / live evidence / runtime gate / action buttons 仍全部為 0 | | P0 agent-bounty 追加 | agent-bounty-protocol Owner Request Draft 已新增 `drafts=11 / control=4 / surface=7 / write_capable=8 / treasury=4 / mcp_a2a=5 / fields=22 / forbidden_inputs=25 / blocked=28 / sent=0 / runtime=0`;這是 repo / refs、deployment、data classification、MCP / A2A、cron / daemon、admin / treasury、webhook / traffic 的人工送件前草稿,不是 owner response、repo push、refs sync、workflow 修改、secret 收集、deploy、compose restart、DB migration、claim / submit、payout / withdrawal、cron / daemon、external send、host write 或 runtime gate | | P1 追加 | Docker / systemd / Host Service Owner Request Draft 已新增 `drafts=9 / write_capable=3 / fields=12 / blocked=14 / sent=0 / runtime=0`;SSH / Firewall / Network Access Owner Request Draft 已新增 `drafts=16 / write_capable=6 / fields=13 / blocked=16 / sent=0 / runtime=0`;Backup / Restore / Escrow Owner Request Draft 已新增 `drafts=38 / write_capable=27 / fields=14 / blocked=18 / sent=0 / runtime=0`;Backup / Restore / Escrow Owner Response Acceptance 已新增 `candidates=38 / write_capable=27 / reviewer_checks=13 / lanes=7 / blocked=22 / accepted=0 / runtime=0`;Monitoring / Alerting / Observability Owner Request Draft 已新增 `drafts=60 / write_capable=11 / fields=14 / blocked=24 / sent=0 / runtime=0`;上述全部仍是人工送件前草稿或只讀 acceptance 帳本,不是 owner response、live evidence、reload、restart、backup、restore、Telegram send、alert smoke、host write 或 runtime gate | | 原則 | 低摩擦分階段;文件、schema、read-only evidence 優先;不做 runtime enforcement、不切 primary | | P0 主控板 | `docs/workplans/2026-06-04-iwooos-security-governance-p0.md` | +## 0.00aaaaa 2026-06-15 Public / Admin / API runtime config 變更證據驗收 + +本輪把公開產品頁、AwoooP 後台、API / CORS、frontend env、Sentry tunnel、webhook / callback 與跨產品 runtime route 從 policy-ready 推進到變更證據驗收只讀帳本:`public_runtime_config_change_evidence_acceptance_v1` 固定 `candidates=6`、`c0=5`、`c1=1`、`write_capable=6`、`source_refs=20`、`required_evidence_fields=21`、`reviewer_checks=21`、`outcome_lanes=8`、`blocked_actions=32`,並讓 `public_admin_api_runtime_config` 只讀治理成熟度 `62% -> 64%`,高價值配置平均只讀成熟度仍維持 `68%`。這是 metadata-only 收件驗收,不是 affected route accepted、admin/auth boundary accepted、API readback accepted、CORS diff accepted、frontend env diff accepted、i18n redaction accepted、desktop/mobile smoke accepted、sensitive scan accepted、runtime approval package、route / CORS / env / auth / webhook 變更、frontend / API deploy、production deploy 或 runtime gate。 + +同步邊界:IwoooS headline 維持 `64%`,active runtime gate 維持 `0`;raw owner namespace、repo slug、內部狀態碼、內部協作文字、cookie、token、secret value、DSN value、raw payload 與未脫敏截圖均為拒收或隔離條件。前台只能顯示脫敏產品 / 專案名稱與控管狀態,不得顯示個人 namespace、內部協作內容或抱怨語句。本段只更新文件、snapshot、guard、覆蓋矩陣與前端 marker,不改 route、不改 CORS、不改 env、不部署、不碰主機。 + ## 0.00aaaa 2026-06-15 CD / Runner / Secret 注入變更證據驗收 本輪把 CD pipeline、程式碼審查、部署通知、執行器證明與 repository secret name parity / injection owner 從 workflow / secret name owner response 再補一層變更證據驗收只讀帳本:`cd_runner_secret_injection_change_evidence_acceptance_v1` 固定 `candidates=5`、`c0=4`、`c1=1`、`write_capable=5`、`workflow_files=33`、`referenced_secret_names=42`、`runner_labels=5`、`required_evidence_fields=19`、`reviewer_checks=19`、`outcome_lanes=8`、`blocked_actions=32`,並讓 `secret_metadata` 只讀治理成熟度 `66% -> 68%`、`gitea_workflow_runner_source_control` 只讀治理成熟度 `70% -> 72%`,高價值配置平均只讀成熟度仍維持 `68%`。這是 metadata-only 收件驗收,不是 workflow diff accepted、runner attestation accepted、secret parity accepted、secret injection route accepted、deploy marker readback accepted、guard result accepted、post-check accepted、runtime approval package、workflow modification、runner change、repo secret change、secret rotation、Gitea action dispatch、production deploy 或 runtime gate。 diff --git a/docs/security/high-value-config-control-coverage.snapshot.json b/docs/security/high-value-config-control-coverage.snapshot.json index 24f92b8a2..9d8c1812d 100644 --- a/docs/security/high-value-config-control-coverage.snapshot.json +++ b/docs/security/high-value-config-control-coverage.snapshot.json @@ -214,15 +214,17 @@ "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。", + "coverage_percent": 64, + "coverage_status": "change_evidence_acceptance_ready_needs_runtime_config_owner_evidence", + "current_gap": "已固定 Public / Admin / API runtime config 變更證據驗收只讀帳本;affected route、admin/auth boundary、API readback、CORS diff、frontend env diff、i18n redaction、webhook owner、desktop/mobile smoke、sensitive string scan、rollback 與 post-check evidence 仍全部為 0。", "evidence_refs": [ "docs/HARD_RULES.md", - "docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md" + "docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md", + "docs/security/PUBLIC-RUNTIME-CONFIG-CHANGE-EVIDENCE-ACCEPTANCE.md", + "docs/security/public-runtime-config-change-evidence-acceptance.snapshot.json" ], "label": "Public / admin / API / frontend runtime config", - "next_owner_action": "補 affected route、admin/auth boundary、CORS/public URL 與 desktop/mobile smoke plan。", + "next_owner_action": "補 affected route refs、admin/auth boundary、API contract readback、CORS origin diff、frontend env diff、i18n redaction review、webhook/callback owner、desktop/mobile smoke、sensitive string scan、rollback owner 與 post-check evidence。", "owner_response_accepted": false, "owner_response_received": false, "owner_response_required": true, @@ -557,21 +559,35 @@ "agent_bounty_runtime_authorized": false, "alert_chain_smoke_authorized": false, "alertmanager_reload_authorized": false, + "api_contract_change_authorized": false, + "api_route_change_authorized": false, "argocd_api_read_authorized": false, "argocd_sync_authorized": false, "backup_run_authorized": false, + "callback_url_change_authorized": false, "certbot_renew_authorized": false, + "cookie_policy_change_authorized": false, + "cors_change_authorized": false, "credential_escrow_marker_write_authorized": false, + "csrf_disable_authorized": false, + "database_migration_authorized": false, + "desktop_mobile_smoke_authorized": false, "dns_tls_change_authorized": false, "exporter_deploy_authorized": false, "force_push_authorized": false, + "frontend_env_change_authorized": false, "grafana_dashboard_apply_authorized": false, "helm_upgrade_authorized": false, "host_live_conf_read_authorized": false, "host_write_authorized": false, + "i18n_public_text_internal_identity_allowed": false, + "internal_ip_exposure_allowed": false, + "internal_status_code_exposure_allowed": false, + "internal_transcript_exposure_allowed": false, "kubectl_action_authorized": false, "langfuse_config_change_authorized": false, "live_alert_fire_authorized": false, + "middleware_auth_change_authorized": false, "network_policy_apply_authorized": false, "nginx_reload_authorized": false, "nginx_test_authorized": false, @@ -580,15 +596,19 @@ "offsite_remote_delete_authorized": false, "offsite_sync_authorized": false, "otel_collector_reload_authorized": false, + "owner_namespace_exposure_allowed": false, "payout_or_withdrawal_authorized": false, "prometheus_reload_authorized": false, "public_gateway_reload_authorized": false, "public_route_change_authorized": false, + "rate_limit_disable_authorized": false, + "raw_payload_storage_allowed": false, "rbac_change_authorized": false, "rclone_config_authorized": false, "receiver_route_change_authorized": false, "refs_sync_authorized": false, "remote_write_change_authorized": false, + "repo_namespace_exposure_allowed": false, "restic_prune_authorized": false, "restore_drill_authorized": false, "restore_run_authorized": false, @@ -596,19 +616,22 @@ "rollback_executed": false, "route_smoke_authorized": false, "runner_change_authorized": false, + "runtime_config_change_authorized": false, "runtime_execution_authorized": false, "secret_value_collection_allowed": false, + "security_header_change_authorized": false, "sentry_deploy_authorized": false, "signoz_rule_apply_authorized": false, "silence_policy_change_authorized": false, "telegram_send_authorized": false, "velero_restore_authorized": false, "webhook_receiver_change_authorized": false, + "webhook_secret_change_authorized": false, "websocket_route_change_authorized": false, "workflow_modification_authorized": false }, - "generated_at": "2026-06-15T03:20:00+08:00", - "git_commit": "a8ade565", + "generated_at": "2026-06-15T04:20:00+08:00", + "git_commit": "77a76d1a", "lowest_coverage_categories": [ { "category_id": "docker_compose_systemd_host_config", @@ -632,11 +655,11 @@ "next_owner_action": "補 provider owner、fallback order、cost review、privacy review、benchmark 與 rollback owner。" }, { - "category_id": "public_admin_api_runtime_config", + "category_id": "backup_restore_credential", "coverage_percent": 62, - "current_gap": "每次產品 route / admin / API / frontend config 變更仍需逐次 smoke 與 owner gate。", - "label": "Public / admin / API / frontend runtime config", - "next_owner_action": "補 affected route、admin/auth boundary、CORS/public URL 與 desktop/mobile smoke plan。" + "current_gap": "已固定 owner response acceptance 只讀帳本;restore drill、offsite sync、credential escrow、retention change、live evidence 與 owner response 仍全部為 0。", + "label": "Backup / restore / escrow / retention", + "next_owner_action": "補 restore drill approval package、offsite owner、escrow owner、retention owner、rollback owner、validation plan 與 no-secret-value evidence。" } ], "next_collection_order": [ @@ -645,6 +668,7 @@ "k8s_production_gitops", "secret_metadata", "gitea_workflow_runner_source_control", + "public_admin_api_runtime_config", "agent_bounty_protocol_runtime", "docker_compose_systemd_host_config", "monitoring_alerting_observability", @@ -669,7 +693,7 @@ "c3_category_count": 1, "category_count": 14, "lowest_coverage_category_count": 4, - "needs_live_evidence_count": 8, + "needs_live_evidence_count": 9, "owner_response_accepted_count": 0, "owner_response_received_count": 0, "owner_response_required_count": 14, diff --git a/docs/security/iwooos-posture-projection.snapshot.json b/docs/security/iwooos-posture-projection.snapshot.json index b206b1650..b256efd77 100644 --- a/docs/security/iwooos-posture-projection.snapshot.json +++ b/docs/security/iwooos-posture-projection.snapshot.json @@ -8391,6 +8391,8 @@ "docs/security/SSH-NETWORK-OWNER-RESPONSE-ACCEPTANCE.md", "docs/security/port-firewall-change-evidence-acceptance.snapshot.json", "docs/security/PORT-FIREWALL-CHANGE-EVIDENCE-ACCEPTANCE.md", + "docs/security/public-runtime-config-change-evidence-acceptance.snapshot.json", + "docs/security/PUBLIC-RUNTIME-CONFIG-CHANGE-EVIDENCE-ACCEPTANCE.md", "docs/schemas/ssh_network_access_inventory_v1.schema.json", "docs/security/backup-restore-escrow-inventory.snapshot.json", "docs/security/BACKUP-RESTORE-ESCROW-INVENTORY.md", @@ -8616,7 +8618,7 @@ "high_value_config_control_coverage_first_layer": true, "high_value_config_control_coverage_item_count": 4, "high_value_config_control_coverage_lowest_category_count": 4, - "high_value_config_control_coverage_needs_live_evidence_count": 8, + "high_value_config_control_coverage_needs_live_evidence_count": 9, "high_value_config_control_coverage_owner_response_accepted_count": 0, "high_value_config_control_coverage_owner_response_received_count": 0, "high_value_config_control_coverage_owner_response_required_count": 14, @@ -8776,6 +8778,33 @@ "port_firewall_change_evidence_acceptance_reviewer_check_count": 16, "port_firewall_change_evidence_acceptance_runtime_gate_count": 0, "port_firewall_change_evidence_acceptance_write_capable_candidate_count": 6, + "public_runtime_config_change_evidence_acceptance_accepted_count": 0, + "public_runtime_config_change_evidence_acceptance_action_button_count": 0, + "public_runtime_config_change_evidence_acceptance_admin_auth_boundary_accepted_count": 0, + "public_runtime_config_change_evidence_acceptance_api_contract_readback_accepted_count": 0, + "public_runtime_config_change_evidence_acceptance_blocked_action_count": 32, + "public_runtime_config_change_evidence_acceptance_c0_candidate_count": 5, + "public_runtime_config_change_evidence_acceptance_c1_candidate_count": 1, + "public_runtime_config_change_evidence_acceptance_candidate_count": 6, + "public_runtime_config_change_evidence_acceptance_cors_origin_diff_accepted_count": 0, + "public_runtime_config_change_evidence_acceptance_coverage_percent_after_acceptance": 64, + "public_runtime_config_change_evidence_acceptance_coverage_percent_before_acceptance": 62, + "public_runtime_config_change_evidence_acceptance_desktop_mobile_smoke_accepted_count": 0, + "public_runtime_config_change_evidence_acceptance_first_layer": true, + "public_runtime_config_change_evidence_acceptance_frontend_env_diff_accepted_count": 0, + "public_runtime_config_change_evidence_acceptance_i18n_redaction_review_accepted_count": 0, + "public_runtime_config_change_evidence_acceptance_outcome_lane_count": 8, + "public_runtime_config_change_evidence_acceptance_postcheck_evidence_accepted_count": 0, + "public_runtime_config_change_evidence_acceptance_received_count": 0, + "public_runtime_config_change_evidence_acceptance_required_evidence_field_count": 21, + "public_runtime_config_change_evidence_acceptance_reviewer_check_count": 21, + "public_runtime_config_change_evidence_acceptance_route_scope_accepted_count": 0, + "public_runtime_config_change_evidence_acceptance_runtime_approval_package_ready_count": 0, + "public_runtime_config_change_evidence_acceptance_runtime_gate_count": 0, + "public_runtime_config_change_evidence_acceptance_sensitive_string_scan_accepted_count": 0, + "public_runtime_config_change_evidence_acceptance_source_ref_count": 20, + "public_runtime_config_change_evidence_acceptance_webhook_callback_owner_accepted_count": 0, + "public_runtime_config_change_evidence_acceptance_write_capable_candidate_count": 6, "professional_security_experience_asset_heat_cell_count": 9, "professional_security_experience_attack_path_node_count": 6, "professional_security_experience_default_visible": false, diff --git a/docs/security/public-runtime-config-change-evidence-acceptance.snapshot.json b/docs/security/public-runtime-config-change-evidence-acceptance.snapshot.json new file mode 100644 index 000000000..8471865e4 --- /dev/null +++ b/docs/security/public-runtime-config-change-evidence-acceptance.snapshot.json @@ -0,0 +1,1521 @@ +{ + "blocked_actions": [ + "change_public_route", + "change_admin_route", + "change_api_route", + "change_cors_origin", + "modify_next_public_env", + "expose_internal_ip", + "expose_repo_slug", + "expose_owner_namespace", + "expose_secret_value", + "bypass_auth", + "change_callback_url", + "change_webhook_secret", + "modify_middleware_auth", + "disable_csrf", + "disable_rate_limit", + "change_cookie_policy", + "change_security_headers", + "publish_internal_transcript", + "publish_internal_status_code", + "deploy_frontend", + "deploy_api", + "rewrite_nginx_route", + "change_public_url", + "change_openapi_contract", + "mutate_database", + "run_migration", + "send_webhook", + "active_scan", + "enable_action_button", + "production_deploy", + "force_push", + "switch_github_primary" + ], + "change_evidence_candidates": [ + { + "action_buttons_allowed": false, + "admin_auth_boundary_accepted": false, + "admin_auth_boundary_ref": null, + "admin_route_change_authorized": false, + "affected_route_refs": [], + "affected_scope": "公開產品頁、IwoooS / AwoooP / Tenants / Code Review 前台文案、raw identity 與內部協作文字防外洩", + "api_contract_change_authorized": false, + "api_contract_readback_accepted": false, + "api_contract_readback_ref": null, + "api_health_readback_ref": null, + "api_route_change_authorized": false, + "blast_radius": "pending_change_evidence", + "blocked_actions": [ + "change_public_route", + "change_admin_route", + "change_api_route", + "change_cors_origin", + "modify_next_public_env", + "expose_internal_ip", + "expose_repo_slug", + "expose_owner_namespace", + "expose_secret_value", + "bypass_auth", + "change_callback_url", + "change_webhook_secret", + "modify_middleware_auth", + "disable_csrf", + "disable_rate_limit", + "change_cookie_policy", + "change_security_headers", + "publish_internal_transcript", + "publish_internal_status_code", + "deploy_frontend", + "deploy_api", + "rewrite_nginx_route", + "change_public_url", + "change_openapi_contract", + "mutate_database", + "run_migration", + "send_webhook", + "active_scan", + "enable_action_button", + "production_deploy", + "force_push", + "switch_github_primary" + ], + "callback_url_change_authorized": false, + "change_evidence_accepted": false, + "change_evidence_candidate_id": "public_runtime_config_change_evidence:public_product_route_and_i18n_redaction", + "change_evidence_fields": [ + "change_evidence_candidate_id", + "source_refs", + "control_tier", + "proposed_runtime_config_change_ref", + "affected_route_refs", + "public_url_or_domain_ref", + "admin_auth_boundary_ref", + "api_contract_readback_ref", + "cors_origin_diff_ref", + "frontend_env_diff_ref", + "i18n_redaction_review_ref", + "webhook_callback_owner_ref", + "desktop_mobile_smoke_ref", + "api_health_readback_ref", + "sensitive_string_scan_ref", + "console_error_scan_ref", + "blast_radius", + "maintenance_window", + "rollback_owner", + "rollback_plan_ref", + "postcheck_evidence_ref", + "redacted_evidence_refs", + "reviewer_outcome", + "not_approval" + ], + "change_evidence_quarantined": false, + "change_evidence_received": false, + "change_evidence_rejected": false, + "console_error_scan_ref": null, + "control_tier": "C0", + "cookie_policy_change_authorized": false, + "cors_change_authorized": false, + "cors_origin_diff_accepted": false, + "cors_origin_diff_ref": null, + "csrf_disable_authorized": false, + "database_migration_authorized": false, + "desktop_mobile_smoke_accepted": false, + "desktop_mobile_smoke_authorized": false, + "desktop_mobile_smoke_ref": null, + "force_push_authorized": false, + "frontend_env_change_authorized": false, + "frontend_env_diff_accepted": false, + "frontend_env_diff_ref": null, + "github_primary_switch_authorized": false, + "i18n_public_text_internal_identity_allowed": false, + "i18n_redaction_review_accepted": false, + "i18n_redaction_review_ref": null, + "internal_ip_exposure_allowed": false, + "internal_status_code_exposure_allowed": false, + "internal_transcript_exposure_allowed": false, + "maintenance_window": "pending_change_evidence", + "middleware_auth_change_authorized": false, + "not_approval": true, + "outcome_lanes": [ + "waiting_change_evidence", + "quarantine_sensitive_payload", + "reject_unredacted_or_runtime_claim", + "request_supplement", + "ready_for_reviewer_acceptance", + "ready_for_runtime_approval_package", + "waiting_maintenance_window", + "waiting_runtime_gate" + ], + "owner_namespace_exposure_allowed": false, + "partial_token_collection_allowed": false, + "postcheck_evidence_accepted": false, + "postcheck_evidence_ref": null, + "production_deploy_authorized": false, + "proposed_runtime_config_change_ref": null, + "public_route_change_authorized": false, + "public_url_or_domain_ref": null, + "rate_limit_disable_authorized": false, + "raw_payload_storage_allowed": false, + "redacted_evidence_refs": [], + "repo_namespace_exposure_allowed": false, + "required_evidence_fields": [ + "proposed_runtime_config_change_ref", + "affected_route_refs", + "public_url_or_domain_ref", + "admin_auth_boundary_ref", + "api_contract_readback_ref", + "cors_origin_diff_ref", + "frontend_env_diff_ref", + "i18n_redaction_review_ref", + "webhook_callback_owner_ref", + "desktop_mobile_smoke_ref", + "api_health_readback_ref", + "sensitive_string_scan_ref", + "console_error_scan_ref", + "blast_radius", + "maintenance_window", + "rollback_owner", + "rollback_plan_ref", + "postcheck_evidence_ref", + "redacted_evidence_refs", + "reviewer_outcome", + "not_approval" + ], + "requires_runtime_approval_package": true, + "reviewer_checks": [ + "change_ref_present", + "affected_route_refs_present", + "public_url_not_internal_ip", + "admin_auth_boundary_called_out", + "api_contract_readback_present", + "cors_origin_diff_ref_only", + "frontend_env_diff_ref_present", + "i18n_redaction_review_present", + "webhook_callback_owner_present", + "desktop_mobile_smoke_present", + "api_health_readback_present", + "sensitive_string_scan_present", + "console_error_scan_present", + "no_secret_value_or_cookie", + "security_header_or_cookie_impact_called_out", + "blast_radius_present", + "maintenance_window_present", + "rollback_owner_present", + "postcheck_evidence_present", + "no_runtime_action_claim", + "cross_project_sync_noted" + ], + "reviewer_outcome": "waiting_change_evidence", + "risk": "HIGH", + "rollback_owner": "pending_change_evidence", + "rollback_plan_ref": null, + "route_scope_accepted": false, + "route_smoke_authorized": false, + "runtime_approval_package_ready": false, + "runtime_config_change_authorized": false, + "runtime_execution_authorized": false, + "runtime_gate": false, + "secret_hash_collection_allowed": false, + "secret_value_collection_allowed": false, + "security_header_change_authorized": false, + "sensitive_string_scan_accepted": false, + "sensitive_string_scan_ref": null, + "source_ref_existing_count": 3, + "source_refs": [ + "apps/web/src/app/[locale]", + "apps/web/messages/zh-TW.json", + "apps/web/messages/en.json" + ], + "status": "waiting_change_evidence", + "title": "Public product route / i18n redaction boundary", + "webhook_callback_owner_accepted": false, + "webhook_callback_owner_ref": null, + "webhook_receiver_change_authorized": false, + "webhook_secret_change_authorized": false, + "write_capable": true + }, + { + "action_buttons_allowed": false, + "admin_auth_boundary_accepted": false, + "admin_auth_boundary_ref": null, + "admin_route_change_authorized": false, + "affected_route_refs": [], + "affected_scope": "AwoooP operator console、approvals、work-items、runs、admin auth / CSRF / owner guard", + "api_contract_change_authorized": false, + "api_contract_readback_accepted": false, + "api_contract_readback_ref": null, + "api_health_readback_ref": null, + "api_route_change_authorized": false, + "blast_radius": "pending_change_evidence", + "blocked_actions": [ + "change_public_route", + "change_admin_route", + "change_api_route", + "change_cors_origin", + "modify_next_public_env", + "expose_internal_ip", + "expose_repo_slug", + "expose_owner_namespace", + "expose_secret_value", + "bypass_auth", + "change_callback_url", + "change_webhook_secret", + "modify_middleware_auth", + "disable_csrf", + "disable_rate_limit", + "change_cookie_policy", + "change_security_headers", + "publish_internal_transcript", + "publish_internal_status_code", + "deploy_frontend", + "deploy_api", + "rewrite_nginx_route", + "change_public_url", + "change_openapi_contract", + "mutate_database", + "run_migration", + "send_webhook", + "active_scan", + "enable_action_button", + "production_deploy", + "force_push", + "switch_github_primary" + ], + "callback_url_change_authorized": false, + "change_evidence_accepted": false, + "change_evidence_candidate_id": "public_runtime_config_change_evidence:admin_auth_and_operator_console_boundary", + "change_evidence_fields": [ + "change_evidence_candidate_id", + "source_refs", + "control_tier", + "proposed_runtime_config_change_ref", + "affected_route_refs", + "public_url_or_domain_ref", + "admin_auth_boundary_ref", + "api_contract_readback_ref", + "cors_origin_diff_ref", + "frontend_env_diff_ref", + "i18n_redaction_review_ref", + "webhook_callback_owner_ref", + "desktop_mobile_smoke_ref", + "api_health_readback_ref", + "sensitive_string_scan_ref", + "console_error_scan_ref", + "blast_radius", + "maintenance_window", + "rollback_owner", + "rollback_plan_ref", + "postcheck_evidence_ref", + "redacted_evidence_refs", + "reviewer_outcome", + "not_approval" + ], + "change_evidence_quarantined": false, + "change_evidence_received": false, + "change_evidence_rejected": false, + "console_error_scan_ref": null, + "control_tier": "C0", + "cookie_policy_change_authorized": false, + "cors_change_authorized": false, + "cors_origin_diff_accepted": false, + "cors_origin_diff_ref": null, + "csrf_disable_authorized": false, + "database_migration_authorized": false, + "desktop_mobile_smoke_accepted": false, + "desktop_mobile_smoke_authorized": false, + "desktop_mobile_smoke_ref": null, + "force_push_authorized": false, + "frontend_env_change_authorized": false, + "frontend_env_diff_accepted": false, + "frontend_env_diff_ref": null, + "github_primary_switch_authorized": false, + "i18n_public_text_internal_identity_allowed": false, + "i18n_redaction_review_accepted": false, + "i18n_redaction_review_ref": null, + "internal_ip_exposure_allowed": false, + "internal_status_code_exposure_allowed": false, + "internal_transcript_exposure_allowed": false, + "maintenance_window": "pending_change_evidence", + "middleware_auth_change_authorized": false, + "not_approval": true, + "outcome_lanes": [ + "waiting_change_evidence", + "quarantine_sensitive_payload", + "reject_unredacted_or_runtime_claim", + "request_supplement", + "ready_for_reviewer_acceptance", + "ready_for_runtime_approval_package", + "waiting_maintenance_window", + "waiting_runtime_gate" + ], + "owner_namespace_exposure_allowed": false, + "partial_token_collection_allowed": false, + "postcheck_evidence_accepted": false, + "postcheck_evidence_ref": null, + "production_deploy_authorized": false, + "proposed_runtime_config_change_ref": null, + "public_route_change_authorized": false, + "public_url_or_domain_ref": null, + "rate_limit_disable_authorized": false, + "raw_payload_storage_allowed": false, + "redacted_evidence_refs": [], + "repo_namespace_exposure_allowed": false, + "required_evidence_fields": [ + "proposed_runtime_config_change_ref", + "affected_route_refs", + "public_url_or_domain_ref", + "admin_auth_boundary_ref", + "api_contract_readback_ref", + "cors_origin_diff_ref", + "frontend_env_diff_ref", + "i18n_redaction_review_ref", + "webhook_callback_owner_ref", + "desktop_mobile_smoke_ref", + "api_health_readback_ref", + "sensitive_string_scan_ref", + "console_error_scan_ref", + "blast_radius", + "maintenance_window", + "rollback_owner", + "rollback_plan_ref", + "postcheck_evidence_ref", + "redacted_evidence_refs", + "reviewer_outcome", + "not_approval" + ], + "requires_runtime_approval_package": true, + "reviewer_checks": [ + "change_ref_present", + "affected_route_refs_present", + "public_url_not_internal_ip", + "admin_auth_boundary_called_out", + "api_contract_readback_present", + "cors_origin_diff_ref_only", + "frontend_env_diff_ref_present", + "i18n_redaction_review_present", + "webhook_callback_owner_present", + "desktop_mobile_smoke_present", + "api_health_readback_present", + "sensitive_string_scan_present", + "console_error_scan_present", + "no_secret_value_or_cookie", + "security_header_or_cookie_impact_called_out", + "blast_radius_present", + "maintenance_window_present", + "rollback_owner_present", + "postcheck_evidence_present", + "no_runtime_action_claim", + "cross_project_sync_noted" + ], + "reviewer_outcome": "waiting_change_evidence", + "risk": "HIGH", + "rollback_owner": "pending_change_evidence", + "rollback_plan_ref": null, + "route_scope_accepted": false, + "route_smoke_authorized": false, + "runtime_approval_package_ready": false, + "runtime_config_change_authorized": false, + "runtime_execution_authorized": false, + "runtime_gate": false, + "secret_hash_collection_allowed": false, + "secret_value_collection_allowed": false, + "security_header_change_authorized": false, + "sensitive_string_scan_accepted": false, + "sensitive_string_scan_ref": null, + "source_ref_existing_count": 3, + "source_refs": [ + "apps/web/src/app/[locale]/awooop", + "apps/api/src/core/awooop_operator_auth.py", + "apps/api/src/core/csrf.py" + ], + "status": "waiting_change_evidence", + "title": "Admin / operator console auth boundary", + "webhook_callback_owner_accepted": false, + "webhook_callback_owner_ref": null, + "webhook_receiver_change_authorized": false, + "webhook_secret_change_authorized": false, + "write_capable": true + }, + { + "action_buttons_allowed": false, + "admin_auth_boundary_accepted": false, + "admin_auth_boundary_ref": null, + "admin_route_change_authorized": false, + "affected_route_refs": [], + "affected_scope": "API base URL、CORS origins、NEXT_PUBLIC build-time config、public domain / internal IP boundary", + "api_contract_change_authorized": false, + "api_contract_readback_accepted": false, + "api_contract_readback_ref": null, + "api_health_readback_ref": null, + "api_route_change_authorized": false, + "blast_radius": "pending_change_evidence", + "blocked_actions": [ + "change_public_route", + "change_admin_route", + "change_api_route", + "change_cors_origin", + "modify_next_public_env", + "expose_internal_ip", + "expose_repo_slug", + "expose_owner_namespace", + "expose_secret_value", + "bypass_auth", + "change_callback_url", + "change_webhook_secret", + "modify_middleware_auth", + "disable_csrf", + "disable_rate_limit", + "change_cookie_policy", + "change_security_headers", + "publish_internal_transcript", + "publish_internal_status_code", + "deploy_frontend", + "deploy_api", + "rewrite_nginx_route", + "change_public_url", + "change_openapi_contract", + "mutate_database", + "run_migration", + "send_webhook", + "active_scan", + "enable_action_button", + "production_deploy", + "force_push", + "switch_github_primary" + ], + "callback_url_change_authorized": false, + "change_evidence_accepted": false, + "change_evidence_candidate_id": "public_runtime_config_change_evidence:api_cors_and_public_url_runtime_config", + "change_evidence_fields": [ + "change_evidence_candidate_id", + "source_refs", + "control_tier", + "proposed_runtime_config_change_ref", + "affected_route_refs", + "public_url_or_domain_ref", + "admin_auth_boundary_ref", + "api_contract_readback_ref", + "cors_origin_diff_ref", + "frontend_env_diff_ref", + "i18n_redaction_review_ref", + "webhook_callback_owner_ref", + "desktop_mobile_smoke_ref", + "api_health_readback_ref", + "sensitive_string_scan_ref", + "console_error_scan_ref", + "blast_radius", + "maintenance_window", + "rollback_owner", + "rollback_plan_ref", + "postcheck_evidence_ref", + "redacted_evidence_refs", + "reviewer_outcome", + "not_approval" + ], + "change_evidence_quarantined": false, + "change_evidence_received": false, + "change_evidence_rejected": false, + "console_error_scan_ref": null, + "control_tier": "C0", + "cookie_policy_change_authorized": false, + "cors_change_authorized": false, + "cors_origin_diff_accepted": false, + "cors_origin_diff_ref": null, + "csrf_disable_authorized": false, + "database_migration_authorized": false, + "desktop_mobile_smoke_accepted": false, + "desktop_mobile_smoke_authorized": false, + "desktop_mobile_smoke_ref": null, + "force_push_authorized": false, + "frontend_env_change_authorized": false, + "frontend_env_diff_accepted": false, + "frontend_env_diff_ref": null, + "github_primary_switch_authorized": false, + "i18n_public_text_internal_identity_allowed": false, + "i18n_redaction_review_accepted": false, + "i18n_redaction_review_ref": null, + "internal_ip_exposure_allowed": false, + "internal_status_code_exposure_allowed": false, + "internal_transcript_exposure_allowed": false, + "maintenance_window": "pending_change_evidence", + "middleware_auth_change_authorized": false, + "not_approval": true, + "outcome_lanes": [ + "waiting_change_evidence", + "quarantine_sensitive_payload", + "reject_unredacted_or_runtime_claim", + "request_supplement", + "ready_for_reviewer_acceptance", + "ready_for_runtime_approval_package", + "waiting_maintenance_window", + "waiting_runtime_gate" + ], + "owner_namespace_exposure_allowed": false, + "partial_token_collection_allowed": false, + "postcheck_evidence_accepted": false, + "postcheck_evidence_ref": null, + "production_deploy_authorized": false, + "proposed_runtime_config_change_ref": null, + "public_route_change_authorized": false, + "public_url_or_domain_ref": null, + "rate_limit_disable_authorized": false, + "raw_payload_storage_allowed": false, + "redacted_evidence_refs": [], + "repo_namespace_exposure_allowed": false, + "required_evidence_fields": [ + "proposed_runtime_config_change_ref", + "affected_route_refs", + "public_url_or_domain_ref", + "admin_auth_boundary_ref", + "api_contract_readback_ref", + "cors_origin_diff_ref", + "frontend_env_diff_ref", + "i18n_redaction_review_ref", + "webhook_callback_owner_ref", + "desktop_mobile_smoke_ref", + "api_health_readback_ref", + "sensitive_string_scan_ref", + "console_error_scan_ref", + "blast_radius", + "maintenance_window", + "rollback_owner", + "rollback_plan_ref", + "postcheck_evidence_ref", + "redacted_evidence_refs", + "reviewer_outcome", + "not_approval" + ], + "requires_runtime_approval_package": true, + "reviewer_checks": [ + "change_ref_present", + "affected_route_refs_present", + "public_url_not_internal_ip", + "admin_auth_boundary_called_out", + "api_contract_readback_present", + "cors_origin_diff_ref_only", + "frontend_env_diff_ref_present", + "i18n_redaction_review_present", + "webhook_callback_owner_present", + "desktop_mobile_smoke_present", + "api_health_readback_present", + "sensitive_string_scan_present", + "console_error_scan_present", + "no_secret_value_or_cookie", + "security_header_or_cookie_impact_called_out", + "blast_radius_present", + "maintenance_window_present", + "rollback_owner_present", + "postcheck_evidence_present", + "no_runtime_action_claim", + "cross_project_sync_noted" + ], + "reviewer_outcome": "waiting_change_evidence", + "risk": "HIGH", + "rollback_owner": "pending_change_evidence", + "rollback_plan_ref": null, + "route_scope_accepted": false, + "route_smoke_authorized": false, + "runtime_approval_package_ready": false, + "runtime_config_change_authorized": false, + "runtime_execution_authorized": false, + "runtime_gate": false, + "secret_hash_collection_allowed": false, + "secret_value_collection_allowed": false, + "security_header_change_authorized": false, + "sensitive_string_scan_accepted": false, + "sensitive_string_scan_ref": null, + "source_ref_existing_count": 4, + "source_refs": [ + "apps/api/src/core/config.py", + "apps/api/src/config.py", + "apps/web/src/lib/config.ts", + "apps/web/src/lib/api-client.ts" + ], + "status": "waiting_change_evidence", + "title": "API / CORS / public URL runtime config", + "webhook_callback_owner_accepted": false, + "webhook_callback_owner_ref": null, + "webhook_receiver_change_authorized": false, + "webhook_secret_change_authorized": false, + "write_capable": true + }, + { + "action_buttons_allowed": false, + "admin_auth_boundary_accepted": false, + "admin_auth_boundary_ref": null, + "admin_route_change_authorized": false, + "affected_route_refs": [], + "affected_scope": "Next.js middleware、Sentry tunnel、browser-facing env、health route 與 console error boundary", + "api_contract_change_authorized": false, + "api_contract_readback_accepted": false, + "api_contract_readback_ref": null, + "api_health_readback_ref": null, + "api_route_change_authorized": false, + "blast_radius": "pending_change_evidence", + "blocked_actions": [ + "change_public_route", + "change_admin_route", + "change_api_route", + "change_cors_origin", + "modify_next_public_env", + "expose_internal_ip", + "expose_repo_slug", + "expose_owner_namespace", + "expose_secret_value", + "bypass_auth", + "change_callback_url", + "change_webhook_secret", + "modify_middleware_auth", + "disable_csrf", + "disable_rate_limit", + "change_cookie_policy", + "change_security_headers", + "publish_internal_transcript", + "publish_internal_status_code", + "deploy_frontend", + "deploy_api", + "rewrite_nginx_route", + "change_public_url", + "change_openapi_contract", + "mutate_database", + "run_migration", + "send_webhook", + "active_scan", + "enable_action_button", + "production_deploy", + "force_push", + "switch_github_primary" + ], + "callback_url_change_authorized": false, + "change_evidence_accepted": false, + "change_evidence_candidate_id": "public_runtime_config_change_evidence:frontend_env_and_sentry_tunnel_runtime_config", + "change_evidence_fields": [ + "change_evidence_candidate_id", + "source_refs", + "control_tier", + "proposed_runtime_config_change_ref", + "affected_route_refs", + "public_url_or_domain_ref", + "admin_auth_boundary_ref", + "api_contract_readback_ref", + "cors_origin_diff_ref", + "frontend_env_diff_ref", + "i18n_redaction_review_ref", + "webhook_callback_owner_ref", + "desktop_mobile_smoke_ref", + "api_health_readback_ref", + "sensitive_string_scan_ref", + "console_error_scan_ref", + "blast_radius", + "maintenance_window", + "rollback_owner", + "rollback_plan_ref", + "postcheck_evidence_ref", + "redacted_evidence_refs", + "reviewer_outcome", + "not_approval" + ], + "change_evidence_quarantined": false, + "change_evidence_received": false, + "change_evidence_rejected": false, + "console_error_scan_ref": null, + "control_tier": "C0", + "cookie_policy_change_authorized": false, + "cors_change_authorized": false, + "cors_origin_diff_accepted": false, + "cors_origin_diff_ref": null, + "csrf_disable_authorized": false, + "database_migration_authorized": false, + "desktop_mobile_smoke_accepted": false, + "desktop_mobile_smoke_authorized": false, + "desktop_mobile_smoke_ref": null, + "force_push_authorized": false, + "frontend_env_change_authorized": false, + "frontend_env_diff_accepted": false, + "frontend_env_diff_ref": null, + "github_primary_switch_authorized": false, + "i18n_public_text_internal_identity_allowed": false, + "i18n_redaction_review_accepted": false, + "i18n_redaction_review_ref": null, + "internal_ip_exposure_allowed": false, + "internal_status_code_exposure_allowed": false, + "internal_transcript_exposure_allowed": false, + "maintenance_window": "pending_change_evidence", + "middleware_auth_change_authorized": false, + "not_approval": true, + "outcome_lanes": [ + "waiting_change_evidence", + "quarantine_sensitive_payload", + "reject_unredacted_or_runtime_claim", + "request_supplement", + "ready_for_reviewer_acceptance", + "ready_for_runtime_approval_package", + "waiting_maintenance_window", + "waiting_runtime_gate" + ], + "owner_namespace_exposure_allowed": false, + "partial_token_collection_allowed": false, + "postcheck_evidence_accepted": false, + "postcheck_evidence_ref": null, + "production_deploy_authorized": false, + "proposed_runtime_config_change_ref": null, + "public_route_change_authorized": false, + "public_url_or_domain_ref": null, + "rate_limit_disable_authorized": false, + "raw_payload_storage_allowed": false, + "redacted_evidence_refs": [], + "repo_namespace_exposure_allowed": false, + "required_evidence_fields": [ + "proposed_runtime_config_change_ref", + "affected_route_refs", + "public_url_or_domain_ref", + "admin_auth_boundary_ref", + "api_contract_readback_ref", + "cors_origin_diff_ref", + "frontend_env_diff_ref", + "i18n_redaction_review_ref", + "webhook_callback_owner_ref", + "desktop_mobile_smoke_ref", + "api_health_readback_ref", + "sensitive_string_scan_ref", + "console_error_scan_ref", + "blast_radius", + "maintenance_window", + "rollback_owner", + "rollback_plan_ref", + "postcheck_evidence_ref", + "redacted_evidence_refs", + "reviewer_outcome", + "not_approval" + ], + "requires_runtime_approval_package": true, + "reviewer_checks": [ + "change_ref_present", + "affected_route_refs_present", + "public_url_not_internal_ip", + "admin_auth_boundary_called_out", + "api_contract_readback_present", + "cors_origin_diff_ref_only", + "frontend_env_diff_ref_present", + "i18n_redaction_review_present", + "webhook_callback_owner_present", + "desktop_mobile_smoke_present", + "api_health_readback_present", + "sensitive_string_scan_present", + "console_error_scan_present", + "no_secret_value_or_cookie", + "security_header_or_cookie_impact_called_out", + "blast_radius_present", + "maintenance_window_present", + "rollback_owner_present", + "postcheck_evidence_present", + "no_runtime_action_claim", + "cross_project_sync_noted" + ], + "reviewer_outcome": "waiting_change_evidence", + "risk": "HIGH", + "rollback_owner": "pending_change_evidence", + "rollback_plan_ref": null, + "route_scope_accepted": false, + "route_smoke_authorized": false, + "runtime_approval_package_ready": false, + "runtime_config_change_authorized": false, + "runtime_execution_authorized": false, + "runtime_gate": false, + "secret_hash_collection_allowed": false, + "secret_value_collection_allowed": false, + "security_header_change_authorized": false, + "sensitive_string_scan_accepted": false, + "sensitive_string_scan_ref": null, + "source_ref_existing_count": 3, + "source_refs": [ + "apps/web/src/middleware.ts", + "apps/web/src/app/api/sentry-tunnel/route.ts", + "apps/web/src/app/api/health/route.ts" + ], + "status": "waiting_change_evidence", + "title": "Frontend env / Sentry tunnel / browser runtime config", + "webhook_callback_owner_accepted": false, + "webhook_callback_owner_ref": null, + "webhook_receiver_change_authorized": false, + "webhook_secret_change_authorized": false, + "write_capable": true + }, + { + "action_buttons_allowed": false, + "admin_auth_boundary_accepted": false, + "admin_auth_boundary_ref": null, + "admin_route_change_authorized": false, + "affected_route_refs": [], + "affected_scope": "webhook callback、proposal route、deep link、notification route 與 external send boundary", + "api_contract_change_authorized": false, + "api_contract_readback_accepted": false, + "api_contract_readback_ref": null, + "api_health_readback_ref": null, + "api_route_change_authorized": false, + "blast_radius": "pending_change_evidence", + "blocked_actions": [ + "change_public_route", + "change_admin_route", + "change_api_route", + "change_cors_origin", + "modify_next_public_env", + "expose_internal_ip", + "expose_repo_slug", + "expose_owner_namespace", + "expose_secret_value", + "bypass_auth", + "change_callback_url", + "change_webhook_secret", + "modify_middleware_auth", + "disable_csrf", + "disable_rate_limit", + "change_cookie_policy", + "change_security_headers", + "publish_internal_transcript", + "publish_internal_status_code", + "deploy_frontend", + "deploy_api", + "rewrite_nginx_route", + "change_public_url", + "change_openapi_contract", + "mutate_database", + "run_migration", + "send_webhook", + "active_scan", + "enable_action_button", + "production_deploy", + "force_push", + "switch_github_primary" + ], + "callback_url_change_authorized": false, + "change_evidence_accepted": false, + "change_evidence_candidate_id": "public_runtime_config_change_evidence:webhook_callback_and_notification_runtime_config", + "change_evidence_fields": [ + "change_evidence_candidate_id", + "source_refs", + "control_tier", + "proposed_runtime_config_change_ref", + "affected_route_refs", + "public_url_or_domain_ref", + "admin_auth_boundary_ref", + "api_contract_readback_ref", + "cors_origin_diff_ref", + "frontend_env_diff_ref", + "i18n_redaction_review_ref", + "webhook_callback_owner_ref", + "desktop_mobile_smoke_ref", + "api_health_readback_ref", + "sensitive_string_scan_ref", + "console_error_scan_ref", + "blast_radius", + "maintenance_window", + "rollback_owner", + "rollback_plan_ref", + "postcheck_evidence_ref", + "redacted_evidence_refs", + "reviewer_outcome", + "not_approval" + ], + "change_evidence_quarantined": false, + "change_evidence_received": false, + "change_evidence_rejected": false, + "console_error_scan_ref": null, + "control_tier": "C0", + "cookie_policy_change_authorized": false, + "cors_change_authorized": false, + "cors_origin_diff_accepted": false, + "cors_origin_diff_ref": null, + "csrf_disable_authorized": false, + "database_migration_authorized": false, + "desktop_mobile_smoke_accepted": false, + "desktop_mobile_smoke_authorized": false, + "desktop_mobile_smoke_ref": null, + "force_push_authorized": false, + "frontend_env_change_authorized": false, + "frontend_env_diff_accepted": false, + "frontend_env_diff_ref": null, + "github_primary_switch_authorized": false, + "i18n_public_text_internal_identity_allowed": false, + "i18n_redaction_review_accepted": false, + "i18n_redaction_review_ref": null, + "internal_ip_exposure_allowed": false, + "internal_status_code_exposure_allowed": false, + "internal_transcript_exposure_allowed": false, + "maintenance_window": "pending_change_evidence", + "middleware_auth_change_authorized": false, + "not_approval": true, + "outcome_lanes": [ + "waiting_change_evidence", + "quarantine_sensitive_payload", + "reject_unredacted_or_runtime_claim", + "request_supplement", + "ready_for_reviewer_acceptance", + "ready_for_runtime_approval_package", + "waiting_maintenance_window", + "waiting_runtime_gate" + ], + "owner_namespace_exposure_allowed": false, + "partial_token_collection_allowed": false, + "postcheck_evidence_accepted": false, + "postcheck_evidence_ref": null, + "production_deploy_authorized": false, + "proposed_runtime_config_change_ref": null, + "public_route_change_authorized": false, + "public_url_or_domain_ref": null, + "rate_limit_disable_authorized": false, + "raw_payload_storage_allowed": false, + "redacted_evidence_refs": [], + "repo_namespace_exposure_allowed": false, + "required_evidence_fields": [ + "proposed_runtime_config_change_ref", + "affected_route_refs", + "public_url_or_domain_ref", + "admin_auth_boundary_ref", + "api_contract_readback_ref", + "cors_origin_diff_ref", + "frontend_env_diff_ref", + "i18n_redaction_review_ref", + "webhook_callback_owner_ref", + "desktop_mobile_smoke_ref", + "api_health_readback_ref", + "sensitive_string_scan_ref", + "console_error_scan_ref", + "blast_radius", + "maintenance_window", + "rollback_owner", + "rollback_plan_ref", + "postcheck_evidence_ref", + "redacted_evidence_refs", + "reviewer_outcome", + "not_approval" + ], + "requires_runtime_approval_package": true, + "reviewer_checks": [ + "change_ref_present", + "affected_route_refs_present", + "public_url_not_internal_ip", + "admin_auth_boundary_called_out", + "api_contract_readback_present", + "cors_origin_diff_ref_only", + "frontend_env_diff_ref_present", + "i18n_redaction_review_present", + "webhook_callback_owner_present", + "desktop_mobile_smoke_present", + "api_health_readback_present", + "sensitive_string_scan_present", + "console_error_scan_present", + "no_secret_value_or_cookie", + "security_header_or_cookie_impact_called_out", + "blast_radius_present", + "maintenance_window_present", + "rollback_owner_present", + "postcheck_evidence_present", + "no_runtime_action_claim", + "cross_project_sync_noted" + ], + "reviewer_outcome": "waiting_change_evidence", + "risk": "HIGH", + "rollback_owner": "pending_change_evidence", + "rollback_plan_ref": null, + "route_scope_accepted": false, + "route_smoke_authorized": false, + "runtime_approval_package_ready": false, + "runtime_config_change_authorized": false, + "runtime_execution_authorized": false, + "runtime_gate": false, + "secret_hash_collection_allowed": false, + "secret_value_collection_allowed": false, + "security_header_change_authorized": false, + "sensitive_string_scan_accepted": false, + "sensitive_string_scan_ref": null, + "source_ref_existing_count": 3, + "source_refs": [ + "apps/api/src/models/webhook.py", + "apps/api/src/routers/proposals.py", + "apps/api/src/core/deep_linking.py" + ], + "status": "waiting_change_evidence", + "title": "Webhook / callback / notification runtime route", + "webhook_callback_owner_accepted": false, + "webhook_callback_owner_ref": null, + "webhook_receiver_change_authorized": false, + "webhook_secret_change_authorized": false, + "write_capable": true + }, + { + "action_buttons_allowed": false, + "admin_auth_boundary_accepted": false, + "admin_auth_boundary_ref": null, + "admin_route_change_authorized": false, + "affected_route_refs": [], + "affected_scope": "VibeWork、agent-bounty-protocol、StockPlatform、官方形象網站、藥局網站與其他產品 runtime route scope", + "api_contract_change_authorized": false, + "api_contract_readback_accepted": false, + "api_contract_readback_ref": null, + "api_health_readback_ref": null, + "api_route_change_authorized": false, + "blast_radius": "pending_change_evidence", + "blocked_actions": [ + "change_public_route", + "change_admin_route", + "change_api_route", + "change_cors_origin", + "modify_next_public_env", + "expose_internal_ip", + "expose_repo_slug", + "expose_owner_namespace", + "expose_secret_value", + "bypass_auth", + "change_callback_url", + "change_webhook_secret", + "modify_middleware_auth", + "disable_csrf", + "disable_rate_limit", + "change_cookie_policy", + "change_security_headers", + "publish_internal_transcript", + "publish_internal_status_code", + "deploy_frontend", + "deploy_api", + "rewrite_nginx_route", + "change_public_url", + "change_openapi_contract", + "mutate_database", + "run_migration", + "send_webhook", + "active_scan", + "enable_action_button", + "production_deploy", + "force_push", + "switch_github_primary" + ], + "callback_url_change_authorized": false, + "change_evidence_accepted": false, + "change_evidence_candidate_id": "public_runtime_config_change_evidence:cross_product_runtime_route_scope", + "change_evidence_fields": [ + "change_evidence_candidate_id", + "source_refs", + "control_tier", + "proposed_runtime_config_change_ref", + "affected_route_refs", + "public_url_or_domain_ref", + "admin_auth_boundary_ref", + "api_contract_readback_ref", + "cors_origin_diff_ref", + "frontend_env_diff_ref", + "i18n_redaction_review_ref", + "webhook_callback_owner_ref", + "desktop_mobile_smoke_ref", + "api_health_readback_ref", + "sensitive_string_scan_ref", + "console_error_scan_ref", + "blast_radius", + "maintenance_window", + "rollback_owner", + "rollback_plan_ref", + "postcheck_evidence_ref", + "redacted_evidence_refs", + "reviewer_outcome", + "not_approval" + ], + "change_evidence_quarantined": false, + "change_evidence_received": false, + "change_evidence_rejected": false, + "console_error_scan_ref": null, + "control_tier": "C1", + "cookie_policy_change_authorized": false, + "cors_change_authorized": false, + "cors_origin_diff_accepted": false, + "cors_origin_diff_ref": null, + "csrf_disable_authorized": false, + "database_migration_authorized": false, + "desktop_mobile_smoke_accepted": false, + "desktop_mobile_smoke_authorized": false, + "desktop_mobile_smoke_ref": null, + "force_push_authorized": false, + "frontend_env_change_authorized": false, + "frontend_env_diff_accepted": false, + "frontend_env_diff_ref": null, + "github_primary_switch_authorized": false, + "i18n_public_text_internal_identity_allowed": false, + "i18n_redaction_review_accepted": false, + "i18n_redaction_review_ref": null, + "internal_ip_exposure_allowed": false, + "internal_status_code_exposure_allowed": false, + "internal_transcript_exposure_allowed": false, + "maintenance_window": "pending_change_evidence", + "middleware_auth_change_authorized": false, + "not_approval": true, + "outcome_lanes": [ + "waiting_change_evidence", + "quarantine_sensitive_payload", + "reject_unredacted_or_runtime_claim", + "request_supplement", + "ready_for_reviewer_acceptance", + "ready_for_runtime_approval_package", + "waiting_maintenance_window", + "waiting_runtime_gate" + ], + "owner_namespace_exposure_allowed": false, + "partial_token_collection_allowed": false, + "postcheck_evidence_accepted": false, + "postcheck_evidence_ref": null, + "production_deploy_authorized": false, + "proposed_runtime_config_change_ref": null, + "public_route_change_authorized": false, + "public_url_or_domain_ref": null, + "rate_limit_disable_authorized": false, + "raw_payload_storage_allowed": false, + "redacted_evidence_refs": [], + "repo_namespace_exposure_allowed": false, + "required_evidence_fields": [ + "proposed_runtime_config_change_ref", + "affected_route_refs", + "public_url_or_domain_ref", + "admin_auth_boundary_ref", + "api_contract_readback_ref", + "cors_origin_diff_ref", + "frontend_env_diff_ref", + "i18n_redaction_review_ref", + "webhook_callback_owner_ref", + "desktop_mobile_smoke_ref", + "api_health_readback_ref", + "sensitive_string_scan_ref", + "console_error_scan_ref", + "blast_radius", + "maintenance_window", + "rollback_owner", + "rollback_plan_ref", + "postcheck_evidence_ref", + "redacted_evidence_refs", + "reviewer_outcome", + "not_approval" + ], + "requires_runtime_approval_package": true, + "reviewer_checks": [ + "change_ref_present", + "affected_route_refs_present", + "public_url_not_internal_ip", + "admin_auth_boundary_called_out", + "api_contract_readback_present", + "cors_origin_diff_ref_only", + "frontend_env_diff_ref_present", + "i18n_redaction_review_present", + "webhook_callback_owner_present", + "desktop_mobile_smoke_present", + "api_health_readback_present", + "sensitive_string_scan_present", + "console_error_scan_present", + "no_secret_value_or_cookie", + "security_header_or_cookie_impact_called_out", + "blast_radius_present", + "maintenance_window_present", + "rollback_owner_present", + "postcheck_evidence_present", + "no_runtime_action_claim", + "cross_project_sync_noted" + ], + "reviewer_outcome": "waiting_change_evidence", + "risk": "MEDIUM", + "rollback_owner": "pending_change_evidence", + "rollback_plan_ref": null, + "route_scope_accepted": false, + "route_smoke_authorized": false, + "runtime_approval_package_ready": false, + "runtime_config_change_authorized": false, + "runtime_execution_authorized": false, + "runtime_gate": false, + "secret_hash_collection_allowed": false, + "secret_value_collection_allowed": false, + "security_header_change_authorized": false, + "sensitive_string_scan_accepted": false, + "sensitive_string_scan_ref": null, + "source_ref_existing_count": 4, + "source_refs": [ + "docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md", + "docs/security/VIBEWORK-IWOOOS-ONBOARDING-HANDOFF.md", + "docs/security/AGENT-BOUNTY-IWOOOS-ONBOARDING-HANDOFF.md", + "docs/security/AGENT-BOUNTY-OWNER-REQUEST-DRAFT.md" + ], + "status": "waiting_change_evidence", + "title": "Cross-product runtime route scope", + "webhook_callback_owner_accepted": false, + "webhook_callback_owner_ref": null, + "webhook_receiver_change_authorized": false, + "webhook_secret_change_authorized": false, + "write_capable": true + } + ], + "change_evidence_fields": [ + "change_evidence_candidate_id", + "source_refs", + "control_tier", + "proposed_runtime_config_change_ref", + "affected_route_refs", + "public_url_or_domain_ref", + "admin_auth_boundary_ref", + "api_contract_readback_ref", + "cors_origin_diff_ref", + "frontend_env_diff_ref", + "i18n_redaction_review_ref", + "webhook_callback_owner_ref", + "desktop_mobile_smoke_ref", + "api_health_readback_ref", + "sensitive_string_scan_ref", + "console_error_scan_ref", + "blast_radius", + "maintenance_window", + "rollback_owner", + "rollback_plan_ref", + "postcheck_evidence_ref", + "redacted_evidence_refs", + "reviewer_outcome", + "not_approval" + ], + "execution_boundaries": { + "action_buttons_allowed": false, + "admin_route_change_authorized": false, + "api_contract_change_authorized": false, + "api_route_change_authorized": false, + "callback_url_change_authorized": false, + "cookie_policy_change_authorized": false, + "cors_change_authorized": false, + "csrf_disable_authorized": false, + "database_migration_authorized": false, + "desktop_mobile_smoke_authorized": false, + "force_push_authorized": false, + "frontend_env_change_authorized": false, + "github_primary_switch_authorized": false, + "i18n_public_text_internal_identity_allowed": false, + "internal_ip_exposure_allowed": false, + "internal_status_code_exposure_allowed": false, + "internal_transcript_exposure_allowed": false, + "middleware_auth_change_authorized": false, + "not_authorization": true, + "owner_namespace_exposure_allowed": false, + "partial_token_collection_allowed": false, + "production_deploy_authorized": false, + "public_route_change_authorized": false, + "rate_limit_disable_authorized": false, + "raw_payload_storage_allowed": false, + "repo_namespace_exposure_allowed": false, + "route_smoke_authorized": false, + "runtime_config_change_authorized": false, + "runtime_execution_authorized": false, + "secret_hash_collection_allowed": false, + "secret_value_collection_allowed": false, + "security_header_change_authorized": false, + "webhook_receiver_change_authorized": false, + "webhook_secret_change_authorized": false + }, + "generated_at": "2026-06-15T04:20:00+08:00", + "git_commit": "77a76d1a", + "mode": "metadata_only_no_secret_no_route_change_no_deploy", + "operator_interpretation": [ + "此帳本只描述 public/admin/API/frontend runtime config 變更證據如何收件與拒收,不是 route、CORS、env、auth 或 webhook 變更批准。", + "前台、public API、HTML、bundle 與 messages 不得顯示 raw owner namespace、repo slug、內部狀態碼、內部對話或 secret value。", + "desktop/mobile smoke、API health、CD success、UI 可見與 AwoooP approval 都不能被解讀成 runtime gate 已開。", + "未來若要修改 public/admin/API route、CORS、NEXT_PUBLIC env、middleware auth、callback 或 webhook,仍需獨立 owner response、維護窗口、rollback owner 與 runtime approval package。" + ], + "outcome_lanes": [ + { + "lane_id": "waiting_change_evidence", + "meaning": "尚未收到 runtime config 變更證據;所有 accepted / runtime count 維持 0。" + }, + { + "lane_id": "quarantine_sensitive_payload", + "meaning": "收到 cookie、token、secret value、raw internal payload 或未脫敏截圖時只能隔離。" + }, + { + "lane_id": "reject_unredacted_or_runtime_claim", + "meaning": "出現 raw identity、internal transcript、internal state code 或把 evidence 誤當批准時直接拒收。" + }, + { + "lane_id": "request_supplement", + "meaning": "缺 route scope、auth boundary、CORS diff、desktop/mobile smoke、rollback 或 post-check 時要求補件。" + }, + { + "lane_id": "ready_for_reviewer_acceptance", + "meaning": "metadata 合格後只能進 reviewer acceptance,不得自動改 route / CORS / env。" + }, + { + "lane_id": "ready_for_runtime_approval_package", + "meaning": "reviewer 接受後也只能形成 runtime approval package,不自動打開 gate。" + }, + { + "lane_id": "waiting_maintenance_window", + "meaning": "若未來要改 public/admin/API runtime config,仍需獨立維護窗口。" + }, + { + "lane_id": "waiting_runtime_gate", + "meaning": "change evidence accepted 後 runtime gate 仍等待獨立人工批准。" + } + ], + "required_evidence_fields": [ + "proposed_runtime_config_change_ref", + "affected_route_refs", + "public_url_or_domain_ref", + "admin_auth_boundary_ref", + "api_contract_readback_ref", + "cors_origin_diff_ref", + "frontend_env_diff_ref", + "i18n_redaction_review_ref", + "webhook_callback_owner_ref", + "desktop_mobile_smoke_ref", + "api_health_readback_ref", + "sensitive_string_scan_ref", + "console_error_scan_ref", + "blast_radius", + "maintenance_window", + "rollback_owner", + "rollback_plan_ref", + "postcheck_evidence_ref", + "redacted_evidence_refs", + "reviewer_outcome", + "not_approval" + ], + "reviewer_checks": [ + { + "check_id": "change_ref_present", + "instruction": "必須有 proposed runtime config change ref,不能只寫口頭同意。" + }, + { + "check_id": "affected_route_refs_present", + "instruction": "必須列出 public / admin / API / callback / webhook / frontend route 影響範圍。" + }, + { + "check_id": "public_url_not_internal_ip", + "instruction": "NEXT_PUBLIC 與公開 URL 只能使用 public domain,不得暴露內網 IP。" + }, + { + "check_id": "admin_auth_boundary_called_out", + "instruction": "涉及後台、operator console 或審批路由時必須標出 auth boundary 與 owner。" + }, + { + "check_id": "api_contract_readback_present", + "instruction": "API 變更需有 readback ref,且 public payload 不得暴露 raw owner namespace、repo slug 或內部狀態碼。" + }, + { + "check_id": "cors_origin_diff_ref_only", + "instruction": "CORS 只能收 origin diff / owner ref,不得直接改白名單或使用萬用來源。" + }, + { + "check_id": "frontend_env_diff_ref_present", + "instruction": "涉及 frontend env / build-time public config 時必須附 diff ref 與 bundle sensitive scan ref。" + }, + { + "check_id": "i18n_redaction_review_present", + "instruction": "前台文案需確認全繁中、無內部對話、無抱怨語句、無 raw identity。" + }, + { + "check_id": "webhook_callback_owner_present", + "instruction": "callback / webhook / Sentry tunnel / Telegram route 需有 owner 與回復方式。" + }, + { + "check_id": "desktop_mobile_smoke_present", + "instruction": "涉及前台或後台 route 時必須有 desktop / mobile smoke ref 與 horizontal overflow 結果。" + }, + { + "check_id": "api_health_readback_present", + "instruction": "API / backend runtime config 需有 health 或 contract readback ref。" + }, + { + "check_id": "sensitive_string_scan_present", + "instruction": "必須附 sensitive string scan ref,至少檢查 raw namespace、internal state code、internal transcript、secret value。" + }, + { + "check_id": "console_error_scan_present", + "instruction": "前端 smoke 需標出 console/page error 結果或說明不適用。" + }, + { + "check_id": "no_secret_value_or_cookie", + "instruction": "不得保存 cookie、token、DSN value、secret value、hash、partial token 或 raw payload。" + }, + { + "check_id": "security_header_or_cookie_impact_called_out", + "instruction": "若影響 headers、cookie、CSRF、rate limit 或 middleware,必須標出安全影響。" + }, + { + "check_id": "blast_radius_present", + "instruction": "必須列出產品、route、API、admin/auth、public domain、callback、webhook 與使用者影響。" + }, + { + "check_id": "maintenance_window_present", + "instruction": "任何 future runtime config 變更都必須另有維護窗口或明確 not-applicable 理由。" + }, + { + "check_id": "rollback_owner_present", + "instruction": "必須有 rollback owner 與回復方式;不能只寫『可回復』。" + }, + { + "check_id": "postcheck_evidence_present", + "instruction": "需有 post-check evidence ref,例如 API readback、browser smoke、bundle scan 或 alert silence review。" + }, + { + "check_id": "no_runtime_action_claim", + "instruction": "不能把本帳本、UI 可見、CD success、AwoooP approval 或 smoke pass 當資安批准。" + }, + { + "check_id": "cross_project_sync_noted", + "instruction": "若影響 AwoooP、IwoooS、agent-bounty、StockPlatform、公開網站或監控,需有跨專案同步 ref。" + } + ], + "schema_version": "public_runtime_config_change_evidence_acceptance_v1", + "source_paths": [ + "docs/HARD_RULES.md", + "docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md", + "apps/api/src/config.py", + "apps/api/src/core/awooop_operator_auth.py", + "apps/api/src/core/config.py", + "apps/api/src/core/csrf.py", + "apps/api/src/core/deep_linking.py", + "apps/api/src/models/webhook.py", + "apps/api/src/routers/proposals.py", + "apps/web/messages/en.json", + "apps/web/messages/zh-TW.json", + "apps/web/src/app/[locale]", + "apps/web/src/app/[locale]/awooop", + "apps/web/src/app/api/health/route.ts", + "apps/web/src/app/api/sentry-tunnel/route.ts", + "apps/web/src/lib/api-client.ts", + "apps/web/src/lib/config.ts", + "apps/web/src/middleware.ts", + "docs/security/AGENT-BOUNTY-IWOOOS-ONBOARDING-HANDOFF.md", + "docs/security/AGENT-BOUNTY-OWNER-REQUEST-DRAFT.md", + "docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md", + "docs/security/VIBEWORK-IWOOOS-ONBOARDING-HANDOFF.md" + ], + "status": "change_evidence_acceptance_ledger_ready_no_runtime_action", + "summary": { + "action_button_count": 0, + "admin_auth_boundary_accepted_count": 0, + "api_contract_readback_accepted_count": 0, + "blocked_action_count": 32, + "c0_change_evidence_candidate_count": 5, + "c1_change_evidence_candidate_count": 1, + "change_evidence_accepted_count": 0, + "change_evidence_candidate_count": 6, + "change_evidence_received_count": 0, + "cors_origin_diff_accepted_count": 0, + "desktop_mobile_smoke_accepted_count": 0, + "existing_source_ref_count": 20, + "frontend_env_diff_accepted_count": 0, + "i18n_redaction_review_accepted_count": 0, + "outcome_lane_count": 8, + "postcheck_evidence_accepted_count": 0, + "public_admin_api_runtime_config_coverage_percent_after_acceptance": 64, + "public_admin_api_runtime_config_coverage_percent_before_acceptance": 62, + "required_evidence_field_count": 21, + "reviewer_check_count": 21, + "route_scope_accepted_count": 0, + "runtime_approval_package_ready_count": 0, + "runtime_gate_count": 0, + "sensitive_string_scan_accepted_count": 0, + "source_ref_count": 20, + "webhook_callback_owner_accepted_count": 0, + "write_capable_candidate_count": 6 + } +} diff --git a/docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md b/docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md index 1a29d5d1e..6c0c660fb 100644 --- a/docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md +++ b/docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md @@ -4914,6 +4914,18 @@ Trigger commit `f5cd37b7` 與 deploy marker `0ba92357` 已把 governance UI 的 **裁決:** 這是 CD / runner / secret injection 變更證據的只讀驗收帳本,不是 change evidence received / accepted、workflow diff accepted、runner attestation accepted、secret name parity accepted、secret injection route accepted、deploy marker readback accepted、guard result accepted、post-check accepted、runtime approval package、workflow modification、runner change、repo secret change、secret value / hash / partial token collection、secret store read、secret rotation、Gitea action dispatch、production deploy 或 runtime gate;所有執行仍需獨立人工批准。 +### 2026-06-15 04:20 (台北) — §3.2 / §5 — 新增 Public / Admin / API runtime config 變更證據驗收只讀帳本 — 把前台脫敏與 route / CORS / env 變更前證據固定成 guard artifact + +**觸發**:AwoooP Tenants 與 IwoooS 前台若直接顯示 raw owner namespace、repo slug、內部狀態碼或內部協作內容,會把內部來源與治理狀態暴露給公開頁面;同時 public route、admin/auth、API / CORS、frontend env、Sentry tunnel、webhook / callback 都屬於會直接影響公開入口與資料邊界的 P0 runtime config。需要補一層只讀變更證據驗收,防止 UI 可見、CD success、browser smoke 或 AwoooP approval 被誤判成 route / CORS / env / auth / webhook 修改批准。 + +**已推進:** +- 新增 `scripts/security/public-runtime-config-change-evidence-acceptance.py`,讀取 repo 內 public route、admin/auth boundary、API/CORS、frontend env、i18n redaction 與 webhook/callback source refs,產生 6 份 Public / Admin / API runtime config change evidence candidate。 +- 新增 `docs/security/public-runtime-config-change-evidence-acceptance.snapshot.json`,固定 `change_evidence_candidate_count=6`、`c0_change_evidence_candidate_count=5`、`c1_change_evidence_candidate_count=1`、`write_capable_candidate_count=6`、`source_ref_count=20`、`required_evidence_field_count=21`、`reviewer_check_count=21`、`outcome_lane_count=8`、`blocked_action_count=32`。 +- 新增 `docs/security/PUBLIC-RUNTIME-CONFIG-CHANGE-EVIDENCE-ACCEPTANCE.md`,說明 affected route refs、admin/auth boundary、API contract readback、CORS origin diff、frontend env diff、i18n redaction review、webhook / callback owner、desktop / mobile smoke、sensitive string scan、console error scan、rollback owner、post-check evidence 與 no-runtime-approval 邊界。 +- 高價值配置 coverage、IwoooS 配置控管總清單、P0 主控板、供應鏈進度、posture source paths 與前端 evidence marker 已同步;`public_admin_api_runtime_config` 只讀治理成熟度從 `62%` 推進到 `64%`,但整體平均仍 `68%`,IwoooS headline 仍 `64%`,runtime gate 仍為 `0`。 + +**裁決:** 這是 Public / Admin / API / frontend runtime config 變更證據的只讀驗收帳本,不是 affected route accepted、admin/auth boundary accepted、API readback accepted、CORS diff accepted、frontend env diff accepted、i18n redaction accepted、desktop/mobile smoke accepted、sensitive scan accepted、runtime approval package、route / CORS / env / auth / webhook 變更、frontend / API deploy、production deploy 或 runtime gate;raw owner namespace、repo slug、內部狀態碼、內部協作文字、cookie、token、secret value、DSN value、raw payload 與未脫敏截圖只能拒收或隔離。 + ### 2026-06-15 00:36 (台北) — §3.2 / §5 — 新增端口 / 防火牆變更證據驗收只讀帳本 — 把 port close / firewall change 事故證據納入可驗收 guard artifact **觸發**:110 端口關閉曾造成多個服務異常,顯示 SSH / network owner response acceptance 仍不足以追溯「誰改了端口 / 防火牆、為什麼、影響哪些服務、如何回復、如何驗證、是否同步其他專案」。需要補一層端口 / 防火牆變更證據驗收帳本,防止未來把 incident evidence、port list 或 UI 可見性誤判成 firewall / port 操作授權。 diff --git a/docs/workplans/2026-06-04-iwooos-security-governance-p0.md b/docs/workplans/2026-06-04-iwooos-security-governance-p0.md index 8df999da0..852dc0000 100644 --- a/docs/workplans/2026-06-04-iwooos-security-governance-p0.md +++ b/docs/workplans/2026-06-04-iwooos-security-governance-p0.md @@ -28,6 +28,7 @@ | 最新 P1 Backup / Restore / Escrow 配置控管基準 | repo-only inventory 已固定 `surface=38`、`write_capable=27`、`live_evidence_required=38`、成熟度 `52% -> 58%`;本輪新增 owner request draft,固定 `drafts=38`、`owner_fields=14`、`blocked_actions=18`、request sent / received / accepted / backup run / restore run / offsite sync / remote delete / escrow marker / retention change / runtime gate 全部 `0` | | 最新 P1 Monitoring / Alerting / Observability 配置控管基準 | repo-only inventory 已固定 `surface=60`、`write_capable=11`、`live_evidence_required=60`、成熟度 `56% -> 62%`;本輪新增 owner request draft,固定 `drafts=60`、`owner_fields=14`、`blocked_actions=24`、request sent / received / accepted / live evidence / reload / receiver route change / Telegram send / alert chain smoke / runtime gate 全部 `0` | | 最新 P0 CD / Runner / Secret 注入變更證據驗收基準 | 已新增 `CD-RUNNER-SECRET-INJECTION-CHANGE-EVIDENCE-ACCEPTANCE.md`、snapshot 與產生器;固定 `candidates=5`、`c0=4`、`c1=1`、`write_capable=5`、`workflow_files=33`、`referenced_secret_names=42`、`runner_labels=5`、`evidence_fields=19`、`reviewer_checks=19`、`outcome_lanes=8`、`blocked_actions=32`;secret metadata 成熟度 `66% -> 68%`,workflow / runner 成熟度 `70% -> 72%`;workflow diff / runner attestation / secret name parity / secret injection route / deploy marker readback / guard result / postcheck evidence accepted、workflow modification、runner change、repo secret change、secret rotation、Gitea action dispatch、production deploy、runtime gate 全部 `0` | +| 最新 P0 Public / Admin / API runtime config 變更證據驗收基準 | 已新增 `PUBLIC-RUNTIME-CONFIG-CHANGE-EVIDENCE-ACCEPTANCE.md`、snapshot 與產生器;固定 `candidates=6`、`c0=5`、`c1=1`、`write_capable=6`、`source_refs=20`、`evidence_fields=21`、`reviewer_checks=21`、`outcome_lanes=8`、`blocked_actions=32`;public/admin/API runtime config 成熟度 `62% -> 64%`;affected route / admin auth / API readback / CORS diff / frontend env diff / i18n redaction / desktop-mobile smoke / sensitive scan / postcheck accepted、route / CORS / env / auth / webhook 變更、production deploy、runtime gate 全部 `0`;raw namespace、repo slug、內部狀態碼、內部協作內容外洩列為拒收或隔離 | | 最新 AI Agent automation P1-305 / P1-306 基準 | code `4f0787f8`、deploy marker `af3a9d48`、CD `2592`、code-review `2593`;任務批准邊界與進度彙總已正式驗證;backlog `70%`、done `16/23`、下一步 `P1-001` | | 最新 AI Agent automation P1-001 基準 | code `de3007b7`、stability fix `fd33591c`、deploy marker `8caba233`、LOGBOOK / marker 對齊 `37c0e171`;runtime surface `22`、Secret surface `4`、live gaps `6`、backlog `74%`、done `17/23`、下一步 `P1-002` | | 最新 AI Agent automation P1-002 基準 | code `943faaee`、stability fix `ff266926`、deploy marker `01b8712d`、LOGBOOK / marker 對齊 `70c01003`;Gitea workflows `9`、runner contracts `4`、需 runner attestation workflows `8`、backlog `78%`、done `18/23`、下一步 `P1-003` | @@ -319,6 +320,7 @@ P1 只讀重盤階段整體完成度:`70%`。它代表 freshness / inventory / | P0-25 | K8s / ArgoCD owner response acceptance 只讀帳本 | 已新增 `K8S-ARGOCD-OWNER-RESPONSE-ACCEPTANCE.md`、`k8s-argocd-owner-response-acceptance.snapshot.json` 與產生器;4 份 acceptance candidate、3 份 C0、11 個 owner 必填欄位、12 個 reviewer checks、7 條 outcome lanes、18 類 blocked action | 帳本 artifact `100%`;owner response received / accepted、rendered manifest diff、ArgoCD API read、ArgoCD sync、kubectl action、live cluster read、secret collection、host write、runtime gate 仍全部 `0 / false` | | P0-26 | Backup / Restore / Escrow owner response acceptance 只讀帳本 | 已新增 `BACKUP-RESTORE-OWNER-RESPONSE-ACCEPTANCE.md`、`backup-restore-owner-response-acceptance.snapshot.json` 與產生器;38 份 acceptance candidate、27 份 write-capable、14 個 owner 必填欄位、13 個 reviewer checks、7 條 outcome lanes、22 類 blocked action | 帳本 artifact `100%`;owner response received / accepted、live backup evidence、backup run、restore run、offsite sync、remote delete、escrow marker write、retention change、secret collection、host write、runtime gate 仍全部 `0 / false`;backup / restore 類別成熟度 `58% -> 62%` | | P0-27 | DNS / TLS / certbot owner response acceptance 只讀帳本 | 已新增 `DOMAIN-TLS-CERTBOT-OWNER-RESPONSE-ACCEPTANCE.md`、`domain-tls-certbot-owner-response-acceptance.snapshot.json` 與產生器;4 份 acceptance candidate、4 份 C0、13 個 owner 必填欄位、13 個 reviewer checks、7 條 outcome lanes、20 類 blocked action | 帳本 artifact `100%`;owner response received / accepted、certificate coverage confirmed、DNS query、live TLS probe、certbot renew、Nginx reload、route smoke、DNS record / certificate path / ACME route 變更、host write、runtime gate 仍全部 `0 / false`;DNS / TLS 類別成熟度 `74% -> 78%` | +| P0-29 | Public / Admin / API runtime config 變更證據驗收只讀帳本 | 已新增 `PUBLIC-RUNTIME-CONFIG-CHANGE-EVIDENCE-ACCEPTANCE.md`、`public-runtime-config-change-evidence-acceptance.snapshot.json` 與產生器;6 份 change evidence candidate、5 份 C0、6 份 write-capable、20 個 source refs、21 個 reviewer checks、8 條 outcome lanes、32 類 blocked action | 帳本 artifact `100%`;affected route、admin/auth boundary、API readback、CORS diff、frontend env diff、i18n redaction、webhook owner、desktop / mobile smoke、sensitive scan、postcheck、runtime approval package、route / CORS / env / auth / webhook 變更、frontend / API deploy、runtime gate 仍全部 `0 / false`;raw namespace / repo slug / 內部狀態碼 / 內部協作內容外洩列為拒收或隔離;完成後需 production desktop / mobile smoke | ## 7. 2026-06-04 本輪驗證紀錄 diff --git a/scripts/security/high-value-config-control-coverage.py b/scripts/security/high-value-config-control-coverage.py index 1c5781e68..949bd3426 100644 --- a/scripts/security/high-value-config-control-coverage.py +++ b/scripts/security/high-value-config-control-coverage.py @@ -101,14 +101,16 @@ CONTROL_STATUS_BY_CATEGORY = { "next_owner_action": "補 workflow diff ref、runner owner attestation、secret name parity ref、Gitea run readback、guard result、maintenance window、rollback owner 與 post-check evidence。", }, "public_admin_api_runtime_config": { - "coverage_status": "policy_ready_needs_change_scoped_smoke", - "coverage_percent": 62, + "coverage_status": "change_evidence_acceptance_ready_needs_runtime_config_owner_evidence", + "coverage_percent": 64, "evidence_refs": [ "docs/HARD_RULES.md", "docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md", + "docs/security/PUBLIC-RUNTIME-CONFIG-CHANGE-EVIDENCE-ACCEPTANCE.md", + "docs/security/public-runtime-config-change-evidence-acceptance.snapshot.json", ], - "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。", + "current_gap": "已固定 Public / Admin / API runtime config 變更證據驗收只讀帳本;affected route、admin/auth boundary、API readback、CORS diff、frontend env diff、i18n redaction、webhook owner、desktop/mobile smoke、sensitive string scan、rollback 與 post-check evidence 仍全部為 0。", + "next_owner_action": "補 affected route refs、admin/auth boundary、API contract readback、CORS origin diff、frontend env diff、i18n redaction review、webhook/callback owner、desktop/mobile smoke、sensitive string scan、rollback owner 與 post-check evidence。", }, "backup_restore_credential": { "coverage_status": "owner_response_acceptance_ledger_ready_needs_restore_drill_owner", @@ -271,6 +273,27 @@ FALSE_BOUNDARIES = { "exporter_deploy_authorized": False, "live_alert_fire_authorized": False, "alert_chain_smoke_authorized": False, + "runtime_config_change_authorized": False, + "api_route_change_authorized": False, + "cors_change_authorized": False, + "frontend_env_change_authorized": False, + "middleware_auth_change_authorized": False, + "callback_url_change_authorized": False, + "webhook_secret_change_authorized": False, + "security_header_change_authorized": False, + "cookie_policy_change_authorized": False, + "csrf_disable_authorized": False, + "rate_limit_disable_authorized": False, + "api_contract_change_authorized": False, + "i18n_public_text_internal_identity_allowed": False, + "internal_ip_exposure_allowed": False, + "repo_namespace_exposure_allowed": False, + "owner_namespace_exposure_allowed": False, + "internal_status_code_exposure_allowed": False, + "internal_transcript_exposure_allowed": False, + "raw_payload_storage_allowed": False, + "desktop_mobile_smoke_authorized": False, + "database_migration_authorized": False, "secret_value_collection_allowed": False, "active_scan_authorized": False, "agent_bounty_runtime_authorized": False, @@ -347,6 +370,7 @@ def build_report(root: Path, generated_at: str | None) -> dict[str, Any]: "change_evidence_acceptance_ready_needs_gitops_owner_evidence", "secret_injection_change_evidence_acceptance_ready_needs_owner_evidence", "cd_runner_secret_injection_change_evidence_acceptance_ready_needs_owner_evidence", + "change_evidence_acceptance_ready_needs_runtime_config_owner_evidence", "owner_response_acceptance_ledger_ready_needs_restore_drill_owner", "owner_response_acceptance_ledger_ready_needs_network_owner", "change_evidence_acceptance_ready_needs_network_owner_evidence", @@ -400,6 +424,7 @@ def build_report(root: Path, generated_at: str | None) -> dict[str, Any]: "k8s_production_gitops", "secret_metadata", "gitea_workflow_runner_source_control", + "public_admin_api_runtime_config", "agent_bounty_protocol_runtime", "docker_compose_systemd_host_config", "monitoring_alerting_observability", diff --git a/scripts/security/public-runtime-config-change-evidence-acceptance.py b/scripts/security/public-runtime-config-change-evidence-acceptance.py new file mode 100644 index 000000000..f5ac48676 --- /dev/null +++ b/scripts/security/public-runtime-config-change-evidence-acceptance.py @@ -0,0 +1,525 @@ +#!/usr/bin/env python3 +""" +IwoooS Public / Admin / API runtime config 變更證據驗收只讀帳本產生器。 + +本工具只整理 repo 內 public route、admin/auth boundary、API/CORS、frontend env、 +i18n redaction 與 webhook/callback 的 source refs,建立未來 runtime config 變更 +證據如何收件、補件、拒收或交給 reviewer 的 metadata-only ledger。它不讀 live +secret、不改 CORS、不改 route、不觸發 webhook、不部署、不開 runtime gate。 +""" + +from __future__ import annotations + +import argparse +import json +import subprocess +import sys +from datetime import datetime, timedelta, timezone +from pathlib import Path +from typing import Any + + +TAIPEI = timezone(timedelta(hours=8)) + +CHANGE_EVIDENCE_FIELDS = [ + "change_evidence_candidate_id", + "source_refs", + "control_tier", + "proposed_runtime_config_change_ref", + "affected_route_refs", + "public_url_or_domain_ref", + "admin_auth_boundary_ref", + "api_contract_readback_ref", + "cors_origin_diff_ref", + "frontend_env_diff_ref", + "i18n_redaction_review_ref", + "webhook_callback_owner_ref", + "desktop_mobile_smoke_ref", + "api_health_readback_ref", + "sensitive_string_scan_ref", + "console_error_scan_ref", + "blast_radius", + "maintenance_window", + "rollback_owner", + "rollback_plan_ref", + "postcheck_evidence_ref", + "redacted_evidence_refs", + "reviewer_outcome", + "not_approval", +] + +REQUIRED_EVIDENCE_FIELDS = [ + "proposed_runtime_config_change_ref", + "affected_route_refs", + "public_url_or_domain_ref", + "admin_auth_boundary_ref", + "api_contract_readback_ref", + "cors_origin_diff_ref", + "frontend_env_diff_ref", + "i18n_redaction_review_ref", + "webhook_callback_owner_ref", + "desktop_mobile_smoke_ref", + "api_health_readback_ref", + "sensitive_string_scan_ref", + "console_error_scan_ref", + "blast_radius", + "maintenance_window", + "rollback_owner", + "rollback_plan_ref", + "postcheck_evidence_ref", + "redacted_evidence_refs", + "reviewer_outcome", + "not_approval", +] + +REVIEWER_CHECKS = [ + { + "check_id": "change_ref_present", + "instruction": "必須有 proposed runtime config change ref,不能只寫口頭同意。", + }, + { + "check_id": "affected_route_refs_present", + "instruction": "必須列出 public / admin / API / callback / webhook / frontend route 影響範圍。", + }, + { + "check_id": "public_url_not_internal_ip", + "instruction": "NEXT_PUBLIC 與公開 URL 只能使用 public domain,不得暴露內網 IP。", + }, + { + "check_id": "admin_auth_boundary_called_out", + "instruction": "涉及後台、operator console 或審批路由時必須標出 auth boundary 與 owner。", + }, + { + "check_id": "api_contract_readback_present", + "instruction": "API 變更需有 readback ref,且 public payload 不得暴露 raw owner namespace、repo slug 或內部狀態碼。", + }, + { + "check_id": "cors_origin_diff_ref_only", + "instruction": "CORS 只能收 origin diff / owner ref,不得直接改白名單或使用萬用來源。", + }, + { + "check_id": "frontend_env_diff_ref_present", + "instruction": "涉及 frontend env / build-time public config 時必須附 diff ref 與 bundle sensitive scan ref。", + }, + { + "check_id": "i18n_redaction_review_present", + "instruction": "前台文案需確認全繁中、無內部對話、無抱怨語句、無 raw identity。", + }, + { + "check_id": "webhook_callback_owner_present", + "instruction": "callback / webhook / Sentry tunnel / Telegram route 需有 owner 與回復方式。", + }, + { + "check_id": "desktop_mobile_smoke_present", + "instruction": "涉及前台或後台 route 時必須有 desktop / mobile smoke ref 與 horizontal overflow 結果。", + }, + { + "check_id": "api_health_readback_present", + "instruction": "API / backend runtime config 需有 health 或 contract readback ref。", + }, + { + "check_id": "sensitive_string_scan_present", + "instruction": "必須附 sensitive string scan ref,至少檢查 raw namespace、internal state code、internal transcript、secret value。", + }, + { + "check_id": "console_error_scan_present", + "instruction": "前端 smoke 需標出 console/page error 結果或說明不適用。", + }, + { + "check_id": "no_secret_value_or_cookie", + "instruction": "不得保存 cookie、token、DSN value、secret value、hash、partial token 或 raw payload。", + }, + { + "check_id": "security_header_or_cookie_impact_called_out", + "instruction": "若影響 headers、cookie、CSRF、rate limit 或 middleware,必須標出安全影響。", + }, + { + "check_id": "blast_radius_present", + "instruction": "必須列出產品、route、API、admin/auth、public domain、callback、webhook 與使用者影響。", + }, + { + "check_id": "maintenance_window_present", + "instruction": "任何 future runtime config 變更都必須另有維護窗口或明確 not-applicable 理由。", + }, + { + "check_id": "rollback_owner_present", + "instruction": "必須有 rollback owner 與回復方式;不能只寫『可回復』。", + }, + { + "check_id": "postcheck_evidence_present", + "instruction": "需有 post-check evidence ref,例如 API readback、browser smoke、bundle scan 或 alert silence review。", + }, + { + "check_id": "no_runtime_action_claim", + "instruction": "不能把本帳本、UI 可見、CD success、AwoooP approval 或 smoke pass 當資安批准。", + }, + { + "check_id": "cross_project_sync_noted", + "instruction": "若影響 AwoooP、IwoooS、agent-bounty、StockPlatform、公開網站或監控,需有跨專案同步 ref。", + }, +] + +OUTCOME_LANES = [ + { + "lane_id": "waiting_change_evidence", + "meaning": "尚未收到 runtime config 變更證據;所有 accepted / runtime count 維持 0。", + }, + { + "lane_id": "quarantine_sensitive_payload", + "meaning": "收到 cookie、token、secret value、raw internal payload 或未脫敏截圖時只能隔離。", + }, + { + "lane_id": "reject_unredacted_or_runtime_claim", + "meaning": "出現 raw identity、internal transcript、internal state code 或把 evidence 誤當批准時直接拒收。", + }, + { + "lane_id": "request_supplement", + "meaning": "缺 route scope、auth boundary、CORS diff、desktop/mobile smoke、rollback 或 post-check 時要求補件。", + }, + { + "lane_id": "ready_for_reviewer_acceptance", + "meaning": "metadata 合格後只能進 reviewer acceptance,不得自動改 route / CORS / env。", + }, + { + "lane_id": "ready_for_runtime_approval_package", + "meaning": "reviewer 接受後也只能形成 runtime approval package,不自動打開 gate。", + }, + { + "lane_id": "waiting_maintenance_window", + "meaning": "若未來要改 public/admin/API runtime config,仍需獨立維護窗口。", + }, + { + "lane_id": "waiting_runtime_gate", + "meaning": "change evidence accepted 後 runtime gate 仍等待獨立人工批准。", + }, +] + +BLOCKED_ACTIONS = [ + "change_public_route", + "change_admin_route", + "change_api_route", + "change_cors_origin", + "modify_next_public_env", + "expose_internal_ip", + "expose_repo_slug", + "expose_owner_namespace", + "expose_secret_value", + "bypass_auth", + "change_callback_url", + "change_webhook_secret", + "modify_middleware_auth", + "disable_csrf", + "disable_rate_limit", + "change_cookie_policy", + "change_security_headers", + "publish_internal_transcript", + "publish_internal_status_code", + "deploy_frontend", + "deploy_api", + "rewrite_nginx_route", + "change_public_url", + "change_openapi_contract", + "mutate_database", + "run_migration", + "send_webhook", + "active_scan", + "enable_action_button", + "production_deploy", + "force_push", + "switch_github_primary", +] + +EXECUTION_BOUNDARIES = { + "runtime_execution_authorized": False, + "runtime_config_change_authorized": False, + "public_route_change_authorized": False, + "admin_route_change_authorized": False, + "api_route_change_authorized": False, + "cors_change_authorized": False, + "frontend_env_change_authorized": False, + "middleware_auth_change_authorized": False, + "callback_url_change_authorized": False, + "webhook_receiver_change_authorized": False, + "webhook_secret_change_authorized": False, + "security_header_change_authorized": False, + "cookie_policy_change_authorized": False, + "csrf_disable_authorized": False, + "rate_limit_disable_authorized": False, + "api_contract_change_authorized": False, + "i18n_public_text_internal_identity_allowed": False, + "internal_ip_exposure_allowed": False, + "repo_namespace_exposure_allowed": False, + "owner_namespace_exposure_allowed": False, + "internal_status_code_exposure_allowed": False, + "internal_transcript_exposure_allowed": False, + "secret_value_collection_allowed": False, + "secret_hash_collection_allowed": False, + "partial_token_collection_allowed": False, + "raw_payload_storage_allowed": False, + "desktop_mobile_smoke_authorized": False, + "route_smoke_authorized": False, + "production_deploy_authorized": False, + "database_migration_authorized": False, + "force_push_authorized": False, + "github_primary_switch_authorized": False, + "action_buttons_allowed": False, + "not_authorization": True, +} + +CANDIDATE_DEFINITIONS = [ + { + "candidate_id": "public_product_route_and_i18n_redaction", + "title": "Public product route / i18n redaction boundary", + "control_tier": "C0", + "risk": "HIGH", + "source_refs": [ + "apps/web/src/app/[locale]", + "apps/web/messages/zh-TW.json", + "apps/web/messages/en.json", + ], + "affected_scope": "公開產品頁、IwoooS / AwoooP / Tenants / Code Review 前台文案、raw identity 與內部協作文字防外洩", + }, + { + "candidate_id": "admin_auth_and_operator_console_boundary", + "title": "Admin / operator console auth boundary", + "control_tier": "C0", + "risk": "HIGH", + "source_refs": [ + "apps/web/src/app/[locale]/awooop", + "apps/api/src/core/awooop_operator_auth.py", + "apps/api/src/core/csrf.py", + ], + "affected_scope": "AwoooP operator console、approvals、work-items、runs、admin auth / CSRF / owner guard", + }, + { + "candidate_id": "api_cors_and_public_url_runtime_config", + "title": "API / CORS / public URL runtime config", + "control_tier": "C0", + "risk": "HIGH", + "source_refs": [ + "apps/api/src/core/config.py", + "apps/api/src/config.py", + "apps/web/src/lib/config.ts", + "apps/web/src/lib/api-client.ts", + ], + "affected_scope": "API base URL、CORS origins、NEXT_PUBLIC build-time config、public domain / internal IP boundary", + }, + { + "candidate_id": "frontend_env_and_sentry_tunnel_runtime_config", + "title": "Frontend env / Sentry tunnel / browser runtime config", + "control_tier": "C0", + "risk": "HIGH", + "source_refs": [ + "apps/web/src/middleware.ts", + "apps/web/src/app/api/sentry-tunnel/route.ts", + "apps/web/src/app/api/health/route.ts", + ], + "affected_scope": "Next.js middleware、Sentry tunnel、browser-facing env、health route 與 console error boundary", + }, + { + "candidate_id": "webhook_callback_and_notification_runtime_config", + "title": "Webhook / callback / notification runtime route", + "control_tier": "C0", + "risk": "HIGH", + "source_refs": [ + "apps/api/src/models/webhook.py", + "apps/api/src/routers/proposals.py", + "apps/api/src/core/deep_linking.py", + ], + "affected_scope": "webhook callback、proposal route、deep link、notification route 與 external send boundary", + }, + { + "candidate_id": "cross_product_runtime_route_scope", + "title": "Cross-product runtime route scope", + "control_tier": "C1", + "risk": "MEDIUM", + "source_refs": [ + "docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md", + "docs/security/VIBEWORK-IWOOOS-ONBOARDING-HANDOFF.md", + "docs/security/AGENT-BOUNTY-IWOOOS-ONBOARDING-HANDOFF.md", + "docs/security/AGENT-BOUNTY-OWNER-REQUEST-DRAFT.md", + ], + "affected_scope": "VibeWork、agent-bounty-protocol、StockPlatform、官方形象網站、藥局網站與其他產品 runtime route scope", + }, +] + + +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 source_ref_exists(root: Path, ref: str) -> bool: + return (root / ref).exists() + + +def build_candidate(root: Path, definition: dict[str, Any]) -> dict[str, Any]: + source_refs = list(definition["source_refs"]) + return { + "change_evidence_candidate_id": f"public_runtime_config_change_evidence:{definition['candidate_id']}", + "status": "waiting_change_evidence", + "title": definition["title"], + "control_tier": definition["control_tier"], + "risk": definition["risk"], + "source_refs": source_refs, + "source_ref_existing_count": sum(1 for ref in source_refs if source_ref_exists(root, ref)), + "write_capable": True, + "requires_runtime_approval_package": True, + "affected_scope": definition["affected_scope"], + "change_evidence_fields": CHANGE_EVIDENCE_FIELDS, + "required_evidence_fields": REQUIRED_EVIDENCE_FIELDS, + "reviewer_checks": [item["check_id"] for item in REVIEWER_CHECKS], + "outcome_lanes": [item["lane_id"] for item in OUTCOME_LANES], + "blocked_actions": BLOCKED_ACTIONS, + "proposed_runtime_config_change_ref": None, + "affected_route_refs": [], + "public_url_or_domain_ref": None, + "admin_auth_boundary_ref": None, + "api_contract_readback_ref": None, + "cors_origin_diff_ref": None, + "frontend_env_diff_ref": None, + "i18n_redaction_review_ref": None, + "webhook_callback_owner_ref": None, + "desktop_mobile_smoke_ref": None, + "api_health_readback_ref": None, + "sensitive_string_scan_ref": None, + "console_error_scan_ref": None, + "blast_radius": "pending_change_evidence", + "maintenance_window": "pending_change_evidence", + "rollback_owner": "pending_change_evidence", + "rollback_plan_ref": None, + "postcheck_evidence_ref": None, + "redacted_evidence_refs": [], + "reviewer_outcome": "waiting_change_evidence", + "not_approval": True, + "change_evidence_received": False, + "change_evidence_accepted": False, + "change_evidence_rejected": False, + "change_evidence_quarantined": False, + "route_scope_accepted": False, + "admin_auth_boundary_accepted": False, + "api_contract_readback_accepted": False, + "cors_origin_diff_accepted": False, + "frontend_env_diff_accepted": False, + "i18n_redaction_review_accepted": False, + "webhook_callback_owner_accepted": False, + "desktop_mobile_smoke_accepted": False, + "sensitive_string_scan_accepted": False, + "postcheck_evidence_accepted": False, + "runtime_approval_package_ready": False, + "runtime_gate": False, + **{ + key: value + for key, value in EXECUTION_BOUNDARIES.items() + if key != "not_authorization" + }, + } + + +def build_report(root: Path, generated_at: str | None) -> dict[str, Any]: + report_time = generated_at or datetime.now(TAIPEI).isoformat(timespec="seconds") + candidates = [build_candidate(root, item) for item in CANDIDATE_DEFINITIONS] + c0_candidates = [item for item in candidates if item["control_tier"] == "C0"] + c1_candidates = [item for item in candidates if item["control_tier"] == "C1"] + source_refs = sorted({ref for item in candidates for ref in item["source_refs"]}) + + return { + "schema_version": "public_runtime_config_change_evidence_acceptance_v1", + "generated_at": report_time, + "git_commit": git_short_sha(root), + "status": "change_evidence_acceptance_ledger_ready_no_runtime_action", + "mode": "metadata_only_no_secret_no_route_change_no_deploy", + "source_paths": [ + "docs/HARD_RULES.md", + "docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md", + *source_refs, + ], + "summary": { + "change_evidence_candidate_count": len(candidates), + "c0_change_evidence_candidate_count": len(c0_candidates), + "c1_change_evidence_candidate_count": len(c1_candidates), + "write_capable_candidate_count": sum(1 for item in candidates if item["write_capable"]), + "source_ref_count": len(source_refs), + "existing_source_ref_count": sum(1 for ref in source_refs if source_ref_exists(root, ref)), + "required_evidence_field_count": len(REQUIRED_EVIDENCE_FIELDS), + "reviewer_check_count": len(REVIEWER_CHECKS), + "outcome_lane_count": len(OUTCOME_LANES), + "blocked_action_count": len(BLOCKED_ACTIONS), + "change_evidence_received_count": 0, + "change_evidence_accepted_count": 0, + "route_scope_accepted_count": 0, + "admin_auth_boundary_accepted_count": 0, + "api_contract_readback_accepted_count": 0, + "cors_origin_diff_accepted_count": 0, + "frontend_env_diff_accepted_count": 0, + "i18n_redaction_review_accepted_count": 0, + "webhook_callback_owner_accepted_count": 0, + "desktop_mobile_smoke_accepted_count": 0, + "sensitive_string_scan_accepted_count": 0, + "postcheck_evidence_accepted_count": 0, + "runtime_approval_package_ready_count": 0, + "runtime_gate_count": 0, + "action_button_count": 0, + "public_admin_api_runtime_config_coverage_percent_before_acceptance": 62, + "public_admin_api_runtime_config_coverage_percent_after_acceptance": 64, + }, + "execution_boundaries": EXECUTION_BOUNDARIES, + "change_evidence_fields": CHANGE_EVIDENCE_FIELDS, + "required_evidence_fields": REQUIRED_EVIDENCE_FIELDS, + "reviewer_checks": REVIEWER_CHECKS, + "outcome_lanes": OUTCOME_LANES, + "blocked_actions": BLOCKED_ACTIONS, + "change_evidence_candidates": candidates, + "operator_interpretation": [ + "此帳本只描述 public/admin/API/frontend runtime config 變更證據如何收件與拒收,不是 route、CORS、env、auth 或 webhook 變更批准。", + "前台、public API、HTML、bundle 與 messages 不得顯示 raw owner namespace、repo slug、內部狀態碼、內部對話或 secret value。", + "desktop/mobile smoke、API health、CD success、UI 可見與 AwoooP approval 都不能被解讀成 runtime gate 已開。", + "未來若要修改 public/admin/API route、CORS、NEXT_PUBLIC env、middleware auth、callback 或 webhook,仍需獨立 owner response、維護窗口、rollback owner 與 runtime approval package。", + ], + } + + +def main() -> int: + parser = argparse.ArgumentParser(description="Public / Admin / API runtime config 變更證據驗收只讀帳本") + 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( + "PUBLIC_RUNTIME_CONFIG_CHANGE_EVIDENCE_ACCEPTANCE_OK " + f"candidates={summary['change_evidence_candidate_count']} " + f"c0={summary['c0_change_evidence_candidate_count']} " + f"write_capable={summary['write_capable_candidate_count']} " + f"checks={summary['reviewer_check_count']} " + f"lanes={summary['outcome_lane_count']} " + f"accepted={summary['change_evidence_accepted_count']} " + f"runtime_gate={summary['runtime_gate_count']}", + file=sys.stderr, + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/security/security-mirror-progress-guard.py b/scripts/security/security-mirror-progress-guard.py index 7e4f12a01..6b1731d4d 100755 --- a/scripts/security/security-mirror-progress-guard.py +++ b/scripts/security/security-mirror-progress-guard.py @@ -149,6 +149,9 @@ def validate(root: Path) -> None: cd_runner_secret_injection_change_evidence_acceptance = load_json( security_dir / "cd-runner-secret-injection-change-evidence-acceptance.snapshot.json" ) + public_runtime_config_change_evidence_acceptance = load_json( + security_dir / "public-runtime-config-change-evidence-acceptance.snapshot.json" + ) public_gateway_preflight_inventory = load_json( security_dir / "public-gateway-preflight-inventory.snapshot.json" ) @@ -2506,7 +2509,7 @@ def validate(root: Path) -> None: assert_equal( "high_value_config_coverage.summary.needs_live_evidence_count", high_value_config_coverage["summary"]["needs_live_evidence_count"], - 8, + 9, ) for key in [ "owner_response_received_count", @@ -2883,6 +2886,32 @@ def validate(root: Path) -> None: workflow_runner_category["evidence_refs"], evidence_ref, ) + public_runtime_config_category = next( + item + for item in high_value_config_coverage["coverage_categories"] + if item["category_id"] == "public_admin_api_runtime_config" + ) + assert_equal( + "high_value_config_coverage.coverage_categories.public_runtime_config.coverage_percent", + public_runtime_config_category["coverage_percent"], + 64, + ) + assert_equal( + "high_value_config_coverage.coverage_categories.public_runtime_config.coverage_status", + public_runtime_config_category["coverage_status"], + "change_evidence_acceptance_ready_needs_runtime_config_owner_evidence", + ) + for evidence_ref in [ + "docs/HARD_RULES.md", + "docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md", + "docs/security/PUBLIC-RUNTIME-CONFIG-CHANGE-EVIDENCE-ACCEPTANCE.md", + "docs/security/public-runtime-config-change-evidence-acceptance.snapshot.json", + ]: + assert_contains( + "high_value_config_coverage.coverage_categories.public_runtime_config.evidence_refs", + public_runtime_config_category["evidence_refs"], + evidence_ref, + ) assert_contains( "high_value_config_coverage.lowest_coverage_categories.docker", [item["category_id"] for item in high_value_config_coverage["lowest_coverage_categories"]], @@ -5765,6 +5794,8 @@ def validate(root: Path) -> None: "docs/security/monitoring-alerting-observability-inventory.snapshot.json", "docs/security/MONITORING-ALERTING-OBSERVABILITY-INVENTORY.md", "docs/schemas/monitoring_alerting_observability_inventory_v1.schema.json", + "docs/security/public-runtime-config-change-evidence-acceptance.snapshot.json", + "docs/security/PUBLIC-RUNTIME-CONFIG-CHANGE-EVIDENCE-ACCEPTANCE.md", ]: assert_contains( "iwooos_projection.source_paths.config_inventory", @@ -5790,7 +5821,7 @@ def validate(root: Path) -> None: "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": 68, - "high_value_config_control_coverage_needs_live_evidence_count": 8, + "high_value_config_control_coverage_needs_live_evidence_count": 9, "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, @@ -5943,6 +5974,32 @@ def validate(root: Path) -> None: "cd_runner_secret_injection_change_evidence_acceptance_secret_metadata_coverage_percent_after_acceptance": 68, "cd_runner_secret_injection_change_evidence_acceptance_gitea_workflow_runner_coverage_percent_before_acceptance": 70, "cd_runner_secret_injection_change_evidence_acceptance_gitea_workflow_runner_coverage_percent_after_acceptance": 72, + "public_runtime_config_change_evidence_acceptance_first_layer": True, + "public_runtime_config_change_evidence_acceptance_candidate_count": 6, + "public_runtime_config_change_evidence_acceptance_c0_candidate_count": 5, + "public_runtime_config_change_evidence_acceptance_c1_candidate_count": 1, + "public_runtime_config_change_evidence_acceptance_write_capable_candidate_count": 6, + "public_runtime_config_change_evidence_acceptance_source_ref_count": 20, + "public_runtime_config_change_evidence_acceptance_required_evidence_field_count": 21, + "public_runtime_config_change_evidence_acceptance_reviewer_check_count": 21, + "public_runtime_config_change_evidence_acceptance_outcome_lane_count": 8, + "public_runtime_config_change_evidence_acceptance_blocked_action_count": 32, + "public_runtime_config_change_evidence_acceptance_received_count": 0, + "public_runtime_config_change_evidence_acceptance_accepted_count": 0, + "public_runtime_config_change_evidence_acceptance_route_scope_accepted_count": 0, + "public_runtime_config_change_evidence_acceptance_admin_auth_boundary_accepted_count": 0, + "public_runtime_config_change_evidence_acceptance_api_contract_readback_accepted_count": 0, + "public_runtime_config_change_evidence_acceptance_cors_origin_diff_accepted_count": 0, + "public_runtime_config_change_evidence_acceptance_frontend_env_diff_accepted_count": 0, + "public_runtime_config_change_evidence_acceptance_i18n_redaction_review_accepted_count": 0, + "public_runtime_config_change_evidence_acceptance_desktop_mobile_smoke_accepted_count": 0, + "public_runtime_config_change_evidence_acceptance_sensitive_string_scan_accepted_count": 0, + "public_runtime_config_change_evidence_acceptance_postcheck_evidence_accepted_count": 0, + "public_runtime_config_change_evidence_acceptance_runtime_approval_package_ready_count": 0, + "public_runtime_config_change_evidence_acceptance_runtime_gate_count": 0, + "public_runtime_config_change_evidence_acceptance_action_button_count": 0, + "public_runtime_config_change_evidence_acceptance_coverage_percent_before_acceptance": 62, + "public_runtime_config_change_evidence_acceptance_coverage_percent_after_acceptance": 64, } for key, expected in expected_ssh_network_projection_summary.items(): assert_equal( @@ -11762,8 +11819,8 @@ def validate(root: Path) -> None: ) for text in [ - "source_scope_id", - "source_namespace_redacted", + "sourcePublicScopeCode(index)", + "sourceRepos.map((repo, index)", "sourceRiskLabel(repo.risk, t)", "sourceReadinessLabel(repo.readiness_state, t)", "sourceActionLabel(repo.readiness_state, t)", @@ -11772,9 +11829,17 @@ def validate(root: Path) -> None: ]: assert_text_contains("awooop_tenants_page.source_namespace_redaction", awooop_tenants_page, text) for text in [ + "{repo.github_repo}", + "{repo.source_key}", + "{repo.source_scope_id}", + "key={`${repo.source_scope_id}", "{repo.risk}", "{repo.readiness_state}", + "{repo.scope_status}", "namespace_redacted={String(repo.source_namespace_redacted)}", + "blocked_waiting_", + "observe_scope_review", + "blockers=", "repo_owner_namespace_redacted=true", "raw_repository_namespace_visible=false", "public_api_raw_repo_namespace_allowed=false", @@ -15533,7 +15598,7 @@ def validate(root: Path) -> None: "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=68", - "high_value_config_control_coverage_needs_live_evidence_count=8", + "high_value_config_control_coverage_needs_live_evidence_count=9", "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", @@ -15543,6 +15608,29 @@ def validate(root: Path) -> None: "k8s_production_gitops_coverage_percent=64", "secret_metadata_coverage_percent=68", "gitea_workflow_runner_source_control_coverage_percent=72", + "public_admin_api_runtime_config_coverage_percent=64", + "public_runtime_config_change_evidence_acceptance_candidate_count=6", + "public_runtime_config_change_evidence_acceptance_c0_candidate_count=5", + "public_runtime_config_change_evidence_acceptance_c1_candidate_count=1", + "public_runtime_config_change_evidence_acceptance_write_capable_candidate_count=6", + "public_runtime_config_change_evidence_acceptance_source_ref_count=20", + "public_runtime_config_change_evidence_acceptance_required_evidence_field_count=21", + "public_runtime_config_change_evidence_acceptance_reviewer_check_count=21", + "public_runtime_config_change_evidence_acceptance_outcome_lane_count=8", + "public_runtime_config_change_evidence_acceptance_blocked_action_count=32", + "public_runtime_config_change_evidence_acceptance_received_count=0", + "public_runtime_config_change_evidence_acceptance_accepted_count=0", + "public_runtime_config_change_evidence_acceptance_route_scope_accepted_count=0", + "public_runtime_config_change_evidence_acceptance_admin_auth_boundary_accepted_count=0", + "public_runtime_config_change_evidence_acceptance_api_contract_readback_accepted_count=0", + "public_runtime_config_change_evidence_acceptance_cors_origin_diff_accepted_count=0", + "public_runtime_config_change_evidence_acceptance_frontend_env_diff_accepted_count=0", + "public_runtime_config_change_evidence_acceptance_i18n_redaction_review_accepted_count=0", + "public_runtime_config_change_evidence_acceptance_desktop_mobile_smoke_accepted_count=0", + "public_runtime_config_change_evidence_acceptance_sensitive_string_scan_accepted_count=0", + "public_runtime_config_change_evidence_acceptance_postcheck_evidence_accepted_count=0", + "public_runtime_config_change_evidence_acceptance_runtime_approval_package_ready_count=0", + "public_runtime_config_change_evidence_acceptance_runtime_gate_count=0", "cd_runner_secret_injection_change_evidence_acceptance_candidate_count=5", "cd_runner_secret_injection_change_evidence_acceptance_c0_candidate_count=4", "cd_runner_secret_injection_change_evidence_acceptance_c1_candidate_count=1", @@ -15701,6 +15789,27 @@ def validate(root: Path) -> None: "secret_rotation_authorized=false", "secret_store_read_authorized=false", "secret_injection_change_authorized=false", + "runtime_config_change_authorized=false", + "api_route_change_authorized=false", + "cors_change_authorized=false", + "frontend_env_change_authorized=false", + "middleware_auth_change_authorized=false", + "callback_url_change_authorized=false", + "webhook_secret_change_authorized=false", + "security_header_change_authorized=false", + "cookie_policy_change_authorized=false", + "csrf_disable_authorized=false", + "rate_limit_disable_authorized=false", + "api_contract_change_authorized=false", + "i18n_public_text_internal_identity_allowed=false", + "internal_ip_exposure_allowed=false", + "repo_namespace_exposure_allowed=false", + "owner_namespace_exposure_allowed=false", + "internal_status_code_exposure_allowed=false", + "internal_transcript_exposure_allowed=false", + "raw_payload_storage_allowed=false", + "desktop_mobile_smoke_authorized=false", + "database_migration_authorized=false", "github_hosted_runner_enable_authorized=false", "gitea_action_dispatch_authorized=false", "cd_pipeline_run_authorized=false", @@ -15744,6 +15853,11 @@ def validate(root: Path) -> None: iwooos_projection_page, "{ key: 'workflowSecret', rank: 'P0-4', state: '68% / 72%'", ) + assert_text_contains( + "iwooos_page.critical_config_priority_public_runtime_percent", + iwooos_projection_page, + "{ key: 'publicRuntime', rank: 'P0-5', state: '64% / route 0'", + ) for text in [ "critical_config_priority_frontstage_summary_count=4", "critical_config_priority_item_count=6", @@ -15760,6 +15874,26 @@ def validate(root: Path) -> None: "nginx_public_gateway_nginx_test_evidence_count=0", "secret_metadata_coverage_percent=68", "gitea_workflow_runner_source_control_coverage_percent=72", + "public_admin_api_runtime_config_coverage_percent=64", + "public_runtime_config_change_evidence_acceptance_candidate_count=6", + "public_runtime_config_change_evidence_acceptance_c0_candidate_count=5", + "public_runtime_config_change_evidence_acceptance_write_capable_candidate_count=6", + "public_runtime_config_change_evidence_acceptance_required_evidence_field_count=21", + "public_runtime_config_change_evidence_acceptance_reviewer_check_count=21", + "public_runtime_config_change_evidence_acceptance_outcome_lane_count=8", + "public_runtime_config_change_evidence_acceptance_blocked_action_count=32", + "public_runtime_config_change_evidence_acceptance_received_count=0", + "public_runtime_config_change_evidence_acceptance_accepted_count=0", + "public_runtime_config_change_evidence_acceptance_route_scope_accepted_count=0", + "public_runtime_config_change_evidence_acceptance_admin_auth_boundary_accepted_count=0", + "public_runtime_config_change_evidence_acceptance_api_contract_readback_accepted_count=0", + "public_runtime_config_change_evidence_acceptance_cors_origin_diff_accepted_count=0", + "public_runtime_config_change_evidence_acceptance_frontend_env_diff_accepted_count=0", + "public_runtime_config_change_evidence_acceptance_i18n_redaction_review_accepted_count=0", + "public_runtime_config_change_evidence_acceptance_desktop_mobile_smoke_accepted_count=0", + "public_runtime_config_change_evidence_acceptance_sensitive_string_scan_accepted_count=0", + "public_runtime_config_change_evidence_acceptance_runtime_approval_package_ready_count=0", + "public_runtime_config_change_evidence_acceptance_runtime_gate_count=0", "cd_runner_secret_injection_change_evidence_acceptance_candidate_count=5", "cd_runner_secret_injection_change_evidence_acceptance_c0_candidate_count=4", "cd_runner_secret_injection_change_evidence_acceptance_write_capable_candidate_count=5", @@ -15796,6 +15930,25 @@ def validate(root: Path) -> None: "runner_change_authorized=false", "repo_secret_change_authorized=false", "secret_injection_change_authorized=false", + "runtime_config_change_authorized=false", + "api_route_change_authorized=false", + "cors_change_authorized=false", + "frontend_env_change_authorized=false", + "middleware_auth_change_authorized=false", + "callback_url_change_authorized=false", + "webhook_secret_change_authorized=false", + "security_header_change_authorized=false", + "cookie_policy_change_authorized=false", + "csrf_disable_authorized=false", + "rate_limit_disable_authorized=false", + "api_contract_change_authorized=false", + "i18n_public_text_internal_identity_allowed=false", + "internal_ip_exposure_allowed=false", + "repo_namespace_exposure_allowed=false", + "owner_namespace_exposure_allowed=false", + "internal_status_code_exposure_allowed=false", + "internal_transcript_exposure_allowed=false", + "raw_payload_storage_allowed=false", "gitea_action_dispatch_authorized=false", "cd_pipeline_run_authorized=false", "production_deploy_authorized=false", @@ -15831,6 +15984,21 @@ def validate(root: Path) -> None: web_messages_en["iwooos"]["criticalConfigPriority"]["items"]["k8sArgocd"]["body"], "GitOps 變更證據驗收", ) + for locale, messages in [ + ("zh-TW", web_messages_zh), + ("en", web_messages_en), + ]: + public_runtime_body = messages["iwooos"]["criticalConfigPriority"]["items"]["publicRuntime"]["body"] + assert_text_contains( + f"web_messages.{locale}.iwooos.criticalConfigPriority.items.publicRuntime.body.acceptance", + public_runtime_body, + "變更證據驗收", + ) + assert_text_contains( + f"web_messages.{locale}.iwooos.criticalConfigPriority.items.publicRuntime.body.redaction", + public_runtime_body, + "不得暴露 raw namespace", + ) assert_contains( "web_messages.zh-TW.iwooos.highValueConfigControlCoverage", list(web_messages_zh["iwooos"].keys()), @@ -17341,6 +17509,244 @@ def validate(root: Path) -> None: f"cd_runner_secret_injection_change_evidence_acceptance.{item['change_evidence_candidate_id']}.{false_key}", item[false_key], ) + assert_equal( + "public_runtime_config_change_evidence_acceptance.schema", + public_runtime_config_change_evidence_acceptance["schema_version"], + "public_runtime_config_change_evidence_acceptance_v1", + ) + assert_equal( + "public_runtime_config_change_evidence_acceptance.status", + public_runtime_config_change_evidence_acceptance["status"], + "change_evidence_acceptance_ledger_ready_no_runtime_action", + ) + assert_equal( + "public_runtime_config_change_evidence_acceptance.mode", + public_runtime_config_change_evidence_acceptance["mode"], + "metadata_only_no_secret_no_route_change_no_deploy", + ) + expected_public_runtime_config_summary = { + "change_evidence_candidate_count": 6, + "c0_change_evidence_candidate_count": 5, + "c1_change_evidence_candidate_count": 1, + "write_capable_candidate_count": 6, + "source_ref_count": 20, + "existing_source_ref_count": 20, + "required_evidence_field_count": 21, + "reviewer_check_count": 21, + "outcome_lane_count": 8, + "blocked_action_count": 32, + "change_evidence_received_count": 0, + "change_evidence_accepted_count": 0, + "route_scope_accepted_count": 0, + "admin_auth_boundary_accepted_count": 0, + "api_contract_readback_accepted_count": 0, + "cors_origin_diff_accepted_count": 0, + "frontend_env_diff_accepted_count": 0, + "i18n_redaction_review_accepted_count": 0, + "webhook_callback_owner_accepted_count": 0, + "desktop_mobile_smoke_accepted_count": 0, + "sensitive_string_scan_accepted_count": 0, + "postcheck_evidence_accepted_count": 0, + "runtime_approval_package_ready_count": 0, + "runtime_gate_count": 0, + "action_button_count": 0, + "public_admin_api_runtime_config_coverage_percent_before_acceptance": 62, + "public_admin_api_runtime_config_coverage_percent_after_acceptance": 64, + } + for key, expected in expected_public_runtime_config_summary.items(): + assert_equal( + f"public_runtime_config_change_evidence_acceptance.summary.{key}", + public_runtime_config_change_evidence_acceptance["summary"][key], + expected, + ) + for key, value in public_runtime_config_change_evidence_acceptance["execution_boundaries"].items(): + if key == "not_authorization": + assert_true(f"public_runtime_config_change_evidence_acceptance.execution_boundaries.{key}", value) + else: + assert_false(f"public_runtime_config_change_evidence_acceptance.execution_boundaries.{key}", value) + expected_public_runtime_config_ids = [ + "public_runtime_config_change_evidence:public_product_route_and_i18n_redaction", + "public_runtime_config_change_evidence:admin_auth_and_operator_console_boundary", + "public_runtime_config_change_evidence:api_cors_and_public_url_runtime_config", + "public_runtime_config_change_evidence:frontend_env_and_sentry_tunnel_runtime_config", + "public_runtime_config_change_evidence:webhook_callback_and_notification_runtime_config", + "public_runtime_config_change_evidence:cross_product_runtime_route_scope", + ] + assert_equal( + "public_runtime_config_change_evidence_acceptance.candidate_ids", + [ + item["change_evidence_candidate_id"] + for item in public_runtime_config_change_evidence_acceptance["change_evidence_candidates"] + ], + expected_public_runtime_config_ids, + ) + expected_public_runtime_config_check_ids = [ + "change_ref_present", + "affected_route_refs_present", + "public_url_not_internal_ip", + "admin_auth_boundary_called_out", + "api_contract_readback_present", + "cors_origin_diff_ref_only", + "frontend_env_diff_ref_present", + "i18n_redaction_review_present", + "webhook_callback_owner_present", + "desktop_mobile_smoke_present", + "api_health_readback_present", + "sensitive_string_scan_present", + "console_error_scan_present", + "no_secret_value_or_cookie", + "security_header_or_cookie_impact_called_out", + "blast_radius_present", + "maintenance_window_present", + "rollback_owner_present", + "postcheck_evidence_present", + "no_runtime_action_claim", + "cross_project_sync_noted", + ] + assert_equal( + "public_runtime_config_change_evidence_acceptance.reviewer_check_ids", + [item["check_id"] for item in public_runtime_config_change_evidence_acceptance["reviewer_checks"]], + expected_public_runtime_config_check_ids, + ) + expected_public_runtime_config_outcome_ids = [ + "waiting_change_evidence", + "quarantine_sensitive_payload", + "reject_unredacted_or_runtime_claim", + "request_supplement", + "ready_for_reviewer_acceptance", + "ready_for_runtime_approval_package", + "waiting_maintenance_window", + "waiting_runtime_gate", + ] + assert_equal( + "public_runtime_config_change_evidence_acceptance.outcome_lane_ids", + [item["lane_id"] for item in public_runtime_config_change_evidence_acceptance["outcome_lanes"]], + expected_public_runtime_config_outcome_ids, + ) + expected_public_runtime_config_blocked_actions = [ + "change_public_route", + "change_admin_route", + "change_api_route", + "change_cors_origin", + "modify_next_public_env", + "expose_internal_ip", + "expose_repo_slug", + "expose_owner_namespace", + "expose_secret_value", + "bypass_auth", + "change_callback_url", + "change_webhook_secret", + "modify_middleware_auth", + "disable_csrf", + "disable_rate_limit", + "change_cookie_policy", + "change_security_headers", + "publish_internal_transcript", + "publish_internal_status_code", + "deploy_frontend", + "deploy_api", + "rewrite_nginx_route", + "change_public_url", + "change_openapi_contract", + "mutate_database", + "run_migration", + "send_webhook", + "active_scan", + "enable_action_button", + "production_deploy", + "force_push", + "switch_github_primary", + ] + assert_equal( + "public_runtime_config_change_evidence_acceptance.blocked_actions", + public_runtime_config_change_evidence_acceptance["blocked_actions"], + expected_public_runtime_config_blocked_actions, + ) + for item in public_runtime_config_change_evidence_acceptance["change_evidence_candidates"]: + assert_equal( + f"public_runtime_config_change_evidence_acceptance.{item['change_evidence_candidate_id']}.change_evidence_fields", + len(item["change_evidence_fields"]), + 24, + ) + assert_equal( + f"public_runtime_config_change_evidence_acceptance.{item['change_evidence_candidate_id']}.required_evidence_fields", + len(item["required_evidence_fields"]), + 21, + ) + assert_equal( + f"public_runtime_config_change_evidence_acceptance.{item['change_evidence_candidate_id']}.reviewer_checks", + len(item["reviewer_checks"]), + 21, + ) + assert_equal( + f"public_runtime_config_change_evidence_acceptance.{item['change_evidence_candidate_id']}.outcome_lanes", + len(item["outcome_lanes"]), + 8, + ) + assert_equal( + f"public_runtime_config_change_evidence_acceptance.{item['change_evidence_candidate_id']}.blocked_actions", + len(item["blocked_actions"]), + 32, + ) + assert_true( + f"public_runtime_config_change_evidence_acceptance.{item['change_evidence_candidate_id']}.not_approval", + item["not_approval"], + ) + for false_key in [ + "change_evidence_received", + "change_evidence_accepted", + "change_evidence_rejected", + "change_evidence_quarantined", + "route_scope_accepted", + "admin_auth_boundary_accepted", + "api_contract_readback_accepted", + "cors_origin_diff_accepted", + "frontend_env_diff_accepted", + "i18n_redaction_review_accepted", + "webhook_callback_owner_accepted", + "desktop_mobile_smoke_accepted", + "sensitive_string_scan_accepted", + "postcheck_evidence_accepted", + "runtime_approval_package_ready", + "runtime_execution_authorized", + "runtime_config_change_authorized", + "public_route_change_authorized", + "admin_route_change_authorized", + "api_route_change_authorized", + "cors_change_authorized", + "frontend_env_change_authorized", + "middleware_auth_change_authorized", + "callback_url_change_authorized", + "webhook_receiver_change_authorized", + "webhook_secret_change_authorized", + "security_header_change_authorized", + "cookie_policy_change_authorized", + "csrf_disable_authorized", + "rate_limit_disable_authorized", + "api_contract_change_authorized", + "i18n_public_text_internal_identity_allowed", + "internal_ip_exposure_allowed", + "repo_namespace_exposure_allowed", + "owner_namespace_exposure_allowed", + "internal_status_code_exposure_allowed", + "internal_transcript_exposure_allowed", + "secret_value_collection_allowed", + "secret_hash_collection_allowed", + "partial_token_collection_allowed", + "raw_payload_storage_allowed", + "desktop_mobile_smoke_authorized", + "route_smoke_authorized", + "production_deploy_authorized", + "database_migration_authorized", + "force_push_authorized", + "github_primary_switch_authorized", + "runtime_gate", + "action_buttons_allowed", + ]: + assert_false( + f"public_runtime_config_change_evidence_acceptance.{item['change_evidence_candidate_id']}.{false_key}", + item[false_key], + ) assert_equal( "iwooos_projection.summary.domain_tls_certbot_inventory_managed_domain_count", iwooos_projection["summary"]["domain_tls_certbot_inventory_managed_domain_count"],