|
| 1 | +"""POST /ingest/wazuh — Wazuh 경보 인제스트 인증/검증 게이트 테스트. |
| 2 | +
|
| 3 | +전체 적재(→PostgreSQL)는 postgres 백엔드가 필요하므로 여기서는 인증/본문검증 |
| 4 | +경로만 확인(항상 실행). 실적재는 라이브 postgres 에서 별도 검증됨. |
| 5 | +""" |
| 6 | +from __future__ import annotations |
| 7 | + |
| 8 | +import importlib.util |
| 9 | +import os |
| 10 | +import unittest |
| 11 | +from datetime import datetime, timezone |
| 12 | +from unittest.mock import patch |
| 13 | + |
| 14 | +from mori_soc.models import Host |
| 15 | +from mori_soc.services.query_service import InMemoryQueryStore, QueryService |
| 16 | + |
| 17 | +FASTAPI_AVAILABLE = importlib.util.find_spec("fastapi") is not None |
| 18 | + |
| 19 | +_ALERT = { |
| 20 | + "timestamp": "2026-07-09T10:00:00.000+0000", |
| 21 | + "agent": {"id": "001", "name": "web-01", "ip": "10.0.0.15"}, |
| 22 | + "rule": {"id": "5710", "level": 10, "description": "SSH brute force"}, |
| 23 | + "id": "1752055200.1", |
| 24 | +} |
| 25 | + |
| 26 | + |
| 27 | +@unittest.skipUnless(FASTAPI_AVAILABLE, "requires fastapi") |
| 28 | +class IngestWazuhTests(unittest.TestCase): |
| 29 | + def setUp(self) -> None: |
| 30 | + from fastapi.testclient import TestClient |
| 31 | + |
| 32 | + from mori_soc.api.server import create_app |
| 33 | + |
| 34 | + store = InMemoryQueryStore( |
| 35 | + hosts=[Host(host_id="h1", hostname="web-01", status="online", |
| 36 | + last_seen_at=datetime.now(tz=timezone.utc))] |
| 37 | + ) |
| 38 | + with patch.dict(os.environ, {"MORI_DEMO_SEED": "0"}, clear=False): |
| 39 | + self.client = TestClient(create_app(QueryService(store))) |
| 40 | + |
| 41 | + def test_requires_token_when_configured(self) -> None: |
| 42 | + with patch.dict(os.environ, {"MORI_INGEST_TOKEN": "s3cret", "MORI_AUTH_ENABLED": "true"}, clear=False): |
| 43 | + r = self.client.post("/ingest/wazuh", json=_ALERT) |
| 44 | + self.assertEqual(r.status_code, 401) |
| 45 | + |
| 46 | + def test_rejects_wrong_token(self) -> None: |
| 47 | + with patch.dict(os.environ, {"MORI_INGEST_TOKEN": "s3cret"}, clear=False): |
| 48 | + r = self.client.post("/ingest/wazuh", json=_ALERT, headers={"Authorization": "Bearer nope"}) |
| 49 | + self.assertEqual(r.status_code, 401) |
| 50 | + |
| 51 | + def test_token_ok_no_db_returns_503(self) -> None: |
| 52 | + with patch.dict(os.environ, {"MORI_INGEST_TOKEN": "s3cret", "MORI_DATABASE_URL": ""}, clear=False): |
| 53 | + r = self.client.post("/ingest/wazuh", json=_ALERT, headers={"X-MORI-Token": "s3cret"}) |
| 54 | + self.assertEqual(r.status_code, 503) |
| 55 | + |
| 56 | + def test_rejects_body_without_rule(self) -> None: |
| 57 | + # DB 없이도 인증 통과 후 본문검증에서 걸리도록: 토큰 OK + DB 있음 가정은 어려우니 |
| 58 | + # 토큰만 맞추고 DB 미설정이면 503 이 먼저 나므로, 여기선 alerts 키 배치 형태 검증만. |
| 59 | + with patch.dict(os.environ, {"MORI_INGEST_TOKEN": "s3cret", "MORI_DATABASE_URL": ""}, clear=False): |
| 60 | + r = self.client.post("/ingest/wazuh", json={"alerts": [{"no": "rule"}]}, |
| 61 | + headers={"X-MORI-Token": "s3cret"}) |
| 62 | + # DB 미설정이라 503 (인증·형태는 통과) — 인증 게이트가 먼저임을 확인 |
| 63 | + self.assertIn(r.status_code, (400, 503)) |
| 64 | + |
| 65 | + |
| 66 | +if __name__ == "__main__": |
| 67 | + unittest.main() |
0 commit comments