diff --git a/plugins/aws/fix_plugin_aws/resource/base.py b/plugins/aws/fix_plugin_aws/resource/base.py index 10381db9c6..76c7ead95b 100644 --- a/plugins/aws/fix_plugin_aws/resource/base.py +++ b/plugins/aws/fix_plugin_aws/resource/base.py @@ -487,7 +487,6 @@ def __init__( graph_nodes_access: Optional[RWLock] = None, graph_edges_access: Optional[RWLock] = None, last_run_started_at: Optional[datetime] = None, - assessment_findings: Optional[Dict[Tuple[str, str, str], Dict[str, List[Finding]]]] = None, ) -> None: self.graph = graph self.cloud = cloud @@ -504,8 +503,8 @@ def __init__( self.last_run_started_at = last_run_started_at self.created_at = utc() self.__builder_cache = {region.safe_name: self} - self._assessment_findings: Dict[Tuple[str, str, str], Dict[str, List[Finding]]] = ( - assessment_findings or defaultdict(lambda: defaultdict(list)) + self._assessment_findings: Dict[Tuple[str, str, str], Dict[str, List[Finding]]] = defaultdict( + lambda: defaultdict(list) ) """ AWS assessment findings that hold a list of AwsInspectorFinding or AwsGuardDutyFinding. @@ -547,7 +546,10 @@ def suppress(self, message: str) -> SuppressWithFeedback: return SuppressWithFeedback(message, self.core_feedback, log) def add_finding(self, provider: str, class_name: str, region: str, class_id: str, finding: Finding) -> None: - self._assessment_findings[(provider, region, class_name)][class_id].append(finding) + global_builder = self.__builder_cache.get("global", None) + if not global_builder: + return + global_builder._assessment_findings[(provider, region, class_name)][class_id].append(finding) def submit_work(self, service: str, fn: Callable[..., T], *args: Any, **kwargs: Any) -> Future[T]: """ @@ -755,7 +757,7 @@ def for_region(self, region: AwsRegion) -> GraphBuilder: self.graph_nodes_access, self.graph_edges_access, self.last_run_started_at, - self._assessment_findings, ) + builder.__builder_cache["global"] = self self.__builder_cache[region.safe_name] = builder return builder diff --git a/plugins/aws/fix_plugin_aws/resource/ecr.py b/plugins/aws/fix_plugin_aws/resource/ecr.py index 461169566b..3830c03e38 100644 --- a/plugins/aws/fix_plugin_aws/resource/ecr.py +++ b/plugins/aws/fix_plugin_aws/resource/ecr.py @@ -8,7 +8,7 @@ from fix_plugin_aws.resource.base import AwsResource, AwsApiSpec, GraphBuilder from fix_plugin_aws.utils import ToDict -from fixlib.baseresources import HasResourcePolicy, PolicySource, PolicySourceKind +from fixlib.baseresources import HasResourcePolicy, ModelReference, PolicySource, PolicySourceKind from fixlib.json import sort_json from fixlib.json_bender import Bender, S, Bend from fixlib.types import Json @@ -34,6 +34,7 @@ class AwsEcrRepository(AwsResource, HasResourcePolicy): _kind_service: ClassVar[Optional[str]] = service_name _metadata: ClassVar[Dict[str, Any]] = {"icon": "repository", "group": "compute"} _aws_metadata: ClassVar[Dict[str, Any]] = {"provider_link_tpl": "https://{region_id}.console.aws.amazon.com/ecr/repositories/{name}?region={region}", "arn_tpl": "arn:{partition}:ecr:{region}:{account}:repository/{name}"} # fmt: skip + _reference_kinds: ClassVar[ModelReference] = {} api_spec: ClassVar[AwsApiSpec] = AwsApiSpec("ecr", "describe-repositories", "repositories") public_spec: ClassVar[AwsApiSpec] = AwsApiSpec("ecr-public", "describe-repositories", "repositories") mapping: ClassVar[Dict[str, Bender]] = { diff --git a/plugins/aws/test/collector_test.py b/plugins/aws/test/collector_test.py index 4be43407d9..e11c6edbd4 100644 --- a/plugins/aws/test/collector_test.py +++ b/plugins/aws/test/collector_test.py @@ -13,6 +13,7 @@ ) from fix_plugin_aws.resource.base import AwsResource, AwsApiSpec, GraphBuilder, AwsRegion from fix_plugin_aws.resource.ec2 import AwsEc2Instance +from fix_plugin_aws.resource.inspector import AwsInspectorFinding from fixlib.baseresources import BaseResource from fixlib.core.model_export import dataclasses_to_fixcore_model from test import account_collector, builder, aws_client, aws_config, no_feedback # noqa: F401 @@ -29,13 +30,16 @@ def count_kind(clazz: Type[AwsResource]) -> int: return count for resource in all_resources: + # we do not add findings to the graph --> skip check + if not isinstance(resource, AwsInspectorFinding): + continue assert count_kind(resource) > 0, f"No instances of {resource.__name__} found" # make sure all threads have been joined assert len(threading.enumerate()) == 1 # ensure the correct number of nodes and edges - assert count_kind(AwsResource) == 262 - assert len(account_collector.graph.edges) == 576 + assert count_kind(AwsResource) == 261 + assert len(account_collector.graph.edges) == 575 assert len(account_collector.graph.deferred_edges) == 2 for node in account_collector.graph.nodes: if isinstance(node, AwsRegion): diff --git a/plugins/aws/test/resources/files/inspector2/list-findings__EQUALS_test.json b/plugins/aws/test/resources/files/inspector2/list-findings__EQUALS_test.json index e95b432cbb..5ed86767be 100644 --- a/plugins/aws/test/resources/files/inspector2/list-findings__EQUALS_test.json +++ b/plugins/aws/test/resources/files/inspector2/list-findings__EQUALS_test.json @@ -3,228 +3,228 @@ { "awsAccountId": "foo", "codeVulnerabilityDetails": { - "cwes": [ - "foo", - "foo", - "foo" - ], - "detectorId": "foo", - "detectorName": "foo", - "detectorTags": [ - "foo", - "foo", - "foo" - ], - "filePath": { - "endLine": 123, - "fileName": "foo", - "filePath": "foo", - "startLine": 123 - }, - "referenceUrls": [ - "foo", - "foo", - "foo" - ], - "ruleId": "foo", - "sourceLambdaLayerArn": "foo" + "cwes": [ + "foo", + "foo", + "foo" + ], + "detectorId": "foo", + "detectorName": "foo", + "detectorTags": [ + "foo", + "foo", + "foo" + ], + "filePath": { + "endLine": 123, + "fileName": "foo", + "filePath": "foo", + "startLine": 123 + }, + "referenceUrls": [ + "foo", + "foo", + "foo" + ], + "ruleId": "foo", + "sourceLambdaLayerArn": "foo" }, "description": "foo", "epss": { - "score": 1.234 + "score": 1.234 }, "exploitAvailable": "NO", "exploitabilityDetails": { - "lastKnownExploitAt": "2024-10-14T18:00:11Z" + "lastKnownExploitAt": "2024-10-14T18:00:11Z" }, "findingArn": "foo", "firstObservedAt": "2024-10-14T18:00:11Z", "fixAvailable": "NO", "inspectorScore": 1.234, "inspectorScoreDetails": { - "adjustedCvss": { - "adjustments": [ - { - "metric": "foo", - "reason": "foo" - }, - { - "metric": "foo", - "reason": "foo" - }, - { - "metric": "foo", - "reason": "foo" + "adjustedCvss": { + "adjustments": [ + { + "metric": "foo", + "reason": "foo" + }, + { + "metric": "foo", + "reason": "foo" + }, + { + "metric": "foo", + "reason": "foo" + } + ], + "cvssSource": "foo", + "score": 1.234, + "scoreSource": "foo", + "scoringVector": "foo", + "version": "foo" } - ], - "cvssSource": "foo", - "score": 1.234, - "scoreSource": "foo", - "scoringVector": "foo", - "version": "foo" - } }, "lastObservedAt": "2024-10-14T18:00:11Z", "networkReachabilityDetails": { - "networkPath": { - "steps": [ - { - "componentId": "foo", - "componentType": "foo" + "networkPath": { + "steps": [ + { + "componentId": "foo", + "componentType": "foo" + }, + { + "componentId": "foo", + "componentType": "foo" + }, + { + "componentId": "foo", + "componentType": "foo" + } + ] }, - { - "componentId": "foo", - "componentType": "foo" + "openPortRange": { + "begin": 123, + "end": 123 }, - { - "componentId": "foo", - "componentType": "foo" - } - ] - }, - "openPortRange": { - "begin": 123, - "end": 123 - }, - "protocol": "UDP" + "protocol": "UDP" }, "packageVulnerabilityDetails": { - "cvss": [ - { - "baseScore": 1.234, - "scoringVector": "foo", - "source": "foo", - "version": "foo" - }, - { - "baseScore": 1.234, - "scoringVector": "foo", - "source": "foo", - "version": "foo" - }, - { - "baseScore": 1.234, - "scoringVector": "foo", - "source": "foo", - "version": "foo" - } - ], - "referenceUrls": [ - "foo", - "foo", - "foo" - ], - "relatedVulnerabilities": [ - "foo", - "foo", - "foo" - ], - "source": "foo", - "sourceUrl": "https://example.com", - "vendorCreatedAt": "2024-10-14T18:00:11Z", - "vendorSeverity": "foo", - "vendorUpdatedAt": "2024-10-14T18:00:11Z", - "vulnerabilityId": "foo", - "vulnerablePackages": [ - { - "arch": "foo", - "epoch": 123, - "filePath": "foo", - "fixedInVersion": "foo", - "name": "foo", - "packageManager": "CARGO", - "release": "foo", - "remediation": "foo", - "sourceLambdaLayerArn": "foo", - "sourceLayerHash": "foo", - "version": "foo" - } - ] - }, - "remediation": { - "recommendation": { - "Url": "https://example.com", - "text": "foo" - } - }, - "resources": [ - { - "details": { - "awsEc2Instance": { - "iamInstanceProfileArn": "foo", - "imageId": "foo", - "ipV4Addresses": [ - "foo", - "foo", - "foo" - ], - "ipV6Addresses": [ - "foo", - "foo", - "foo" - ], - "keyName": "foo", - "launchedAt": "2024-10-14T18:00:11Z", - "platform": "foo", - "subnetId": "foo", - "type": "foo", - "vpcId": "foo" - }, - "awsEcrContainerImage": { - "architecture": "foo", - "author": "foo", - "imageHash": "foo", - "imageTags": [ + "cvss": [ + { + "baseScore": 1.234, + "scoringVector": "foo", + "source": "foo", + "version": "foo" + }, + { + "baseScore": 1.234, + "scoringVector": "foo", + "source": "foo", + "version": "foo" + }, + { + "baseScore": 1.234, + "scoringVector": "foo", + "source": "foo", + "version": "foo" + } + ], + "referenceUrls": [ "foo", "foo", "foo" - ], - "platform": "foo", - "pushedAt": "2024-10-14T18:00:11Z", - "registry": "foo", - "repositoryName": "foo" - }, - "awsLambdaFunction": { - "architectures": [ - "ARM64", - "ARM64", - "ARM64" - ], - "codeSha256": "foo", - "executionRoleArn": "foo", - "functionName": "foo", - "lastModifiedAt": "2024-10-14T18:00:11Z", - "layers": [ + ], + "relatedVulnerabilities": [ "foo", "foo", "foo" - ], - "packageType": "ZIP", - "runtime": "NODEJS_12_X", - "version": "foo", - "vpcConfig": { - "securityGroupIds": [ - "foo", - "foo", - "foo" - ], - "subnetIds": [ - "foo", - "foo", - "foo" - ], - "vpcId": "foo" + ], + "source": "foo", + "sourceUrl": "https://example.com", + "vendorCreatedAt": "2024-10-14T18:00:11Z", + "vendorSeverity": "foo", + "vendorUpdatedAt": "2024-10-14T18:00:11Z", + "vulnerabilityId": "foo", + "vulnerablePackages": [ + { + "arch": "foo", + "epoch": 123, + "filePath": "foo", + "fixedInVersion": "foo", + "name": "foo", + "packageManager": "CARGO", + "release": "foo", + "remediation": "foo", + "sourceLambdaLayerArn": "foo", + "sourceLayerHash": "foo", + "version": "foo" } + ] + }, + "remediation": { + "recommendation": { + "Url": "https://example.com", + "text": "foo" + } + }, + "resources": [ + { + "details": { + "awsEc2Instance": { + "iamInstanceProfileArn": "foo", + "imageId": "foo", + "ipV4Addresses": [ + "foo", + "foo", + "foo" + ], + "ipV6Addresses": [ + "foo", + "foo", + "foo" + ], + "keyName": "foo", + "launchedAt": "2024-10-14T18:00:11Z", + "platform": "foo", + "subnetId": "foo", + "type": "foo", + "vpcId": "foo" + }, + "awsEcrContainerImage": { + "architecture": "foo", + "author": "foo", + "imageHash": "foo", + "imageTags": [ + "foo", + "foo", + "foo" + ], + "platform": "foo", + "pushedAt": "2024-10-14T18:00:11Z", + "registry": "foo", + "repositoryName": "foo" + }, + "awsLambdaFunction": { + "architectures": [ + "ARM64", + "ARM64", + "ARM64" + ], + "codeSha256": "foo", + "executionRoleArn": "foo", + "functionName": "foo", + "lastModifiedAt": "2024-10-14T18:00:11Z", + "layers": [ + "foo", + "foo", + "foo" + ], + "packageType": "ZIP", + "runtime": "NODEJS_12_X", + "version": "foo", + "vpcConfig": { + "securityGroupIds": [ + "foo", + "foo", + "foo" + ], + "subnetIds": [ + "foo", + "foo", + "foo" + ], + "vpcId": "foo" + } + } + }, + "id": "i-1", + "partition": "foo", + "region": "global", + "tags": { + "0": "foo" + }, + "type": "AWS_EC2_INSTANCE" } - }, - "id": "foo", - "partition": "foo", - "region": "foo", - "tags": { - "0": "foo" - }, - "type": "AWS_ECR_CONTAINER_IMAGE" - } ], "severity": "LOW", "status": "SUPPRESSED", diff --git a/plugins/aws/test/resources/inspector_test.py b/plugins/aws/test/resources/inspector_test.py index 61e8e2199c..7f9bdebfda 100644 --- a/plugins/aws/test/resources/inspector_test.py +++ b/plugins/aws/test/resources/inspector_test.py @@ -1,6 +1,9 @@ from fix_plugin_aws.resource.inspector import AwsInspectorFinding +from fix_plugin_aws.resource.ec2 import AwsEc2Instance + from test.resources import round_trip_for def test_inspector_findings() -> None: - round_trip_for(AwsInspectorFinding) + collected, _ = round_trip_for(AwsEc2Instance, region_name="global", collect_also=[AwsInspectorFinding]) + assert len(collected._assessments) == 1