sraverify 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sraverify/__init__.py +36 -0
- sraverify/checks/__init__.py +56 -0
- sraverify/checks/accessanalyzer/SRA_IAA_1.py +188 -0
- sraverify/checks/accessanalyzer/SRA_IAA_2.py +162 -0
- sraverify/checks/accessanalyzer/SRA_IAA_3.py +260 -0
- sraverify/checks/accessanalyzer/SRA_IAA_4.py +207 -0
- sraverify/checks/accessanalyzer/__init__.py +3 -0
- sraverify/checks/cloudtrail/SRA-CT-1.py +220 -0
- sraverify/checks/cloudtrail/SRA-CT-10.py +229 -0
- sraverify/checks/cloudtrail/SRA-CT-11.py +242 -0
- sraverify/checks/cloudtrail/SRA-CT-12.py +163 -0
- sraverify/checks/cloudtrail/SRA-CT-13.py +279 -0
- sraverify/checks/cloudtrail/SRA-CT-2.py +218 -0
- sraverify/checks/cloudtrail/SRA-CT-3.py +196 -0
- sraverify/checks/cloudtrail/SRA-CT-4.py +161 -0
- sraverify/checks/cloudtrail/SRA-CT-5.py +200 -0
- sraverify/checks/cloudtrail/SRA-CT-6.py +161 -0
- sraverify/checks/cloudtrail/SRA-CT-7.py +194 -0
- sraverify/checks/cloudtrail/SRA-CT-8.py +226 -0
- sraverify/checks/cloudtrail/SRA-CT-9.py +226 -0
- sraverify/checks/cloudtrail/__init__.py +3 -0
- sraverify/checks/config/SRA-CONFIG-1.py +197 -0
- sraverify/checks/config/__init__.py +3 -0
- sraverify/core/__init__.py +3 -0
- sraverify/core/check.py +227 -0
- sraverify/core/logging.py +37 -0
- sraverify/core/session.py +47 -0
- sraverify/lib/__init__.py +4 -0
- sraverify/lib/audit_info.py +37 -0
- sraverify/lib/banner.py +42 -0
- sraverify/lib/check_loader.py +80 -0
- sraverify/lib/org_mgmt_checker.py +86 -0
- sraverify/lib/outputs.py +46 -0
- sraverify/lib/progress.py +75 -0
- sraverify/lib/regions.py +27 -0
- sraverify/lib/session.py +23 -0
- sraverify/main.py +350 -0
- sraverify/services/__init__.py +3 -0
- sraverify/services/accessanalyzer/__init__.py +15 -0
- sraverify/services/accessanalyzer/base.py +123 -0
- sraverify/services/accessanalyzer/checks/__init__.py +3 -0
- sraverify/services/accessanalyzer/checks/sra_accessanalyzer_01.py +82 -0
- sraverify/services/accessanalyzer/checks/sra_accessanalyzer_02.py +82 -0
- sraverify/services/accessanalyzer/checks/sra_accessanalyzer_03.py +103 -0
- sraverify/services/accessanalyzer/checks/sra_accessanalyzer_04.py +139 -0
- sraverify/services/accessanalyzer/client.py +123 -0
- sraverify/services/account/__init__.py +9 -0
- sraverify/services/account/base.py +56 -0
- sraverify/services/account/checks/__init__.py +1 -0
- sraverify/services/account/checks/sra_account_01.py +65 -0
- sraverify/services/account/checks/sra_account_02.py +63 -0
- sraverify/services/account/checks/sra_account_03.py +63 -0
- sraverify/services/account/client.py +51 -0
- sraverify/services/auditmanager/__init__.py +10 -0
- sraverify/services/auditmanager/base.py +72 -0
- sraverify/services/auditmanager/checks/__init__.py +1 -0
- sraverify/services/auditmanager/checks/sra_auditmanager_01.py +58 -0
- sraverify/services/auditmanager/checks/sra_auditmanager_02.py +80 -0
- sraverify/services/auditmanager/client.py +58 -0
- sraverify/services/cloudtrail/__init__.py +33 -0
- sraverify/services/cloudtrail/base.py +167 -0
- sraverify/services/cloudtrail/checks/__init__.py +1 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_01.py +83 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_02.py +99 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_03.py +94 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_04.py +92 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_05.py +106 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_06.py +93 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_07.py +96 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_08.py +145 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_09.py +167 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_10.py +162 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_11.py +178 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_12.py +77 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_13.py +120 -0
- sraverify/services/cloudtrail/client.py +118 -0
- sraverify/services/config/__init__.py +25 -0
- sraverify/services/config/base.py +249 -0
- sraverify/services/config/checks/__init__.py +1 -0
- sraverify/services/config/checks/sra_config_01.py +123 -0
- sraverify/services/config/checks/sra_config_02.py +156 -0
- sraverify/services/config/checks/sra_config_03.py +149 -0
- sraverify/services/config/checks/sra_config_04.py +104 -0
- sraverify/services/config/checks/sra_config_05.py +104 -0
- sraverify/services/config/checks/sra_config_06.py +194 -0
- sraverify/services/config/checks/sra_config_07.py +162 -0
- sraverify/services/config/checks/sra_config_08.py +185 -0
- sraverify/services/config/checks/sra_config_09.py +177 -0
- sraverify/services/config/client.py +264 -0
- sraverify/services/ec2/__init__.py +8 -0
- sraverify/services/ec2/base.py +75 -0
- sraverify/services/ec2/checks/__init__.py +1 -0
- sraverify/services/ec2/checks/sra_ec2_01.py +83 -0
- sraverify/services/ec2/client.py +63 -0
- sraverify/services/firewallmanager/__init__.py +23 -0
- sraverify/services/firewallmanager/base.py +48 -0
- sraverify/services/firewallmanager/checks/__init__.py +1 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_01.py +75 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_02.py +57 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_03.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_04.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_05.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_06.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_07.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_08.py +61 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_09.py +61 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_10.py +71 -0
- sraverify/services/firewallmanager/client.py +40 -0
- sraverify/services/guardduty/__init__.py +58 -0
- sraverify/services/guardduty/base.py +207 -0
- sraverify/services/guardduty/checks/__init__.py +3 -0
- sraverify/services/guardduty/checks/sra_guardduty_01.py +51 -0
- sraverify/services/guardduty/checks/sra_guardduty_02.py +80 -0
- sraverify/services/guardduty/checks/sra_guardduty_03.py +77 -0
- sraverify/services/guardduty/checks/sra_guardduty_04.py +84 -0
- sraverify/services/guardduty/checks/sra_guardduty_05.py +84 -0
- sraverify/services/guardduty/checks/sra_guardduty_06.py +84 -0
- sraverify/services/guardduty/checks/sra_guardduty_07.py +85 -0
- sraverify/services/guardduty/checks/sra_guardduty_08.py +83 -0
- sraverify/services/guardduty/checks/sra_guardduty_09.py +84 -0
- sraverify/services/guardduty/checks/sra_guardduty_10.py +83 -0
- sraverify/services/guardduty/checks/sra_guardduty_11.py +93 -0
- sraverify/services/guardduty/checks/sra_guardduty_12.py +83 -0
- sraverify/services/guardduty/checks/sra_guardduty_13.py +90 -0
- sraverify/services/guardduty/checks/sra_guardduty_14.py +136 -0
- sraverify/services/guardduty/checks/sra_guardduty_15.py +94 -0
- sraverify/services/guardduty/checks/sra_guardduty_16.py +94 -0
- sraverify/services/guardduty/checks/sra_guardduty_17.py +91 -0
- sraverify/services/guardduty/checks/sra_guardduty_18.py +91 -0
- sraverify/services/guardduty/checks/sra_guardduty_19.py +91 -0
- sraverify/services/guardduty/checks/sra_guardduty_20.py +111 -0
- sraverify/services/guardduty/checks/sra_guardduty_21.py +112 -0
- sraverify/services/guardduty/checks/sra_guardduty_22.py +111 -0
- sraverify/services/guardduty/checks/sra_guardduty_23.py +154 -0
- sraverify/services/guardduty/checks/sra_guardduty_24.py +111 -0
- sraverify/services/guardduty/checks/sra_guardduty_25.py +111 -0
- sraverify/services/guardduty/client.py +107 -0
- sraverify/services/inspector/__init__.py +29 -0
- sraverify/services/inspector/base.py +233 -0
- sraverify/services/inspector/checks/__init__.py +3 -0
- sraverify/services/inspector/checks/sra_inspector_01.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_02.py +68 -0
- sraverify/services/inspector/checks/sra_inspector_03.py +68 -0
- sraverify/services/inspector/checks/sra_inspector_04.py +70 -0
- sraverify/services/inspector/checks/sra_inspector_05.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_06.py +115 -0
- sraverify/services/inspector/checks/sra_inspector_07.py +109 -0
- sraverify/services/inspector/checks/sra_inspector_08.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_09.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_10.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_11.py +69 -0
- sraverify/services/inspector/client.py +99 -0
- sraverify/services/macie/__init__.py +27 -0
- sraverify/services/macie/base.py +271 -0
- sraverify/services/macie/checks/__init__.py +1 -0
- sraverify/services/macie/checks/sra_macie_01.py +100 -0
- sraverify/services/macie/checks/sra_macie_02.py +102 -0
- sraverify/services/macie/checks/sra_macie_03.py +152 -0
- sraverify/services/macie/checks/sra_macie_04.py +120 -0
- sraverify/services/macie/checks/sra_macie_05.py +85 -0
- sraverify/services/macie/checks/sra_macie_06.py +124 -0
- sraverify/services/macie/checks/sra_macie_07.py +138 -0
- sraverify/services/macie/checks/sra_macie_08.py +82 -0
- sraverify/services/macie/checks/sra_macie_09.py +103 -0
- sraverify/services/macie/checks/sra_macie_10.py +81 -0
- sraverify/services/macie/client.py +220 -0
- sraverify/services/s3/__init__.py +16 -0
- sraverify/services/s3/base.py +69 -0
- sraverify/services/s3/checks/__init__.py +1 -0
- sraverify/services/s3/checks/sra_s3_01.py +89 -0
- sraverify/services/s3/checks/sra_s3_02.py +89 -0
- sraverify/services/s3/checks/sra_s3_03.py +88 -0
- sraverify/services/s3/checks/sra_s3_04.py +88 -0
- sraverify/services/s3/client.py +52 -0
- sraverify/services/securityhub/__init__.py +27 -0
- sraverify/services/securityhub/base.py +349 -0
- sraverify/services/securityhub/checks/__init__.py +1 -0
- sraverify/services/securityhub/checks/sra_securityhub_01.py +115 -0
- sraverify/services/securityhub/checks/sra_securityhub_02.py +114 -0
- sraverify/services/securityhub/checks/sra_securityhub_03.py +136 -0
- sraverify/services/securityhub/checks/sra_securityhub_04.py +75 -0
- sraverify/services/securityhub/checks/sra_securityhub_05.py +102 -0
- sraverify/services/securityhub/checks/sra_securityhub_06.py +113 -0
- sraverify/services/securityhub/checks/sra_securityhub_07.py +121 -0
- sraverify/services/securityhub/checks/sra_securityhub_08.py +113 -0
- sraverify/services/securityhub/checks/sra_securityhub_09.py +100 -0
- sraverify/services/securityhub/checks/sra_securityhub_10.py +94 -0
- sraverify/services/securityhub/checks/sra_securityhub_11.py +73 -0
- sraverify/services/securityhub/client.py +249 -0
- sraverify/services/securityincidentresponse/__init__.py +13 -0
- sraverify/services/securityincidentresponse/base.py +95 -0
- sraverify/services/securityincidentresponse/checks/__init__.py +1 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_01.py +77 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_02.py +72 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_03.py +86 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_04.py +117 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_05.py +55 -0
- sraverify/services/securityincidentresponse/client.py +71 -0
- sraverify/services/securitylake/__init__.py +39 -0
- sraverify/services/securitylake/base.py +461 -0
- sraverify/services/securitylake/checks/__init__.py +1 -0
- sraverify/services/securitylake/checks/sra_securitylake_01.py +98 -0
- sraverify/services/securitylake/checks/sra_securitylake_02.py +133 -0
- sraverify/services/securitylake/checks/sra_securitylake_03.py +116 -0
- sraverify/services/securitylake/checks/sra_securitylake_04.py +72 -0
- sraverify/services/securitylake/checks/sra_securitylake_05.py +116 -0
- sraverify/services/securitylake/checks/sra_securitylake_06.py +104 -0
- sraverify/services/securitylake/checks/sra_securitylake_07.py +108 -0
- sraverify/services/securitylake/checks/sra_securitylake_08.py +107 -0
- sraverify/services/securitylake/checks/sra_securitylake_09.py +107 -0
- sraverify/services/securitylake/checks/sra_securitylake_10.py +106 -0
- sraverify/services/securitylake/checks/sra_securitylake_11.py +109 -0
- sraverify/services/securitylake/checks/sra_securitylake_12.py +108 -0
- sraverify/services/securitylake/checks/sra_securitylake_13.py +108 -0
- sraverify/services/securitylake/checks/sra_securitylake_14.py +72 -0
- sraverify/services/securitylake/checks/sra_securitylake_15.py +120 -0
- sraverify/services/securitylake/checks/sra_securitylake_16.py +104 -0
- sraverify/services/securitylake/checks/sra_securitylake_17.py +103 -0
- sraverify/services/securitylake/client.py +247 -0
- sraverify/services/shield/__init__.py +33 -0
- sraverify/services/shield/base.py +199 -0
- sraverify/services/shield/checks/__init__.py +1 -0
- sraverify/services/shield/checks/sra_shield_01.py +68 -0
- sraverify/services/shield/checks/sra_shield_02.py +77 -0
- sraverify/services/shield/checks/sra_shield_03.py +84 -0
- sraverify/services/shield/checks/sra_shield_04.py +84 -0
- sraverify/services/shield/checks/sra_shield_05.py +84 -0
- sraverify/services/shield/checks/sra_shield_06.py +84 -0
- sraverify/services/shield/checks/sra_shield_07.py +84 -0
- sraverify/services/shield/checks/sra_shield_08.py +69 -0
- sraverify/services/shield/checks/sra_shield_09.py +86 -0
- sraverify/services/shield/checks/sra_shield_10.py +100 -0
- sraverify/services/shield/checks/sra_shield_11.py +71 -0
- sraverify/services/shield/checks/sra_shield_12.py +130 -0
- sraverify/services/shield/checks/sra_shield_13.py +112 -0
- sraverify/services/shield/checks/sra_shield_14.py +111 -0
- sraverify/services/shield/client.py +214 -0
- sraverify/services/waf/__init__.py +21 -0
- sraverify/services/waf/base.py +100 -0
- sraverify/services/waf/checks/__init__.py +1 -0
- sraverify/services/waf/checks/sra_waf_01.py +63 -0
- sraverify/services/waf/checks/sra_waf_02.py +82 -0
- sraverify/services/waf/checks/sra_waf_03.py +123 -0
- sraverify/services/waf/checks/sra_waf_04.py +94 -0
- sraverify/services/waf/checks/sra_waf_05.py +94 -0
- sraverify/services/waf/checks/sra_waf_06.py +91 -0
- sraverify/services/waf/checks/sra_waf_07.py +94 -0
- sraverify/services/waf/checks/sra_waf_08.py +66 -0
- sraverify/services/waf/checks/sra_waf_09.py +95 -0
- sraverify/services/waf/client.py +109 -0
- sraverify/utils/__init__.py +3 -0
- sraverify/utils/banner.py +65 -0
- sraverify/utils/outputs.py +57 -0
- sraverify/utils/progress.py +97 -0
- sraverify-0.1.0.dist-info/LICENSE +175 -0
- sraverify-0.1.0.dist-info/METADATA +516 -0
- sraverify-0.1.0.dist-info/NOTICE +1 -0
- sraverify-0.1.0.dist-info/RECORD +261 -0
- sraverify-0.1.0.dist-info/WHEEL +5 -0
- sraverify-0.1.0.dist-info/entry_points.txt +2 -0
- sraverify-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Logging configuration for SRA Verify.
|
|
3
|
+
"""
|
|
4
|
+
import logging
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
# Create a logger
|
|
8
|
+
logger = logging.getLogger("sraverify")
|
|
9
|
+
|
|
10
|
+
# Create handlers
|
|
11
|
+
console_handler = logging.StreamHandler(sys.stdout)
|
|
12
|
+
|
|
13
|
+
# Create formatters
|
|
14
|
+
default_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
15
|
+
console_formatter = logging.Formatter(default_format)
|
|
16
|
+
|
|
17
|
+
# Add formatters to handlers
|
|
18
|
+
console_handler.setFormatter(console_formatter)
|
|
19
|
+
|
|
20
|
+
# Add handlers to logger
|
|
21
|
+
logger.addHandler(console_handler)
|
|
22
|
+
|
|
23
|
+
# Set default level
|
|
24
|
+
logger.setLevel(logging.INFO)
|
|
25
|
+
|
|
26
|
+
def configure_logging(debug=False):
|
|
27
|
+
"""
|
|
28
|
+
Configure logging level based on debug flag.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
debug: If True, set logging level to DEBUG, otherwise INFO
|
|
32
|
+
"""
|
|
33
|
+
if debug:
|
|
34
|
+
logger.setLevel(logging.DEBUG)
|
|
35
|
+
logger.debug("Debug logging enabled")
|
|
36
|
+
else:
|
|
37
|
+
logger.setLevel(logging.INFO)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AWS session management.
|
|
3
|
+
"""
|
|
4
|
+
from typing import Optional
|
|
5
|
+
import boto3
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_session(region: Optional[str] = None, profile: Optional[str] = None,
|
|
9
|
+
role_arn: Optional[str] = None) -> boto3.Session:
|
|
10
|
+
"""
|
|
11
|
+
Get AWS session with optional region, profile, and role.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
region: AWS region name
|
|
15
|
+
profile: AWS profile name
|
|
16
|
+
role_arn: ARN of IAM role to assume
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
AWS session
|
|
20
|
+
|
|
21
|
+
Raises:
|
|
22
|
+
Exception: If session creation fails
|
|
23
|
+
"""
|
|
24
|
+
try:
|
|
25
|
+
# First create a session with the provided profile or default credentials
|
|
26
|
+
session = boto3.Session(region_name=region, profile_name=profile)
|
|
27
|
+
|
|
28
|
+
# If a role ARN is provided, assume that role
|
|
29
|
+
if role_arn:
|
|
30
|
+
sts_client = session.client('sts')
|
|
31
|
+
response = sts_client.assume_role(
|
|
32
|
+
RoleArn=role_arn,
|
|
33
|
+
RoleSessionName='sraverify-session'
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# Create a new session with the assumed role credentials
|
|
37
|
+
credentials = response['Credentials']
|
|
38
|
+
return boto3.Session(
|
|
39
|
+
aws_access_key_id=credentials['AccessKeyId'],
|
|
40
|
+
aws_secret_access_key=credentials['SecretAccessKey'],
|
|
41
|
+
aws_session_token=credentials['SessionToken'],
|
|
42
|
+
region_name=region
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
return session
|
|
46
|
+
except Exception as e:
|
|
47
|
+
raise Exception(f"Failed to create AWS session: {str(e)}")
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# sraverify/lib/audit_info.py
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
import boto3
|
|
4
|
+
from .session import get_session
|
|
5
|
+
from .regions import validate_regions
|
|
6
|
+
from .org_mgmt_checker import OrgMgmtChecker
|
|
7
|
+
|
|
8
|
+
class AuditInfo:
|
|
9
|
+
"""Class to hold audit context information"""
|
|
10
|
+
def __init__(self,
|
|
11
|
+
regions: Optional[List[str]] = None,
|
|
12
|
+
profile: Optional[str] = None,
|
|
13
|
+
session: Optional[boto3.Session] = None):
|
|
14
|
+
self.session = session or get_session(profile=profile)
|
|
15
|
+
self.regions = self._initialize_regions(regions)
|
|
16
|
+
self.profile = profile
|
|
17
|
+
self.account_id = self._get_account_id()
|
|
18
|
+
self.org_checker = OrgMgmtChecker()
|
|
19
|
+
if session:
|
|
20
|
+
self.org_checker.initialize(session)
|
|
21
|
+
|
|
22
|
+
def _initialize_regions(self, specified_regions: Optional[List[str]] = None) -> List[str]:
|
|
23
|
+
"""Initialize and validate regions"""
|
|
24
|
+
return validate_regions(specified_regions, self.session)
|
|
25
|
+
|
|
26
|
+
def _get_account_id(self) -> str:
|
|
27
|
+
"""Get AWS account ID"""
|
|
28
|
+
try:
|
|
29
|
+
return self.session.client('sts').get_caller_identity()['Account']
|
|
30
|
+
except Exception as e:
|
|
31
|
+
raise Exception(f"Failed to get AWS account ID: {str(e)}")
|
|
32
|
+
|
|
33
|
+
def get_regional_session(self, region: str) -> boto3.Session:
|
|
34
|
+
"""Get a session for a specific region"""
|
|
35
|
+
if region not in self.regions:
|
|
36
|
+
raise ValueError(f"Region {region} is not in the list of validated regions")
|
|
37
|
+
return get_session(region=region, profile=self.profile)
|
sraverify/lib/banner.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from colorama import Fore, Style
|
|
2
|
+
import datetime
|
|
3
|
+
import os
|
|
4
|
+
import boto3
|
|
5
|
+
|
|
6
|
+
def print_banner(profile: str, region: str, session: boto3.Session = None):
|
|
7
|
+
"""Print the SRAVerify banner and initial execution information."""
|
|
8
|
+
# ASCII art banner with version
|
|
9
|
+
print(f"""
|
|
10
|
+
_____ _____ ___ ___ _ __
|
|
11
|
+
/ ____| __ \ /\ \ \ / / (_)/ _|
|
|
12
|
+
| (___ | |__) | / \ \ \ / /__ _ __ _| |_ _ _
|
|
13
|
+
\___ \| _ / / /\ \ \ v / _ \| '__| | _| | | |
|
|
14
|
+
____) | | \ \ / ____ \ \ / __/| | | | | | |_| |
|
|
15
|
+
|_____/|_| \_\/_/ \_\ \___/ \___||_| |_|_| \__, |
|
|
16
|
+
__/ |
|
|
17
|
+
|___/ {Fore.BLUE}
|
|
18
|
+
the security architecture verifier tool{Style.RESET_ALL}
|
|
19
|
+
""")
|
|
20
|
+
|
|
21
|
+
print(f"{Fore.YELLOW}Date: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}{Style.RESET_ALL}\n")
|
|
22
|
+
|
|
23
|
+
# Print AWS credentials information
|
|
24
|
+
print("-> Using the AWS credentials below:")
|
|
25
|
+
print(f" · AWS-CLI Profile: {profile}")
|
|
26
|
+
print(f" · AWS Region: {region}")
|
|
27
|
+
|
|
28
|
+
if session:
|
|
29
|
+
try:
|
|
30
|
+
sts = session.client('sts')
|
|
31
|
+
caller_identity = sts.get_caller_identity()
|
|
32
|
+
print(f" · AWS Account: {caller_identity['Account']}")
|
|
33
|
+
print(f" · User Id: {caller_identity['UserId']}")
|
|
34
|
+
print(f" · Caller Identity ARN: {caller_identity['Arn']}")
|
|
35
|
+
except Exception as e:
|
|
36
|
+
print(f" · Unable to retrieve identity information: {str(e)}")
|
|
37
|
+
|
|
38
|
+
print("\n-> Using the following configuration:")
|
|
39
|
+
config_dir = os.path.join(os.path.dirname(__file__), "..", "config")
|
|
40
|
+
print(f" · Config File: {os.path.join(config_dir, 'config.yaml')}")
|
|
41
|
+
print(f" · Mutelist File: {os.path.join(config_dir, 'mutelist.yaml')}")
|
|
42
|
+
print(" · Scanning unused services and resources: False\n")
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# sraverify/lib/check_loader.py
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import importlib.util
|
|
5
|
+
from typing import List, Type, Dict
|
|
6
|
+
from sraverify.checks import SecurityCheck
|
|
7
|
+
|
|
8
|
+
def discover_checks(check_type: str = "all", debug: bool = False, security_ou_name: str = None) -> List[Type[SecurityCheck]]:
|
|
9
|
+
"""
|
|
10
|
+
Discover and load all security check classes.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
check_type: Type of check to run ("all", "account" or "organization")
|
|
14
|
+
debug: Whether to print debug information
|
|
15
|
+
security_ou_name: Optional name of security OU to search for
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
List of security check classes
|
|
19
|
+
"""
|
|
20
|
+
check_classes = []
|
|
21
|
+
checks_dir = os.path.join(os.path.dirname(__file__), '..', 'checks')
|
|
22
|
+
|
|
23
|
+
# Keep track of loaded check IDs to avoid duplicates
|
|
24
|
+
loaded_check_ids = set()
|
|
25
|
+
|
|
26
|
+
for root, _, files in os.walk(checks_dir):
|
|
27
|
+
for file in files:
|
|
28
|
+
if file.endswith('.py') and not file.startswith('__'):
|
|
29
|
+
file_path = os.path.join(root, file)
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
# Import the module
|
|
33
|
+
spec = importlib.util.spec_from_file_location(
|
|
34
|
+
f"sraverify.checks.{file[:-3]}",
|
|
35
|
+
file_path
|
|
36
|
+
)
|
|
37
|
+
if spec and spec.loader:
|
|
38
|
+
module = importlib.util.module_from_spec(spec)
|
|
39
|
+
spec.loader.exec_module(module)
|
|
40
|
+
|
|
41
|
+
# Find security check classes in the module
|
|
42
|
+
for item_name in dir(module):
|
|
43
|
+
item = getattr(module, item_name)
|
|
44
|
+
if (isinstance(item, type) and
|
|
45
|
+
issubclass(item, SecurityCheck) and
|
|
46
|
+
item != SecurityCheck):
|
|
47
|
+
|
|
48
|
+
# Create an instance to check its type
|
|
49
|
+
check_instance = item()
|
|
50
|
+
|
|
51
|
+
check_id = getattr(check_instance, 'check_id', None)
|
|
52
|
+
instance_check_type = getattr(check_instance, 'check_type', 'account')
|
|
53
|
+
|
|
54
|
+
# Skip if we've already loaded this check
|
|
55
|
+
if check_id in loaded_check_ids:
|
|
56
|
+
continue
|
|
57
|
+
|
|
58
|
+
# Filter based on check_type parameter
|
|
59
|
+
if check_type != "all" and instance_check_type != check_type:
|
|
60
|
+
if debug:
|
|
61
|
+
print(f"Debug: Skipping {check_id} (type: {instance_check_type}) - not matching requested type: {check_type}")
|
|
62
|
+
continue
|
|
63
|
+
|
|
64
|
+
loaded_check_ids.add(check_id)
|
|
65
|
+
check_classes.append(item)
|
|
66
|
+
|
|
67
|
+
if debug:
|
|
68
|
+
print(f"Debug: Loaded check {check_id} (type: {instance_check_type}) from {file}")
|
|
69
|
+
|
|
70
|
+
except Exception as e:
|
|
71
|
+
if debug:
|
|
72
|
+
print(f"Debug: Error loading {file}: {str(e)}")
|
|
73
|
+
continue
|
|
74
|
+
|
|
75
|
+
if debug:
|
|
76
|
+
print(f"\nDebug: Total checks loaded: {len(check_classes)}")
|
|
77
|
+
if check_type != "all":
|
|
78
|
+
print(f"Debug: Filtered for check_type: {check_type}")
|
|
79
|
+
|
|
80
|
+
return check_classes
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Organization Management Account Checker"""
|
|
2
|
+
|
|
3
|
+
from typing import Optional, Tuple
|
|
4
|
+
import boto3
|
|
5
|
+
from botocore.exceptions import ClientError
|
|
6
|
+
import logging
|
|
7
|
+
|
|
8
|
+
class OrgMgmtChecker:
|
|
9
|
+
"""Singleton class to check and cache organization management account status"""
|
|
10
|
+
|
|
11
|
+
_instance = None
|
|
12
|
+
_initialized = False
|
|
13
|
+
_is_org_management = False
|
|
14
|
+
_management_account_id = None
|
|
15
|
+
_current_account_id = None
|
|
16
|
+
_error_message = None
|
|
17
|
+
_logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
def __new__(cls):
|
|
20
|
+
if cls._instance is None:
|
|
21
|
+
cls._instance = super(OrgMgmtChecker, cls).__new__(cls)
|
|
22
|
+
return cls._instance
|
|
23
|
+
|
|
24
|
+
def __init__(self):
|
|
25
|
+
if not self._initialized:
|
|
26
|
+
self._initialized = True
|
|
27
|
+
|
|
28
|
+
def initialize(self, session: boto3.Session) -> None:
|
|
29
|
+
"""Initialize the checker with a session"""
|
|
30
|
+
try:
|
|
31
|
+
# Get current account ID
|
|
32
|
+
sts_client = session.client('sts')
|
|
33
|
+
self._current_account_id = sts_client.get_caller_identity()['Account']
|
|
34
|
+
|
|
35
|
+
# Get organization details
|
|
36
|
+
org_client = session.client('organizations')
|
|
37
|
+
try:
|
|
38
|
+
org_details = org_client.describe_organization()['Organization']
|
|
39
|
+
self._management_account_id = org_details.get('MasterAccountId') # For older API versions
|
|
40
|
+
if not self._management_account_id:
|
|
41
|
+
self._management_account_id = org_details.get('ManagementAccountId') # For newer API versions
|
|
42
|
+
|
|
43
|
+
# Check if current account is management account
|
|
44
|
+
self._is_org_management = self._current_account_id == self._management_account_id
|
|
45
|
+
self._error_message = None
|
|
46
|
+
|
|
47
|
+
if self._logger.isEnabledFor(logging.DEBUG):
|
|
48
|
+
self._logger.debug(f"Current Account: {self._current_account_id}")
|
|
49
|
+
self._logger.debug(f"Management Account: {self._management_account_id}")
|
|
50
|
+
self._logger.debug(f"Is Management: {self._is_org_management}")
|
|
51
|
+
|
|
52
|
+
except ClientError as e:
|
|
53
|
+
error_code = e.response['Error']['Code']
|
|
54
|
+
if error_code == 'AWSOrganizationsNotInUseException':
|
|
55
|
+
self._error_message = "AWS Organizations is not in use for this account"
|
|
56
|
+
else:
|
|
57
|
+
self._error_message = f"Organizations API error: {str(e)}"
|
|
58
|
+
self._is_org_management = False
|
|
59
|
+
self._management_account_id = None
|
|
60
|
+
|
|
61
|
+
except Exception as e:
|
|
62
|
+
self._error_message = str(e)
|
|
63
|
+
self._is_org_management = False
|
|
64
|
+
self._management_account_id = None
|
|
65
|
+
if self._logger.isEnabledFor(logging.DEBUG):
|
|
66
|
+
self._logger.debug(f"Error during initialization: {str(e)}")
|
|
67
|
+
|
|
68
|
+
def verify_org_management(self) -> Tuple[bool, Optional[str]]:
|
|
69
|
+
"""
|
|
70
|
+
Verify if current account is organization management account
|
|
71
|
+
Returns: (is_management_account, error_message)
|
|
72
|
+
"""
|
|
73
|
+
return self._is_org_management, self._error_message
|
|
74
|
+
|
|
75
|
+
def get_management_account_id(self) -> Optional[str]:
|
|
76
|
+
"""Get the management account ID if available"""
|
|
77
|
+
return self._management_account_id
|
|
78
|
+
|
|
79
|
+
def get_current_account_id(self) -> Optional[str]:
|
|
80
|
+
"""Get the current account ID"""
|
|
81
|
+
return self._current_account_id
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def is_initialized(self) -> bool:
|
|
85
|
+
"""Check if the checker has been initialized"""
|
|
86
|
+
return self._initialized
|
sraverify/lib/outputs.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Output handling for sraverify scan results
|
|
3
|
+
"""
|
|
4
|
+
import csv
|
|
5
|
+
from typing import List, Dict
|
|
6
|
+
|
|
7
|
+
# Required fields as per developer guide
|
|
8
|
+
REQUIRED_FIELDS = [
|
|
9
|
+
'CheckId',
|
|
10
|
+
'Status',
|
|
11
|
+
'Region',
|
|
12
|
+
'Severity',
|
|
13
|
+
'Title',
|
|
14
|
+
'Description',
|
|
15
|
+
'ResourceId',
|
|
16
|
+
'ResourceType',
|
|
17
|
+
'AccountId',
|
|
18
|
+
'CheckedValue',
|
|
19
|
+
'ActualValue',
|
|
20
|
+
'Remediation',
|
|
21
|
+
'Service',
|
|
22
|
+
'CheckLogic',
|
|
23
|
+
'CheckType'
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
def write_csv_output(findings: List[Dict], output_file: str):
|
|
27
|
+
"""
|
|
28
|
+
Write scan findings to a CSV file ensuring all required fields are present
|
|
29
|
+
"""
|
|
30
|
+
# Always create the CSV file, even if there are no findings
|
|
31
|
+
if not findings:
|
|
32
|
+
findings = []
|
|
33
|
+
|
|
34
|
+
# Ensure each finding has all required fields
|
|
35
|
+
for finding in findings:
|
|
36
|
+
for field in REQUIRED_FIELDS:
|
|
37
|
+
if field not in finding:
|
|
38
|
+
finding[field] = '' # Add empty string for missing fields
|
|
39
|
+
|
|
40
|
+
with open(output_file, 'w', newline='') as csvfile:
|
|
41
|
+
writer = csv.DictWriter(csvfile, fieldnames=REQUIRED_FIELDS)
|
|
42
|
+
writer.writeheader()
|
|
43
|
+
for finding in findings:
|
|
44
|
+
# Only write the required fields, in the correct order
|
|
45
|
+
row = {field: finding.get(field, '') for field in REQUIRED_FIELDS}
|
|
46
|
+
writer.writerow(row)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
class ScanProgress:
|
|
5
|
+
SPINNER = ['/', '-', '\\', '|']
|
|
6
|
+
|
|
7
|
+
def __init__(self, total_checks: int):
|
|
8
|
+
"""Initialize the progress tracker.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
total_checks: The total number of checks to be performed
|
|
12
|
+
|
|
13
|
+
Raises:
|
|
14
|
+
ValueError: If total_checks is less than or equal to 0
|
|
15
|
+
"""
|
|
16
|
+
if total_checks <= 0:
|
|
17
|
+
raise ValueError("total_checks must be greater than 0")
|
|
18
|
+
self.total_checks = total_checks
|
|
19
|
+
self.completed_checks = 0
|
|
20
|
+
self.start_time = time.time()
|
|
21
|
+
self.current_service: Optional[str] = None
|
|
22
|
+
self.spinner_index = 0
|
|
23
|
+
self.last_print_time = 0
|
|
24
|
+
self.print_interval = 0.1 # Update display every 0.1 seconds
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def progress(self) -> float:
|
|
28
|
+
"""Calculate the progress percentage."""
|
|
29
|
+
return (self.completed_checks / self.total_checks * 100)
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def duration(self) -> str:
|
|
33
|
+
"""Calculate the elapsed time in minutes:seconds format."""
|
|
34
|
+
elapsed = int(time.time() - self.start_time)
|
|
35
|
+
minutes = elapsed // 60
|
|
36
|
+
seconds = elapsed % 60
|
|
37
|
+
return f"{minutes}:{seconds:02d}"
|
|
38
|
+
|
|
39
|
+
def update(self, service: str):
|
|
40
|
+
"""Update the current service being scanned."""
|
|
41
|
+
self.current_service = service
|
|
42
|
+
self.print_progress()
|
|
43
|
+
|
|
44
|
+
def increment(self):
|
|
45
|
+
"""Increment the completed checks counter."""
|
|
46
|
+
self.completed_checks += 1
|
|
47
|
+
self.spinner_index = (self.spinner_index + 1) % len(self.SPINNER)
|
|
48
|
+
self.print_progress()
|
|
49
|
+
|
|
50
|
+
def print_progress(self):
|
|
51
|
+
"""Print the current progress status with rate limiting."""
|
|
52
|
+
current_time = time.time()
|
|
53
|
+
# Only update display if enough time has passed since last update
|
|
54
|
+
if current_time - self.last_print_time >= self.print_interval:
|
|
55
|
+
self._do_print()
|
|
56
|
+
self.last_print_time = current_time
|
|
57
|
+
|
|
58
|
+
def _do_print(self):
|
|
59
|
+
"""Actually perform the progress printing."""
|
|
60
|
+
spinner = self.SPINNER[self.spinner_index]
|
|
61
|
+
|
|
62
|
+
# Calculate the width of the progress bar (50 characters)
|
|
63
|
+
bar_width = 50
|
|
64
|
+
filled_length = int(self.progress / 100 * bar_width)
|
|
65
|
+
bar = '=' * filled_length + ' ' * (bar_width - filled_length)
|
|
66
|
+
|
|
67
|
+
# Format the progress bar with current status
|
|
68
|
+
print(f"\r-> Scanning {self.current_service} service |{bar}| {spinner} "
|
|
69
|
+
f"{self.completed_checks}/{self.total_checks} "
|
|
70
|
+
f"[{self.progress:.0f}%] in {self.duration}",
|
|
71
|
+
end="", flush=True)
|
|
72
|
+
|
|
73
|
+
def finish(self):
|
|
74
|
+
"""Complete the progress display with a newline."""
|
|
75
|
+
print() # Print newline to finish the progress display
|
sraverify/lib/regions.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# sraverify/lib/regions.py
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
import boto3
|
|
4
|
+
|
|
5
|
+
def get_enabled_regions(session: boto3.Session) -> List[str]:
|
|
6
|
+
"""Get all enabled regions in the AWS account"""
|
|
7
|
+
try:
|
|
8
|
+
ec2_client = session.client('ec2', region_name='us-east-1')
|
|
9
|
+
response = ec2_client.describe_regions(AllRegions=False)
|
|
10
|
+
return [region['RegionName'] for region in response['Regions']]
|
|
11
|
+
except Exception as e:
|
|
12
|
+
raise Exception(f"Failed to get enabled regions: {str(e)}")
|
|
13
|
+
|
|
14
|
+
def validate_regions(regions: List[str], session: boto3.Session) -> List[str]:
|
|
15
|
+
"""Validate if specified regions are valid and enabled"""
|
|
16
|
+
try:
|
|
17
|
+
enabled_regions = set(get_enabled_regions(session))
|
|
18
|
+
|
|
19
|
+
if regions:
|
|
20
|
+
invalid_regions = set(regions) - enabled_regions
|
|
21
|
+
if invalid_regions:
|
|
22
|
+
raise ValueError(f"Invalid or disabled regions: {', '.join(invalid_regions)}")
|
|
23
|
+
return regions
|
|
24
|
+
|
|
25
|
+
return list(enabled_regions)
|
|
26
|
+
except Exception as e:
|
|
27
|
+
raise Exception(f"Failed to validate regions: {str(e)}")
|
sraverify/lib/session.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# sraverify/lib/session.py
|
|
2
|
+
import boto3
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
def get_session(region: Optional[str] = None, profile: Optional[str] = None) -> boto3.Session:
|
|
6
|
+
"""Get AWS session with optional region and profile"""
|
|
7
|
+
try:
|
|
8
|
+
return boto3.Session(region_name=region, profile_name=profile)
|
|
9
|
+
except Exception as e:
|
|
10
|
+
raise Exception(f"Failed to create AWS session: {str(e)}")
|
|
11
|
+
|
|
12
|
+
def get_regional_session(base_session: boto3.Session, region: str) -> boto3.Session:
|
|
13
|
+
"""Create a new session for a specific region while maintaining credentials"""
|
|
14
|
+
try:
|
|
15
|
+
credentials = base_session.get_credentials()
|
|
16
|
+
return boto3.Session(
|
|
17
|
+
aws_access_key_id=credentials.access_key,
|
|
18
|
+
aws_secret_access_key=credentials.secret_key,
|
|
19
|
+
aws_session_token=credentials.token,
|
|
20
|
+
region_name=region
|
|
21
|
+
)
|
|
22
|
+
except Exception as e:
|
|
23
|
+
raise Exception(f"Failed to create regional session: {str(e)}")
|