Feature: Guardduty (#4667)
This commit is contained in:
parent
0c5a3cc8ca
commit
cd5357ca41
@ -2481,6 +2481,70 @@
|
|||||||
- [ ] update_workflow
|
- [ ] update_workflow
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
## guardduty
|
||||||
|
<details>
|
||||||
|
<summary>3% implemented</summary>
|
||||||
|
|
||||||
|
- [ ] accept_invitation
|
||||||
|
- [ ] archive_findings
|
||||||
|
- [X] create_detector
|
||||||
|
- [ ] create_filter
|
||||||
|
- [ ] create_ip_set
|
||||||
|
- [ ] create_members
|
||||||
|
- [ ] create_publishing_destination
|
||||||
|
- [ ] create_sample_findings
|
||||||
|
- [ ] create_threat_intel_set
|
||||||
|
- [ ] decline_invitations
|
||||||
|
- [ ] delete_detector
|
||||||
|
- [ ] delete_filter
|
||||||
|
- [ ] delete_invitations
|
||||||
|
- [ ] delete_ip_set
|
||||||
|
- [ ] delete_members
|
||||||
|
- [ ] delete_publishing_destination
|
||||||
|
- [ ] delete_threat_intel_set
|
||||||
|
- [ ] describe_organization_configuration
|
||||||
|
- [ ] describe_publishing_destination
|
||||||
|
- [ ] disable_organization_admin_account
|
||||||
|
- [ ] disassociate_from_master_account
|
||||||
|
- [ ] disassociate_members
|
||||||
|
- [ ] enable_organization_admin_account
|
||||||
|
- [ ] get_detector
|
||||||
|
- [ ] get_filter
|
||||||
|
- [ ] get_findings
|
||||||
|
- [ ] get_findings_statistics
|
||||||
|
- [ ] get_invitations_count
|
||||||
|
- [ ] get_ip_set
|
||||||
|
- [ ] get_master_account
|
||||||
|
- [ ] get_member_detectors
|
||||||
|
- [ ] get_members
|
||||||
|
- [ ] get_threat_intel_set
|
||||||
|
- [ ] get_usage_statistics
|
||||||
|
- [ ] invite_members
|
||||||
|
- [X] list_detectors
|
||||||
|
- [ ] list_filters
|
||||||
|
- [ ] list_findings
|
||||||
|
- [ ] list_invitations
|
||||||
|
- [ ] list_ip_sets
|
||||||
|
- [ ] list_members
|
||||||
|
- [ ] list_organization_admin_accounts
|
||||||
|
- [ ] list_publishing_destinations
|
||||||
|
- [ ] list_tags_for_resource
|
||||||
|
- [ ] list_threat_intel_sets
|
||||||
|
- [ ] start_monitoring_members
|
||||||
|
- [ ] stop_monitoring_members
|
||||||
|
- [ ] tag_resource
|
||||||
|
- [ ] unarchive_findings
|
||||||
|
- [ ] untag_resource
|
||||||
|
- [ ] update_detector
|
||||||
|
- [ ] update_filter
|
||||||
|
- [ ] update_findings_feedback
|
||||||
|
- [ ] update_ip_set
|
||||||
|
- [ ] update_member_detectors
|
||||||
|
- [ ] update_organization_configuration
|
||||||
|
- [ ] update_publishing_destination
|
||||||
|
- [ ] update_threat_intel_set
|
||||||
|
</details>
|
||||||
|
|
||||||
## iam
|
## iam
|
||||||
<details>
|
<details>
|
||||||
<summary>67% implemented</summary>
|
<summary>67% implemented</summary>
|
||||||
@ -4945,7 +5009,6 @@
|
|||||||
- greengrass
|
- greengrass
|
||||||
- greengrassv2
|
- greengrassv2
|
||||||
- groundstation
|
- groundstation
|
||||||
- guardduty
|
|
||||||
- health
|
- health
|
||||||
- healthlake
|
- healthlake
|
||||||
- honeycode
|
- honeycode
|
||||||
|
90
docs/docs/services/guardduty.rst
Normal file
90
docs/docs/services/guardduty.rst
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
.. _implementedservice_guardduty:
|
||||||
|
|
||||||
|
.. |start-h3| raw:: html
|
||||||
|
|
||||||
|
<h3>
|
||||||
|
|
||||||
|
.. |end-h3| raw:: html
|
||||||
|
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
=========
|
||||||
|
guardduty
|
||||||
|
=========
|
||||||
|
|
||||||
|
|start-h3| Example usage |end-h3|
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
@mock_guardduty
|
||||||
|
def test_guardduty_behaviour:
|
||||||
|
boto3.client("guardduty")
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|start-h3| Implemented features for this service |end-h3|
|
||||||
|
|
||||||
|
- [ ] accept_invitation
|
||||||
|
- [ ] archive_findings
|
||||||
|
- [X] create_detector
|
||||||
|
- [ ] create_filter
|
||||||
|
- [ ] create_ip_set
|
||||||
|
- [ ] create_members
|
||||||
|
- [ ] create_publishing_destination
|
||||||
|
- [ ] create_sample_findings
|
||||||
|
- [ ] create_threat_intel_set
|
||||||
|
- [ ] decline_invitations
|
||||||
|
- [ ] delete_detector
|
||||||
|
- [ ] delete_filter
|
||||||
|
- [ ] delete_invitations
|
||||||
|
- [ ] delete_ip_set
|
||||||
|
- [ ] delete_members
|
||||||
|
- [ ] delete_publishing_destination
|
||||||
|
- [ ] delete_threat_intel_set
|
||||||
|
- [ ] describe_organization_configuration
|
||||||
|
- [ ] describe_publishing_destination
|
||||||
|
- [ ] disable_organization_admin_account
|
||||||
|
- [ ] disassociate_from_master_account
|
||||||
|
- [ ] disassociate_members
|
||||||
|
- [ ] enable_organization_admin_account
|
||||||
|
- [ ] get_detector
|
||||||
|
- [ ] get_filter
|
||||||
|
- [ ] get_findings
|
||||||
|
- [ ] get_findings_statistics
|
||||||
|
- [ ] get_invitations_count
|
||||||
|
- [ ] get_ip_set
|
||||||
|
- [ ] get_master_account
|
||||||
|
- [ ] get_member_detectors
|
||||||
|
- [ ] get_members
|
||||||
|
- [ ] get_threat_intel_set
|
||||||
|
- [ ] get_usage_statistics
|
||||||
|
- [ ] invite_members
|
||||||
|
- [X] list_detectors
|
||||||
|
|
||||||
|
The MaxResults and NextToken-parameter have not yet been implemented.
|
||||||
|
|
||||||
|
|
||||||
|
- [ ] list_filters
|
||||||
|
- [ ] list_findings
|
||||||
|
- [ ] list_invitations
|
||||||
|
- [ ] list_ip_sets
|
||||||
|
- [ ] list_members
|
||||||
|
- [ ] list_organization_admin_accounts
|
||||||
|
- [ ] list_publishing_destinations
|
||||||
|
- [ ] list_tags_for_resource
|
||||||
|
- [ ] list_threat_intel_sets
|
||||||
|
- [ ] start_monitoring_members
|
||||||
|
- [ ] stop_monitoring_members
|
||||||
|
- [ ] tag_resource
|
||||||
|
- [ ] unarchive_findings
|
||||||
|
- [ ] untag_resource
|
||||||
|
- [ ] update_detector
|
||||||
|
- [ ] update_filter
|
||||||
|
- [ ] update_findings_feedback
|
||||||
|
- [ ] update_ip_set
|
||||||
|
- [ ] update_member_detectors
|
||||||
|
- [ ] update_organization_configuration
|
||||||
|
- [ ] update_publishing_destination
|
||||||
|
- [ ] update_threat_intel_set
|
||||||
|
|
@ -95,6 +95,7 @@ mock_forecast = lazy_load(".forecast", "mock_forecast")
|
|||||||
mock_glacier = lazy_load(".glacier", "mock_glacier")
|
mock_glacier = lazy_load(".glacier", "mock_glacier")
|
||||||
mock_glacier_deprecated = lazy_load(".glacier", "mock_glacier_deprecated")
|
mock_glacier_deprecated = lazy_load(".glacier", "mock_glacier_deprecated")
|
||||||
mock_glue = lazy_load(".glue", "mock_glue")
|
mock_glue = lazy_load(".glue", "mock_glue")
|
||||||
|
mock_guardduty = lazy_load(".guardduty", "mock_guardduty")
|
||||||
mock_iam = lazy_load(".iam", "mock_iam")
|
mock_iam = lazy_load(".iam", "mock_iam")
|
||||||
mock_iam_deprecated = lazy_load(".iam", "mock_iam_deprecated")
|
mock_iam_deprecated = lazy_load(".iam", "mock_iam_deprecated")
|
||||||
mock_iot = lazy_load(".iot", "mock_iot")
|
mock_iot = lazy_load(".iot", "mock_iot")
|
||||||
@ -198,7 +199,6 @@ class MockAll(ContextDecorator):
|
|||||||
|
|
||||||
mock_all = MockAll
|
mock_all = MockAll
|
||||||
|
|
||||||
|
|
||||||
# import logging
|
# import logging
|
||||||
# logging.getLogger('boto').setLevel(logging.CRITICAL)
|
# logging.getLogger('boto').setLevel(logging.CRITICAL)
|
||||||
|
|
||||||
|
@ -66,6 +66,7 @@ backend_url_patterns = [
|
|||||||
("forecast", re.compile("https?://forecast\\.(.+)\\.amazonaws\\.com")),
|
("forecast", re.compile("https?://forecast\\.(.+)\\.amazonaws\\.com")),
|
||||||
("glacier", re.compile("https?://glacier\\.(.+)\\.amazonaws.com")),
|
("glacier", re.compile("https?://glacier\\.(.+)\\.amazonaws.com")),
|
||||||
("glue", re.compile("https?://glue\\.(.+)\\.amazonaws\\.com")),
|
("glue", re.compile("https?://glue\\.(.+)\\.amazonaws\\.com")),
|
||||||
|
("guardduty", re.compile("https?://guardduty\\.(.+)\\.amazonaws\\.com")),
|
||||||
("iam", re.compile("https?://iam\\.(.*\\.)?amazonaws\\.com")),
|
("iam", re.compile("https?://iam\\.(.*\\.)?amazonaws\\.com")),
|
||||||
("iot", re.compile("https?://iot\\.(.+)\\.amazonaws\\.com")),
|
("iot", re.compile("https?://iot\\.(.+)\\.amazonaws\\.com")),
|
||||||
("iot-data", re.compile("https?://data\\.iot\\.(.+)\\.amazonaws.com")),
|
("iot-data", re.compile("https?://data\\.iot\\.(.+)\\.amazonaws.com")),
|
||||||
|
7
moto/guardduty/__init__.py
Normal file
7
moto/guardduty/__init__.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from .models import guardduty_backends
|
||||||
|
from ..core.models import base_decorator
|
||||||
|
|
||||||
|
guardduty_backend = guardduty_backends["us-east-1"]
|
||||||
|
mock_guardduty = base_decorator(guardduty_backends)
|
79
moto/guardduty/models.py
Normal file
79
moto/guardduty/models.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from boto3 import Session
|
||||||
|
from moto.core import BaseBackend, BaseModel
|
||||||
|
from datetime import datetime
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
|
|
||||||
|
class GuardDutyBackend(BaseBackend):
|
||||||
|
def __init__(self, region_name=None):
|
||||||
|
super(GuardDutyBackend, self).__init__()
|
||||||
|
self.region_name = region_name
|
||||||
|
self.detectors = {}
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
region_name = self.region_name
|
||||||
|
self.__dict__ = {}
|
||||||
|
self.__init__(region_name)
|
||||||
|
|
||||||
|
def create_detector(
|
||||||
|
self, enable, client_token, finding_publishing_frequency, data_sources, tags
|
||||||
|
):
|
||||||
|
if finding_publishing_frequency not in [
|
||||||
|
"FIFTEEN_MINUTES",
|
||||||
|
"ONE_HOUR",
|
||||||
|
"SIX_HOURS",
|
||||||
|
]:
|
||||||
|
finding_publishing_frequency = "SIX_HOURS"
|
||||||
|
|
||||||
|
service_role = "AWSServiceRoleForAmazonGuardDuty"
|
||||||
|
detector = Detector(
|
||||||
|
self,
|
||||||
|
datetime.now,
|
||||||
|
finding_publishing_frequency,
|
||||||
|
service_role,
|
||||||
|
enable,
|
||||||
|
data_sources,
|
||||||
|
tags,
|
||||||
|
)
|
||||||
|
self.detectors[detector.id] = detector
|
||||||
|
return detector.id
|
||||||
|
|
||||||
|
def list_detectors(self):
|
||||||
|
"""
|
||||||
|
The MaxResults and NextToken-parameter have not yet been implemented.
|
||||||
|
"""
|
||||||
|
detectorids = []
|
||||||
|
for detector in self.detectors:
|
||||||
|
detectorids.append(self.detectors[detector].id)
|
||||||
|
return detectorids
|
||||||
|
|
||||||
|
|
||||||
|
class Detector(BaseModel):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
created_at,
|
||||||
|
finding_publish_freq,
|
||||||
|
service_role,
|
||||||
|
status,
|
||||||
|
updated_at,
|
||||||
|
datasources,
|
||||||
|
tags,
|
||||||
|
):
|
||||||
|
self.id = str(uuid4())
|
||||||
|
self.created_at = created_at
|
||||||
|
self.finding_publish_freq = finding_publish_freq
|
||||||
|
self.service_role = service_role
|
||||||
|
self.status = status
|
||||||
|
self.updated_at = updated_at
|
||||||
|
self.datasources = datasources
|
||||||
|
self.tags = tags
|
||||||
|
|
||||||
|
|
||||||
|
guardduty_backends = {}
|
||||||
|
for region in Session().get_available_regions("guardduty"):
|
||||||
|
guardduty_backends[region] = GuardDutyBackend()
|
||||||
|
for region in Session().get_available_regions("guardduty", partition_name="aws-us-gov"):
|
||||||
|
guardduty_backends[region] = GuardDutyBackend()
|
||||||
|
for region in Session().get_available_regions("guardduty", partition_name="aws-cn"):
|
||||||
|
guardduty_backends[region] = GuardDutyBackend()
|
39
moto/guardduty/responses.py
Normal file
39
moto/guardduty/responses.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from moto.core.responses import BaseResponse
|
||||||
|
from .models import guardduty_backends
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
class GuardDutyResponse(BaseResponse):
|
||||||
|
SERVICE_NAME = "guardduty"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def guardduty_backend(self):
|
||||||
|
return guardduty_backends[self.region]
|
||||||
|
|
||||||
|
def detector(self, request, full_url, headers):
|
||||||
|
self.setup_class(request, full_url, headers)
|
||||||
|
if request.method == "POST":
|
||||||
|
return self.create_detector()
|
||||||
|
elif request.method == "GET":
|
||||||
|
return self.list_detectors()
|
||||||
|
else:
|
||||||
|
return 404, {}, ""
|
||||||
|
|
||||||
|
def create_detector(self):
|
||||||
|
enable = self._get_param("enable")
|
||||||
|
client_token = self._get_param("clientToken")
|
||||||
|
finding_publishing_frequency = self._get_param("findingPublishingFrequency")
|
||||||
|
data_sources = self._get_param("dataSources")
|
||||||
|
tags = self._get_param("tags")
|
||||||
|
|
||||||
|
detector_id = self.guardduty_backend.create_detector(
|
||||||
|
enable, client_token, finding_publishing_frequency, data_sources, tags
|
||||||
|
)
|
||||||
|
|
||||||
|
return 200, {}, json.dumps(dict(detectorId=detector_id))
|
||||||
|
|
||||||
|
def list_detectors(self):
|
||||||
|
detector_ids = self.guardduty_backend.list_detectors()
|
||||||
|
|
||||||
|
return 200, {}, json.dumps({"detectorIds": detector_ids})
|
13
moto/guardduty/urls.py
Normal file
13
moto/guardduty/urls.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from .responses import GuardDutyResponse
|
||||||
|
|
||||||
|
response = GuardDutyResponse()
|
||||||
|
|
||||||
|
url_bases = [
|
||||||
|
"https?://guardduty\\.(.+)\\.amazonaws\\.com",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
url_paths = {
|
||||||
|
"{0}/detector$": response.detector,
|
||||||
|
}
|
0
tests/test_guardduty/__init__.py
Normal file
0
tests/test_guardduty/__init__.py
Normal file
51
tests/test_guardduty/test_guardduty.py
Normal file
51
tests/test_guardduty/test_guardduty.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import boto3
|
||||||
|
import sure # noqa # pylint: disable=unused-import
|
||||||
|
|
||||||
|
from moto import mock_guardduty
|
||||||
|
|
||||||
|
|
||||||
|
@mock_guardduty
|
||||||
|
def test_create_detector():
|
||||||
|
client = boto3.client("guardduty", region_name="us-east-1")
|
||||||
|
response = client.create_detector(
|
||||||
|
Enable=True,
|
||||||
|
ClientToken="745645734574758463758",
|
||||||
|
FindingPublishingFrequency="ONE_HOUR",
|
||||||
|
DataSources={"S3Logs": {"Enable": True}},
|
||||||
|
Tags={},
|
||||||
|
)
|
||||||
|
response.should.have.key("DetectorId")
|
||||||
|
response["DetectorId"].shouldnt.equal(None)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_guardduty
|
||||||
|
def test_create_detector_with_minimal_params():
|
||||||
|
client = boto3.client("guardduty", region_name="us-east-1")
|
||||||
|
response = client.create_detector(Enable=True)
|
||||||
|
response.should.have.key("DetectorId")
|
||||||
|
response["DetectorId"].shouldnt.equal(None)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_guardduty
|
||||||
|
def test_list_detectors_initial():
|
||||||
|
client = boto3.client("guardduty", region_name="us-east-1")
|
||||||
|
|
||||||
|
response = client.list_detectors()
|
||||||
|
response.should.have.key("DetectorIds").equals([])
|
||||||
|
|
||||||
|
|
||||||
|
@mock_guardduty
|
||||||
|
def test_list_detectors():
|
||||||
|
client = boto3.client("guardduty", region_name="us-east-1")
|
||||||
|
d1 = client.create_detector(
|
||||||
|
Enable=True,
|
||||||
|
ClientToken="745645734574758463758",
|
||||||
|
FindingPublishingFrequency="ONE_HOUR",
|
||||||
|
DataSources={"S3Logs": {"Enable": True}},
|
||||||
|
Tags={},
|
||||||
|
)["DetectorId"]
|
||||||
|
d2 = client.create_detector(Enable=False,)["DetectorId"]
|
||||||
|
|
||||||
|
response = client.list_detectors()
|
||||||
|
response.should.have.key("DetectorIds")
|
||||||
|
set(response["DetectorIds"]).should.equal({d1, d2})
|
14
tests/test_guardduty/test_server.py
Normal file
14
tests/test_guardduty/test_server.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import json
|
||||||
|
import sure # noqa # pylint: disable=unused-import
|
||||||
|
|
||||||
|
import moto.server as server
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_without_enable_option():
|
||||||
|
backend = server.create_backend_app("guardduty")
|
||||||
|
test_client = backend.test_client()
|
||||||
|
|
||||||
|
body = {"enable": "True"}
|
||||||
|
response = test_client.post("/detector", data=json.dumps(body))
|
||||||
|
response.status_code.should.equal(200)
|
||||||
|
json.loads(response.data).should.have.key("detectorId")
|
Loading…
Reference in New Issue
Block a user