forked from p15670423/monkey
UT: Add unit tests for aws_command_runner
This commit is contained in:
parent
79eb584c5d
commit
27f8195be5
|
@ -7,6 +7,8 @@ from common.utils import Timer
|
||||||
|
|
||||||
REMOTE_COMMAND_TIMEOUT = 5
|
REMOTE_COMMAND_TIMEOUT = 5
|
||||||
STATUS_CHECK_SLEEP_TIME = 1
|
STATUS_CHECK_SLEEP_TIME = 1
|
||||||
|
LINUX_DOCUMENT_NAME = "AWS-RunShellScript"
|
||||||
|
WINDOWS_DOCUMENT_NAME = "AWS-RunPowerShellScript"
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -66,7 +68,7 @@ def _get_run_monkey_cmd_windows_line(island_ip):
|
||||||
def _run_command_async(
|
def _run_command_async(
|
||||||
aws_client: botocore.client.BaseClient, target_instance_id: str, target_os: str, command: str
|
aws_client: botocore.client.BaseClient, target_instance_id: str, target_os: str, command: str
|
||||||
):
|
):
|
||||||
doc_name = "AWS-RunShellScript" if target_os == "linux" else "AWS-RunPowerShellScript"
|
doc_name = LINUX_DOCUMENT_NAME if target_os == "linux" else WINDOWS_DOCUMENT_NAME
|
||||||
|
|
||||||
logger.debug(f'Running command on {target_instance_id} -- {doc_name}: "{command}"')
|
logger.debug(f'Running command on {target_instance_id} -- {doc_name}: "{command}"')
|
||||||
command_response = aws_client.send_command(
|
command_response = aws_client.send_command(
|
||||||
|
|
|
@ -0,0 +1,220 @@
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from monkey_island.cc.services.aws.aws_command_runner import (
|
||||||
|
LINUX_DOCUMENT_NAME,
|
||||||
|
WINDOWS_DOCUMENT_NAME,
|
||||||
|
start_infection_monkey_agent,
|
||||||
|
)
|
||||||
|
|
||||||
|
INSTANCE_ID = "BEEFFACE"
|
||||||
|
ISLAND_IP = "127.0.0.1"
|
||||||
|
"""
|
||||||
|
"commands": [
|
||||||
|
"wget --no-check-certificate "
|
||||||
|
"https://172.31.32.78:5000/api/agent/download/linux "
|
||||||
|
"-O monkey-linux-64; chmod +x "
|
||||||
|
"monkey-linux-64; ./monkey-linux-64 "
|
||||||
|
"m0nk3y -s 172.31.32.78:5000"
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def send_command_response():
|
||||||
|
return {
|
||||||
|
"Command": {
|
||||||
|
"CloudWatchOutputConfig": {
|
||||||
|
"CloudWatchLogGroupName": "",
|
||||||
|
"CloudWatchOutputEnabled": False,
|
||||||
|
},
|
||||||
|
"CommandId": "fe3cf24f-71b7-42b9-93ca-e27c34dd0581",
|
||||||
|
"CompletedCount": 0,
|
||||||
|
"DocumentName": "AWS-RunShellScript",
|
||||||
|
"DocumentVersion": "$DEFAULT",
|
||||||
|
"InstanceIds": ["i-0b62d6f0b0d9d7e77"],
|
||||||
|
"OutputS3Region": "eu-central-1",
|
||||||
|
"Parameters": {"commands": []},
|
||||||
|
"Status": "Pending",
|
||||||
|
"StatusDetails": "Pending",
|
||||||
|
"TargetCount": 1,
|
||||||
|
"Targets": [],
|
||||||
|
"TimeoutSeconds": 3600,
|
||||||
|
},
|
||||||
|
"ResponseMetadata": {
|
||||||
|
"HTTPHeaders": {
|
||||||
|
"connection": "keep-alive",
|
||||||
|
"content-length": "973",
|
||||||
|
"content-type": "application/x-amz-json-1.1",
|
||||||
|
"date": "Tue, 10 May 2022 12:35:49 GMT",
|
||||||
|
"server": "Server",
|
||||||
|
"x-amzn-requestid": "110b1563-aaf0-4e09-bd23-2db465822be7",
|
||||||
|
},
|
||||||
|
"HTTPStatusCode": 200,
|
||||||
|
"RequestId": "110b1563-aaf0-4e09-bd23-2db465822be7",
|
||||||
|
"RetryAttempts": 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def in_progress_response():
|
||||||
|
return {
|
||||||
|
"CloudWatchOutputConfig": {"CloudWatchLogGroupName": "", "CloudWatchOutputEnabled": False},
|
||||||
|
"CommandId": "a5332cc6-0f9f-48e6-826a-d4bd7cabc2ee",
|
||||||
|
"Comment": "",
|
||||||
|
"DocumentName": "AWS-RunShellScript",
|
||||||
|
"DocumentVersion": "$DEFAULT",
|
||||||
|
"ExecutionEndDateTime": "",
|
||||||
|
"InstanceId": "i-0b62d6f0b0d9d7e77",
|
||||||
|
"PluginName": "aws:runShellScript",
|
||||||
|
"ResponseCode": -1,
|
||||||
|
"StandardErrorContent": "",
|
||||||
|
"StandardErrorUrl": "",
|
||||||
|
"StandardOutputContent": "",
|
||||||
|
"StandardOutputUrl": "",
|
||||||
|
"Status": "InProgress",
|
||||||
|
"StatusDetails": "InProgress",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def success_response():
|
||||||
|
return {
|
||||||
|
"CloudWatchOutputConfig": {"CloudWatchLogGroupName": "", "CloudWatchOutputEnabled": False},
|
||||||
|
"CommandId": "a5332cc6-0f9f-48e6-826a-d4bd7cabc2ee",
|
||||||
|
"Comment": "",
|
||||||
|
"DocumentName": "AWS-RunShellScript",
|
||||||
|
"DocumentVersion": "$DEFAULT",
|
||||||
|
"ExecutionEndDateTime": "",
|
||||||
|
"InstanceId": "i-0b62d6f0b0d9d7e77",
|
||||||
|
"PluginName": "aws:runShellScript",
|
||||||
|
"ResponseCode": -1,
|
||||||
|
"StandardErrorContent": "",
|
||||||
|
"StandardErrorUrl": "",
|
||||||
|
"StandardOutputContent": "",
|
||||||
|
"StandardOutputUrl": "",
|
||||||
|
"Status": "Success",
|
||||||
|
"StatusDetails": "Success",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def error_response():
|
||||||
|
return {
|
||||||
|
"CloudWatchOutputConfig": {"CloudWatchLogGroupName": "", "CloudWatchOutputEnabled": False},
|
||||||
|
"CommandId": "a5332cc6-0f9f-48e6-826a-d4bd7cabc2ee",
|
||||||
|
"Comment": "",
|
||||||
|
"DocumentName": "AWS-RunShellScript",
|
||||||
|
"DocumentVersion": "$DEFAULT",
|
||||||
|
"ExecutionEndDateTime": "",
|
||||||
|
"InstanceId": "i-0b62d6f0b0d9d7e77",
|
||||||
|
"PluginName": "aws:runShellScript",
|
||||||
|
"ResponseCode": -1,
|
||||||
|
"StandardErrorContent": "ERROR RUNNING COMMAND",
|
||||||
|
"StandardErrorUrl": "",
|
||||||
|
"StandardOutputContent": "",
|
||||||
|
"StandardOutputUrl": "",
|
||||||
|
# NOTE: "Error" is technically not a valid value for this field, but we want to test that
|
||||||
|
# anything other than "Success" and "InProgress" is treated as an error. This is
|
||||||
|
# simpler than testing all of the different possible error cases.
|
||||||
|
"Status": "Error",
|
||||||
|
"StatusDetails": "Error",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def patch_timeouts(monkeypatch):
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"monkey_island.cc.services.aws.aws_command_runner.REMOTE_COMMAND_TIMEOUT", 0.03
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"monkey_island.cc.services.aws.aws_command_runner.STATUS_CHECK_SLEEP_TIME", 0.01
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def successful_mock_client(send_command_response, success_response):
|
||||||
|
aws_client = MagicMock()
|
||||||
|
aws_client.send_command = MagicMock(return_value=send_command_response)
|
||||||
|
aws_client.get_command_invocation = MagicMock(return_value=success_response)
|
||||||
|
|
||||||
|
return aws_client
|
||||||
|
|
||||||
|
|
||||||
|
def test_correct_instance_id(successful_mock_client, send_command_response, success_response):
|
||||||
|
start_infection_monkey_agent(successful_mock_client, INSTANCE_ID, "linux", ISLAND_IP)
|
||||||
|
|
||||||
|
successful_mock_client.send_command.assert_called_once()
|
||||||
|
assert successful_mock_client.send_command.call_args.kwargs["InstanceIds"] == [INSTANCE_ID]
|
||||||
|
|
||||||
|
|
||||||
|
def test_linux_doc_name(successful_mock_client, send_command_response, success_response):
|
||||||
|
start_infection_monkey_agent(successful_mock_client, INSTANCE_ID, "linux", ISLAND_IP)
|
||||||
|
|
||||||
|
successful_mock_client.send_command.assert_called_once()
|
||||||
|
assert (
|
||||||
|
successful_mock_client.send_command.call_args.kwargs["DocumentName"] == LINUX_DOCUMENT_NAME
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_windows_doc_name(successful_mock_client, send_command_response, success_response):
|
||||||
|
start_infection_monkey_agent(successful_mock_client, INSTANCE_ID, "windows", ISLAND_IP)
|
||||||
|
|
||||||
|
successful_mock_client.send_command.assert_called_once()
|
||||||
|
assert (
|
||||||
|
successful_mock_client.send_command.call_args.kwargs["DocumentName"]
|
||||||
|
== WINDOWS_DOCUMENT_NAME
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_linux_command(successful_mock_client, send_command_response, success_response):
|
||||||
|
start_infection_monkey_agent(successful_mock_client, INSTANCE_ID, "linux", ISLAND_IP)
|
||||||
|
|
||||||
|
successful_mock_client.send_command.assert_called_once()
|
||||||
|
assert (
|
||||||
|
"wget" in successful_mock_client.send_command.call_args.kwargs["Parameters"]["commands"][0]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_windows_command(successful_mock_client, send_command_response, success_response):
|
||||||
|
start_infection_monkey_agent(successful_mock_client, INSTANCE_ID, "windows", ISLAND_IP)
|
||||||
|
|
||||||
|
successful_mock_client.send_command.assert_called_once()
|
||||||
|
assert (
|
||||||
|
"DownloadFile"
|
||||||
|
in successful_mock_client.send_command.call_args.kwargs["Parameters"]["commands"][0]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_in_progress_no_timeout(send_command_response, in_progress_response, success_response):
|
||||||
|
aws_client = MagicMock()
|
||||||
|
aws_client.send_command = MagicMock(return_value=send_command_response)
|
||||||
|
aws_client.get_command_invocation = MagicMock(
|
||||||
|
side_effect=[in_progress_response, in_progress_response, success_response]
|
||||||
|
)
|
||||||
|
|
||||||
|
# If this test fails, an exception will be raised
|
||||||
|
start_infection_monkey_agent(aws_client, INSTANCE_ID, "windows", ISLAND_IP)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Address this test case
|
||||||
|
"""
|
||||||
|
def test_in_progress_timeout(send_command_response, in_progress_response):
|
||||||
|
aws_client = MagicMock()
|
||||||
|
aws_client.send_command = MagicMock(return_value=send_command_response)
|
||||||
|
aws_client.get_command_invocation = MagicMock(return_value=in_progress_response)
|
||||||
|
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
start_infection_monkey_agent(aws_client, INSTANCE_ID, "windows", ISLAND_IP)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def test_failed_command(send_command_response, error_response):
|
||||||
|
aws_client = MagicMock()
|
||||||
|
aws_client.send_command = MagicMock(return_value=send_command_response)
|
||||||
|
aws_client.get_command_invocation = MagicMock(return_value=error_response)
|
||||||
|
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
start_infection_monkey_agent(aws_client, INSTANCE_ID, "windows", ISLAND_IP)
|
Loading…
Reference in New Issue