forked from p15670423/monkey
commit
dd882df1cc
|
@ -46,6 +46,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
#1761, #1695, #1605, #2028, #2003
|
#1761, #1695, #1605, #2028, #2003
|
||||||
- `/api/island-mode` to accept and return new "unset" mode. #2036
|
- `/api/island-mode` to accept and return new "unset" mode. #2036
|
||||||
- `/api/version-update` to `api/island/version`. #2109
|
- `/api/version-update` to `api/island/version`. #2109
|
||||||
|
- The `/api/island-mode` to `/api/island/mode`. #2106
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
- VSFTPD exploiter. #1533
|
- VSFTPD exploiter. #1533
|
||||||
|
|
|
@ -120,7 +120,7 @@ class MonkeyIslandClient(object):
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
def _reset_island_mode(self):
|
def _reset_island_mode(self):
|
||||||
if self.requests.post("api/island-mode", data='{"mode": "unset"}').ok:
|
if self.requests.put("api/island/mode", data='{"mode": "unset"}').ok:
|
||||||
LOGGER.info("Resseting island mode after the test.")
|
LOGGER.info("Resseting island mode after the test.")
|
||||||
else:
|
else:
|
||||||
LOGGER.error("Failed to reset island mode")
|
LOGGER.error("Failed to reset island mode")
|
||||||
|
|
|
@ -82,6 +82,12 @@ class MonkeyIslandRequests(object):
|
||||||
self.addr + url, data=data, headers=self.get_jwt_header(), verify=False
|
self.addr + url, data=data, headers=self.get_jwt_header(), verify=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@_Decorators.refresh_jwt_token
|
||||||
|
def put(self, url, data):
|
||||||
|
return requests.put( # noqa: DUO123
|
||||||
|
self.addr + url, data=data, headers=self.get_jwt_header(), verify=False
|
||||||
|
)
|
||||||
|
|
||||||
@_Decorators.refresh_jwt_token
|
@_Decorators.refresh_jwt_token
|
||||||
def post_json(self, url, json: Dict):
|
def post_json(self, url, json: Dict):
|
||||||
return requests.post( # noqa: DUO123
|
return requests.post( # noqa: DUO123
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
from http import HTTPStatus
|
||||||
|
|
||||||
from flask import make_response, request
|
from flask import make_response, request
|
||||||
|
|
||||||
|
@ -12,30 +13,26 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class IslandMode(AbstractResource):
|
class IslandMode(AbstractResource):
|
||||||
urls = ["/api/island-mode"]
|
urls = ["/api/island/mode"]
|
||||||
|
|
||||||
def __init__(self, island_mode_service: IslandModeService):
|
def __init__(self, island_mode_service: IslandModeService):
|
||||||
self._island_mode_service = island_mode_service
|
self._island_mode_service = island_mode_service
|
||||||
|
|
||||||
# API Spec: Instead of POST, this should be PUT
|
|
||||||
@jwt_required
|
@jwt_required
|
||||||
def post(self):
|
def put(self):
|
||||||
try:
|
try:
|
||||||
body = json.loads(request.data)
|
body = json.loads(request.data)
|
||||||
mode = IslandModeEnum(body.get("mode"))
|
mode = IslandModeEnum(body.get("mode"))
|
||||||
|
|
||||||
self._island_mode_service.set_mode(mode)
|
self._island_mode_service.set_mode(mode)
|
||||||
|
|
||||||
# TODO: Do any of these returns need a body and make_response? What happens if we just
|
return {}, HTTPStatus.NO_CONTENT
|
||||||
# return the response code?
|
|
||||||
# API Spec: This should be 204 (NO CONTENT)
|
|
||||||
return make_response({}, 200)
|
|
||||||
except (AttributeError, json.decoder.JSONDecodeError):
|
except (AttributeError, json.decoder.JSONDecodeError):
|
||||||
return make_response({}, 400)
|
return {}, HTTPStatus.BAD_REQUEST
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return make_response({}, 422)
|
return {}, HTTPStatus.UNPROCESSABLE_ENTITY
|
||||||
|
|
||||||
@jwt_required
|
@jwt_required
|
||||||
def get(self):
|
def get(self):
|
||||||
island_mode = self._island_mode_service.get_mode()
|
island_mode = self._island_mode_service.get_mode()
|
||||||
return make_response({"mode": island_mode.value}, 200)
|
return make_response({"mode": island_mode.value}, HTTPStatus.OK)
|
||||||
|
|
|
@ -12,11 +12,11 @@ export class Response{
|
||||||
}
|
}
|
||||||
|
|
||||||
class IslandHttpClient extends AuthComponent {
|
class IslandHttpClient extends AuthComponent {
|
||||||
post(endpoint: string, contents: any): Promise<Response>{
|
put(endpoint: string, contents: any): Promise<Response>{
|
||||||
let status = null;
|
let status = null;
|
||||||
return this.authFetch(endpoint,
|
return this.authFetch(endpoint,
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'PUT',
|
||||||
headers: {'Content-Type': 'application/json'},
|
headers: {'Content-Type': 'application/json'},
|
||||||
body: JSON.stringify(contents)
|
body: JSON.stringify(contents)
|
||||||
})
|
})
|
||||||
|
|
|
@ -113,7 +113,7 @@ class AppComponent extends AuthComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
setMode = () => {
|
setMode = () => {
|
||||||
return IslandHttpClient.get('/api/island-mode')
|
return IslandHttpClient.get('/api/island/mode')
|
||||||
.then(res => {
|
.then(res => {
|
||||||
this.setState({islandMode: res.body.mode});
|
this.setState({islandMode: res.body.mode});
|
||||||
});
|
});
|
||||||
|
|
|
@ -74,7 +74,7 @@ const LandingPageComponent = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setScenario(scenario: string) {
|
function setScenario(scenario: string) {
|
||||||
IslandHttpClient.post('/api/island-mode', {'mode': scenario})
|
IslandHttpClient.put('/api/island/mode', {'mode': scenario})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
props.onStatusChange();
|
props.onStatusChange();
|
||||||
});
|
});
|
||||||
|
|
|
@ -96,7 +96,7 @@ const IslandResetModal = (props: Props) => {
|
||||||
function clearSimulationData(callback: () => void) {
|
function clearSimulationData(callback: () => void) {
|
||||||
auth.authFetch('/api/clear-simulation-data', {method: 'POST'})
|
auth.authFetch('/api/clear-simulation-data', {method: 'POST'})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.status === 200) {
|
if (res.ok) {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -104,19 +104,19 @@ const IslandResetModal = (props: Props) => {
|
||||||
function resetAll() {
|
function resetAll() {
|
||||||
auth.authFetch('/api/reset-agent-configuration', {method: 'POST'})
|
auth.authFetch('/api/reset-agent-configuration', {method: 'POST'})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.status === 200) {
|
if (res.ok) {
|
||||||
return auth.authFetch('/api/clear-simulation-data', {method: 'POST'})
|
return auth.authFetch('/api/clear-simulation-data', {method: 'POST'})
|
||||||
}})
|
}})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.status === 200) {
|
if (res.ok) {
|
||||||
return auth.authFetch('/api/propagation-credentials/configured-credentials', {method: 'PUT', body:'[]'})
|
return auth.authFetch('/api/propagation-credentials/configured-credentials', {method: 'PUT', body:'[]'})
|
||||||
}})
|
}})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.status === 200) {
|
if (res.ok) {
|
||||||
return auth.authFetch('/api/island-mode', {method: 'POST', body: '{"mode": "unset"}'})
|
return auth.authFetch('/api/island/mode', {method: 'PUT', body: '{"mode": "unset"}'})
|
||||||
}})
|
}})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.status !== 200) {
|
if (!res.ok) {
|
||||||
throw 'Error resetting the simulation'
|
throw 'Error resetting the simulation'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import json
|
import json
|
||||||
|
from http import HTTPStatus
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -36,23 +37,23 @@ def flask_client(build_flask_client):
|
||||||
[IslandMode.RANSOMWARE.value, IslandMode.ADVANCED.value, IslandMode.UNSET.value],
|
[IslandMode.RANSOMWARE.value, IslandMode.ADVANCED.value, IslandMode.UNSET.value],
|
||||||
)
|
)
|
||||||
def test_island_mode_post(flask_client, mode):
|
def test_island_mode_post(flask_client, mode):
|
||||||
resp = flask_client.post(
|
resp = flask_client.put(
|
||||||
IslandModeResource.urls[0], data=json.dumps({"mode": mode}), follow_redirects=True
|
IslandModeResource.urls[0], data=json.dumps({"mode": mode}), follow_redirects=True
|
||||||
)
|
)
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == HTTPStatus.NO_CONTENT
|
||||||
|
|
||||||
|
|
||||||
def test_island_mode_post__invalid_mode(flask_client):
|
def test_island_mode_post__invalid_mode(flask_client):
|
||||||
resp = flask_client.post(
|
resp = flask_client.put(
|
||||||
IslandModeResource.urls[0], data=json.dumps({"mode": "bogus mode"}), follow_redirects=True
|
IslandModeResource.urls[0], data=json.dumps({"mode": "bogus mode"}), follow_redirects=True
|
||||||
)
|
)
|
||||||
assert resp.status_code == 422
|
assert resp.status_code == HTTPStatus.UNPROCESSABLE_ENTITY
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("invalid_json", ["42", "{test"])
|
@pytest.mark.parametrize("invalid_json", ["42", "{test"])
|
||||||
def test_island_mode_post__invalid_json(flask_client, invalid_json):
|
def test_island_mode_post__invalid_json(flask_client, invalid_json):
|
||||||
resp = flask_client.post(IslandModeResource.urls[0], data="{test", follow_redirects=True)
|
resp = flask_client.put(IslandModeResource.urls[0], data="{test", follow_redirects=True)
|
||||||
assert resp.status_code == 400
|
assert resp.status_code == HTTPStatus.BAD_REQUEST
|
||||||
|
|
||||||
|
|
||||||
def test_island_mode_post__internal_server_error(build_flask_client):
|
def test_island_mode_post__internal_server_error(build_flask_client):
|
||||||
|
@ -63,29 +64,29 @@ def test_island_mode_post__internal_server_error(build_flask_client):
|
||||||
container.register_instance(IslandModeService, mock_island_mode_service)
|
container.register_instance(IslandModeService, mock_island_mode_service)
|
||||||
|
|
||||||
with build_flask_client(container) as flask_client:
|
with build_flask_client(container) as flask_client:
|
||||||
resp = flask_client.post(
|
resp = flask_client.put(
|
||||||
IslandModeResource.urls[0],
|
IslandModeResource.urls[0],
|
||||||
data=json.dumps({"mode": IslandMode.RANSOMWARE.value}),
|
data=json.dumps({"mode": IslandMode.RANSOMWARE.value}),
|
||||||
follow_redirects=True,
|
follow_redirects=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert resp.status_code == 500
|
assert resp.status_code == HTTPStatus.INTERNAL_SERVER_ERROR
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("mode", [IslandMode.RANSOMWARE.value, IslandMode.ADVANCED.value])
|
@pytest.mark.parametrize("mode", [IslandMode.RANSOMWARE.value, IslandMode.ADVANCED.value])
|
||||||
def test_island_mode_endpoint(flask_client, mode):
|
def test_island_mode_endpoint(flask_client, mode):
|
||||||
flask_client.post(
|
flask_client.put(
|
||||||
IslandModeResource.urls[0], data=json.dumps({"mode": mode}), follow_redirects=True
|
IslandModeResource.urls[0], data=json.dumps({"mode": mode}), follow_redirects=True
|
||||||
)
|
)
|
||||||
resp = flask_client.get(IslandModeResource.urls[0], follow_redirects=True)
|
resp = flask_client.get(IslandModeResource.urls[0], follow_redirects=True)
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == HTTPStatus.OK
|
||||||
assert json.loads(resp.data)["mode"] == mode
|
assert json.loads(resp.data)["mode"] == mode
|
||||||
|
|
||||||
|
|
||||||
def test_island_mode_endpoint__invalid_mode(flask_client):
|
def test_island_mode_endpoint__invalid_mode(flask_client):
|
||||||
resp_post = flask_client.post(
|
resp_post = flask_client.put(
|
||||||
IslandModeResource.urls[0], data=json.dumps({"mode": "bogus_mode"}), follow_redirects=True
|
IslandModeResource.urls[0], data=json.dumps({"mode": "bogus_mode"}), follow_redirects=True
|
||||||
)
|
)
|
||||||
resp_get = flask_client.get(IslandModeResource.urls[0], follow_redirects=True)
|
resp_get = flask_client.get(IslandModeResource.urls[0], follow_redirects=True)
|
||||||
assert resp_post.status_code == 422
|
assert resp_post.status_code == HTTPStatus.UNPROCESSABLE_ENTITY
|
||||||
assert json.loads(resp_get.data)["mode"] == IslandMode.UNSET.value
|
assert json.loads(resp_get.data)["mode"] == IslandMode.UNSET.value
|
||||||
|
|
Loading…
Reference in New Issue