@simbimbo/brainstem 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/README.md +26 -0
- package/brainstem/__init__.py +1 -1
- package/brainstem/adapters.py +120 -0
- package/brainstem/api.py +468 -57
- package/brainstem/config.py +136 -0
- package/brainstem/connectors/logicmonitor.py +57 -0
- package/brainstem/demo.py +16 -2
- package/brainstem/fingerprint.py +54 -0
- package/brainstem/ingest.py +440 -33
- package/brainstem/interesting.py +56 -1
- package/brainstem/listener.py +181 -0
- package/brainstem/models.py +1 -0
- package/brainstem/recurrence.py +63 -9
- package/brainstem/scoring.py +6 -4
- package/brainstem/source_drivers.py +179 -0
- package/brainstem/storage.py +389 -12
- package/docs/README.md +103 -0
- package/docs/api.md +260 -280
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/tests/test_adapters.py +95 -0
- package/tests/test_api.py +812 -0
- package/tests/test_canonicalization.py +8 -0
- package/tests/test_config.py +39 -0
- package/tests/test_file_ingest.py +77 -0
- package/tests/test_fingerprint.py +51 -1
- package/tests/test_interesting.py +10 -0
- package/tests/test_listener.py +253 -0
- package/tests/test_logicmonitor.py +54 -1
- package/tests/test_recurrence.py +16 -0
- package/tests/test_source_drivers.py +95 -0
- package/tests/test_storage.py +178 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
from brainstem.adapters import (
|
|
2
|
+
RawInputAdapter,
|
|
3
|
+
SyslogRawInputAdapter,
|
|
4
|
+
build_raw_input_envelope,
|
|
5
|
+
get_raw_input_adapter,
|
|
6
|
+
list_raw_input_source_types,
|
|
7
|
+
register_raw_input_adapter,
|
|
8
|
+
)
|
|
9
|
+
import pytest
|
|
10
|
+
from brainstem.ingest import parse_file_line
|
|
11
|
+
from brainstem.ingest import parse_syslog_envelope
|
|
12
|
+
from brainstem.models import RawInputEnvelope
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_syslog_adapter_registry_is_populated() -> None:
|
|
16
|
+
sources = list_raw_input_source_types()
|
|
17
|
+
assert "syslog" in sources
|
|
18
|
+
assert "file" in sources
|
|
19
|
+
assert "logicmonitor" in sources
|
|
20
|
+
assert isinstance(get_raw_input_adapter("syslog"), RawInputAdapter)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_file_adapter_builds_raw_envelope() -> None:
|
|
24
|
+
raw_line = "vpn tunnel dropped and recovered"
|
|
25
|
+
raw = build_raw_input_envelope(
|
|
26
|
+
"file",
|
|
27
|
+
raw_line,
|
|
28
|
+
tenant_id="client-a",
|
|
29
|
+
source_path="/tmp/sample.log",
|
|
30
|
+
)
|
|
31
|
+
assert isinstance(raw, RawInputEnvelope)
|
|
32
|
+
assert raw.source_type == "file"
|
|
33
|
+
assert raw.message_raw == raw_line
|
|
34
|
+
assert raw.metadata["raw_line"] == raw_line
|
|
35
|
+
assert raw.source_path == "/tmp/sample.log"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_syslog_adapter_builds_raw_envelope() -> None:
|
|
39
|
+
raw_line = "Mar 22 00:00:01 fw-01 charon: VPN tunnel dropped and recovered"
|
|
40
|
+
raw = build_raw_input_envelope(
|
|
41
|
+
"syslog",
|
|
42
|
+
raw_line,
|
|
43
|
+
tenant_id="client-a",
|
|
44
|
+
source_path="/var/log/syslog",
|
|
45
|
+
)
|
|
46
|
+
assert isinstance(raw, RawInputEnvelope)
|
|
47
|
+
assert raw.source_type == "syslog"
|
|
48
|
+
assert raw.host == "fw-01"
|
|
49
|
+
assert raw.service == "charon"
|
|
50
|
+
assert raw.message_raw == "VPN tunnel dropped and recovered"
|
|
51
|
+
assert raw.metadata["raw_line"] == raw_line
|
|
52
|
+
assert raw.source_path == "/var/log/syslog"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_file_adapter_parse_path() -> None:
|
|
56
|
+
raw = parse_file_line("first line", tenant_id="client-a", source_path="/tmp/file.log")
|
|
57
|
+
assert raw.message_raw == "first line"
|
|
58
|
+
assert raw.source_type == "file"
|
|
59
|
+
assert raw.metadata["raw_line"] == "first line"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_syslog_dispatch_is_used_by_ingest_parse_path() -> None:
|
|
63
|
+
class StubAdapter:
|
|
64
|
+
source_type = "syslog"
|
|
65
|
+
calls = 0
|
|
66
|
+
|
|
67
|
+
def parse_raw_input(self, payload, *, tenant_id: str, source_path: str = "") -> RawInputEnvelope:
|
|
68
|
+
StubAdapter.calls += 1
|
|
69
|
+
return RawInputEnvelope(
|
|
70
|
+
tenant_id=tenant_id,
|
|
71
|
+
source_type="syslog",
|
|
72
|
+
timestamp="2026-03-22T00:00:00Z",
|
|
73
|
+
message_raw="stub-message",
|
|
74
|
+
host="host",
|
|
75
|
+
service="service",
|
|
76
|
+
source_path=source_path,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
original = get_raw_input_adapter("syslog")
|
|
80
|
+
try:
|
|
81
|
+
register_raw_input_adapter(StubAdapter())
|
|
82
|
+
raw = parse_syslog_envelope("ignored-line", tenant_id="client-a", source_path="/tmp/test.log")
|
|
83
|
+
assert raw.message_raw == "stub-message"
|
|
84
|
+
assert StubAdapter.calls == 1
|
|
85
|
+
finally:
|
|
86
|
+
if original is not None:
|
|
87
|
+
register_raw_input_adapter(original)
|
|
88
|
+
else:
|
|
89
|
+
# defensive: restore default adapter if registration state changed unexpectedly
|
|
90
|
+
register_raw_input_adapter(SyslogRawInputAdapter())
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_unknown_source_type_raises_clear_error() -> None:
|
|
94
|
+
with pytest.raises(ValueError, match="unsupported source_type"):
|
|
95
|
+
build_raw_input_envelope("not-a-source", "payload", tenant_id="client-a")
|