Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat gsn-10845: test basic suppression parsing #15

Merged
merged 3 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion awsfindingsmanagerlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"""
from ._version import __version__
from .awsfindingsmanagerlib import FindingsManager, Rule, Finding
from .backends import Http, S3
from .backends import Http, Local, S3

__author__ = '''Marwin Baumann <[email protected]>, Costas Tyfoxylos <[email protected]>'''
__docformat__ = '''google'''
Expand All @@ -46,4 +46,5 @@
assert Finding

assert Http
assert Local
assert S3
51 changes: 32 additions & 19 deletions awsfindingsmanagerlib/awsfindingsmanagerlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,7 @@ def default_query_filter(self):
self.denied_account_ids))

@property
def rules(self):
def rules(self) -> List[Rule]:
"""The registered rules of the manager."""
return list(self._rules)

Expand Down Expand Up @@ -608,6 +608,11 @@ def _get_security_hub_client(region: str):
raise NoRegion(f'Security Hub client requires a valid region set to connect, message was:{msg}') from None
return client

def _get_security_hub_paginator_iterator(self, region: str, operation_name: str, query_filter: dict):
security_hub = self._get_security_hub_client(region=region)
paginator = security_hub.get_paginator(operation_name)
return paginator.paginate(Filters=query_filter)

@staticmethod
def _get_ec2_client(region: str):
kwargs = {}
Expand Down Expand Up @@ -712,23 +717,22 @@ def _get_findings(self, query_filter: Dict):
regions_to_retrieve = [aggregating_region] if aggregating_region else self.regions
for region in regions_to_retrieve:
self._logger.debug(f'Trying to get findings for region {region}')
session = boto3.Session(region_name=region)
security_hub = session.client('securityhub')
paginator = security_hub.get_paginator('get_findings')
iterator = paginator.paginate(Filters=query_filter)
iterator = self._get_security_hub_paginator_iterator(region, 'get_findings', query_filter)
try:
for page in iterator:
for finding_data in page['Findings']:
finding = Finding(finding_data)
self._logger.debug(f'Adding finding with id {finding.id}')
findings.add(finding)
except (security_hub.exceptions.InvalidAccessException, security_hub.exceptions.AccessDeniedException):
self._logger.debug(f'No access for Security Hub for region {region}.')
continue
except botocore.exceptions.ClientError as error:
if error.response['Error']['Code'] in ['AccessDeniedException', 'InvalidAccessException']:
self._logger.debug(f'No access for Security Hub for region {region}.')
continue
raise error
return list(findings)

@staticmethod
def _get_matching_findings(rule: Rule, findings: List[Finding], logger: logging.Logger):
def _get_matching_findings(rule: Rule, findings: List[Finding], logger: logging.Logger) -> List[Finding]:
if any([rule.resource_ids, rule.tags]):
matching_findings = [finding for finding in findings
if any([finding.is_matching_resource_ids(rule.resource_ids),
Expand All @@ -742,7 +746,7 @@ def _get_matching_findings(rule: Rule, findings: List[Finding], logger: logging.
finding.matched_rule = rule
return matching_findings

def get_findings(self):
def get_findings(self) -> List[Finding]:
"""Retrieves findings from security hub based on the registered rules.

Returns:
Expand All @@ -751,10 +755,7 @@ def get_findings(self):
"""
all_findings = []
for rule in self.rules:
query = self.default_query_filter
query.update(rule.query_filter)
findings = self._get_findings(query)
matching_findings = self._get_matching_findings(rule, findings, self._logger)
matching_findings = self.get_findings_by_matching_rule(rule)
all_findings.extend(matching_findings)
initial_size = len(all_findings)
findings = list(set(all_findings))
Expand All @@ -763,7 +764,22 @@ def get_findings(self):
self._logger.warning(f'Missmatch of finding numbers, there seems to be an overlap of {diff}')
return findings

def get_findings_by_rule_match(self, note: str, action: str, match_on: Dict):
def get_findings_by_matching_rule(self, rule: Rule) -> List[Finding]:
"""Retrieves findings by the provided rule.

Args:
rule: The rule to match findings on.

Returns:
A list of findings that match the provided rule.

"""
query = self.default_query_filter
query.update(rule.query_filter)
findings = self._get_findings(query)
return self._get_matching_findings(rule, findings, self._logger)

def get_findings_by_matching_rule_data(self, note: str, action: str, match_on: Dict) -> List[Finding]:
"""Retrieves findings by the provided rule data.

Args:
Expand All @@ -776,10 +792,7 @@ def get_findings_by_rule_match(self, note: str, action: str, match_on: Dict):

"""
rule = Rule(note, action, match_on)
query = self.default_query_filter
query.update(rule.query_filter)
findings = self._get_findings(query)
return self._get_matching_findings(rule, findings, self._logger)
return self.get_findings_by_matching_rule(rule)

@staticmethod
def _chunk(iterable, size):
Expand Down
11 changes: 11 additions & 0 deletions awsfindingsmanagerlib/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,17 @@ def get_rules(self):
return [validate_rule_data(data) for data in self._get_rules()]


class Local(Backend):

def __init__(self, path):
self.path = path

def _get_rules(self):
with open(self.path, encoding='utf-8') as suppressions_file:
data = yaml.safe_load(suppressions_file)
return data.get('Rules')


class Http(Backend):

def __init__(self, url):
Expand Down
19 changes: 19 additions & 0 deletions tests/fixtures/batch_update_findings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"FindingIdentifiers": [
{
"Id": "arn:aws:securityhub:eu-west-1:2345678901:security-control/GuardDuty.1/finding/a32acb1c-8a92-4122-b32a-5b35e71d0a6e",
"ProductArn": "arn:aws:securityhub:eu-west-1::product/aws/securityhub"
},
{
"Id": "arn:aws:securityhub:eu-west-1:1234567890:security-control/GuardDuty.1/finding/bbfe351f-dc6b-4ea3-9ece-08934a88798d",
"ProductArn": "arn:aws:securityhub:eu-west-1::product/aws/securityhub"
}
],
"Workflow": {
"Status": "SUPPRESSED"
},
"Note": {
"Text": "Maybe later",
"UpdatedBy": "FindingsManager"
}
}
190 changes: 190 additions & 0 deletions tests/fixtures/findings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
{
"ResponseMetadata": {
"RequestId": "a6dd7122-1490-4d3b-84d7-489e6005f07e",
"HTTPStatusCode": 200,
"HTTPHeaders": {
"date": "Mon, 24 Jun 2024 14:07:21 GMT",
"content-type": "application/json",
"content-length": "4799",
"connection": "keep-alive",
"x-amzn-requestid": "a6dd7122-1490-4d3b-84d7-489e6005f07e",
"access-control-allow-origin": "*",
"access-control-allow-headers": "Authorization,Date,X-Amz-Date,X-Amz-Security-Token,X-Amz-Target,content-type,x-amz-content-sha256,x-amz-user-agent,x-amzn-platform-id,x-amzn-trace-id",
"x-amz-apigw-id": "Z4CP9FJXDoAAABg=",
"cache-control": "no-cache",
"access-control-allow-methods": "GET,POST,OPTIONS,PUT,PATCH,DELETE",
"access-control-expose-headers": "x-amzn-errortype,x-amzn-requestid,x-amzn-errormessage,x-amzn-trace-id,x-amz-apigw-id,date",
"x-amzn-trace-id": "Root=1-66797d99-1400daac266a556d768b6360",
"access-control-max-age": "86400"
},
"RetryAttempts": 0
},
"Findings": [
{
"SchemaVersion": "2018-10-08",
"Id": "arn:aws:securityhub:eu-west-1:1234567890:security-control/GuardDuty.1/finding/bbfe351f-dc6b-4ea3-9ece-08934a88798d",
"ProductArn": "arn:aws:securityhub:eu-west-1::product/aws/securityhub",
"ProductName": "Security Hub",
"CompanyName": "AWS",
"Region": "eu-west-1",
"GeneratorId": "security-control/GuardDuty.1",
"AwsAccountId": "1234567890",
"Types": [
"Software and Configuration Checks/Industry and Regulatory Standards"
],
"FirstObservedAt": "2023-11-21T08:22:16.054Z",
"LastObservedAt": "2024-06-24T09:00:51.562Z",
"CreatedAt": "2023-11-21T08:22:16.054Z",
"UpdatedAt": "2024-06-24T09:00:45.686Z",
"Severity": {
"Label": "HIGH",
"Normalized": 70,
"Original": "HIGH"
},
"Title": "GuardDuty should be enabled",
"Description": "This AWS control checks whether Amazon GuardDuty is enabled in your AWS account and region.",
"Remediation": {
"Recommendation": {
"Text": "For information on how to correct this issue, consult the AWS Security Hub controls documentation.",
"Url": "https://docs.aws.amazon.com/console/securityhub/GuardDuty.1/remediation"
}
},
"ProductFields": {
"RelatedAWSResources:0/name": "securityhub-guardduty-enabled-centralized-c07f55ef",
"RelatedAWSResources:0/type": "AWS::Config::ConfigRule",
"aws/securityhub/ProductName": "Security Hub",
"aws/securityhub/CompanyName": "AWS",
"aws/securityhub/annotation": "Amazon GuardDuty is not configured.",
"Resources:0/Id": "arn:aws:iam::1234567890:root",
"aws/securityhub/FindingId": "arn:aws:securityhub:eu-west-1::product/aws/securityhub/arn:aws:securityhub:eu-west-1:1234567890:security-control/GuardDuty.1/finding/bbfe351f-dc6b-4ea3-9ece-08934a88798d"
},
"Resources": [
{
"Type": "AwsAccount",
"Id": "AWS::::Account:1234567890",
"Partition": "aws",
"Region": "eu-west-1"
}
],
"Compliance": {
"Status": "FAILED",
"RelatedRequirements": [
"PCI DSS v3.2.1/11.4"
],
"SecurityControlId": "GuardDuty.1",
"AssociatedStandards": [
{
"StandardsId": "standards/aws-foundational-security-best-practices/v/1.0.0"
},
{
"StandardsId": "standards/pci-dss/v/3.2.1"
}
]
},
"WorkflowState": "NEW",
"Workflow": {
"Status": "SUPPRESSED"
},
"RecordState": "ACTIVE",
"Note": {
"Text": "Maybe later",
"UpdatedBy": "FindingsManager",
"UpdatedAt": "2024-06-24T12:45:06.677Z"
},
"FindingProviderFields": {
"Severity": {
"Label": "HIGH",
"Original": "HIGH"
},
"Types": [
"Software and Configuration Checks/Industry and Regulatory Standards"
]
},
"ProcessedAt": "2024-06-24T12:45:06.702Z",
"AwsAccountName": "account1"
},
{
"SchemaVersion": "2018-10-08",
"Id": "arn:aws:securityhub:eu-west-1:2345678901:security-control/GuardDuty.1/finding/a32acb1c-8a92-4122-b32a-5b35e71d0a6e",
"ProductArn": "arn:aws:securityhub:eu-west-1::product/aws/securityhub",
"ProductName": "Security Hub",
"CompanyName": "AWS",
"Region": "eu-west-1",
"GeneratorId": "security-control/GuardDuty.1",
"AwsAccountId": "2345678901",
"Types": [
"Software and Configuration Checks/Industry and Regulatory Standards"
],
"FirstObservedAt": "2023-11-21T08:43:17.051Z",
"LastObservedAt": "2024-06-24T08:27:57.806Z",
"CreatedAt": "2023-11-21T08:43:17.051Z",
"UpdatedAt": "2024-06-24T08:27:43.972Z",
"Severity": {
"Label": "HIGH",
"Normalized": 70,
"Original": "HIGH"
},
"Title": "GuardDuty should be enabled",
"Description": "This AWS control checks whether Amazon GuardDuty is enabled in your AWS account and region.",
"Remediation": {
"Recommendation": {
"Text": "For information on how to correct this issue, consult the AWS Security Hub controls documentation.",
"Url": "https://docs.aws.amazon.com/console/securityhub/GuardDuty.1/remediation"
}
},
"ProductFields": {
"RelatedAWSResources:0/name": "securityhub-guardduty-enabled-centralized-e9e5d5d1",
"RelatedAWSResources:0/type": "AWS::Config::ConfigRule",
"aws/securityhub/ProductName": "Security Hub",
"aws/securityhub/CompanyName": "AWS",
"aws/securityhub/annotation": "Amazon GuardDuty is not configured.",
"Resources:0/Id": "arn:aws:iam::2345678901:root",
"aws/securityhub/FindingId": "arn:aws:securityhub:eu-west-1::product/aws/securityhub/arn:aws:securityhub:eu-west-1:2345678901:security-control/GuardDuty.1/finding/a32acb1c-8a92-4122-b32a-5b35e71d0a6e"
},
"Resources": [
{
"Type": "AwsAccount",
"Id": "AWS::::Account:2345678901",
"Partition": "aws",
"Region": "eu-west-1"
}
],
"Compliance": {
"Status": "FAILED",
"RelatedRequirements": [
"PCI DSS v3.2.1/11.4"
],
"SecurityControlId": "GuardDuty.1",
"AssociatedStandards": [
{
"StandardsId": "standards/aws-foundational-security-best-practices/v/1.0.0"
},
{
"StandardsId": "standards/pci-dss/v/3.2.1"
}
]
},
"WorkflowState": "NEW",
"Workflow": {
"Status": "SUPPRESSED"
},
"RecordState": "ACTIVE",
"Note": {
"Text": "Maybe later",
"UpdatedBy": "FindingsManager",
"UpdatedAt": "2024-06-24T12:45:06.677Z"
},
"FindingProviderFields": {
"Severity": {
"Label": "HIGH",
"Original": "HIGH"
},
"Types": [
"Software and Configuration Checks/Industry and Regulatory Standards"
]
},
"ProcessedAt": "2024-06-24T12:45:06.706Z",
"AwsAccountName": "account2"
}
]
}
12 changes: 12 additions & 0 deletions tests/fixtures/suppressions.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Comments
Rules:
- note: 'Maybe later'
action: 'SUPPRESSED'
match_on:
security_control_id: 'GuardDuty.1'
- note: 'Public'
action: 'SUPPRESSED'
match_on:
resource_ids:
- '^arn:aws:s3:::public-bucket$'
- '^arn:aws:s3:::bucket-public$'
File renamed without changes.
Loading
Loading