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 Added support for SecurityHub integration findings #22

Merged
merged 3 commits into from
Nov 15, 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
66 changes: 61 additions & 5 deletions awsfindingsmanagerlib/awsfindingsmanagerlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ def product_arn(self) -> str:
"""Product ARN."""
return self._data.get('ProductArn')

@property
def product_name(self) -> str:
"""Product Name."""
return self._data.get('ProductName')

@property
def region(self) -> str:
"""Region."""
Expand Down Expand Up @@ -333,9 +338,9 @@ def match_if_set(left, right):
def is_matching_rule(self, rule: Rule) -> bool:
"""Checks a rule for a match with the finding.

If any of control_id, security_control_id or rule_id attributes match between the rule and the finding and the
rule does not have any filtering attributes like resource_id_regexps or tags then it is considered a match.
(Big blast radius) only matching on the control.
If any of control_id, security_control_id, rule_id or product_name attributes match between the rule and the
finding and the rule does not have any filtering attributes like resource_id_regexps or tags then it is
considered a match. (Big blast radius) only matching on the control or product.

If the rule has any attributes like resource_id_regexps or tags then a secondary match is searched for any of
them with the corresponding finding attributes. If any match is found then the rule is found matching if none
Expand All @@ -357,10 +362,15 @@ def is_matching_rule(self, rule: Rule) -> bool:
self.match_if_set(self.security_control_id,
rule.security_control_id),
self.match_if_set(self.control_id, rule.rule_or_control_id),
self.match_if_set(self.rule_id, rule.rule_or_control_id)
self.match_if_set(self.rule_id, rule.rule_or_control_id),
fernandogoncalves-me marked this conversation as resolved.
Show resolved Hide resolved
self.match_if_set(self.product_name, rule.product_name),
]):
self._logger.debug(
f'Matched with rule "{rule.note}" on one of "control_id, security_control_id"')
f'Matched with rule "{rule.note}" on one of "control_id, security_control_id, product_name"')
if self.match_if_set(self.title, rule.title):
self._logger.debug(
f'Matched with rule "{rule.note}" on title.')
return True
if not any([rule.tags, rule.resource_id_regexps]):
self._logger.debug(
f'Rule "{rule.note}" does not seem to have filters for resources or tags.')
Expand Down Expand Up @@ -411,6 +421,11 @@ def match_on(self) -> Dict:
"""The match_on data of the rule."""
return self._data.get('match_on')

@property
def product_name(self) -> str:
"""The product name if any, empty string otherwise."""
return self.match_on.get('product_name', '')

@property
def security_control_id(self) -> str:
"""The security control ID if any, empty string otherwise."""
Expand All @@ -426,11 +441,33 @@ def resource_id_regexps(self) -> List[Optional[str]]:
"""The resource ids specified under the match_on attribute."""
return self.match_on.get('resource_id_regexps', [])

@property
def title(self) -> str:
"""The title if any, empty string otherwise."""
return self.match_on.get('title', '')

@property
def tags(self) -> List[Optional[str]]:
"""The tags specified under the match_on attribute."""
return self.match_on.get('tags', [])

@staticmethod
def _get_product_name_query(match_on_data) -> Dict:
"""Constructs a valid query based on product name if any.

Args:
match_on_data: The match_on data of the Rule

Returns:
The query matching the product name, empty dictionary otherwise.

"""
product_name = match_on_data.get('product_name')
if not product_name:
return {}
return {'ProductName': [{'Value': product_name,
'Comparison': 'EQUALS'}]}

@staticmethod
def _get_rule_or_control_id_query(match_on_data) -> Dict:
"""Constructs a valid query based on a set control ID if any.
Expand Down Expand Up @@ -490,6 +527,23 @@ def _get_tag_query(match_on_data) -> Dict:
'Comparison': 'EQUALS'}
for tag in tags]}

@staticmethod
def _get_title_query(match_on_data) -> Dict:
"""Constructs a valid query based on title if any.

Args:
match_on_data: The match_on data of the Rule

Returns:
The query matching the title, empty dictionary otherwise.

"""
title = match_on_data.get('title')
if not title:
return {}
return {'Title': [{'Value': title,
'Comparison': 'EQUALS'}]}

@property
def query_filter(self) -> Dict:
"""The query filter of the Rule based on all set attributes.
Expand All @@ -502,6 +556,8 @@ def query_filter(self) -> Dict:
query.update(self._get_rule_or_control_id_query(self.match_on))
query.update(self._get_security_control_id_query(self.match_on))
query.update(self._get_tag_query(self.match_on))
query.update(self._get_title_query(self.match_on))
query.update(self._get_product_name_query(self.match_on))
return deepcopy(query)


Expand Down
2 changes: 2 additions & 0 deletions awsfindingsmanagerlib/validations.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
__status__ = '''Development''' # "Prototype", "Development", "Production".

rule_schema = Schema({'match_on': {Optional('rule_or_control_id'): str,
Optional('title'): str,
Optional('product_name'): str,
Optional('security_control_id'): str,
Optional('resource_id_regexps'): [str],
Optional('tags'): [{'key': str,
Expand Down
25 changes: 24 additions & 1 deletion tests/fixtures/batch_update_findings_full.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,28 @@
"Text": "MF-Neigh",
"UpdatedBy": "FindingsManager"
}
},
{
"FindingIdentifiers": [
{
"Id": "arn:aws:inspector2:eu-west-1:012345678912:finding/ff4ebfb9d83b0ee89c7140b30eed5ef9",
"ProductArn": "arn:aws:securityhub:eu-west-1::product/aws/inspector"
},
{
"Id": "arn:aws:inspector2:eu-west-1:012345678912:finding/ioperfb9d83b0ee89c7140b30eed5lf9",
"ProductArn": "arn:aws:securityhub:eu-west-1::product/aws/inspector"
},
{
"Id": "arn:aws:inspector2:eu-west-1:012345678912:finding/m58hv3b9d83b0ee89c7140b30eed55kv",
"ProductArn": "arn:aws:securityhub:eu-west-1::product/aws/inspector"
}
],
"Workflow": {
"Status": "SUPPRESSED"
},
"Note": {
"Text": "We support Inspector too",
"UpdatedBy": "FindingsManager"
}
}
]
]
154 changes: 154 additions & 0 deletions tests/fixtures/findings/full/Inspector/acc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
{
"AwsAccountId": "012345678912",
"AwsAccountName": "account",
"CompanyName": "Amazon",
"CreatedAt": "2024-11-11T14:40:00.016Z",
"Description": "A flaw was found in python. An improperly handled HTTP response in the HTTP client code of python may allow a remote attacker, who controls the HTTP server, to make the client script enter an infinite loop, consuming CPU time. The highest threat from this vulnerability is to system availability.",
"FindingProviderFields": {
"Types": [
"Software and Configuration Checks/Vulnerabilities/CVE"
],
"Severity": {
"Normalized": 70,
"Label": "HIGH"
}
},
"FirstObservedAt": "2024-11-11T14:40:00.016Z",
"GeneratorId": "AWSInspector",
"Id": "arn:aws:inspector2:eu-west-1:012345678912:finding/ioperfb9d83b0ee89c7140b30eed5lf9",
"LastObservedAt": "2024-11-11T14:40:00.016Z",
"ProcessedAt": "2024-11-11T14:45:08.567Z",
"ProductArn": "arn:aws:securityhub:eu-west-1::product/aws/inspector",
"ProductFields": {
"aws/inspector/ProductVersion": "2",
"aws/inspector/FindingStatus": "CLOSED",
"aws/inspector/inspectorScore": "7.5",
"aws/inspector/instanceId": "i-01e8db61387e018b4",
"aws/inspector/resources/1/resourceDetails/awsEc2InstanceDetails/platform": "UBUNTU_22_04",
"aws/securityhub/FindingId": "arn:aws:securityhub:eu-west-1::product/aws/inspector/arn:aws:inspector2:eu-west-1:012345678912:finding/ff4ebfb9d83b0ee89c7140b30eed5ef9",
"aws/securityhub/ProductName": "Inspector",
"aws/securityhub/CompanyName": "Amazon"
},
"ProductName": "Inspector",
"RecordState": "ARCHIVED",
"Region": "eu-west-1",
"Remediation": {
"Recommendation": {
"Text": "Remediation is available. Please refer to the Fixed version in the vulnerability details section above.For detailed remediation guidance for each of the affected packages, refer to the vulnerabilities section of the detailed finding JSON."
}
},
"Resources": [
{
"Details": {
"AwsEc2Instance": {
"Type": "t3.large",
"VpcId": "vpc-0d165124e6f1211e6",
"ImageId": "ami-030c1a56dd9a0ccd8",
"IpV4Addresses": [
"192.168.0.74"
],
"SubnetId": "subnet-062ae210766aa9614",
"LaunchedAt": "2024-11-11T14:38:42.000Z",
"IamInstanceProfileArn": "arn:aws:iam::012345678912:instance-profile/example/example-profile"
}
},
"Id": "arn:aws:ec2:eu-west-1:012345678912:instance/i-01e8db61387e018b4",
"Partition": "aws",
"Region": "eu-west-1",
"Tags": {
"aws:ec2:fleet-id": "fleet-28b70307-1da4-e616-0cb8-0d20555fe75c",
"ghr:environment": "example",
"aws:ec2launchtemplate:version": "9",
"aws:ec2launchtemplate:id": "lt-0c064043433a6dcfe",
"Name": "example-action-runner"
},
"Type": "AwsEc2Instance"
}
],
"SchemaVersion": "2018-10-08",
"Severity": {
"Label": "HIGH",
"Normalized": 70
},
"Title": "CVE-2021-3737 - python3.10, python3.10-minimal",
"Types": [
"Software and Configuration Checks/Vulnerabilities/CVE"
],
"UpdatedAt": "2024-11-11T14:44:51.175Z",
"Vulnerabilities": [
{
"Cvss": [
{
"BaseScore": 7.5,
"BaseVector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"Source": "UBUNTU_CVE",
"Version": "3.1"
},
{
"BaseScore": 7.5,
"BaseVector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"Source": "NVD",
"Version": "3.1"
},
{
"BaseScore": 7.5,
"BaseVector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"Source": "UBUNTU_CVE",
"Version": "3.1"
}
],
"EpssScore": 0.01559,
"ExploitAvailable": "NO",
"FixAvailable": "YES",
"Id": "CVE-2021-3737",
"ReferenceUrls": [
"https://ubuntu.com/security/notices/USN-5083-1",
"https://ubuntu.com/security/notices/USN-5201-1",
"https://ubuntu.com/security/notices/USN-5200-1",
"https://ubuntu.com/security/notices/USN-6891-1",
"https://bugs.python.org/issue44022",
"https://www.cve.org/CVERecord?id=CVE-2021-3737",
"https://ubuntu.com/security/notices/USN-5199-1"
],
"RelatedVulnerabilities": [
"USN-5200-1",
"USN-5201-1",
"USN-5083-1",
"USN-6891-1",
"USN-5199-1"
],
"Vendor": {
"VendorCreatedAt": "2022-03-04T19:15:00.000Z",
"VendorSeverity": "medium",
"Url": "https://people.canonical.com/~ubuntu-security/cve/2021/CVE-2021-3737.html",
"Name": "UBUNTU_CVE"
},
"VulnerablePackages": [
{
"Architecture": "X86_64",
"PackageManager": "OS",
"Version": "3.10.6",
"Epoch": "0",
"FixedInVersion": "0:3.10.12-1~22.04.4",
"Remediation": "apt-get update && apt-get upgrade",
"Release": "1~22.04.2ubuntu1.1",
"Name": "python3.10"
},
{
"Architecture": "X86_64",
"PackageManager": "OS",
"Version": "3.10.6",
"Epoch": "0",
"FixedInVersion": "0:3.10.12-1~22.04.4",
"Remediation": "apt-get update && apt-get upgrade",
"Release": "1~22.04.2ubuntu1.1",
"Name": "python3.10-minimal"
}
]
}
],
"Workflow": {
"Status": "NEW"
},
"WorkflowState": "NEW"
}
Loading
Loading