Files
awoooi/scripts/security/high-value-config-control-coverage.py
Your Name bb459d59f9
Some checks failed
Code Review / ai-code-review (push) Successful in 15s
CD Pipeline / tests (push) Successful in 1m43s
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled
feat(iwooos): 新增 CD runner secret 事故回讀 gate
2026-06-16 11:42:38 +08:00

514 lines
33 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
IwoooS 高價值配置控管覆蓋矩陣。
本工具讀取 high-value-config-change-gate.py 的 CATEGORIES 定義,產生一份
全域配置控管覆蓋 snapshot。它只整理 source-of-truth、owner gate、
evidence refs、缺口與 0/false 邊界,不讀 live host、不連外、不執行
Nginx / DNS / K8s / workflow / agent runtime 動作。
"""
from __future__ import annotations
import argparse
import json
import runpy
import subprocess
import sys
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Any
TAIPEI = timezone(timedelta(hours=8))
CONTROL_STATUS_BY_CATEGORY = {
"nginx_public_gateway": {
"coverage_status": "post_incident_readback_plan_ready_needs_public_gateway_owner_evidence",
"coverage_percent": 92,
"evidence_refs": [
"docs/security/NGINX-CONFIG-DRIFT-DETECTOR.md",
"docs/security/nginx-config-drift-repo.snapshot.json",
"docs/security/DOMAIN-TLS-CERTBOT-INVENTORY.md",
"docs/security/PUBLIC-GATEWAY-PREFLIGHT-INVENTORY.md",
"docs/security/public-gateway-preflight-inventory.snapshot.json",
"docs/security/PUBLIC-GATEWAY-OWNER-RESPONSE-ACCEPTANCE.md",
"docs/security/public-gateway-owner-response-acceptance.snapshot.json",
"docs/security/PUBLIC-GATEWAY-RENDERED-DIFF-ACCEPTANCE.md",
"docs/security/public-gateway-rendered-diff-acceptance.snapshot.json",
"docs/security/PUBLIC-GATEWAY-POST-INCIDENT-READBACK-PLAN.md",
"docs/security/public-gateway-post-incident-readback-plan.snapshot.json",
"docs/schemas/public_gateway_preflight_inventory_v1.schema.json",
],
"current_gap": "已固定 owner response acceptance、手動 / 緊急 gateway 變更回補欄位、rendered diff evidence acceptance 與 post-incident readback plan 只讀帳本actor、時間窗、改前改後 route、source-to-live diff、nginx -t readback、reload / no-reload、route smoke、TLS / ACME、WebSocket、upstream、AI provider、monitoring、跨專案同步、防再發與 no-false-green 已納入owner response、live conf、rendered diff evidence、nginx -t evidence、route smoke evidence、事故回讀包、maintenance window 與 rollback owner 仍全部為 0。",
"next_owner_action": "補 Public Gateway / Nginx 事故回讀包owner 回覆、owner-provided live conf、source-to-live rendered diff ref、nginx -t evidence ref、route smoke evidence ref、change intent / break-glass、actor role / team、變更時間窗、改前改後 route state、upstream / WebSocket / TLS / ACME 影響、AI provider / monitoring 影響、跨專案同步、route health impact、rollback validation、post-change monitoring、recurrence guard、maintenance window 與 rollback owner。",
},
"dns_tls_certbot": {
"coverage_status": "owner_response_acceptance_ledger_ready_needs_certificate_owner_evidence",
"coverage_percent": 78,
"evidence_refs": [
"docs/security/DOMAIN-TLS-CERTBOT-INVENTORY.md",
"docs/security/domain-tls-certbot-inventory.snapshot.json",
"docs/security/DOMAIN-TLS-CERTBOT-OWNER-CONFIRMATION-REQUEST.md",
"docs/security/domain-tls-certbot-owner-confirmation-request.snapshot.json",
"docs/security/DOMAIN-TLS-CERTBOT-OWNER-RESPONSE-ACCEPTANCE.md",
"docs/security/domain-tls-certbot-owner-response-acceptance.snapshot.json",
],
"current_gap": "已固定 4 份 DNS / TLS / certbot owner response acceptance candidate仍缺 owner response、certificate coverage metadata ref、expiry metadata ref、renewal owner、ACME route owner、maintenance window、rollback owner 與 validation plan。",
"next_owner_action": "補 SAN / wildcard / 共用憑證覆蓋依據 ref、到期 metadata ref、renewal owner、ACME route owner、維護窗口、rollback owner 與 validation plan。",
},
"k8s_production_gitops": {
"coverage_status": "post_incident_readback_plan_ready_needs_gitops_owner_evidence",
"coverage_percent": 66,
"evidence_refs": [
"docs/security/HIGH-VALUE-CONFIG-CHANGE-GATE.md",
"docs/security/K8S-ARGOCD-MANIFEST-INVENTORY.md",
"docs/security/k8s-argocd-manifest-inventory.snapshot.json",
"docs/security/K8S-ARGOCD-OWNER-RESPONSE-ACCEPTANCE.md",
"docs/security/k8s-argocd-owner-response-acceptance.snapshot.json",
"docs/security/K8S-ARGOCD-CHANGE-EVIDENCE-ACCEPTANCE.md",
"docs/security/k8s-argocd-change-evidence-acceptance.snapshot.json",
"docs/security/K8S-ARGOCD-POST-INCIDENT-READBACK-PLAN.md",
"docs/security/k8s-argocd-post-incident-readback-plan.snapshot.json",
"k8s/awoooi-prod",
"k8s/argocd",
],
"current_gap": "已固定 owner response acceptance、GitOps 變更證據驗收與事故後回讀計畫只讀帳本ArgoCD sync / health / degraded、Pending workload、image pull / scheduling、rollout before-after、event / metrics / alert、drift scanner、CronJob schedule、NetworkPolicy / RBAC、route / AI provider / monitoring 影響、防再發與 no-false-green 已納入;仍缺 owner response、事故回讀包、rendered manifest diff、ArgoCD revision、maintenance window、rollback revision、post-check owner 與 no-secret-value evidence。",
"next_owner_action": "補 GitOps / K8s 事故回讀包incident / change ref、actor role / team、ArgoCD sync / health / revision、degraded / Pending / image pull / scheduling 摘要、rollout before-after、event summary、metrics / alert、drift scanner、CronJob schedule、NetworkPolicy / Service / RBAC 影響、public/admin route、AI provider、monitoring、operator notification、cross-project sync、recovery 或 still-degraded ref、recurrence guard、maintenance window、rollback revision、rollback owner、post-check owner 與 no-secret-value evidence。",
},
"secret_metadata": {
"coverage_status": "post_incident_readback_plan_ready_needs_secret_injection_owner_evidence",
"coverage_percent": 70,
"evidence_refs": [
"docs/security/SOURCE-CONTROL-WORKFLOW-SECRET-NAME-INVENTORY.md",
"docs/security/source-control-workflow-secret-name-inventory.snapshot.json",
"docs/security/SOURCE-CONTROL-WORKFLOW-SECRET-NAME-OWNER-RESPONSE.md",
"docs/security/source-control-workflow-secret-name-owner-response.snapshot.json",
"docs/security/CD-RUNNER-SECRET-INJECTION-CHANGE-EVIDENCE-ACCEPTANCE.md",
"docs/security/cd-runner-secret-injection-change-evidence-acceptance.snapshot.json",
"docs/security/CD-RUNNER-SECRET-INJECTION-POST-INCIDENT-READBACK-PLAN.md",
"docs/security/cd-runner-secret-injection-post-incident-readback-plan.snapshot.json",
"docs/security/SECRETS_REFERENCE.md",
],
"current_gap": "已固定 secret name / injection owner 變更證據驗收帳本並新增事故後回讀計畫secret name parity state、secret injection route、step-env secret guard、log redaction、deploy marker / Gitea run readback、rollback、post-check、防再發與 no-false-green 已納入secret value、hash、partial token、runner token、secret store read、secret rotation、repo secret change 與 injection path change 仍全部為 0。",
"next_owner_action": "補 secret name parity state ref、secret injection route state ref、step-env secret guard result、log redaction readback、deploy marker / Gitea run readback、notification receipt、rollback owner、post-check evidence、recurrence guard 與 no-secret-value evidence。",
},
"gitea_workflow_runner_source_control": {
"coverage_status": "post_incident_readback_plan_ready_needs_workflow_runner_owner_evidence",
"coverage_percent": 74,
"evidence_refs": [
"docs/security/SOURCE-CONTROL-WORKFLOW-SECRET-NAME-INVENTORY.md",
"docs/security/SOURCE-CONTROL-WORKFLOW-SECRET-NAME-OWNER-RESPONSE.md",
"docs/security/CD-RUNNER-SECRET-INJECTION-CHANGE-EVIDENCE-ACCEPTANCE.md",
"docs/security/cd-runner-secret-injection-change-evidence-acceptance.snapshot.json",
"docs/security/CD-RUNNER-SECRET-INJECTION-POST-INCIDENT-READBACK-PLAN.md",
"docs/security/cd-runner-secret-injection-post-incident-readback-plan.snapshot.json",
"docs/security/SOURCE-CONTROL-PRIMARY-READINESS-GATE.md",
],
"current_gap": "已固定 CD / runner / secret injection 變更證據驗收帳本並新增事故後回讀計畫workflow diff state、runner attestation、executor / host、workspace cleanup、permission scope、webhook / notification、deploy key / branch protection / CODEOWNERS、deploy marker / Gitea run、rollback、post-change monitoring、防再發與 no-false-green 已納入workflow 修改、dispatch、runner 啟用 / 重啟、GitHub hosted runner、webhook / deploy key / branch protection / CODEOWNERS 修改仍全部為 0。",
"next_owner_action": "補 workflow diff state ref、runner owner attestation、executor / host readback、workspace cleanup、permission scope、Gitea run readback、deploy marker readback、webhook / notification receipt、maintenance window、rollback owner、post-change monitoring 與 recurrence guard evidence。",
},
"public_admin_api_runtime_config": {
"coverage_status": "frontend_sensitive_surface_guard_ready_needs_runtime_config_owner_evidence",
"coverage_percent": 66,
"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",
"docs/security/public-frontend-sensitive-surface-guard.snapshot.json",
],
"current_gap": "已固定 Public / Admin / API runtime config 變更證據驗收只讀帳本,並新增前台 source / messages 敏感資訊防洩漏 guardaffected route、admin/auth boundary、API readback、CORS diff、frontend env diff、i18n redaction、webhook owner、desktop/mobile production smoke、bundle 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 production smoke、bundle sensitive scan、rollback owner 與 post-check evidence。",
},
"backup_restore_credential": {
"coverage_status": "restore_recovery_backfill_ready_needs_owner_live_evidence",
"coverage_percent": 64,
"evidence_refs": [
"docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md",
"docs/security/BACKUP-RESTORE-ESCROW-INVENTORY.md",
"docs/security/backup-restore-escrow-inventory.snapshot.json",
"docs/security/BACKUP-RESTORE-OWNER-RESPONSE-ACCEPTANCE.md",
"docs/security/backup-restore-owner-response-acceptance.snapshot.json",
"docs/schemas/backup_restore_escrow_inventory_v1.schema.json",
],
"current_gap": "已固定 owner response acceptance、restore recovery backfill、remote delete guard、retention runway、credential recovery metadata-only 與 backup health no-false-green 只讀帳本restore drill、offsite sync、credential escrow、retention change、live evidence 與 owner response 仍全部為 0。",
"next_owner_action": "補 restore drill approval package、freshness SLO、隔離 restore target、依賴圖、資料分級、offsite remote delete guard、credential recovery non-secret proof、retention runway、observer / stop condition、rollback owner、validation plan 與 no-secret-value evidence。",
},
"agent_bounty_protocol_runtime": {
"coverage_status": "owner_request_draft_ready_needs_runtime_owner",
"coverage_percent": 68,
"evidence_refs": [
"docs/security/AGENT-BOUNTY-IWOOOS-ONBOARDING-HANDOFF.md",
"docs/security/agent-bounty-iwooos-onboarding-handoff.snapshot.json",
"docs/security/AGENT-BOUNTY-OWNER-REQUEST-DRAFT.md",
"docs/security/agent-bounty-owner-request-draft.snapshot.json",
"docs/security/GITHUB-TARGET-OWNER-DECISION-RESPONSE.md",
],
"current_gap": "owner request draft 已固定 11 份草稿;尚未收到 runtime / MCP / A2A / treasury / payout owner responseruntime gate 必須維持 0。",
"next_owner_action": "補 repo owner、external agent owner、treasury owner、runtime gate owner、maintenance window、rollback owner 與 validation plan。",
},
"monitoring_alerting_observability": {
"coverage_status": "post_incident_readback_plan_ready_needs_monitoring_owner_evidence",
"coverage_percent": 70,
"evidence_refs": [
"docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md",
"docs/security/MONITORING-ALERTING-OBSERVABILITY-INVENTORY.md",
"docs/security/monitoring-alerting-observability-inventory.snapshot.json",
"docs/security/MONITORING-OWNER-REQUEST-DRAFT.md",
"docs/security/monitoring-owner-request-draft.snapshot.json",
"docs/security/MONITORING-OWNER-RESPONSE-ACCEPTANCE.md",
"docs/security/monitoring-owner-response-acceptance.snapshot.json",
"docs/security/MONITORING-POST-INCIDENT-READBACK-PLAN.md",
"docs/security/monitoring-post-incident-readback-plan.snapshot.json",
"docs/schemas/monitoring_alerting_observability_inventory_v1.schema.json",
],
"current_gap": "已固定 60 份 monitoring / alerting / observability owner response acceptance candidate並新增事故後回讀計畫actor、時間窗、改前改後 alert state、rule / datasource / scrape、receiver route、reload / no-reload、receiver receipt、stale / silence、dashboard / trace / log freshness、notification delivery、alert chain health、cross-project sync、rollback、post-change monitoring、防再發與 no-false-green 已納入只讀帳本;仍缺 owner response、事故回讀包、live config hash、rule diff、receiver diff、receipt proof、stale / silence review、post-reload readback、noise budget owner 與 maintenance window。",
"next_owner_action": "補 Prometheus / Alertmanager / Grafana / SigNoz / Sentry / Langfuse / Telegram 事故回讀包owner、actor role / team、change / outage time window、before / after alert state、rule / datasource / scrape state、receiver route state、reload / no-reload、receiver receipt、stale alert review、silence / dedup / inhibit review、dashboard / trace / log freshness、notification metadata、alert chain health、cross-project sync、rollback owner、post-change monitoring、recurrence guard、noise budget 與 no-secret-value evidence。",
},
"docker_compose_systemd_host_config": {
"coverage_status": "post_incident_readback_plan_ready_needs_host_service_owner_evidence",
"coverage_percent": 64,
"evidence_refs": [
"docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md",
"docs/security/HOST-SERVICE-CONFIG-INVENTORY.md",
"docs/security/host-service-config-inventory.snapshot.json",
"docs/security/HOST-SERVICE-OWNER-REQUEST-DRAFT.md",
"docs/security/host-service-owner-request-draft.snapshot.json",
"docs/security/HOST-SERVICE-OWNER-RESPONSE-ACCEPTANCE.md",
"docs/security/host-service-owner-response-acceptance.snapshot.json",
"docs/security/HOST-SERVICE-CHANGE-EVIDENCE-ACCEPTANCE.md",
"docs/security/host-service-change-evidence-acceptance.snapshot.json",
"docs/security/HOST-SERVICE-POST-INCIDENT-READBACK-PLAN.md",
"docs/security/host-service-post-incident-readback-plan.snapshot.json",
"docs/security/DEV-HOSTS-112-111-168-OBSERVE-ONLY-MAPPING.md",
],
"current_gap": "已固定 9 份 Docker / systemd / host service owner response acceptance candidate、change evidence acceptance 與 post-incident readback plan重啟 actor、boot / recovery window、before / after state、Docker daemon、compose / systemd state、failed unit、port binding、dependency、public/admin route recovery、AI provider health、monitoring alert、operator notice、cross-project sync、restoration、recurrence guard 與 no-false-green 皆已納入只讀帳本;仍缺 owner response、live hash、事故回讀包、maintenance / restart window、rollback owner、post-check plan、disable switch 與 no-secret-value evidence。",
"next_owner_action": "補 host service 事故回讀包owner-provided live hash / disposition、change / incident ref、actor role / team、boot time、restart / recovery window、before / after state、Docker daemon state、compose / systemd state、failed unit review、port binding、服務依賴圖、public/admin route recovery、AI provider health、monitoring alert、operator notification、cross-project sync、restoration evidence、recurrence guard、rollback owner、post-check plan 與 no-secret-value evidence。",
},
"ssh_firewall_network_access": {
"coverage_status": "post_incident_readback_plan_ready_needs_network_owner_evidence",
"coverage_percent": 64,
"evidence_refs": [
"docs/HARD_RULES.md",
"docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md",
"docs/security/SSH-NETWORK-ACCESS-INVENTORY.md",
"docs/security/ssh-network-access-inventory.snapshot.json",
"docs/security/SSH-NETWORK-OWNER-REQUEST-DRAFT.md",
"docs/security/ssh-network-owner-request-draft.snapshot.json",
"docs/security/SSH-NETWORK-OWNER-RESPONSE-ACCEPTANCE.md",
"docs/security/ssh-network-owner-response-acceptance.snapshot.json",
"docs/security/PORT-FIREWALL-CHANGE-EVIDENCE-ACCEPTANCE.md",
"docs/security/port-firewall-change-evidence-acceptance.snapshot.json",
"docs/security/SSH-NETWORK-POST-INCIDENT-READBACK-PLAN.md",
"docs/security/ssh-network-post-incident-readback-plan.snapshot.json",
],
"current_gap": "owner response acceptance、端口 / 防火牆事故型變更證據驗收與 post-incident readback plan 都已固定;仍缺 owner-provided change / incident evidence、actor、before / after state、service / AI provider / monitoring impact、operator notification、cross-project sync、restoration evidence、recurrence guard、maintenance window、rollback owner 與 post-check evidence。",
"next_owner_action": "補端口 / 防火牆事故回讀包change / incident ref、actor role / team、affected scope、before / after state、service dependency、public route impact、AI provider impact、monitoring alert impact、operator notification、cross-project sync、restoration evidence、recurrence guard、rollback owner 與 post-check 指標。",
},
"ai_provider_model_routing": {
"coverage_status": "owner_response_acceptance_ready_needs_provider_owner_evidence",
"coverage_percent": 64,
"evidence_refs": [
"docs/HARD_RULES.md",
"docs/ai",
"docs/security/AI-PROVIDER-OWNER-RESPONSE-ACCEPTANCE.md",
"docs/security/ai-provider-owner-response-acceptance.snapshot.json",
],
"current_gap": "已固定 AI provider / model routing owner response acceptance 帳本provider owner、fallback order、dry-run、benchmark、成本、privacy、data classification、prompt redaction、rollback 與 post-check evidence 仍全部為 0目前不切 production。",
"next_owner_action": "補 provider owner、fallback order、dry-run result、benchmark result、cost review、privacy review、data classification、prompt redaction、quota budget、quality gate、rollback owner 與 post-check evidence。",
},
"product_surface_runtime_routes": {
"coverage_status": "scope_inventory_ready",
"coverage_percent": 72,
"evidence_refs": [
"docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md",
"docs/security/VIBEWORK-IWOOOS-ONBOARDING-HANDOFF.md",
"docs/security/AGENT-BOUNTY-IWOOOS-ONBOARDING-HANDOFF.md",
"docs/security/AGENT-BOUNTY-OWNER-REQUEST-DRAFT.md",
],
"current_gap": "跨產品 owner response 尚未 accepted產品 route / admin / webhook 仍需逐產品補證。",
"next_owner_action": "補 AWOOOI、AwoooP、IwoooS、VibeWork、agent-bounty-protocol 與公開網站 owner response。",
},
"security_evidence_tooling": {
"coverage_status": "guard_ready",
"coverage_percent": 86,
"evidence_refs": [
"scripts/security/security-mirror-progress-guard.py",
"scripts/security/high-value-config-change-gate.py",
"scripts/security/high-value-config-owner-packet.py",
"docs/security/high-value-config-change-gate.snapshot.json",
],
"current_gap": "guard 已可重跑,但尚未接 blocking CI本階段刻意維持低摩擦。",
"next_owner_action": "維持 guard / doc secret sanity若要 CI blocking 需另開人工批准與 rollout plan。",
},
}
FALSE_BOUNDARIES = {
"runtime_execution_authorized": False,
"host_write_authorized": False,
"host_live_conf_read_authorized": False,
"nginx_test_authorized": False,
"public_gateway_reload_authorized": False,
"public_route_change_authorized": False,
"admin_route_change_authorized": False,
"websocket_route_change_authorized": False,
"acme_challenge_change_authorized": False,
"route_smoke_authorized": False,
"rollback_executed": False,
"nginx_reload_authorized": False,
"dns_tls_change_authorized": False,
"certbot_renew_authorized": False,
"argocd_api_read_authorized": False,
"argocd_sync_authorized": False,
"kubectl_action_authorized": False,
"helm_upgrade_authorized": False,
"network_policy_apply_authorized": False,
"nodeport_change_authorized": False,
"rbac_change_authorized": False,
"backup_run_authorized": False,
"restore_run_authorized": False,
"restore_drill_authorized": False,
"offsite_sync_authorized": False,
"offsite_remote_delete_authorized": False,
"credential_escrow_marker_write_authorized": False,
"retention_change_authorized": False,
"restic_prune_authorized": False,
"rclone_config_authorized": False,
"velero_restore_authorized": False,
"workflow_modification_authorized": False,
"runner_change_authorized": False,
"refs_sync_authorized": False,
"force_push_authorized": False,
"prometheus_reload_authorized": False,
"alertmanager_reload_authorized": False,
"grafana_dashboard_apply_authorized": False,
"signoz_rule_apply_authorized": False,
"sentry_deploy_authorized": False,
"langfuse_config_change_authorized": False,
"otel_collector_reload_authorized": False,
"receiver_route_change_authorized": False,
"silence_policy_change_authorized": False,
"telegram_send_authorized": False,
"notification_route_change_authorized": False,
"webhook_receiver_change_authorized": False,
"remote_write_change_authorized": False,
"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,
"payout_or_withdrawal_authorized": False,
"action_buttons_allowed": False,
}
def git_short_sha(root: Path) -> str:
try:
result = subprocess.run(
["git", "rev-parse", "--short", "HEAD"],
cwd=root,
check=True,
capture_output=True,
text=True,
)
return result.stdout.strip()
except Exception:
return "unknown"
def load_gate_categories(root: Path) -> list[Any]:
gate_path = root / "scripts" / "security" / "high-value-config-change-gate.py"
namespace = runpy.run_path(str(gate_path))
return list(namespace["CATEGORIES"])
def category_to_dict(category: Any) -> dict[str, Any]:
status = CONTROL_STATUS_BY_CATEGORY[category.category_id]
return {
"category_id": category.category_id,
"label": category.label,
"priority": category.priority,
"control_tier": category.control_tier,
"required_gate": category.required_gate,
"coverage_status": status["coverage_status"],
"coverage_percent": status["coverage_percent"],
"patterns": list(category.patterns),
"required_validation": list(category.required_validation),
"evidence_refs": status["evidence_refs"],
"current_gap": status["current_gap"],
"next_owner_action": status["next_owner_action"],
"owner_response_required": True,
"owner_response_received": False,
"owner_response_accepted": False,
"runtime_gate_open": False,
"action_buttons_allowed": False,
}
def count_by(items: list[dict[str, Any]], key: str, value: Any) -> int:
return sum(1 for item in items if item[key] == value)
def build_report(root: Path, generated_at: str | None) -> dict[str, Any]:
report_time = generated_at or datetime.now(TAIPEI).isoformat(timespec="seconds")
categories = [category_to_dict(category) for category in load_gate_categories(root)]
c0_categories = [item for item in categories if item["control_tier"] == "C0"]
c1_categories = [item for item in categories if item["control_tier"] == "C1"]
needs_live_evidence = [
item
for item in categories
if item["coverage_status"]
in {
"gate_defined_needs_runtime_evidence",
"policy_defined_needs_restore_drill_owner",
"repo_only_inventory_ready_needs_restore_drill_owner",
"repo_only_inventory_ready_needs_live_route_evidence",
"repo_only_preflight_contract_ready_needs_owner_live_diff",
"owner_response_acceptance_ledger_ready_needs_owner_live_diff",
"rendered_diff_acceptance_ledger_ready_needs_owner_live_diff",
"emergency_change_backfill_ready_needs_owner_live_diff",
"post_incident_readback_plan_ready_needs_public_gateway_owner_evidence",
"owner_response_acceptance_ledger_ready_needs_runtime_evidence",
"change_evidence_acceptance_ready_needs_gitops_owner_evidence",
"post_incident_readback_plan_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",
"post_incident_readback_plan_ready_needs_secret_injection_owner_evidence",
"post_incident_readback_plan_ready_needs_workflow_runner_owner_evidence",
"change_evidence_acceptance_ready_needs_runtime_config_owner_evidence",
"frontend_sensitive_surface_guard_ready_needs_runtime_config_owner_evidence",
"owner_response_acceptance_ledger_ready_needs_restore_drill_owner",
"restore_recovery_backfill_ready_needs_owner_live_evidence",
"owner_response_acceptance_ledger_ready_needs_network_owner",
"owner_response_acceptance_ledger_ready_needs_live_route_evidence",
"alert_chain_no_false_green_backfill_ready_needs_live_evidence_receipt",
"post_incident_readback_plan_ready_needs_monitoring_owner_evidence",
"change_evidence_acceptance_ready_needs_network_owner_evidence",
"incident_change_evidence_acceptance_ready_needs_network_owner_evidence",
"post_incident_readback_plan_ready_needs_network_owner_evidence",
"policy_ready_needs_drift_evidence",
"inventory_needed",
"repo_only_inventory_ready_needs_live_owner_evidence",
"incident_recovery_backfill_ready_needs_live_owner_evidence",
"change_evidence_acceptance_ready_needs_host_service_owner_evidence",
"policy_ready_needs_network_matrix",
"policy_ready_needs_dry_run_pack",
"owner_response_acceptance_ready_needs_provider_owner_evidence",
}
]
average_coverage = round(sum(item["coverage_percent"] for item in categories) / len(categories))
lowest_categories = sorted(categories, key=lambda item: item["coverage_percent"])[:4]
return {
"schema_version": "high_value_config_control_coverage_v1",
"generated_at": report_time,
"git_commit": git_short_sha(root),
"source_category_definition": "scripts/security/high-value-config-change-gate.py",
"status": "coverage_matrix_ready",
"summary": {
"category_count": len(categories),
"c0_category_count": len(c0_categories),
"c1_category_count": len(c1_categories),
"c2_category_count": count_by(categories, "control_tier", "C2"),
"c3_category_count": count_by(categories, "control_tier", "C3"),
"registered_control_count": len(categories),
"owner_response_required_count": len(categories),
"owner_response_received_count": 0,
"owner_response_accepted_count": 0,
"runtime_gate_count": 0,
"action_button_count": 0,
"average_coverage_percent": average_coverage,
"needs_live_evidence_count": len(needs_live_evidence),
"lowest_coverage_category_count": len(lowest_categories),
},
"execution_boundaries": FALSE_BOUNDARIES,
"coverage_categories": categories,
"lowest_coverage_categories": [
{
"category_id": item["category_id"],
"label": item["label"],
"coverage_percent": item["coverage_percent"],
"current_gap": item["current_gap"],
"next_owner_action": item["next_owner_action"],
}
for item in lowest_categories
],
"next_collection_order": [
"nginx_public_gateway",
"dns_tls_certbot",
"k8s_production_gitops",
"secret_metadata",
"gitea_workflow_runner_source_control",
"public_admin_api_runtime_config",
"agent_bounty_protocol_runtime",
"docker_compose_systemd_host_config",
"ssh_firewall_network_access",
"ai_provider_model_routing",
"monitoring_alerting_observability",
"backup_restore_credential",
],
"operator_interpretation": [
"這是全域配置控管覆蓋矩陣,不是單次 git diff 變更分類。",
"所有 category 都已有高價值配置 Gate 註冊與 owner response 欄位,但 owner response received / accepted 仍為 0。",
"C0 / C1 coverage percent 只代表只讀框架成熟度,不代表 runtime 可執行。",
"缺 live evidence 的項目只能收 owner-provided redacted evidence不得主動 SSH、reload、scan 或讀 secret value。",
],
}
def main() -> int:
parser = argparse.ArgumentParser(description="IwoooS 高價值配置控管覆蓋矩陣")
parser.add_argument("--root", default=".", help="repo root")
parser.add_argument("--output", help="寫出 JSON 報告")
parser.add_argument("--generated-at", help="固定報告時間,供 committed snapshot 使用")
args = parser.parse_args()
root = Path(args.root).resolve()
report = build_report(root, args.generated_at)
payload = json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True)
if args.output:
output = Path(args.output)
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(payload + "\n", encoding="utf-8")
else:
print(payload)
summary = report["summary"]
print(
"HIGH_VALUE_CONFIG_CONTROL_COVERAGE_OK "
f"categories={summary['category_count']} "
f"c0={summary['c0_category_count']} "
f"avg={summary['average_coverage_percent']} "
f"runtime_gate={summary['runtime_gate_count']}",
file=sys.stderr,
)
return 0
if __name__ == "__main__":
sys.exit(main())