forked from p15670423/monkey
Merge branch '2274-implement-new-agent-logs-endpoint' into develop
PR #2365
This commit is contained in:
commit
4f3fd6987e
|
@ -25,6 +25,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
encrypting files. #1242
|
encrypting files. #1242
|
||||||
- `/api/agents` endpoint.
|
- `/api/agents` endpoint.
|
||||||
- `/api/agent-signals` endpoint. #2261
|
- `/api/agent-signals` endpoint. #2261
|
||||||
|
- `/api/agent-logs/<uuid:agent_id>` endpoint. #2274
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Reset workflow. Now it's possible to delete data gathered by agents without
|
- Reset workflow. Now it's possible to delete data gathered by agents without
|
||||||
|
|
|
@ -14,6 +14,7 @@ from monkey_island.cc.resources import (
|
||||||
AgentBinaries,
|
AgentBinaries,
|
||||||
AgentConfiguration,
|
AgentConfiguration,
|
||||||
AgentEvents,
|
AgentEvents,
|
||||||
|
AgentLogs,
|
||||||
Agents,
|
Agents,
|
||||||
AgentSignals,
|
AgentSignals,
|
||||||
ClearSimulationData,
|
ClearSimulationData,
|
||||||
|
@ -185,6 +186,7 @@ def init_restful_endpoints(api: FlaskDIWrapper):
|
||||||
api.add_resource(ZeroTrustFindingEvent)
|
api.add_resource(ZeroTrustFindingEvent)
|
||||||
api.add_resource(TelemetryFeed)
|
api.add_resource(TelemetryFeed)
|
||||||
api.add_resource(Log)
|
api.add_resource(Log)
|
||||||
|
api.add_resource(AgentLogs)
|
||||||
api.add_resource(IslandLog)
|
api.add_resource(IslandLog)
|
||||||
api.add_resource(IPAddresses)
|
api.add_resource(IPAddresses)
|
||||||
|
|
||||||
|
|
|
@ -11,3 +11,4 @@ from .pba_file_download import PBAFileDownload
|
||||||
from .agent_events import AgentEvents
|
from .agent_events import AgentEvents
|
||||||
from .agents import Agents
|
from .agents import Agents
|
||||||
from .agent_signals import AgentSignals, TerminateAllAgents
|
from .agent_signals import AgentSignals, TerminateAllAgents
|
||||||
|
from .agent_logs import AgentLogs
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import logging
|
||||||
|
from http import HTTPStatus
|
||||||
|
|
||||||
|
from flask import request
|
||||||
|
|
||||||
|
from common.types import AgentID
|
||||||
|
from monkey_island.cc.repository import IAgentLogRepository, UnknownRecordError
|
||||||
|
from monkey_island.cc.resources.AbstractResource import AbstractResource
|
||||||
|
from monkey_island.cc.resources.request_authentication import jwt_required
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class AgentLogs(AbstractResource):
|
||||||
|
urls = ["/api/agent-logs/<uuid:agent_id>"]
|
||||||
|
|
||||||
|
def __init__(self, agent_log_repository: IAgentLogRepository):
|
||||||
|
self._agent_log_repository = agent_log_repository
|
||||||
|
|
||||||
|
@jwt_required
|
||||||
|
def get(self, agent_id: AgentID):
|
||||||
|
try:
|
||||||
|
log_contents = self._agent_log_repository.get_agent_log(agent_id)
|
||||||
|
except UnknownRecordError as err:
|
||||||
|
logger.error(f"Error occurred while getting agent log: {err}")
|
||||||
|
return {}, HTTPStatus.NOT_FOUND
|
||||||
|
|
||||||
|
return log_contents, HTTPStatus.OK
|
||||||
|
|
||||||
|
def put(self, agent_id: AgentID):
|
||||||
|
log_contents = request.json
|
||||||
|
|
||||||
|
self._agent_log_repository.upsert_agent_log(agent_id, log_contents)
|
||||||
|
|
||||||
|
return {}, HTTPStatus.NO_CONTENT
|
|
@ -1,6 +1,7 @@
|
||||||
import logging
|
import logging
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
|
|
||||||
|
from common.types import AgentID
|
||||||
from monkey_island.cc.resources.AbstractResource import AbstractResource
|
from monkey_island.cc.resources.AbstractResource import AbstractResource
|
||||||
from monkey_island.cc.services import AgentSignalsService
|
from monkey_island.cc.services import AgentSignalsService
|
||||||
|
|
||||||
|
@ -8,7 +9,7 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class AgentSignals(AbstractResource):
|
class AgentSignals(AbstractResource):
|
||||||
urls = ["/api/agent-signals/<string:agent_id>"]
|
urls = ["/api/agent-signals/<uuid:agent_id>"]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
@ -16,6 +17,6 @@ class AgentSignals(AbstractResource):
|
||||||
):
|
):
|
||||||
self._agent_signals_service = agent_signals_service
|
self._agent_signals_service = agent_signals_service
|
||||||
|
|
||||||
def get(self, agent_id: str):
|
def get(self, agent_id: AgentID):
|
||||||
agent_signals = self._agent_signals_service.get_signals(agent_id)
|
agent_signals = self._agent_signals_service.get_signals(agent_id)
|
||||||
return agent_signals.dict(simplify=True), HTTPStatus.OK
|
return agent_signals.dict(simplify=True), HTTPStatus.OK
|
||||||
|
|
|
@ -4,4 +4,5 @@ from .open_error_file_repository import OpenErrorFileRepository
|
||||||
from .in_memory_agent_configuration_repository import InMemoryAgentConfigurationRepository
|
from .in_memory_agent_configuration_repository import InMemoryAgentConfigurationRepository
|
||||||
from .in_memory_simulation_configuration import InMemorySimulationRepository
|
from .in_memory_simulation_configuration import InMemorySimulationRepository
|
||||||
from .in_memory_credentials_repository import InMemoryCredentialsRepository
|
from .in_memory_credentials_repository import InMemoryCredentialsRepository
|
||||||
|
from .in_memory_agent_log_repository import InMemoryAgentLogRepository
|
||||||
from .in_memory_file_repository import InMemoryFileRepository
|
from .in_memory_file_repository import InMemoryFileRepository
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
from common.types import AgentID
|
||||||
|
from monkey_island.cc.repository import IAgentLogRepository, UnknownRecordError
|
||||||
|
|
||||||
|
|
||||||
|
class InMemoryAgentLogRepository(IAgentLogRepository):
|
||||||
|
def __init__(self):
|
||||||
|
self._agent_logs = {}
|
||||||
|
|
||||||
|
def upsert_agent_log(self, agent_id: AgentID, log_contents: str):
|
||||||
|
if agent_id not in self._agent_logs.keys():
|
||||||
|
self._agent_logs[agent_id] = log_contents
|
||||||
|
|
||||||
|
def get_agent_log(self, agent_id: AgentID) -> str:
|
||||||
|
if agent_id not in self._agent_logs:
|
||||||
|
raise UnknownRecordError(f"Unknown agent {agent_id}")
|
||||||
|
|
||||||
|
return self._agent_logs[agent_id]
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self._agent_logs = {}
|
|
@ -0,0 +1,39 @@
|
||||||
|
from http import HTTPStatus
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from tests.common import StubDIContainer
|
||||||
|
from tests.monkey_island import InMemoryAgentLogRepository
|
||||||
|
|
||||||
|
from monkey_island.cc.repository import IAgentLogRepository
|
||||||
|
|
||||||
|
AGENT_ID_1 = UUID("c0dd10b3-e21a-4da9-9d96-a99c19ebd7c5")
|
||||||
|
AGENT_ID_2 = UUID("f811ad00-5a68-4437-bd51-7b5cc1768ad5")
|
||||||
|
|
||||||
|
AGENT_LOGS_URL_1 = f"/api/agent-logs/{AGENT_ID_1}"
|
||||||
|
AGENT_LOGS_URL_2 = f"/api/agent-logs/{AGENT_ID_2}"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def flask_client(build_flask_client):
|
||||||
|
container = StubDIContainer()
|
||||||
|
container.register_instance(IAgentLogRepository, InMemoryAgentLogRepository())
|
||||||
|
|
||||||
|
with build_flask_client(container) as flask_client:
|
||||||
|
yield flask_client
|
||||||
|
|
||||||
|
|
||||||
|
def test_agent_logs_endpoint__get_empty(flask_client):
|
||||||
|
resp = flask_client.get(AGENT_LOGS_URL_1, follow_redirects=True)
|
||||||
|
assert resp.status_code == HTTPStatus.NOT_FOUND
|
||||||
|
assert resp.json == {}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"url,log", [(AGENT_LOGS_URL_1, "LoremIpsum1"), (AGENT_LOGS_URL_2, "SecondLoremIpsum")]
|
||||||
|
)
|
||||||
|
def test_agent_logs_endpoint(flask_client, url, log):
|
||||||
|
flask_client.put(url, json=log, follow_redirects=True)
|
||||||
|
resp = flask_client.get(url, follow_redirects=True)
|
||||||
|
assert resp.status_code == HTTPStatus.OK
|
||||||
|
assert resp.json == log
|
Loading…
Reference in New Issue