Files
security-alert-center/backend/tests/test_agent_update.py
T
PapaTramp cd2f27792d feat: agent updates from SAC, poll config, SSH/WinRM fallback (0.12.0)
Add agent update settings, pending self-update via poll, desired config per host, and automatic or manual SSH/WinRM fallback when agents do not report agent.update events.
2026-06-20 15:52:10 +10:00

209 lines
6.4 KiB
Python

"""Tests for agent update settings, poll, request and ingest lifecycle."""
import uuid
from datetime import datetime, timedelta, timezone
from unittest.mock import patch
from app.models import Host
from app.services.agent_poll import build_agent_poll_payload
from app.services.agent_update import (
AGENT_UPDATE_SUCCESS,
AgentUpdateNotAllowedError,
process_agent_update_ingest,
request_agent_update,
)
from app.services.agent_update_settings import upsert_agent_update_settings
from app.services.ingest import ingest_event
from tests.test_ingest import VALID_EVENT
def test_agent_update_settings_sac_mode(db_session):
cfg = upsert_agent_update_settings(
db_session,
mode="sac",
recommended_ssh_version="2.1.0-SAC",
fallback_after_minutes=10,
)
assert cfg.mode == "sac"
assert cfg.recommended_ssh_version == "2.1.0-SAC"
assert cfg.fallback_after_minutes == 10
def test_request_agent_update_sets_pending(db_session):
upsert_agent_update_settings(db_session, mode="sac", recommended_ssh_version="2.1.0-SAC")
host = Host(
hostname="linux-1",
os_family="linux",
product="ssh-monitor",
product_version="2.0.0-SAC",
agent_instance_id="agent-uuid-1",
)
db_session.add(host)
db_session.commit()
request_agent_update(db_session, host)
assert host.pending_agent_update is True
assert host.pending_update_target_version == "2.1.0-SAC"
assert host.agent_update_state == "requested"
def test_request_agent_update_blocked_in_gpo_mode(db_session):
upsert_agent_update_settings(db_session, mode="gpo")
host = Host(
hostname="linux-2",
os_family="linux",
product="ssh-monitor",
agent_instance_id="agent-uuid-2",
)
db_session.add(host)
db_session.commit()
try:
request_agent_update(db_session, host)
raise AssertionError("expected AgentUpdateNotAllowedError")
except AgentUpdateNotAllowedError:
pass
def test_poll_payload_includes_update_and_config(db_session):
upsert_agent_update_settings(db_session, mode="sac")
host = Host(
hostname="win-1",
os_family="windows",
product="rdp-login-monitor",
agent_instance_id="agent-uuid-3",
pending_agent_update=True,
pending_update_target_version="2.1.0-SAC",
agent_config={"ServerDisplayName": "Test PC"},
agent_config_revision=3,
)
db_session.add(host)
db_session.commit()
payload = build_agent_poll_payload(db_session, host)
assert payload["config_revision"] == 3
assert payload["config"]["settings"]["ServerDisplayName"] == "Test PC"
assert payload["update"]["requested"] is True
assert payload["update"]["target_version"] == "2.1.0-SAC"
assert payload["update"]["source"] == "sac"
def test_ingest_agent_update_success_clears_pending(db_session):
host = Host(
hostname="linux-3",
os_family="linux",
product="ssh-monitor",
agent_instance_id="agent-uuid-4",
pending_agent_update=True,
pending_update_target_version="2.1.0-SAC",
)
db_session.add(host)
db_session.commit()
process_agent_update_ingest(
db_session,
host,
AGENT_UPDATE_SUCCESS,
{"product_version": "2.1.0-SAC"},
)
assert host.pending_agent_update is False
assert host.product_version == "2.1.0-SAC"
assert host.agent_update_state == "success"
def test_api_request_agent_update(jwt_headers, client, db_session):
upsert_agent_update_settings(db_session, mode="sac", recommended_ssh_version="2.1.0-SAC")
host = Host(
hostname="api-linux",
os_family="linux",
product="ssh-monitor",
agent_instance_id="agent-api-1",
)
db_session.add(host)
db_session.commit()
response = client.post(f"/api/v1/hosts/{host.id}/actions/request-agent-update", headers=jwt_headers)
assert response.status_code == 200
body = response.json()
assert body["pending_agent_update"] is True
assert body["target_version"] == "2.1.0-SAC"
def test_api_agent_poll_extended(auth_headers, client, db_session):
upsert_agent_update_settings(db_session, mode="sac")
host = Host(
hostname="poll-host",
os_family="linux",
product="ssh-monitor",
agent_instance_id="poll-agent-id",
pending_agent_update=True,
pending_update_target_version="2.1.0-SAC",
)
db_session.add(host)
db_session.commit()
response = client.get(
"/api/v1/agent/commands",
params={"agent_instance_id": "poll-agent-id"},
headers=auth_headers,
)
assert response.status_code == 200
body = response.json()
assert body["update"]["requested"] is True
assert body["commands"] == []
def test_api_patch_agent_config(jwt_headers, client, db_session):
host = Host(
hostname="cfg-host",
os_family="windows",
product="rdp-login-monitor",
)
db_session.add(host)
db_session.commit()
response = client.patch(
f"/api/v1/hosts/{host.id}/agent-config",
json={"settings": {"ServerDisplayName": "UNMS Kalina", "GetInventory": True}},
headers=jwt_headers,
)
assert response.status_code == 200
body = response.json()
assert body["config_revision"] == 1
assert body["settings"]["ServerDisplayName"] == "UNMS Kalina"
def test_api_agent_update_fallback_ssh(jwt_headers, client, db_session, monkeypatch):
monkeypatch.setenv("SAC_LINUX_ADMIN_USER", "root")
monkeypatch.setenv("SAC_LINUX_ADMIN_PASSWORD", "pw")
from app.config import get_settings
get_settings.cache_clear()
host = Host(
hostname="fb-linux",
os_family="linux",
product="ssh-monitor",
ipv4="10.10.36.9",
)
db_session.add(host)
db_session.commit()
with patch("app.services.agent_update.run_ssh_monitor_update") as mock_update:
from app.services.ssh_connect import SshCommandResult
mock_update.return_value = SshCommandResult(
ok=True,
message="updated",
target="fb-linux",
agent_version="2.1.0-SAC",
)
response = client.post(
f"/api/v1/hosts/{host.id}/actions/agent-update-fallback",
headers=jwt_headers,
)
assert response.status_code == 200
assert response.json()["ok"] is True
assert response.json()["product_version"] == "2.1.0-SAC"