Files
security-alert-center/backend/tests/test_rdp_session_logoff.py
T
PapaTramp 91be282ec6 feat: close rdp.login.success on rdp.session.logoff (v0.5.12)
Ingest correlates direct RDP logoff with open login events on the same host and user. Terminate-session treats missing qwinsta as already logged off.
2026-07-13 17:10:24 +10:00

230 lines
7.0 KiB
Python

"""Tests for direct RDP logoff → workstation rdp.login.success correlation."""
import uuid
from datetime import datetime, timedelta, timezone
from app.services.event_summary import event_to_summary
from app.services.host_sessions import event_session_terminated
from app.services.ingest import ingest_event
from app.services.rdp_session_logoff import (
SESSION_CLOSED_BY_LOGOFF_AT_KEY,
SESSION_CLOSED_BY_LOGOFF_EVENT_ID_KEY,
close_workstation_session_for_rdp_logoff,
find_logoff_after_workstation_login,
resolve_workstation_login_closed_by_logoff,
)
from tests.test_ingest import VALID_EVENT
def _payload(**overrides):
base = {
**VALID_EVENT,
"event_id": str(uuid.uuid4()),
"occurred_at": datetime.now(timezone.utc).isoformat(),
}
base.update(overrides)
return base
def _ingest(db, occurred_at: datetime, **overrides):
payload = _payload(**overrides)
payload["occurred_at"] = occurred_at.isoformat()
event, _ = ingest_event(db, payload)
db.flush()
return event
def test_logoff_marks_workstation_login_closed_on_ingest(db_session):
t0 = datetime.now(timezone.utc)
user = r"B26\papatramp"
host = {"hostname": "BIV-PC", "os_family": "windows", "ipv4": "192.168.165.39"}
source = {"product": "rdp-login-monitor", "product_version": "2.1.11-SAC"}
login = _ingest(
db_session,
t0 + timedelta(seconds=1),
host=host,
source=source,
type="rdp.login.success",
category="auth",
severity="info",
title="RDP login",
summary="4624",
details={"user": user, "ip_address": "192.168.160.3", "logon_type": 10},
)
logoff = _ingest(
db_session,
t0 + timedelta(hours=1),
host=host,
source=source,
type="rdp.session.logoff",
category="auth",
severity="info",
title="RDP session logoff",
summary="4634",
details={
"user": user,
"ip_address": "192.168.160.3",
"logon_type": 10,
"event_id_windows": 4634,
},
)
assert login.details[SESSION_CLOSED_BY_LOGOFF_AT_KEY] == logoff.occurred_at.isoformat()
assert login.details[SESSION_CLOSED_BY_LOGOFF_EVENT_ID_KEY] == logoff.id
assert event_session_terminated(login, db=db_session) is True
assert event_to_summary(login, db_session).session_terminated is True
def test_user_mismatch_does_not_close_login(db_session):
t0 = datetime.now(timezone.utc)
host = {"hostname": "BIV-PC", "os_family": "windows", "ipv4": "192.168.165.39"}
source = {"product": "rdp-login-monitor", "product_version": "2.1.11-SAC"}
login = _ingest(
db_session,
t0,
host=host,
source=source,
type="rdp.login.success",
category="auth",
severity="info",
title="RDP login",
summary="4624",
details={"user": r"B26\Alice"},
)
logoff = _ingest(
db_session,
t0 + timedelta(hours=1),
host=host,
source=source,
type="rdp.session.logoff",
category="auth",
severity="info",
title="RDP session logoff",
summary="4634",
details={"user": r"B26\Bob", "logon_type": 10, "event_id_windows": 4634},
)
db_session.refresh(login)
assert close_workstation_session_for_rdp_logoff(db_session, logoff) is None
assert event_session_terminated(login, db=db_session) is False
def test_ip_mismatch_does_not_close_login_when_both_ips_present(db_session):
t0 = datetime.now(timezone.utc)
user = r"B26\papatramp"
host = {"hostname": "BIV-PC", "os_family": "windows", "ipv4": "192.168.165.39"}
source = {"product": "rdp-login-monitor", "product_version": "2.1.11-SAC"}
login = _ingest(
db_session,
t0,
host=host,
source=source,
type="rdp.login.success",
category="auth",
severity="info",
title="RDP login",
summary="4624",
details={"user": user, "ip_address": "192.168.160.3", "logon_type": 10},
)
logoff = _ingest(
db_session,
t0 + timedelta(hours=1),
host=host,
source=source,
type="rdp.session.logoff",
category="auth",
severity="info",
title="RDP session logoff",
summary="4634",
details={"user": user, "ip_address": "192.168.160.99", "logon_type": 10, "event_id_windows": 4634},
)
db_session.refresh(login)
assert close_workstation_session_for_rdp_logoff(db_session, logoff) is None
assert event_session_terminated(login, db=db_session) is False
def test_runtime_resolve_for_historical_login_without_flag(db_session):
from app.models import Event, Host
t0 = datetime.now(timezone.utc)
user = r"B26\papatramp"
host = Host(
hostname="BIV-PC",
os_family="windows",
product="rdp-login-monitor",
ipv4="192.168.165.39",
)
db_session.add(host)
db_session.commit()
login = Event(
event_id=str(uuid.uuid4()),
host_id=host.id,
occurred_at=t0 + timedelta(seconds=1),
received_at=t0,
category="auth",
type="rdp.login.success",
severity="info",
title="RDP login",
summary="4624",
payload={},
details={"user": "papatramp", "ip_address": "192.168.160.3"},
)
logoff = Event(
event_id=str(uuid.uuid4()),
host_id=host.id,
occurred_at=t0 + timedelta(hours=2),
received_at=t0,
category="auth",
type="rdp.session.logoff",
severity="info",
title="4634",
summary="4634",
payload={},
details={"user": user, "ip_address": "192.168.160.3", "event_id_windows": 4634},
)
db_session.add_all([login, logoff])
db_session.commit()
assert resolve_workstation_login_closed_by_logoff(db_session, login) is True
assert find_logoff_after_workstation_login(db_session, login) is not None
assert event_to_summary(login, db_session).session_terminated is True
def test_sam_domain_user_match_on_logoff(db_session):
t0 = datetime.now(timezone.utc)
host = {"hostname": "BIV-PC", "os_family": "windows", "ipv4": "192.168.165.39"}
source = {"product": "rdp-login-monitor", "product_version": "2.1.11-SAC"}
login = _ingest(
db_session,
t0,
host=host,
source=source,
type="rdp.login.success",
category="auth",
severity="info",
title="RDP login",
summary="4624",
details={"user": r"B26\papatramp", "logon_type": 10},
)
_ingest(
db_session,
t0 + timedelta(minutes=30),
host=host,
source=source,
type="rdp.session.logoff",
category="auth",
severity="info",
title="RDP session logoff",
summary="4647",
details={"user": "papatramp", "logon_type": 10, "event_id_windows": 4647},
)
db_session.refresh(login)
assert event_session_terminated(login, db=db_session) is True