Organize codebase into hub, clients, and common packages with backward compatibility and comprehensive unit test suites
CI Test Suite / Run Component Tests & Pipeline Verification (push) Failing after 1m18s

This commit is contained in:
2026-10-02 23:14:55 +02:00
parent 9ebfadbe89
commit df9f5d191b
47 changed files with 10262 additions and 9068 deletions
+178
View File
@@ -0,0 +1,178 @@
"""
LOGAR - Comprehensive Unit Tests for Edge Clients & Forwarders
Covers log_collector, client_transport, client_updater, Win_Client, and Linux_Client.
"""
import os
import sys
import json
import time
import tempfile
import unittest
import socket
import ssl
from unittest.mock import MagicMock, patch
from src.clients.log_collector import (
get_machine_identifier,
is_path_writable,
get_default_data_dir,
resolve_writable_path,
get_state_path,
load_state,
save_state,
commit_state,
WindowsLogCollector,
LinuxLogCollector,
BaseLogCollector,
get_log_collector,
is_routine_service_noise,
collect_system_metrics
)
from src.clients.client_updater import ClientUpdater
from src.clients.client_transport import (
is_cert_expiring_soon,
enroll_client_if_needed,
load_config,
ensure_enrolled,
send_encrypted_logs_over_socket,
check_and_apply_auto_update
)
import src.clients.Win_Client as Win_Client
import src.clients.Linux_Client as Linux_Client
class TestClientsComprehensive(unittest.TestCase):
def setUp(self):
self.test_dir = tempfile.mkdtemp()
def tearDown(self):
import shutil
if os.path.exists(self.test_dir):
try:
shutil.rmtree(self.test_dir)
except Exception:
pass
def test_log_collector_utilities(self):
"""Tests machine identifier, path resolution, and state persistence."""
mid = get_machine_identifier()
self.assertTrue(len(mid) > 0)
data_dir = get_default_data_dir("TEST-LOGAR")
self.assertTrue(isinstance(data_dir, str))
resolved = resolve_writable_path(os.path.join(self.test_dir, "test.db"), "TEST-LOGAR")
self.assertEqual(os.path.abspath(resolved), os.path.abspath(os.path.join(self.test_dir, "test.db")))
state_path = get_state_path("test_state.json", "TEST-LOGAR")
self.assertTrue(isinstance(state_path, str))
# State load/save/commit
st_file = os.path.join(self.test_dir, "st.json")
st_data = load_state(st_file)
self.assertIsInstance(st_data, dict)
st_data["processed_records"] = 42
st_data["new_last_record_number"] = 100
save_state(st_file, st_data)
loaded = load_state(st_file)
self.assertEqual(loaded["processed_records"], 42)
commit_state(loaded, st_file)
loaded2 = load_state(st_file)
self.assertEqual(loaded2["last_record_number"], 100)
def test_metrics_collection_and_noise_filter(self):
"""Tests system metrics telemetry and routine noise filtering."""
metrics = collect_system_metrics()
self.assertIn("cpu_percent", metrics)
self.assertIn("memory_percent", metrics)
self.assertIn("disk_percent_free", metrics)
# Routine noise filter: signature, message, severity
self.assertTrue(is_routine_service_noise("Service Control Manager", "Windows Error Reporting Service entered the stopped state.", "INFO"))
self.assertTrue(is_routine_service_noise("Service Control Manager", "Background Intelligent Transfer Service entered the running state.", "INFO"))
self.assertFalse(is_routine_service_noise("NTFS", "Fatal disk corruption error detected on C:", "ERROR"))
def test_log_collector_classes(self):
"""Tests WindowsLogCollector and LinuxLogCollector implementations."""
# Linux Log Collector
linux_col = LinuxLogCollector()
self.assertIsNotNone(linux_col)
# Windows Log Collector
win_col = WindowsLogCollector()
self.assertEqual(win_col.channels, ["Application"])
factory_col = get_log_collector()
self.assertIsInstance(factory_col, BaseLogCollector)
def test_client_updater_lifecycle(self):
"""Tests ClientUpdater download, local hash match, and update application routines."""
updater = ClientUpdater(config={"server_host": "127.0.0.1", "hermes_port": 8443}, os_type="linux")
self.assertEqual(updater.os_type, "linux")
base_url = updater.get_server_base_url()
self.assertEqual(base_url, "http://127.0.0.1:8443")
# Linux apply update mock
dummy_bin = os.path.join(self.test_dir, "dummy_new_bin")
dummy_target = os.path.join(self.test_dir, "dummy_target_bin")
with open(dummy_bin, "wb") as f:
f.write(b"NEW_BINARY_CONTENT")
with open(dummy_target, "wb") as f:
f.write(b"OLD_BINARY_CONTENT")
ok = updater.apply_update_linux(dummy_bin, dummy_target)
self.assertTrue(ok)
with open(dummy_target, "rb") as f:
self.assertEqual(f.read(), b"NEW_BINARY_CONTENT")
# Windows updater batch generation
updater_win = ClientUpdater(config={"server_host": "127.0.0.1", "hermes_port": 8443}, os_type="windows")
with patch("subprocess.Popen") as mock_popen:
win_bin = os.path.join(self.test_dir, "win_new.exe")
win_target = os.path.join(self.test_dir, "win_target.exe")
with open(win_bin, "wb") as f:
f.write(b"WIN_EXE")
with open(win_target, "wb") as f:
f.write(b"WIN_TARGET")
res = updater_win.apply_update_windows(win_bin, win_target)
self.assertTrue(res)
self.assertTrue(mock_popen.called)
def test_client_transport_config_and_enrollment(self):
"""Tests loading client config, cert expiration check, and fallback."""
cfg_file = os.path.join(self.test_dir, "client_config.json")
cert_dir = os.path.join(self.test_dir, "certs")
os.makedirs(cert_dir, exist_ok=True)
cfg_data = {
"server_host": "127.0.0.1",
"server_port": 9443,
"hermes_host": "127.0.0.1",
"hermes_port": 8443,
"enrollment_secret": "test_sec",
"cert_dir": cert_dir,
"server_fingerprint": "FP123",
"server_public_key": "",
"auth_token": "token123"
}
with open(cfg_file, "w", encoding="utf-8") as f:
json.dump(cfg_data, f)
loaded_cfg = load_config(cfg_file)
self.assertEqual(loaded_cfg["server_host"], "127.0.0.1")
# Expiry check
self.assertTrue(is_cert_expiring_soon(os.path.join(cert_dir, "nonexistent.crt")))
def test_client_main_helpers(self):
"""Tests Win_Client and Linux_Client config loading and log retrieval fallbacks."""
logs = Win_Client.get_recent_windows_logs(hours=1, state={})
self.assertIsInstance(logs, list)
logs_l = Linux_Client.get_recent_linux_logs(hours=1, state={})
self.assertIsInstance(logs_l, list)
if __name__ == "__main__":
unittest.main()
+109
View File
@@ -0,0 +1,109 @@
"""
Comprehensive unit tests for LOGAR Client Transport, Windows & Linux forwarders, and update checks.
"""
import os
import sys
import json
import shutil
import tempfile
import unittest
from unittest.mock import MagicMock, patch
from src.clients import client_transport
from src.clients import Win_Client, Linux_Client
class TestClientsDeep(unittest.TestCase):
def setUp(self):
self.temp_dir = tempfile.mkdtemp()
self.cert_dir = os.path.join(self.temp_dir, "certs")
os.makedirs(self.cert_dir, exist_ok=True)
self.config_path = os.path.join(self.temp_dir, "client_config.json")
def tearDown(self):
shutil.rmtree(self.temp_dir, ignore_errors=True)
def test_client_transport_config_loader_and_helpers(self):
"""Tests client configuration loading, writable directory fallback, and path resolution."""
sample_config = {
"server_host": "127.0.0.1",
"server_port": 9443,
"hermes_port": 8443,
"cert_dir": self.cert_dir,
"enrollment_secret": "test_enroll_secret_2026",
"auth_token": "token_123",
"ca_cert": "-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----\n"
}
with open(self.config_path, "w", encoding="utf-8") as f:
json.dump(sample_config, f)
cfg = client_transport.load_config(self.config_path)
self.assertEqual(cfg["server_host"], "127.0.0.1")
self.assertEqual(cfg["server_port"], 9443)
self.assertTrue(os.path.exists(os.path.join(cfg["cert_dir"], "ca.crt")))
# Test cert expiry check on non-existent cert
self.assertTrue(client_transport.is_cert_expiring_soon(os.path.join(self.cert_dir, "nonexistent.crt")))
def test_client_transport_ensure_enrolled(self):
"""Tests proactive client enrollment bootstrap check."""
cfg = {
"server_host": "127.0.0.1",
"server_port": 9443,
"hermes_host": "127.0.0.1",
"hermes_port": 8443,
"cert_dir": self.cert_dir,
"enrollment_secret": "secret"
}
with patch("src.clients.client_transport.enroll_client_if_needed") as mock_enroll:
mock_enroll.return_value = True
ok = client_transport.ensure_enrolled(cfg, "machine_01", os_type="windows")
self.assertTrue(ok)
mock_enroll.assert_called_once()
def test_client_transport_openpgp_fallback_transmission(self):
"""Tests fallback to OpenPGP envelope over plain TCP socket when certs are absent."""
from src.hub.storage_engine import StorageCryptoManager
priv, pub, fp = StorageCryptoManager.generate_server_keypair("Hub Test Key")
cfg = {
"server_host": "127.0.0.1",
"server_port": 9443,
"cert_dir": os.path.join(self.temp_dir, "empty_certs"),
"server_public_key": pub,
"server_fingerprint": fp,
"auth_token": "client_token_abc"
}
mock_socket = MagicMock()
# Mock server returning 4-byte big-endian length + JSON response
resp_data = json.dumps({"status": "success", "processed": 5}).encode("utf-8")
resp_len_header = len(resp_data).to_bytes(4, byteorder="big")
mock_socket.recv.side_effect = [resp_len_header, resp_data, b""]
with patch("socket.socket") as mock_sock_cls:
mock_sock_cls.return_value.__enter__.return_value = mock_socket
logs = [{"timestamp": "2026-01-01T00:00:00Z", "event_id": 4625, "message": "Failed logon"}]
res = client_transport.send_encrypted_logs_over_socket(cfg, logs, machine_id="test_win_node")
self.assertEqual(res.get("status"), "success")
self.assertEqual(res.get("processed"), 5)
def test_win_client_and_linux_client_entrypoints(self):
"""Tests command-line parsing, help options, and diagnostic checks."""
# Test help flag execution
with self.assertRaises(SystemExit) as cm:
with patch("sys.argv", ["Win_Client.py", "--help"]):
Win_Client.main()
self.assertEqual(cm.exception.code, 0)
with self.assertRaises(SystemExit) as cm:
with patch("sys.argv", ["Linux_Client.py", "--help"]):
Linux_Client.main()
self.assertEqual(cm.exception.code, 0)
if __name__ == "__main__":
unittest.main()
+52
View File
@@ -0,0 +1,52 @@
"""
LOGAR - Comprehensive Unit Tests for Common Version Module
100% test coverage for semantic versioning, version parsing, comparisons, and edge cases.
"""
import unittest
from src.common.version import (
LOGAR_VERSION,
parse_version,
compare_versions,
is_newer_version
)
class TestCommonVersion(unittest.TestCase):
def test_version_constants(self):
self.assertIsInstance(LOGAR_VERSION, str)
self.assertTrue(len(LOGAR_VERSION) > 0)
def test_parse_version_valid(self):
self.assertEqual(parse_version("1.2.3.4"), (1, 2, 3, 4))
self.assertEqual(parse_version("v1.2.3.4"), (1, 2, 3, 4))
self.assertEqual(parse_version("V1.2.3"), (1, 2, 3, 0))
self.assertEqual(parse_version("1.2"), (1, 2, 0, 0))
self.assertEqual(parse_version("1"), (1, 0, 0, 0))
self.assertEqual(parse_version("1.2.3.4.5"), (1, 2, 3, 4, 5))
def test_parse_version_invalid_and_edge_cases(self):
self.assertEqual(parse_version(None), (0, 0, 0, 0))
self.assertEqual(parse_version(""), (0, 0, 0, 0))
self.assertEqual(parse_version(" "), (0, 0, 0, 0))
self.assertEqual(parse_version("invalid"), (0, 0, 0, 0))
self.assertEqual(parse_version("v1.alpha.2"), (1, 0, 0, 0))
def test_compare_versions(self):
self.assertEqual(compare_versions("1.0.0.0", "1.0.0.0"), 0)
self.assertEqual(compare_versions("1.0.0.1", "1.0.0.0"), 1)
self.assertEqual(compare_versions("1.0.0.0", "1.0.0.1"), -1)
self.assertEqual(compare_versions("2.0.0.0", "1.9.9.9"), 1)
self.assertEqual(compare_versions("0.3.5.2", "0.3.5.2"), 0)
self.assertEqual(compare_versions("0.3.6.0", "0.3.5.2"), 1)
self.assertEqual(compare_versions("0.3.5.1", "0.3.5.2"), -1)
def test_is_newer_version(self):
self.assertTrue(is_newer_version("1.0.0.1", "1.0.0.0"))
self.assertFalse(is_newer_version("1.0.0.0", "1.0.0.0"))
self.assertFalse(is_newer_version("0.9.9.9", "1.0.0.0"))
self.assertTrue(is_newer_version("v0.3.6.0", "0.3.5.2"))
if __name__ == "__main__":
unittest.main()
+201
View File
@@ -0,0 +1,201 @@
"""
LOGAR - Comprehensive Unit Tests for Central Server Hub
Covers License_Authority, auth_manager, ingest_queue, license_manager, pki_manager,
server_enrollment, server_models, storage_engine, update_manager, web_ui, and Server endpoints.
"""
import os
import sys
import json
import uuid
import time
import tempfile
import unittest
import sqlite3
from unittest.mock import MagicMock, patch
from src.hub.server_models import (
ClientEnrollRequest,
ClientActionRequest,
ClientUpdateRequest,
LicenseImportRequest,
HermesTokenCreateRequest,
HermesTokenRevokeRequest,
ClientProfileExportRequest,
ChangePasswordRequest,
DbConnectionTestRequest,
DbSwitchBackendRequest,
DbFullConfigRequest
)
from src.hub.auth_manager import (
PasswordManager,
SessionManager,
RateLimiter,
TOTPManager,
HermesTokenManager
)
from src.hub.ingest_queue import IngestQueueManager
from src.hub.license_manager import LicenseManager
from src.hub.pki_manager import PKIManager
from src.hub.storage_engine import StorageEngine
from src.hub.update_manager import ServerUpdateManager
from src.hub.web_ui import WebUIRenderer
import src.hub.Server as Server
import src.hub.server_enrollment as enrollment
class TestHubComprehensive(unittest.TestCase):
def setUp(self):
self.test_dir = tempfile.mkdtemp()
self.test_db = os.path.join(self.test_dir, f"test_hub_{uuid.uuid4().hex[:8]}.db")
self.cert_dir = os.path.join(self.test_dir, "certs")
self.updates_dir = os.path.join(self.test_dir, "updates")
StorageEngine.init_db(self.test_db, max_seats=10)
self.update_mgr = ServerUpdateManager(update_dir=self.updates_dir, check_enabled=False)
Server.SERVER_STATE["update_manager"] = self.update_mgr
Server.SERVER_STATE["config"] = {
"db_path": self.test_db,
"cert_dir": self.cert_dir,
"server_name": "LOGAR-Test-Hub",
"server_fingerprint": "TEST_FP_123",
"tcp_host": "127.0.0.1",
"tcp_port": 9443,
"hermes_port": 8443,
"auth_token": "test_auth_token_secret"
}
def tearDown(self):
import shutil
if os.path.exists(self.test_dir):
try:
shutil.rmtree(self.test_dir)
except Exception:
pass
def test_server_models(self):
"""Tests Pydantic validation on all Server Hub data models."""
enr = ClientEnrollRequest(client_id="c1", hostname="host1", os="windows", enrollment_secret="sec")
self.assertEqual(enr.client_id, "c1")
act = ClientActionRequest(client_id="c1")
self.assertEqual(act.client_id, "c1")
upd = ClientUpdateRequest(client_id="c1", os="windows", current_version="0.3.5.0")
self.assertEqual(upd.current_version, "0.3.5.0")
lic_imp = LicenseImportRequest(ca_cert="PEM_CERT_DATA")
self.assertEqual(lic_imp.ca_cert, "PEM_CERT_DATA")
htc = HermesTokenCreateRequest(name="agent1")
self.assertEqual(htc.name, "agent1")
htr = HermesTokenRevokeRequest(token="tok123")
self.assertEqual(htr.token, "tok123")
cpe = ClientProfileExportRequest(server_host="10.0.0.1")
self.assertEqual(cpe.server_host, "10.0.0.1")
cp = ChangePasswordRequest(old_password="old", new_password="new")
self.assertEqual(cp.new_password, "new")
dct = DbConnectionTestRequest(host="localhost", port=5432, database="logar")
self.assertEqual(dct.port, 5432)
dfc = DbFullConfigRequest(storage_engine="sqlite", cache_mode="direct")
self.assertEqual(dfc.storage_engine, "sqlite")
def test_auth_manager_edge_cases(self):
"""Tests password hashing, TOTP secret generation, and rate limiting edge cases."""
# Password
h, s = PasswordManager.hash_password("admin_password")
self.assertTrue(PasswordManager.verify_password("admin_password", s, h))
self.assertFalse(PasswordManager.verify_password("wrong_password", s, h))
# Session token
tok = SessionManager.generate_session_token("admin", db_path=self.test_db)
self.assertEqual(SessionManager.verify_session_token(tok, db_path=self.test_db), "admin")
self.assertIsNone(SessionManager.verify_session_token("invalid_token", db_path=self.test_db))
# TOTP Manager
sec = TOTPManager.generate_totp_secret(length=20)
self.assertTrue(len(sec) >= 20)
code = TOTPManager.get_totp_token(sec)
self.assertTrue(len(code) == 6)
self.assertTrue(TOTPManager.verify_totp_code(sec, code))
uri = TOTPManager.get_totp_uri("admin", sec)
self.assertIn("otpauth://totp/", uri)
# Hermes Token Manager
ht = HermesTokenManager.create_hermes_token("Agent 1", db_path=self.test_db)
self.assertIn("token", ht)
self.assertTrue(HermesTokenManager.verify_hermes_token(ht["token"], db_path=self.test_db))
tokens = HermesTokenManager.list_hermes_tokens(db_path=self.test_db)
self.assertTrue(len(tokens) >= 1)
HermesTokenManager.revoke_hermes_token(ht["token"], db_path=self.test_db)
self.assertFalse(HermesTokenManager.verify_hermes_token(ht["token"], db_path=self.test_db))
HermesTokenManager.delete_hermes_token(ht["token"], db_path=self.test_db)
def test_pki_and_server_enrollment(self):
"""Tests dynamic CA, Server, and client certificate issuance and verification."""
ca_cert, ca_key, ca_pem, ca_key_pem = enrollment.generate_ca_if_needed(cert_dir=self.cert_dir)
self.assertIsNotNone(ca_cert)
self.assertTrue(os.path.exists(os.path.join(self.cert_dir, "ca.crt")))
# Issue server cert
srv_cert, srv_key, srv_pem, srv_key_pem = enrollment.generate_server_cert_if_needed(
ca_cert, ca_key,
hostnames=["127.0.0.1", "localhost"],
cert_dir=self.cert_dir
)
self.assertIsNotNone(srv_cert)
# Issue client cert
cl_pem, cl_key_pem = enrollment.issue_client_cert(
client_id="client-hub-test",
ca_cert=ca_cert,
ca_key=ca_key
)
self.assertIn("BEGIN CERTIFICATE", cl_pem)
self.assertIn("BEGIN RSA PRIVATE KEY", cl_key_pem)
# Validate renewal logic
ca_ren, srv_ren = enrollment.check_and_renew_hub_pki(cert_dir=self.cert_dir, hostnames=["127.0.0.1"])
self.assertFalse(ca_ren)
self.assertFalse(srv_ren)
rotated = PKIManager.check_and_rotate_server_certs(cert_dir=self.cert_dir, hostnames=["127.0.0.1"])
self.assertFalse(rotated)
def test_storage_engine_sqlite_and_migrations(self):
"""Tests StorageEngine table initialization, column migrations, and data persistence."""
db_path = StorageEngine.init_db(self.test_db)
self.assertTrue(os.path.exists(db_path))
# Check tables
conn = sqlite3.connect(db_path)
tables = {row[0] for row in conn.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall()}
conn.close()
self.assertIn("active_issues", tables)
self.assertIn("clients", tables)
self.assertIn("license_config", tables)
self.assertIn("users", tables)
self.assertIn("hermes_tokens", tables)
def test_web_ui_renderer_all_tabs(self):
"""Tests rendering of all WebUI tabs: Fleet, Hermes, Anomalies, PKI, DB, Updates."""
html = WebUIRenderer.render_dashboard_html(
server_state=Server.SERVER_STATE,
ingest_queue=Server.INGEST_QUEUE
)
self.assertIn("LOGAR Fleet Hub", html)
self.assertIn("tabPane-fleet", html)
self.assertIn("tabPane-hermes", html)
self.assertIn("tabPane-anomalies", html)
self.assertIn("tabPane-pki", html)
self.assertIn("tabPane-db", html)
self.assertIn("tabPane-updates", html)
if __name__ == "__main__":
unittest.main()
+109
View File
@@ -0,0 +1,109 @@
"""
Comprehensive unit tests for dynamic PKI, Sub-CA issuance, offline CSR activation, and LicenseAuthority.
"""
import os
import shutil
import tempfile
import sqlite3
import unittest
from unittest.mock import MagicMock, patch
from src.hub import server_enrollment as enrollment
from src.hub.pki_manager import PKIManager
from src.hub import License_Authority
class TestHubEnrollmentPKIDeep(unittest.TestCase):
def setUp(self):
self.temp_dir = tempfile.mkdtemp()
self.cert_dir = os.path.join(self.temp_dir, "certs")
os.makedirs(self.cert_dir, exist_ok=True)
self.db_path = os.path.join(self.temp_dir, "logar_test.db")
def tearDown(self):
shutil.rmtree(self.temp_dir, ignore_errors=True)
def test_pki_lifecycle_and_extensions(self):
"""Tests CA generation, Server TLS cert generation, client cert issuance, and license OID parsing."""
# 1. CA Generation
ca_cert, ca_key, ca_pem, ca_key_pem = enrollment.generate_ca_if_needed(cert_dir=self.cert_dir)
self.assertIsNotNone(ca_cert)
self.assertTrue(os.path.exists(os.path.join(self.cert_dir, "ca.crt")))
self.assertTrue(os.path.exists(os.path.join(self.cert_dir, "ca.key")))
# 2. Server TLS Certificate Generation
srv_cert, srv_key, srv_pem, srv_key_pem = enrollment.generate_server_cert_if_needed(
ca_cert=ca_cert,
ca_key=ca_key,
hostnames=["127.0.0.1", "localhost"],
cert_dir=self.cert_dir
)
self.assertIsNotNone(srv_cert)
self.assertTrue(os.path.exists(os.path.join(self.cert_dir, "server.crt")))
self.assertTrue(os.path.exists(os.path.join(self.cert_dir, "server.key")))
# 3. Client Certificate Issuance
cl_pem, cl_key_pem = enrollment.issue_client_cert(
client_id="forwarder-node-01",
ca_cert=ca_cert,
ca_key=ca_key,
days_valid=365
)
self.assertIn("BEGIN CERTIFICATE", cl_pem)
self.assertIn("BEGIN RSA PRIVATE KEY", cl_key_pem)
# 4. Extract license info
info = enrollment.extract_license_info_from_cert(ca_pem)
self.assertIn("is_self_signed", info)
# 5. Check cert expiry helper
self.assertFalse(enrollment.is_cert_expiring_soon(ca_pem, threshold_days=30))
# 6. Initialize mTLS Server context
ctx = PKIManager.init_mtls_server_context(cert_dir=self.cert_dir)
self.assertIsNotNone(ctx)
# 7. Reload mTLS context
PKIManager.reload_mtls_context(ctx, cert_dir=self.cert_dir)
def test_offline_csr_and_activation(self):
"""Tests export of offline license request CSR and activation bundle unpack."""
csr_pem, csr_key_pem = enrollment.generate_ca_csr(cert_dir=self.cert_dir)
self.assertIn("BEGIN CERTIFICATE REQUEST", csr_pem)
# Sign the CSR locally as Sub-CA for test
ca_cert, ca_key, ca_pem, ca_key_pem = enrollment.generate_ca_if_needed(cert_dir=self.cert_dir)
sub_ca_pem = enrollment.sign_ca_csr_with_seats(
csr_pem=csr_pem,
master_ca_cert_pem=ca_pem,
master_ca_key_pem=ca_key_pem,
seats=25,
days_valid=365,
license_id="LIC-TEST-2026",
features=["postgres", "realtime_alerting"]
)
self.assertIn("BEGIN CERTIFICATE", sub_ca_pem)
# Verify signed Sub-CA contains license extensions
info = enrollment.extract_license_info_from_cert(sub_ca_pem)
self.assertEqual(info.get("seats"), 25)
self.assertEqual(info.get("license_id"), "LIC-TEST-2026")
self.assertIn("postgres", info.get("features", []))
# Test create_license_request_lic
lic_data, lic_json = enrollment.create_license_request_lic(cert_dir=self.cert_dir)
self.assertEqual(lic_data["format"], "LOGAR_LICENSE_REQUEST")
self.assertIn("csr", lic_data)
def test_license_authority_cli_dispatch(self):
"""Tests License Authority helper and CLI options."""
# Test help flag execution
with self.assertRaises(SystemExit) as cm:
with patch("sys.argv", ["License_Authority.py", "--help"]):
License_Authority.main()
self.assertEqual(cm.exception.code, 0)
if __name__ == "__main__":
unittest.main()
+149
View File
@@ -0,0 +1,149 @@
"""
Comprehensive unit tests for LOGAR Server FastAPI endpoints, state management, and log processing.
"""
import os
import sys
import json
import shutil
import tempfile
import sqlite3
import unittest
from unittest.mock import MagicMock, patch
from fastapi.testclient import TestClient
from src.hub import Server
from src.hub.auth_manager import SessionManager
class TestHubServerDeep(unittest.TestCase):
def setUp(self):
self.temp_dir = tempfile.mkdtemp()
self.cert_dir = os.path.join(self.temp_dir, "certs")
os.makedirs(self.cert_dir, exist_ok=True)
self.db_path = os.path.join(self.temp_dir, "test_server.db")
self.config_path = os.path.join(self.temp_dir, "server_config.json")
# Initialize test state
Server.StorageEngine.init_db(self.db_path, enrollment_secret="test_secret", max_seats=50)
Server.SERVER_STATE["config"] = {
"db_path": self.db_path,
"enrollment_secret": "test_secret",
"max_seats": 50,
"cert_dir": self.cert_dir,
"encrypt_at_rest": False
}
# Generate and load CA
ca_cert, ca_key, ca_pem, _ = Server.enrollment.generate_ca_if_needed(cert_dir=self.cert_dir)
Server.SERVER_STATE["ca_cert"] = ca_cert
Server.SERVER_STATE["ca_key"] = ca_key
Server.SERVER_STATE["ca_cert_pem"] = ca_pem
self.session_token = SessionManager.generate_session_token("admin", db_path=self.db_path)
self.client = TestClient(Server.app, cookies={SessionManager.SESSION_COOKIE_NAME: self.session_token})
def tearDown(self):
shutil.rmtree(self.temp_dir, ignore_errors=True)
def test_server_status_and_update_routes(self):
"""Tests /api/update/status and /api/server/update/status routes."""
resp = self.client.get("/api/update/status")
self.assertEqual(resp.status_code, 200)
data = resp.json()
self.assertIn("hosted_packages", data)
def test_client_enrollment_and_action_endpoints(self):
"""Tests /api/client/enroll and /api/client/revoke/approve/delete endpoints."""
# Ensure license in DB is marked active with seats
conn = sqlite3.connect(self.db_path)
conn.execute("UPDATE license_config SET max_seats = 50, license_status = 'active' WHERE id = 1")
conn.commit()
conn.close()
# Enroll client
enroll_payload = {
"client_id": "test_agent_deep_01",
"hostname": "workstation-01",
"os": "windows",
"enrollment_secret": "test_secret"
}
resp = self.client.post("/api/client/enroll", json=enroll_payload)
self.assertEqual(resp.status_code, 200)
data = resp.json()
self.assertEqual(data.get("status"), "success")
self.assertIn("client_cert", data)
self.assertIn("client_key", data)
# Check client list
resp_list = self.client.get("/api/clients")
self.assertEqual(resp_list.status_code, 200)
clients_data = resp_list.json()
self.assertGreaterEqual(clients_data.get("active_seats", 0), 1)
# Client action: revoke
resp_rev = self.client.post("/api/client/revoke", json={"client_id": "test_agent_deep_01"})
self.assertEqual(resp_rev.status_code, 200)
self.assertEqual(resp_rev.json().get("status"), "revoked")
# Reactivate
resp_app = self.client.post("/api/client/approve", json={"client_id": "test_agent_deep_01"})
self.assertEqual(resp_app.status_code, 200)
self.assertEqual(resp_app.json().get("status"), "active")
# Delete
resp_del = self.client.post("/api/client/delete", json={"client_id": "test_agent_deep_01"})
self.assertEqual(resp_del.status_code, 200)
def test_hermes_tokens_and_auth_routes(self):
"""Tests Hermes API token creation, listing, and revoking."""
# Create token
tok_payload = {"name": "Test Integration Agent"}
resp = self.client.post("/api/hermes/tokens/create", json=tok_payload)
self.assertEqual(resp.status_code, 200)
tok_data = resp.json()
token_val = tok_data.get("token")
self.assertIsNotNone(token_val)
# Revoke token
resp_rev = self.client.post("/api/hermes/tokens/revoke", json={"token": token_val})
self.assertEqual(resp_rev.status_code, 200)
def test_process_ingested_logs_and_temporal_tracking(self):
"""Tests processing ingested log entries, deduplication, and anomaly promotion."""
payload = {
"server": "web-prod-01",
"timestamp": "2026-01-01T12:00:00Z",
"logs": [
{
"site_name": "Default-Site",
"server": "web-prod-01",
"signature": "SECURITY_4625_BRUTEFORCE",
"severity": "CRITICAL",
"message": "Repeated failed logon attempts detected from 10.0.0.99",
"os_type": "windows",
"timestamp": "2026-01-01T12:00:00Z"
}
]
}
res = Server.process_ingested_logs(
payload=payload,
db_path=self.db_path,
window_hours=24,
min_runs=1
)
self.assertEqual(res.get("processed"), 1)
# Query database directly to confirm issue recorded
conn = sqlite3.connect(self.db_path)
c = conn.cursor()
c.execute("SELECT signature, severity, run_count FROM active_issues")
row = c.fetchone()
conn.close()
self.assertIsNotNone(row)
self.assertEqual(row[0], "SECURITY_4625_BRUTEFORCE")
self.assertEqual(row[1], "CRITICAL")
self.assertEqual(row[2], 1)
if __name__ == "__main__":
unittest.main()
+141
View File
@@ -0,0 +1,141 @@
"""
Comprehensive unit tests for LOGAR StorageEngine, StorageCryptoManager, and Multi-Backend Migrations.
"""
import os
import sys
import json
import shutil
import tempfile
import sqlite3
import unittest
from unittest.mock import MagicMock, patch
from src.hub.storage_engine import StorageEngine, StorageCryptoManager
class TestStorageEngineDeep(unittest.TestCase):
def setUp(self):
self.temp_dir = tempfile.mkdtemp()
self.db_path = os.path.join(self.temp_dir, "test_logar.db")
def tearDown(self):
shutil.rmtree(self.temp_dir, ignore_errors=True)
def test_storage_crypto_manager_keypair_and_encryption(self):
"""Tests keypair generation, field encryption, decryption, and error resilience."""
priv, pub, fp = StorageCryptoManager.generate_server_keypair("Test Server Key")
self.assertIn("BEGIN PGP PRIVATE KEY", priv)
self.assertIn("BEGIN PGP PUBLIC KEY", pub)
self.assertTrue(len(fp) > 0)
server_state = {
"config": {
"encrypt_at_rest": True,
"public_key": pub,
"private_key": priv
}
}
# Test empty or non-string inputs
self.assertEqual(StorageCryptoManager.encrypt_field_for_storage("", server_state=server_state, is_unlocked=True), "")
self.assertEqual(StorageCryptoManager.encrypt_field_for_storage(None, server_state=server_state, is_unlocked=True), None)
self.assertEqual(StorageCryptoManager.decrypt_field_from_storage("", server_state=server_state), "")
self.assertEqual(StorageCryptoManager.decrypt_field_from_storage(None, server_state=server_state), None)
# Test non-encrypted plain string decrypt passthrough
plain_msg = "Security Audit Message 2026"
self.assertEqual(StorageCryptoManager.decrypt_field_from_storage(plain_msg, server_state=server_state), plain_msg)
# Encrypt when unlocked
encrypted = StorageCryptoManager.encrypt_field_for_storage(plain_msg, server_state=server_state, is_unlocked=True)
self.assertTrue(encrypted.startswith("-----BEGIN PGP MESSAGE-----"))
# Encrypt idempotence (should not double-encrypt)
double_enc = StorageCryptoManager.encrypt_field_for_storage(encrypted, server_state=server_state, is_unlocked=True)
self.assertEqual(double_enc, encrypted)
# Decrypt
decrypted = StorageCryptoManager.decrypt_field_from_storage(encrypted, server_state=server_state)
self.assertEqual(decrypted, plain_msg)
# Decrypt with state missing private_key_obj (reloads from blob)
clean_state = {"config": {"private_key": priv}}
decrypted_from_clean = StorageCryptoManager.decrypt_field_from_storage(encrypted, server_state=clean_state)
self.assertEqual(decrypted_from_clean, plain_msg)
# Encrypt when locked / encrypt_at_rest is false
unenc_state = {"config": {"encrypt_at_rest": False, "public_key": pub}}
res_plain = StorageCryptoManager.encrypt_field_for_storage(plain_msg, server_state=unenc_state, is_unlocked=False)
self.assertEqual(res_plain, plain_msg)
def test_storage_engine_sqlite_init_and_telemetry(self):
"""Tests SQLite database initialization, WAL settings, column migrations, and telemetry."""
db = StorageEngine.init_db(self.db_path, enrollment_secret="sec-test-123", max_seats=50)
self.assertEqual(db, self.db_path)
self.assertTrue(os.path.exists(db))
# Re-running init_db executes migrations smoothly
db_reinit = StorageEngine.init_db(self.db_path, enrollment_secret="sec-test-123", max_seats=50)
self.assertEqual(db_reinit, self.db_path)
# Check telemetry info
state = {"config": {"db_path": self.db_path, "db_type": "sqlite"}}
info = StorageEngine.get_storage_engine_info(self.db_path, server_state=state, is_unlocked=True)
self.assertEqual(info["engine"], "sqlite")
self.assertFalse(info["is_enterprise"])
self.assertIn("active_issues", info["table_stats"])
self.assertIn("clients", info["table_stats"])
def test_storage_engine_postgres_mocked(self):
"""Tests Postgres schema initialization and SQLite-to-Postgres data migration with mocked drivers."""
pg_cfg = {
"host": "127.0.0.1",
"port": 5432,
"database": "logar_test",
"user": "logar_user",
"password": "secret_password",
"sslmode": "disable"
}
# Initialize SQLite DB with test records
StorageEngine.init_db(self.db_path, enrollment_secret="sec-migrate", max_seats=10)
conn = sqlite3.connect(self.db_path)
conn.execute("""
INSERT INTO active_issues (fingerprint, site_name, server, signature, severity, message, os_type, first_seen, last_seen, run_count, status, last_run_id)
VALUES ('fp1', 'SiteA', 'srv1', 'Sig1', 'HIGH', 'Test warning message', 'linux', '2026-01-01', '2026-01-02', 1, 'active', 'run1')
""")
conn.commit()
conn.close()
# Mock psycopg2
mock_psycopg2 = MagicMock()
mock_conn = MagicMock()
mock_cur = MagicMock()
mock_conn.cursor.return_value.__enter__.return_value = mock_cur
mock_psycopg2.connect.return_value = mock_conn
with patch.dict(sys.modules, {"psycopg2": mock_psycopg2}):
# Test Postgres DB init
ok, msg = StorageEngine.init_postgres_db(pg_cfg)
self.assertTrue(ok)
self.assertIn("successfully initialized", msg)
# Test Migration
encrypt_mock = lambda x: f"ENC({x})"
ok, msg, counts = StorageEngine.migrate_sqlite_to_postgres(self.db_path, pg_cfg, encrypt_fn=encrypt_mock)
self.assertTrue(ok)
self.assertIn("active_issues", counts)
self.assertEqual(counts["active_issues"], 1)
# Test Postgres info retrieval
mock_cur.fetchone.return_value = (10,)
state = {"config": {"db_type": "postgres", "postgres": pg_cfg}}
info = StorageEngine.get_storage_engine_info(None, server_state=state, is_unlocked=True)
self.assertEqual(info["engine"], "postgres")
self.assertTrue(info["is_enterprise"])
self.assertEqual(info["table_stats"]["active_issues"], 10)
if __name__ == "__main__":
unittest.main()
+150
View File
@@ -0,0 +1,150 @@
"""
Comprehensive unit tests for LOGAR UpdateManager, AuthManager, and IngestQueue.
"""
import os
import sys
import time
import shutil
import tempfile
import unittest
from unittest.mock import MagicMock, patch
from src.hub.update_manager import ServerUpdateManager
from src.hub.auth_manager import PasswordManager, TOTPManager, SessionManager, RateLimiter
from src.hub.ingest_queue import IngestQueueManager
class TestHubUpdateAuthDeep(unittest.TestCase):
def setUp(self):
self.temp_dir = tempfile.mkdtemp()
self.update_dir = os.path.join(self.temp_dir, "updates")
self.db_path = os.path.join(self.temp_dir, "auth_test.db")
def tearDown(self):
shutil.rmtree(self.temp_dir, ignore_errors=True)
def test_update_manager_caching_and_manifest(self):
"""Tests package registration, SHA256 integrity, local caching, and download serving."""
manager = ServerUpdateManager(update_dir=self.update_dir, check_enabled=False)
# Create dummy binaries
win_bin = os.path.join(self.temp_dir, "Win_Client.exe")
linux_bin = os.path.join(self.temp_dir, "Linux_Client")
with open(win_bin, "wb") as f:
f.write(b"MZ_TEST_WIN_CLIENT_BINARY_2026")
with open(linux_bin, "wb") as f:
f.write(b"\x7fELF_TEST_LINUX_CLIENT_BINARY_2026")
# Register packages
res_win = manager.register_client_package(
os_type="windows",
file_path=win_bin,
version="2.5.0",
filename="Win_Client.exe"
)
self.assertEqual(res_win.get("version"), "2.5.0")
self.assertEqual(res_win.get("os"), "windows")
self.assertIsNotNone(res_win.get("sha256"))
res_linux = manager.register_client_package(
os_type="linux",
file_path=linux_bin,
version="2.5.0",
filename="Linux_Client"
)
self.assertEqual(res_linux.get("os"), "linux")
# Get hosted packages
packages = manager.get_hosted_packages()
self.assertIn("windows", packages)
self.assertIn("linux", packages)
self.assertEqual(packages["windows"]["version"], "2.5.0")
# Test download path resolution
pkg_res = manager.get_client_package_path(os_type="windows")
self.assertIsNotNone(pkg_res)
win_path, win_filename, win_sha = pkg_res
self.assertTrue(os.path.exists(win_path))
self.assertEqual(win_filename, "Win_Client.exe")
# Test update decision logic
decision = manager.check_client_update(client_id="node-win-01", current_version="1.0.0", os_type="windows")
self.assertTrue(decision.get("update_available"))
self.assertEqual(decision.get("latest_version"), "2.5.0")
decision_uptodate = manager.check_client_update(client_id="node-win-01", current_version="2.5.0", os_type="windows")
self.assertFalse(decision_uptodate.get("update_available"))
def test_auth_manager_components(self):
"""Tests PasswordManager, TOTPManager, SessionManager, and RateLimiter."""
# 1. PasswordManager
pwd = "Enterprise_Admin_Password_!2026"
pwd_hash, salt = PasswordManager.hash_password(pwd)
self.assertTrue(PasswordManager.verify_password(pwd, salt, pwd_hash))
self.assertFalse(PasswordManager.verify_password("WrongPassword", salt, pwd_hash))
# 2. TOTPManager
secret = TOTPManager.generate_totp_secret()
self.assertTrue(len(secret) >= 16)
uri = TOTPManager.get_totp_uri("admin", secret, "LOGAR Fleet Hub")
self.assertIn("otpauth://totp/", uri)
token = TOTPManager.get_totp_token(secret)
self.assertEqual(len(token), 6)
self.assertTrue(TOTPManager.verify_totp_code(secret, token))
self.assertFalse(TOTPManager.verify_totp_code(secret, "000000" if token != "000000" else "111111"))
# 3. SessionManager
session_token = SessionManager.generate_session_token("admin", db_path=self.db_path)
self.assertIsNotNone(session_token)
username = SessionManager.verify_session_token(session_token, db_path=self.db_path)
self.assertEqual(username, "admin")
self.assertIsNone(SessionManager.verify_session_token("invalid:token:format", db_path=self.db_path))
# 4. RateLimiter
limiter = RateLimiter()
ip = "192.168.1.100"
for _ in range(5):
self.assertTrue(limiter.check_ip_rate_limit(ip, max_attempts=5, window_seconds=60, db_path=self.db_path))
limiter.record_failed_ip_attempt(ip, db_path=self.db_path)
# 6th attempt exceeded
self.assertFalse(limiter.check_ip_rate_limit(ip, max_attempts=5, window_seconds=60, db_path=self.db_path))
# Reset
limiter.reset_failed_ip_attempts(ip, db_path=self.db_path)
self.assertTrue(limiter.check_ip_rate_limit(ip, max_attempts=5, window_seconds=60, db_path=self.db_path))
def test_ingest_queue_lifecycle_and_batching(self):
"""Tests IngestQueueManager push, stats, and flush callback."""
processed_items = []
def mock_processor(payload, db_path, window_hours, min_runs):
processed_items.extend(payload.get("logs", []))
return {"processed": len(payload.get("logs", [])), "promoted_verified": 0}
queue = IngestQueueManager(flush_interval=0.05, batch_size=10, max_queue=100)
# Enqueue items
for i in range(15):
pushed = queue.push({"server": "node1", "logs": [{"id": i, "data": f"log_{i}"}]})
self.assertTrue(pushed)
stats = queue.get_stats()
self.assertEqual(stats["queue_size"], 15)
self.assertEqual(stats["total_ingested"], 15)
# Flush
flush_res = queue.flush(db_path=self.db_path, process_callback=mock_processor)
self.assertEqual(flush_res["status"], "success")
self.assertEqual(flush_res["flushed_runs"], 15)
self.assertEqual(flush_res["processed_logs"], 15)
self.assertEqual(len(processed_items), 15)
stats_after = queue.get_stats()
self.assertEqual(stats_after["queue_size"], 0)
self.assertEqual(stats_after["total_flushed"], 15)
if __name__ == "__main__":
unittest.main()