Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d897644
Create a new clean Command & CommandContext interface
bitterpanda63 Nov 13, 2025
1e005c0
Remove ATTACK event
bitterpanda63 Nov 13, 2025
dc5777f
Create a PutEventCommand - with the new modern command standard
bitterpanda63 Nov 13, 2025
c5f4251
Cleanup the process_incoming_command
bitterpanda63 Nov 13, 2025
2d30deb
Merge branch 'main' into clean-attack-reporting
bitterpanda63 Nov 24, 2025
0b35c50
Create a report_event on the cloud_connection_manager
bitterpanda63 Nov 24, 2025
14b34ed
use this report_event
bitterpanda63 Nov 24, 2025
cf3bf1c
cleanup useless functions on cloud_connection_manager
bitterpanda63 Nov 24, 2025
a81cfa5
rename to report_api_event
bitterpanda63 Nov 24, 2025
d179445
update the queue empty-ing
bitterpanda63 Nov 24, 2025
964ad49
Update comment in put_event
bitterpanda63 Nov 24, 2025
48d6df9
remove on_detected_attack in favour of create_detected_attack_api_event
bitterpanda63 Nov 24, 2025
fba6c2c
use the new create_detected_attack_api_event
bitterpanda63 Nov 24, 2025
7d4c053
move test cases for create_detected_attack_api_event
bitterpanda63 Nov 24, 2025
346d4e2
update how event is generated in vulnerabilities
bitterpanda63 Nov 24, 2025
f811a61
create a send_payload
bitterpanda63 Nov 24, 2025
fa94194
update command types to add Payload class
bitterpanda63 Nov 24, 2025
b7a25ac
create send_payload helper function
bitterpanda63 Nov 24, 2025
dcfe12d
move command.py to command_types in helper
bitterpanda63 Nov 24, 2025
88676cd
move is_private_ip to aikido_zen/helpers/net
bitterpanda63 Nov 24, 2025
5d481a4
Fix regular old commands
bitterpanda63 Nov 24, 2025
e240d03
Add limit to the serialize_to_json
bitterpanda63 Nov 24, 2025
5a7e6d1
revert: add limit
bitterpanda63 Nov 24, 2025
4901d34
improve log in vulnerabilities/__init__.py
bitterpanda63 Nov 24, 2025
9884fee
linting fix
bitterpanda63 Nov 24, 2025
34d1a32
Update e2e test to include regex package
bitterpanda63 Nov 24, 2025
2ac1c58
Fix test cases for new put_event data structure changes
bitterpanda63 Nov 24, 2025
f1648e9
Merge branch 'main' into clean-attack-reporting
bitterpanda63 Dec 2, 2025
d7014ec
Update aikido_zen/background_process/comms.py
bitterpanda63 Dec 8, 2025
d97b807
Update aikido_zen/background_process/commands/put_event.py
bitterpanda63 Dec 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions aikido_zen/background_process/aikido_background_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,7 @@ def send_to_connection_manager(self, event_scheduler):
EMPTY_QUEUE_INTERVAL, 1, self.send_to_connection_manager, (event_scheduler,)
)
while not self.queue.empty():
queue_attack_item = self.queue.get()
self.connection_manager.on_detected_attack(
attack=queue_attack_item[0],
context=queue_attack_item[1],
blocked=queue_attack_item[2],
stack=queue_attack_item[3],
)
self.connection_manager.report_api_event(self.queue.get())
Comment thread
bitterpanda63 marked this conversation as resolved.


def add_exit_handlers():
Expand Down
39 changes: 25 additions & 14 deletions aikido_zen/background_process/cloud_connection_manager/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
from aikido_zen.storage.users import Users
from aikido_zen.storage.hostnames import Hostnames
from ..realtime.start_polling_for_changes import start_polling_for_changes
from ...helpers.get_current_unixtime_ms import get_unixtime_ms
from ...storage.ai_statistics import AIStatistics
from ...storage.firewall_lists import FirewallLists
from ...storage.statistics import Statistics

# Import functions :
from .on_detected_attack import on_detected_attack
from .get_manager_info import get_manager_info
from .update_service_config import update_service_config
from .on_start import on_start
Expand Down Expand Up @@ -57,7 +57,7 @@ def __init__(self, block, api, token, serverless):

def start(self, event_scheduler):
"""Send out start event and add heartbeats"""
res = self.on_start()
res = on_start(self)
if res.get("error", None) == "invalid_token":
logger.info(
"Token was invalid, not starting heartbeats and realtime polling."
Expand All @@ -81,26 +81,37 @@ def report_initial_stats(self):
if should_report_initial_stats:
self.send_heartbeat()

def on_detected_attack(self, attack, context, blocked, stack):
"""This will send something to the API when an attack is detected"""
return on_detected_attack(self, attack, context, blocked, stack)

def on_start(self):
"""This will send out an Event signalling the start to the server"""
return on_start(self)

def send_heartbeat(self):
"""This will send a heartbeat to the server"""
return send_heartbeat(self)

def get_manager_info(self):
"""This returns info about the connection_manager"""
return get_manager_info(self)

def update_service_config(self, res):
"""Update configuration based on the server's response"""
return update_service_config(self, res)

def update_firewall_lists(self):
"""Will update service config with blocklist of IP addresses"""
return update_firewall_lists(self)

def report_api_event(self, event):
if not self.token:
return {"success": False, "error": "invalid_token"}
try:
payload = {
"time": get_unixtime_ms(),
"agent": get_manager_info(self),
}
payload.update(event) # Merge default fields with event fields

result = self.api.report(self.token, payload, self.timeout_in_sec)
Comment thread
bitterpanda63 marked this conversation as resolved.
if not result.get("success", True):
logger.error(
"CloudConnectionManager: Reporting to api failed, error=%s",
result.get("error", "unknown"),
)
return result
except Exception as e:
logger.debug(e)
logger.error(
"CloudConnectionManager: Reporting to api failed, unexpected error (see debug logs)"
)

This file was deleted.

24 changes: 7 additions & 17 deletions aikido_zen/background_process/cloud_connection_manager/on_start.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,16 @@


def on_start(connection_manager):
"""
This will send out an Event signalling the start to the server
"""
if not connection_manager.token:
return
res = connection_manager.api.report(
connection_manager.token,
{
"type": "started",
"time": get_unixtime_ms(),
"agent": connection_manager.get_manager_info(),
},
connection_manager.timeout_in_sec,
)
event = {"type": "started"}
Copy link
Copy Markdown

@aikido-pr-checks aikido-pr-checks Bot Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function on_start no longer fails fast when connection_manager.token is missing; the prior early-return was removed, reducing clarity of control flow

Details

🔧 How do I fix it?
Place parameter validation and guard clauses at the function start. Use early returns to reduce nesting levels and improve readability.

More info - Comment @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.

res = connection_manager.report_api_event(event)

if not res.get("success", True):
# Update config time even in failure :
connection_manager.conf.last_updated_at = get_unixtime_ms()
logger.error("Failed to communicate with Aikido Server : %s", res["error"])
connection_manager.conf.last_updated_at = (
get_unixtime_ms()
) # Update config time even in failure
else:
connection_manager.update_service_config(res)
connection_manager.update_firewall_lists()
logger.info("Established connection with Aikido Server")

return res
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def mock_connection_manager():
connection_manager = MagicMock()
connection_manager.token = "test_token"
connection_manager.timeout_in_sec = 5
connection_manager.api.report = MagicMock(return_value={"success": True})
connection_manager.report_api_event = MagicMock(return_value={"success": True})
connection_manager.get_manager_info = lambda: {}
connection_manager.update_service_config = MagicMock()
return connection_manager
Expand All @@ -19,15 +19,15 @@ def test_on_start_no_token():
connection_manager = MagicMock()
connection_manager.token = None
on_start(connection_manager)
connection_manager.api.report.assert_not_called()
connection_manager.report_api_event.assert_called()


def test_on_start_success(mock_connection_manager, caplog):
"""Test that the API call is made successfully and the service config is updated."""
on_start(mock_connection_manager)

# Check that the API report method was called
mock_connection_manager.api.report.assert_called_once()
mock_connection_manager.report_api_event.assert_called_once()

# Check that the service config was updated
mock_connection_manager.update_service_config.assert_called_once()
Expand All @@ -38,18 +38,15 @@ def test_on_start_success(mock_connection_manager, caplog):

def test_on_start_failure(mock_connection_manager, caplog):
"""Test that an error is logged when the API call fails."""
mock_connection_manager.api.report.return_value = {
mock_connection_manager.report_api_event.return_value = {
"success": False,
"error": "Some error",
}

on_start(mock_connection_manager)

# Check that the API report method was called
mock_connection_manager.api.report.assert_called_once()
mock_connection_manager.report_api_event.assert_called_once()

# Check that the service config was not updated
mock_connection_manager.update_service_config.assert_not_called()

# Check that the error log was called
assert "Failed to communicate with Aikido Server : Some error" in caplog.text
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from aikido_zen.background_process.packages import PackagesStore
from aikido_zen.helpers.logging import logger
from aikido_zen.helpers.get_current_unixtime_ms import get_unixtime_ms


def send_heartbeat(connection_manager):
Expand All @@ -26,20 +25,16 @@ def send_heartbeat(connection_manager):
connection_manager.ai_stats.clear()
PackagesStore.clear()

res = connection_manager.api.report(
connection_manager.token,
res = connection_manager.report_api_event(
Comment thread
bitterpanda63 marked this conversation as resolved.
{
"type": "heartbeat",
"time": get_unixtime_ms(),
"agent": connection_manager.get_manager_info(),
"stats": stats,
"ai": ai_stats,
"hostnames": outgoing_domains,
"packages": packages,
"routes": routes,
"users": users,
"middlewareInstalled": connection_manager.middleware_installed,
},
connection_manager.timeout_in_sec,
}
)
connection_manager.update_service_config(res)
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ def update_service_config(connection_manager, res):
Update configuration based on the server's response
"""
if res.get("success", False) is False:
logger.debug(res)
return

if "block" in res.keys() and res["block"] != connection_manager.block:
logger.debug("Updating blocking, setting blocking to : %s", res["block"])
connection_manager.block = bool(res["block"])
Expand Down
44 changes: 22 additions & 22 deletions aikido_zen/background_process/commands/__init__.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
"""Commands __init__.py file"""

from aikido_zen.helpers.logging import logger
from .attack import process_attack
from aikido_zen.helpers.ipc.command_types import CommandContext
from .put_event import PutEventCommand
from .check_firewall_lists import process_check_firewall_lists
from .read_property import process_read_property
from .should_ratelimit import process_should_ratelimit
from .ping import process_ping
from .sync_data import process_sync_data

commands_map = {
# This maps to a tuple : (function, returns_data?)
# Commands that don't return data :
"ATTACK": (process_attack, False),
# Commands that return data :
"SYNC_DATA": (process_sync_data, True),
"READ_PROPERTY": (process_read_property, True),
"SHOULD_RATELIMIT": (process_should_ratelimit, True),
"PING": (process_ping, True),
"CHECK_FIREWALL_LISTS": (process_check_firewall_lists, True),
"SYNC_DATA": process_sync_data,
"READ_PROPERTY": process_read_property,
"SHOULD_RATELIMIT": process_should_ratelimit,
"PING": process_ping,
"CHECK_FIREWALL_LISTS": process_check_firewall_lists,
}

modern_commands = [PutEventCommand]


def process_incoming_command(connection_manager, obj, conn, queue):
"""Processes an incoming command"""
action = obj[0]
data = obj[1]
if action in commands_map:
func, returns_data = commands_map[action]
if returns_data:
return conn.send(func(connection_manager, data, queue))
func(connection_manager, data, queue)
else:
logger.debug("Command : `%s` not found, aborting", action)
inbound_identifier = obj[0]
inbound_request = obj[1]
if inbound_identifier in commands_map:
func = commands_map[inbound_identifier]
return conn.send(func(connection_manager, inbound_request))

for cmd in modern_commands:
if cmd.identifier() == inbound_identifier:
cmd.run(CommandContext(connection_manager, queue, conn), inbound_request)
return None

logger.debug("Command : `%s` not found - did not execute", inbound_identifier)
return None
9 changes: 0 additions & 9 deletions aikido_zen/background_process/commands/attack.py

This file was deleted.

Loading
Loading