From 887713db85b6d75c04140f73396940db1d619152 Mon Sep 17 00:00:00 2001 From: uruwhy <58484522+uruwhy@users.noreply.github.com> Date: Thu, 8 Jan 2026 16:55:55 +0000 Subject: [PATCH 01/30] decouple manx from core --- app/contacts/contact_tcp.py | 39 ++++++++++++++------------- app/contacts/utility/c_tcp_session.py | 32 ++++++++++++++++++++++ 2 files changed, 52 insertions(+), 19 deletions(-) create mode 100644 app/contacts/utility/c_tcp_session.py diff --git a/app/contacts/contact_tcp.py b/app/contacts/contact_tcp.py index 3cb2300c0..2d04f0dae 100644 --- a/app/contacts/contact_tcp.py +++ b/app/contacts/contact_tcp.py @@ -6,7 +6,7 @@ from typing import Tuple from app.utility.base_world import BaseWorld -from plugins.manx.app.c_session import Session +from app.contacts.utility.c_tcp_session import TCPSession class Contact(BaseWorld): @@ -54,18 +54,14 @@ def __init__(self, services, log): self.sessions = [] async def refresh(self): - index = 0 - - while index < len(self.sessions): - session = self.sessions[index] - + refreshed_sessions = [] + for session in self.sessions: try: - session.writer.write(str.encode(' ')) + session.write_bytes(str.encode(' ')) + refreshed_sessions.append(session) except socket.error: self.log.debug('Error occurred when refreshing session %s. Removing from session pool.', session.id) - del self.sessions[index] - else: - index += 1 + self.sessions = refreshed_sessions async def accept(self, reader, writer): try: @@ -76,19 +72,24 @@ async def accept(self, reader, writer): profile['executors'] = [e for e in profile['executors'].split(',') if e] profile['contact'] = 'tcp' agent, _ = await self.services.get('contact_svc').handle_heartbeat(**profile) - new_session = Session(id=self.generate_number(size=6), paw=agent.paw, reader=reader, writer=writer) + new_session = TCPSession(id=self.generate_number(size=6), paw=agent.paw, reader=reader, writer=writer) self.sessions.append(new_session) await self.send(new_session.id, agent.paw, timeout=5) async def send(self, session_id: int, cmd: str, timeout: int = 60) -> Tuple[int, str, str, str]: try: session = next(i for i in self.sessions if i.id == int(session_id)) - session.writer.write(str.encode(' ')) + session.write_bytes(str.encode(' ')) time.sleep(0.01) - session.writer.write(str.encode('%s\n' % cmd)) - response = await self._attempt_connection(session_id, session.reader, timeout=timeout) - response = json.loads(response) - return response['status'], response['pwd'], response['response'], response.get('agent_reported_time', '') + session.write_bytes(str.encode('%s\n' % cmd)) + response = await self._attempt_connection(session, timeout=timeout) + if response: + response = json.loads(response) + return response.get('status', 1), response.get('pwd', '~$ '), response.get('response', 'No response provided'), response.get('agent_reported_time', '') + else: + msg = f'Failed to read data from session {session.id}' + self.log.error(msg) + return 1, '~$ ', msg, '' except Exception as e: self.log.exception(e) return 1, '~$ ', str(e), '' @@ -98,17 +99,17 @@ async def _handshake(reader): profile_bites = (await reader.readline()).strip() return json.loads(profile_bites) - async def _attempt_connection(self, session_id, reader, timeout): + async def _attempt_connection(self, session, timeout): buffer = 4096 data = b'' time.sleep(0.1) # initial wait for fast operations. while True: try: - part = await reader.read(buffer) + part = await session.read_bytes(buffer) data += part if len(part) < buffer: break except Exception as err: - self.log.error("Timeout reached for session %s", session_id) + self.log.error("Timeout reached for session %s", session.id) return json.dumps(dict(status=1, pwd='~$ ', response=str(err))) return str(data, 'utf-8') diff --git a/app/contacts/utility/c_tcp_session.py b/app/contacts/utility/c_tcp_session.py new file mode 100644 index 000000000..528585fcb --- /dev/null +++ b/app/contacts/utility/c_tcp_session.py @@ -0,0 +1,32 @@ +from app.utility.base_object import BaseObject + + +class TCPSession(BaseObject): + + @property + def unique(self): + return self.hash('%s' % self.paw) + + def __init__(self, id, paw, reader, writer): + super().__init__() + self.id = id + self.paw = paw + self._reader = reader + self._writer = writer + + def store(self, ram): + existing = self.retrieve(ram['sessions'], self.unique) + if not existing: + ram['sessions'].append(self) + return self.retrieve(ram['sessions'], self.unique) + return existing + + def write_bytes(self, input): + """Wrapper for self._writer.write""" + + return self._writer.write(input) + + def read_bytes(self, buffer): + """Wrapper for self._reader.read""" + + return self._reader.read(buffer) From 5ca37cd5ab549d7aa2d0903db88953f472a49414 Mon Sep 17 00:00:00 2001 From: uruwhy <58484522+uruwhy@users.noreply.github.com> Date: Thu, 8 Jan 2026 22:39:51 +0000 Subject: [PATCH 02/30] iteration fallback --- app/contacts/contact_tcp.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/contacts/contact_tcp.py b/app/contacts/contact_tcp.py index 2d04f0dae..a6ca2673a 100644 --- a/app/contacts/contact_tcp.py +++ b/app/contacts/contact_tcp.py @@ -78,7 +78,13 @@ async def accept(self, reader, writer): async def send(self, session_id: int, cmd: str, timeout: int = 60) -> Tuple[int, str, str, str]: try: - session = next(i for i in self.sessions if i.id == int(session_id)) + try: + session = next(i for i in self.sessions if i.id == int(session_id)) + except StopIteration: + msg = f'Could not find session with ID {session_id}' + self.log.error(msg) + return 1, '~$ ', msg, '' + session.write_bytes(str.encode(' ')) time.sleep(0.01) session.write_bytes(str.encode('%s\n' % cmd)) From 4e6f53a52ba9aa59963a1d68aa91d4695b0c929b Mon Sep 17 00:00:00 2001 From: uruwhy <58484522+uruwhy@users.noreply.github.com> Date: Thu, 8 Jan 2026 22:39:56 +0000 Subject: [PATCH 03/30] fix unit test --- tests/contacts/test_contact_tcp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index 70af5ba38..de19f1306 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -13,7 +13,7 @@ def test_refresh_with_socket_errors(self, event_loop): handler = TcpSessionHandler(services=None, log=logger) session_with_socket_error = mock.Mock() - session_with_socket_error.writer.write.side_effect = socket.error() + session_with_socket_error.write_bytes.side_effect = socket.error() handler.sessions = [ session_with_socket_error, From 75c8a4e0bbfbbeb478dd5503f896ae14e22998a0 Mon Sep 17 00:00:00 2001 From: uruwhy <58484522+uruwhy@users.noreply.github.com> Date: Thu, 8 Jan 2026 22:43:32 +0000 Subject: [PATCH 04/30] style fix --- app/contacts/contact_tcp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/contacts/contact_tcp.py b/app/contacts/contact_tcp.py index a6ca2673a..fe84e4382 100644 --- a/app/contacts/contact_tcp.py +++ b/app/contacts/contact_tcp.py @@ -84,7 +84,7 @@ async def send(self, session_id: int, cmd: str, timeout: int = 60) -> Tuple[int, msg = f'Could not find session with ID {session_id}' self.log.error(msg) return 1, '~$ ', msg, '' - + session.write_bytes(str.encode(' ')) time.sleep(0.01) session.write_bytes(str.encode('%s\n' % cmd)) From 071ad80716726b8eae8571329b1fb5b64b3912ed Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Fri, 9 Jan 2026 15:33:01 -0500 Subject: [PATCH 05/30] Updated test for new Contact class --- tests/contacts/test_contact_tcp.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index de19f1306..f2a66e299 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -3,6 +3,7 @@ from unittest import mock from app.contacts.contact_tcp import TcpSessionHandler +from app.contacts.contact_tcp import Contact logger = logging.getLogger(__name__) @@ -35,3 +36,9 @@ def test_refresh_without_socket_errors(self, event_loop): event_loop.run_until_complete(handler.refresh()) assert len(handler.sessions) == 3 + +class TestContact: + + def test_tcp_contact(self, event_loop): + tcp_c2 = Contact(services=None, log=logger) + return tcp_c2 From a861d61689855ab07eafa921202bbaa75dd9fa37 Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Tue, 13 Jan 2026 15:14:03 -0500 Subject: [PATCH 06/30] Updated test for new Contact class --- tests/contacts/test_contact_tcp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index f2a66e299..650e284cb 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -40,5 +40,5 @@ def test_refresh_without_socket_errors(self, event_loop): class TestContact: def test_tcp_contact(self, event_loop): - tcp_c2 = Contact(services=None, log=logger) + tcp_c2 = Contact(services=None) return tcp_c2 From 8b912cec22dce2abe382c1dda305c5a3619a02ec Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Tue, 13 Jan 2026 17:06:29 -0500 Subject: [PATCH 07/30] Updated test for new Contact class --- tests/contacts/test_contact_tcp.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index 650e284cb..e16404ec6 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -37,8 +37,15 @@ def test_refresh_without_socket_errors(self, event_loop): event_loop.run_until_complete(handler.refresh()) assert len(handler.sessions) == 3 + class TestContact: def test_tcp_contact(self, event_loop): tcp_c2 = Contact(services=None) + assert tcp_c2 is not None + # ensure background tasks are stopped to avoid pending-task warnings + if hasattr(tcp_c2, "stop"): + event_loop.run_until_complete(tcp_c2.stop()) + elif hasattr(tcp_c2, "shutdown"): + event_loop.run_until_complete(tcp_c2.shutdown()) return tcp_c2 From d83b25ecb1c797ad9c6757fa97658a6664a98a5f Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Tue, 13 Jan 2026 17:30:17 -0500 Subject: [PATCH 08/30] Updated test for new Contact class --- tests/contacts/test_contact_tcp.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index e16404ec6..b9e8d2477 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -42,10 +42,11 @@ class TestContact: def test_tcp_contact(self, event_loop): tcp_c2 = Contact(services=None) + tcp_c2.set_up_server = mock.Mock() + event_loop.run_until_complete(tcp_c2.start()) assert tcp_c2 is not None # ensure background tasks are stopped to avoid pending-task warnings if hasattr(tcp_c2, "stop"): event_loop.run_until_complete(tcp_c2.stop()) elif hasattr(tcp_c2, "shutdown"): - event_loop.run_until_complete(tcp_c2.shutdown()) - return tcp_c2 + event_loop.run_until_complete(tcp_c2.shutdown()) \ No newline at end of file From 67fa739536779169ec00ca67f9b2f499a5dd07bb Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Wed, 14 Jan 2026 01:53:35 -0500 Subject: [PATCH 09/30] Updated new Contact class to handle services=None from test_contact_tcp.py --- app/contacts/contact_tcp.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/contacts/contact_tcp.py b/app/contacts/contact_tcp.py index fe84e4382..ec0372ada 100644 --- a/app/contacts/contact_tcp.py +++ b/app/contacts/contact_tcp.py @@ -16,7 +16,9 @@ def __init__(self, services): self.description = 'Accept beacons through a raw TCP socket' self.log = self.create_logger('contact_tcp') self.contact_svc = services.get('contact_svc') - self.tcp_handler = TcpSessionHandler(services, self.log) + # Ensure services is always a mapping to avoid AttributeError when tests pass None + self.services = services or {} + self.tcp_handler = TcpSessionHandler(self.services, self.log) async def start(self): loop = asyncio.get_event_loop() From 0d9cc27a48fee8a09ff00799a7914674f1e13c58 Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Wed, 14 Jan 2026 02:13:42 -0500 Subject: [PATCH 10/30] Updated new Contact class to handle services=None from test_contact_tcp.py --- app/contacts/contact_tcp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/contacts/contact_tcp.py b/app/contacts/contact_tcp.py index ec0372ada..943a8c2f7 100644 --- a/app/contacts/contact_tcp.py +++ b/app/contacts/contact_tcp.py @@ -14,10 +14,10 @@ class Contact(BaseWorld): def __init__(self, services): self.name = 'tcp' self.description = 'Accept beacons through a raw TCP socket' - self.log = self.create_logger('contact_tcp') - self.contact_svc = services.get('contact_svc') # Ensure services is always a mapping to avoid AttributeError when tests pass None self.services = services or {} + self.log = self.create_logger('contact_tcp') + self.contact_svc = self.services.get('contact_svc') self.tcp_handler = TcpSessionHandler(self.services, self.log) async def start(self): From d196b281c958c86d975c96559d9660e628922437 Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Wed, 14 Jan 2026 02:26:21 -0500 Subject: [PATCH 11/30] Updated TestContact class to address test issues --- tests/contacts/test_contact_tcp.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index b9e8d2477..acebe7090 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -43,10 +43,13 @@ class TestContact: def test_tcp_contact(self, event_loop): tcp_c2 = Contact(services=None) tcp_c2.set_up_server = mock.Mock() + # ensure start() has a valid address string to split + # prefer the real attribute name used by Contact (address/server_address/host). If unknown, + # set both common names: + if not getattr(tcp_c2, "server_address", None): + tcp_c2.server_address = "127.0.0.1:0" + if not getattr(tcp_c2, "address", None): + tcp_c2.address = "127.0.0.1:0" event_loop.run_until_complete(tcp_c2.start()) assert tcp_c2 is not None - # ensure background tasks are stopped to avoid pending-task warnings - if hasattr(tcp_c2, "stop"): - event_loop.run_until_complete(tcp_c2.stop()) - elif hasattr(tcp_c2, "shutdown"): - event_loop.run_until_complete(tcp_c2.shutdown()) \ No newline at end of file + assert tcp_c2.tcp_handler is not None From 79e1ef92e44934c7e52e3b1610423d8df0b83183 Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Wed, 14 Jan 2026 07:21:05 -0500 Subject: [PATCH 12/30] Updated TestContact class to address test issues --- tests/contacts/test_contact_tcp.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index acebe7090..7202bcde2 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -41,15 +41,6 @@ def test_refresh_without_socket_errors(self, event_loop): class TestContact: def test_tcp_contact(self, event_loop): - tcp_c2 = Contact(services=None) - tcp_c2.set_up_server = mock.Mock() - # ensure start() has a valid address string to split - # prefer the real attribute name used by Contact (address/server_address/host). If unknown, - # set both common names: - if not getattr(tcp_c2, "server_address", None): - tcp_c2.server_address = "127.0.0.1:0" - if not getattr(tcp_c2, "address", None): - tcp_c2.address = "127.0.0.1:0" + tcp_c2 = Contact(services=mock.Mock())) event_loop.run_until_complete(tcp_c2.start()) - assert tcp_c2 is not None - assert tcp_c2.tcp_handler is not None + assert tcp_c2 is not None \ No newline at end of file From 14b85be27db2ab56f60b80bccc9b3d3e81bd5cac Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Wed, 14 Jan 2026 07:25:08 -0500 Subject: [PATCH 13/30] Updated TestContact class to address test issues --- tests/contacts/test_contact_tcp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index 7202bcde2..f1e894ff2 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -41,6 +41,6 @@ def test_refresh_without_socket_errors(self, event_loop): class TestContact: def test_tcp_contact(self, event_loop): - tcp_c2 = Contact(services=mock.Mock())) + tcp_c2 = Contact(services=mock.Mock()) event_loop.run_until_complete(tcp_c2.start()) assert tcp_c2 is not None \ No newline at end of file From 853578b4cf0c21a17d68997ba7f2c1283574f77f Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Wed, 14 Jan 2026 07:32:27 -0500 Subject: [PATCH 14/30] Updated TestContact class to address test issues --- tests/contacts/test_contact_tcp.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index f1e894ff2..8b4d58187 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -41,6 +41,7 @@ def test_refresh_without_socket_errors(self, event_loop): class TestContact: def test_tcp_contact(self, event_loop): - tcp_c2 = Contact(services=mock.Mock()) + self.services = {'data_svc': mock.Mock(), 'knowledge_svc': mock.Mock(), 'contact_svc': mock.Mock(), 'planning_svc': mock.Mock(), 'rest_svc': mock.Mock(), 'auth_svc': mock.Mock(), 'file_svc': mock.Mock(), 'learning_svc': mock.Mock(), 'event_svc': mock.Mock(), 'app_svc': mock.Mock()} + tcp_c2 = Contact(services=self.services) event_loop.run_until_complete(tcp_c2.start()) assert tcp_c2 is not None \ No newline at end of file From c440be1b7aeda3160345f8a8aecfaf72015e69bd Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Wed, 14 Jan 2026 13:00:52 -0500 Subject: [PATCH 15/30] Updated TestContact class to address test issues --- tests/contacts/test_contact_tcp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index 8b4d58187..19644f728 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -41,7 +41,7 @@ def test_refresh_without_socket_errors(self, event_loop): class TestContact: def test_tcp_contact(self, event_loop): - self.services = {'data_svc': mock.Mock(), 'knowledge_svc': mock.Mock(), 'contact_svc': mock.Mock(), 'planning_svc': mock.Mock(), 'rest_svc': mock.Mock(), 'auth_svc': mock.Mock(), 'file_svc': mock.Mock(), 'learning_svc': mock.Mock(), 'event_svc': mock.Mock(), 'app_svc': mock.Mock()} + self.services = app_svc.get_services() tcp_c2 = Contact(services=self.services) event_loop.run_until_complete(tcp_c2.start()) assert tcp_c2 is not None \ No newline at end of file From 6999dc07f402d9755ba8ae7e6a5800c97d5184f3 Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Fri, 16 Jan 2026 12:33:29 -0500 Subject: [PATCH 16/30] Updated TestContact class to address test issues --- tests/contacts/test_contact_tcp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index 19644f728..86db28e43 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -40,7 +40,7 @@ def test_refresh_without_socket_errors(self, event_loop): class TestContact: - def test_tcp_contact(self, event_loop): + def test_tcp_contact(self, event_loop, app_svc): self.services = app_svc.get_services() tcp_c2 = Contact(services=self.services) event_loop.run_until_complete(tcp_c2.start()) From c3f610bf45b5bb6d64501fd2594dbcb920aed221 Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Fri, 16 Jan 2026 13:52:38 -0500 Subject: [PATCH 17/30] Updated TestContact class to address test issues --- app/contacts/contact_tcp.py | 3 +-- tests/contacts/test_contact_tcp.py | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/contacts/contact_tcp.py b/app/contacts/contact_tcp.py index 943a8c2f7..d35771539 100644 --- a/app/contacts/contact_tcp.py +++ b/app/contacts/contact_tcp.py @@ -14,8 +14,7 @@ class Contact(BaseWorld): def __init__(self, services): self.name = 'tcp' self.description = 'Accept beacons through a raw TCP socket' - # Ensure services is always a mapping to avoid AttributeError when tests pass None - self.services = services or {} + self.services = services self.log = self.create_logger('contact_tcp') self.contact_svc = self.services.get('contact_svc') self.tcp_handler = TcpSessionHandler(self.services, self.log) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index 86db28e43..84484dce9 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -2,6 +2,7 @@ import socket from unittest import mock +from app.utility.base_world import BaseWorld from app.contacts.contact_tcp import TcpSessionHandler from app.contacts.contact_tcp import Contact @@ -41,6 +42,7 @@ def test_refresh_without_socket_errors(self, event_loop): class TestContact: def test_tcp_contact(self, event_loop, app_svc): + BaseWorld.set_config('main', 'app.contact.tcp', '127.0.0.1:57012') self.services = app_svc.get_services() tcp_c2 = Contact(services=self.services) event_loop.run_until_complete(tcp_c2.start()) From ea59f65516628c61a5a072a10160b0ce580da2e3 Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Fri, 16 Jan 2026 14:02:19 -0500 Subject: [PATCH 18/30] Fixed newline issue at end of file --- tests/contacts/test_contact_tcp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index 84484dce9..f3addd624 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -46,4 +46,4 @@ def test_tcp_contact(self, event_loop, app_svc): self.services = app_svc.get_services() tcp_c2 = Contact(services=self.services) event_loop.run_until_complete(tcp_c2.start()) - assert tcp_c2 is not None \ No newline at end of file + assert tcp_c2 is not None From 1831020313efdd60daca8c59714332d768c3575e Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Tue, 20 Jan 2026 10:18:09 -0500 Subject: [PATCH 19/30] Added to test_contact_tcp.py --- tests/contacts/test_contact_tcp.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index f3addd624..6d8df9a83 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -1,6 +1,7 @@ import logging import socket from unittest import mock +import time from app.utility.base_world import BaseWorld from app.contacts.contact_tcp import TcpSessionHandler @@ -45,5 +46,31 @@ def test_tcp_contact(self, event_loop, app_svc): BaseWorld.set_config('main', 'app.contact.tcp', '127.0.0.1:57012') self.services = app_svc.get_services() tcp_c2 = Contact(services=self.services) + tcp_c2.tcp_handler.sessions = [ + mock.Mock(), + mock.Mock(), + mock.Mock() + ] + event_loop.run_until_complete(tcp_c2.start()) + tcp_c2.tcp_handler.accept = mock.AsyncMock() + tcp_c2.tcp_handler.send = mock.AsyncMock(return_value=(200, None, b'response', time.time())) + tcp_c2.contact_svc.handle_heartbeat = mock.AsyncMock(return_value=(None, [])) + tcp_c2.tcp_handler.refresh = mock.AsyncMock() + tcp_c2.operation_loop = mock.AsyncMock() + tcp_c2.tcp_handler._attempt_connection = mock.AsyncMock() + event_loop.run_until_complete(tcp_c2.start()) + assert tcp_c2 is not None + + def test_tcp_contact_errors(self, event_loop, app_svc): + BaseWorld.set_config('main', 'app.contact.tcp', '127.0.0.1:57012') + self.services = app_svc.get_services() + tcp_c2 = Contact(services=self.services) + session_with_socket_error = mock.Mock() + session_with_socket_error.write_bytes.side_effect = socket.error() + tcp_c2.tcp_handler.sessions = [ + session_with_socket_error, + session_with_socket_error, + mock.Mock() + ] event_loop.run_until_complete(tcp_c2.start()) assert tcp_c2 is not None From e293d514875d33330587d9a902757ac84d27ecff Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Wed, 21 Jan 2026 15:42:45 -0500 Subject: [PATCH 20/30] Initial patching for TestContact added to test_contact_tcp.py --- tests/contacts/test_contact_tcp.py | 57 +++++++++++++++++++----------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index 6d8df9a83..fdf25d55d 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -42,6 +42,40 @@ def test_refresh_without_socket_errors(self, event_loop): class TestContact: + def setUp(self): + self.patcher1 = mock.patch('app.contacts.contact_tcp.asyncio.start_server', autospec=True) + self.patcher2 = mock.patch('app.contacts.contact_tcp.asyncio.get_event_loop', autospec=True) + self.patcher3 = mock.patch('app.contacts.contact_tcp.BaseWorld.get_config', autospec=True) + self.patcher4 = mock.patch('app.contacts.contact_tcp.Contact.create_logger', autospec=True) + self.patcher5 = mock.patch('app.contacts.contact_tcp.Contact.operation_loop', autospec=True) + self.patcher6 = mock.patch('app.contacts.contact_tcp.Contact.decode_bytes', autospec=True) + self.patcher7 = mock.patch('app.contacts.contact_tcp.Contact.encode_string', autospec=True) + self.patcher8 = mock.patch('app.contacts.contact_tcp.Contact.services', autospec=True) + self.patcher9 = mock.patch('app.contacts.contact_tcp.Contact.contact_svc', autospec=True) + self.patcher10 = mock.patch('app.contacts.contact_tcp.TcpSessionHandler.send', autospec=True) + self.patcher11 = mock.patch('app.contacts.contact_tcp.TcpSessionHandler.accept', autospec=True) + self.patcher12 = mock.patch('app.contacts.contact_tcp.TcpSessionHandler.refresh', autospec=True) + self.patcher13 = mock.patch('app.contacts.utility.c_tcp_session.TCPSession.write_bytes', autospec=True) + self.patcher14 = mock.patch('app.contacts.utility.c_tcp_session.TCPSession.read_bytes', autospec=True) + self.patcher15 = mock.patch('app.contacts.contact_tcp.TcpSessionHandler._handshake', autospec=True) + self.patcher16 = mock.patch('app.contacts.contact_tcp.TcpSessionHandler._attempt_connection', autospec=True) + self.patcher1.start() + self.patcher2.start() + self.patcher3.start() + self.patcher4.start() + self.patcher5.start() + self.patcher6.start() + self.patcher7.start() + self.patcher8.start() + self.patcher9.start() + self.patcher10.start() + self.patcher11.start() + self.patcher12.start() + self.patcher13.start() + self.patcher14.start() + self.patcher15.start() + self.patcher16.start() + def test_tcp_contact(self, event_loop, app_svc): BaseWorld.set_config('main', 'app.contact.tcp', '127.0.0.1:57012') self.services = app_svc.get_services() @@ -52,25 +86,8 @@ def test_tcp_contact(self, event_loop, app_svc): mock.Mock() ] event_loop.run_until_complete(tcp_c2.start()) - tcp_c2.tcp_handler.accept = mock.AsyncMock() - tcp_c2.tcp_handler.send = mock.AsyncMock(return_value=(200, None, b'response', time.time())) - tcp_c2.contact_svc.handle_heartbeat = mock.AsyncMock(return_value=(None, [])) - tcp_c2.tcp_handler.refresh = mock.AsyncMock() - tcp_c2.operation_loop = mock.AsyncMock() - tcp_c2.tcp_handler._attempt_connection = mock.AsyncMock() - event_loop.run_until_complete(tcp_c2.start()) + event_loop.run_until_complete(tcp_c2.tcp_handler.accept(None, None)) assert tcp_c2 is not None - def test_tcp_contact_errors(self, event_loop, app_svc): - BaseWorld.set_config('main', 'app.contact.tcp', '127.0.0.1:57012') - self.services = app_svc.get_services() - tcp_c2 = Contact(services=self.services) - session_with_socket_error = mock.Mock() - session_with_socket_error.write_bytes.side_effect = socket.error() - tcp_c2.tcp_handler.sessions = [ - session_with_socket_error, - session_with_socket_error, - mock.Mock() - ] - event_loop.run_until_complete(tcp_c2.start()) - assert tcp_c2 is not None + def tearDown(self): + mock.patch.stopall() \ No newline at end of file From 2a842079d64112f0ebbf8bc1b9b6de71032fe04e Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Wed, 21 Jan 2026 15:55:51 -0500 Subject: [PATCH 21/30] Corrected leftover artifacts in test_contact_tcp.py --- tests/contacts/test_contact_tcp.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index fdf25d55d..616c8f083 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -1,7 +1,6 @@ import logging import socket from unittest import mock -import time from app.utility.base_world import BaseWorld from app.contacts.contact_tcp import TcpSessionHandler @@ -90,4 +89,4 @@ def test_tcp_contact(self, event_loop, app_svc): assert tcp_c2 is not None def tearDown(self): - mock.patch.stopall() \ No newline at end of file + mock.patch.stopall() From f8581880d74f6fcee612c821c7cb63afd97ac384 Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Wed, 21 Jan 2026 16:18:48 -0500 Subject: [PATCH 22/30] Added patchers to test_contact_tcp.py --- tests/contacts/test_contact_tcp.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index 616c8f083..7323ecefd 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -58,6 +58,13 @@ def setUp(self): self.patcher14 = mock.patch('app.contacts.utility.c_tcp_session.TCPSession.read_bytes', autospec=True) self.patcher15 = mock.patch('app.contacts.contact_tcp.TcpSessionHandler._handshake', autospec=True) self.patcher16 = mock.patch('app.contacts.contact_tcp.TcpSessionHandler._attempt_connection', autospec=True) + self.patcher17 = mock.patch('app.contacts.utility.c_tcp_session.TCPSession.store', autospec=True) + self.patcher18 = mock.patch('app.contacts.utility.c_tcp_session.TCPSession.__init__', autospec=True) + self.patcher19 = mock.patch('app.contacts.contact_tcp.Contact.handle_sessions', autospec=True) + self.patcher20 = mock.patch('app.contacts.contact_tcp.Contact.handle_sessions.contact_svc.handle_heartbeat', autospec=True) + self.patcher21 = mock.patch('app.contacts.contact_tcp.TcpSessionHandler.accept._handshake', autospec=True) + self.patcher22 = mock.patch('app.contacts.contact_tcp.TcpSessionHandler.accept._handshake', autospec=True) + self.patcher23 = mock.patch('app.contacts.contact_tcp.TcpSessionHandler.sessions', autospec=True) self.patcher1.start() self.patcher2.start() self.patcher3.start() @@ -74,6 +81,12 @@ def setUp(self): self.patcher14.start() self.patcher15.start() self.patcher16.start() + self.patcher17.start() + self.patcher18.start() + self.patcher19.start() + self.patcher20.start() + self.patcher21.start() + self.patcher22.start() def test_tcp_contact(self, event_loop, app_svc): BaseWorld.set_config('main', 'app.contact.tcp', '127.0.0.1:57012') From cd22dc34a48736512329135d73bb98621c563c64 Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Fri, 30 Jan 2026 19:47:04 -0500 Subject: [PATCH 23/30] Added test for _attempt_connection function --- tests/contacts/test_contact_tcp.py | 76 ++++++++++-------------------- 1 file changed, 24 insertions(+), 52 deletions(-) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index 7323ecefd..fa644379d 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -1,10 +1,13 @@ import logging import socket from unittest import mock +import pytest + from app.utility.base_world import BaseWorld from app.contacts.contact_tcp import TcpSessionHandler from app.contacts.contact_tcp import Contact +from app.contacts.utility.c_tcp_session import TCPSession logger = logging.getLogger(__name__) @@ -39,59 +42,24 @@ def test_refresh_without_socket_errors(self, event_loop): assert len(handler.sessions) == 3 +@pytest.fixture +def tcp_c2(app_svc, contact_svc, data_svc, obfuscator): + services = app_svc.get_services() + tcp_contact_svc = Contact(services=services) + return tcp_contact_svc + +class _MockReader: + async def read(self, n=-1): + return b'MockContent' + +class _MockWriter: + def write(self, data): + pass + class TestContact: - def setUp(self): - self.patcher1 = mock.patch('app.contacts.contact_tcp.asyncio.start_server', autospec=True) - self.patcher2 = mock.patch('app.contacts.contact_tcp.asyncio.get_event_loop', autospec=True) - self.patcher3 = mock.patch('app.contacts.contact_tcp.BaseWorld.get_config', autospec=True) - self.patcher4 = mock.patch('app.contacts.contact_tcp.Contact.create_logger', autospec=True) - self.patcher5 = mock.patch('app.contacts.contact_tcp.Contact.operation_loop', autospec=True) - self.patcher6 = mock.patch('app.contacts.contact_tcp.Contact.decode_bytes', autospec=True) - self.patcher7 = mock.patch('app.contacts.contact_tcp.Contact.encode_string', autospec=True) - self.patcher8 = mock.patch('app.contacts.contact_tcp.Contact.services', autospec=True) - self.patcher9 = mock.patch('app.contacts.contact_tcp.Contact.contact_svc', autospec=True) - self.patcher10 = mock.patch('app.contacts.contact_tcp.TcpSessionHandler.send', autospec=True) - self.patcher11 = mock.patch('app.contacts.contact_tcp.TcpSessionHandler.accept', autospec=True) - self.patcher12 = mock.patch('app.contacts.contact_tcp.TcpSessionHandler.refresh', autospec=True) - self.patcher13 = mock.patch('app.contacts.utility.c_tcp_session.TCPSession.write_bytes', autospec=True) - self.patcher14 = mock.patch('app.contacts.utility.c_tcp_session.TCPSession.read_bytes', autospec=True) - self.patcher15 = mock.patch('app.contacts.contact_tcp.TcpSessionHandler._handshake', autospec=True) - self.patcher16 = mock.patch('app.contacts.contact_tcp.TcpSessionHandler._attempt_connection', autospec=True) - self.patcher17 = mock.patch('app.contacts.utility.c_tcp_session.TCPSession.store', autospec=True) - self.patcher18 = mock.patch('app.contacts.utility.c_tcp_session.TCPSession.__init__', autospec=True) - self.patcher19 = mock.patch('app.contacts.contact_tcp.Contact.handle_sessions', autospec=True) - self.patcher20 = mock.patch('app.contacts.contact_tcp.Contact.handle_sessions.contact_svc.handle_heartbeat', autospec=True) - self.patcher21 = mock.patch('app.contacts.contact_tcp.TcpSessionHandler.accept._handshake', autospec=True) - self.patcher22 = mock.patch('app.contacts.contact_tcp.TcpSessionHandler.accept._handshake', autospec=True) - self.patcher23 = mock.patch('app.contacts.contact_tcp.TcpSessionHandler.sessions', autospec=True) - self.patcher1.start() - self.patcher2.start() - self.patcher3.start() - self.patcher4.start() - self.patcher5.start() - self.patcher6.start() - self.patcher7.start() - self.patcher8.start() - self.patcher9.start() - self.patcher10.start() - self.patcher11.start() - self.patcher12.start() - self.patcher13.start() - self.patcher14.start() - self.patcher15.start() - self.patcher16.start() - self.patcher17.start() - self.patcher18.start() - self.patcher19.start() - self.patcher20.start() - self.patcher21.start() - self.patcher22.start() - - def test_tcp_contact(self, event_loop, app_svc): + def test_tcp_contact(self, event_loop, tcp_c2): BaseWorld.set_config('main', 'app.contact.tcp', '127.0.0.1:57012') - self.services = app_svc.get_services() - tcp_c2 = Contact(services=self.services) tcp_c2.tcp_handler.sessions = [ mock.Mock(), mock.Mock(), @@ -101,5 +69,9 @@ def test_tcp_contact(self, event_loop, app_svc): event_loop.run_until_complete(tcp_c2.tcp_handler.accept(None, None)) assert tcp_c2 is not None - def tearDown(self): - mock.patch.stopall() + + + async def test_attempt_connection(self, tcp_c2): + MockSession = TCPSession(id=123456, paw='testpaw', reader=_MockReader(), writer=_MockWriter()) + assert "MockContent" == await tcp_c2.tcp_handler._attempt_connection(MockSession, timeout=1) + From d0b5a45e5f33a2c4bfffd4a6f4fa11373bf4a61a Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Fri, 30 Jan 2026 19:56:15 -0500 Subject: [PATCH 24/30] Removed blank lines for flake8 compliance --- tests/contacts/test_contact_tcp.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index fa644379d..bc3a0588f 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -69,9 +69,6 @@ def test_tcp_contact(self, event_loop, tcp_c2): event_loop.run_until_complete(tcp_c2.tcp_handler.accept(None, None)) assert tcp_c2 is not None - - async def test_attempt_connection(self, tcp_c2): MockSession = TCPSession(id=123456, paw='testpaw', reader=_MockReader(), writer=_MockWriter()) assert "MockContent" == await tcp_c2.tcp_handler._attempt_connection(MockSession, timeout=1) - From e0cba5c1d8a44a0f3ff69de2d1bcda720a4af945 Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Fri, 30 Jan 2026 20:07:24 -0500 Subject: [PATCH 25/30] Added blank lines for flake8 compliance --- tests/contacts/test_contact_tcp.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index bc3a0588f..b03c02a7a 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -48,14 +48,17 @@ def tcp_c2(app_svc, contact_svc, data_svc, obfuscator): tcp_contact_svc = Contact(services=services) return tcp_contact_svc + class _MockReader: async def read(self, n=-1): return b'MockContent' + class _MockWriter: def write(self, data): pass + class TestContact: def test_tcp_contact(self, event_loop, tcp_c2): From 22346416b2d15ea5dfd158bff84d20f857ff4455 Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Mon, 2 Feb 2026 11:59:26 -0500 Subject: [PATCH 26/30] Added test for handle_sessions function within Contact class --- tests/contacts/test_contact_tcp.py | 69 ++++++++++++++++++------------ 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index b03c02a7a..61ff20f0a 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -4,14 +4,33 @@ import pytest +from app.service.contact_svc import ContactService from app.utility.base_world import BaseWorld from app.contacts.contact_tcp import TcpSessionHandler from app.contacts.contact_tcp import Contact from app.contacts.utility.c_tcp_session import TCPSession +from app.objects.secondclass.c_instruction import Instruction logger = logging.getLogger(__name__) +@pytest.fixture +def tcp_c2(app_svc, contact_svc, data_svc, obfuscator): + services = app_svc.get_services() + tcp_contact_svc = Contact(services=services) + return tcp_contact_svc + + +class _MockReader: + async def read(self, n=-1): + return b'MockContent' + + +class _MockWriter: + def write(self, data): + pass + + class TestTcpSessionHandler: def test_refresh_with_socket_errors(self, event_loop): @@ -41,37 +60,33 @@ def test_refresh_without_socket_errors(self, event_loop): event_loop.run_until_complete(handler.refresh()) assert len(handler.sessions) == 3 - -@pytest.fixture -def tcp_c2(app_svc, contact_svc, data_svc, obfuscator): - services = app_svc.get_services() - tcp_contact_svc = Contact(services=services) - return tcp_contact_svc - - -class _MockReader: - async def read(self, n=-1): - return b'MockContent' - - -class _MockWriter: - def write(self, data): - pass + async def test_attempt_connection(self, tcp_c2): + MockSession = TCPSession(id=123456, paw='testpaw', reader=_MockReader(), writer=_MockWriter()) + assert "MockContent" == await tcp_c2.tcp_handler._attempt_connection(MockSession, timeout=1) class TestContact: def test_tcp_contact(self, event_loop, tcp_c2): BaseWorld.set_config('main', 'app.contact.tcp', '127.0.0.1:57012') - tcp_c2.tcp_handler.sessions = [ - mock.Mock(), - mock.Mock(), - mock.Mock() - ] + dummy_instruction = Instruction( + id='123', + sleep=5, + command='whoami', + executor='sh', + timeout=60, + payloads=[], + uploads=[], + deadman=False, + delete_payload=True + ) + tcp_c2.tcp_handler.sessions.append(TCPSession( + id=1, + paw='dummy_paw', + reader=_MockReader(), + writer=_MockWriter() + )) event_loop.run_until_complete(tcp_c2.start()) - event_loop.run_until_complete(tcp_c2.tcp_handler.accept(None, None)) - assert tcp_c2 is not None - - async def test_attempt_connection(self, tcp_c2): - MockSession = TCPSession(id=123456, paw='testpaw', reader=_MockReader(), writer=_MockWriter()) - assert "MockContent" == await tcp_c2.tcp_handler._attempt_connection(MockSession, timeout=1) + with mock.patch.object(ContactService, 'handle_heartbeat', return_value=('dummy_paw', [dummy_instruction])): + event_loop.run_until_complete(tcp_c2.handle_sessions()) + assert len(tcp_c2.tcp_handler.sessions) == 1 From ea5862c2871147536df4baa91de6b21af6f25fe1 Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Mon, 2 Feb 2026 14:09:35 -0500 Subject: [PATCH 27/30] Added tests for accept and send functions within TcpSessionHandler class --- tests/contacts/test_contact_tcp.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index 61ff20f0a..94b00561d 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -64,6 +64,26 @@ async def test_attempt_connection(self, tcp_c2): MockSession = TCPSession(id=123456, paw='testpaw', reader=_MockReader(), writer=_MockWriter()) assert "MockContent" == await tcp_c2.tcp_handler._attempt_connection(MockSession, timeout=1) + async def test_accept(self, tcp_c2): + dummy_profile = { + 'architecture': 'amd64', + 'exe_name': 'splunkd', + 'executors': 'sh', + 'host': 'Caldera', + 'location': './splunkd', + 'pid': 10057, + 'platform': 'linux', + 'ppid': 9752, + 'server': '0.0.0.0:7010', 'username': 'caldera' + } + with mock.patch.object(TcpSessionHandler, '_handshake', return_value=(dummy_profile)): + await tcp_c2.tcp_handler.accept(reader=_MockReader(), writer=_MockWriter()) + assert len(tcp_c2.tcp_handler.sessions) is not None + + async def test_accept_err(self, tcp_c2): + await tcp_c2.tcp_handler.accept(reader=_MockReader(), writer=_MockWriter()) + assert len(tcp_c2.tcp_handler.sessions) is not None + class TestContact: From 3a9e433d7e476d0b3377c027d209bcba81684f7b Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Wed, 4 Feb 2026 12:47:51 -0500 Subject: [PATCH 28/30] Added test for send function (invalid session id) within TcpSessionHandler class --- tests/contacts/test_contact_tcp.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index 94b00561d..fe14b3645 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -84,6 +84,11 @@ async def test_accept_err(self, tcp_c2): await tcp_c2.tcp_handler.accept(reader=_MockReader(), writer=_MockWriter()) assert len(tcp_c2.tcp_handler.sessions) is not None + async def test_send_no_session(self, tcp_c2): + status, pwd, response, agent_time = await tcp_c2.tcp_handler.send(session_id=999999, cmd='whoami', timeout=1) + assert status == 1 + assert 'Could not find session' in response + class TestContact: From 65d38aa8c50473bfaa549fd58440d134ba5edd01 Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Wed, 4 Feb 2026 12:59:47 -0500 Subject: [PATCH 29/30] Added tests for send function (session errors) within TcpSessionHandler class --- tests/contacts/test_contact_tcp.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index fe14b3645..cfc934b96 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -89,6 +89,22 @@ async def test_send_no_session(self, tcp_c2): assert status == 1 assert 'Could not find session' in response + async def test_send_with_session_err(self, tcp_c2): + mock_session = TCPSession(id=123456, paw='testpaw', reader=_MockReader(), writer=_MockWriter()) + tcp_c2.tcp_handler.sessions.append(mock_session) + with mock.patch.object(TcpSessionHandler, '_attempt_connection', side_effect=Exception('Test exception')): + status, pwd, response, agent_time = await tcp_c2.tcp_handler.send(session_id=123456, cmd='whoami', timeout=1) + assert status == 1 + assert 'Test exception' in response + + async def test_send_with_session_no_response(self, tcp_c2): + mock_session = TCPSession(id=123456, paw='testpaw', reader=_MockReader(), writer=_MockWriter()) + tcp_c2.tcp_handler.sessions.append(mock_session) + with mock.patch.object(TcpSessionHandler, '_attempt_connection', return_value=None): + status, pwd, response, agent_time = await tcp_c2.tcp_handler.send(session_id=123456, cmd='whoami', timeout=1) + assert status == 1 + assert 'Failed to read data' in response + class TestContact: From d2cdd55268efbe0048d91c2c64b8af41003d8ed8 Mon Sep 17 00:00:00 2001 From: uruwhy <58484522+uruwhy@users.noreply.github.com> Date: Fri, 6 Feb 2026 05:50:20 +0000 Subject: [PATCH 30/30] update tcp tests --- tests/contacts/test_contact_tcp.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/tests/contacts/test_contact_tcp.py b/tests/contacts/test_contact_tcp.py index cfc934b96..ceaaeca57 100644 --- a/tests/contacts/test_contact_tcp.py +++ b/tests/contacts/test_contact_tcp.py @@ -74,20 +74,24 @@ async def test_accept(self, tcp_c2): 'pid': 10057, 'platform': 'linux', 'ppid': 9752, - 'server': '0.0.0.0:7010', 'username': 'caldera' + 'server': '0.0.0.0:7010', + 'username': 'caldera' } with mock.patch.object(TcpSessionHandler, '_handshake', return_value=(dummy_profile)): await tcp_c2.tcp_handler.accept(reader=_MockReader(), writer=_MockWriter()) - assert len(tcp_c2.tcp_handler.sessions) is not None + assert len(tcp_c2.tcp_handler.sessions) == 1 async def test_accept_err(self, tcp_c2): - await tcp_c2.tcp_handler.accept(reader=_MockReader(), writer=_MockWriter()) - assert len(tcp_c2.tcp_handler.sessions) is not None + with mock.patch.object(TcpSessionHandler, '_handshake', side_effect=Exception('mock exception')): + await tcp_c2.tcp_handler.accept(reader=_MockReader(), writer=_MockWriter()) + assert len(tcp_c2.tcp_handler.sessions) == 0 async def test_send_no_session(self, tcp_c2): status, pwd, response, agent_time = await tcp_c2.tcp_handler.send(session_id=999999, cmd='whoami', timeout=1) assert status == 1 - assert 'Could not find session' in response + assert 'Could not find session with ID 999999' == response + assert pwd == '~$ ' + assert agent_time == '' async def test_send_with_session_err(self, tcp_c2): mock_session = TCPSession(id=123456, paw='testpaw', reader=_MockReader(), writer=_MockWriter()) @@ -95,19 +99,22 @@ async def test_send_with_session_err(self, tcp_c2): with mock.patch.object(TcpSessionHandler, '_attempt_connection', side_effect=Exception('Test exception')): status, pwd, response, agent_time = await tcp_c2.tcp_handler.send(session_id=123456, cmd='whoami', timeout=1) assert status == 1 - assert 'Test exception' in response + assert 'Test exception' == response + assert pwd == '~$ ' + assert agent_time == '' async def test_send_with_session_no_response(self, tcp_c2): mock_session = TCPSession(id=123456, paw='testpaw', reader=_MockReader(), writer=_MockWriter()) tcp_c2.tcp_handler.sessions.append(mock_session) - with mock.patch.object(TcpSessionHandler, '_attempt_connection', return_value=None): + with mock.patch.object(TcpSessionHandler, '_attempt_connection', return_value=''): status, pwd, response, agent_time = await tcp_c2.tcp_handler.send(session_id=123456, cmd='whoami', timeout=1) assert status == 1 - assert 'Failed to read data' in response + assert 'Failed to read data from session 123456' == response + assert pwd == '~$ ' + assert agent_time == '' class TestContact: - def test_tcp_contact(self, event_loop, tcp_c2): BaseWorld.set_config('main', 'app.contact.tcp', '127.0.0.1:57012') dummy_instruction = Instruction(