Files
awoooi/scripts/security/high-value-config-control-coverage.py
Your Name 8a424f0c56
All checks were successful
CD Pipeline / tests (push) Successful in 1m26s
Code Review / ai-code-review (push) Successful in 23s
CD Pipeline / build-and-deploy (push) Successful in 4m52s
CD Pipeline / post-deploy-checks (push) Successful in 1m59s
feat(security): 新增 monitoring alerting 只讀清冊
2026-06-12 00:45:08 +08:00

387 lines
18 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": "repo_only_drift_detector_ready",
"coverage_percent": 78,
"evidence_refs": [
"docs/security/NGINX-CONFIG-DRIFT-DETECTOR.md",
"docs/security/nginx-config-drift-repo.snapshot.json",
"docs/security/DOMAIN-TLS-CERTBOT-INVENTORY.md",
],
"current_gap": "缺 owner-provided live conf 匯出與 approved maintenance window不得 SSH 擷取或 reload。",
"next_owner_action": "提供脫敏 live conf 匯出檔或確認維護窗口、rollback owner 與 route smoke plan。",
},
"dns_tls_certbot": {
"coverage_status": "repo_only_inventory_ready",
"coverage_percent": 74,
"evidence_refs": [
"docs/security/DOMAIN-TLS-CERTBOT-INVENTORY.md",
"docs/security/domain-tls-certbot-inventory.snapshot.json",
],
"current_gap": "4 個 certificate path 關係需 owner 確認live DNS / TLS probe 未執行。",
"next_owner_action": "確認 SAN / 共用憑證關係、renewal owner、ACME smoke 與 rollback owner。",
},
"k8s_production_gitops": {
"coverage_status": "gate_defined_needs_runtime_evidence",
"coverage_percent": 58,
"evidence_refs": [
"docs/security/HIGH-VALUE-CONFIG-CHANGE-GATE.md",
"k8s/awoooi-prod",
"k8s/argocd",
],
"current_gap": "尚未把 ArgoCD health / sync readback 與 rollback revision 收成 owner packet。",
"next_owner_action": "補 GitOps owner、rollback revision、health readback 與 post-deploy validation plan。",
},
"secret_metadata": {
"coverage_status": "metadata_policy_ready",
"coverage_percent": 66,
"evidence_refs": [
"docs/security/SOURCE-CONTROL-WORKFLOW-SECRET-NAME-INVENTORY.md",
"docs/security/source-control-workflow-secret-name-inventory.snapshot.json",
"docs/security/SECRETS_REFERENCE.md",
],
"current_gap": "只允許 secret name / metadata仍缺 owner response 與 parity acceptance。",
"next_owner_action": "只回覆 secret name owner、rotation owner、injection owner 與 redacted evidence refs。",
},
"gitea_workflow_runner_source_control": {
"coverage_status": "metadata_inventory_ready",
"coverage_percent": 70,
"evidence_refs": [
"docs/security/SOURCE-CONTROL-WORKFLOW-SECRET-NAME-INVENTORY.md",
"docs/security/SOURCE-CONTROL-WORKFLOW-SECRET-NAME-OWNER-RESPONSE.md",
"docs/security/SOURCE-CONTROL-PRIMARY-READINESS-GATE.md",
],
"current_gap": "workflow / runner / deploy key / webhook / branch protection 仍待 owner response不得改 workflow。",
"next_owner_action": "補 runner label、webhook、deploy key、branch protection 與 workflow parity owner metadata。",
},
"public_admin_api_runtime_config": {
"coverage_status": "policy_ready_needs_change_scoped_smoke",
"coverage_percent": 62,
"evidence_refs": [
"docs/HARD_RULES.md",
"docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md",
],
"current_gap": "每次產品 route / admin / API / frontend config 變更仍需逐次 smoke 與 owner gate。",
"next_owner_action": "補 affected route、admin/auth boundary、CORS/public URL 與 desktop/mobile smoke plan。",
},
"backup_restore_credential": {
"coverage_status": "repo_only_inventory_ready_needs_restore_drill_owner",
"coverage_percent": 58,
"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/schemas/backup_restore_escrow_inventory_v1.schema.json",
],
"current_gap": "repo-only 清冊已納入 38 個 backup / restore / escrow / retention surfacerestore drill、offsite sync、credential escrow、retention change、live evidence 與 owner response 仍全部為 0。",
"next_owner_action": "補 restore drill approval package、offsite owner、escrow owner、retention owner、rollback owner 與 no-secret-value evidence。",
},
"agent_bounty_protocol_runtime": {
"coverage_status": "onboarding_handoff_ready_needs_runtime_owner",
"coverage_percent": 68,
"evidence_refs": [
"docs/security/AGENT-BOUNTY-IWOOOS-ONBOARDING-HANDOFF.md",
"docs/security/agent-bounty-iwooos-onboarding-handoff.snapshot.json",
"docs/security/GITHUB-TARGET-OWNER-DECISION-RESPONSE.md",
],
"current_gap": "尚未收到 runtime / MCP / A2A / treasury / payout owner responseruntime gate 必須維持 0。",
"next_owner_action": "補 repo owner、external agent owner、treasury owner、runtime gate owner 與 validation plan。",
},
"monitoring_alerting_observability": {
"coverage_status": "repo_only_inventory_ready_needs_live_route_evidence",
"coverage_percent": 62,
"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/schemas/monitoring_alerting_observability_inventory_v1.schema.json",
],
"current_gap": "repo-only 清冊已納入 60 個 monitoring / alerting / observability surface仍缺 live config hash、rule diff、receiver diff、reload owner、route smoke、receipt proof 與 owner response。",
"next_owner_action": "補 Prometheus / Alertmanager / Grafana / SigNoz / Sentry / Langfuse / Telegram owner、live drift evidence、reload window、receiver owner、rollback owner 與 no-secret-value evidence。",
},
"docker_compose_systemd_host_config": {
"coverage_status": "repo_only_inventory_ready_needs_live_owner_evidence",
"coverage_percent": 50,
"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/DEV-HOSTS-112-111-168-OBSERVE-ONLY-MAPPING.md",
],
"current_gap": "repo-only 清冊已納入 9 個 surface仍缺 110 / 188 live hash、restart window、rollback owner 與 post-check 指標。",
"next_owner_action": "補 owner-provided live hash / disposition、compose / systemd owner、restart window、rollback owner 與 post-check 指標。",
},
"ssh_firewall_network_access": {
"coverage_status": "repo_only_inventory_ready_needs_live_owner_evidence",
"coverage_percent": 54,
"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",
],
"current_gap": "repo-only 清冊已納入 16 個 SSH / network access surface仍缺 live firewall / sudoers / known_hosts / NetworkPolicy / NodePort / WireGuard evidence、network owner 與 rollback owner。",
"next_owner_action": "補 owner-provided live hash / disposition、host key pinning、firewall owner、NetworkPolicy / NodePort owner、WireGuard owner、maintenance window、rollback owner 與 post-check 指標。",
},
"ai_provider_model_routing": {
"coverage_status": "policy_ready_needs_dry_run_pack",
"coverage_percent": 60,
"evidence_refs": [
"docs/HARD_RULES.md",
"docs/ai",
],
"current_gap": "模型 / provider / Ollama proxy 切換需 dry-run、benchmark、成本與 privacy review目前不切 production。",
"next_owner_action": "補 provider owner、fallback order、cost review、privacy review、benchmark 與 rollback owner。",
},
"product_surface_runtime_routes": {
"coverage_status": "scope_inventory_ready",
"coverage_percent": 72,
"evidence_refs": [
"docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md",
"docs/security/VIBEWORK-IWOOOS-ONBOARDING-HANDOFF.md",
"docs/security/AGENT-BOUNTY-IWOOOS-ONBOARDING-HANDOFF.md",
],
"current_gap": "跨產品 owner response 尚未 accepted產品 route / admin / webhook 仍需逐產品補證。",
"next_owner_action": "補 AWOOOI、AwoooP、IwoooS、VibeWork、agent-bounty-protocol 與公開網站 owner response。",
},
"security_evidence_tooling": {
"coverage_status": "guard_ready",
"coverage_percent": 86,
"evidence_refs": [
"scripts/security/security-mirror-progress-guard.py",
"scripts/security/high-value-config-change-gate.py",
"scripts/security/high-value-config-owner-packet.py",
"docs/security/high-value-config-change-gate.snapshot.json",
],
"current_gap": "guard 已可重跑,但尚未接 blocking CI本階段刻意維持低摩擦。",
"next_owner_action": "維持 guard / doc secret sanity若要 CI blocking 需另開人工批准與 rollout plan。",
},
}
FALSE_BOUNDARIES = {
"runtime_execution_authorized": False,
"host_write_authorized": False,
"nginx_reload_authorized": False,
"dns_tls_change_authorized": False,
"certbot_renew_authorized": False,
"argocd_sync_authorized": False,
"kubectl_action_authorized": False,
"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,
"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",
"policy_ready_needs_drift_evidence",
"inventory_needed",
"repo_only_inventory_ready_needs_live_owner_evidence",
"policy_ready_needs_network_matrix",
"policy_ready_needs_dry_run_pack",
}
]
average_coverage = round(sum(item["coverage_percent"] for item in categories) / len(categories))
lowest_categories = sorted(categories, key=lambda item: item["coverage_percent"])[:4]
return {
"schema_version": "high_value_config_control_coverage_v1",
"generated_at": report_time,
"git_commit": git_short_sha(root),
"source_category_definition": "scripts/security/high-value-config-change-gate.py",
"status": "coverage_matrix_ready",
"summary": {
"category_count": len(categories),
"c0_category_count": len(c0_categories),
"c1_category_count": len(c1_categories),
"c2_category_count": count_by(categories, "control_tier", "C2"),
"c3_category_count": count_by(categories, "control_tier", "C3"),
"registered_control_count": len(categories),
"owner_response_required_count": len(categories),
"owner_response_received_count": 0,
"owner_response_accepted_count": 0,
"runtime_gate_count": 0,
"action_button_count": 0,
"average_coverage_percent": average_coverage,
"needs_live_evidence_count": len(needs_live_evidence),
"lowest_coverage_category_count": len(lowest_categories),
},
"execution_boundaries": FALSE_BOUNDARIES,
"coverage_categories": categories,
"lowest_coverage_categories": [
{
"category_id": item["category_id"],
"label": item["label"],
"coverage_percent": item["coverage_percent"],
"current_gap": item["current_gap"],
"next_owner_action": item["next_owner_action"],
}
for item in lowest_categories
],
"next_collection_order": [
"nginx_public_gateway",
"dns_tls_certbot",
"secret_metadata",
"gitea_workflow_runner_source_control",
"agent_bounty_protocol_runtime",
"docker_compose_systemd_host_config",
"monitoring_alerting_observability",
"ssh_firewall_network_access",
"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())