feat(iwooos): add Wazuh owner evidence preflight
This commit is contained in:
@@ -1,3 +1,27 @@
|
||||
## 2026-06-25|Wazuh agent registry owner evidence 收件預檢
|
||||
|
||||
**背景**:Wazuh 用戶端消失事故的下一個關鍵不是再看 Dashboard,而是讓 owner 能用脫敏、可驗收的格式提供 manager registry truth。若沒有收件 preflight,agent 數字、截圖、raw log 或口頭回覆都容易被誤當成驗收證據。
|
||||
|
||||
**完成**:
|
||||
- 新增 `scripts/security/wazuh-agent-visibility-owner-evidence-preflight.py`。
|
||||
- 新增 `docs/security/wazuh-agent-visibility-owner-evidence-preflight.snapshot.json`。
|
||||
- 新增人讀版 `docs/security/WAZUH-AGENT-VISIBILITY-OWNER-EVIDENCE-PREFLIGHT.md`。
|
||||
- 收件欄位固定為 18 項:owner、decision、scope、agent counts、last_seen window、manager health ref、dashboard API status ref、redacted evidence refs、followup、rollback 與 postcheck。
|
||||
- reviewer checks 固定為 7 項,outcome lanes 固定為 5 條,forbidden payloads 固定為 18 項。
|
||||
- `security-mirror-progress-guard.py` 已納入此 preflight;`received / accepted / runtime_gate` 全部仍為 `0`。
|
||||
|
||||
**驗證**:
|
||||
- `python3 scripts/security/wazuh-agent-visibility-owner-evidence-preflight.py --root .`:`fields=18 checks=7 received=0 accepted=0 runtime_gate=0`。
|
||||
- `python3 scripts/security/security-mirror-progress-guard.py --root .`:待提交前總驗證。
|
||||
|
||||
**完成度同步**:
|
||||
- Wazuh agent visibility owner evidence preflight:`100%` source-side。
|
||||
- Wazuh P0-A manager registry 只讀驗收:`48% -> 56%` source-side、`0%` live registry accepted。
|
||||
- SOC / Wazuh no-false-green 納管:`66% -> 69%`。
|
||||
- production deploy、live Wazuh registry accepted、Dashboard stored API 修復、owner response accepted、active response / host write:仍維持 `0%`。
|
||||
|
||||
**邊界**:本輪沒有查 Wazuh、沒有 SSH、沒有讀 secret、沒有保存 raw log、沒有重新註冊 agent、沒有送 Telegram、沒有部署,也沒有啟用 active response。
|
||||
|
||||
## 2026-06-25|IwoooS Wazuh 正式路由只讀讀回前台
|
||||
|
||||
**背景**:IwoooS 前台已有 Wazuh 入侵回讀與環境閘門,但缺少直接顯示 `/api/iwooos/wazuh` 目前讀回狀態的卡片;operator 仍可能把「Wazuh 已建置」誤解成「正式路由已部署、agent registry 已驗收」。
|
||||
|
||||
@@ -58,6 +58,8 @@
|
||||
|
||||
本輪再補 IwoooS Wazuh API source-side no-false-green:`/api/iwooos/wazuh` 與 `/api/v1/iwooos/wazuh` 在 `agent_total=0` 時回 `wazuh_agent_registry_empty`,在低於 server-side `IWOOOS_WAZUH_EXPECTED_MIN_AGENT_COUNT` 時回 `wazuh_agent_registry_below_expected`。這只改判讀與 metadata,不代表 production 已部署、manager registry 已驗收或 active response 已授權。
|
||||
|
||||
本輪另新增 Wazuh agent visibility owner evidence preflight:`scripts/security/wazuh-agent-visibility-owner-evidence-preflight.py`、`docs/security/wazuh-agent-visibility-owner-evidence-preflight.snapshot.json` 與人讀版 `docs/security/WAZUH-AGENT-VISIBILITY-OWNER-EVIDENCE-PREFLIGHT.md`。它固定 owner 必要欄位、reviewer 檢查、拒收分流與禁止 payload;`received / accepted / runtime_gate` 仍全部為 `0`。
|
||||
|
||||
## 4. 立即凍結邊界
|
||||
|
||||
在 manager 端 agent registry 被只讀驗收前,以下全部維持禁止:
|
||||
@@ -73,7 +75,7 @@
|
||||
|
||||
| 優先 | 工作 | 驗收條件 | 目前完成度 |
|
||||
|------|------|----------|------------|
|
||||
| P0-A | Wazuh manager agent registry 只讀驗收 | owner 提供脫敏 `agent_total / active / disconnected / last_seen` ref,或經 server-side secret metadata 啟用 IwoooS 只讀 API;source-side 已能把 registry 空或低於預期判成退化 | `48%` source-side、`0%` live registry accepted |
|
||||
| P0-A | Wazuh manager agent registry 只讀驗收 | owner 提供脫敏 `agent_total / active / disconnected / last_seen` ref,或經 server-side secret metadata 啟用 IwoooS 只讀 API;source-side 已能把 registry 空或低於預期判成退化,並新增 owner evidence 收件預檢 | `56%` source-side、`0%` live registry accepted |
|
||||
| P0-B | Dashboard stored API / rate-limit / TLS trust 修復 gate | 查明 `/api/check-stored-api` 429/500 根因;維修前有 owner、rollback、postcheck;維修後 Dashboard 與 API count 一致 | `35%` |
|
||||
| P0-C | IwoooS live metadata route 正式部署 | `/api/iwooos/wazuh` 不再 404,回傳 schema `iwooos_wazuh_readonly_status_v1`,不洩漏 agent identity / internal IP / secret | `55%` source-side、`0%` production |
|
||||
| P0-D | Wazuh agent disappearance alert card | 產出 `ai_automation_alert_card_v1`,包含 agent count delta、Dashboard API status、manager health、next gate、owner;本輪已新增 `wazuh_dashboard_api_readback_degraded` formatter / test / guard、AwoooP `/runs/ai-alert-cards` delivery readback contract 與 Runs 前台面板 | `92%` source-side、`0%` production receipt |
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
# Wazuh Agent Visibility Owner Evidence 收件預檢
|
||||
|
||||
> 狀態:source-side preflight ready。這不是 owner evidence received、不是 accepted、不是 runtime authorization。
|
||||
|
||||
## 目的
|
||||
|
||||
Wazuh 用戶端消失事故不能用 Dashboard 畫面、代理服務在線、TCP 連線存在或口頭宣稱結案。IwoooS 需要的是 Wazuh manager registry 的脫敏計數與時間窗,才能判斷代理清單是否真的恢復。
|
||||
|
||||
本預檢定義 owner 要提供的欄位、reviewer 要檢查的項目、拒收分流與禁止 payload。它不查 Wazuh、不 SSH、不讀 secret、不保存 raw log、不啟用 active response,也不開任何主機寫入。
|
||||
|
||||
## 必要欄位
|
||||
|
||||
- `owner_role`
|
||||
- `team`
|
||||
- `decision`
|
||||
- `decision_reason`
|
||||
- `affected_scope`
|
||||
- `agent_total`
|
||||
- `agent_active`
|
||||
- `agent_disconnected`
|
||||
- `agent_never_connected`
|
||||
- `last_seen_window_start`
|
||||
- `last_seen_window_end`
|
||||
- `registry_collected_at`
|
||||
- `manager_health_ref`
|
||||
- `dashboard_api_status_ref`
|
||||
- `redacted_evidence_refs`
|
||||
- `followup_owner`
|
||||
- `rollback_owner`
|
||||
- `postcheck_plan`
|
||||
|
||||
## Reviewer 檢查
|
||||
|
||||
- 欄位齊全且皆為脫敏 metadata。
|
||||
- `agent_total` 不可小於 `agent_active + agent_disconnected + agent_never_connected`。
|
||||
- `last_seen` 時間窗需能覆蓋事故觀察區間。
|
||||
- manager health ref 與 dashboard API status ref 不可互相替代。
|
||||
- redacted evidence refs 不得包含 raw payload、截圖原文或主機完整輸出。
|
||||
- owner decision 不可直接授權 active response、host write 或 secret rotation。
|
||||
- rollback owner 與 postcheck plan 必須存在。
|
||||
|
||||
## 拒收與隔離
|
||||
|
||||
以下內容不得進 repo、前台、LOGBOOK 或 AwoooP 訊息本文:
|
||||
|
||||
- raw Wazuh payload、raw log、完整 journal、未脫敏截圖。
|
||||
- agent 原名、內網位址、完整主機輸出。
|
||||
- authorization header、token、basic auth、password、cookie、private key、client key。
|
||||
- 夾帶 active response、host write、firewall change、Nginx reload 或其他 runtime 操作要求。
|
||||
|
||||
## 現況計數
|
||||
|
||||
| 項目 | 計數 |
|
||||
|---|---:|
|
||||
| 必要欄位 | `18` |
|
||||
| Reviewer 檢查 | `7` |
|
||||
| Outcome lanes | `5` |
|
||||
| Forbidden payloads | `18` |
|
||||
| Owner evidence received | `0` |
|
||||
| Owner evidence accepted | `0` |
|
||||
| Runtime gate | `0` |
|
||||
|
||||
## 驗證
|
||||
|
||||
```bash
|
||||
python3 scripts/security/wazuh-agent-visibility-owner-evidence-preflight.py --root .
|
||||
```
|
||||
|
||||
預期:
|
||||
|
||||
```text
|
||||
WAZUH_AGENT_VISIBILITY_OWNER_EVIDENCE_PREFLIGHT_OK fields=18 checks=7 received=0 accepted=0 runtime_gate=0
|
||||
```
|
||||
|
||||
## 邊界
|
||||
|
||||
本預檢通過只代表收件格式和安全邊界已固定,不代表 Wazuh agent registry 已驗收、不代表 Dashboard stored API 已修復、不代表 IwoooS production route 已部署、不代表主機乾淨,也不授權 active response、host write、Kali active scan、secret rotation、Nginx / Docker / K8s / firewall 操作。
|
||||
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"execution_boundaries": {
|
||||
"agent_identity_public_display_allowed": false,
|
||||
"host_write_authorized": false,
|
||||
"internal_ip_public_display_allowed": false,
|
||||
"not_authorization": true,
|
||||
"raw_wazuh_payload_storage_allowed": false,
|
||||
"runtime_execution_authorized": false,
|
||||
"secret_value_collection_allowed": false,
|
||||
"wazuh_active_response_authorized": false,
|
||||
"wazuh_api_live_query_authorized": false
|
||||
},
|
||||
"forbidden_payloads": [
|
||||
"raw_wazuh_payload",
|
||||
"raw_log",
|
||||
"full_journal",
|
||||
"unredacted_screenshot",
|
||||
"agent_name",
|
||||
"internal_ip",
|
||||
"authorization_header",
|
||||
"bearer_token",
|
||||
"basic_auth",
|
||||
"password",
|
||||
"cookie",
|
||||
"private_key",
|
||||
"client_keys",
|
||||
"active_response_enable",
|
||||
"host_write",
|
||||
"firewall_change",
|
||||
"nginx_reload"
|
||||
],
|
||||
"mode": "redacted_metadata_only_no_secret_no_wazuh_query",
|
||||
"operator_interpretation": [
|
||||
"這是 Wazuh agent registry 脫敏證據收件前的預檢,不代表已收到或已接受 owner evidence。",
|
||||
"agent service active、TCP 連線存在、Dashboard 可見或口頭宣稱都不可替代 manager registry counts。",
|
||||
"若 evidence 夾帶 raw log、未脫敏截圖、內網位址、agent 原名或 secret,必須隔離,不得渲染到前台。",
|
||||
"任何 active response、host write、firewall、Nginx、Docker、K8s 或 secret 變更都要切獨立人工批准。"
|
||||
],
|
||||
"outcome_lanes": [
|
||||
"waiting_owner_evidence",
|
||||
"request_missing_fields",
|
||||
"quarantine_sensitive_payload",
|
||||
"reject_runtime_action_request",
|
||||
"ready_for_reviewer_validation"
|
||||
],
|
||||
"required_fields": [
|
||||
"owner_role",
|
||||
"team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
"agent_total",
|
||||
"agent_active",
|
||||
"agent_disconnected",
|
||||
"agent_never_connected",
|
||||
"last_seen_window_start",
|
||||
"last_seen_window_end",
|
||||
"registry_collected_at",
|
||||
"manager_health_ref",
|
||||
"dashboard_api_status_ref",
|
||||
"redacted_evidence_refs",
|
||||
"followup_owner",
|
||||
"rollback_owner",
|
||||
"postcheck_plan"
|
||||
],
|
||||
"reviewer_checks": [
|
||||
"欄位齊全且皆為脫敏 metadata。",
|
||||
"agent_total 不可小於 active + disconnected + never_connected。",
|
||||
"last_seen 時間窗需能覆蓋事故觀察區間。",
|
||||
"manager health ref 與 dashboard API status ref 不可互相替代。",
|
||||
"redacted evidence refs 不得包含 raw payload、截圖原文或主機完整輸出。",
|
||||
"owner decision 不可直接授權 active response、host write 或 secret rotation。",
|
||||
"rollback owner 與 postcheck plan 必須存在。"
|
||||
],
|
||||
"schema_version": "wazuh_agent_visibility_owner_evidence_preflight_v1",
|
||||
"scope": "wazuh_agent_visibility_registry_truth",
|
||||
"status": "owner_evidence_preflight_ready_no_runtime_action",
|
||||
"summary": {
|
||||
"active_response_authorized_count": 0,
|
||||
"forbidden_payload_count": 17,
|
||||
"host_write_authorized_count": 0,
|
||||
"outcome_lane_count": 5,
|
||||
"owner_evidence_accepted_count": 0,
|
||||
"owner_evidence_quarantined_count": 0,
|
||||
"owner_evidence_received_count": 0,
|
||||
"owner_evidence_rejected_count": 0,
|
||||
"required_field_count": 18,
|
||||
"reviewer_check_count": 7,
|
||||
"runtime_gate_count": 0,
|
||||
"secret_value_collection_allowed_count": 0
|
||||
}
|
||||
}
|
||||
@@ -115,6 +115,10 @@ def validate(root: Path) -> None:
|
||||
str(root / "scripts" / "security" / "wazuh-agent-visibility-runtime-gate.py")
|
||||
)
|
||||
wazuh_agent_visibility_runtime_gate["validate"](root)
|
||||
wazuh_agent_visibility_owner_evidence_preflight = runpy.run_path(
|
||||
str(root / "scripts" / "security" / "wazuh-agent-visibility-owner-evidence-preflight.py")
|
||||
)
|
||||
wazuh_agent_visibility_owner_evidence_preflight["validate"](root)
|
||||
telegram_alert_readability_guard = runpy.run_path(
|
||||
str(root / "scripts" / "security" / "telegram-alert-readability-guard.py")
|
||||
)
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Wazuh agent visibility owner evidence 收件預檢。
|
||||
|
||||
本工具只驗證 repo 內 committed snapshot;不查 Wazuh、不 SSH、不讀
|
||||
secret、不保存 raw log、不重新註冊 agent,也不啟用 active response。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
SNAPSHOT_PATH = Path("docs/security/wazuh-agent-visibility-owner-evidence-preflight.snapshot.json")
|
||||
SCHEMA_VERSION = "wazuh_agent_visibility_owner_evidence_preflight_v1"
|
||||
|
||||
REQUIRED_FIELDS = [
|
||||
"owner_role",
|
||||
"team",
|
||||
"decision",
|
||||
"decision_reason",
|
||||
"affected_scope",
|
||||
"agent_total",
|
||||
"agent_active",
|
||||
"agent_disconnected",
|
||||
"agent_never_connected",
|
||||
"last_seen_window_start",
|
||||
"last_seen_window_end",
|
||||
"registry_collected_at",
|
||||
"manager_health_ref",
|
||||
"dashboard_api_status_ref",
|
||||
"redacted_evidence_refs",
|
||||
"followup_owner",
|
||||
"rollback_owner",
|
||||
"postcheck_plan",
|
||||
]
|
||||
|
||||
REVIEWER_CHECKS = [
|
||||
"欄位齊全且皆為脫敏 metadata。",
|
||||
"agent_total 不可小於 active + disconnected + never_connected。",
|
||||
"last_seen 時間窗需能覆蓋事故觀察區間。",
|
||||
"manager health ref 與 dashboard API status ref 不可互相替代。",
|
||||
"redacted evidence refs 不得包含 raw payload、截圖原文或主機完整輸出。",
|
||||
"owner decision 不可直接授權 active response、host write 或 secret rotation。",
|
||||
"rollback owner 與 postcheck plan 必須存在。",
|
||||
]
|
||||
|
||||
OUTCOME_LANES = [
|
||||
"waiting_owner_evidence",
|
||||
"request_missing_fields",
|
||||
"quarantine_sensitive_payload",
|
||||
"reject_runtime_action_request",
|
||||
"ready_for_reviewer_validation",
|
||||
]
|
||||
|
||||
FORBIDDEN_PAYLOADS = [
|
||||
"raw_wazuh_payload",
|
||||
"raw_log",
|
||||
"full_journal",
|
||||
"unredacted_screenshot",
|
||||
"agent_name",
|
||||
"internal_ip",
|
||||
"authorization_header",
|
||||
"bearer_token",
|
||||
"basic_auth",
|
||||
"password",
|
||||
"cookie",
|
||||
"private_key",
|
||||
"client_keys",
|
||||
"active_response_enable",
|
||||
"host_write",
|
||||
"firewall_change",
|
||||
"nginx_reload",
|
||||
]
|
||||
|
||||
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"client\.keys", re.IGNORECASE),
|
||||
re.compile(r"-----BEGIN [A-Z ]*PRIVATE KEY-----"),
|
||||
]
|
||||
|
||||
|
||||
def load_json(path: Path) -> dict[str, Any]:
|
||||
return json.loads(path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def assert_equal(label: str, actual: Any, expected: Any) -> None:
|
||||
if actual != expected:
|
||||
raise SystemExit(f"BLOCKED {label}: expected {expected!r}, got {actual!r}")
|
||||
|
||||
|
||||
def assert_false(label: str, actual: Any) -> None:
|
||||
assert_equal(label, actual, False)
|
||||
|
||||
|
||||
def assert_zero(label: str, actual: Any) -> None:
|
||||
assert_equal(label, actual, 0)
|
||||
|
||||
|
||||
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 = []
|
||||
for item in value.values():
|
||||
values.extend(collect_string_values(item))
|
||||
return values
|
||||
return []
|
||||
|
||||
|
||||
def validate_no_forbidden_text(snapshot: dict[str, Any]) -> None:
|
||||
for text in collect_string_values(snapshot):
|
||||
for pattern in FORBIDDEN_TEXT_PATTERNS:
|
||||
if pattern.search(text):
|
||||
raise SystemExit(
|
||||
"BLOCKED wazuh_agent_visibility_owner_evidence_preflight: "
|
||||
"snapshot contains forbidden sensitive text"
|
||||
)
|
||||
|
||||
|
||||
def build_snapshot() -> dict[str, Any]:
|
||||
return {
|
||||
"schema_version": SCHEMA_VERSION,
|
||||
"status": "owner_evidence_preflight_ready_no_runtime_action",
|
||||
"mode": "redacted_metadata_only_no_secret_no_wazuh_query",
|
||||
"scope": "wazuh_agent_visibility_registry_truth",
|
||||
"required_fields": REQUIRED_FIELDS,
|
||||
"reviewer_checks": REVIEWER_CHECKS,
|
||||
"outcome_lanes": OUTCOME_LANES,
|
||||
"forbidden_payloads": FORBIDDEN_PAYLOADS,
|
||||
"summary": {
|
||||
"required_field_count": len(REQUIRED_FIELDS),
|
||||
"reviewer_check_count": len(REVIEWER_CHECKS),
|
||||
"outcome_lane_count": len(OUTCOME_LANES),
|
||||
"forbidden_payload_count": len(FORBIDDEN_PAYLOADS),
|
||||
"owner_evidence_received_count": 0,
|
||||
"owner_evidence_accepted_count": 0,
|
||||
"owner_evidence_rejected_count": 0,
|
||||
"owner_evidence_quarantined_count": 0,
|
||||
"runtime_gate_count": 0,
|
||||
"active_response_authorized_count": 0,
|
||||
"host_write_authorized_count": 0,
|
||||
"secret_value_collection_allowed_count": 0,
|
||||
},
|
||||
"execution_boundaries": {
|
||||
"wazuh_api_live_query_authorized": False,
|
||||
"wazuh_active_response_authorized": False,
|
||||
"host_write_authorized": False,
|
||||
"secret_value_collection_allowed": False,
|
||||
"raw_wazuh_payload_storage_allowed": False,
|
||||
"agent_identity_public_display_allowed": False,
|
||||
"internal_ip_public_display_allowed": False,
|
||||
"runtime_execution_authorized": False,
|
||||
"not_authorization": True,
|
||||
},
|
||||
"operator_interpretation": [
|
||||
"這是 Wazuh agent registry 脫敏證據收件前的預檢,不代表已收到或已接受 owner evidence。",
|
||||
"agent service active、TCP 連線存在、Dashboard 可見或口頭宣稱都不可替代 manager registry counts。",
|
||||
"若 evidence 夾帶 raw log、未脫敏截圖、內網位址、agent 原名或 secret,必須隔離,不得渲染到前台。",
|
||||
"任何 active response、host write、firewall、Nginx、Docker、K8s 或 secret 變更都要切獨立人工批准。",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def validate(root: Path) -> None:
|
||||
snapshot = load_json(root / SNAPSHOT_PATH)
|
||||
assert_equal("schema_version", snapshot.get("schema_version"), SCHEMA_VERSION)
|
||||
assert_equal(
|
||||
"status",
|
||||
snapshot.get("status"),
|
||||
"owner_evidence_preflight_ready_no_runtime_action",
|
||||
)
|
||||
assert_equal(
|
||||
"mode",
|
||||
snapshot.get("mode"),
|
||||
"redacted_metadata_only_no_secret_no_wazuh_query",
|
||||
)
|
||||
assert_equal("required_fields", snapshot.get("required_fields"), REQUIRED_FIELDS)
|
||||
assert_equal("reviewer_checks", snapshot.get("reviewer_checks"), REVIEWER_CHECKS)
|
||||
assert_equal("outcome_lanes", snapshot.get("outcome_lanes"), OUTCOME_LANES)
|
||||
assert_equal("forbidden_payloads", snapshot.get("forbidden_payloads"), FORBIDDEN_PAYLOADS)
|
||||
|
||||
summary = snapshot.get("summary", {})
|
||||
assert_equal("summary.required_field_count", summary.get("required_field_count"), len(REQUIRED_FIELDS))
|
||||
assert_equal("summary.reviewer_check_count", summary.get("reviewer_check_count"), len(REVIEWER_CHECKS))
|
||||
assert_equal("summary.outcome_lane_count", summary.get("outcome_lane_count"), len(OUTCOME_LANES))
|
||||
assert_equal("summary.forbidden_payload_count", summary.get("forbidden_payload_count"), len(FORBIDDEN_PAYLOADS))
|
||||
for key in [
|
||||
"owner_evidence_received_count",
|
||||
"owner_evidence_accepted_count",
|
||||
"owner_evidence_rejected_count",
|
||||
"owner_evidence_quarantined_count",
|
||||
"runtime_gate_count",
|
||||
"active_response_authorized_count",
|
||||
"host_write_authorized_count",
|
||||
"secret_value_collection_allowed_count",
|
||||
]:
|
||||
assert_zero(f"summary.{key}", summary.get(key))
|
||||
|
||||
boundaries = snapshot.get("execution_boundaries", {})
|
||||
for key, value in boundaries.items():
|
||||
if key == "not_authorization":
|
||||
assert_equal(f"execution_boundaries.{key}", value, True)
|
||||
else:
|
||||
assert_false(f"execution_boundaries.{key}", value)
|
||||
validate_no_forbidden_text(snapshot)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Wazuh agent visibility owner evidence 收件預檢")
|
||||
parser.add_argument("--root", type=Path, default=Path.cwd())
|
||||
parser.add_argument("--output", type=Path)
|
||||
parser.add_argument("--json", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
root = args.root.resolve()
|
||||
if args.output:
|
||||
snapshot = build_snapshot()
|
||||
output = args.output if args.output.is_absolute() else root / args.output
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(snapshot, ensure_ascii=False, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
|
||||
validate(root)
|
||||
snapshot = load_json(root / SNAPSHOT_PATH)
|
||||
if args.json:
|
||||
print(json.dumps(snapshot, ensure_ascii=False, sort_keys=True))
|
||||
return
|
||||
summary = snapshot["summary"]
|
||||
print(
|
||||
"WAZUH_AGENT_VISIBILITY_OWNER_EVIDENCE_PREFLIGHT_OK "
|
||||
f"fields={summary['required_field_count']} "
|
||||
f"checks={summary['reviewer_check_count']} "
|
||||
f"received={summary['owner_evidence_received_count']} "
|
||||
f"accepted={summary['owner_evidence_accepted_count']} "
|
||||
f"runtime_gate={summary['runtime_gate_count']}"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user