Merge branch '2274-implement-new-agent-logs-endpoint' into develop

PR #2365
This commit is contained in:
Mike Salvatore 2022-09-28 14:19:21 -04:00
commit 4f3fd6987e
8 changed files with 102 additions and 2 deletions

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 = {}

View File

@ -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