feat(iwooos): add P0 security incident convergence gate

This commit is contained in:
ogt
2026-06-25 19:25:55 +08:00
parent e11130440b
commit 97affa698a
6 changed files with 1361 additions and 1 deletions

View File

@@ -19802,6 +19802,67 @@
}
}
},
"p0SecurityIncidentConvergence": {
"eyebrow": "P0 資安事件收斂 Gate",
"title": "把 Wazuh、主機入侵、Nginx、端口與告警紅點收成同一張處置表",
"subtitle": "這張卡彙總 12 個既有只讀 snapshot固定 8 條 P0 事件線Wazuh API / 登錄、主機鑑識、公開入口、SSH / firewall、主機 runtime、告警收件、SOC 事件單與跨專案 freeze目前只顯示缺口與 0/false 邊界,不連 Wazuh、不掃描、不 reload、不封鎖、不重啟、不送通知。",
"checkLabel": "優先",
"stateLabel": "狀態",
"boundaryTitle": "P0 事件收斂邊界",
"boundaryIntro": "以下鍵值固定P0 事件收斂不是修復完成,也不是 runtime 授權Dashboard index pattern 綠燈、route 200、agent active、CD success、UI 可見或外部宣稱都不能單獨當資安完成。",
"summary": {
"sources": {
"label": "來源",
"detail": "12 個只讀 snapshot 已被收成同一張 P0 事件總覽。"
},
"lanes": {
"label": "P0 線",
"detail": "8 條事件線全部仍等待 owner 脫敏證據。"
},
"accepted": {
"label": "已接受",
"detail": "owner response、事件證據與 registry acceptance 仍為 0%。"
},
"runtimeGate": {
"label": "執行期",
"detail": "active response、scan、reload、firewall、host write 與 action button 都是 0。"
}
},
"items": {
"wazuhApi": {
"title": "Wazuh API / registry 是第一硬 Gate",
"body": "API connection 與 API version 仍未通過index pattern 綠燈不能替代 manager registry 與逐主機納管證據。"
},
"hostForensics": {
"title": "主機入侵不能只靠宣稱",
"body": "需要 Wazuh event、auth、process、network、FIM、package、persistence、containment 與 recovery proof refs。"
},
"gatewayNginx": {
"title": "Nginx 入口先收 live diff",
"body": "需要 owner 提供脫敏 live conf、rendered diff、nginx test readback、route smoke、維護窗口與 rollback。"
},
"sshFirewall": {
"title": "端口與防火牆要 before / after",
"body": "SSH、firewall、WireGuard、NodePort 與 NetworkPolicy 需 actor、影響、服務健康、回滾與 postcheck。"
},
"hostRuntime": {
"title": "Docker 與 systemd 要收 runtime 證據",
"body": "需要 daemon、unit、process、port binding、dependency、recovery proof 與 postcheck refs避免只靠 route 回 200 誤判。"
},
"alertReceipt": {
"title": "告警要能收件與行動",
"body": "告警需有白話摘要、嚴重度、信心、asset alias、receipt、dedupe、noise budget 與 owner gate。"
},
"socCase": {
"title": "SOC / Kali / Wazuh 要 case 化",
"body": "事件要有 case id、severity、confidence、Kali scope envelope、chain of custody 與可追蹤處置 owner。"
},
"runtimeBoundary": {
"title": "跨專案與 runtime 動作不得自動執行",
"body": "需要跨專案同步、維護窗口或 break-glass、rollback ownerWazuh active response、Kali scan、reload、firewall、host write、Telegram 實發與 SOAR 都需獨立批准。"
}
}
},
"securityAssetControlLedger": {
"eyebrow": "P0-A 資安資產控制總帳",
"title": "把主機、入口、版本來源、監控、Wazuh、Kali 與供應鏈收成一張總帳",

View File

@@ -19802,6 +19802,67 @@
}
}
},
"p0SecurityIncidentConvergence": {
"eyebrow": "P0 資安事件收斂 Gate",
"title": "把 Wazuh、主機入侵、Nginx、端口與告警紅點收成同一張處置表",
"subtitle": "這張卡彙總 12 個既有只讀 snapshot固定 8 條 P0 事件線Wazuh API / 登錄、主機鑑識、公開入口、SSH / firewall、主機 runtime、告警收件、SOC 事件單與跨專案 freeze目前只顯示缺口與 0/false 邊界,不連 Wazuh、不掃描、不 reload、不封鎖、不重啟、不送通知。",
"checkLabel": "優先",
"stateLabel": "狀態",
"boundaryTitle": "P0 事件收斂邊界",
"boundaryIntro": "以下鍵值固定P0 事件收斂不是修復完成,也不是 runtime 授權Dashboard index pattern 綠燈、route 200、agent active、CD success、UI 可見或外部宣稱都不能單獨當資安完成。",
"summary": {
"sources": {
"label": "來源",
"detail": "12 個只讀 snapshot 已被收成同一張 P0 事件總覽。"
},
"lanes": {
"label": "P0 線",
"detail": "8 條事件線全部仍等待 owner 脫敏證據。"
},
"accepted": {
"label": "已接受",
"detail": "owner response、事件證據與 registry acceptance 仍為 0%。"
},
"runtimeGate": {
"label": "執行期",
"detail": "active response、scan、reload、firewall、host write 與 action button 都是 0。"
}
},
"items": {
"wazuhApi": {
"title": "Wazuh API / registry 是第一硬 Gate",
"body": "API connection 與 API version 仍未通過index pattern 綠燈不能替代 manager registry 與逐主機納管證據。"
},
"hostForensics": {
"title": "主機入侵不能只靠宣稱",
"body": "需要 Wazuh event、auth、process、network、FIM、package、persistence、containment 與 recovery proof refs。"
},
"gatewayNginx": {
"title": "Nginx 入口先收 live diff",
"body": "需要 owner 提供脫敏 live conf、rendered diff、nginx test readback、route smoke、維護窗口與 rollback。"
},
"sshFirewall": {
"title": "端口與防火牆要 before / after",
"body": "SSH、firewall、WireGuard、NodePort 與 NetworkPolicy 需 actor、影響、服務健康、回滾與 postcheck。"
},
"hostRuntime": {
"title": "Docker 與 systemd 要收 runtime 證據",
"body": "需要 daemon、unit、process、port binding、dependency、recovery proof 與 postcheck refs避免只靠 route 回 200 誤判。"
},
"alertReceipt": {
"title": "告警要能收件與行動",
"body": "告警需有白話摘要、嚴重度、信心、asset alias、receipt、dedupe、noise budget 與 owner gate。"
},
"socCase": {
"title": "SOC / Kali / Wazuh 要 case 化",
"body": "事件要有 case id、severity、confidence、Kali scope envelope、chain of custody 與可追蹤處置 owner。"
},
"runtimeBoundary": {
"title": "跨專案與 runtime 動作不得自動執行",
"body": "需要跨專案同步、維護窗口或 break-glass、rollback ownerWazuh active response、Kali scan、reload、firewall、host write、Telegram 實發與 SOAR 都需獨立批准。"
}
}
},
"securityAssetControlLedger": {
"eyebrow": "P0-A 資安資產控制總帳",
"title": "把主機、入口、版本來源、監控、Wazuh、Kali 與供應鏈收成一張總帳",

View File

@@ -351,6 +351,14 @@ type ExternalHostIntrusionPreventionControlItem = {
tone: 'steady' | 'warn' | 'locked'
}
type P0SecurityIncidentConvergenceItem = {
key: string
check: string
state: string
icon: typeof ShieldCheck
tone: 'steady' | 'warn' | 'locked'
}
type HighValueConfigOwnerPacketItem = {
key: string
gate: string
@@ -2544,6 +2552,77 @@ const socSiemKaliWazuhIntegrationBoundaries = [
'not_authorization=true',
] as const
const p0SecurityIncidentConvergenceSummary = [
{ key: 'sources', value: '12', icon: FileText, tone: 'steady' },
{ key: 'lanes', value: '8', icon: AlertTriangle, tone: 'warn' },
{ key: 'accepted', value: '0%', icon: Lock, tone: 'locked' },
{ key: 'runtimeGate', value: '0', icon: Lock, tone: 'locked' },
] as const
const p0SecurityIncidentConvergenceItems: P0SecurityIncidentConvergenceItem[] = [
{ key: 'wazuhApi', check: 'P0-1', state: 'API / 登錄 0', icon: Radar, tone: 'locked' },
{ key: 'hostForensics', check: 'P0-2', state: '事件 ref 0', icon: Server, tone: 'warn' },
{ key: 'gatewayNginx', check: 'P0-3', state: '環境差異 0', icon: Route, tone: 'warn' },
{ key: 'sshFirewall', check: 'P0-4', state: '前後狀態 0', icon: Network, tone: 'warn' },
{ key: 'hostRuntime', check: 'P0-5', state: '服務證據 0', icon: Activity, tone: 'warn' },
{ key: 'alertReceipt', check: 'P0-6', state: '收件 0', icon: Bell, tone: 'warn' },
{ key: 'socCase', check: 'P0-7', state: '事件單 0', icon: ClipboardCheck, tone: 'warn' },
{ key: 'runtimeBoundary', check: 'P0-8', state: '不得自動', icon: Lock, tone: 'locked' },
] as const
const p0SecurityIncidentConvergenceBoundaries = [
'iwooos_p0_security_incident_convergence_gate_visible=true',
'iwooos_p0_security_incident_convergence_source_snapshot_count=12',
'iwooos_p0_security_incident_convergence_p0_lane_count=8',
'iwooos_p0_security_incident_convergence_blocked_lane_count=8',
'iwooos_p0_security_incident_convergence_source_side_rollup_ready_percent=100',
'iwooos_p0_security_incident_convergence_owner_response_received_count=0',
'iwooos_p0_security_incident_convergence_owner_response_accepted_count=0',
'iwooos_p0_security_incident_convergence_redacted_evidence_received_count=0',
'iwooos_p0_security_incident_convergence_redacted_evidence_accepted_count=0',
'iwooos_p0_security_incident_convergence_dashboard_api_connection_ok_count=0',
'iwooos_p0_security_incident_convergence_dashboard_api_version_ok_count=0',
'iwooos_p0_security_incident_convergence_dashboard_index_pattern_ok_count=3',
'iwooos_p0_security_incident_convergence_manager_registry_accepted_count=0',
'iwooos_p0_security_incident_convergence_expected_host_scope_count=6',
'iwooos_p0_security_incident_convergence_direct_agent_active_observed_count=2',
'iwooos_p0_security_incident_convergence_wazuh_event_ref_received_count=0',
'iwooos_p0_security_incident_convergence_host_forensics_ref_received_count=0',
'iwooos_p0_security_incident_convergence_containment_decision_accepted_count=0',
'iwooos_p0_security_incident_convergence_public_gateway_live_conf_received_count=0',
'iwooos_p0_security_incident_convergence_nginx_test_evidence_count=0',
'iwooos_p0_security_incident_convergence_route_smoke_evidence_count=0',
'iwooos_p0_security_incident_convergence_port_firewall_change_evidence_received_count=0',
'iwooos_p0_security_incident_convergence_monitoring_post_incident_readback_received_count=0',
'iwooos_p0_security_incident_convergence_alert_route_accepted_count=0',
'iwooos_p0_security_incident_convergence_incident_case_accepted_count=0',
'iwooos_p0_security_incident_convergence_high_value_config_category_count=14',
'iwooos_p0_security_incident_convergence_high_value_config_average_coverage_percent=73',
'iwooos_p0_security_incident_convergence_external_host_coverage_percent=74',
'iwooos_p0_security_incident_convergence_wazuh_active_response_authorized_count=0',
'iwooos_p0_security_incident_convergence_kali_active_scan_authorized_count=0',
'iwooos_p0_security_incident_convergence_host_write_authorized_count=0',
'iwooos_p0_security_incident_convergence_firewall_change_authorized_count=0',
'iwooos_p0_security_incident_convergence_nginx_reload_authorized_count=0',
'iwooos_p0_security_incident_convergence_runtime_gate_count=0',
'iwooos_p0_security_incident_convergence_action_button_count=0',
'wazuh_api_live_query_authorized=false',
'wazuh_active_response_authorized=false',
'kali_active_scan_authorized=false',
'kali_execute_authorized=false',
'ssh_write_authorized=false',
'host_write_authorized=false',
'firewall_change_authorized=false',
'nginx_reload_authorized=false',
'telegram_send_authorized=false',
'soar_case_create_authorized=false',
'auto_block_authorized=false',
'production_write_authorized=false',
'runtime_execution_authorized=false',
'action_buttons_allowed=false',
'not_authorization=true',
] as const
const securityAssetControlLedgerSummary = [
{ key: 'assetGroups', value: '16', icon: ShieldCheck, tone: 'steady' },
{ key: 'p0Groups', value: '14', icon: AlertTriangle, tone: 'warn' },
@@ -2661,7 +2740,7 @@ const externalHostIntrusionPreventionControlBoundaries = [
'external_host_intrusion_prevention_control_action_button_count=0',
'docker_compose_systemd_host_config_coverage_percent=68',
'ssh_firewall_network_access_coverage_percent=70',
'monitoring_alerting_observability_coverage_percent=78',
'monitoring_alerting_observability_coverage_percent=74',
'host_write_authorized=false',
'ssh_write_authorized=false',
'firewall_change_authorized=false',
@@ -8923,6 +9002,137 @@ function IwoooSSocSiemKaliWazuhIntegrationBoard() {
)
}
function IwoooSP0SecurityIncidentConvergenceBoard() {
const t = useTranslations('iwooos.p0SecurityIncidentConvergence')
const textWrap = { overflowWrap: 'anywhere' as const, wordBreak: 'break-word' as const }
return (
<section
style={{ marginBottom: 14, maxWidth: '100%', overflow: 'hidden' }}
data-testid="iwooos-p0-security-incident-convergence-board"
>
<div style={{ ...band, padding: 16, background: '#fff8f6', borderColor: '#e5b9aa' }}>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 300px), 1fr))', gap: 14 }}>
<div style={{ minWidth: 0 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, color: '#9a4228', fontSize: 12, fontWeight: 700 }}>
<AlertTriangle size={17} color="#b94f2f" />
{t('eyebrow')}
</div>
<h2 style={{ fontSize: 17, margin: '8px 0 0', color: '#141413' }}>{t('title')}</h2>
<p style={{ fontSize: 12, color: '#7a4b3c', margin: '6px 0 0', lineHeight: 1.55, ...textWrap }}>
{t('subtitle')}
</p>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(126px, 1fr))', gap: 8 }}>
{p0SecurityIncidentConvergenceSummary.map(item => {
const Icon = item.icon
return (
<div key={item.key} style={{ border: '0.5px solid #efc9bd', borderRadius: 8, padding: 12, background: '#fff' }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
<span style={{ fontSize: 11, color: '#8d685d' }}>{t(`summary.${item.key}.label` as never)}</span>
<Icon size={16} color={toneColors[item.tone]} />
</div>
<div style={{ fontSize: 19, fontWeight: 700, color: toneColors[item.tone], lineHeight: 1.1, marginTop: 8, ...textWrap }}>
{item.value}
</div>
<p style={{ fontSize: 11, color: '#7a4b3c', lineHeight: 1.45, margin: '8px 0 0', ...textWrap }}>
{t(`summary.${item.key}.detail` as never)}
</p>
</div>
)
})}
</div>
</div>
<div
style={{
marginTop: 14,
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 210px), 1fr))',
gap: 10,
}}
>
{p0SecurityIncidentConvergenceItems.map(item => {
const Icon = item.icon
return (
<div
key={item.key}
style={{
border: '0.5px solid #efc9bd',
borderRadius: 8,
background: item.tone === 'locked' ? '#fff4f1' : '#fff',
padding: 13,
minHeight: 150,
display: 'grid',
alignContent: 'space-between',
minWidth: 0,
...textWrap,
}}
>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
<span style={{ color: toneColors[item.tone], fontSize: 12, fontWeight: 700 }}>
{t('checkLabel')} {item.check}
</span>
<Icon size={18} color={toneColors[item.tone]} />
</div>
<div style={{ minWidth: 0 }}>
<div style={{ fontSize: 15, fontWeight: 700, color: '#141413', lineHeight: 1.25 }}>
{t(`items.${item.key}.title` as never)}
</div>
<div style={{ fontSize: 12, color: toneColors[item.tone], fontWeight: 700, marginTop: 5 }}>
{t('stateLabel')}{item.state}
</div>
</div>
<p style={{ fontSize: 11, color: '#7a4b3c', lineHeight: 1.45, margin: 0, ...textWrap }}>
{t(`items.${item.key}.body` as never)}
</p>
</div>
)
})}
</div>
<details
data-testid="iwooos-p0-security-incident-convergence-boundaries"
style={{
marginTop: 12,
border: '0.5px solid #efc9bd',
borderRadius: 8,
background: '#fff',
padding: '8px 10px',
}}
>
<summary style={{ cursor: 'pointer', fontSize: 12, fontWeight: 700, color: '#9a4228' }}>
{t('boundaryTitle')}
</summary>
<p style={{ fontSize: 11, color: '#7a4b3c', lineHeight: 1.5, margin: '8px 0', ...textWrap }}>
{t('boundaryIntro')}
</p>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(230px, 1fr))', gap: 6 }}>
{p0SecurityIncidentConvergenceBoundaries.map(item => (
<code
key={item}
style={{
border: '0.5px solid #efc9bd',
borderRadius: 8,
padding: '6px 8px',
color: '#7a4b3c',
fontSize: 11,
lineHeight: 1.4,
background: '#fff8f6',
overflowWrap: 'anywhere',
}}
>
{item}
</code>
))}
</div>
</details>
</div>
</section>
)
}
function IwoooSSecurityAssetControlLedgerBoard() {
const t = useTranslations('iwooos.securityAssetControlLedger')
const textWrap = { overflowWrap: 'anywhere' as const, wordBreak: 'break-word' as const }
@@ -21722,6 +21932,7 @@ export default function IwoooSPage({ params }: { params: { locale: string } }) {
<IwoooSWazuhLiveMetadataEnvGateBoard />
<IwoooSSecurityOperatingSystemBoard />
<IwoooSSocSiemKaliWazuhIntegrationBoard />
<IwoooSP0SecurityIncidentConvergenceBoard />
<IwoooSSecurityAssetControlLedgerBoard />
<IwoooSExternalHostIntrusionPreventionControlBoard />
<IwoooSCriticalConfigPriorityBoard />

View File

@@ -0,0 +1,77 @@
# IwoooS P0 資安事件收斂 Gate
| 項目 | 狀態 |
| --- | --- |
| 工具 | `scripts/security/iwooos-p0-security-incident-convergence-gate.py` |
| Snapshot | `docs/security/iwooos-p0-security-incident-convergence-gate.snapshot.json` |
| Schema | `iwooos_p0_security_incident_convergence_gate_v1` |
| 狀態 | `p0_security_incident_convergence_blocked_waiting_owner_evidence` |
| Runtime gate | `0` |
| Action button | `0` |
## 目的
這個 Gate 把目前最容易造成即時資安或服務風險的紅點收成同一張只讀總覽:
1. Wazuh API 與 manager registry 真相。
2. 主機入侵鑑識與 containment 決策。
3. Public Gateway / Nginx 變更收斂。
4. SSH / firewall / port / network policy baseline。
5. Docker / systemd / process / port binding。
6. 監控告警可讀性、收件與 no-false-green。
7. SOC / Kali / Wazuh 事件 case 化。
8. 跨專案 freeze、runtime 邊界與防衝突。
它只讀取既有 snapshot不連線 Wazuh、不 SSH、不讀 live config、不做 scan、不 reload、不封鎖端口、不重啟服務、不送通知、不建立 SOAR case、不收 secret。
## 目前讀回
| 指標 | 讀回 |
| --- | ---: |
| source snapshot | `12` |
| P0 lane | `8` |
| blocked lane | `8` |
| source-side rollup ready | `100%` |
| owner response received / accepted | `0 / 0` |
| redacted evidence received / accepted | `0 / 0` |
| Wazuh dashboard API connection ok | `0` |
| Wazuh dashboard API version ok | `0` |
| Wazuh manager registry accepted | `0` |
| Wazuh event ref received | `0` |
| host forensics ref received | `0` |
| Public Gateway live conf / rendered diff / nginx test / route smoke | `0 / 0 / 0 / 0` |
| monitoring post-incident readback received | `0` |
| incident case accepted | `0` |
| runtime gate / action button | `0 / 0` |
## 不可誤判
- Wazuh Dashboard index pattern 綠燈只能當局部訊號API connection、API version 與 manager registry 仍是硬 Gate。
- route 200、Dashboard 可見、agent active、CD success、UI 可見或外部 Agent 宣稱,都不能單獨當資安完成。
- Nginx、firewall、host runtime、monitoring 與 SOC evidence 必須用脫敏 owner refs 補齊。
- 不得貼 raw log、raw Wazuh payload、工作視窗對話、內網 IP、個人 namespace、secret、token、private key 或未脫敏截圖。
## 下一步優先序
| 優先 | 工作 | 驗收前維持 |
| --- | --- | --- |
| P0-1 | Wazuh Dashboard API 修復 postcheck 與 manager registry owner evidence | `manager_registry_accepted_count=0` |
| P0-2 | Wazuh event、host forensic、containment decision 與 recovery proof refs | `wazuh_event_ref_received_count=0` |
| P0-3 | Nginx owner live conf、rendered diff、nginx test readback、route smoke 與 rollback refs | `nginx_reload_authorized_count=0` |
| P0-4 | SSH / firewall / port before-after state、actor、impact 與 rollback refs | `firewall_change_authorized_count=0` |
| P0-5 | Docker / systemd / process / port binding 與 dependency postcheck refs | `host_write_authorized_count=0` |
| P0-6 | 告警訊息合約、receipt、dedupe、noise budget 與 post-change monitoring refs | `alert_route_accepted_count=0` |
| P0-7 | SOC case id、severity / confidence、Kali scope envelope 與 chain of custody refs | `incident_case_accepted_count=0` |
| P0-8 | 跨專案同步、維護窗口或 break-glass、rollback owner 與 runtime authorization refs | `runtime_gate_count=0` |
## 驗證指令
```bash
python3 scripts/security/iwooos-p0-security-incident-convergence-gate.py --root .
python3 scripts/security/security-mirror-progress-guard.py --root .
python3 scripts/security/iwooos-frontend-display-redaction-guard.py --root .
```
## 邊界
這個 Gate 不是批准、不是修復完成、不是 active response、不是 Kali active scan、不是 Nginx reload、不是 firewall change、不是 host write、不是 secret rotation也不是 production write。所有 runtime 動作仍需獨立 owner、維護窗口、rollback、postcheck 與人工批准。

View File

@@ -0,0 +1,447 @@
{
"blocked_runtime_actions": [
"wazuh_api_live_query",
"wazuh_active_response",
"kali_active_scan",
"kali_execute",
"ssh_read",
"ssh_write",
"host_write",
"firewall_change",
"port_close",
"port_open",
"nginx_test",
"nginx_reload",
"docker_restart",
"systemctl_restart",
"argocd_sync",
"workflow_modification",
"repo_secret_change",
"secret_rotation",
"telegram_send",
"soar_case_create",
"auto_block",
"production_write",
"force_push"
],
"execution_boundaries": {
"action_buttons_allowed": false,
"argocd_sync_authorized": false,
"auto_block_authorized": false,
"docker_restart_authorized": false,
"firewall_change_authorized": false,
"host_write_authorized": false,
"kali_active_scan_authorized": false,
"kali_execute_authorized": false,
"nginx_reload_authorized": false,
"nginx_test_authorized": false,
"not_authorization": true,
"production_write_authorized": false,
"repo_secret_change_authorized": false,
"runtime_execution_authorized": false,
"secret_value_collection_allowed": false,
"soar_case_create_authorized": false,
"ssh_read_authorized": false,
"ssh_write_authorized": false,
"systemctl_restart_authorized": false,
"telegram_send_authorized": false,
"wazuh_active_response_authorized": false,
"wazuh_api_live_query_authorized": false,
"workflow_modification_authorized": false
},
"generated_at": "2026-06-25T19:23:16+08:00",
"git_commit": "e1113044",
"mode": "snapshot_rollup_only_no_runtime_no_secret_collection",
"operator_interpretation": [
"這張 Gate 是 P0 事件彙總,不是 runtime 修復授權。",
"Wazuh Dashboard index pattern 綠燈只能當局部訊號API connection、API version 與 manager registry 仍是硬 Gate。",
"Nginx、firewall、host runtime、monitoring 與 SOC evidence 必須用脫敏 owner refs 補齊,不能貼 raw log 或工作視窗內容。",
"所有 containment、active response、scan、reload、restart、secret rotation 與 production write 仍需獨立人工批准。"
],
"p0_lanes": [
{
"action_buttons_allowed": false,
"label": "Wazuh API 與 manager registry 真相",
"lane_id": "wazuh_dashboard_api_registry",
"next_gate": "dashboard_api_repair_postcheck_and_manager_registry_owner_evidence",
"not_authorization": true,
"owner_response_accepted": false,
"owner_response_received": false,
"priority": "P0",
"redacted_evidence_accepted": false,
"redacted_evidence_received": false,
"required_evidence": [
{
"accepted": false,
"evidence_id": "dashboard_api_connection_ok_ref"
},
{
"accepted": false,
"evidence_id": "dashboard_api_version_ok_ref"
},
{
"accepted": false,
"evidence_id": "manager_registry_agent_counts_ref"
},
{
"accepted": false,
"evidence_id": "per_host_agent_scope_matrix_ref"
}
],
"runtime_authorized": false,
"source_snapshot_refs": [
"docs/security/wazuh-agent-visibility-runtime-gate.snapshot.json",
"docs/security/wazuh-managed-host-coverage-gate.snapshot.json"
],
"source_statuses": {
"wazuh_coverage": "blocked_waiting_full_host_registry_readback",
"wazuh_runtime": "blocked_waiting_manager_agent_registry_readback"
},
"status": "blocked_waiting_owner_evidence"
},
{
"action_buttons_allowed": false,
"label": "主機入侵鑑識與 containment 決策",
"lane_id": "host_intrusion_forensics",
"next_gate": "wazuh_event_host_forensic_containment_owner_packet",
"not_authorization": true,
"owner_response_accepted": false,
"owner_response_received": false,
"priority": "P0",
"redacted_evidence_accepted": false,
"redacted_evidence_received": false,
"required_evidence": [
{
"accepted": false,
"evidence_id": "wazuh_event_refs"
},
{
"accepted": false,
"evidence_id": "host_auth_process_network_fim_refs"
},
{
"accepted": false,
"evidence_id": "containment_decision_ref"
},
{
"accepted": false,
"evidence_id": "recovery_proof_and_postcheck_ref"
}
],
"runtime_authorized": false,
"source_snapshot_refs": [
"docs/security/wazuh-iwooos-intrusion-readback-plan.snapshot.json",
"docs/security/external-host-intrusion-prevention-control.snapshot.json"
],
"source_statuses": {
"external_host": "external_host_intrusion_prevention_control_ready_no_runtime_action",
"wazuh_intrusion": "wazuh_intrusion_readback_plan_ready_no_runtime_action"
},
"status": "blocked_waiting_owner_evidence"
},
{
"action_buttons_allowed": false,
"label": "Public Gateway / Nginx 變更收斂",
"lane_id": "public_gateway_nginx",
"next_gate": "owner_live_conf_rendered_diff_nginx_test_route_smoke_packet",
"not_authorization": true,
"owner_response_accepted": false,
"owner_response_received": false,
"priority": "P0",
"redacted_evidence_accepted": false,
"redacted_evidence_received": false,
"required_evidence": [
{
"accepted": false,
"evidence_id": "owner_provided_redacted_live_conf_ref"
},
{
"accepted": false,
"evidence_id": "source_to_live_rendered_diff_ref"
},
{
"accepted": false,
"evidence_id": "nginx_test_readback_ref"
},
{
"accepted": false,
"evidence_id": "route_smoke_and_rollback_ref"
}
],
"runtime_authorized": false,
"source_snapshot_refs": [
"docs/security/public-gateway-preflight-inventory.snapshot.json",
"docs/security/public-gateway-post-incident-readback-plan.snapshot.json",
"docs/security/high-value-config-control-coverage.snapshot.json"
],
"source_statuses": {
"high_value_config": "coverage_matrix_ready",
"public_gateway": "repo_only_preflight_contract_ready",
"public_gateway_post_incident": "post_incident_readback_plan_ready_no_runtime_action"
},
"status": "blocked_waiting_owner_evidence"
},
{
"action_buttons_allowed": false,
"label": "SSH / firewall / port / network policy baseline",
"lane_id": "ssh_firewall_ports",
"next_gate": "before_after_state_actor_impact_rollback_packet",
"not_authorization": true,
"owner_response_accepted": false,
"owner_response_received": false,
"priority": "P0",
"redacted_evidence_accepted": false,
"redacted_evidence_received": false,
"required_evidence": [
{
"accepted": false,
"evidence_id": "before_state_ref"
},
{
"accepted": false,
"evidence_id": "after_state_ref"
},
{
"accepted": false,
"evidence_id": "actor_attribution_ref"
},
{
"accepted": false,
"evidence_id": "service_health_impact_and_rollback_ref"
}
],
"runtime_authorized": false,
"source_snapshot_refs": [
"docs/security/ssh-network-post-incident-readback-plan.snapshot.json",
"docs/security/port-firewall-change-evidence-acceptance.snapshot.json",
"docs/security/external-host-intrusion-prevention-control.snapshot.json"
],
"source_statuses": {
"external_host": "external_host_intrusion_prevention_control_ready_no_runtime_action",
"port_firewall": "change_evidence_acceptance_ready_no_runtime_action",
"ssh_network_post_incident": "post_incident_readback_plan_ready_no_runtime_action"
},
"status": "blocked_waiting_owner_evidence"
},
{
"action_buttons_allowed": false,
"label": "Docker / systemd / process / port binding",
"lane_id": "host_runtime_services",
"next_gate": "host_runtime_forensic_service_recovery_packet",
"not_authorization": true,
"owner_response_accepted": false,
"owner_response_received": false,
"priority": "P0",
"redacted_evidence_accepted": false,
"redacted_evidence_received": false,
"required_evidence": [
{
"accepted": false,
"evidence_id": "docker_daemon_state_ref"
},
{
"accepted": false,
"evidence_id": "systemd_unit_state_ref"
},
{
"accepted": false,
"evidence_id": "process_port_binding_ref"
},
{
"accepted": false,
"evidence_id": "dependency_and_postcheck_ref"
}
],
"runtime_authorized": false,
"source_snapshot_refs": [
"docs/security/host-service-post-incident-readback-plan.snapshot.json",
"docs/security/external-host-intrusion-prevention-control.snapshot.json"
],
"source_statuses": {
"external_host": "external_host_intrusion_prevention_control_ready_no_runtime_action",
"host_service_post_incident": "post_incident_readback_plan_ready_no_runtime_action"
},
"status": "blocked_waiting_owner_evidence"
},
{
"action_buttons_allowed": false,
"label": "監控告警可讀性、收件與 no-false-green",
"lane_id": "monitoring_alert_receipt",
"next_gate": "alert_receipt_noise_budget_readable_message_contract",
"not_authorization": true,
"owner_response_accepted": false,
"owner_response_received": false,
"priority": "P0",
"redacted_evidence_accepted": false,
"redacted_evidence_received": false,
"required_evidence": [
{
"accepted": false,
"evidence_id": "alert_route_receipt_ref"
},
{
"accepted": false,
"evidence_id": "message_contract_readability_ref"
},
{
"accepted": false,
"evidence_id": "dedupe_noise_budget_ref"
},
{
"accepted": false,
"evidence_id": "post_change_monitoring_window_ref"
}
],
"runtime_authorized": false,
"source_snapshot_refs": [
"docs/security/monitoring-post-incident-readback-plan.snapshot.json",
"docs/security/soc-siem-kali-wazuh-integration-control.snapshot.json"
],
"source_statuses": {
"monitoring_post_incident": "post_incident_readback_plan_ready_no_runtime_action",
"soc_integration": "soc_siem_kali_wazuh_integration_control_ready_no_runtime_action"
},
"status": "blocked_waiting_owner_evidence"
},
{
"action_buttons_allowed": false,
"label": "SOC / Kali / Wazuh 事件 case 化",
"lane_id": "soc_kali_wazuh_case",
"next_gate": "incident_case_owner_severity_confidence_chain_of_custody_packet",
"not_authorization": true,
"owner_response_accepted": false,
"owner_response_received": false,
"priority": "P0",
"redacted_evidence_accepted": false,
"redacted_evidence_received": false,
"required_evidence": [
{
"accepted": false,
"evidence_id": "incident_case_ref"
},
{
"accepted": false,
"evidence_id": "severity_confidence_mapping_ref"
},
{
"accepted": false,
"evidence_id": "kali_scope_and_finding_envelope_ref"
},
{
"accepted": false,
"evidence_id": "chain_of_custody_ref"
}
],
"runtime_authorized": false,
"source_snapshot_refs": [
"docs/security/soc-siem-kali-wazuh-integration-control.snapshot.json",
"docs/security/wazuh-iwooos-intrusion-readback-plan.snapshot.json"
],
"source_statuses": {
"soc_integration": "soc_siem_kali_wazuh_integration_control_ready_no_runtime_action",
"wazuh_intrusion": "wazuh_intrusion_readback_plan_ready_no_runtime_action"
},
"status": "blocked_waiting_owner_evidence"
},
{
"action_buttons_allowed": false,
"label": "跨專案 freeze、runtime 邊界與防衝突",
"lane_id": "cross_project_freeze_runtime_boundary",
"next_gate": "cross_project_sync_runtime_authorization_owner_packet",
"not_authorization": true,
"owner_response_accepted": false,
"owner_response_received": false,
"priority": "P0",
"redacted_evidence_accepted": false,
"redacted_evidence_received": false,
"required_evidence": [
{
"accepted": false,
"evidence_id": "cross_project_sync_ref"
},
{
"accepted": false,
"evidence_id": "maintenance_window_or_break_glass_ref"
},
{
"accepted": false,
"evidence_id": "rollback_owner_ref"
},
{
"accepted": false,
"evidence_id": "runtime_authorization_ref"
}
],
"runtime_authorized": false,
"source_snapshot_refs": [
"docs/security/external-host-intrusion-prevention-control.snapshot.json",
"docs/security/high-value-config-control-coverage.snapshot.json",
"docs/security/soc-siem-kali-wazuh-integration-control.snapshot.json"
],
"source_statuses": {
"external_host": "external_host_intrusion_prevention_control_ready_no_runtime_action",
"high_value_config": "coverage_matrix_ready",
"soc_integration": "soc_siem_kali_wazuh_integration_control_ready_no_runtime_action"
},
"status": "blocked_waiting_owner_evidence"
}
],
"schema_version": "iwooos_p0_security_incident_convergence_gate_v1",
"source_snapshot_refs": {
"external_host": "docs/security/external-host-intrusion-prevention-control.snapshot.json",
"high_value_config": "docs/security/high-value-config-control-coverage.snapshot.json",
"host_service_post_incident": "docs/security/host-service-post-incident-readback-plan.snapshot.json",
"monitoring_post_incident": "docs/security/monitoring-post-incident-readback-plan.snapshot.json",
"port_firewall": "docs/security/port-firewall-change-evidence-acceptance.snapshot.json",
"public_gateway": "docs/security/public-gateway-preflight-inventory.snapshot.json",
"public_gateway_post_incident": "docs/security/public-gateway-post-incident-readback-plan.snapshot.json",
"soc_integration": "docs/security/soc-siem-kali-wazuh-integration-control.snapshot.json",
"ssh_network_post_incident": "docs/security/ssh-network-post-incident-readback-plan.snapshot.json",
"wazuh_coverage": "docs/security/wazuh-managed-host-coverage-gate.snapshot.json",
"wazuh_intrusion": "docs/security/wazuh-iwooos-intrusion-readback-plan.snapshot.json",
"wazuh_runtime": "docs/security/wazuh-agent-visibility-runtime-gate.snapshot.json"
},
"status": "p0_security_incident_convergence_blocked_waiting_owner_evidence",
"summary": {
"action_button_count": 0,
"alert_route_accepted_count": 0,
"blocked_lane_count": 8,
"containment_decision_accepted_count": 0,
"dashboard_api_connection_ok_count": 0,
"dashboard_api_version_ok_count": 0,
"dashboard_index_pattern_ok_count": 3,
"direct_agent_active_observed_count": 2,
"expected_host_scope_count": 6,
"external_host_coverage_percent": 74,
"external_host_prevention_candidate_count": 14,
"firewall_change_authorized_count": 0,
"gateway_post_incident_readback_received_count": 0,
"high_value_config_average_coverage_percent": 73,
"high_value_config_category_count": 14,
"host_forensics_ref_received_count": 0,
"host_service_post_incident_readback_received_count": 0,
"host_write_authorized_count": 0,
"incident_case_accepted_count": 0,
"kali_active_scan_authorized_count": 0,
"manager_registry_accepted_count": 0,
"monitoring_post_incident_readback_received_count": 0,
"nginx_reload_authorized_count": 0,
"nginx_test_evidence_count": 0,
"owner_response_accepted_count": 0,
"owner_response_received_count": 0,
"p0_lane_count": 8,
"port_firewall_change_evidence_received_count": 0,
"public_gateway_live_conf_received_count": 0,
"public_gateway_rendered_diff_ready_count": 0,
"recovery_proof_accepted_count": 0,
"redacted_evidence_accepted_count": 0,
"redacted_evidence_received_count": 0,
"route_smoke_evidence_count": 0,
"runtime_gate_count": 0,
"source_side_rollup_ready_percent": 100,
"source_snapshot_count": 12,
"ssh_network_post_incident_readback_received_count": 0,
"wazuh_active_response_authorized_count": 0,
"wazuh_event_ref_received_count": 0
}
}

View File

@@ -0,0 +1,503 @@
#!/usr/bin/env python3
"""
IwoooS P0 資安事件收斂 Gate。
本工具讀取既有只讀 snapshot將 Wazuh API / agent 納管、主機入侵、
Public Gateway / Nginx、SSH / firewall、host runtime、monitoring alert、
SOC / Kali 與高價值配置收斂成一張 P0 事件總覽。
它不連線 Wazuh、不 SSH、不讀 live config、不做 scan、不 reload、
不封鎖端口、不重啟服務、不送通知、不建立 SOAR case、不收 secret
也不把 route 200、Dashboard 可見、agent active 或外部宣稱視為完成。
"""
from __future__ import annotations
import argparse
import json
import re
import subprocess
import sys
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Any
TAIPEI = timezone(timedelta(hours=8))
SCHEMA_VERSION = "iwooos_p0_security_incident_convergence_gate_v1"
SOURCE_SNAPSHOTS = {
"wazuh_runtime": "docs/security/wazuh-agent-visibility-runtime-gate.snapshot.json",
"wazuh_coverage": "docs/security/wazuh-managed-host-coverage-gate.snapshot.json",
"wazuh_intrusion": "docs/security/wazuh-iwooos-intrusion-readback-plan.snapshot.json",
"soc_integration": "docs/security/soc-siem-kali-wazuh-integration-control.snapshot.json",
"external_host": "docs/security/external-host-intrusion-prevention-control.snapshot.json",
"high_value_config": "docs/security/high-value-config-control-coverage.snapshot.json",
"public_gateway": "docs/security/public-gateway-preflight-inventory.snapshot.json",
"public_gateway_post_incident": "docs/security/public-gateway-post-incident-readback-plan.snapshot.json",
"ssh_network_post_incident": "docs/security/ssh-network-post-incident-readback-plan.snapshot.json",
"port_firewall": "docs/security/port-firewall-change-evidence-acceptance.snapshot.json",
"host_service_post_incident": "docs/security/host-service-post-incident-readback-plan.snapshot.json",
"monitoring_post_incident": "docs/security/monitoring-post-incident-readback-plan.snapshot.json",
}
P0_LANE_DEFINITIONS = [
{
"lane_id": "wazuh_dashboard_api_registry",
"priority": "P0",
"label": "Wazuh API 與 manager registry 真相",
"source_keys": ["wazuh_runtime", "wazuh_coverage"],
"next_gate": "dashboard_api_repair_postcheck_and_manager_registry_owner_evidence",
"required_evidence": [
"dashboard_api_connection_ok_ref",
"dashboard_api_version_ok_ref",
"manager_registry_agent_counts_ref",
"per_host_agent_scope_matrix_ref",
],
},
{
"lane_id": "host_intrusion_forensics",
"priority": "P0",
"label": "主機入侵鑑識與 containment 決策",
"source_keys": ["wazuh_intrusion", "external_host"],
"next_gate": "wazuh_event_host_forensic_containment_owner_packet",
"required_evidence": [
"wazuh_event_refs",
"host_auth_process_network_fim_refs",
"containment_decision_ref",
"recovery_proof_and_postcheck_ref",
],
},
{
"lane_id": "public_gateway_nginx",
"priority": "P0",
"label": "Public Gateway / Nginx 變更收斂",
"source_keys": ["public_gateway", "public_gateway_post_incident", "high_value_config"],
"next_gate": "owner_live_conf_rendered_diff_nginx_test_route_smoke_packet",
"required_evidence": [
"owner_provided_redacted_live_conf_ref",
"source_to_live_rendered_diff_ref",
"nginx_test_readback_ref",
"route_smoke_and_rollback_ref",
],
},
{
"lane_id": "ssh_firewall_ports",
"priority": "P0",
"label": "SSH / firewall / port / network policy baseline",
"source_keys": ["ssh_network_post_incident", "port_firewall", "external_host"],
"next_gate": "before_after_state_actor_impact_rollback_packet",
"required_evidence": [
"before_state_ref",
"after_state_ref",
"actor_attribution_ref",
"service_health_impact_and_rollback_ref",
],
},
{
"lane_id": "host_runtime_services",
"priority": "P0",
"label": "Docker / systemd / process / port binding",
"source_keys": ["host_service_post_incident", "external_host"],
"next_gate": "host_runtime_forensic_service_recovery_packet",
"required_evidence": [
"docker_daemon_state_ref",
"systemd_unit_state_ref",
"process_port_binding_ref",
"dependency_and_postcheck_ref",
],
},
{
"lane_id": "monitoring_alert_receipt",
"priority": "P0",
"label": "監控告警可讀性、收件與 no-false-green",
"source_keys": ["monitoring_post_incident", "soc_integration"],
"next_gate": "alert_receipt_noise_budget_readable_message_contract",
"required_evidence": [
"alert_route_receipt_ref",
"message_contract_readability_ref",
"dedupe_noise_budget_ref",
"post_change_monitoring_window_ref",
],
},
{
"lane_id": "soc_kali_wazuh_case",
"priority": "P0",
"label": "SOC / Kali / Wazuh 事件 case 化",
"source_keys": ["soc_integration", "wazuh_intrusion"],
"next_gate": "incident_case_owner_severity_confidence_chain_of_custody_packet",
"required_evidence": [
"incident_case_ref",
"severity_confidence_mapping_ref",
"kali_scope_and_finding_envelope_ref",
"chain_of_custody_ref",
],
},
{
"lane_id": "cross_project_freeze_runtime_boundary",
"priority": "P0",
"label": "跨專案 freeze、runtime 邊界與防衝突",
"source_keys": ["external_host", "high_value_config", "soc_integration"],
"next_gate": "cross_project_sync_runtime_authorization_owner_packet",
"required_evidence": [
"cross_project_sync_ref",
"maintenance_window_or_break_glass_ref",
"rollback_owner_ref",
"runtime_authorization_ref",
],
},
]
FORBIDDEN_TEXT_PATTERNS = [
re.compile(r"\b(?:10|127|172\.(?:1[6-9]|2\d|3[01])|192\.168)\.\d{1,3}\.\d{1,3}\b"),
re.compile(r"Authorization\s*:", re.IGNORECASE),
re.compile(r"Bearer\s+[A-Za-z0-9._-]{10,}", re.IGNORECASE),
re.compile(r"Basic\s+[A-Za-z0-9+/=]{10,}", re.IGNORECASE),
re.compile(r"password\s*[:=]\s*['\"][^'\"]+['\"]", re.IGNORECASE),
re.compile(r"token\s*[:=]\s*['\"][^'\"]+['\"]", re.IGNORECASE),
re.compile(r"cookie\s*[:=]\s*['\"][^'\"]+['\"]", re.IGNORECASE),
re.compile(r"-----BEGIN [A-Z ]*PRIVATE KEY-----"),
re.compile(r"/Users/"),
re.compile(r"\.codex", re.IGNORECASE),
re.compile(r"codex_delegation", re.IGNORECASE),
re.compile(r"In app browser", re.IGNORECASE),
re.compile(r"My request for Codex", re.IGNORECASE),
re.compile(r"批准!繼續"),
]
BLOCKED_RUNTIME_ACTIONS = [
"wazuh_api_live_query",
"wazuh_active_response",
"kali_active_scan",
"kali_execute",
"ssh_read",
"ssh_write",
"host_write",
"firewall_change",
"port_close",
"port_open",
"nginx_test",
"nginx_reload",
"docker_restart",
"systemctl_restart",
"argocd_sync",
"workflow_modification",
"repo_secret_change",
"secret_rotation",
"telegram_send",
"soar_case_create",
"auto_block",
"production_write",
"force_push",
]
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_json(root: Path, relative_path: str) -> dict[str, Any]:
path = root / relative_path
if not path.exists():
raise SystemExit(f"BLOCKED source_snapshot_missing: {relative_path}")
return json.loads(path.read_text(encoding="utf-8"))
def nested_get(data: dict[str, Any], key: str, default: Any = 0) -> Any:
if key in data:
return data[key]
summary = data.get("summary", {})
if isinstance(summary, dict) and key in summary:
return summary[key]
return default
def collect_string_values(value: Any) -> list[str]:
if isinstance(value, str):
return [value]
if isinstance(value, list):
values: list[str] = []
for item in value:
values.extend(collect_string_values(item))
return values
if isinstance(value, dict):
values: list[str] = []
for item in value.values():
values.extend(collect_string_values(item))
return values
return []
def validate_no_forbidden_text(report: dict[str, Any]) -> None:
for text in collect_string_values(report):
for pattern in FORBIDDEN_TEXT_PATTERNS:
if pattern.search(text):
raise SystemExit("BLOCKED iwooos_p0_security_incident_convergence_gate: forbidden sensitive text detected")
def bool_int(value: Any) -> int:
return 1 if value else 0
def build_lane(definition: dict[str, Any], snapshots: dict[str, dict[str, Any]]) -> dict[str, Any]:
source_refs = [SOURCE_SNAPSHOTS[key] for key in definition["source_keys"]]
return {
"lane_id": definition["lane_id"],
"priority": definition["priority"],
"label": definition["label"],
"status": "blocked_waiting_owner_evidence",
"source_snapshot_refs": source_refs,
"next_gate": definition["next_gate"],
"required_evidence": [
{"evidence_id": evidence_id, "accepted": False}
for evidence_id in definition["required_evidence"]
],
"owner_response_received": False,
"owner_response_accepted": False,
"redacted_evidence_received": False,
"redacted_evidence_accepted": False,
"runtime_authorized": False,
"action_buttons_allowed": False,
"not_authorization": True,
"source_statuses": {
key: snapshots[key].get("status", "unknown")
for key in definition["source_keys"]
},
}
def build_report(root: Path, generated_at: str | None) -> dict[str, Any]:
report_time = generated_at or datetime.now(TAIPEI).isoformat(timespec="seconds")
snapshots = {key: load_json(root, path) for key, path in SOURCE_SNAPSHOTS.items()}
lanes = [build_lane(definition, snapshots) for definition in P0_LANE_DEFINITIONS]
wazuh_runtime = snapshots["wazuh_runtime"]
wazuh_coverage = snapshots["wazuh_coverage"]
wazuh_intrusion = snapshots["wazuh_intrusion"]
public_gateway = snapshots["public_gateway"]
public_gateway_post = snapshots["public_gateway_post_incident"]
ssh_network = snapshots["ssh_network_post_incident"]
port_firewall = snapshots["port_firewall"]
host_service = snapshots["host_service_post_incident"]
monitoring = snapshots["monitoring_post_incident"]
soc = snapshots["soc_integration"]
high_value = snapshots["high_value_config"]
external_host = snapshots["external_host"]
owner_received_total = sum(
int(nested_get(snapshot, "owner_response_received_count", 0) or 0)
for snapshot in snapshots.values()
)
owner_accepted_total = sum(
int(nested_get(snapshot, "owner_response_accepted_count", 0) or 0)
for snapshot in snapshots.values()
)
runtime_gate_total = sum(
int(nested_get(snapshot, "runtime_gate_count", 0) or 0)
for snapshot in snapshots.values()
)
action_button_total = sum(
int(nested_get(snapshot, "action_button_count", 0) or 0)
for snapshot in snapshots.values()
)
return {
"schema_version": SCHEMA_VERSION,
"generated_at": report_time,
"git_commit": git_short_sha(root),
"status": "p0_security_incident_convergence_blocked_waiting_owner_evidence",
"mode": "snapshot_rollup_only_no_runtime_no_secret_collection",
"source_snapshot_refs": SOURCE_SNAPSHOTS,
"summary": {
"source_snapshot_count": len(SOURCE_SNAPSHOTS),
"p0_lane_count": len(lanes),
"blocked_lane_count": len(lanes),
"source_side_rollup_ready_percent": 100,
"owner_response_received_count": owner_received_total,
"owner_response_accepted_count": owner_accepted_total,
"redacted_evidence_received_count": 0,
"redacted_evidence_accepted_count": 0,
"dashboard_api_connection_ok_count": int(nested_get(wazuh_runtime, "dashboard_api_connection_ok_count", 0) or 0),
"dashboard_api_version_ok_count": int(nested_get(wazuh_runtime, "dashboard_api_version_ok_count", 0) or 0),
"dashboard_index_pattern_ok_count": int(nested_get(wazuh_runtime, "dashboard_index_pattern_ok_count", 0) or 0),
"manager_registry_accepted_count": int(nested_get(wazuh_coverage, "manager_registry_accepted_count", 0) or 0),
"expected_host_scope_count": int(nested_get(wazuh_coverage, "expected_host_scope_count", 0) or 0),
"direct_agent_active_observed_count": int(nested_get(wazuh_coverage, "direct_agent_active_observed_count", 0) or 0),
"wazuh_event_ref_received_count": int(nested_get(wazuh_intrusion, "wazuh_event_ref_received_count", 0) or 0),
"host_forensics_ref_received_count": int(nested_get(wazuh_intrusion, "host_forensics_ref_received_count", 0) or 0),
"containment_decision_accepted_count": int(nested_get(wazuh_intrusion, "containment_decision_accepted_count", 0) or 0),
"recovery_proof_accepted_count": int(nested_get(wazuh_intrusion, "recovery_proof_accepted_count", 0) or 0),
"public_gateway_live_conf_received_count": int(nested_get(public_gateway, "owner_provided_live_conf_received_count", 0) or 0),
"public_gateway_rendered_diff_ready_count": int(nested_get(public_gateway, "rendered_diff_ready_count", 0) or 0),
"nginx_test_evidence_count": int(nested_get(public_gateway, "nginx_test_evidence_count", 0) or 0),
"route_smoke_evidence_count": int(nested_get(public_gateway, "route_smoke_evidence_count", 0) or 0),
"gateway_post_incident_readback_received_count": int(nested_get(public_gateway_post, "post_incident_readback_received_count", 0) or 0),
"ssh_network_post_incident_readback_received_count": int(nested_get(ssh_network, "post_incident_readback_received_count", 0) or 0),
"port_firewall_change_evidence_received_count": int(nested_get(port_firewall, "change_evidence_received_count", 0) or 0),
"host_service_post_incident_readback_received_count": int(nested_get(host_service, "post_incident_readback_received_count", 0) or 0),
"monitoring_post_incident_readback_received_count": int(nested_get(monitoring, "post_incident_readback_received_count", 0) or 0),
"alert_route_accepted_count": int(nested_get(soc, "alert_route_accepted_count", 0) or 0),
"incident_case_accepted_count": int(nested_get(soc, "incident_case_accepted_count", 0) or 0),
"high_value_config_category_count": int(nested_get(high_value, "category_count", 0) or 0),
"high_value_config_average_coverage_percent": int(nested_get(high_value, "average_coverage_percent", 0) or 0),
"external_host_prevention_candidate_count": int(nested_get(external_host, "control_candidate_count", 0) or 0),
"external_host_coverage_percent": int(nested_get(external_host, "coverage_percent_after_prevention_control", 0) or 0),
"wazuh_active_response_authorized_count": 0,
"kali_active_scan_authorized_count": 0,
"host_write_authorized_count": 0,
"firewall_change_authorized_count": 0,
"nginx_reload_authorized_count": 0,
"runtime_gate_count": runtime_gate_total,
"action_button_count": action_button_total,
},
"p0_lanes": lanes,
"blocked_runtime_actions": BLOCKED_RUNTIME_ACTIONS,
"execution_boundaries": {
"wazuh_api_live_query_authorized": False,
"wazuh_active_response_authorized": False,
"kali_active_scan_authorized": False,
"kali_execute_authorized": False,
"ssh_read_authorized": False,
"ssh_write_authorized": False,
"host_write_authorized": False,
"firewall_change_authorized": False,
"nginx_test_authorized": False,
"nginx_reload_authorized": False,
"docker_restart_authorized": False,
"systemctl_restart_authorized": False,
"argocd_sync_authorized": False,
"workflow_modification_authorized": False,
"repo_secret_change_authorized": False,
"secret_value_collection_allowed": False,
"telegram_send_authorized": False,
"soar_case_create_authorized": False,
"auto_block_authorized": False,
"production_write_authorized": False,
"runtime_execution_authorized": False,
"action_buttons_allowed": False,
"not_authorization": True,
},
"operator_interpretation": [
"這張 Gate 是 P0 事件彙總,不是 runtime 修復授權。",
"Wazuh Dashboard index pattern 綠燈只能當局部訊號API connection、API version 與 manager registry 仍是硬 Gate。",
"Nginx、firewall、host runtime、monitoring 與 SOC evidence 必須用脫敏 owner refs 補齊,不能貼 raw log 或工作視窗內容。",
"所有 containment、active response、scan、reload、restart、secret rotation 與 production write 仍需獨立人工批准。",
],
}
def validate(report: dict[str, Any]) -> None:
if report.get("schema_version") != SCHEMA_VERSION:
raise SystemExit("BLOCKED schema_version")
if report.get("status") != "p0_security_incident_convergence_blocked_waiting_owner_evidence":
raise SystemExit("BLOCKED status")
summary = report["summary"]
expected_zero_keys = [
"owner_response_received_count",
"owner_response_accepted_count",
"redacted_evidence_received_count",
"redacted_evidence_accepted_count",
"dashboard_api_connection_ok_count",
"dashboard_api_version_ok_count",
"manager_registry_accepted_count",
"wazuh_event_ref_received_count",
"host_forensics_ref_received_count",
"containment_decision_accepted_count",
"public_gateway_live_conf_received_count",
"public_gateway_rendered_diff_ready_count",
"nginx_test_evidence_count",
"route_smoke_evidence_count",
"gateway_post_incident_readback_received_count",
"ssh_network_post_incident_readback_received_count",
"port_firewall_change_evidence_received_count",
"host_service_post_incident_readback_received_count",
"monitoring_post_incident_readback_received_count",
"alert_route_accepted_count",
"incident_case_accepted_count",
"wazuh_active_response_authorized_count",
"kali_active_scan_authorized_count",
"host_write_authorized_count",
"firewall_change_authorized_count",
"nginx_reload_authorized_count",
"runtime_gate_count",
"action_button_count",
]
for key in expected_zero_keys:
if summary.get(key) != 0:
raise SystemExit(f"BLOCKED summary.{key}: expected 0, got {summary.get(key)!r}")
if summary.get("source_snapshot_count") != len(SOURCE_SNAPSHOTS):
raise SystemExit("BLOCKED source_snapshot_count")
if summary.get("p0_lane_count") != len(P0_LANE_DEFINITIONS):
raise SystemExit("BLOCKED p0_lane_count")
if summary.get("blocked_lane_count") != len(P0_LANE_DEFINITIONS):
raise SystemExit("BLOCKED blocked_lane_count")
for lane in report.get("p0_lanes", []):
if lane.get("status") != "blocked_waiting_owner_evidence":
raise SystemExit(f"BLOCKED lane.status: {lane.get('lane_id')}")
for field in [
"owner_response_received",
"owner_response_accepted",
"redacted_evidence_received",
"redacted_evidence_accepted",
"runtime_authorized",
"action_buttons_allowed",
]:
if lane.get(field) is not False:
raise SystemExit(f"BLOCKED lane.{field}: {lane.get('lane_id')}")
for key, value in report.get("execution_boundaries", {}).items():
if key == "not_authorization":
if value is not True:
raise SystemExit("BLOCKED execution_boundaries.not_authorization")
elif value is not False:
raise SystemExit(f"BLOCKED execution_boundaries.{key}")
validate_no_forbidden_text(report)
def main() -> int:
parser = argparse.ArgumentParser(description="IwoooS P0 資安事件收斂 Gate")
parser.add_argument("--root", default=".", help="repo root")
parser.add_argument("--output", help="寫出 JSON 報告")
parser.add_argument("--generated-at", help="固定報告時間,供 committed snapshot 使用")
parser.add_argument("--json", action="store_true")
args = parser.parse_args()
root = Path(args.root).resolve()
report = build_report(root, args.generated_at)
validate(report)
payload = json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True)
if args.output:
output = Path(args.output)
if not output.is_absolute():
output = root / output
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(payload + "\n", encoding="utf-8")
if args.json or not args.output:
print(payload)
summary = report["summary"]
print(
"IWOOOS_P0_SECURITY_INCIDENT_CONVERGENCE_GATE_OK "
f"sources={summary['source_snapshot_count']} "
f"lanes={summary['p0_lane_count']} "
f"blocked={summary['blocked_lane_count']} "
f"registry={summary['manager_registry_accepted_count']} "
f"evidence={summary['redacted_evidence_accepted_count']} "
f"runtime_gate={summary['runtime_gate_count']}",
file=sys.stderr,
)
return 0
if __name__ == "__main__":
sys.exit(main())