fix(iwooos): expose security runtime gaps truthfully
Some checks failed
CD Pipeline / select-latest-carrier (push) Successful in 58s
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m53s
CD Pipeline / revalidate-deploy-carrier (push) Successful in 38s
CD Pipeline / revalidate-post-deploy-carrier (push) Has been cancelled
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled

This commit is contained in:
Your Name
2026-07-22 12:47:43 +08:00
parent 50c7a7f7f5
commit 2babb1b155
5 changed files with 358 additions and 85 deletions

View File

@@ -33,6 +33,104 @@ _WINDOW_HOURS = 24
_STRICT_RUNTIME_SCHEMA_VERSION = "ai_agent_strict_runtime_completion_v2"
_STRICT_RUNTIME_SOURCE_ID = "autonomous_strict_runtime"
_WORK_ITEM_ORDER = {
"source_health": 0,
"asset_discovery": 10,
"asset_scope": 20,
"asset_ownership": 30,
"automation_coverage": 40,
"telemetry_detection": 50,
"siem_pipeline": 60,
"endpoint_xdr_wazuh": 70,
"incident_response_soar": 80,
"vulnerability_exposure": 90,
"identity_zero_trust": 100,
"data_secrets_privacy": 110,
"backup_dr_resilience": 120,
"siem_soc_threat_intel": 130,
"security_audit": 140,
"ai_security_automation": 150,
"supply_chain_policy": 160,
"certificate_live_verification": 170,
"network_dns_tls": 180,
"appsec_api": 190,
"asset_relationship": 200,
"audit_integrity": 210,
}
_SECURITY_DOMAIN_WORK_ITEMS: tuple[tuple[str, str, str, str, str], ...] = (
(
"endpoint_xdr_wazuh",
"AIA-P0-006-07",
"P0",
"接通 Wazuh / XDR 全資產 runtime 證據",
"把 manager、agent、host、service 與事件 identity 接入資產圖驗證偵測、隔離候選、postcheck 與復原證據。",
),
(
"incident_response_soar",
"AIA-P0-007-02",
"P0",
"封閉事件應變與 SOAR 處置",
"將 open incident 串接 owner、decision、bounded apply、獨立 verifier、rollback 與 durable closure receipt。",
),
(
"vulnerability_exposure",
"AIA-P0-006-08",
"P0",
"接入 CVE 與外部暴露面驗證",
"以 SBOM、映像掃描、DAST 與 public exposure verifier 產生可追溯 finding、修復與複驗。",
),
(
"identity_zero_trust",
"AIA-P0-006-09",
"P0",
"建立 IAM 與 Zero Trust runtime 稽核",
"盤點人員、服務與 Agent identity驗證最小權限、存取審查、失效權限與 break-glass 使用紀錄。",
),
(
"data_secrets_privacy",
"AIA-P0-006-10",
"P0",
"建立資料、Secret 與隱私控制證據",
"驗證 secret rotation、at-rest/in-transit encryption、敏感資料流與資料保留不得讀取 secret value。",
),
(
"telemetry_detection",
"AIA-P0-006-11",
"P0",
"補齊全資產監控與偵測覆蓋",
"將每項資產綁定 telemetry、alert rule、owner、SLO 與可驗證的失效告警 receipt。",
),
(
"backup_dr_resilience",
"AIA-P0-006-12",
"P0",
"完成備份、還原演練與 DR 證據",
"把 backup target、freshness、checksum、restore drill、RPO/RTO 與獨立 verifier 接入資產圖。",
),
(
"siem_soc_threat_intel",
"AIA-P0-007-03",
"P0",
"完成 SIEM / SOC 威脅情報閉環",
"將 threat intel、規則命中、case、處置、驗證與 learning receipt 綁在同一事件與同一 run。",
),
(
"network_dns_tls",
"AIA-P1-006-13",
"P1",
"封閉網路、DNS 與 TLS 控制缺口",
"對 public route、DNS、certificate chain、expiry 與 network policy 建立持續驗證與受控修復。",
),
(
"appsec_api",
"AIA-P1-006-14",
"P1",
"完成 AppSec、API 與 DAST 證據鏈",
"把 SAST、DAST、API contract、dependency finding、修復 commit 與 production verifier 串成同一證據鏈。",
),
)
_ASSET_TYPES = (
"host",
"container",
@@ -684,8 +782,10 @@ def _work_item(
title: str,
gap_count: int,
next_action: str,
*,
gap_percent: int | None = None,
) -> dict[str, Any]:
return {
item = {
"work_item_id": work_item_id,
"priority": priority,
"control_id": control_id,
@@ -693,7 +793,11 @@ def _work_item(
"gap_count": gap_count,
"status": "open",
"next_action": next_action,
"order": _WORK_ITEM_ORDER.get(control_id, 999),
}
if gap_percent is not None:
item["gap_percent"] = max(0, min(100, gap_percent))
return item
def _strict_runtime_completion_projection(row: Any) -> dict[str, Any]:
@@ -1283,7 +1387,38 @@ def build_iwooos_security_asset_control_plane(
)
)
work_items.sort(key=lambda item: (item["priority"], item["work_item_id"]))
domain_by_id = {
str(domain["domain_id"]): domain for domain in security_program_domains
}
active_control_ids = {str(item["control_id"]) for item in work_items}
for (
domain_id,
work_item_id,
priority,
title,
next_action,
) in _SECURITY_DOMAIN_WORK_ITEMS:
domain = domain_by_id.get(domain_id)
if not domain or int(domain.get("controlled_percent", 0)) >= 100:
continue
if domain_id in active_control_ids:
continue
work_items.append(
_work_item(
work_item_id,
priority,
domain_id,
title,
1,
next_action,
gap_percent=max(0, 100 - int(domain.get("controlled_percent", 0))),
)
)
active_control_ids.add(domain_id)
work_items.sort(
key=lambda item: (item["priority"], item["order"], item["work_item_id"])
)
overall_control_percent = round(
sum(function["controlled_percent"] for function in control_functions)
/ len(control_functions)
@@ -1829,8 +1964,7 @@ async def _read_strict_runtime_source() -> tuple[dict[str, Any], dict[str, Any]]
readback
)
if (
strict_runtime.get("schema_version")
!= _STRICT_RUNTIME_SCHEMA_VERSION
strict_runtime.get("schema_version") != _STRICT_RUNTIME_SCHEMA_VERSION
or strict_runtime.get("runtime_contract_schema_version")
!= AI_AUTOMATION_RUNTIME_CONTRACT_SCHEMA_VERSION
):
@@ -2056,21 +2190,45 @@ class IwoooSSecurityAssetControlPlaneService:
source_id="security_compliance",
statement=_sql(
"""
WITH recent AS MATERIALIZED (
SELECT snapshot_id, asset_id, dimension, status, detected_at
FROM asset_compliance_snapshot
ORDER BY snapshot_id DESC
LIMIT 100000
), latest AS (
SELECT DISTINCT ON (asset_id, dimension)
asset_id, dimension, status
FROM recent
ORDER BY asset_id, dimension, detected_at DESC
WITH dimensions(dimension) AS (
SELECT unnest(ARRAY[
'ssl_cert_valid', 'cve_scan', 'secret_rotated',
'backup_tested', 'audit_log_enabled',
'access_reviewed', 'encryption_at_rest'
]::text[])
)
SELECT dimension, status, count(*) AS cnt
FROM latest
GROUP BY dimension, status
ORDER BY dimension, status
SELECT
dimensions.dimension,
CASE
WHEN latest.detected_at IS NULL
OR latest.detected_at < NOW() - INTERVAL '36 hours'
THEN 'unknown'
ELSE latest.status
END AS status,
count(*) AS cnt
FROM asset_inventory asset
CROSS JOIN dimensions
LEFT JOIN LATERAL (
SELECT snapshot.status, snapshot.detected_at
FROM asset_compliance_snapshot snapshot
WHERE snapshot.asset_id = asset.asset_id
AND snapshot.dimension = dimensions.dimension
ORDER BY snapshot.detected_at DESC, snapshot.snapshot_id DESC
LIMIT 1
) latest ON true
WHERE asset.lifecycle_state = 'active'
GROUP BY dimensions.dimension, CASE
WHEN latest.detected_at IS NULL
OR latest.detected_at < NOW() - INTERVAL '36 hours'
THEN 'unknown'
ELSE latest.status
END
ORDER BY dimensions.dimension, CASE
WHEN latest.detected_at IS NULL
OR latest.detected_at < NOW() - INTERVAL '36 hours'
THEN 'unknown'
ELSE latest.status
END
"""
),
result_mode="all",
@@ -2225,30 +2383,36 @@ class IwoooSSecurityAssetControlPlaneService:
source_id="audit_runtime",
statement=_sql(
"""
WITH k8s AS (
SELECT
count(*) AS total,
count(*) FILTER (WHERE success IS FALSE) AS failed
FROM audit_logs
WHERE project_id = 'awoooi'
AND created_at >= NOW() - INTERVAL '24 hours'
), mcp AS (
SELECT
count(*) AS total,
count(*) FILTER (WHERE success IS FALSE) AS failed
FROM mcp_audit_log
WHERE created_at >= NOW() - INTERVAL '24 hours'
), asset_changes AS (
SELECT
count(*) AS total,
count(*) FILTER (WHERE ai_analysis IS NOT NULL) AS analyzed
FROM asset_change_event
WHERE detected_at >= NOW() - INTERVAL '24 hours'
)
SELECT
(SELECT count(*) FROM audit_logs
WHERE project_id = 'awoooi'
AND created_at >= NOW() - INTERVAL '24 hours')
AS k8s_audit_count_24h,
(SELECT count(*) FROM audit_logs
WHERE project_id = 'awoooi'
AND created_at >= NOW() - INTERVAL '24 hours'
AND success IS FALSE)
AS k8s_audit_failure_count_24h,
(SELECT count(*) FROM mcp_audit_log
WHERE created_at >= NOW() - INTERVAL '24 hours')
AS mcp_audit_count_24h,
(SELECT count(*) FROM mcp_audit_log
WHERE created_at >= NOW() - INTERVAL '24 hours'
AND success IS FALSE)
AS mcp_audit_failure_count_24h,
(SELECT count(*) FROM asset_change_event
WHERE detected_at >= NOW() - INTERVAL '24 hours')
AS asset_change_count_24h,
(SELECT count(*) FROM asset_change_event
WHERE detected_at >= NOW() - INTERVAL '24 hours'
AND ai_analysis IS NOT NULL)
AS asset_change_ai_analysis_count_24h
k8s.total AS k8s_audit_count_24h,
k8s.failed AS k8s_audit_failure_count_24h,
mcp.total AS mcp_audit_count_24h,
mcp.failed AS mcp_audit_failure_count_24h,
asset_changes.total AS asset_change_count_24h,
asset_changes.analyzed AS asset_change_ai_analysis_count_24h
FROM k8s
CROSS JOIN mcp
CROSS JOIN asset_changes
"""
),
result_mode="one",