diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index de70cdd99..aeb46f577 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -8719,6 +8719,12 @@ "iwooosMirror": "IwoooS 顯示資安鏡像態勢、進度、證據參照與禁止動作。", "hostCoverage": "三台指定主機目前只納入資安視野與證據就緒度;不做 SSH、更新、憑證式掃描或阻擋型控制。", "ownerResponse": "S4.9-S4.12 負責人回覆已收到 / 已接受仍為 0,租戶範圍顯示不等於批准。" + }, + "boundaryItems": { + "migrationLocked": "租戶遷移模式沒有變更。", + "policyLocked": "租戶政策沒有被修改,也沒有授權修改。", + "runtimeLocked": "執行期操作維持關閉;不啟動掃描、部署、修復或主機操作。", + "actionButtonsLocked": "此頁不提供任何可執行操作按鈕。" } }, "githubTenantReadinessScope": { @@ -8753,6 +8759,17 @@ "giteaInventoryOwner": "S4.9仍等待 Gitea 清冊負責人證明;覆蓋範圍接受前不得補寫專案庫範圍。", "githubTargetOwner": "S4.10仍等待 GitHub 目標負責人決策;目標負責人未接受前不得建立專案庫或改可見性。", "workflowSecretOwner": "S4.12只等待工作流程 / 機密名稱負責人回覆;仍不得收機密明文值或修改 GitHub 機密設定。" + }, + "boundaryItems": { + "sourceScopeWaiting": "租戶原始碼範圍仍等待正式負責人回覆。", + "ownerResponseWaiting": "專案庫負責人回覆尚未接受。", + "repoCreationLocked": "未取得正式決策前,不建立 GitHub 專案庫或修改可見性。", + "refsMutationLocked": "未取得分支 / 標籤真相證據前,不同步或修改 refs。", + "githubPrimaryLocked": "GitHub primary 尚未核准切換。", + "giteaDisableLocked": "Gitea 不得停用,仍是目前 CI/CD 來源。", + "tenantPolicyLocked": "租戶政策與遷移模式不得因就緒度顯示而改變。", + "runtimeLocked": "執行期操作維持關閉。", + "actionButtonsLocked": "此頁不提供任何可執行操作按鈕。" } }, "ownerResponseValidationScope": { @@ -8799,6 +8816,20 @@ "title": "S4.12 工作流程 / 機密名稱負責人回覆", "detail": "5 類名稱與脫敏證據仍等待回覆;只允許名稱清冊,不允許機密明文值。" } + }, + "boundaryItems": { + "responseNotReceived": "負責人回覆尚未收到。", + "responseNotAccepted": "負責人回覆尚未接受。", + "responseNotRejected": "目前沒有正式拒收紀錄。", + "tenantScopeWaiting": "租戶原始碼範圍仍等待驗收。", + "tenantPolicyLocked": "租戶政策不得修改。", + "repoCreationLocked": "不得建立專案庫或修改可見性。", + "refsSyncLocked": "不得同步、刪除或強制推送分支 / 標籤。", + "workflowLocked": "不得修改工作流程或 runner 設定。", + "secretValueBlocked": "不得收集或顯示任何機密明文值。", + "githubPrimaryLocked": "不得切換 GitHub primary。", + "runtimeLocked": "不得開啟執行期閘門。", + "actionButtonsLocked": "不得提供可執行操作按鈕。" } } }, diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index de70cdd99..aeb46f577 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -8719,6 +8719,12 @@ "iwooosMirror": "IwoooS 顯示資安鏡像態勢、進度、證據參照與禁止動作。", "hostCoverage": "三台指定主機目前只納入資安視野與證據就緒度;不做 SSH、更新、憑證式掃描或阻擋型控制。", "ownerResponse": "S4.9-S4.12 負責人回覆已收到 / 已接受仍為 0,租戶範圍顯示不等於批准。" + }, + "boundaryItems": { + "migrationLocked": "租戶遷移模式沒有變更。", + "policyLocked": "租戶政策沒有被修改,也沒有授權修改。", + "runtimeLocked": "執行期操作維持關閉;不啟動掃描、部署、修復或主機操作。", + "actionButtonsLocked": "此頁不提供任何可執行操作按鈕。" } }, "githubTenantReadinessScope": { @@ -8753,6 +8759,17 @@ "giteaInventoryOwner": "S4.9仍等待 Gitea 清冊負責人證明;覆蓋範圍接受前不得補寫專案庫範圍。", "githubTargetOwner": "S4.10仍等待 GitHub 目標負責人決策;目標負責人未接受前不得建立專案庫或改可見性。", "workflowSecretOwner": "S4.12只等待工作流程 / 機密名稱負責人回覆;仍不得收機密明文值或修改 GitHub 機密設定。" + }, + "boundaryItems": { + "sourceScopeWaiting": "租戶原始碼範圍仍等待正式負責人回覆。", + "ownerResponseWaiting": "專案庫負責人回覆尚未接受。", + "repoCreationLocked": "未取得正式決策前,不建立 GitHub 專案庫或修改可見性。", + "refsMutationLocked": "未取得分支 / 標籤真相證據前,不同步或修改 refs。", + "githubPrimaryLocked": "GitHub primary 尚未核准切換。", + "giteaDisableLocked": "Gitea 不得停用,仍是目前 CI/CD 來源。", + "tenantPolicyLocked": "租戶政策與遷移模式不得因就緒度顯示而改變。", + "runtimeLocked": "執行期操作維持關閉。", + "actionButtonsLocked": "此頁不提供任何可執行操作按鈕。" } }, "ownerResponseValidationScope": { @@ -8799,6 +8816,20 @@ "title": "S4.12 工作流程 / 機密名稱負責人回覆", "detail": "5 類名稱與脫敏證據仍等待回覆;只允許名稱清冊,不允許機密明文值。" } + }, + "boundaryItems": { + "responseNotReceived": "負責人回覆尚未收到。", + "responseNotAccepted": "負責人回覆尚未接受。", + "responseNotRejected": "目前沒有正式拒收紀錄。", + "tenantScopeWaiting": "租戶原始碼範圍仍等待驗收。", + "tenantPolicyLocked": "租戶政策不得修改。", + "repoCreationLocked": "不得建立專案庫或修改可見性。", + "refsSyncLocked": "不得同步、刪除或強制推送分支 / 標籤。", + "workflowLocked": "不得修改工作流程或 runner 設定。", + "secretValueBlocked": "不得收集或顯示任何機密明文值。", + "githubPrimaryLocked": "不得切換 GitHub primary。", + "runtimeLocked": "不得開啟執行期閘門。", + "actionButtonsLocked": "不得提供可執行操作按鈕。" } } }, diff --git a/apps/web/src/app/[locale]/awooop/tenants/page.tsx b/apps/web/src/app/[locale]/awooop/tenants/page.tsx index 292bb2b2a..9b7be45bb 100644 --- a/apps/web/src/app/[locale]/awooop/tenants/page.tsx +++ b/apps/web/src/app/[locale]/awooop/tenants/page.tsx @@ -147,7 +147,6 @@ type GitHubTenantReadinessScope = { type OwnerResponseValidationTenantRef = { phase: string; key: string; - contract: string; }; type AssetTone = "steady" | "warn" | "locked"; @@ -169,7 +168,7 @@ const githubTenantReadinessScopes: GitHubTenantReadinessScope[] = [ { key: "tenantSourceScope", name: "AWOOOI 第一租戶原始碼管控範圍", - status: "waiting", + status: "等待中", }, { key: "giteaInventoryOwner", @@ -188,59 +187,61 @@ const githubTenantReadinessScopes: GitHubTenantReadinessScope[] = [ }, ]; +const tenantScopeBoundaryItems = [ + "migrationLocked", + "policyLocked", + "runtimeLocked", + "actionButtonsLocked", +]; + const githubTenantReadinessBoundaries = [ - "tenant_source_control_scope_accepted=false", - "repo_owner_response_accepted=false", - "repo_creation_authorized=false", - "refs_mutation_authorized=false", - "github_primary_switch_authorized=false", - "gitea_disablement_authorized=false", - "tenant_policy_mutation_authorized=false", - "runtime_execution_authorized=false", - "action_buttons_allowed=false", + "sourceScopeWaiting", + "ownerResponseWaiting", + "repoCreationLocked", + "refsMutationLocked", + "githubPrimaryLocked", + "giteaDisableLocked", + "tenantPolicyLocked", + "runtimeLocked", + "actionButtonsLocked", ]; const ownerResponseValidationTenantRefs: OwnerResponseValidationTenantRef[] = [ { phase: "S4.13", key: "validationRollup", - contract: "source_control_owner_response_validation_rollup_v1", }, { phase: "S4.9", key: "giteaAttestation", - contract: "gitea_inventory_owner_attestation_response_v1", }, { phase: "S4.10", key: "githubTarget", - contract: "github_target_owner_decision_response_v1", }, { phase: "S4.11", key: "refsTruth", - contract: "source_control_ref_truth_owner_response_v1", }, { phase: "S4.12", key: "workflowSecret", - contract: "source_control_workflow_secret_name_owner_response_v1", }, ]; const ownerResponseValidationTenantBoundaries = [ - "owner_response_validation_received_count=0", - "owner_response_validation_accepted_count=0", - "owner_response_validation_rejected_count=0", - "tenant_source_control_scope_accepted=false", - "tenant_policy_mutation_authorized=false", - "repo_creation_authorized=false", - "refs_sync_authorized=false", - "workflow_modification_authorized=false", - "secret_value_collection_allowed=false", - "github_primary_switch_authorized=false", - "runtime_execution_authorized=false", - "action_buttons_allowed=false", + "responseNotReceived", + "responseNotAccepted", + "responseNotRejected", + "tenantScopeWaiting", + "tenantPolicyLocked", + "repoCreationLocked", + "refsSyncLocked", + "workflowLocked", + "secretValueBlocked", + "githubPrimaryLocked", + "runtimeLocked", + "actionButtonsLocked", ]; const globalAssetBoundaryItems = [ @@ -546,7 +547,8 @@ function SecurityTenantScopeCandidatePanel() {

{t("boundaryTitle")}

-

4 gates = false

+

{t("boundaryItems.runtimeLocked")}

+

{t("boundaryItems.actionButtonsLocked")}

{t("boundaryLabel")}

{t("boundaryTitle")}

{t("boundaryDetail")}

-
- tenant_migration_mode_changed=false - tenant_policy_mutation_authorized=false - runtime_execution_authorized=false - action_buttons_allowed=false +
+ {tenantScopeBoundaryItems.map((item) => ( + {t(`boundaryItems.${item}` as never)} + ))}
{t("boundaryLabel")}

{t("boundaryTitle")}

{t("boundaryDetail")}

-
+
{githubTenantReadinessBoundaries.map((boundary) => ( - {boundary} + {t(`boundaryItems.${boundary}` as never)} ))}
@@ -805,9 +806,6 @@ function OwnerResponseValidationTenantScopePanel() {

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

-

- {item.contract} -

))} @@ -818,9 +816,9 @@ function OwnerResponseValidationTenantScopePanel() {

{t("boundaryLabel")}

{t("boundaryTitle")}

{t("boundaryDetail")}

-
+
{ownerResponseValidationTenantBoundaries.map((boundary) => ( - {boundary} + {t(`boundaryItems.${boundary}` as never)} ))}
diff --git a/scripts/security/security-mirror-progress-guard.py b/scripts/security/security-mirror-progress-guard.py index 889795486..34ff63f64 100755 --- a/scripts/security/security-mirror-progress-guard.py +++ b/scripts/security/security-mirror-progress-guard.py @@ -11722,13 +11722,24 @@ def validate(root: Path) -> None: "SecurityTenantScopeCandidatePanel", ) assert_text_contains("awooop_tenants_page.iwooos_link", awooop_tenants_page, 'href="/iwooos"') + for text in [ + "tenantScopeBoundaryItems", + "migrationLocked", + "policyLocked", + "runtimeLocked", + "actionButtonsLocked", + "boundaryItems.runtimeLocked", + "boundaryItems.actionButtonsLocked", + ]: + assert_text_contains("awooop_tenants_page.security_tenant_boundary", awooop_tenants_page, text) for text in [ "tenant_migration_mode_changed=false", "tenant_policy_mutation_authorized=false", "runtime_execution_authorized=false", "action_buttons_allowed=false", + "4 gates = false", ]: - assert_text_contains("awooop_tenants_page.security_tenant_boundary", awooop_tenants_page, text) + assert_text_not_contains("awooop_tenants_page.security_tenant_raw_boundary_leak", awooop_tenants_page, text) for text in [ "AWOOOI 第一租戶", "IwoooS 資安鏡像", @@ -11752,6 +11763,7 @@ def validate(root: Path) -> None: "openIwooos", "metrics", "scopeRefs", + "boundaryItems", ]: assert_contains( "web_messages.zh-TW.awooop.tenants.securityTenantScopeCandidate", @@ -11763,6 +11775,17 @@ def validate(root: Path) -> None: list(web_messages_en["awooop"]["tenants"]["securityTenantScopeCandidate"].keys()), key, ) + for key in ["migrationLocked", "policyLocked", "runtimeLocked", "actionButtonsLocked"]: + assert_contains( + "web_messages.zh-TW.awooop.tenants.securityTenantScopeCandidate.boundaryItems", + list(web_messages_zh["awooop"]["tenants"]["securityTenantScopeCandidate"]["boundaryItems"].keys()), + key, + ) + assert_contains( + "web_messages.en.awooop.tenants.securityTenantScopeCandidate.boundaryItems", + list(web_messages_en["awooop"]["tenants"]["securityTenantScopeCandidate"]["boundaryItems"].keys()), + key, + ) assert_text_contains( "awooop_tenants_page.github_tenant_scope_panel", @@ -11784,6 +11807,19 @@ def validate(root: Path) -> None: "S4.9 Gitea 清冊負責人證明", "S4.10 GitHub 目標負責人決策", "S4.12 工作流程 / 機密名稱負責人回覆", + "githubTenantReadinessBoundaries", + "sourceScopeWaiting", + "ownerResponseWaiting", + "repoCreationLocked", + "refsMutationLocked", + "githubPrimaryLocked", + "giteaDisableLocked", + "tenantPolicyLocked", + "runtimeLocked", + "actionButtonsLocked", + ]: + assert_text_contains("awooop_tenants_page.github_tenant_scope_boundary", awooop_tenants_page, text) + for text in [ "tenant_source_control_scope_accepted=false", "repo_owner_response_accepted=false", "repo_creation_authorized=false", @@ -11794,7 +11830,7 @@ def validate(root: Path) -> None: "runtime_execution_authorized=false", "action_buttons_allowed=false", ]: - assert_text_contains("awooop_tenants_page.github_tenant_scope_boundary", awooop_tenants_page, text) + assert_text_not_contains("awooop_tenants_page.github_tenant_raw_boundary_leak", awooop_tenants_page, text) for key in [ "title", "subtitle", @@ -11806,6 +11842,7 @@ def validate(root: Path) -> None: "boundaryDetail", "metrics", "scopeRefs", + "boundaryItems", ]: assert_contains( "web_messages.zh-TW.awooop.tenants.githubTenantReadinessScope", @@ -11817,6 +11854,27 @@ def validate(root: Path) -> None: list(web_messages_en["awooop"]["tenants"]["githubTenantReadinessScope"].keys()), key, ) + for key in [ + "sourceScopeWaiting", + "ownerResponseWaiting", + "repoCreationLocked", + "refsMutationLocked", + "githubPrimaryLocked", + "giteaDisableLocked", + "tenantPolicyLocked", + "runtimeLocked", + "actionButtonsLocked", + ]: + assert_contains( + "web_messages.zh-TW.awooop.tenants.githubTenantReadinessScope.boundaryItems", + list(web_messages_zh["awooop"]["tenants"]["githubTenantReadinessScope"]["boundaryItems"].keys()), + key, + ) + assert_contains( + "web_messages.en.awooop.tenants.githubTenantReadinessScope.boundaryItems", + list(web_messages_en["awooop"]["tenants"]["githubTenantReadinessScope"]["boundaryItems"].keys()), + key, + ) for text in [ "sourcePublicScopeCode(index)", @@ -11913,6 +11971,22 @@ def validate(root: Path) -> None: awooop_tenants_page, 'href="/iwooos"', ) + for text in [ + "ownerResponseValidationTenantBoundaries", + "responseNotReceived", + "responseNotAccepted", + "responseNotRejected", + "tenantScopeWaiting", + "tenantPolicyLocked", + "repoCreationLocked", + "refsSyncLocked", + "workflowLocked", + "secretValueBlocked", + "githubPrimaryLocked", + "runtimeLocked", + "actionButtonsLocked", + ]: + assert_text_contains("awooop_tenants_page.owner_response_validation_boundary", awooop_tenants_page, text) for text in [ "source_control_owner_response_validation_rollup_v1", "gitea_inventory_owner_attestation_response_v1", @@ -11932,7 +12006,7 @@ def validate(root: Path) -> None: "runtime_execution_authorized=false", "action_buttons_allowed=false", ]: - assert_text_contains("awooop_tenants_page.owner_response_validation_boundary", awooop_tenants_page, text) + assert_text_not_contains("awooop_tenants_page.owner_response_validation_raw_boundary_leak", awooop_tenants_page, text) for key in [ "title", "subtitle", @@ -11944,6 +12018,7 @@ def validate(root: Path) -> None: "boundaryDetail", "metrics", "scopeRefs", + "boundaryItems", ]: assert_contains( "web_messages.zh-TW.awooop.tenants.ownerResponseValidationScope", @@ -11955,6 +12030,30 @@ def validate(root: Path) -> None: list(web_messages_en["awooop"]["tenants"]["ownerResponseValidationScope"].keys()), key, ) + for key in [ + "responseNotReceived", + "responseNotAccepted", + "responseNotRejected", + "tenantScopeWaiting", + "tenantPolicyLocked", + "repoCreationLocked", + "refsSyncLocked", + "workflowLocked", + "secretValueBlocked", + "githubPrimaryLocked", + "runtimeLocked", + "actionButtonsLocked", + ]: + assert_contains( + "web_messages.zh-TW.awooop.tenants.ownerResponseValidationScope.boundaryItems", + list(web_messages_zh["awooop"]["tenants"]["ownerResponseValidationScope"]["boundaryItems"].keys()), + key, + ) + assert_contains( + "web_messages.en.awooop.tenants.ownerResponseValidationScope.boundaryItems", + list(web_messages_en["awooop"]["tenants"]["ownerResponseValidationScope"]["boundaryItems"].keys()), + key, + ) for key in [ "validationRollup", "giteaAttestation",