From 5b9db9795d2cfed02f6bf2ff12e1f4cd6c83db4b Mon Sep 17 00:00:00 2001 From: Sergio Garcia <38561120+sergargar@users.noreply.github.com> Date: Wed, 8 Feb 2023 17:39:25 +0100 Subject: [PATCH] feat(new check): add accessanalyzer_enabled check (#1864) Co-authored-by: sergargar --- prowler/compliance/aws/cis_1.4_aws.json | 2 +- prowler/compliance/aws/cis_1.5_aws.json | 2 +- .../accessanalyzer_enabled/__init__.py | 0 .../accessanalyzer_enabled.metadata.json | 36 +++++ .../accessanalyzer_enabled.py | 36 +++++ ...accessanalyzer_enabled_without_findings.py | 2 +- .../accessanalyzer_enabled_test.py | 140 ++++++++++++++++++ ...sanalyzer_enabled_without_findings_test.py | 18 +-- 8 files changed, 224 insertions(+), 12 deletions(-) create mode 100644 prowler/providers/aws/services/accessanalyzer/accessanalyzer_enabled/__init__.py create mode 100644 prowler/providers/aws/services/accessanalyzer/accessanalyzer_enabled/accessanalyzer_enabled.metadata.json create mode 100644 prowler/providers/aws/services/accessanalyzer/accessanalyzer_enabled/accessanalyzer_enabled.py create mode 100644 tests/providers/aws/services/accessanalyzer/accessanalyzer_enabled/accessanalyzer_enabled_test.py diff --git a/prowler/compliance/aws/cis_1.4_aws.json b/prowler/compliance/aws/cis_1.4_aws.json index d9b2b8d2..f262037c 100644 --- a/prowler/compliance/aws/cis_1.4_aws.json +++ b/prowler/compliance/aws/cis_1.4_aws.json @@ -258,7 +258,7 @@ "Id": "1.20", "Description": "Ensure that IAM Access analyzer is enabled for all regions", "Checks": [ - "accessanalyzer_enabled_without_findings" + "accessanalyzer_enabled" ], "Attributes": [ { diff --git a/prowler/compliance/aws/cis_1.5_aws.json b/prowler/compliance/aws/cis_1.5_aws.json index f81c963d..fcc2d207 100644 --- a/prowler/compliance/aws/cis_1.5_aws.json +++ b/prowler/compliance/aws/cis_1.5_aws.json @@ -258,7 +258,7 @@ "Id": "1.20", "Description": "Ensure that IAM Access analyzer is enabled for all regions", "Checks": [ - "accessanalyzer_enabled_without_findings" + "accessanalyzer_enabled" ], "Attributes": [ { diff --git a/prowler/providers/aws/services/accessanalyzer/accessanalyzer_enabled/__init__.py b/prowler/providers/aws/services/accessanalyzer/accessanalyzer_enabled/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/prowler/providers/aws/services/accessanalyzer/accessanalyzer_enabled/accessanalyzer_enabled.metadata.json b/prowler/providers/aws/services/accessanalyzer/accessanalyzer_enabled/accessanalyzer_enabled.metadata.json new file mode 100644 index 00000000..d7623c15 --- /dev/null +++ b/prowler/providers/aws/services/accessanalyzer/accessanalyzer_enabled/accessanalyzer_enabled.metadata.json @@ -0,0 +1,36 @@ +{ + "Provider": "aws", + "CheckID": "accessanalyzer_enabled", + "CheckTitle": "Check if IAM Access Analyzer is enabled", + "CheckType": [ + "IAM" + ], + "ServiceName": "accessanalyzer", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:access-analyzer:region:account-id:analyzer/resource-id", + "Severity": "low", + "ResourceType": "Other", + "Description": "Check if IAM Access Analyzer is enabled", + "Risk": "AWS IAM Access Analyzer helps you identify the resources in your organization and accounts, such as Amazon S3 buckets or IAM roles, that are shared with an external entity. This lets you identify unintended access to your resources and data, which is a security risk. IAM Access Analyzer uses a form of mathematical analysis called automated reasoning, which applies logic and mathematical inference to determine all possible access paths allowed by a resource policy.", + "RelatedUrl": "https://docs.aws.amazon.com/IAM/latest/UserGuide/what-is-access-analyzer.html", + "Remediation": { + "Code": { + "CLI": "aws accessanalyzer create-analyzer --analyzer-name --type ", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Enable IAM Access Analyzer for all accounts, create analyzer and take action over it is recommendations (IAM Access Analyzer is available at no additional cost).", + "Url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/what-is-access-analyzer.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/aws/services/accessanalyzer/accessanalyzer_enabled/accessanalyzer_enabled.py b/prowler/providers/aws/services/accessanalyzer/accessanalyzer_enabled/accessanalyzer_enabled.py new file mode 100644 index 00000000..62c605fc --- /dev/null +++ b/prowler/providers/aws/services/accessanalyzer/accessanalyzer_enabled/accessanalyzer_enabled.py @@ -0,0 +1,36 @@ +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.accessanalyzer.accessanalyzer_client import ( + accessanalyzer_client, +) + + +class accessanalyzer_enabled(Check): + def execute(self): + findings = [] + for analyzer in accessanalyzer_client.analyzers: + report = Check_Report_AWS(self.metadata()) + report.region = analyzer.region + if analyzer.status == "ACTIVE": + report.status = "PASS" + report.status_extended = ( + f"IAM Access Analyzer {analyzer.name} is enabled" + ) + report.resource_id = analyzer.name + report.resource_arn = analyzer.arn + + elif analyzer.status == "NOT_AVAILABLE": + report.status = "FAIL" + report.status_extended = ( + f"IAM Access Analyzer in account {analyzer.name} is not enabled" + ) + report.resource_id = analyzer.name + else: + report.status = "FAIL" + report.status_extended = ( + f"IAM Access Analyzer {analyzer.name} is not active" + ) + report.resource_id = analyzer.name + report.resource_arn = analyzer.arn + findings.append(report) + + return findings diff --git a/prowler/providers/aws/services/accessanalyzer/accessanalyzer_enabled_without_findings/accessanalyzer_enabled_without_findings.py b/prowler/providers/aws/services/accessanalyzer/accessanalyzer_enabled_without_findings/accessanalyzer_enabled_without_findings.py index cd3d16b2..eedadc55 100644 --- a/prowler/providers/aws/services/accessanalyzer/accessanalyzer_enabled_without_findings/accessanalyzer_enabled_without_findings.py +++ b/prowler/providers/aws/services/accessanalyzer/accessanalyzer_enabled_without_findings/accessanalyzer_enabled_without_findings.py @@ -31,7 +31,7 @@ class accessanalyzer_enabled_without_findings(Check): elif analyzer.status == "NOT_AVAILABLE": report.status = "FAIL" report.status_extended = ( - f"IAM Access Analyzer {analyzer.name} is not enabled" + f"IAM Access Analyzer in account {analyzer.name} is not enabled" ) report.resource_id = analyzer.name else: diff --git a/tests/providers/aws/services/accessanalyzer/accessanalyzer_enabled/accessanalyzer_enabled_test.py b/tests/providers/aws/services/accessanalyzer/accessanalyzer_enabled/accessanalyzer_enabled_test.py new file mode 100644 index 00000000..671f16ea --- /dev/null +++ b/tests/providers/aws/services/accessanalyzer/accessanalyzer_enabled/accessanalyzer_enabled_test.py @@ -0,0 +1,140 @@ +from unittest import mock + +from prowler.providers.aws.services.accessanalyzer.accessanalyzer_service import ( + Analyzer, +) + + +class Test_accessanalyzer_enabled: + def test_no_analyzers(self): + accessanalyzer_client = mock.MagicMock + accessanalyzer_client.analyzers = [] + with mock.patch( + "prowler.providers.aws.services.accessanalyzer.accessanalyzer_service.AccessAnalyzer", + new=accessanalyzer_client, + ): + # Test Check + from prowler.providers.aws.services.accessanalyzer.accessanalyzer_enabled.accessanalyzer_enabled import ( + accessanalyzer_enabled, + ) + + check = accessanalyzer_enabled() + result = check.execute() + + assert len(result) == 0 + + def test_one_analyzer_not_available(self): + # Include analyzers to check + accessanalyzer_client = mock.MagicMock + accessanalyzer_client.analyzers = [ + Analyzer( + arn="", + name="012345678910", + status="NOT_AVAILABLE", + tags="", + type="", + region="eu-west-1", + ) + ] + with mock.patch( + "prowler.providers.aws.services.accessanalyzer.accessanalyzer_service.AccessAnalyzer", + accessanalyzer_client, + ): + from prowler.providers.aws.services.accessanalyzer.accessanalyzer_enabled.accessanalyzer_enabled import ( + accessanalyzer_enabled, + ) + + check = accessanalyzer_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "IAM Access Analyzer in account 012345678910 is not enabled" + ) + assert result[0].resource_id == "012345678910" + + def test_two_analyzers(self): + accessanalyzer_client = mock.MagicMock + accessanalyzer_client.analyzers = [ + Analyzer( + arn="", + name="012345678910", + status="NOT_AVAILABLE", + tags="", + type="", + region="eu-west-1", + ), + Analyzer( + arn="", + name="Test Analyzer", + status="ACTIVE", + tags="", + type="", + region="eu-west-2", + ), + ] + + # Patch AccessAnalyzer Client + with mock.patch( + "prowler.providers.aws.services.accessanalyzer.accessanalyzer_service.AccessAnalyzer", + new=accessanalyzer_client, + ): + # Test Check + from prowler.providers.aws.services.accessanalyzer.accessanalyzer_enabled.accessanalyzer_enabled import ( + accessanalyzer_enabled, + ) + + check = accessanalyzer_enabled() + result = check.execute() + + assert len(result) == 2 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "IAM Access Analyzer in account 012345678910 is not enabled" + ) + assert result[0].resource_id == "012345678910" + assert result[0].region == "eu-west-1" + assert result[1].status == "PASS" + assert ( + result[1].status_extended + == "IAM Access Analyzer Test Analyzer is enabled" + ) + assert result[1].resource_id == "Test Analyzer" + assert result[1].region == "eu-west-2" + + def test_one_active_analyzer(self): + accessanalyzer_client = mock.MagicMock + accessanalyzer_client.analyzers = [ + Analyzer( + arn="", + name="Test Analyzer", + status="ACTIVE", + tags="", + type="", + region="eu-west-2", + ) + ] + + with mock.patch( + "prowler.providers.aws.services.accessanalyzer.accessanalyzer_service.AccessAnalyzer", + new=accessanalyzer_client, + ): + # Test Check + from prowler.providers.aws.services.accessanalyzer.accessanalyzer_enabled.accessanalyzer_enabled import ( + accessanalyzer_enabled, + ) + + check = accessanalyzer_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "IAM Access Analyzer Test Analyzer is enabled" + ) + assert result[0].resource_id == "Test Analyzer" + assert result[0].region == "eu-west-2" diff --git a/tests/providers/aws/services/accessanalyzer/accessanalyzer_enabled_without_findings/accessanalyzer_enabled_without_findings_test.py b/tests/providers/aws/services/accessanalyzer/accessanalyzer_enabled_without_findings/accessanalyzer_enabled_without_findings_test.py index 77ab1f4d..08b05816 100644 --- a/tests/providers/aws/services/accessanalyzer/accessanalyzer_enabled_without_findings/accessanalyzer_enabled_without_findings_test.py +++ b/tests/providers/aws/services/accessanalyzer/accessanalyzer_enabled_without_findings/accessanalyzer_enabled_without_findings_test.py @@ -30,7 +30,7 @@ class Test_accessanalyzer_enabled_without_findings: accessanalyzer_client.analyzers = [ Analyzer( arn="", - name="Test Analyzer", + name="012345678910", status="NOT_AVAILABLE", tags="", type="", @@ -52,16 +52,16 @@ class Test_accessanalyzer_enabled_without_findings: assert result[0].status == "FAIL" assert ( result[0].status_extended - == "IAM Access Analyzer Test Analyzer is not enabled" + == "IAM Access Analyzer in account 012345678910 is not enabled" ) - assert result[0].resource_id == "Test Analyzer" + assert result[0].resource_id == "012345678910" def test_two_analyzers(self): accessanalyzer_client = mock.MagicMock accessanalyzer_client.analyzers = [ Analyzer( arn="", - name="Test Analyzer", + name="012345678910", status="NOT_AVAILABLE", tags="", type="", @@ -104,9 +104,9 @@ class Test_accessanalyzer_enabled_without_findings: assert result[0].status == "FAIL" assert ( result[0].status_extended - == "IAM Access Analyzer Test Analyzer is not enabled" + == "IAM Access Analyzer in account 012345678910 is not enabled" ) - assert result[0].resource_id == "Test Analyzer" + assert result[0].resource_id == "012345678910" assert result[0].region == "eu-west-1" assert result[1].status == "FAIL" assert ( @@ -155,7 +155,7 @@ class Test_accessanalyzer_enabled_without_findings: accessanalyzer_client.analyzers = [ Analyzer( arn="", - name="Test Analyzer", + name="012345678910", status="NOT_AVAILABLE", tags="", type="", @@ -179,7 +179,7 @@ class Test_accessanalyzer_enabled_without_findings: assert result[0].status == "FAIL" assert ( result[0].status_extended - == "IAM Access Analyzer Test Analyzer is not enabled" + == "IAM Access Analyzer in account 012345678910 is not enabled" ) - assert result[0].resource_id == "Test Analyzer" + assert result[0].resource_id == "012345678910" assert result[0].region == "eu-west-1"