mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-04-01 13:47:21 +00:00
Compare commits
38 Commits
PRWLR-7212
...
PRWLR-7205
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a8a286850 | ||
|
|
02b65a1471 | ||
|
|
82ab20deec | ||
|
|
d7e3b1c760 | ||
|
|
166e07939d | ||
|
|
c5cf1c4bfb | ||
|
|
2147866229 | ||
|
|
761c97100a | ||
|
|
4fcb6b34e7 | ||
|
|
07c5ae547f | ||
|
|
09b33d05a3 | ||
|
|
6a7cfd175c | ||
|
|
82543c0d63 | ||
|
|
7360395263 | ||
|
|
4ae790ee73 | ||
|
|
7a2d3db082 | ||
|
|
40934d34b2 | ||
|
|
5c93372210 | ||
|
|
ffcc516f00 | ||
|
|
9d4094e19e | ||
|
|
00e491415f | ||
|
|
e17cbed4b3 | ||
|
|
d1e41f16ef | ||
|
|
a17c3f94fc | ||
|
|
70f8232747 | ||
|
|
31189f0d11 | ||
|
|
5aaf6e4858 | ||
|
|
e05cc4cfab | ||
|
|
18a6f29593 | ||
|
|
fc826da50c | ||
|
|
b30ee077da | ||
|
|
efdd967763 | ||
|
|
ee146cd43e | ||
|
|
f40aea757e | ||
|
|
7db24f8cb7 | ||
|
|
f78e5c9e33 | ||
|
|
d91bbe1ef4 | ||
|
|
c0d211492e |
@@ -12,6 +12,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- `vm_ensure_using_approved_images` check for Azure provider [(#8168)](https://github.com/prowler-cloud/prowler/pull/8168)
|
||||
- `vm_scaleset_associated_load_balancer` check for Azure provider [(#8181)](https://github.com/prowler-cloud/prowler/pull/8181)
|
||||
- Add `test_connection` method to GitHub provider [(#8248)](https://github.com/prowler-cloud/prowler/pull/8248)
|
||||
- Migrate AWS fixers to new class structure [(#8009)](https://github.com/prowler-cloud/prowler/pull/8009)
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -90,6 +91,9 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
### Removed
|
||||
- OCSF version number references to point always to the latest [(#8064)](https://github.com/prowler-cloud/prowler/pull/8064)
|
||||
|
||||
### Fixed
|
||||
- Update SDK Azure call for ftps_state in the App Service. [(#7923)](https://github.com/prowler-cloud/prowler/pull/7923)
|
||||
|
||||
---
|
||||
|
||||
## [v5.7.5] (Prowler 5.7.5)
|
||||
|
||||
@@ -31,7 +31,6 @@ from prowler.lib.check.check import (
|
||||
print_fixers,
|
||||
print_services,
|
||||
remove_custom_checks_module,
|
||||
run_fixer,
|
||||
)
|
||||
from prowler.lib.check.checks_loader import load_checks_to_execute
|
||||
from prowler.lib.check.compliance import update_checks_metadata_with_compliance
|
||||
@@ -42,6 +41,7 @@ from prowler.lib.check.custom_checks_metadata import (
|
||||
)
|
||||
from prowler.lib.check.models import CheckMetadata
|
||||
from prowler.lib.cli.parser import ProwlerArgumentParser
|
||||
from prowler.lib.fix.fixer import Fixer
|
||||
from prowler.lib.logger import logger, set_logging_config
|
||||
from prowler.lib.outputs.asff.asff import ASFF
|
||||
from prowler.lib.outputs.compliance.aws_well_architected.aws_well_architected import (
|
||||
@@ -300,6 +300,7 @@ def prowler():
|
||||
output_options = M365OutputOptions(
|
||||
args, bulk_checks_metadata, global_provider.identity
|
||||
)
|
||||
global_provider.set_output_options(output_options)
|
||||
elif provider == "nhn":
|
||||
output_options = NHNOutputOptions(
|
||||
args, bulk_checks_metadata, global_provider.identity
|
||||
@@ -332,11 +333,11 @@ def prowler():
|
||||
)
|
||||
|
||||
# Prowler Fixer
|
||||
if output_options.fixer:
|
||||
if args.fixer:
|
||||
print(f"{Style.BRIGHT}\nRunning Prowler Fixer, please wait...{Style.RESET_ALL}")
|
||||
# Check if there are any FAIL findings
|
||||
if any("FAIL" in finding.status for finding in findings):
|
||||
fixed_findings = run_fixer(findings)
|
||||
fixed_findings = Fixer.run_fixer(findings)
|
||||
if not fixed_findings:
|
||||
print(
|
||||
f"{Style.BRIGHT}{Fore.RED}\nThere were findings to fix, but the fixer failed or it is not implemented for those findings yet. {Style.RESET_ALL}\n"
|
||||
|
||||
@@ -298,91 +298,6 @@ def import_check(check_path: str) -> ModuleType:
|
||||
return lib
|
||||
|
||||
|
||||
def run_fixer(check_findings: list) -> int:
|
||||
"""
|
||||
Run the fixer for the check if it exists and there are any FAIL findings
|
||||
Args:
|
||||
check_findings (list): list of findings
|
||||
Returns:
|
||||
int: number of fixed findings
|
||||
"""
|
||||
try:
|
||||
# Map findings to each check
|
||||
findings_dict = {}
|
||||
fixed_findings = 0
|
||||
for finding in check_findings:
|
||||
if finding.check_metadata.CheckID not in findings_dict:
|
||||
findings_dict[finding.check_metadata.CheckID] = []
|
||||
findings_dict[finding.check_metadata.CheckID].append(finding)
|
||||
|
||||
for check, findings in findings_dict.items():
|
||||
# Check if there are any FAIL findings for the check
|
||||
if any("FAIL" in finding.status for finding in findings):
|
||||
try:
|
||||
check_module_path = f"prowler.providers.{findings[0].check_metadata.Provider}.services.{findings[0].check_metadata.ServiceName}.{check}.{check}_fixer"
|
||||
lib = import_check(check_module_path)
|
||||
fixer = getattr(lib, "fixer")
|
||||
except ModuleNotFoundError:
|
||||
logger.error(f"Fixer method not implemented for check {check}")
|
||||
else:
|
||||
print(
|
||||
f"\nFixing fails for check {Fore.YELLOW}{check}{Style.RESET_ALL}..."
|
||||
)
|
||||
for finding in findings:
|
||||
if finding.status == "FAIL":
|
||||
# Check what type of fixer is:
|
||||
# - If it is a fixer for a specific resource and region
|
||||
# - If it is a fixer for a specific region
|
||||
# - If it is a fixer for a specific resource
|
||||
if (
|
||||
"region" in fixer.__code__.co_varnames
|
||||
and "resource_id" in fixer.__code__.co_varnames
|
||||
):
|
||||
print(
|
||||
f"\t{orange_color}FIXING{Style.RESET_ALL} {finding.resource_id} in {finding.region}... "
|
||||
)
|
||||
if fixer(
|
||||
resource_id=finding.resource_id,
|
||||
region=finding.region,
|
||||
):
|
||||
fixed_findings += 1
|
||||
print(f"\t{Fore.GREEN}DONE{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"\t{Fore.RED}ERROR{Style.RESET_ALL}")
|
||||
elif "region" in fixer.__code__.co_varnames:
|
||||
print(
|
||||
f"\t{orange_color}FIXING{Style.RESET_ALL} {finding.region}... "
|
||||
)
|
||||
if fixer(region=finding.region):
|
||||
fixed_findings += 1
|
||||
print(f"\t{Fore.GREEN}DONE{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"\t{Fore.RED}ERROR{Style.RESET_ALL}")
|
||||
elif "resource_arn" in fixer.__code__.co_varnames:
|
||||
print(
|
||||
f"\t{orange_color}FIXING{Style.RESET_ALL} Resource {finding.resource_arn}... "
|
||||
)
|
||||
if fixer(resource_arn=finding.resource_arn):
|
||||
fixed_findings += 1
|
||||
print(f"\t{Fore.GREEN}DONE{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"\t{Fore.RED}ERROR{Style.RESET_ALL}")
|
||||
else:
|
||||
print(
|
||||
f"\t{orange_color}FIXING{Style.RESET_ALL} Resource {finding.resource_id}... "
|
||||
)
|
||||
if fixer(resource_id=finding.resource_id):
|
||||
fixed_findings += 1
|
||||
print(f"\t\t{Fore.GREEN}DONE{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"\t\t{Fore.RED}ERROR{Style.RESET_ALL}")
|
||||
return fixed_findings
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
|
||||
def execute_checks(
|
||||
checks_to_execute: list,
|
||||
global_provider: Any,
|
||||
|
||||
@@ -72,6 +72,7 @@ Detailed documentation at https://docs.prowler.com
|
||||
self.__init_config_parser__()
|
||||
self.__init_custom_checks_metadata_parser__()
|
||||
self.__init_third_party_integrations_parser__()
|
||||
self.__init_fixer_parser__()
|
||||
|
||||
# Init Providers Arguments
|
||||
init_providers_parser(self)
|
||||
@@ -393,3 +394,12 @@ Detailed documentation at https://docs.prowler.com
|
||||
action="store_true",
|
||||
help="Send a summary of the execution with a Slack APP in your channel. Environment variables SLACK_API_TOKEN and SLACK_CHANNEL_NAME are required (see more in https://docs.prowler.cloud/en/latest/tutorials/integrations/#slack).",
|
||||
)
|
||||
|
||||
def __init_fixer_parser__(self):
|
||||
"""Initialize the fixer parser with its arguments"""
|
||||
fixer_parser = self.common_providers_parser.add_argument_group("Fixer")
|
||||
fixer_parser.add_argument(
|
||||
"--fixer",
|
||||
action="store_true",
|
||||
help="Fix the failed findings that can be fixed by Prowler",
|
||||
)
|
||||
|
||||
0
prowler/lib/fix/__init__.py
Normal file
0
prowler/lib/fix/__init__.py
Normal file
219
prowler/lib/fix/fixer.py
Normal file
219
prowler/lib/fix/fixer.py
Normal file
@@ -0,0 +1,219 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Dict, List, Optional, Union
|
||||
|
||||
from colorama import Fore, Style
|
||||
|
||||
from prowler.lib.check.models import Check_Report
|
||||
from prowler.lib.logger import logger
|
||||
|
||||
|
||||
class Fixer(ABC):
|
||||
"""Base class for all fixers"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
description: str,
|
||||
cost_impact: bool = False,
|
||||
cost_description: Optional[str] = None,
|
||||
):
|
||||
"""
|
||||
Initialize base fixer class.
|
||||
|
||||
Args:
|
||||
description (str): Description of the fixer
|
||||
cost_impact (bool): Whether the fixer has a cost impact
|
||||
cost_description (Optional[str]): Description of the cost impact
|
||||
"""
|
||||
self._client = None
|
||||
self.logger = logger
|
||||
self.description = description
|
||||
self.cost_impact = cost_impact
|
||||
self.cost_description = cost_description
|
||||
|
||||
def _get_fixer_info(self) -> Dict:
|
||||
"""Get fixer metadata"""
|
||||
return {
|
||||
"description": self.description,
|
||||
"cost_impact": self.cost_impact,
|
||||
"cost_description": self.cost_description,
|
||||
}
|
||||
|
||||
@abstractmethod
|
||||
def fix(self, finding: Optional[Check_Report] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Main method that all fixers must implement.
|
||||
|
||||
Args:
|
||||
finding (Optional[Check_Report]): Finding to fix
|
||||
**kwargs: Additional arguments specific to each fixer
|
||||
|
||||
Returns:
|
||||
bool: True if fix was successful, False otherwise
|
||||
"""
|
||||
|
||||
@property
|
||||
def client(self):
|
||||
"""Lazy load of the client"""
|
||||
return self._client
|
||||
|
||||
@classmethod
|
||||
def get_fixer_for_finding(
|
||||
cls,
|
||||
finding: Check_Report,
|
||||
) -> Optional["Fixer"]:
|
||||
"""
|
||||
Factory method to get the appropriate fixer for a finding.
|
||||
|
||||
Args:
|
||||
finding (Check_Report): The finding to fix
|
||||
credentials (Optional[Dict]): Optional credentials for isolated execution
|
||||
session_config (Optional[Dict]): Optional session configuration
|
||||
|
||||
Returns:
|
||||
Optional[Fixer]: An instance of the appropriate fixer or None if no fixer is found
|
||||
"""
|
||||
try:
|
||||
# Extract check name from finding
|
||||
check_name = finding.check_metadata.CheckID
|
||||
if not check_name:
|
||||
logger.error("Finding does not contain a check ID")
|
||||
return None
|
||||
|
||||
# Convert check name to fixer class name
|
||||
# Example: rds_instance_no_public_access -> RdsInstanceNoPublicAccessFixer
|
||||
fixer_name = (
|
||||
"".join(word.capitalize() for word in check_name.split("_")) + "Fixer"
|
||||
)
|
||||
|
||||
# Get provider from finding
|
||||
provider = finding.check_metadata.Provider
|
||||
if not provider:
|
||||
logger.error("Finding does not contain a provider")
|
||||
return None
|
||||
|
||||
# Get service name from finding
|
||||
service_name = finding.check_metadata.ServiceName
|
||||
|
||||
# Import the fixer class dynamically
|
||||
try:
|
||||
# Build the module path using the service name and check name
|
||||
module_path = f"prowler.providers.{provider.lower()}.services.{service_name}.{check_name}.{check_name}_fixer"
|
||||
module = __import__(module_path, fromlist=[fixer_name])
|
||||
fixer_class = getattr(module, fixer_name)
|
||||
return fixer_class()
|
||||
except (ImportError, AttributeError):
|
||||
print(
|
||||
f"\n{Fore.YELLOW}No fixer available for check {check_name}{Style.RESET_ALL}"
|
||||
)
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting fixer for finding: {str(e)}")
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def run_fixer(
|
||||
cls,
|
||||
findings: Union[Check_Report, List[Check_Report]],
|
||||
) -> int:
|
||||
"""
|
||||
Method to execute the fixer on one or multiple findings.
|
||||
|
||||
Args:
|
||||
findings (Union[Check_Report, List[Check_Report]]): A single finding or list of findings to fix
|
||||
|
||||
Returns:
|
||||
int: Number of findings successfully fixed
|
||||
"""
|
||||
try:
|
||||
# Handle single finding case
|
||||
if isinstance(findings, Check_Report):
|
||||
if findings.status != "FAIL":
|
||||
return 0
|
||||
check_id = findings.check_metadata.CheckID
|
||||
if not check_id:
|
||||
return 0
|
||||
return cls.run_individual_fixer(check_id, [findings])
|
||||
|
||||
# Handle multiple findings case
|
||||
fixed_findings = 0
|
||||
findings_by_check = {}
|
||||
|
||||
# Group findings by check
|
||||
for finding in findings:
|
||||
if finding.status != "FAIL":
|
||||
continue
|
||||
check_id = finding.check_metadata.CheckID
|
||||
if not check_id:
|
||||
continue
|
||||
if check_id not in findings_by_check:
|
||||
findings_by_check[check_id] = []
|
||||
findings_by_check[check_id].append(finding)
|
||||
|
||||
# Process each check
|
||||
for check_id, check_findings in findings_by_check.items():
|
||||
fixed_findings += cls.run_individual_fixer(check_id, check_findings)
|
||||
|
||||
return fixed_findings
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return 0
|
||||
|
||||
@classmethod
|
||||
def run_individual_fixer(cls, check_id: str, findings: List[Check_Report]) -> int:
|
||||
"""
|
||||
Run the fixer for a specific check ID.
|
||||
|
||||
Args:
|
||||
check_id (str): The check ID to fix
|
||||
findings (List[Check_Report]): List of findings to process
|
||||
|
||||
Returns:
|
||||
int: Number of findings successfully fixed
|
||||
"""
|
||||
try:
|
||||
# Filter findings for this check_id and status FAIL
|
||||
check_findings = [
|
||||
finding
|
||||
for finding in findings
|
||||
if finding.check_metadata.CheckID == check_id
|
||||
and finding.status == "FAIL"
|
||||
]
|
||||
|
||||
if not check_findings:
|
||||
return 0
|
||||
|
||||
# Get the fixer for this check
|
||||
fixer = cls.get_fixer_for_finding(check_findings[0])
|
||||
if not fixer:
|
||||
return 0
|
||||
|
||||
# Print fixer information
|
||||
print(f"\n{Fore.CYAN}Fixer Information for {check_id}:{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}================================={Style.RESET_ALL}")
|
||||
for key, value in fixer._get_fixer_info().items():
|
||||
print(f"{Fore.CYAN}{key}: {Style.RESET_ALL}{value}")
|
||||
print(f"{Fore.CYAN}================================={Style.RESET_ALL}\n")
|
||||
|
||||
print(
|
||||
f"\nFixing fails for check {Fore.YELLOW}{check_id}{Style.RESET_ALL}..."
|
||||
)
|
||||
|
||||
fixed_findings = 0
|
||||
for finding in check_findings:
|
||||
if fixer.fix(finding=finding):
|
||||
fixed_findings += 1
|
||||
print(f"\t{Fore.GREEN}DONE{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"\t{Fore.RED}ERROR{Style.RESET_ALL}")
|
||||
|
||||
return fixed_findings
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return 0
|
||||
@@ -159,14 +159,6 @@ def init_parser(self):
|
||||
help="Scan unused services",
|
||||
)
|
||||
|
||||
# Prowler Fixer
|
||||
prowler_fixer_subparser = aws_parser.add_argument_group("Prowler Fixer")
|
||||
prowler_fixer_subparser.add_argument(
|
||||
"--fixer",
|
||||
action="store_true",
|
||||
help="Fix the failed findings that can be fixed by Prowler",
|
||||
)
|
||||
|
||||
|
||||
def validate_session_duration(session_duration: int) -> int:
|
||||
"""validate_session_duration validates that the input session_duration is valid"""
|
||||
|
||||
0
prowler/providers/aws/lib/fix/__init__.py
Normal file
0
prowler/providers/aws/lib/fix/__init__.py
Normal file
101
prowler/providers/aws/lib/fix/fixer.py
Normal file
101
prowler/providers/aws/lib/fix/fixer.py
Normal file
@@ -0,0 +1,101 @@
|
||||
from typing import Dict, Optional
|
||||
|
||||
from colorama import Style
|
||||
|
||||
from prowler.config.config import orange_color
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.fix.fixer import Fixer
|
||||
from prowler.lib.logger import logger
|
||||
|
||||
|
||||
class AWSFixer(Fixer):
|
||||
"""AWS specific fixer implementation"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
description: str,
|
||||
cost_impact: bool = False,
|
||||
cost_description: Optional[str] = None,
|
||||
service: str = "",
|
||||
iam_policy_required: Optional[Dict] = None,
|
||||
):
|
||||
"""
|
||||
Initialize AWS fixer with metadata.
|
||||
|
||||
Args:
|
||||
description (str): Description of the fixer
|
||||
cost_impact (bool): Whether the fixer has a cost impact
|
||||
cost_description (Optional[str]): Description of the cost impact
|
||||
service (str): AWS service name
|
||||
iam_policy_required (Optional[Dict]): Required IAM policy for the fixer
|
||||
"""
|
||||
super().__init__(description, cost_impact, cost_description)
|
||||
self.service = service
|
||||
self.iam_policy_required = iam_policy_required or {}
|
||||
|
||||
def _get_fixer_info(self):
|
||||
"""Each fixer must define its metadata"""
|
||||
fixer_info = super()._get_fixer_info()
|
||||
fixer_info["service"] = self.service
|
||||
fixer_info["iam_policy_required"] = self.iam_policy_required
|
||||
return fixer_info
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
AWS specific method to execute the fixer.
|
||||
This method handles the printing of fixing status messages.
|
||||
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: Additional AWS-specific arguments (region, resource_id, resource_arn)
|
||||
|
||||
Returns:
|
||||
bool: True if fixing was successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
# Get values either from finding or kwargs
|
||||
region = None
|
||||
resource_id = None
|
||||
resource_arn = None
|
||||
|
||||
if finding:
|
||||
region = finding.region if hasattr(finding, "region") else None
|
||||
resource_id = (
|
||||
finding.resource_id if hasattr(finding, "resource_id") else None
|
||||
)
|
||||
resource_arn = (
|
||||
finding.resource_arn if hasattr(finding, "resource_arn") else None
|
||||
)
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
resource_id = kwargs.get("resource_id")
|
||||
resource_arn = kwargs.get("resource_arn")
|
||||
|
||||
# Print the appropriate message based on available information
|
||||
if region and resource_id:
|
||||
print(
|
||||
f"\t{orange_color}FIXING {resource_id} in {region}...{Style.RESET_ALL}"
|
||||
)
|
||||
elif region:
|
||||
print(f"\t{orange_color}FIXING {region}...{Style.RESET_ALL}")
|
||||
elif resource_arn:
|
||||
print(
|
||||
f"\t{orange_color}FIXING Resource {resource_arn}...{Style.RESET_ALL}"
|
||||
)
|
||||
elif resource_id:
|
||||
print(
|
||||
f"\t{orange_color}FIXING Resource {resource_id}...{Style.RESET_ALL}"
|
||||
)
|
||||
else:
|
||||
logger.error(
|
||||
"Either finding or required kwargs (region, resource_id, resource_arn) must be provided"
|
||||
)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
@@ -1,42 +1,69 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.accessanalyzer.accessanalyzer_client import (
|
||||
accessanalyzer_client,
|
||||
)
|
||||
|
||||
|
||||
def fixer(region):
|
||||
class AccessAnalyzerEnabledFixer(AWSFixer):
|
||||
"""
|
||||
Enable Access Analyzer in a region. Requires the access-analyzer:CreateAnalyzer permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "access-analyzer:CreateAnalyzer",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
region (str): AWS region
|
||||
Returns:
|
||||
bool: True if Access Analyzer is enabled, False otherwise
|
||||
Fixer for enabling Access Analyzer in a region.
|
||||
"""
|
||||
try:
|
||||
regional_client = accessanalyzer_client.regional_clients[region]
|
||||
regional_client.create_analyzer(
|
||||
analyzerName=accessanalyzer_client.fixer_config.get(
|
||||
"accessanalyzer_enabled", {}
|
||||
).get("AnalyzerName", "DefaultAnalyzer"),
|
||||
type=accessanalyzer_client.fixer_config.get(
|
||||
"accessanalyzer_enabled", {}
|
||||
).get("AnalyzerType", "ACCOUNT_UNUSED_ACCESS"),
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Enable Access Analyzer in a region",
|
||||
cost_impact=True,
|
||||
cost_description="Enabling Access Analyzer may incur costs for policy and resource analysis, especially in large accounts or organizations. See AWS Access Analyzer pricing.",
|
||||
service="accessanalyzer",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "access-analyzer:CreateAnalyzer",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Enable Access Analyzer in a region.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if Access Analyzer is enabled, False otherwise
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not region:
|
||||
raise ValueError("Region is required")
|
||||
|
||||
# Show the fixing message
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = accessanalyzer_client.regional_clients[region]
|
||||
regional_client.create_analyzer(
|
||||
analyzerName=accessanalyzer_client.fixer_config.get(
|
||||
"accessanalyzer_enabled", {}
|
||||
).get("AnalyzerName", "DefaultAnalyzer"),
|
||||
type=accessanalyzer_client.fixer_config.get(
|
||||
"accessanalyzer_enabled", {}
|
||||
).get("AnalyzerType", "ACCOUNT_UNUSED_ACCESS"),
|
||||
)
|
||||
return True
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -1,61 +1,83 @@
|
||||
import json
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.awslambda.awslambda_client import awslambda_client
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class LambdaFunctionNotPubliclyAccessibleFixer(AWSFixer):
|
||||
"""
|
||||
Remove the Lambda function's resource-based policy to prevent public access and add a new permission for the account.
|
||||
Specifically, this fixer deletes all permission statements associated with the Lambda function's policy and then adds a new permission.
|
||||
Requires the lambda:RemovePermission and lambda:AddPermission permissions.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "lambda:RemovePermission",
|
||||
"Resource": "*"
|
||||
Fixer for removing public access from Lambda function policies.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Remove public access from Lambda function's resource-based policy and add a new permission for the account.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="awslambda",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "lambda:RemovePermission",
|
||||
"Resource": "*",
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "lambda:AddPermission",
|
||||
"Resource": "*",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "lambda:AddPermission",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The Lambda function name or ARN.
|
||||
region (str): AWS region where the Lambda function exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (policy removed and permission added), False otherwise.
|
||||
"""
|
||||
try:
|
||||
account_id = awslambda_client.audited_account
|
||||
|
||||
regional_client = awslambda_client.regional_clients[region]
|
||||
policy_response = regional_client.get_policy(FunctionName=resource_id)
|
||||
policy = json.loads(policy_response.get("Policy"))
|
||||
|
||||
for statement in policy.get("Statement", []):
|
||||
statement_id = statement.get("Sid")
|
||||
if statement_id:
|
||||
regional_client.remove_permission(
|
||||
FunctionName=resource_id, StatementId=statement_id
|
||||
)
|
||||
|
||||
regional_client.add_permission(
|
||||
FunctionName=resource_id,
|
||||
StatementId="ProwlerFixerStatement",
|
||||
Principal=account_id,
|
||||
Action="lambda:InvokeFunction",
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Remove the Lambda function's resource-based policy to prevent public access and add a new permission for the account.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region, resource_id (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful, False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not region or not resource_id:
|
||||
raise ValueError("Region and resource_id are required")
|
||||
|
||||
super().fix(region=region, resource_id=resource_id)
|
||||
|
||||
account_id = awslambda_client.audited_account
|
||||
regional_client = awslambda_client.regional_clients[region]
|
||||
policy_response = regional_client.get_policy(FunctionName=resource_id)
|
||||
policy = json.loads(policy_response.get("Policy"))
|
||||
|
||||
for statement in policy.get("Statement", []):
|
||||
statement_id = statement.get("Sid")
|
||||
if statement_id:
|
||||
regional_client.remove_permission(
|
||||
FunctionName=resource_id, StatementId=statement_id
|
||||
)
|
||||
|
||||
regional_client.add_permission(
|
||||
FunctionName=resource_id,
|
||||
StatementId="ProwlerFixerStatement",
|
||||
Principal=account_id,
|
||||
Action="lambda:InvokeFunction",
|
||||
)
|
||||
return True
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -1,52 +1,75 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
|
||||
cloudtrail_client,
|
||||
)
|
||||
from prowler.providers.aws.services.s3.s3_client import s3_client
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class CloudtrailLogsS3BucketIsNotPubliclyAccessibleFixer(AWSFixer):
|
||||
"""
|
||||
Modify the CloudTrail's associated S3 bucket's public access settings to ensure the bucket is not publicly accessible.
|
||||
Specifically, this fixer configures the S3 bucket's public access block settings to block all public access.
|
||||
Requires the s3:PutBucketPublicAccessBlock permissions.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:PutBucketPublicAccessBlock",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The CloudTrail name.
|
||||
region (str): AWS region where the CloudTrail and S3 bucket exist.
|
||||
Returns:
|
||||
bool: True if the operation is successful (policy and ACL updated), False otherwise.
|
||||
Fixer for ensuring CloudTrail's associated S3 bucket is not publicly accessible.
|
||||
"""
|
||||
try:
|
||||
regional_client = s3_client.regional_clients[region]
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.name == resource_id:
|
||||
trail_bucket = trail.s3_bucket
|
||||
|
||||
regional_client.put_public_access_block(
|
||||
Bucket=trail_bucket,
|
||||
PublicAccessBlockConfiguration={
|
||||
"BlockPublicAcls": True,
|
||||
"IgnorePublicAcls": True,
|
||||
"BlockPublicPolicy": True,
|
||||
"RestrictPublicBuckets": True,
|
||||
},
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Modify the CloudTrail's associated S3 bucket's public access settings to ensure the bucket is not publicly accessible.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="cloudtrail",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:PutBucketPublicAccessBlock",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Modify the CloudTrail's associated S3 bucket's public access settings to ensure the bucket is not publicly accessible.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region, resource_id (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful, False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not region or not resource_id:
|
||||
raise ValueError("Region and resource_id are required")
|
||||
|
||||
super().fix(region=region, resource_id=resource_id)
|
||||
|
||||
regional_client = s3_client.regional_clients[region]
|
||||
for trail in cloudtrail_client.trails.values():
|
||||
if trail.name == resource_id:
|
||||
trail_bucket = trail.s3_bucket
|
||||
regional_client.put_public_access_block(
|
||||
Bucket=trail_bucket,
|
||||
PublicAccessBlockConfiguration={
|
||||
"BlockPublicAcls": True,
|
||||
"IgnorePublicAcls": True,
|
||||
"BlockPublicPolicy": True,
|
||||
"RestrictPublicBuckets": True,
|
||||
},
|
||||
)
|
||||
return True
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -1,59 +1,84 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
|
||||
cloudtrail_client,
|
||||
)
|
||||
|
||||
|
||||
def fixer(region):
|
||||
class CloudtrailMultiRegionEnabledFixer(AWSFixer):
|
||||
"""
|
||||
NOTE: Define the S3 bucket name in the fixer_config.yaml file.
|
||||
Enable CloudTrail in a region. Requires the cloudtrail:CreateTrail permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "cloudtrail:CreateTrail",
|
||||
"Resource": "*"
|
||||
Fixer for enabling CloudTrail as a multi-region trail in a region.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Enable CloudTrail as a multi-region trail in a region.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="cloudtrail",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "cloudtrail:CreateTrail",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Enable CloudTrail as a multi-region trail in a region.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if CloudTrail is enabled, False otherwise
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not region:
|
||||
raise ValueError("Region is required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
cloudtrail_fixer_config = cloudtrail_client.fixer_config.get(
|
||||
"cloudtrail_multi_region_enabled", {}
|
||||
)
|
||||
regional_client = cloudtrail_client.regional_clients[region]
|
||||
args = {
|
||||
"Name": cloudtrail_fixer_config.get("TrailName", "DefaultTrail"),
|
||||
"S3BucketName": cloudtrail_fixer_config.get("S3BucketName"),
|
||||
"IsMultiRegionTrail": cloudtrail_fixer_config.get(
|
||||
"IsMultiRegionTrail", True
|
||||
),
|
||||
"EnableLogFileValidation": cloudtrail_fixer_config.get(
|
||||
"EnableLogFileValidation", True
|
||||
),
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
region (str): AWS region
|
||||
Returns:
|
||||
bool: True if CloudTrail is enabled, False otherwise
|
||||
"""
|
||||
try:
|
||||
cloudtrail_fixer_config = cloudtrail_client.fixer_config.get(
|
||||
"cloudtrail_multi_region_enabled", {}
|
||||
)
|
||||
regional_client = cloudtrail_client.regional_clients[region]
|
||||
args = {
|
||||
"Name": cloudtrail_fixer_config.get("TrailName", "DefaultTrail"),
|
||||
"S3BucketName": cloudtrail_fixer_config.get("S3BucketName"),
|
||||
"IsMultiRegionTrail": cloudtrail_fixer_config.get(
|
||||
"IsMultiRegionTrail", True
|
||||
),
|
||||
"EnableLogFileValidation": cloudtrail_fixer_config.get(
|
||||
"EnableLogFileValidation", True
|
||||
),
|
||||
}
|
||||
if cloudtrail_fixer_config.get("CloudWatchLogsLogGroupArn"):
|
||||
args["CloudWatchLogsLogGroupArn"] = cloudtrail_fixer_config.get(
|
||||
"CloudWatchLogsLogGroupArn"
|
||||
if cloudtrail_fixer_config.get("CloudWatchLogsLogGroupArn"):
|
||||
args["CloudWatchLogsLogGroupArn"] = cloudtrail_fixer_config.get(
|
||||
"CloudWatchLogsLogGroupArn"
|
||||
)
|
||||
if cloudtrail_fixer_config.get("CloudWatchLogsRoleArn"):
|
||||
args["CloudWatchLogsRoleArn"] = cloudtrail_fixer_config.get(
|
||||
"CloudWatchLogsRoleArn"
|
||||
)
|
||||
if cloudtrail_fixer_config.get("KmsKeyId"):
|
||||
args["KmsKeyId"] = cloudtrail_fixer_config.get("KmsKeyId")
|
||||
regional_client.create_trail(**args)
|
||||
return True
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
if cloudtrail_fixer_config.get("CloudWatchLogsRoleArn"):
|
||||
args["CloudWatchLogsRoleArn"] = cloudtrail_fixer_config.get(
|
||||
"CloudWatchLogsRoleArn"
|
||||
)
|
||||
if cloudtrail_fixer_config.get("KmsKeyId"):
|
||||
args["KmsKeyId"] = cloudtrail_fixer_config.get("KmsKeyId")
|
||||
regional_client.create_trail(**args)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -1,71 +1,94 @@
|
||||
import json
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
|
||||
|
||||
def fixer(resource_arn: str) -> bool:
|
||||
class CloudtrailThreatDetectionEnumerationFixer(AWSFixer):
|
||||
"""
|
||||
Fixer for restricting access to a compromised AWS entity by attaching a deny-all inline policy to the user or role.
|
||||
"""
|
||||
Restricts access to a compromised AWS entity by attaching a deny-all inline policy to the user or role.
|
||||
|
||||
Requires the following permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"iam:PutUserPolicy",
|
||||
"iam:PutRolePolicy",
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Restricts access to a compromised AWS entity by attaching a deny-all inline policy to the user or role.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="cloudtrail",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"iam:PutUserPolicy",
|
||||
"iam:PutRolePolicy",
|
||||
],
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Args:
|
||||
resource_arn (str): The ARN of the compromised AWS entity (IAM User or Role).
|
||||
|
||||
Returns:
|
||||
bool: True if the fix was applied successfully, False otherwise.
|
||||
"""
|
||||
try:
|
||||
if ":user/" in resource_arn:
|
||||
entity_type = "user"
|
||||
entity_name = resource_arn.split("/")[-1]
|
||||
elif ":role/" in resource_arn:
|
||||
entity_type = "role"
|
||||
entity_name = resource_arn.split("/")[-1]
|
||||
else:
|
||||
return False
|
||||
|
||||
deny_policy = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [{"Effect": "Deny", "Action": "*", "Resource": "*"}],
|
||||
}
|
||||
|
||||
policy_name = "DenyAllAccess"
|
||||
|
||||
if entity_type == "user":
|
||||
iam_client.client.put_user_policy(
|
||||
UserName=entity_name,
|
||||
PolicyName=policy_name,
|
||||
PolicyDocument=json.dumps(deny_policy),
|
||||
)
|
||||
logger.info(f"Applied Deny policy to user {entity_name}")
|
||||
|
||||
elif entity_type == "role":
|
||||
iam_client.client.put_role_policy(
|
||||
RoleName=entity_name,
|
||||
PolicyName=policy_name,
|
||||
PolicyDocument=json.dumps(deny_policy),
|
||||
)
|
||||
logger.info(f"Applied Deny policy to role {entity_name}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
},
|
||||
)
|
||||
return False
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Restricts access to a compromised AWS entity by attaching a deny-all inline policy to the user or role.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix (should have resource_arn)
|
||||
**kwargs: resource_arn (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the fix was applied successfully, False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_arn = getattr(finding, "resource_arn", None)
|
||||
else:
|
||||
resource_arn = kwargs.get("resource_arn")
|
||||
|
||||
if not resource_arn:
|
||||
raise ValueError("resource_arn is required")
|
||||
|
||||
super().fix(resource_arn=resource_arn)
|
||||
|
||||
if ":user/" in resource_arn:
|
||||
entity_type = "user"
|
||||
entity_name = resource_arn.split("/")[-1]
|
||||
elif ":role/" in resource_arn:
|
||||
entity_type = "role"
|
||||
entity_name = resource_arn.split("/")[-1]
|
||||
else:
|
||||
return False
|
||||
|
||||
deny_policy = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [{"Effect": "Deny", "Action": "*", "Resource": "*"}],
|
||||
}
|
||||
|
||||
policy_name = "DenyAllAccess"
|
||||
|
||||
if entity_type == "user":
|
||||
iam_client.client.put_user_policy(
|
||||
UserName=entity_name,
|
||||
PolicyName=policy_name,
|
||||
PolicyDocument=json.dumps(deny_policy),
|
||||
)
|
||||
logger.info(f"Applied Deny policy to user {entity_name}")
|
||||
|
||||
elif entity_type == "role":
|
||||
iam_client.client.put_role_policy(
|
||||
RoleName=entity_name,
|
||||
PolicyName=policy_name,
|
||||
PolicyDocument=json.dumps(deny_policy),
|
||||
)
|
||||
logger.info(f"Applied Deny policy to role {entity_name}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -1,71 +1,94 @@
|
||||
import json
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
|
||||
|
||||
def fixer(resource_arn: str) -> bool:
|
||||
class CloudtrailThreatDetectionLlmJackingFixer(AWSFixer):
|
||||
"""
|
||||
Fixer for restricting access to a compromised AWS entity by attaching a deny-all inline policy to the user or role.
|
||||
"""
|
||||
Restricts access to a compromised AWS entity by attaching a deny-all inline policy to the user or role.
|
||||
|
||||
Requires the following permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"iam:PutUserPolicy",
|
||||
"iam:PutRolePolicy",
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Restricts access to a compromised AWS entity by attaching a deny-all inline policy to the user or role.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="cloudtrail",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"iam:PutUserPolicy",
|
||||
"iam:PutRolePolicy",
|
||||
],
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Args:
|
||||
resource_arn (str): The ARN of the compromised AWS entity (IAM User or Role).
|
||||
|
||||
Returns:
|
||||
bool: True if the fix was applied successfully, False otherwise.
|
||||
"""
|
||||
try:
|
||||
if ":user/" in resource_arn:
|
||||
entity_type = "user"
|
||||
entity_name = resource_arn.split("/")[-1]
|
||||
elif ":role/" in resource_arn:
|
||||
entity_type = "role"
|
||||
entity_name = resource_arn.split("/")[-1]
|
||||
else:
|
||||
return False
|
||||
|
||||
deny_policy = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [{"Effect": "Deny", "Action": "*", "Resource": "*"}],
|
||||
}
|
||||
|
||||
policy_name = "DenyAllAccess"
|
||||
|
||||
if entity_type == "user":
|
||||
iam_client.client.put_user_policy(
|
||||
UserName=entity_name,
|
||||
PolicyName=policy_name,
|
||||
PolicyDocument=json.dumps(deny_policy),
|
||||
)
|
||||
logger.info(f"Applied Deny policy to user {entity_name}")
|
||||
|
||||
elif entity_type == "role":
|
||||
iam_client.client.put_role_policy(
|
||||
RoleName=entity_name,
|
||||
PolicyName=policy_name,
|
||||
PolicyDocument=json.dumps(deny_policy),
|
||||
)
|
||||
logger.info(f"Applied Deny policy to role {entity_name}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
},
|
||||
)
|
||||
return False
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Restricts access to a compromised AWS entity by attaching a deny-all inline policy to the user or role.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix (should have resource_arn)
|
||||
**kwargs: resource_arn (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the fix was applied successfully, False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_arn = getattr(finding, "resource_arn", None)
|
||||
else:
|
||||
resource_arn = kwargs.get("resource_arn")
|
||||
|
||||
if not resource_arn:
|
||||
raise ValueError("resource_arn is required")
|
||||
|
||||
super().fix(resource_arn=resource_arn)
|
||||
|
||||
if ":user/" in resource_arn:
|
||||
entity_type = "user"
|
||||
entity_name = resource_arn.split("/")[-1]
|
||||
elif ":role/" in resource_arn:
|
||||
entity_type = "role"
|
||||
entity_name = resource_arn.split("/")[-1]
|
||||
else:
|
||||
return False
|
||||
|
||||
deny_policy = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [{"Effect": "Deny", "Action": "*", "Resource": "*"}],
|
||||
}
|
||||
|
||||
policy_name = "DenyAllAccess"
|
||||
|
||||
if entity_type == "user":
|
||||
iam_client.client.put_user_policy(
|
||||
UserName=entity_name,
|
||||
PolicyName=policy_name,
|
||||
PolicyDocument=json.dumps(deny_policy),
|
||||
)
|
||||
logger.info(f"Applied Deny policy to user {entity_name}")
|
||||
|
||||
elif entity_type == "role":
|
||||
iam_client.client.put_role_policy(
|
||||
RoleName=entity_name,
|
||||
PolicyName=policy_name,
|
||||
PolicyDocument=json.dumps(deny_policy),
|
||||
)
|
||||
logger.info(f"Applied Deny policy to role {entity_name}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -1,71 +1,94 @@
|
||||
import json
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
|
||||
|
||||
def fixer(resource_arn: str) -> bool:
|
||||
class CloudtrailThreatDetectionPrivilegeEscalationFixer(AWSFixer):
|
||||
"""
|
||||
Fixer for restricting access to a compromised AWS entity by attaching a deny-all inline policy to the user or role.
|
||||
"""
|
||||
Restricts access to a compromised AWS entity by attaching a deny-all inline policy to the user or role.
|
||||
|
||||
Requires the following permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"iam:PutUserPolicy",
|
||||
"iam:PutRolePolicy",
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Restricts access to a compromised AWS entity by attaching a deny-all inline policy to the user or role.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="cloudtrail",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"iam:PutUserPolicy",
|
||||
"iam:PutRolePolicy",
|
||||
],
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Args:
|
||||
resource_arn (str): The ARN of the compromised AWS entity (IAM User or Role).
|
||||
|
||||
Returns:
|
||||
bool: True if the fix was applied successfully, False otherwise.
|
||||
"""
|
||||
try:
|
||||
if ":user/" in resource_arn:
|
||||
entity_type = "user"
|
||||
entity_name = resource_arn.split("/")[-1]
|
||||
elif ":role/" in resource_arn:
|
||||
entity_type = "role"
|
||||
entity_name = resource_arn.split("/")[-1]
|
||||
else:
|
||||
return False
|
||||
|
||||
deny_policy = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [{"Effect": "Deny", "Action": "*", "Resource": "*"}],
|
||||
}
|
||||
|
||||
policy_name = "DenyAllAccess"
|
||||
|
||||
if entity_type == "user":
|
||||
iam_client.client.put_user_policy(
|
||||
UserName=entity_name,
|
||||
PolicyName=policy_name,
|
||||
PolicyDocument=json.dumps(deny_policy),
|
||||
)
|
||||
logger.info(f"Applied Deny policy to user {entity_name}")
|
||||
|
||||
elif entity_type == "role":
|
||||
iam_client.client.put_role_policy(
|
||||
RoleName=entity_name,
|
||||
PolicyName=policy_name,
|
||||
PolicyDocument=json.dumps(deny_policy),
|
||||
)
|
||||
logger.info(f"Applied Deny policy to role {entity_name}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
},
|
||||
)
|
||||
return False
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Restricts access to a compromised AWS entity by attaching a deny-all inline policy to the user or role.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix (should have resource_arn)
|
||||
**kwargs: resource_arn (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the fix was applied successfully, False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_arn = getattr(finding, "resource_arn", None)
|
||||
else:
|
||||
resource_arn = kwargs.get("resource_arn")
|
||||
|
||||
if not resource_arn:
|
||||
raise ValueError("resource_arn is required")
|
||||
|
||||
super().fix(resource_arn=resource_arn)
|
||||
|
||||
if ":user/" in resource_arn:
|
||||
entity_type = "user"
|
||||
entity_name = resource_arn.split("/")[-1]
|
||||
elif ":role/" in resource_arn:
|
||||
entity_type = "role"
|
||||
entity_name = resource_arn.split("/")[-1]
|
||||
else:
|
||||
return False
|
||||
|
||||
deny_policy = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [{"Effect": "Deny", "Action": "*", "Resource": "*"}],
|
||||
}
|
||||
|
||||
policy_name = "DenyAllAccess"
|
||||
|
||||
if entity_type == "user":
|
||||
iam_client.client.put_user_policy(
|
||||
UserName=entity_name,
|
||||
PolicyName=policy_name,
|
||||
PolicyDocument=json.dumps(deny_policy),
|
||||
)
|
||||
logger.info(f"Applied Deny policy to user {entity_name}")
|
||||
|
||||
elif entity_type == "role":
|
||||
iam_client.client.put_role_policy(
|
||||
RoleName=entity_name,
|
||||
PolicyName=policy_name,
|
||||
PolicyDocument=json.dumps(deny_policy),
|
||||
)
|
||||
logger.info(f"Applied Deny policy to role {entity_name}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -1,59 +1,81 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.codeartifact.codeartifact_client import (
|
||||
codeartifact_client,
|
||||
)
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class CodeartifactPackagesExternalPublicPublishingDisabledFixer(AWSFixer):
|
||||
"""
|
||||
Modify the CodeArtifact package's configuration to restrict public access.
|
||||
Specifically, this fixer changes the package's configuration to block public access by
|
||||
setting restrictions on the "publish" and "upstream" actions.
|
||||
Requires the codeartifact:PutPackageOriginConfiguration permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "codeartifact:PutPackageOriginConfiguration",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The CodeArtifact package name in the format "domain_name/package_name".
|
||||
region (str): AWS region where the CodeArtifact package exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (configuration updated), False otherwise.
|
||||
Fixer for modifying the CodeArtifact package's configuration to restrict public access.
|
||||
"""
|
||||
try:
|
||||
domain_name, package_name = resource_id.split("/")
|
||||
|
||||
regional_client = codeartifact_client.regional_clients[region]
|
||||
|
||||
for repository in codeartifact_client.repositories.values():
|
||||
if repository.domain_name == domain_name:
|
||||
for package in repository.packages:
|
||||
if package.name == package_name:
|
||||
publish_value = (
|
||||
package.origin_configuration.restrictions.publish.value
|
||||
)
|
||||
regional_client.put_package_origin_configuration(
|
||||
domain=domain_name,
|
||||
repository=repository.name,
|
||||
format=package.format,
|
||||
package=package_name,
|
||||
restrictions={
|
||||
"publish": publish_value,
|
||||
"upstream": "BLOCK",
|
||||
},
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Modify the CodeArtifact package's configuration to restrict public access.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="codeartifact",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "codeartifact:PutPackageOriginConfiguration",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Modify the CodeArtifact package's configuration to restrict public access.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region, resource_id (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (configuration updated), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not region or not resource_id:
|
||||
raise ValueError("Region and resource_id are required")
|
||||
|
||||
super().fix(region=region, resource_id=resource_id)
|
||||
|
||||
domain_name, package_name = resource_id.split("/")
|
||||
regional_client = codeartifact_client.regional_clients[region]
|
||||
|
||||
for repository in codeartifact_client.repositories.values():
|
||||
if repository.domain_name == domain_name:
|
||||
for package in repository.packages:
|
||||
if package.name == package_name:
|
||||
publish_value = (
|
||||
package.origin_configuration.restrictions.publish.value
|
||||
)
|
||||
regional_client.put_package_origin_configuration(
|
||||
domain=domain_name,
|
||||
repository=repository.name,
|
||||
format=package.format,
|
||||
package=package_name,
|
||||
restrictions={
|
||||
"publish": publish_value,
|
||||
"upstream": "BLOCK",
|
||||
},
|
||||
)
|
||||
return True
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -1,42 +1,67 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.documentdb.documentdb_client import (
|
||||
documentdb_client,
|
||||
)
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class DocumentdbClusterPublicSnapshotFixer(AWSFixer):
|
||||
"""
|
||||
Modify the attributes of a DocumentDB cluster snapshot to remove public access.
|
||||
Specifically, this fixer removes the 'all' value from the 'restore' attribute to
|
||||
prevent the snapshot from being publicly accessible. Requires the rds:ModifyDBClusterSnapshotAttribute permissions.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "rds:ModifyDBClusterSnapshotAttribute",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The DB cluster snapshot identifier.
|
||||
region (str): AWS region where the snapshot exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (public access is removed), False otherwise.
|
||||
Fixer for modifying the attributes of a DocumentDB cluster snapshot to remove public access.
|
||||
"""
|
||||
try:
|
||||
regional_client = documentdb_client.regional_clients[region]
|
||||
regional_client.modify_db_cluster_snapshot_attribute(
|
||||
DBClusterSnapshotIdentifier=resource_id,
|
||||
AttributeName="restore",
|
||||
ValuesToRemove=["all"],
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Modify the attributes of a DocumentDB cluster snapshot to remove public access.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="documentdb",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "rds:ModifyDBClusterSnapshotAttribute",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Modify the attributes of a DocumentDB cluster snapshot to remove public access.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region, resource_id (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (public access is removed), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not region or not resource_id:
|
||||
raise ValueError("Region and resource_id are required")
|
||||
|
||||
super().fix(region=region, resource_id=resource_id)
|
||||
|
||||
regional_client = documentdb_client.regional_clients[region]
|
||||
regional_client.modify_db_cluster_snapshot_attribute(
|
||||
DBClusterSnapshotIdentifier=resource_id,
|
||||
AttributeName="restore",
|
||||
ValuesToRemove=["all"],
|
||||
)
|
||||
return True
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -1,40 +1,64 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class Ec2AmiPublicFixer(AWSFixer):
|
||||
"""
|
||||
Modify the attributes of an EC2 AMI to remove public access.
|
||||
Specifically, this fixer removes the 'all' value from the 'LaunchPermission' attribute
|
||||
to prevent the AMI from being publicly accessible.
|
||||
Requires the ec2:ModifyImageAttribute permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:ModifyImageAttribute",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The ID of the EC2 AMI to make private.
|
||||
region (str): AWS region where the AMI exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (the AMI is no longer publicly accessible), False otherwise.
|
||||
Fixer for modifying the attributes of an EC2 AMI to remove public access.
|
||||
"""
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
regional_client.modify_image_attribute(
|
||||
ImageId=resource_id,
|
||||
LaunchPermission={"Remove": [{"Group": "all"}]},
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Modify the attributes of an EC2 AMI to remove public access.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:ModifyImageAttribute",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Modify the attributes of an EC2 AMI to remove public access.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region, resource_id (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (the AMI is no longer publicly accessible), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not region or not resource_id:
|
||||
raise ValueError("Region and resource_id are required")
|
||||
|
||||
super().fix(region=region, resource_id=resource_id)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
regional_client.modify_image_attribute(
|
||||
ImageId=resource_id,
|
||||
LaunchPermission={"Remove": [{"Group": "all"}]},
|
||||
)
|
||||
return True
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -1,34 +1,60 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
|
||||
|
||||
def fixer(region):
|
||||
class Ec2EbsDefaultEncryptionFixer(AWSFixer):
|
||||
"""
|
||||
Enable EBS encryption by default in a region. NOTE: Custom KMS keys for EBS Default Encryption may be overwritten.
|
||||
Requires the ec2:EnableEbsEncryptionByDefault permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:EnableEbsEncryptionByDefault",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
region (str): AWS region
|
||||
Returns:
|
||||
bool: True if EBS encryption by default is enabled, False otherwise
|
||||
Fixer to enable EBS encryption by default in a region.
|
||||
"""
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
return regional_client.enable_ebs_encryption_by_default()[
|
||||
"EbsEncryptionByDefault"
|
||||
]
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Enable EBS encryption by default in a region. NOTE: Custom KMS keys for EBS Default Encryption may be overwritten.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:EnableEbsEncryptionByDefault",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Enable EBS encryption by default in a region.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if EBS encryption by default is enabled, False otherwise
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not region:
|
||||
raise ValueError("Region is required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
return regional_client.enable_ebs_encryption_by_default()[
|
||||
"EbsEncryptionByDefault"
|
||||
]
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -1,41 +1,66 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class Ec2EbsPublicSnapshotFixer(AWSFixer):
|
||||
"""
|
||||
Modify the attributes of an EBS snapshot to remove public access.
|
||||
Specifically, this fixer removes the 'all' value from the 'createVolumePermission' attribute to
|
||||
prevent the snapshot from being publicly accessible. Requires the ec2:ModifySnapshotAttribute permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:ModifySnapshotAttribute",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The snapshot identifier.
|
||||
region (str): AWS region where the snapshot exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (public access is removed), False otherwise.
|
||||
Fixer for modifying the attributes of an EBS snapshot to remove public access.
|
||||
"""
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
regional_client.modify_snapshot_attribute(
|
||||
SnapshotId=resource_id,
|
||||
Attribute="createVolumePermission",
|
||||
OperationType="remove",
|
||||
GroupNames=["all"],
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Modify the attributes of an EBS snapshot to remove public access.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:ModifySnapshotAttribute",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Modify the attributes of an EBS snapshot to remove public access.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region, resource_id (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (public access is removed), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not region or not resource_id:
|
||||
raise ValueError("Region and resource_id are required")
|
||||
|
||||
super().fix(region=region, resource_id=resource_id)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
regional_client.modify_snapshot_attribute(
|
||||
SnapshotId=resource_id,
|
||||
Attribute="createVolumePermission",
|
||||
OperationType="remove",
|
||||
GroupNames=["all"],
|
||||
)
|
||||
return True
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -1,38 +1,66 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
|
||||
|
||||
def fixer(region):
|
||||
class Ec2EbsSnapshotAccountBlockPublicAccessFixer(AWSFixer):
|
||||
"""
|
||||
Enable EBS snapshot block public access in a region.
|
||||
Requires the ec2:EnableSnapshotBlockPublicAccess permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:EnableSnapshotBlockPublicAccess",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
region (str): AWS region
|
||||
Returns:
|
||||
bool: True if EBS snapshot block public access is enabled, False otherwise
|
||||
Fixer to enable EBS snapshot block public access in a region.
|
||||
"""
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
state = ec2_client.fixer_config.get(
|
||||
"ec2_ebs_snapshot_account_block_public_access", {}
|
||||
).get("State", "block-all-sharing")
|
||||
return (
|
||||
regional_client.enable_snapshot_block_public_access(State=state)["State"]
|
||||
== state
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Enable EBS snapshot block public access in a region.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:EnableSnapshotBlockPublicAccess",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Enable EBS snapshot block public access in a region.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if EBS snapshot block public access is enabled, False otherwise
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not region:
|
||||
raise ValueError("Region is required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
state = ec2_client.fixer_config.get(
|
||||
"ec2_ebs_snapshot_account_block_public_access", {}
|
||||
).get("State", "block-all-sharing")
|
||||
return (
|
||||
regional_client.enable_snapshot_block_public_access(State=state)[
|
||||
"State"
|
||||
]
|
||||
== state
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -1,35 +1,60 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
|
||||
|
||||
def fixer(region):
|
||||
class Ec2InstanceAccountImdsv2EnabledFixer(AWSFixer):
|
||||
"""
|
||||
Enable IMDSv2 for EC2 instances in the specified region.
|
||||
Requires the ec2:ModifyInstanceMetadataDefaults permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:ModifyInstanceMetadataDefaults",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
region (str): AWS region
|
||||
Returns:
|
||||
bool: True if IMDSv2 is enabled, False otherwise
|
||||
Fixer to enable IMDSv2 for EC2 instances in a region.
|
||||
"""
|
||||
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
return regional_client.modify_instance_metadata_defaults(HttpTokens="required")[
|
||||
"Return"
|
||||
]
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Enable IMDSv2 for EC2 instances in a region.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:ModifyInstanceMetadataDefaults",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Enable IMDSv2 for EC2 instances in a region.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if IMDSv2 is enabled, False otherwise
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not region:
|
||||
raise ValueError("Region is required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
return regional_client.modify_instance_metadata_defaults(
|
||||
HttpTokens="required"
|
||||
)["Return"]
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -1,51 +1,75 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class Ec2InstancePortCassandraExposedToInternetFixer(AWSFixer):
|
||||
"""
|
||||
Revokes any ingress rule allowing Cassandra ports (7000, 7001, 7199, 9042, 9160) from any address (0.0.0.0/0)
|
||||
for the EC2 instance's security groups.
|
||||
This fixer will only be triggered if the check identifies Cassandra ports open to the Internet.
|
||||
Requires the ec2:RevokeSecurityGroupIngress permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The EC2 instance ID.
|
||||
region (str): The AWS region where the EC2 instance exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
Fixer to revoke ingress rules allowing Cassandra ports from any address for EC2 instances' security groups.
|
||||
"""
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [7000, 7001, 7199, 9042, 9160]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Revoke ingress rules allowing Cassandra ports from any address for EC2 instances' security groups.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Revoke ingress rules allowing Cassandra ports from any address for EC2 instances' security groups.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id, region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
region = finding.region
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not resource_id or not region:
|
||||
raise ValueError("resource_id and region are required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [7000, 7001, 7199, 9042, 9160]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,51 +1,75 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class Ec2InstancePortCifsExposedToInternetFixer(AWSFixer):
|
||||
"""
|
||||
Revokes any ingress rule allowing CIFS ports (139, 445) from any address (0.0.0.0/0)
|
||||
for the EC2 instance's security groups.
|
||||
This fixer will only be triggered if the check identifies CIFS ports open to the Internet.
|
||||
Requires the ec2:RevokeSecurityGroupIngress permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The EC2 instance ID.
|
||||
region (str): The AWS region where the EC2 instance exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
Fixer to revoke ingress rules allowing CIFS ports from any address for EC2 instances' security groups.
|
||||
"""
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [139, 445]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Revoke ingress rules allowing CIFS ports from any address for EC2 instances' security groups.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Revoke ingress rules allowing CIFS ports from any address for EC2 instances' security groups.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id, region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
region = finding.region
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not resource_id or not region:
|
||||
raise ValueError("resource_id and region are required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [139, 445]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,51 +1,75 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class Ec2InstancePortElasticsearchKibanaExposedToInternetFixer(AWSFixer):
|
||||
"""
|
||||
Revokes any ingress rule allowing Elasticsearch and Kibana ports (9200, 9300, 5601)
|
||||
from any address (0.0.0.0/0) for the EC2 instance's security groups.
|
||||
This fixer will only be triggered if the check identifies those ports open to the Internet.
|
||||
Requires the ec2:RevokeSecurityGroupIngress permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The EC2 instance ID.
|
||||
region (str): The AWS region where the EC2 instance exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
Fixer to revoke ingress rules allowing Elasticsearch and Kibana ports from any address for EC2 instances' security groups.
|
||||
"""
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [9200, 9300, 5601]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Revoke ingress rules allowing Elasticsearch and Kibana ports from any address for EC2 instances' security groups.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Revoke ingress rules allowing Elasticsearch and Kibana ports from any address for EC2 instances' security groups.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id, region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
region = finding.region
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not resource_id or not region:
|
||||
raise ValueError("resource_id and region are required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [9200, 9300, 5601]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,51 +1,75 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class Ec2InstancePortFtpExposedToInternetFixer(AWSFixer):
|
||||
"""
|
||||
Revokes any ingress rule allowing FTP ports (20, 21) from any address (0.0.0.0/0)
|
||||
for the EC2 instance's security groups.
|
||||
This fixer will only be triggered if the check identifies FTP ports open to the Internet.
|
||||
Requires the ec2:RevokeSecurityGroupIngress permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The EC2 instance ID.
|
||||
region (str): The AWS region where the EC2 instance exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
Fixer to revoke ingress rules allowing FTP ports from any address for EC2 instances' security groups.
|
||||
"""
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [20, 21]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Revoke ingress rules allowing FTP ports from any address for EC2 instances' security groups.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Revoke ingress rules allowing FTP ports from any address for EC2 instances' security groups.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id, region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
region = finding.region
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not resource_id or not region:
|
||||
raise ValueError("resource_id and region are required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [20, 21]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,51 +1,75 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class Ec2InstancePortKafkaExposedToInternetFixer(AWSFixer):
|
||||
"""
|
||||
Revokes any ingress rule allowing Kafka ports (9092) from any address (0.0.0.0/0)
|
||||
for the EC2 instance's security groups.
|
||||
This fixer will only be triggered if the check identifies Kafka ports open to the Internet.
|
||||
Requires the ec2:RevokeSecurityGroupIngress permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The EC2 instance ID.
|
||||
region (str): The AWS region where the EC2 instance exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
Fixer to revoke ingress rules allowing Kafka ports from any address for EC2 instances' security groups.
|
||||
"""
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [9092]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Revoke ingress rules allowing Kafka ports from any address for EC2 instances' security groups.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Revoke ingress rules allowing Kafka ports from any address for EC2 instances' security groups.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id, region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
region = finding.region
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not resource_id or not region:
|
||||
raise ValueError("resource_id and region are required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [9092]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,51 +1,75 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class Ec2InstancePortKerberosExposedToInternetFixer(AWSFixer):
|
||||
"""
|
||||
Revokes any ingress rule allowing Kerberos ports (88, 464, 749, 750) from any address (0.0.0.0/0)
|
||||
for the EC2 instance's security groups.
|
||||
This fixer will only be triggered if the check identifies Kerberos ports open to the Internet.
|
||||
Requires the ec2:RevokeSecurityGroupIngress permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The EC2 instance ID.
|
||||
region (str): The AWS region where the EC2 instance exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
Fixer to revoke ingress rules allowing Kerberos ports from any address for EC2 instances' security groups.
|
||||
"""
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [88, 464, 749, 750]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Revoke ingress rules allowing Kerberos ports from any address for EC2 instances' security groups.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Revoke ingress rules allowing Kerberos ports from any address for EC2 instances' security groups.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id, region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
region = finding.region
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not resource_id or not region:
|
||||
raise ValueError("resource_id and region are required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [88, 464, 749, 750]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,51 +1,75 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class Ec2InstancePortLdapExposedToInternetFixer(AWSFixer):
|
||||
"""
|
||||
Revokes any ingress rule allowing LDAP ports (389, 636) from any address (0.0.0.0/0)
|
||||
for the EC2 instance's security groups.
|
||||
This fixer will only be triggered if the check identifies LDAP ports open to the Internet.
|
||||
Requires the ec2:RevokeSecurityGroupIngress permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The EC2 instance ID.
|
||||
region (str): The AWS region where the EC2 instance exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
Fixer to revoke ingress rules allowing LDAP ports from any address for EC2 instances' security groups.
|
||||
"""
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [389, 636]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Revoke ingress rules allowing LDAP ports from any address for EC2 instances' security groups.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Revoke ingress rules allowing LDAP ports from any address for EC2 instances' security groups.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id, region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
region = finding.region
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not resource_id or not region:
|
||||
raise ValueError("resource_id and region are required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [389, 636]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,51 +1,75 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class Ec2InstancePortMemcachedExposedToInternetFixer(AWSFixer):
|
||||
"""
|
||||
Revokes any ingress rule allowing Memcached ports (11211) from any address (0.0.0.0/0)
|
||||
for the EC2 instance's security groups.
|
||||
This fixer will only be triggered if the check identifies Memcached ports open to the Internet.
|
||||
Requires the ec2:RevokeSecurityGroupIngress permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The EC2 instance ID.
|
||||
region (str): The AWS region where the EC2 instance exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
Fixer to revoke ingress rules allowing Memcached ports from any address for EC2 instances' security groups.
|
||||
"""
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [11211]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Revoke ingress rules allowing Memcached ports from any address for EC2 instances' security groups.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Revoke ingress rules allowing Memcached ports from any address for EC2 instances' security groups.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id, region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
region = finding.region
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not resource_id or not region:
|
||||
raise ValueError("resource_id and region are required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [11211]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,51 +1,75 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class Ec2InstancePortMongodbExposedToInternetFixer(AWSFixer):
|
||||
"""
|
||||
Revokes any ingress rule allowing MongoDB ports (27017, 27018) from any address (0.0.0.0/0)
|
||||
for the EC2 instance's security groups.
|
||||
This fixer will only be triggered if the check identifies MongoDB ports open to the Internet.
|
||||
Requires the ec2:RevokeSecurityGroupIngress permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The EC2 instance ID.
|
||||
region (str): The AWS region where the EC2 instance exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
Fixer to revoke ingress rules allowing MongoDB ports from any address for EC2 instances' security groups.
|
||||
"""
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [27017, 27018]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Revoke ingress rules allowing MongoDB ports from any address for EC2 instances' security groups.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Revoke ingress rules allowing MongoDB ports from any address for EC2 instances' security groups.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id, region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
region = finding.region
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not resource_id or not region:
|
||||
raise ValueError("resource_id and region are required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [27017, 27018]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,51 +1,75 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class Ec2InstancePortMysqlExposedToInternetFixer(AWSFixer):
|
||||
"""
|
||||
Revokes any ingress rule allowing MySQL ports (3306) from any address (0.0.0.0/0)
|
||||
for the EC2 instance's security groups.
|
||||
This fixer will only be triggered if the check identifies MySQL ports open to the Internet.
|
||||
Requires the ec2:RevokeSecurityGroupIngress permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The EC2 instance ID.
|
||||
region (str): The AWS region where the EC2 instance exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
Fixer to revoke ingress rules allowing MySQL ports from any address for EC2 instances' security groups.
|
||||
"""
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [3306]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Revoke ingress rules allowing MySQL ports from any address for EC2 instances' security groups.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Revoke ingress rules allowing MySQL ports from any address for EC2 instances' security groups.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id, region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
region = finding.region
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not resource_id or not region:
|
||||
raise ValueError("resource_id and region are required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [3306]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,51 +1,75 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class Ec2InstancePortOracleExposedToInternetFixer(AWSFixer):
|
||||
"""
|
||||
Revokes any ingress rule allowing Oracle ports (1521, 2483, 2484) from any address (0.0.0.0/0)
|
||||
for the EC2 instance's security groups.
|
||||
This fixer will only be triggered if the check identifies Oracle ports open to the Internet.
|
||||
Requires the ec2:RevokeSecurityGroupIngress permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The EC2 instance ID.
|
||||
region (str): The AWS region where the EC2 instance exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
Fixer to revoke ingress rules allowing Oracle ports from any address for EC2 instances' security groups.
|
||||
"""
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [1521, 2483, 2484]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Revoke ingress rules allowing Oracle ports from any address for EC2 instances' security groups.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Revoke ingress rules allowing Oracle ports from any address for EC2 instances' security groups.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id, region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
region = finding.region
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not resource_id or not region:
|
||||
raise ValueError("resource_id and region are required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [1521, 2483, 2484]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,51 +1,75 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class Ec2InstancePortPostgresqlExposedToInternetFixer(AWSFixer):
|
||||
"""
|
||||
Revokes any ingress rule allowing PostgreSQL ports (5432) from any address (0.0.0.0/0)
|
||||
for the EC2 instance's security groups.
|
||||
This fixer will only be triggered if the check identifies PostgreSQL ports open to the Internet.
|
||||
Requires the ec2:RevokeSecurityGroupIngress permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The EC2 instance ID.
|
||||
region (str): The AWS region where the EC2 instance exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
Fixer to revoke ingress rules allowing PostgreSQL ports from any address for EC2 instances' security groups.
|
||||
"""
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [5432]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Revoke ingress rules allowing PostgreSQL ports from any address for EC2 instances' security groups.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Revoke ingress rules allowing PostgreSQL ports from any address for EC2 instances' security groups.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id, region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
region = finding.region
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not resource_id or not region:
|
||||
raise ValueError("resource_id and region are required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [5432]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,51 +1,75 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class Ec2InstancePortRdpExposedToInternetFixer(AWSFixer):
|
||||
"""
|
||||
Revokes any ingress rule allowing RDP ports (3389) from any address (0.0.0.0/0)
|
||||
for the EC2 instance's security groups.
|
||||
This fixer will only be triggered if the check identifies RDP ports open to the Internet.
|
||||
Requires the ec2:RevokeSecurityGroupIngress permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The EC2 instance ID.
|
||||
region (str): The AWS region where the EC2 instance exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
Fixer to revoke ingress rules allowing RDP ports from any address for EC2 instances' security groups.
|
||||
"""
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [3389]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Revoke ingress rules allowing RDP ports from any address for EC2 instances' security groups.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Revoke ingress rules allowing RDP ports from any address for EC2 instances' security groups.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id, region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
region = finding.region
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not resource_id or not region:
|
||||
raise ValueError("resource_id and region are required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [3389]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,51 +1,75 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class Ec2InstancePortRedisExposedToInternetFixer(AWSFixer):
|
||||
"""
|
||||
Revokes any ingress rule allowing Redis ports (6379) from any address (0.0.0.0/0)
|
||||
for the EC2 instance's security groups.
|
||||
This fixer will only be triggered if the check identifies Redis ports open to the Internet.
|
||||
Requires the ec2:RevokeSecurityGroupIngress permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The EC2 instance ID.
|
||||
region (str): The AWS region where the EC2 instance exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
Fixer to revoke ingress rules allowing Redis ports from any address for EC2 instances' security groups.
|
||||
"""
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [6379]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Revoke ingress rules allowing Redis ports from any address for EC2 instances' security groups.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Revoke ingress rules allowing Redis ports from any address for EC2 instances' security groups.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id, region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
region = finding.region
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not resource_id or not region:
|
||||
raise ValueError("resource_id and region are required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [6379]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,51 +1,75 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class Ec2InstancePortSqlserverExposedToInternetFixer(AWSFixer):
|
||||
"""
|
||||
Revokes any ingress rule allowing SQLServer ports (1433, 1434) from any address (0.0.0.0/0)
|
||||
for the EC2 instance's security groups.
|
||||
This fixer will only be triggered if the check identifies SQLServer ports open to the Internet.
|
||||
Requires the ec2:RevokeSecurityGroupIngress permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The EC2 instance ID.
|
||||
region (str): The AWS region where the EC2 instance exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
Fixer to revoke ingress rules allowing SQLServer ports from any address for EC2 instances' security groups.
|
||||
"""
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [1433, 1434]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Revoke ingress rules allowing SQLServer ports from any address for EC2 instances' security groups.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Revoke ingress rules allowing SQLServer ports from any address for EC2 instances' security groups.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id, region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
region = finding.region
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not resource_id or not region:
|
||||
raise ValueError("resource_id and region are required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [1433, 1434]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,51 +1,75 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class Ec2InstancePortSshExposedToInternetFixer(AWSFixer):
|
||||
"""
|
||||
Revokes any ingress rule allowing SSH ports (22) from any address (0.0.0.0/0)
|
||||
for the EC2 instance's security groups.
|
||||
This fixer will only be triggered if the check identifies SSH ports open to the Internet.
|
||||
Requires the ec2:RevokeSecurityGroupIngress permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The EC2 instance ID.
|
||||
region (str): The AWS region where the EC2 instance exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
Fixer to revoke ingress rules allowing SSH ports from any address for EC2 instances' security groups.
|
||||
"""
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [22]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Revoke ingress rules allowing SSH ports from any address for EC2 instances' security groups.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Revoke ingress rules allowing SSH ports from any address for EC2 instances' security groups.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id, region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
region = finding.region
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not resource_id or not region:
|
||||
raise ValueError("resource_id and region are required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [22]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,51 +1,75 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class Ec2InstancePortTelnetExposedToInternetFixer(AWSFixer):
|
||||
"""
|
||||
Revokes any ingress rule allowing Telnet ports (23) from any address (0.0.0.0/0)
|
||||
for the EC2 instance's security groups.
|
||||
This fixer will only be triggered if the check identifies Telnet ports open to the Internet.
|
||||
Requires the ec2:RevokeSecurityGroupIngress permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The EC2 instance ID.
|
||||
region (str): The AWS region where the EC2 instance exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
Fixer to revoke ingress rules allowing Telnet ports from any address for EC2 instances' security groups.
|
||||
"""
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [23]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Revoke ingress rules allowing Telnet ports from any address for EC2 instances' security groups.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Revoke ingress rules allowing Telnet ports from any address for EC2 instances' security groups.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id, region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
region = finding.region
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not resource_id or not region:
|
||||
raise ValueError("resource_id and region are required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = [23]
|
||||
for instance in ec2_client.instances:
|
||||
if instance.id == resource_id:
|
||||
for sg in ec2_client.security_groups.values():
|
||||
if sg.id in instance.security_groups:
|
||||
for ingress_rule in sg.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=sg.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,52 +1,76 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
|
||||
from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class Ec2SecuritygroupAllowIngressFromInternetToHighRiskTcpPortsFixer(AWSFixer):
|
||||
"""
|
||||
Revokes any ingress rule allowing high risk ports (25, 110, 135, 143, 445, 3000, 4333, 5000, 5500, 8080, 8088)
|
||||
from any address (0.0.0.0/0) for the security groups.
|
||||
This fixer will only be triggered if the check identifies high risk ports open to the Internet.
|
||||
Requires the ec2:RevokeSecurityGroupIngress permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The Security Group ID.
|
||||
region (str): The AWS region where the Security Group exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
Fixer to revoke ingress rules allowing high risk ports from any address for security groups.
|
||||
"""
|
||||
try:
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = ec2_client.audit_config.get(
|
||||
"ec2_high_risk_ports",
|
||||
[25, 110, 135, 143, 445, 3000, 4333, 5000, 5500, 8080, 8088],
|
||||
)
|
||||
for security_group in ec2_client.security_groups.values():
|
||||
if security_group.id == resource_id:
|
||||
for ingress_rule in security_group.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=security_group.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Revoke ingress rules allowing high risk ports from any address for security groups.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ec2",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ec2:RevokeSecurityGroupIngress",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Revoke ingress rules allowing high risk ports from any address for security groups.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id, region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (ingress rule revoked), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
region = finding.region
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not resource_id or not region:
|
||||
raise ValueError("resource_id and region are required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = ec2_client.regional_clients[region]
|
||||
check_ports = ec2_client.audit_config.get(
|
||||
"ec2_high_risk_ports",
|
||||
[25, 110, 135, 143, 445, 3000, 4333, 5000, 5500, 8080, 8088],
|
||||
)
|
||||
for security_group in ec2_client.security_groups.values():
|
||||
if security_group.id == resource_id:
|
||||
for ingress_rule in security_group.ingress_rules:
|
||||
if check_security_group(
|
||||
ingress_rule, "tcp", check_ports, any_address=True
|
||||
):
|
||||
regional_client.revoke_security_group_ingress(
|
||||
GroupId=security_group.id,
|
||||
IpPermissions=[ingress_rule],
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,38 +1,62 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.ecr.ecr_client import ecr_client
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class EcrRepositoriesNotPubliclyAccessibleFixer(AWSFixer):
|
||||
"""
|
||||
Modify the ECR repository's policy to remove public access.
|
||||
Specifically, this fixer delete the policy that had public access.
|
||||
Requires the ecr:DeleteRepositoryPolicy permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ecr:DeleteRepositoryPolicy",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The ECR repository name.
|
||||
region (str): AWS region where the ECR repository exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (policy updated), False otherwise.
|
||||
Fixer to remove public access from ECR repositories by deleting their policy.
|
||||
"""
|
||||
try:
|
||||
regional_client = ecr_client.regional_clients[region]
|
||||
|
||||
regional_client.delete_repository_policy(repositoryName=resource_id)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Remove public access from ECR repositories by deleting their policy.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="ecr",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "ecr:DeleteRepositoryPolicy",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Remove public access from ECR repositories by deleting their policy.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id, region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (policy updated), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
region = finding.region
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not resource_id or not region:
|
||||
raise ValueError("resource_id and region are required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = ecr_client.regional_clients[region]
|
||||
regional_client.delete_repository_policy(repositoryName=resource_id)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,38 +1,62 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.glacier.glacier_client import glacier_client
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class GlacierVaultsPolicyPublicAccessFixer(AWSFixer):
|
||||
"""
|
||||
Modify the Glacier vault's policy to remove public access.
|
||||
Specifically, this fixer delete the vault policy that has public access.
|
||||
Requires the glacier:DeleteVaultAccessPolicy permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "glacier:DeleteVaultAccessPolicy",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The Glacier vault name.
|
||||
region (str): AWS region where the Glacier vault exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (policy updated), False otherwise.
|
||||
Fixer to remove public access from Glacier vaults by deleting their access policy.
|
||||
"""
|
||||
try:
|
||||
regional_client = glacier_client.regional_clients[region]
|
||||
|
||||
regional_client.delete_vault_access_policy(vaultName=resource_id)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Remove public access from Glacier vaults by deleting their access policy.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="glacier",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "glacier:DeleteVaultAccessPolicy",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Remove public access from Glacier vaults by deleting their access policy.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id, region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the operation is successful (policy updated), False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
region = finding.region
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not resource_id or not region:
|
||||
raise ValueError("resource_id and region are required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = glacier_client.regional_clients[region]
|
||||
regional_client.delete_vault_access_policy(vaultName=resource_id)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,33 +1,60 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.guardduty.guardduty_client import guardduty_client
|
||||
|
||||
|
||||
def fixer(region):
|
||||
class GuarddutyIsEnabledFixer(AWSFixer):
|
||||
"""
|
||||
Enable GuardDuty in a region. Requires the guardduty:CreateDetector permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "guardduty:CreateDetector",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
region (str): AWS region
|
||||
Returns:
|
||||
bool: True if GuardDuty is enabled, False otherwise
|
||||
Fixer to enable GuardDuty in a region.
|
||||
"""
|
||||
try:
|
||||
regional_client = guardduty_client.regional_clients[region]
|
||||
regional_client.create_detector(Enable=True)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Enable GuardDuty in a region.",
|
||||
cost_impact=True,
|
||||
cost_description="Enabling GuardDuty incurs costs based on the volume of logs and events analyzed. Charges apply per GB of data processed and for threat detection features. See AWS GuardDuty pricing.",
|
||||
service="guardduty",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "guardduty:CreateDetector",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Enable GuardDuty in a region.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if GuardDuty is enabled, False otherwise
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not region:
|
||||
raise ValueError("region is required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = guardduty_client.regional_clients[region]
|
||||
regional_client.create_detector(Enable=True)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,45 +1,71 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
|
||||
|
||||
def fixer(resource_id: str) -> bool:
|
||||
class IamPasswordPolicyExpiresPasswordsWithin90DaysOrLessFixer(AWSFixer):
|
||||
"""
|
||||
Enable IAM password policy to expire passwords within 90 days or less or the configurable value in prowler/config/fixer_config.yaml.
|
||||
Requires the iam:UpdateAccountPasswordPolicy permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "iam:UpdateAccountPasswordPolicy",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): AWS account ID
|
||||
Returns:
|
||||
bool: True if IAM password policy is updated, False otherwise
|
||||
Fixer to enable IAM password policy to expire passwords within 90 days or less (or configurable value).
|
||||
"""
|
||||
try:
|
||||
iam_client.client.update_account_password_policy(
|
||||
MinimumPasswordLength=iam_client.password_policy.length,
|
||||
RequireSymbols=iam_client.password_policy.symbols,
|
||||
RequireNumbers=iam_client.password_policy.numbers,
|
||||
RequireUppercaseCharacters=iam_client.password_policy.uppercase,
|
||||
RequireLowercaseCharacters=iam_client.password_policy.lowercase,
|
||||
AllowUsersToChangePassword=iam_client.password_policy.allow_change,
|
||||
MaxPasswordAge=iam_client.fixer_config.get("iam_password_policy", {}).get(
|
||||
"MaxPasswordAge", 90
|
||||
),
|
||||
PasswordReusePrevention=iam_client.password_policy.reuse_prevention,
|
||||
HardExpiry=iam_client.password_policy.hard_expiry,
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Enable IAM password policy to expire passwords within 90 days or less (or configurable value).",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="iam",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "iam:UpdateAccountPasswordPolicy",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Enable IAM password policy to expire passwords within 90 days or less (or configurable value).
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id (account id, if finding is not provided)
|
||||
Returns:
|
||||
bool: True if IAM password policy is updated, False otherwise
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not resource_id:
|
||||
raise ValueError("resource_id (account id) is required")
|
||||
|
||||
super().fix()
|
||||
|
||||
iam_client.client.update_account_password_policy(
|
||||
MinimumPasswordLength=iam_client.password_policy.length,
|
||||
RequireSymbols=iam_client.password_policy.symbols,
|
||||
RequireNumbers=iam_client.password_policy.numbers,
|
||||
RequireUppercaseCharacters=iam_client.password_policy.uppercase,
|
||||
RequireLowercaseCharacters=iam_client.password_policy.lowercase,
|
||||
AllowUsersToChangePassword=iam_client.password_policy.allow_change,
|
||||
MaxPasswordAge=iam_client.fixer_config.get(
|
||||
"iam_password_policy", {}
|
||||
).get("MaxPasswordAge", 90),
|
||||
PasswordReusePrevention=iam_client.password_policy.reuse_prevention,
|
||||
HardExpiry=iam_client.password_policy.hard_expiry,
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,45 +1,71 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
|
||||
|
||||
def fixer(resource_id: str) -> bool:
|
||||
class IamPasswordPolicyLowercaseFixer(AWSFixer):
|
||||
"""
|
||||
Enable IAM password policy to require lowercase characters or the configurable value in prowler/config/fixer_config.yaml.
|
||||
Requires the iam:UpdateAccountPasswordPolicy permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "iam:UpdateAccountPasswordPolicy",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): AWS account ID
|
||||
Returns:
|
||||
bool: True if IAM password policy is updated, False otherwise
|
||||
Fixer to enable IAM password policy to require lowercase characters (or configurable value).
|
||||
"""
|
||||
try:
|
||||
iam_client.client.update_account_password_policy(
|
||||
MinimumPasswordLength=iam_client.password_policy.length,
|
||||
RequireSymbols=iam_client.password_policy.symbols,
|
||||
RequireNumbers=iam_client.password_policy.numbers,
|
||||
RequireUppercaseCharacters=iam_client.password_policy.uppercase,
|
||||
RequireLowercaseCharacters=iam_client.fixer_config.get(
|
||||
"iam_password_policy", {}
|
||||
).get("RequireLowercaseCharacters", True),
|
||||
AllowUsersToChangePassword=iam_client.password_policy.allow_change,
|
||||
MaxPasswordAge=iam_client.password_policy.max_age,
|
||||
PasswordReusePrevention=iam_client.password_policy.reuse_prevention,
|
||||
HardExpiry=iam_client.password_policy.hard_expiry,
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Enable IAM password policy to require lowercase characters (or configurable value).",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="iam",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "iam:UpdateAccountPasswordPolicy",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Enable IAM password policy to require lowercase characters (or configurable value).
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id (account id, if finding is not provided)
|
||||
Returns:
|
||||
bool: True if IAM password policy is updated, False otherwise
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not resource_id:
|
||||
raise ValueError("resource_id (account id) is required")
|
||||
|
||||
super().fix()
|
||||
|
||||
iam_client.client.update_account_password_policy(
|
||||
MinimumPasswordLength=iam_client.password_policy.length,
|
||||
RequireSymbols=iam_client.password_policy.symbols,
|
||||
RequireNumbers=iam_client.password_policy.numbers,
|
||||
RequireUppercaseCharacters=iam_client.password_policy.uppercase,
|
||||
RequireLowercaseCharacters=iam_client.fixer_config.get(
|
||||
"iam_password_policy", {}
|
||||
).get("RequireLowercaseCharacters", True),
|
||||
AllowUsersToChangePassword=iam_client.password_policy.allow_change,
|
||||
MaxPasswordAge=iam_client.password_policy.max_age,
|
||||
PasswordReusePrevention=iam_client.password_policy.reuse_prevention,
|
||||
HardExpiry=iam_client.password_policy.hard_expiry,
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,45 +1,71 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
|
||||
|
||||
def fixer(resource_id: str) -> bool:
|
||||
class IamPasswordPolicyMinimumLength14Fixer(AWSFixer):
|
||||
"""
|
||||
Enable IAM password policy to require a minimum password length of 14 characters or the configurable value in prowler/config/fixer_config.yaml.
|
||||
Requires the iam:UpdateAccountPasswordPolicy permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "iam:UpdateAccountPasswordPolicy",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): AWS account ID
|
||||
Returns:
|
||||
bool: True if IAM password policy is updated, False otherwise
|
||||
Fixer to enable IAM password policy to require a minimum password length of 14 characters (or configurable value).
|
||||
"""
|
||||
try:
|
||||
iam_client.client.update_account_password_policy(
|
||||
MinimumPasswordLength=iam_client.fixer_config.get(
|
||||
"iam_password_policy", {}
|
||||
).get("MinimumPasswordLength", 14),
|
||||
RequireSymbols=iam_client.password_policy.symbols,
|
||||
RequireNumbers=iam_client.password_policy.numbers,
|
||||
RequireUppercaseCharacters=iam_client.password_policy.uppercase,
|
||||
RequireLowercaseCharacters=iam_client.password_policy.lowercase,
|
||||
AllowUsersToChangePassword=iam_client.password_policy.allow_change,
|
||||
MaxPasswordAge=iam_client.password_policy.max_age,
|
||||
PasswordReusePrevention=iam_client.password_policy.reuse_prevention,
|
||||
HardExpiry=iam_client.password_policy.hard_expiry,
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Enable IAM password policy to require a minimum password length of 14 characters (or configurable value).",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="iam",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "iam:UpdateAccountPasswordPolicy",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Enable IAM password policy to require a minimum password length of 14 characters (or configurable value).
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id (account id, if finding is not provided)
|
||||
Returns:
|
||||
bool: True if IAM password policy is updated, False otherwise
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not resource_id:
|
||||
raise ValueError("resource_id (account id) is required")
|
||||
|
||||
super().fix()
|
||||
|
||||
iam_client.client.update_account_password_policy(
|
||||
MinimumPasswordLength=iam_client.fixer_config.get(
|
||||
"iam_password_policy", {}
|
||||
).get("MinimumPasswordLength", 14),
|
||||
RequireSymbols=iam_client.password_policy.symbols,
|
||||
RequireNumbers=iam_client.password_policy.numbers,
|
||||
RequireUppercaseCharacters=iam_client.password_policy.uppercase,
|
||||
RequireLowercaseCharacters=iam_client.password_policy.lowercase,
|
||||
AllowUsersToChangePassword=iam_client.password_policy.allow_change,
|
||||
MaxPasswordAge=iam_client.password_policy.max_age,
|
||||
PasswordReusePrevention=iam_client.password_policy.reuse_prevention,
|
||||
HardExpiry=iam_client.password_policy.hard_expiry,
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,45 +1,71 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
|
||||
|
||||
def fixer(resource_id: str) -> bool:
|
||||
class IamPasswordPolicyNumberFixer(AWSFixer):
|
||||
"""
|
||||
Enable IAM password policy to require numbers or the configurable value in prowler/config/fixer_config.yaml.
|
||||
Requires the iam:UpdateAccountPasswordPolicy permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "iam:UpdateAccountPasswordPolicy",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): AWS account ID
|
||||
Returns:
|
||||
bool: True if IAM password policy is updated, False otherwise
|
||||
Fixer to enable IAM password policy to require numbers (or configurable value).
|
||||
"""
|
||||
try:
|
||||
iam_client.client.update_account_password_policy(
|
||||
MinimumPasswordLength=iam_client.password_policy.length,
|
||||
RequireSymbols=iam_client.password_policy.symbols,
|
||||
RequireNumbers=iam_client.fixer_config.get("iam_password_policy", {}).get(
|
||||
"RequireNumbers", True
|
||||
),
|
||||
RequireUppercaseCharacters=iam_client.password_policy.uppercase,
|
||||
RequireLowercaseCharacters=iam_client.password_policy.lowercase,
|
||||
AllowUsersToChangePassword=iam_client.password_policy.allow_change,
|
||||
MaxPasswordAge=iam_client.password_policy.max_age,
|
||||
PasswordReusePrevention=iam_client.password_policy.reuse_prevention,
|
||||
HardExpiry=iam_client.password_policy.hard_expiry,
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Enable IAM password policy to require numbers (or configurable value).",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="iam",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "iam:UpdateAccountPasswordPolicy",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Enable IAM password policy to require numbers (or configurable value).
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id (account id, if finding is not provided)
|
||||
Returns:
|
||||
bool: True if IAM password policy is updated, False otherwise
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not resource_id:
|
||||
raise ValueError("resource_id (account id) is required")
|
||||
|
||||
super().fix()
|
||||
|
||||
iam_client.client.update_account_password_policy(
|
||||
MinimumPasswordLength=iam_client.password_policy.length,
|
||||
RequireSymbols=iam_client.password_policy.symbols,
|
||||
RequireNumbers=iam_client.fixer_config.get(
|
||||
"iam_password_policy", {}
|
||||
).get("RequireNumbers", True),
|
||||
RequireUppercaseCharacters=iam_client.password_policy.uppercase,
|
||||
RequireLowercaseCharacters=iam_client.password_policy.lowercase,
|
||||
AllowUsersToChangePassword=iam_client.password_policy.allow_change,
|
||||
MaxPasswordAge=iam_client.password_policy.max_age,
|
||||
PasswordReusePrevention=iam_client.password_policy.reuse_prevention,
|
||||
HardExpiry=iam_client.password_policy.hard_expiry,
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,45 +1,71 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
|
||||
|
||||
def fixer(resource_id: str) -> bool:
|
||||
class IamPasswordPolicyReuse24Fixer(AWSFixer):
|
||||
"""
|
||||
Enable IAM password policy to prevent reusing the 24 previous passwords or the configurable value in prowler/config/fixer_config.yaml.
|
||||
Requires the iam:UpdateAccountPasswordPolicy permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "iam:UpdateAccountPasswordPolicy",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): AWS account ID
|
||||
Returns:
|
||||
bool: True if IAM password policy is updated, False otherwise
|
||||
Fixer to enable IAM password policy to prevent reusing the 24 previous passwords (or configurable value).
|
||||
"""
|
||||
try:
|
||||
iam_client.client.update_account_password_policy(
|
||||
MinimumPasswordLength=iam_client.password_policy.length,
|
||||
RequireSymbols=iam_client.password_policy.symbols,
|
||||
RequireNumbers=iam_client.password_policy.numbers,
|
||||
RequireUppercaseCharacters=iam_client.password_policy.uppercase,
|
||||
RequireLowercaseCharacters=iam_client.password_policy.lowercase,
|
||||
AllowUsersToChangePassword=iam_client.password_policy.allow_change,
|
||||
MaxPasswordAge=iam_client.password_policy.max_age,
|
||||
PasswordReusePrevention=iam_client.fixer_config.get(
|
||||
"iam_password_policy", {}
|
||||
).get("PasswordReusePrevention", 24),
|
||||
HardExpiry=iam_client.password_policy.hard_expiry,
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Enable IAM password policy to prevent reusing the 24 previous passwords (or configurable value).",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="iam",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "iam:UpdateAccountPasswordPolicy",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Enable IAM password policy to prevent reusing the 24 previous passwords (or configurable value).
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id (account id, if finding is not provided)
|
||||
Returns:
|
||||
bool: True if IAM password policy is updated, False otherwise
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not resource_id:
|
||||
raise ValueError("resource_id (account id) is required")
|
||||
|
||||
super().fix()
|
||||
|
||||
iam_client.client.update_account_password_policy(
|
||||
MinimumPasswordLength=iam_client.password_policy.length,
|
||||
RequireSymbols=iam_client.password_policy.symbols,
|
||||
RequireNumbers=iam_client.password_policy.numbers,
|
||||
RequireUppercaseCharacters=iam_client.password_policy.uppercase,
|
||||
RequireLowercaseCharacters=iam_client.password_policy.lowercase,
|
||||
AllowUsersToChangePassword=iam_client.password_policy.allow_change,
|
||||
MaxPasswordAge=iam_client.password_policy.max_age,
|
||||
PasswordReusePrevention=iam_client.fixer_config.get(
|
||||
"iam_password_policy", {}
|
||||
).get("PasswordReusePrevention", 24),
|
||||
HardExpiry=iam_client.password_policy.hard_expiry,
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,45 +1,71 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
|
||||
|
||||
def fixer(resource_id: str) -> bool:
|
||||
class IamPasswordPolicySymbolFixer(AWSFixer):
|
||||
"""
|
||||
Enable IAM password policy to require symbols or the configurable value in prowler/config/fixer_config.yaml.
|
||||
Requires the iam:UpdateAccountPasswordPolicy permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "iam:UpdateAccountPasswordPolicy",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): AWS account ID
|
||||
Returns:
|
||||
bool: True if IAM password policy is updated, False otherwise
|
||||
Fixer to enable IAM password policy to require symbols (or configurable value).
|
||||
"""
|
||||
try:
|
||||
iam_client.client.update_account_password_policy(
|
||||
MinimumPasswordLength=iam_client.password_policy.length,
|
||||
RequireSymbols=iam_client.fixer_config.get("iam_password_policy", {}).get(
|
||||
"RequireSymbols", True
|
||||
),
|
||||
RequireNumbers=iam_client.password_policy.numbers,
|
||||
RequireUppercaseCharacters=iam_client.password_policy.uppercase,
|
||||
RequireLowercaseCharacters=iam_client.password_policy.lowercase,
|
||||
AllowUsersToChangePassword=iam_client.password_policy.allow_change,
|
||||
MaxPasswordAge=iam_client.password_policy.max_age,
|
||||
PasswordReusePrevention=iam_client.password_policy.reuse_prevention,
|
||||
HardExpiry=iam_client.password_policy.hard_expiry,
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Enable IAM password policy to require symbols (or configurable value).",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="iam",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "iam:UpdateAccountPasswordPolicy",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Enable IAM password policy to require symbols (or configurable value).
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id (account id, if finding is not provided)
|
||||
Returns:
|
||||
bool: True if IAM password policy is updated, False otherwise
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not resource_id:
|
||||
raise ValueError("resource_id (account id) is required")
|
||||
|
||||
super().fix()
|
||||
|
||||
iam_client.client.update_account_password_policy(
|
||||
MinimumPasswordLength=iam_client.password_policy.length,
|
||||
RequireSymbols=iam_client.fixer_config.get(
|
||||
"iam_password_policy", {}
|
||||
).get("RequireSymbols", True),
|
||||
RequireNumbers=iam_client.password_policy.numbers,
|
||||
RequireUppercaseCharacters=iam_client.password_policy.uppercase,
|
||||
RequireLowercaseCharacters=iam_client.password_policy.lowercase,
|
||||
AllowUsersToChangePassword=iam_client.password_policy.allow_change,
|
||||
MaxPasswordAge=iam_client.password_policy.max_age,
|
||||
PasswordReusePrevention=iam_client.password_policy.reuse_prevention,
|
||||
HardExpiry=iam_client.password_policy.hard_expiry,
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,45 +1,71 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
|
||||
|
||||
def fixer(resource_id: str) -> bool:
|
||||
class IamPasswordPolicyUppercaseFixer(AWSFixer):
|
||||
"""
|
||||
Enable IAM password policy to require uppercase characters or the configurable value in prowler/config/fixer_config.yaml.
|
||||
Requires the iam:UpdateAccountPasswordPolicy permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "iam:UpdateAccountPasswordPolicy",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): AWS account ID
|
||||
Returns:
|
||||
bool: True if IAM password policy is updated, False otherwise
|
||||
Fixer to enable IAM password policy to require uppercase characters (or configurable value).
|
||||
"""
|
||||
try:
|
||||
iam_client.client.update_account_password_policy(
|
||||
MinimumPasswordLength=iam_client.password_policy.length,
|
||||
RequireSymbols=iam_client.password_policy.symbols,
|
||||
RequireNumbers=iam_client.password_policy.numbers,
|
||||
RequireUppercaseCharacters=iam_client.fixer_config.get(
|
||||
"iam_password_policy", {}
|
||||
).get("RequireUppercaseCharacters", True),
|
||||
RequireLowercaseCharacters=iam_client.password_policy.lowercase,
|
||||
AllowUsersToChangePassword=iam_client.password_policy.allow_change,
|
||||
MaxPasswordAge=iam_client.password_policy.max_age,
|
||||
PasswordReusePrevention=iam_client.password_policy.reuse_prevention,
|
||||
HardExpiry=iam_client.password_policy.hard_expiry,
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Enable IAM password policy to require uppercase characters (or configurable value).",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="iam",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "iam:UpdateAccountPasswordPolicy",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Enable IAM password policy to require uppercase characters (or configurable value).
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id (account id, if finding is not provided)
|
||||
Returns:
|
||||
bool: True if IAM password policy is updated, False otherwise
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not resource_id:
|
||||
raise ValueError("resource_id (account id) is required")
|
||||
|
||||
super().fix()
|
||||
|
||||
iam_client.client.update_account_password_policy(
|
||||
MinimumPasswordLength=iam_client.password_policy.length,
|
||||
RequireSymbols=iam_client.password_policy.symbols,
|
||||
RequireNumbers=iam_client.password_policy.numbers,
|
||||
RequireUppercaseCharacters=iam_client.fixer_config.get(
|
||||
"iam_password_policy", {}
|
||||
).get("RequireUppercaseCharacters", True),
|
||||
RequireLowercaseCharacters=iam_client.password_policy.lowercase,
|
||||
AllowUsersToChangePassword=iam_client.password_policy.allow_change,
|
||||
MaxPasswordAge=iam_client.password_policy.max_age,
|
||||
PasswordReusePrevention=iam_client.password_policy.reuse_prevention,
|
||||
HardExpiry=iam_client.password_policy.hard_expiry,
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,36 +1,74 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.kms.kms_client import kms_client
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class KmsCmkNotDeletedUnintentionallyFixer(AWSFixer):
|
||||
"""
|
||||
Cancel the scheduled deletion of a KMS key.
|
||||
Specifically, this fixer calls the 'cancel_key_deletion' method to restore the KMS key's availability if it is marked for deletion.
|
||||
Requires the kms:CancelKeyDeletion permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "kms:CancelKeyDeletion",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The ID of the KMS key to cancel the deletion for.
|
||||
region (str): AWS region where the KMS key exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (deletion cancellation is completed), False otherwise.
|
||||
Fixer for KMS keys marked for deletion.
|
||||
This fixer cancels the scheduled deletion of KMS keys.
|
||||
"""
|
||||
try:
|
||||
regional_client = kms_client.regional_clients[region]
|
||||
regional_client.cancel_key_deletion(KeyId=resource_id)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Initialize KMS fixer.
|
||||
"""
|
||||
super().__init__(
|
||||
description="Cancel the scheduled deletion of a KMS key",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="kms",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "kms:CancelKeyDeletion",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Cancel the scheduled deletion of a KMS key.
|
||||
This fixer calls the 'cancel_key_deletion' method to restore the KMS key's availability
|
||||
if it is marked for deletion.
|
||||
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: Additional arguments (region and resource_id are required if finding is not provided)
|
||||
|
||||
Returns:
|
||||
bool: True if the operation is successful (deletion cancellation is completed), False otherwise
|
||||
"""
|
||||
try:
|
||||
# Get region and resource_id either from finding or kwargs
|
||||
if finding:
|
||||
region = finding.region
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not region or not resource_id:
|
||||
raise ValueError("Region and resource_id are required")
|
||||
|
||||
# Show the fixing message
|
||||
super().fix(region=region, resource_id=resource_id)
|
||||
|
||||
# Get the client for this region
|
||||
regional_client = kms_client.regional_clients[region]
|
||||
|
||||
# Cancel key deletion
|
||||
regional_client.cancel_key_deletion(KeyId=resource_id)
|
||||
return True
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -1,34 +1,62 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.kms.kms_client import kms_client
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class KmsCmkRotationEnabledFixer(AWSFixer):
|
||||
"""
|
||||
Enable CMK rotation. Requires the kms:EnableKeyRotation permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "kms:EnableKeyRotation",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): KMS CMK ID
|
||||
region (str): AWS region
|
||||
Returns:
|
||||
bool: True if CMK rotation is enabled, False otherwise
|
||||
Fixer to enable CMK rotation.
|
||||
"""
|
||||
try:
|
||||
regional_client = kms_client.regional_clients[region]
|
||||
regional_client.enable_key_rotation(KeyId=resource_id)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Enable CMK rotation.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="kms",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "kms:EnableKeyRotation",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Enable CMK rotation.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region, resource_id (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if CMK rotation is enabled, False otherwise
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not region or not resource_id:
|
||||
raise ValueError("region and resource_id are required")
|
||||
|
||||
super().fix(region=region, resource_id=resource_id)
|
||||
|
||||
regional_client = kms_client.regional_clients[region]
|
||||
regional_client.enable_key_rotation(KeyId=resource_id)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,40 +1,66 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.neptune.neptune_client import neptune_client
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class NeptuneClusterPublicSnapshotFixer(AWSFixer):
|
||||
"""
|
||||
Modify the attributes of a Neptune DB cluster snapshot to remove public access.
|
||||
Specifically, this fixer removes the 'all' value from the 'restore' attribute to
|
||||
prevent the snapshot from being publicly accessible. Requires the rds:ModifyDBClusterSnapshotAttribute permissions.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "rds:ModifyDBClusterSnapshotAttribute",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The DB cluster snapshot identifier.
|
||||
region (str): AWS region where the snapshot exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (public access is removed), False otherwise.
|
||||
Fixer to remove public access from Neptune DB cluster snapshots.
|
||||
"""
|
||||
try:
|
||||
regional_client = neptune_client.regional_clients[region]
|
||||
regional_client.modify_db_cluster_snapshot_attribute(
|
||||
DBClusterSnapshotIdentifier=resource_id,
|
||||
AttributeName="restore",
|
||||
ValuesToRemove=["all"],
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Remove public access from Neptune DB cluster snapshots.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="neptune",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "rds:ModifyDBClusterSnapshotAttribute",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Remove public access from Neptune DB cluster snapshots.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region, resource_id (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if public access is removed, False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not region or not resource_id:
|
||||
raise ValueError("region and resource_id are required")
|
||||
|
||||
super().fix(region=region, resource_id=resource_id)
|
||||
|
||||
regional_client = neptune_client.regional_clients[region]
|
||||
regional_client.modify_db_cluster_snapshot_attribute(
|
||||
DBClusterSnapshotIdentifier=resource_id,
|
||||
AttributeName="restore",
|
||||
ValuesToRemove=["all"],
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,43 +1,67 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.opensearch.opensearch_client import (
|
||||
opensearch_client,
|
||||
)
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class OpensearchServiceDomainsNotPubliclyAccessibleFixer(AWSFixer):
|
||||
"""
|
||||
Modify the OpenSearch domain's resource-based policy to remove public access.
|
||||
Specifically, this fixer update the domain config and add an empty policy to remove the old one.
|
||||
Requires the es:UpdateDomainConfig permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "es:UpdateDomainConfig",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The OpenSearch domain name.
|
||||
region (str): AWS region where the OpenSearch domain exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (policy updated), False otherwise.
|
||||
Fixer to remove public access from OpenSearch domains.
|
||||
"""
|
||||
try:
|
||||
regional_client = opensearch_client.regional_clients[region]
|
||||
|
||||
regional_client.update_domain_config(
|
||||
DomainName=resource_id,
|
||||
AccessPolicies="",
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Remove public access from OpenSearch domains.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="opensearch",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "es:UpdateDomainConfig",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Remove public access from OpenSearch domains.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region, resource_id (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if public access is removed, False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not region or not resource_id:
|
||||
raise ValueError("region and resource_id are required")
|
||||
|
||||
super().fix(region=region, resource_id=resource_id)
|
||||
|
||||
regional_client = opensearch_client.regional_clients[region]
|
||||
regional_client.update_domain_config(
|
||||
DomainName=resource_id,
|
||||
AccessPolicies="",
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,40 +1,66 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.rds.rds_client import rds_client
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class RdsInstanceNoPublicAccessFixer(AWSFixer):
|
||||
"""
|
||||
Modify the attributes of an RDS instance to disable public accessibility.
|
||||
Specifically, this fixer sets the 'PubliclyAccessible' attribute to False
|
||||
to prevent the RDS instance from being publicly accessible. Requires the rds:ModifyDBInstance permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "rds:ModifyDBInstance",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The DB instance identifier.
|
||||
region (str): AWS region where the DB instance exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (public access is disabled), False otherwise.
|
||||
Fixer to disable public accessibility for RDS instances.
|
||||
"""
|
||||
try:
|
||||
regional_client = rds_client.regional_clients[region]
|
||||
regional_client.modify_db_instance(
|
||||
DBInstanceIdentifier=resource_id,
|
||||
PubliclyAccessible=False,
|
||||
ApplyImmediately=True,
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Disable public accessibility for RDS instances.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="rds",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "rds:ModifyDBInstance",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Disable public accessibility for RDS instances.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region, resource_id (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if public access is disabled, False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not region or not resource_id:
|
||||
raise ValueError("region and resource_id are required")
|
||||
|
||||
super().fix(region=region, resource_id=resource_id)
|
||||
|
||||
regional_client = rds_client.regional_clients[region]
|
||||
regional_client.modify_db_instance(
|
||||
DBInstanceIdentifier=resource_id,
|
||||
PubliclyAccessible=False,
|
||||
ApplyImmediately=True,
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,60 +1,86 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.rds.rds_client import rds_client
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class RdsSnapshotsPublicAccessFixer(AWSFixer):
|
||||
"""
|
||||
Modify the attributes of an RDS DB snapshot or DB cluster snapshot to remove public access.
|
||||
Specifically, this fixer removes the 'all' value from the 'restore' attribute to prevent the snapshot from being publicly accessible
|
||||
for both DB snapshots and DB cluster snapshots. Requires the rds:ModifyDBSnapshotAttribute or rds:ModifyDBClusterSnapshotAttribute permissions.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "rds:ModifyDBSnapshotAttribute",
|
||||
"Resource": "*"
|
||||
Fixer to remove public access from RDS DB snapshots and DB cluster snapshots.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Remove public access from RDS DB snapshots and DB cluster snapshots.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="rds",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "rds:ModifyDBSnapshotAttribute",
|
||||
"Resource": "*",
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "rds:ModifyDBClusterSnapshotAttribute",
|
||||
"Resource": "*",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "rds:ModifyDBClusterSnapshotAttribute",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The DB snapshot or DB cluster snapshot identifier.
|
||||
region (str): AWS region where the snapshot exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (public access is removed), False otherwise.
|
||||
"""
|
||||
try:
|
||||
regional_client = rds_client.regional_clients[region]
|
||||
|
||||
# Check if the resource is a DB Cluster Snapshot or a DB Instance Snapshot
|
||||
try:
|
||||
regional_client.describe_db_cluster_snapshots(
|
||||
DBClusterSnapshotIdentifier=resource_id
|
||||
)
|
||||
# If the describe call is successful, it's a DB cluster snapshot
|
||||
regional_client.modify_db_cluster_snapshot_attribute(
|
||||
DBClusterSnapshotIdentifier=resource_id,
|
||||
AttributeName="restore",
|
||||
ValuesToRemove=["all"],
|
||||
)
|
||||
except regional_client.exceptions.DBClusterSnapshotNotFoundFault:
|
||||
# If the DB cluster snapshot doesn't exist, it's an instance snapshot
|
||||
regional_client.modify_db_snapshot_attribute(
|
||||
DBSnapshotIdentifier=resource_id,
|
||||
AttributeName="restore",
|
||||
ValuesToRemove=["all"],
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Remove public access from RDS DB snapshots and DB cluster snapshots.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region, resource_id (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if public access is removed, False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not region or not resource_id:
|
||||
raise ValueError("region and resource_id are required")
|
||||
|
||||
super().fix(region=region, resource_id=resource_id)
|
||||
|
||||
regional_client = rds_client.regional_clients[region]
|
||||
|
||||
# Check if the resource is a DB Cluster Snapshot or a DB Instance Snapshot
|
||||
try:
|
||||
regional_client.describe_db_cluster_snapshots(
|
||||
DBClusterSnapshotIdentifier=resource_id
|
||||
)
|
||||
# If the describe call is successful, it's a DB cluster snapshot
|
||||
regional_client.modify_db_cluster_snapshot_attribute(
|
||||
DBClusterSnapshotIdentifier=resource_id,
|
||||
AttributeName="restore",
|
||||
ValuesToRemove=["all"],
|
||||
)
|
||||
except regional_client.exceptions.DBClusterSnapshotNotFoundFault:
|
||||
# If the DB cluster snapshot doesn't exist, it's an instance snapshot
|
||||
regional_client.modify_db_snapshot_attribute(
|
||||
DBSnapshotIdentifier=resource_id,
|
||||
AttributeName="restore",
|
||||
ValuesToRemove=["all"],
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,41 +1,67 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.s3.s3control_client import s3control_client
|
||||
|
||||
|
||||
def fixer(resource_id: str) -> bool:
|
||||
class S3AccountLevelPublicAccessBlocksFixer(AWSFixer):
|
||||
"""
|
||||
Enable S3 Block Public Access for the account. NOTE: By blocking all S3 public access you may break public S3 buckets.
|
||||
Requires the s3:PutAccountPublicAccessBlock permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:PutAccountPublicAccessBlock",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The AWS account ID.
|
||||
Returns:
|
||||
bool: True if S3 Block Public Access is enabled, False otherwise
|
||||
Fixer to enable S3 Block Public Access for the account.
|
||||
"""
|
||||
try:
|
||||
s3control_client.client.put_public_access_block(
|
||||
AccountId=resource_id,
|
||||
PublicAccessBlockConfiguration={
|
||||
"BlockPublicAcls": True,
|
||||
"IgnorePublicAcls": True,
|
||||
"BlockPublicPolicy": True,
|
||||
"RestrictPublicBuckets": True,
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Enable S3 Block Public Access for the account.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="s3",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:PutAccountPublicAccessBlock",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Enable S3 Block Public Access for the account.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: resource_id (account id, if finding is not provided)
|
||||
Returns:
|
||||
bool: True if S3 Block Public Access is enabled, False otherwise
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not resource_id:
|
||||
raise ValueError("resource_id (account id) is required")
|
||||
|
||||
super().fix()
|
||||
|
||||
s3control_client.client.put_public_access_block(
|
||||
AccountId=resource_id,
|
||||
PublicAccessBlockConfiguration={
|
||||
"BlockPublicAcls": True,
|
||||
"IgnorePublicAcls": True,
|
||||
"BlockPublicPolicy": True,
|
||||
"RestrictPublicBuckets": True,
|
||||
},
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,38 +1,62 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.s3.s3_client import s3_client
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class S3BucketPolicyPublicWriteAccessFixer(AWSFixer):
|
||||
"""
|
||||
Modify the S3 bucket's policy to remove public access.
|
||||
Specifically, this fixer delete the policy of the public bucket.
|
||||
Requires the s3:DeleteBucketPolicy permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:DeleteBucketPolicy",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The S3 bucket name.
|
||||
region (str): AWS region where the S3 bucket exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (policy updated), False otherwise.
|
||||
Fixer to remove public write access from S3 bucket policies.
|
||||
"""
|
||||
try:
|
||||
regional_client = s3_client.regional_clients[region]
|
||||
|
||||
regional_client.delete_bucket_policy(Bucket=resource_id)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Remove public write access from S3 bucket policies.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="s3",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:DeleteBucketPolicy",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Remove public write access from S3 bucket policies.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region, resource_id (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the policy is removed, False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not region or not resource_id:
|
||||
raise ValueError("region and resource_id are required")
|
||||
|
||||
super().fix(region=region, resource_id=resource_id)
|
||||
|
||||
regional_client = s3_client.regional_clients[region]
|
||||
regional_client.delete_bucket_policy(Bucket=resource_id)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,46 +1,70 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.s3.s3_client import s3_client
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class S3BucketPublicAccessFixer(AWSFixer):
|
||||
"""
|
||||
Modify the S3 bucket's public access settings to block all public access.
|
||||
Specifically, this fixer configures the bucket's public access block settings to
|
||||
prevent any public access (ACLs and policies). Requires the s3:PutBucketPublicAccessBlock
|
||||
permission to modify the public access settings.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:PutBucketPublicAccessBlock",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The S3 bucket name.
|
||||
region (str): AWS region where the S3 bucket exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (public access is blocked),
|
||||
False otherwise.
|
||||
Fixer to block all public access to S3 buckets.
|
||||
"""
|
||||
try:
|
||||
regional_client = s3_client.regional_clients[region]
|
||||
regional_client.put_public_access_block(
|
||||
Bucket=resource_id,
|
||||
PublicAccessBlockConfiguration={
|
||||
"BlockPublicAcls": True,
|
||||
"IgnorePublicAcls": True,
|
||||
"BlockPublicPolicy": True,
|
||||
"RestrictPublicBuckets": True,
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Block all public access to S3 buckets.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="s3",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:PutBucketPublicAccessBlock",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Block all public access to S3 buckets.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region, resource_id (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if public access is blocked, False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not region or not resource_id:
|
||||
raise ValueError("region and resource_id are required")
|
||||
|
||||
super().fix(region=region, resource_id=resource_id)
|
||||
|
||||
regional_client = s3_client.regional_clients[region]
|
||||
regional_client.put_public_access_block(
|
||||
Bucket=resource_id,
|
||||
PublicAccessBlockConfiguration={
|
||||
"BlockPublicAcls": True,
|
||||
"IgnorePublicAcls": True,
|
||||
"BlockPublicPolicy": True,
|
||||
"RestrictPublicBuckets": True,
|
||||
},
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,40 +1,62 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.s3.s3_client import s3_client
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class S3BucketPublicListAclFixer(AWSFixer):
|
||||
"""
|
||||
Modify the S3 bucket ACL to restrict public read access.
|
||||
Specifically, this fixer sets the ACL of the bucket to 'private' to prevent
|
||||
any public access to the S3 bucket.
|
||||
Requires the s3:PutBucketAcl permission.
|
||||
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:PutBucketAcl",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Args:
|
||||
resource_id (str): The S3 bucket name.
|
||||
region (str): AWS region where the S3 bucket exists.
|
||||
|
||||
Returns:
|
||||
bool: True if the operation is successful (bucket access is updated), False otherwise.
|
||||
Fixer to restrict public read access by setting the S3 bucket ACL to 'private'.
|
||||
"""
|
||||
try:
|
||||
regional_client = s3_client.regional_clients[region]
|
||||
regional_client.put_bucket_acl(Bucket=resource_id, ACL="private")
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Restrict public read access by setting the S3 bucket ACL to 'private'.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="s3",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:PutBucketAcl",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Restrict public read access by setting the S3 bucket ACL to 'private'.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region, resource_id (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the bucket ACL is set to private, False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not region or not resource_id:
|
||||
raise ValueError("region and resource_id are required")
|
||||
|
||||
super().fix(region=region, resource_id=resource_id)
|
||||
|
||||
regional_client = s3_client.regional_clients[region]
|
||||
regional_client.put_bucket_acl(Bucket=resource_id, ACL="private")
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,36 +1,62 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.s3.s3_client import s3_client
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class S3BucketPublicWriteAclFixer(AWSFixer):
|
||||
"""
|
||||
Modify the S3 bucket ACL to restrict public write access.
|
||||
Specifically, this fixer sets the ACL of the bucket to 'private' to prevent
|
||||
public write access to the S3 bucket. Requires the s3:PutBucketAcl permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:PutBucketAcl",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The S3 bucket id.
|
||||
region (str): AWS region where the S3 bucket exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (bucket access is updated), False otherwise.
|
||||
Fixer to restrict public write access by setting the S3 bucket ACL to 'private'.
|
||||
"""
|
||||
try:
|
||||
regional_client = s3_client.regional_clients[region]
|
||||
regional_client.put_bucket_acl(Bucket=resource_id, ACL="private")
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Restrict public write access by setting the S3 bucket ACL to 'private'.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="s3",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:PutBucketAcl",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Restrict public write access by setting the S3 bucket ACL to 'private'.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region, resource_id (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the bucket ACL is set to private, False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not region or not resource_id:
|
||||
raise ValueError("region and resource_id are required")
|
||||
|
||||
super().fix(region=region, resource_id=resource_id)
|
||||
|
||||
regional_client = s3_client.regional_clients[region]
|
||||
regional_client.put_bucket_acl(Bucket=resource_id, ACL="private")
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,39 +1,66 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.securityhub.securityhub_client import (
|
||||
securityhub_client,
|
||||
)
|
||||
|
||||
|
||||
def fixer(region):
|
||||
class SecurityhubEnabledFixer(AWSFixer):
|
||||
"""
|
||||
Enable Security Hub in a region. Requires the securityhub:EnableSecurityHub permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "securityhub:EnableSecurityHub",
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
region (str): AWS region
|
||||
Returns:
|
||||
bool: True if Security Hub is enabled, False otherwise
|
||||
Fixer to enable Security Hub in a region.
|
||||
"""
|
||||
try:
|
||||
regional_client = securityhub_client.regional_clients[region]
|
||||
regional_client.enable_security_hub(
|
||||
EnableDefaultStandards=securityhub_client.fixer_config.get(
|
||||
"securityhub_enabled", {}
|
||||
).get("EnableDefaultStandards", True)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Enable Security Hub in a region.",
|
||||
cost_impact=True,
|
||||
cost_description="Enabling Security Hub incurs costs for findings ingestion, analysis, and resource coverage. Charges apply per finding and per resource. See AWS Security Hub pricing.",
|
||||
service="securityhub",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "securityhub:EnableSecurityHub",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Enable Security Hub in a region.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if Security Hub is enabled, False otherwise
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
|
||||
if not region:
|
||||
raise ValueError("region is required")
|
||||
|
||||
super().fix(region=region)
|
||||
|
||||
regional_client = securityhub_client.regional_clients[region]
|
||||
regional_client.enable_security_hub(
|
||||
EnableDefaultStandards=securityhub_client.fixer_config.get(
|
||||
"securityhub_enabled", {}
|
||||
).get("EnableDefaultStandards", True)
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,64 +1,85 @@
|
||||
import json
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_AWS
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
from prowler.providers.aws.services.sqs.sqs_client import sqs_client
|
||||
|
||||
|
||||
def fixer(resource_id: str, region: str) -> bool:
|
||||
class SqsQueuesNotPubliclyAccessibleFixer(AWSFixer):
|
||||
"""
|
||||
Modify the SQS queue's resource-based policy to remove public access and replace with trusted account access.
|
||||
Specifically, this fixer checks if any statement has a public Principal (e.g., "*" or "CanonicalUser")
|
||||
and replaces it with the ARN of the trusted AWS account.
|
||||
Requires the sqs:SetQueueAttributes permission.
|
||||
Permissions:
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "sqs:SetQueueAttributes",
|
||||
"Resource": "*"
|
||||
Fixer to remove public access from SQS queue policies and replace with trusted account access.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Remove public access from SQS queue policies and replace with trusted account access.",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="sqs",
|
||||
iam_policy_required={
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "sqs:SetQueueAttributes",
|
||||
"Resource": "*",
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_AWS] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Remove public access from SQS queue policies and replace with trusted account access.
|
||||
Args:
|
||||
finding (Optional[Check_Report_AWS]): Finding to fix
|
||||
**kwargs: region, resource_id (if finding is not provided)
|
||||
Returns:
|
||||
bool: True if the policy is updated, False otherwise.
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
region = finding.region
|
||||
resource_id = finding.resource_id
|
||||
else:
|
||||
region = kwargs.get("region")
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
if not region or not resource_id:
|
||||
raise ValueError("region and resource_id are required")
|
||||
|
||||
super().fix(region=region, resource_id=resource_id)
|
||||
|
||||
account_id = sqs_client.audited_account
|
||||
audited_partition = sqs_client.audited_partition
|
||||
regional_client = sqs_client.regional_clients[region]
|
||||
queue_name = resource_id.split("/")[-1]
|
||||
|
||||
trusted_policy = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "ProwlerFixerStatement",
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"AWS": account_id,
|
||||
},
|
||||
"Action": "sqs:*",
|
||||
"Resource": f"arn:{audited_partition}:sqs:{region}:{account_id}:{queue_name}",
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
Args:
|
||||
resource_id (str): The SQS queue name or ARN.
|
||||
region (str): AWS region where the SQS queue exists.
|
||||
Returns:
|
||||
bool: True if the operation is successful (policy updated), False otherwise.
|
||||
"""
|
||||
try:
|
||||
account_id = sqs_client.audited_account
|
||||
audited_partition = sqs_client.audited_partition
|
||||
|
||||
regional_client = sqs_client.regional_clients[region]
|
||||
|
||||
queue_name = resource_id.split("/")[-1]
|
||||
|
||||
trusted_policy = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "ProwlerFixerStatement",
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"AWS": account_id,
|
||||
},
|
||||
"Action": "sqs:*",
|
||||
"Resource": f"arn:{audited_partition}:sqs:{region}:{account_id}:{queue_name}",
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
regional_client.set_queue_attributes(
|
||||
QueueUrl=resource_id,
|
||||
Attributes={"Policy": json.dumps(trusted_policy)},
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
regional_client.set_queue_attributes(
|
||||
QueueUrl=resource_id,
|
||||
Attributes={"Policy": json.dumps(trusted_policy)},
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{region if 'region' in locals() else 'unknown'} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
0
prowler/providers/azure/lib/fix/__init__.py
Normal file
0
prowler/providers/azure/lib/fix/__init__.py
Normal file
97
prowler/providers/azure/lib/fix/fixer.py
Normal file
97
prowler/providers/azure/lib/fix/fixer.py
Normal file
@@ -0,0 +1,97 @@
|
||||
from typing import Dict, Optional
|
||||
|
||||
from colorama import Style
|
||||
|
||||
from prowler.config.config import orange_color
|
||||
from prowler.lib.check.models import Check_Report_Azure
|
||||
from prowler.lib.fix.fixer import Fixer
|
||||
from prowler.lib.logger import logger
|
||||
|
||||
|
||||
class AzureFixer(Fixer):
|
||||
"""Azure specific fixer implementation"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
description: str,
|
||||
cost_impact: bool = False,
|
||||
cost_description: Optional[str] = None,
|
||||
service: str = "",
|
||||
permissions_required: Optional[Dict] = None,
|
||||
):
|
||||
super().__init__(description, cost_impact, cost_description)
|
||||
self.service = service
|
||||
self.permissions_required = permissions_required or {}
|
||||
|
||||
def _get_fixer_info(self):
|
||||
"""Each fixer must define its metadata"""
|
||||
fixer_info = super()._get_fixer_info()
|
||||
fixer_info["service"] = self.service
|
||||
fixer_info["permissions_required"] = self.permissions_required
|
||||
return fixer_info
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_Azure] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Azure specific method to execute the fixer.
|
||||
This method handles the printing of fixing status messages.
|
||||
|
||||
Args:
|
||||
finding (Optional[Check_Report_Azure]): Finding to fix
|
||||
**kwargs: Additional Azure-specific arguments (subscription_id, resource_id, resource_group)
|
||||
|
||||
Returns:
|
||||
bool: True if fixing was successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
# Get values either from finding or kwargs
|
||||
subscription_id = None
|
||||
resource_id = None
|
||||
resource_group = None
|
||||
|
||||
if finding:
|
||||
subscription_id = (
|
||||
finding.subscription if hasattr(finding, "subscription") else None
|
||||
)
|
||||
resource_id = (
|
||||
finding.resource_id if hasattr(finding, "resource_id") else None
|
||||
)
|
||||
resource_group = (
|
||||
finding.resource.get("resource_group_name")
|
||||
if hasattr(finding.resource, "resource_group_name")
|
||||
else None
|
||||
)
|
||||
else:
|
||||
subscription_id = kwargs.get("subscription_id")
|
||||
resource_id = kwargs.get("resource_id")
|
||||
resource_group = kwargs.get("resource_group")
|
||||
|
||||
# Print the appropriate message based on available information
|
||||
if subscription_id and resource_id and resource_group:
|
||||
print(
|
||||
f"\t{orange_color}FIXING Resource {resource_id} in Resource Group {resource_group} (Subscription: {subscription_id})...{Style.RESET_ALL}"
|
||||
)
|
||||
elif subscription_id and resource_id:
|
||||
print(
|
||||
f"\t{orange_color}FIXING Resource {resource_id} (Subscription: {subscription_id})...{Style.RESET_ALL}"
|
||||
)
|
||||
elif subscription_id:
|
||||
print(
|
||||
f"\t{orange_color}FIXING Subscription {subscription_id}...{Style.RESET_ALL}"
|
||||
)
|
||||
elif resource_id:
|
||||
print(
|
||||
f"\t{orange_color}FIXING Resource {resource_id}...{Style.RESET_ALL}"
|
||||
)
|
||||
else:
|
||||
logger.error(
|
||||
"Either finding or required kwargs (subscription_id, resource_id, resource_group) must be provided"
|
||||
)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
@@ -0,0 +1,75 @@
|
||||
from typing import Optional
|
||||
|
||||
from azure.mgmt.web.models import SiteConfigResource
|
||||
|
||||
from prowler.lib.check.models import Check_Report_Azure
|
||||
from prowler.providers.azure.lib.fix.fixer import AzureFixer
|
||||
from prowler.providers.azure.services.app.app_client import app_client
|
||||
|
||||
|
||||
class AppFunctionFtpsDeploymentDisabledFixer(AzureFixer):
|
||||
"""
|
||||
This class handles the remediation of the app_function_ftps_deployment_disabled check.
|
||||
It disables FTP/FTPS deployments for Azure Functions to prevent unauthorized access.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
description="Disable FTP/FTPS deployments for Azure Functions",
|
||||
service="app",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
permissions_required={
|
||||
"Microsoft.Web/sites/config/write": "Write access to the site configuration",
|
||||
},
|
||||
)
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_Azure] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Fix the failed check by disabling FTP/FTPS deployments for the Azure Function.
|
||||
|
||||
Args:
|
||||
finding (Check_Report_Azure): Finding to fix
|
||||
**kwargs: Additional Azure-specific arguments (subscription_id, resource_id, resource_group)
|
||||
|
||||
Returns:
|
||||
bool: True if FTP/FTPS is disabled, False otherwise
|
||||
"""
|
||||
try:
|
||||
if finding:
|
||||
resource_group = finding.resource.get("resource_group_name")
|
||||
resource_id = finding.resource_name
|
||||
suscription_id = finding.subscription
|
||||
else:
|
||||
resource_group = kwargs.get("resource_group")
|
||||
resource_id = kwargs.get("resource_id")
|
||||
suscription_id = kwargs.get("subscription_id")
|
||||
|
||||
if not resource_group or not resource_id or not suscription_id:
|
||||
raise ValueError(
|
||||
"Resource group, app name and subscription name are required"
|
||||
)
|
||||
|
||||
super().fix(
|
||||
resource_group=resource_group,
|
||||
resource_id=resource_id,
|
||||
suscription_id=suscription_id,
|
||||
)
|
||||
|
||||
client = app_client.clients[suscription_id]
|
||||
|
||||
site_config = SiteConfigResource(ftps_state="Disabled")
|
||||
|
||||
client.web_apps.update_configuration(
|
||||
resource_group_name=resource_group,
|
||||
name=resource_id,
|
||||
site_config=site_config,
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as error:
|
||||
self.logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
@@ -170,6 +170,7 @@ class App(AzureService):
|
||||
ftps_state=getattr(
|
||||
function_config, "ftps_state", None
|
||||
),
|
||||
resource_group_name=function.resource_group,
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -293,3 +294,4 @@ class FunctionApp:
|
||||
public_access: bool
|
||||
vnet_subnet_id: str
|
||||
ftps_state: Optional[str]
|
||||
resource_group_name: str
|
||||
|
||||
0
prowler/providers/gcp/lib/fix/__init__.py
Normal file
0
prowler/providers/gcp/lib/fix/__init__.py
Normal file
97
prowler/providers/gcp/lib/fix/fixer.py
Normal file
97
prowler/providers/gcp/lib/fix/fixer.py
Normal file
@@ -0,0 +1,97 @@
|
||||
from typing import Dict, Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_GCP
|
||||
from prowler.lib.fix.fixer import Fixer
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.gcp.gcp_provider import GcpProvider
|
||||
|
||||
|
||||
class GCPFixer(Fixer):
|
||||
"""GCP specific fixer implementation"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
description: str,
|
||||
cost_impact: bool = False,
|
||||
cost_description: Optional[str] = None,
|
||||
service: str = "",
|
||||
iam_policy_required: Optional[Dict] = None,
|
||||
):
|
||||
"""
|
||||
Initialize GCP fixer with metadata.
|
||||
|
||||
Args:
|
||||
description (str): Description of the fixer
|
||||
cost_impact (bool): Whether the fixer has a cost impact
|
||||
cost_description (Optional[str]): Description of the cost impact
|
||||
service (str): GCP service name
|
||||
iam_policy_required (Optional[Dict]): Required IAM policy for the fixer
|
||||
"""
|
||||
super().__init__(description, cost_impact, cost_description)
|
||||
self.service = service
|
||||
self.iam_policy_required = iam_policy_required or {}
|
||||
self._provider = None
|
||||
|
||||
@property
|
||||
def provider(self) -> GcpProvider:
|
||||
"""Get the GCP provider instance"""
|
||||
if not self._provider:
|
||||
self._provider = GcpProvider()
|
||||
return self._provider
|
||||
|
||||
def _get_fixer_info(self) -> Dict:
|
||||
"""Get fixer metadata"""
|
||||
info = super()._get_fixer_info()
|
||||
info["service"] = self.service
|
||||
info["iam_policy_required"] = self.iam_policy_required
|
||||
info["provider"] = "gcp"
|
||||
return info
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_GCP] = None, **kwargs) -> bool:
|
||||
"""
|
||||
GCP specific method to execute the fixer.
|
||||
This method handles the printing of fixing status messages.
|
||||
|
||||
Args:
|
||||
finding (Optional[Check_Report_GCP]): Finding to fix
|
||||
**kwargs: Additional GCP-specific arguments (project_id, resource_id)
|
||||
|
||||
Returns:
|
||||
bool: True if fixing was successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
# Get values either from finding or kwargs
|
||||
project_id = None
|
||||
resource_id = None
|
||||
|
||||
if finding:
|
||||
project_id = (
|
||||
finding.project_id if hasattr(finding, "project_id") else None
|
||||
)
|
||||
resource_id = (
|
||||
finding.resource_id if hasattr(finding, "resource_id") else None
|
||||
)
|
||||
else:
|
||||
project_id = kwargs.get("project_id")
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
# Print the appropriate message based on available information
|
||||
if project_id and resource_id:
|
||||
print(f"\tFIXING {resource_id} in project {project_id}...")
|
||||
elif project_id:
|
||||
print(f"\tFIXING project {project_id}...")
|
||||
elif resource_id:
|
||||
print(f"\tFIXING Resource {resource_id}...")
|
||||
else:
|
||||
logger.error(
|
||||
"Either finding or required kwargs (project_id, resource_id) must be provided"
|
||||
)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
@@ -0,0 +1,63 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import Check_Report_GCP
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.gcp.lib.fix.fixer import GCPFixer
|
||||
from prowler.providers.gcp.services.compute.compute_client import compute_client
|
||||
|
||||
|
||||
class ComputeProjectOsLoginEnabledFixer(GCPFixer):
|
||||
"""
|
||||
Fixer for enabling OS Login at the project level.
|
||||
This fixer enables the OS Login feature which provides centralized and automated SSH key pair management.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Initialize Compute Engine fixer.
|
||||
"""
|
||||
super().__init__(
|
||||
description="Enable OS Login at the project level",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="compute",
|
||||
iam_policy_required={
|
||||
"roles": ["roles/compute.admin"],
|
||||
},
|
||||
)
|
||||
|
||||
def fix(self, finding: Optional[Check_Report_GCP] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Enable OS Login at the project level.
|
||||
|
||||
Args:
|
||||
finding (Optional[Check_Report_GCP]): Finding to fix
|
||||
**kwargs: Additional arguments (project_id is required if finding is not provided)
|
||||
|
||||
Returns:
|
||||
bool: True if the operation is successful (OS Login is enabled), False otherwise
|
||||
"""
|
||||
try:
|
||||
# Get project_id either from finding or kwargs
|
||||
if finding:
|
||||
project_id = finding.project_id
|
||||
else:
|
||||
project_id = kwargs.get("project_id")
|
||||
|
||||
if not project_id:
|
||||
raise ValueError("project_id is required")
|
||||
|
||||
# Enable OS Login
|
||||
request = compute_client.client.projects().setCommonInstanceMetadata(
|
||||
project=project_id,
|
||||
body={"items": [{"key": "enable-oslogin", "value": "TRUE"}]},
|
||||
)
|
||||
request.execute()
|
||||
|
||||
return True
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
0
prowler/providers/m365/lib/fix/__init__.py
Normal file
0
prowler/providers/m365/lib/fix/__init__.py
Normal file
68
prowler/providers/m365/lib/fix/fixer.py
Normal file
68
prowler/providers/m365/lib/fix/fixer.py
Normal file
@@ -0,0 +1,68 @@
|
||||
from typing import Optional
|
||||
|
||||
from colorama import Style
|
||||
|
||||
from prowler.config.config import orange_color
|
||||
from prowler.lib.check.models import CheckReportM365
|
||||
from prowler.lib.fix.fixer import Fixer
|
||||
from prowler.lib.logger import logger
|
||||
|
||||
|
||||
class M365Fixer(Fixer):
|
||||
"""M365 specific fixer implementation"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
description: str,
|
||||
cost_impact: bool = False,
|
||||
cost_description: Optional[str] = None,
|
||||
service: str = "",
|
||||
):
|
||||
super().__init__(description, cost_impact, cost_description)
|
||||
self.service = service
|
||||
|
||||
def _get_fixer_info(self):
|
||||
"""Each fixer must define its metadata"""
|
||||
fixer_info = super()._get_fixer_info()
|
||||
fixer_info["service"] = self.service
|
||||
return fixer_info
|
||||
|
||||
def fix(self, finding: Optional[CheckReportM365] = None, **kwargs) -> bool:
|
||||
"""
|
||||
M365 specific method to execute the fixer.
|
||||
This method handles the printing of fixing status messages.
|
||||
|
||||
Args:
|
||||
finding (Optional[CheckReportM365]): Finding to fix
|
||||
**kwargs: Additional M365-specific arguments (resource_id)
|
||||
|
||||
Returns:
|
||||
bool: True if fixing was successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
# Get values either from finding or kwargs
|
||||
resource_id = None
|
||||
|
||||
if finding:
|
||||
resource_id = (
|
||||
finding.resource_id if hasattr(finding, "resource_id") else None
|
||||
)
|
||||
elif kwargs.get("resource_id"):
|
||||
resource_id = kwargs.get("resource_id")
|
||||
|
||||
# Print the appropriate message based on available information
|
||||
if resource_id:
|
||||
print(
|
||||
f"\t{orange_color}FIXING Resource {resource_id}...{Style.RESET_ALL}"
|
||||
)
|
||||
else:
|
||||
# If no resource_id is provided, we'll still try to proceed
|
||||
print(f"\t{orange_color}FIXING...{Style.RESET_ALL}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return False
|
||||
@@ -869,6 +869,20 @@ class M365PowerShell(PowerShellSession):
|
||||
"""
|
||||
return self.execute("Get-TransportConfig | ConvertTo-Json", json_parse=True)
|
||||
|
||||
def set_audit_log_config(self):
|
||||
"""
|
||||
Set Purview Admin Audit Log Settings.
|
||||
|
||||
Sets the audit log configuration settings for Microsoft Purview.
|
||||
|
||||
Args:
|
||||
enabled (bool): Whether to enable or disable the audit log.
|
||||
|
||||
"""
|
||||
return self.execute(
|
||||
"Set-AdminAuditLogConfig -UnifiedAuditLogIngestionEnabled $true"
|
||||
)
|
||||
|
||||
def get_sharing_policy(self) -> dict:
|
||||
"""
|
||||
Get Exchange Online Sharing Policy.
|
||||
|
||||
@@ -219,6 +219,9 @@ class M365Provider(Provider):
|
||||
# Fixer Config
|
||||
self._fixer_config = fixer_config
|
||||
|
||||
# Output Options
|
||||
self._output_options = None
|
||||
|
||||
# Mutelist
|
||||
if mutelist_content:
|
||||
self._mutelist = M365Mutelist(
|
||||
@@ -1136,3 +1139,10 @@ class M365Provider(Provider):
|
||||
except Exception as error:
|
||||
# Generic exception handling for unexpected errors
|
||||
raise RuntimeError(f"An unexpected error occurred: {str(error)}")
|
||||
|
||||
@property
|
||||
def output_options(self):
|
||||
return self._output_options
|
||||
|
||||
def set_output_options(self, output_options):
|
||||
self._output_options = output_options
|
||||
|
||||
@@ -56,3 +56,6 @@ class M365OutputOptions(ProviderOutputOptions):
|
||||
)
|
||||
else:
|
||||
self.output_filename = arguments.output_filename
|
||||
|
||||
# Add fixer mode to the output options
|
||||
self.fixer = arguments.fixer if hasattr(arguments, "fixer") else False
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
from typing import Optional
|
||||
|
||||
from prowler.lib.check.models import CheckReportM365
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.m365.lib.fix.fixer import M365Fixer
|
||||
from prowler.providers.m365.services.purview.purview_client import purview_client
|
||||
|
||||
|
||||
class PurviewAuditLogSearchEnabledFixer(M365Fixer):
|
||||
"""
|
||||
Fixer for Purview audit log search.
|
||||
This fixer enables the audit log search using PowerShell.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Initialize Purview audit log search fixer.
|
||||
"""
|
||||
super().__init__(
|
||||
description="Enable Purview audit log search",
|
||||
cost_impact=False,
|
||||
cost_description=None,
|
||||
service="purview",
|
||||
)
|
||||
|
||||
def fix(self, finding: Optional[CheckReportM365] = None, **kwargs) -> bool:
|
||||
"""
|
||||
Enable Purview audit log search using PowerShell.
|
||||
This fixer executes the Set-AdminAuditLogConfig cmdlet to enable the audit log search.
|
||||
|
||||
Args:
|
||||
finding (Optional[CheckReportM365]): Finding to fix
|
||||
**kwargs: Additional arguments
|
||||
|
||||
Returns:
|
||||
bool: True if the operation is successful (audit log search is enabled), False otherwise
|
||||
"""
|
||||
try:
|
||||
super().fix()
|
||||
|
||||
purview_client.powershell.set_audit_log_config()
|
||||
purview_client.powershell.close()
|
||||
return True
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
purview_client.powershell.close()
|
||||
return False
|
||||
@@ -13,7 +13,8 @@ class Purview(M365Service):
|
||||
if self.powershell:
|
||||
self.powershell.connect_exchange_online()
|
||||
self.audit_log_config = self._get_audit_log_config()
|
||||
self.powershell.close()
|
||||
if not provider.output_options.fixer:
|
||||
self.powershell.close()
|
||||
|
||||
def _get_audit_log_config(self):
|
||||
logger.info("M365 - Getting Admin Audit Log settings...")
|
||||
|
||||
207
tests/lib/fix/fixer_test.py
Normal file
207
tests/lib/fix/fixer_test.py
Normal file
@@ -0,0 +1,207 @@
|
||||
import json
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from prowler.lib.check.models import (
|
||||
Check_Report,
|
||||
CheckMetadata,
|
||||
Code,
|
||||
Recommendation,
|
||||
Remediation,
|
||||
)
|
||||
from prowler.lib.fix.fixer import Fixer
|
||||
|
||||
|
||||
def get_mock_metadata(
|
||||
provider="aws", check_id="test_check", service_name="testservice"
|
||||
):
|
||||
return CheckMetadata(
|
||||
Provider=provider,
|
||||
CheckID=check_id,
|
||||
CheckTitle="Test Check",
|
||||
CheckType=["type1"],
|
||||
CheckAliases=[],
|
||||
ServiceName=service_name,
|
||||
SubServiceName="",
|
||||
ResourceIdTemplate="",
|
||||
Severity="low",
|
||||
ResourceType="resource",
|
||||
Description="desc",
|
||||
Risk="risk",
|
||||
RelatedUrl="url",
|
||||
Remediation=Remediation(
|
||||
Code=Code(NativeIaC="", Terraform="", CLI="", Other=""),
|
||||
Recommendation=Recommendation(Text="", Url=""),
|
||||
),
|
||||
Categories=["cat1"],
|
||||
DependsOn=[],
|
||||
RelatedTo=[],
|
||||
Notes="",
|
||||
Compliance=[],
|
||||
)
|
||||
|
||||
|
||||
def build_metadata(provider="aws", check_id="test_check", service_name="testservice"):
|
||||
return CheckMetadata(
|
||||
Provider=provider,
|
||||
CheckID=check_id,
|
||||
CheckTitle="Test Check",
|
||||
CheckType=["type1"],
|
||||
CheckAliases=[],
|
||||
ServiceName=service_name,
|
||||
SubServiceName="",
|
||||
ResourceIdTemplate="",
|
||||
Severity="low",
|
||||
ResourceType="resource",
|
||||
Description="desc",
|
||||
Risk="risk",
|
||||
RelatedUrl="url",
|
||||
Remediation=Remediation(
|
||||
Code=Code(NativeIaC="", Terraform="", CLI="", Other=""),
|
||||
Recommendation=Recommendation(Text="", Url=""),
|
||||
),
|
||||
Categories=["cat1"],
|
||||
DependsOn=[],
|
||||
RelatedTo=[],
|
||||
Notes="",
|
||||
Compliance=[],
|
||||
)
|
||||
|
||||
|
||||
def build_finding(
|
||||
status="FAIL", provider="aws", check_id="test_check", service_name="testservice"
|
||||
):
|
||||
metadata = build_metadata(provider, check_id, service_name)
|
||||
resource = MagicMock()
|
||||
finding = Check_Report(json.dumps(metadata.dict()), resource)
|
||||
finding.status = status
|
||||
return finding
|
||||
|
||||
|
||||
class DummyFixer(Fixer):
|
||||
def fix(self, finding=None, **kwargs):
|
||||
return True
|
||||
|
||||
|
||||
class TestFixer:
|
||||
def test_get_fixer_info(self):
|
||||
fixer = DummyFixer(
|
||||
description="desc", cost_impact=True, cost_description="cost"
|
||||
)
|
||||
info = fixer._get_fixer_info()
|
||||
assert info == {
|
||||
"description": "desc",
|
||||
"cost_impact": True,
|
||||
"cost_description": "cost",
|
||||
}
|
||||
|
||||
def test_client_property(self):
|
||||
fixer = DummyFixer(description="desc")
|
||||
assert fixer.client is None
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"check_id,provider,service_name,expected_class",
|
||||
[
|
||||
(None, "aws", "testservice", None),
|
||||
("test_check", None, "testservice", None),
|
||||
("nonexistent_check", "aws", "testservice", None),
|
||||
],
|
||||
)
|
||||
def test_get_fixer_for_finding_edge(
|
||||
self, check_id, provider, service_name, expected_class
|
||||
):
|
||||
finding = MagicMock()
|
||||
finding.check_metadata.CheckID = check_id
|
||||
finding.check_metadata.Provider = provider
|
||||
finding.check_metadata.ServiceName = service_name
|
||||
with patch("prowler.lib.fix.fixer.logger"):
|
||||
fixer = Fixer.get_fixer_for_finding(finding)
|
||||
assert fixer is expected_class
|
||||
|
||||
def test_get_fixer_for_finding_importerror_print(self):
|
||||
finding = MagicMock()
|
||||
finding.check_metadata.CheckID = "nonexistent_check"
|
||||
finding.check_metadata.Provider = "aws"
|
||||
finding.check_metadata.ServiceName = "testservice"
|
||||
with patch("builtins.print") as mock_print:
|
||||
fixer = Fixer.get_fixer_for_finding(finding)
|
||||
assert fixer is None
|
||||
assert mock_print.called
|
||||
|
||||
def test_run_fixer_single_and_multiple(self):
|
||||
finding = build_finding(status="FAIL")
|
||||
with patch.object(Fixer, "run_individual_fixer", return_value=1) as mock_run:
|
||||
assert Fixer.run_fixer(finding) == 1
|
||||
assert mock_run.called
|
||||
finding.status = "PASS"
|
||||
assert Fixer.run_fixer(finding) == 0
|
||||
finding1 = build_finding(status="FAIL")
|
||||
finding2 = build_finding(status="FAIL")
|
||||
with patch.object(Fixer, "run_individual_fixer", return_value=2) as mock_run:
|
||||
assert Fixer.run_fixer([finding1, finding2]) == 2
|
||||
assert mock_run.called
|
||||
|
||||
def test_run_fixer_grouping(self):
|
||||
finding1 = build_finding(status="FAIL", check_id="check1")
|
||||
finding2 = build_finding(status="FAIL", check_id="check1")
|
||||
finding3 = build_finding(status="FAIL", check_id="check2")
|
||||
calls = {}
|
||||
|
||||
def fake_run_individual_fixer(check_id, findings):
|
||||
calls[check_id] = len(findings)
|
||||
return len(findings)
|
||||
|
||||
with patch.object(
|
||||
Fixer, "run_individual_fixer", side_effect=fake_run_individual_fixer
|
||||
):
|
||||
total = Fixer.run_fixer([finding1, finding2, finding3])
|
||||
assert total == 3
|
||||
assert calls == {"check1": 2, "check2": 1}
|
||||
|
||||
def test_run_fixer_exception(self):
|
||||
finding = build_finding(status="FAIL")
|
||||
with patch.object(Fixer, "run_individual_fixer", side_effect=Exception("fail")):
|
||||
with patch("prowler.lib.fix.fixer.logger") as mock_logger:
|
||||
assert Fixer.run_fixer(finding) == 0
|
||||
assert mock_logger.error.called
|
||||
|
||||
def test_run_individual_fixer_success(self):
|
||||
finding = build_finding(status="FAIL")
|
||||
with (
|
||||
patch.object(Fixer, "get_fixer_for_finding") as mock_factory,
|
||||
patch("builtins.print") as mock_print,
|
||||
):
|
||||
fixer = DummyFixer(description="desc")
|
||||
mock_factory.return_value = fixer
|
||||
with patch.object(fixer, "fix", return_value=True):
|
||||
total = Fixer.run_individual_fixer("test_check", [finding])
|
||||
assert total == 1
|
||||
assert mock_print.call_count > 0
|
||||
|
||||
def test_run_individual_fixer_no_fixer(self):
|
||||
finding = build_finding(status="FAIL")
|
||||
with patch.object(Fixer, "get_fixer_for_finding", return_value=None):
|
||||
assert Fixer.run_individual_fixer("test_check", [finding]) == 0
|
||||
|
||||
def test_run_individual_fixer_fix_error(self):
|
||||
finding = build_finding(status="FAIL")
|
||||
with (
|
||||
patch.object(Fixer, "get_fixer_for_finding") as mock_factory,
|
||||
patch("builtins.print") as mock_print,
|
||||
):
|
||||
fixer = DummyFixer(description="desc")
|
||||
mock_factory.return_value = fixer
|
||||
with patch.object(fixer, "fix", return_value=False):
|
||||
total = Fixer.run_individual_fixer("test_check", [finding])
|
||||
assert total == 0
|
||||
assert mock_print.call_count > 0
|
||||
|
||||
def test_run_individual_fixer_exception(self):
|
||||
finding = build_finding(status="FAIL")
|
||||
with patch.object(
|
||||
Fixer, "get_fixer_for_finding", side_effect=Exception("fail")
|
||||
):
|
||||
with patch("prowler.lib.fix.fixer.logger") as mock_logger:
|
||||
assert Fixer.run_individual_fixer("test_check", [finding]) == 0
|
||||
assert mock_logger.error.called
|
||||
104
tests/providers/aws/lib/fix/awsfixer_test.py
Normal file
104
tests/providers/aws/lib/fix/awsfixer_test.py
Normal file
@@ -0,0 +1,104 @@
|
||||
import json
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from prowler.lib.check.models import (
|
||||
Check_Report_AWS,
|
||||
CheckMetadata,
|
||||
Code,
|
||||
Recommendation,
|
||||
Remediation,
|
||||
)
|
||||
from prowler.providers.aws.lib.fix.fixer import AWSFixer
|
||||
|
||||
|
||||
def get_mock_aws_finding():
|
||||
metadata = CheckMetadata(
|
||||
Provider="aws",
|
||||
CheckID="test_check",
|
||||
CheckTitle="Test Check",
|
||||
CheckType=["type1"],
|
||||
CheckAliases=[],
|
||||
ServiceName="testservice",
|
||||
SubServiceName="",
|
||||
ResourceIdTemplate="",
|
||||
Severity="low",
|
||||
ResourceType="resource",
|
||||
Description="desc",
|
||||
Risk="risk",
|
||||
RelatedUrl="url",
|
||||
Remediation=Remediation(
|
||||
Code=Code(NativeIaC="", Terraform="", CLI="", Other=""),
|
||||
Recommendation=Recommendation(Text="", Url=""),
|
||||
),
|
||||
Categories=["cat1"],
|
||||
DependsOn=[],
|
||||
RelatedTo=[],
|
||||
Notes="",
|
||||
Compliance=[],
|
||||
)
|
||||
resource = MagicMock()
|
||||
resource.id = "res_id"
|
||||
resource.arn = "arn:aws:test"
|
||||
resource.region = "eu-west-1"
|
||||
return Check_Report_AWS(json.dumps(metadata.dict()), resource)
|
||||
|
||||
|
||||
class TestAWSFixer:
|
||||
def test_fix_success(self):
|
||||
finding = get_mock_aws_finding()
|
||||
finding.status = "FAIL"
|
||||
with patch("prowler.providers.aws.lib.fix.fixer.AWSFixer.client"):
|
||||
fixer = AWSFixer(description="desc", service="ec2")
|
||||
assert fixer.fix(finding=finding)
|
||||
|
||||
def test_fix_failure(self, caplog):
|
||||
fixer = AWSFixer(description="desc", service="ec2")
|
||||
with patch("prowler.providers.aws.lib.fix.fixer.logger") as mock_logger:
|
||||
with caplog.at_level("ERROR"):
|
||||
result = fixer.fix(finding=None)
|
||||
assert result is False
|
||||
assert mock_logger.error.called
|
||||
|
||||
def test_get_fixer_info(self):
|
||||
fixer = AWSFixer(
|
||||
description="desc",
|
||||
service="ec2",
|
||||
cost_impact=True,
|
||||
cost_description="cost",
|
||||
iam_policy_required={"Action": ["ec2:DescribeInstances"]},
|
||||
)
|
||||
info = fixer._get_fixer_info()
|
||||
assert info["description"] == "desc"
|
||||
assert info["cost_impact"] is True
|
||||
assert info["cost_description"] == "cost"
|
||||
assert info["service"] == "ec2"
|
||||
assert info["iam_policy_required"] == {"Action": ["ec2:DescribeInstances"]}
|
||||
|
||||
def test_fix_prints(self):
|
||||
fixer = AWSFixer(description="desc", service="ec2")
|
||||
finding = get_mock_aws_finding()
|
||||
finding.region = "eu-west-1"
|
||||
finding.resource_id = "res_id"
|
||||
finding.resource_arn = "arn:aws:test"
|
||||
with (
|
||||
patch("builtins.print") as mock_print,
|
||||
patch("prowler.providers.aws.lib.fix.fixer.logger") as mock_logger,
|
||||
):
|
||||
result = fixer.fix(finding=finding)
|
||||
if (
|
||||
finding.region
|
||||
or finding.resource_id
|
||||
or getattr(finding, "resource_arn", None)
|
||||
):
|
||||
assert result is True
|
||||
assert mock_print.called
|
||||
else:
|
||||
assert result is False
|
||||
assert mock_logger.error.called
|
||||
|
||||
def test_fix_exception(self):
|
||||
fixer = AWSFixer(description="desc", service="ec2")
|
||||
with patch("prowler.providers.aws.lib.fix.fixer.logger") as mock_logger:
|
||||
result = fixer.fix(finding=None)
|
||||
assert result is False
|
||||
assert mock_logger.error.called
|
||||
@@ -3,7 +3,7 @@ from unittest import mock
|
||||
from tests.providers.aws.utils import AWS_ACCOUNT_ARN, AWS_REGION_EU_WEST_1
|
||||
|
||||
|
||||
class Test_accessanalyzer_enabled_fixer:
|
||||
class TestAccessAnalyzerEnabledFixer:
|
||||
def test_accessanalyzer_enabled_fixer(self):
|
||||
regional_client = mock.MagicMock()
|
||||
accessanalyzer_client = mock.MagicMock()
|
||||
@@ -26,7 +26,7 @@ class Test_accessanalyzer_enabled_fixer:
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.accessanalyzer.accessanalyzer_enabled.accessanalyzer_enabled_fixer import (
|
||||
fixer,
|
||||
AccessAnalyzerEnabledFixer,
|
||||
)
|
||||
|
||||
assert fixer(AWS_REGION_EU_WEST_1)
|
||||
assert AccessAnalyzerEnabledFixer().fix(region=AWS_REGION_EU_WEST_1)
|
||||
|
||||
@@ -11,7 +11,7 @@ from tests.providers.aws.utils import (
|
||||
)
|
||||
|
||||
|
||||
class Test_awslambda_function_not_publicly_accessible_fixer:
|
||||
class TestLambdaFunctionNotPubliclyAccessibleFixer:
|
||||
@mock_aws
|
||||
def test_function_public(self):
|
||||
# Create the mock IAM role
|
||||
@@ -71,12 +71,12 @@ class Test_awslambda_function_not_publicly_accessible_fixer:
|
||||
new=Lambda(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible_fixer import (
|
||||
fixer,
|
||||
LambdaFunctionNotPubliclyAccessibleFixer,
|
||||
)
|
||||
|
||||
assert fixer(function_name, AWS_REGION_EU_WEST_1)
|
||||
fixer = LambdaFunctionNotPubliclyAccessibleFixer()
|
||||
assert fixer.fix(region=AWS_REGION_EU_WEST_1, resource_id=function_name)
|
||||
|
||||
@mock_aws
|
||||
def test_function_public_with_source_account(self):
|
||||
@@ -139,12 +139,12 @@ class Test_awslambda_function_not_publicly_accessible_fixer:
|
||||
new=Lambda(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible_fixer import (
|
||||
fixer,
|
||||
LambdaFunctionNotPubliclyAccessibleFixer,
|
||||
)
|
||||
|
||||
assert fixer(function_name, AWS_REGION_EU_WEST_1)
|
||||
fixer = LambdaFunctionNotPubliclyAccessibleFixer()
|
||||
assert fixer.fix(region=AWS_REGION_EU_WEST_1, resource_id=function_name)
|
||||
|
||||
@mock_aws
|
||||
def test_function_not_public(self):
|
||||
@@ -206,12 +206,12 @@ class Test_awslambda_function_not_publicly_accessible_fixer:
|
||||
new=Lambda(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible_fixer import (
|
||||
fixer,
|
||||
LambdaFunctionNotPubliclyAccessibleFixer,
|
||||
)
|
||||
|
||||
assert fixer(function_name, AWS_REGION_EU_WEST_1)
|
||||
fixer = LambdaFunctionNotPubliclyAccessibleFixer()
|
||||
assert fixer.fix(region=AWS_REGION_EU_WEST_1, resource_id=function_name)
|
||||
|
||||
@mock_aws
|
||||
def test_function_public_error(self):
|
||||
@@ -272,9 +272,9 @@ class Test_awslambda_function_not_publicly_accessible_fixer:
|
||||
new=Lambda(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible_fixer import (
|
||||
fixer,
|
||||
LambdaFunctionNotPubliclyAccessibleFixer,
|
||||
)
|
||||
|
||||
assert not fixer("non-exsting", AWS_REGION_EU_WEST_1)
|
||||
fixer = LambdaFunctionNotPubliclyAccessibleFixer()
|
||||
assert not fixer.fix(region=AWS_REGION_EU_WEST_1, resource_id="non-exsting")
|
||||
|
||||
@@ -28,7 +28,7 @@ def mock_make_api_call_error(self, operation_name, kwarg):
|
||||
return mock_make_api_call(self, operation_name, kwarg)
|
||||
|
||||
|
||||
class Test_cloudtrail_logs_s3_bucket_is_not_publicly_accessible_fixer:
|
||||
class TestCloudtrailLogsS3BucketIsNotPubliclyAccessibleFixer:
|
||||
@mock_aws
|
||||
def test_trail_bucket_public_acl(self):
|
||||
aws_provider = set_mocked_aws_provider(
|
||||
@@ -81,12 +81,12 @@ class Test_cloudtrail_logs_s3_bucket_is_not_publicly_accessible_fixer:
|
||||
new=S3(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_logs_s3_bucket_is_not_publicly_accessible_fixer import (
|
||||
fixer,
|
||||
CloudtrailLogsS3BucketIsNotPubliclyAccessibleFixer,
|
||||
)
|
||||
|
||||
assert fixer(trail_name_us, AWS_REGION_US_EAST_1)
|
||||
fixer = CloudtrailLogsS3BucketIsNotPubliclyAccessibleFixer()
|
||||
assert fixer.fix(region=AWS_REGION_US_EAST_1, resource_id=trail_name_us)
|
||||
|
||||
@mock_aws
|
||||
def test_trail_bucket_public_acl_error(self):
|
||||
@@ -145,9 +145,11 @@ class Test_cloudtrail_logs_s3_bucket_is_not_publicly_accessible_fixer:
|
||||
new=S3(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_logs_s3_bucket_is_not_publicly_accessible.cloudtrail_logs_s3_bucket_is_not_publicly_accessible_fixer import (
|
||||
fixer,
|
||||
CloudtrailLogsS3BucketIsNotPubliclyAccessibleFixer,
|
||||
)
|
||||
|
||||
assert not fixer(trail_name_us, AWS_REGION_US_EAST_1)
|
||||
fixer = CloudtrailLogsS3BucketIsNotPubliclyAccessibleFixer()
|
||||
assert not fixer.fix(
|
||||
region=AWS_REGION_US_EAST_1, resource_id=trail_name_us
|
||||
)
|
||||
|
||||
@@ -6,7 +6,7 @@ from moto import mock_aws
|
||||
from tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider
|
||||
|
||||
|
||||
class Test_cloudtrail_multi_region_enabled_fixer:
|
||||
class TestCloudtrailMultiRegionEnabledFixer:
|
||||
@mock_aws
|
||||
def test_cloudtrail_multi_region_enabled_fixer(self):
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
|
||||
@@ -36,9 +36,9 @@ class Test_cloudtrail_multi_region_enabled_fixer:
|
||||
"prowler.providers.aws.services.cloudtrail.cloudtrail_multi_region_enabled.cloudtrail_multi_region_enabled_fixer.cloudtrail_client",
|
||||
new=Cloudtrail(aws_provider),
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.cloudtrail.cloudtrail_multi_region_enabled.cloudtrail_multi_region_enabled_fixer import (
|
||||
fixer,
|
||||
CloudtrailMultiRegionEnabledFixer,
|
||||
)
|
||||
|
||||
assert fixer(AWS_REGION_US_EAST_1)
|
||||
fixer = CloudtrailMultiRegionEnabledFixer()
|
||||
assert fixer.fix(region=AWS_REGION_US_EAST_1)
|
||||
|
||||
@@ -38,7 +38,7 @@ def mock_make_api_call_codeartifact_error(self, operation_name, kwarg):
|
||||
return mock_make_api_call(self, operation_name, kwarg)
|
||||
|
||||
|
||||
class Test_codeartifact_packages_external_public_publishing_disabled_fixer:
|
||||
class TestCodeartifactPackagesExternalPublicPublishingDisabledFixer:
|
||||
@mock_aws
|
||||
def test_repository_package_public_publishing_origin_internal(self):
|
||||
with mock.patch(
|
||||
@@ -57,12 +57,14 @@ class Test_codeartifact_packages_external_public_publishing_disabled_fixer:
|
||||
new=CodeArtifact(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.codeartifact.codeartifact_packages_external_public_publishing_disabled.codeartifact_packages_external_public_publishing_disabled_fixer import (
|
||||
fixer,
|
||||
CodeartifactPackagesExternalPublicPublishingDisabledFixer,
|
||||
)
|
||||
|
||||
assert fixer("test/test-package", AWS_REGION_EU_WEST_1)
|
||||
fixer = CodeartifactPackagesExternalPublicPublishingDisabledFixer()
|
||||
assert fixer.fix(
|
||||
region=AWS_REGION_EU_WEST_1, resource_id="test/test-package"
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_repository_package_public_publishing_origin_internal_error(self):
|
||||
@@ -70,7 +72,6 @@ class Test_codeartifact_packages_external_public_publishing_disabled_fixer:
|
||||
"botocore.client.BaseClient._make_api_call",
|
||||
new=mock_make_api_call_codeartifact_error,
|
||||
):
|
||||
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
|
||||
|
||||
with (
|
||||
@@ -83,9 +84,11 @@ class Test_codeartifact_packages_external_public_publishing_disabled_fixer:
|
||||
new=CodeArtifact(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.codeartifact.codeartifact_packages_external_public_publishing_disabled.codeartifact_packages_external_public_publishing_disabled_fixer import (
|
||||
fixer,
|
||||
CodeartifactPackagesExternalPublicPublishingDisabledFixer,
|
||||
)
|
||||
|
||||
assert not fixer("non-existing-package", AWS_REGION_EU_WEST_1)
|
||||
fixer = CodeartifactPackagesExternalPublicPublishingDisabledFixer()
|
||||
assert not fixer.fix(
|
||||
region=AWS_REGION_EU_WEST_1, resource_id="non-existing-package"
|
||||
)
|
||||
|
||||
@@ -39,7 +39,7 @@ def mock_make_api_call_public_snapshot_error(self, operation_name, kwarg):
|
||||
return mock_make_api_call(self, operation_name, kwarg)
|
||||
|
||||
|
||||
class Test_documentdb_cluster_public_snapshot_fixer:
|
||||
class TestDocumentdbClusterPublicSnapshotFixer:
|
||||
@mock_aws
|
||||
def test_documentdb_cluster_public_snapshot_fixer(self):
|
||||
with mock.patch(
|
||||
@@ -63,10 +63,13 @@ class Test_documentdb_cluster_public_snapshot_fixer:
|
||||
),
|
||||
):
|
||||
from prowler.providers.aws.services.documentdb.documentdb_cluster_public_snapshot.documentdb_cluster_public_snapshot_fixer import (
|
||||
fixer,
|
||||
DocumentdbClusterPublicSnapshotFixer,
|
||||
)
|
||||
|
||||
assert fixer(resource_id="test-snapshot", region=AWS_REGION_EU_WEST_1)
|
||||
fixer = DocumentdbClusterPublicSnapshotFixer()
|
||||
assert fixer.fix(
|
||||
region=AWS_REGION_EU_WEST_1, resource_id="test-snapshot"
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_documentdb_cluster_public_snapshot_fixer_error(self):
|
||||
@@ -91,9 +94,10 @@ class Test_documentdb_cluster_public_snapshot_fixer:
|
||||
),
|
||||
):
|
||||
from prowler.providers.aws.services.documentdb.documentdb_cluster_public_snapshot.documentdb_cluster_public_snapshot_fixer import (
|
||||
fixer,
|
||||
DocumentdbClusterPublicSnapshotFixer,
|
||||
)
|
||||
|
||||
assert not fixer(
|
||||
resource_id="test-snapshot", region=AWS_REGION_EU_WEST_1
|
||||
fixer = DocumentdbClusterPublicSnapshotFixer()
|
||||
assert not fixer.fix(
|
||||
region=AWS_REGION_EU_WEST_1, resource_id="test-snapshot"
|
||||
)
|
||||
|
||||
@@ -12,7 +12,7 @@ from tests.providers.aws.utils import (
|
||||
EXAMPLE_AMI_ID = "ami-12c6146b"
|
||||
|
||||
|
||||
class Test_ec2_ami_public_fixer:
|
||||
class TestEc2AmiPublicFixer:
|
||||
@mock_aws
|
||||
def test_one_private_ami(self):
|
||||
ec2 = client("ec2", region_name=AWS_REGION_US_EAST_1)
|
||||
@@ -42,10 +42,11 @@ class Test_ec2_ami_public_fixer:
|
||||
),
|
||||
):
|
||||
from prowler.providers.aws.services.ec2.ec2_ami_public.ec2_ami_public_fixer import (
|
||||
fixer,
|
||||
Ec2AmiPublicFixer,
|
||||
)
|
||||
|
||||
assert fixer(image_id, AWS_REGION_US_EAST_1)
|
||||
fixer = Ec2AmiPublicFixer()
|
||||
assert fixer.fix(region=AWS_REGION_US_EAST_1, resource_id=image_id)
|
||||
|
||||
@mock_aws
|
||||
def test_one_public_ami(self):
|
||||
@@ -85,10 +86,11 @@ class Test_ec2_ami_public_fixer:
|
||||
),
|
||||
):
|
||||
from prowler.providers.aws.services.ec2.ec2_ami_public.ec2_ami_public_fixer import (
|
||||
fixer,
|
||||
Ec2AmiPublicFixer,
|
||||
)
|
||||
|
||||
assert fixer(image_id, AWS_REGION_US_EAST_1)
|
||||
fixer = Ec2AmiPublicFixer()
|
||||
assert fixer.fix(region=AWS_REGION_US_EAST_1, resource_id=image_id)
|
||||
|
||||
@mock_aws
|
||||
def test_one_public_ami_error(self):
|
||||
@@ -128,7 +130,10 @@ class Test_ec2_ami_public_fixer:
|
||||
),
|
||||
):
|
||||
from prowler.providers.aws.services.ec2.ec2_ami_public.ec2_ami_public_fixer import (
|
||||
fixer,
|
||||
Ec2AmiPublicFixer,
|
||||
)
|
||||
|
||||
assert not fixer("image_id_non_existing", AWS_REGION_US_EAST_1)
|
||||
fixer = Ec2AmiPublicFixer()
|
||||
assert not fixer.fix(
|
||||
region=AWS_REGION_US_EAST_1, resource_id="image_id_non_existing"
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ from moto import mock_aws
|
||||
from tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider
|
||||
|
||||
|
||||
class Test_ec2_ebs_default_encryption_fixer:
|
||||
class TestEc2EbsDefaultEncryptionFixer:
|
||||
@mock_aws
|
||||
def test_ec2_ebs_encryption_fixer(self):
|
||||
from prowler.providers.aws.services.ec2.ec2_service import EC2
|
||||
@@ -23,8 +23,8 @@ class Test_ec2_ebs_default_encryption_fixer:
|
||||
),
|
||||
):
|
||||
from prowler.providers.aws.services.ec2.ec2_ebs_default_encryption.ec2_ebs_default_encryption_fixer import (
|
||||
fixer,
|
||||
Ec2EbsDefaultEncryptionFixer,
|
||||
)
|
||||
|
||||
# By default, the account has not public access blocked
|
||||
assert fixer(region=AWS_REGION_US_EAST_1)
|
||||
fixer = Ec2EbsDefaultEncryptionFixer()
|
||||
assert fixer.fix(region=AWS_REGION_US_EAST_1)
|
||||
|
||||
@@ -38,14 +38,13 @@ def mock_make_api_call_error(self, operation_name, kwarg):
|
||||
return mock_make_api_call(self, operation_name, kwarg)
|
||||
|
||||
|
||||
class Test_ec2_ebs_public_snapshot_fixer_test:
|
||||
class TestEc2EbsPublicSnapshotFixer:
|
||||
@mock_aws
|
||||
def test_ebs_public_snapshot(self):
|
||||
with mock.patch(
|
||||
"botocore.client.BaseClient._make_api_call",
|
||||
new=mock_make_api_call_public_snapshot,
|
||||
):
|
||||
|
||||
from prowler.providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
aws_provider = set_mocked_aws_provider(
|
||||
@@ -62,19 +61,18 @@ class Test_ec2_ebs_public_snapshot_fixer_test:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.ec2.ec2_ebs_public_snapshot.ec2_ebs_public_snapshot_fixer import (
|
||||
fixer,
|
||||
Ec2EbsPublicSnapshotFixer,
|
||||
)
|
||||
|
||||
assert fixer("testsnap", AWS_REGION_US_EAST_1)
|
||||
fixer = Ec2EbsPublicSnapshotFixer()
|
||||
assert fixer.fix(region=AWS_REGION_US_EAST_1, resource_id="testsnap")
|
||||
|
||||
@mock_aws
|
||||
def test_ebs_public_snapshot_error(self):
|
||||
with mock.patch(
|
||||
"botocore.client.BaseClient._make_api_call", new=mock_make_api_call_error
|
||||
):
|
||||
|
||||
from prowler.providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
aws_provider = set_mocked_aws_provider(
|
||||
@@ -91,9 +89,11 @@ class Test_ec2_ebs_public_snapshot_fixer_test:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.ec2.ec2_ebs_public_snapshot.ec2_ebs_public_snapshot_fixer import (
|
||||
fixer,
|
||||
Ec2EbsPublicSnapshotFixer,
|
||||
)
|
||||
|
||||
assert not fixer("testsnap", AWS_REGION_US_EAST_1)
|
||||
fixer = Ec2EbsPublicSnapshotFixer()
|
||||
assert not fixer.fix(
|
||||
region=AWS_REGION_US_EAST_1, resource_id="testsnap"
|
||||
)
|
||||
|
||||
@@ -26,7 +26,7 @@ def mock_enable_snapshot_block_public_access(State):
|
||||
return {"State": State}
|
||||
|
||||
|
||||
class Test_ec2_ebs_snapshot_account_block_public_access_fixer:
|
||||
class TestEc2EbsSnapshotAccountBlockPublicAccessFixer:
|
||||
@mock_aws
|
||||
def test_ec2_ebs_snapshot_account_block_public_access_fixer(self):
|
||||
ec2_service = mock.MagicMock()
|
||||
@@ -61,10 +61,9 @@ class Test_ec2_ebs_snapshot_account_block_public_access_fixer:
|
||||
ec2_service,
|
||||
),
|
||||
):
|
||||
|
||||
from prowler.providers.aws.services.ec2.ec2_ebs_snapshot_account_block_public_access.ec2_ebs_snapshot_account_block_public_access_fixer import (
|
||||
fixer,
|
||||
Ec2EbsSnapshotAccountBlockPublicAccessFixer,
|
||||
)
|
||||
|
||||
# By default, the account has not public access blocked
|
||||
assert fixer(region=AWS_REGION_US_EAST_1)
|
||||
fixer = Ec2EbsSnapshotAccountBlockPublicAccessFixer()
|
||||
assert fixer.fix(region=AWS_REGION_US_EAST_1)
|
||||
|
||||
@@ -27,7 +27,7 @@ def mock_modify_instance_metadata_defaults(HttpTokens):
|
||||
return {"Return": True}
|
||||
|
||||
|
||||
class Test_ec2_instance_account_imdsv2_enabled_fixer:
|
||||
class TestEc2InstanceAccountImdsv2EnabledFixer:
|
||||
@mock_aws
|
||||
def test_ec2_instance_account_imdsv2_enabled_fixer(self):
|
||||
ec2_service = mock.MagicMock()
|
||||
@@ -64,8 +64,9 @@ class Test_ec2_instance_account_imdsv2_enabled_fixer:
|
||||
):
|
||||
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_account_imdsv2_enabled.ec2_instance_account_imdsv2_enabled_fixer import (
|
||||
fixer,
|
||||
Ec2InstanceAccountImdsv2EnabledFixer,
|
||||
)
|
||||
|
||||
# By default, the account has not public access blocked
|
||||
assert fixer(region=AWS_REGION_US_EAST_1)
|
||||
assert Ec2InstanceAccountImdsv2EnabledFixer().fix(
|
||||
region=AWS_REGION_US_EAST_1
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ def mock_make_api_call_error(self, operation_name, kwarg):
|
||||
return mock_make_api_call(self, operation_name, kwarg)
|
||||
|
||||
|
||||
class Test_ec2_instance_port_cassandra_exposed_to_internet_fixer:
|
||||
class TestEc2InstancePortCassandraExposedToInternetFixer:
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_with_ip4_and_ip6(self):
|
||||
# Create EC2 Mocked Resources
|
||||
@@ -83,12 +83,13 @@ class Test_ec2_instance_port_cassandra_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_cassandra_exposed_to_internet.ec2_instance_port_cassandra_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortCassandraExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortCassandraExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_error(self):
|
||||
@@ -166,12 +167,13 @@ class Test_ec2_instance_port_cassandra_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_cassandra_exposed_to_internet.ec2_instance_port_cassandra_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortCassandraExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert not fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert not Ec2InstancePortCassandraExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_only_with_ip4(self):
|
||||
@@ -223,12 +225,13 @@ class Test_ec2_instance_port_cassandra_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_cassandra_exposed_to_internet.ec2_instance_port_cassandra_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortCassandraExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortCassandraExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_only_with_ip6(self):
|
||||
@@ -280,12 +283,13 @@ class Test_ec2_instance_port_cassandra_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_cassandra_exposed_to_internet.ec2_instance_port_cassandra_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortCassandraExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortCassandraExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_public_subnet_all_ports(self):
|
||||
@@ -343,9 +347,10 @@ class Test_ec2_instance_port_cassandra_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_cassandra_exposed_to_internet.ec2_instance_port_cassandra_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortCassandraExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance.id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortCassandraExposedToInternetFixer().fix(
|
||||
resource_id=instance.id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ def mock_make_api_call_error(self, operation_name, kwarg):
|
||||
return mock_make_api_call(self, operation_name, kwarg)
|
||||
|
||||
|
||||
class Test_ec2_instance_port_cifs_exposed_to_internet_fixer:
|
||||
class TestEc2InstancePortCifsExposedToInternetFixer:
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_with_ip4_and_ip6(self):
|
||||
# Create EC2 Mocked Resources
|
||||
@@ -83,12 +83,13 @@ class Test_ec2_instance_port_cifs_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_cifs_exposed_to_internet.ec2_instance_port_cifs_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortCifsExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortCifsExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_error(self):
|
||||
@@ -166,12 +167,13 @@ class Test_ec2_instance_port_cifs_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_cifs_exposed_to_internet.ec2_instance_port_cifs_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortCifsExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert not fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert not Ec2InstancePortCifsExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_only_with_ip4(self):
|
||||
@@ -229,12 +231,13 @@ class Test_ec2_instance_port_cifs_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_cifs_exposed_to_internet.ec2_instance_port_cifs_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortCifsExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortCifsExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_only_with_ip6(self):
|
||||
@@ -292,12 +295,13 @@ class Test_ec2_instance_port_cifs_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_cifs_exposed_to_internet.ec2_instance_port_cifs_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortCifsExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortCifsExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_public_subnet_only_139_port(self):
|
||||
@@ -355,12 +359,13 @@ class Test_ec2_instance_port_cifs_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_cifs_exposed_to_internet.ec2_instance_port_cifs_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortCifsExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance.id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortCifsExposedToInternetFixer().fix(
|
||||
resource_id=instance.id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_with_public_ip_in_public_subnet_only_445_port(
|
||||
@@ -440,9 +445,10 @@ class Test_ec2_instance_port_cifs_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_cifs_exposed_to_internet.ec2_instance_port_cifs_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortCifsExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance.id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortCifsExposedToInternetFixer().fix(
|
||||
resource_id=instance.id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ def mock_make_api_call_error(self, operation_name, kwarg):
|
||||
return mock_make_api_call(self, operation_name, kwarg)
|
||||
|
||||
|
||||
class Test_ec2_instance_port_elasticsearch_kibana_exposed_to_internet_fixer:
|
||||
class TestEc2InstancePortElasticsearchKibanaExposedToInternetFixer:
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_with_ip4_and_ip6(self):
|
||||
# Create EC2 Mocked Resources
|
||||
@@ -83,12 +83,13 @@ class Test_ec2_instance_port_elasticsearch_kibana_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_elasticsearch_kibana_exposed_to_internet.ec2_instance_port_elasticsearch_kibana_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortElasticsearchKibanaExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortElasticsearchKibanaExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_error(self):
|
||||
@@ -166,12 +167,15 @@ class Test_ec2_instance_port_elasticsearch_kibana_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_elasticsearch_kibana_exposed_to_internet.ec2_instance_port_elasticsearch_kibana_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortElasticsearchKibanaExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert not fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert (
|
||||
not Ec2InstancePortElasticsearchKibanaExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_only_with_ip4(self):
|
||||
@@ -229,12 +233,13 @@ class Test_ec2_instance_port_elasticsearch_kibana_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_elasticsearch_kibana_exposed_to_internet.ec2_instance_port_elasticsearch_kibana_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortElasticsearchKibanaExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortElasticsearchKibanaExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_only_with_ip6(self):
|
||||
@@ -292,12 +297,13 @@ class Test_ec2_instance_port_elasticsearch_kibana_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_elasticsearch_kibana_exposed_to_internet.ec2_instance_port_elasticsearch_kibana_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortElasticsearchKibanaExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortElasticsearchKibanaExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_public_subnet_only_9200_9300_port(self):
|
||||
@@ -355,12 +361,13 @@ class Test_ec2_instance_port_elasticsearch_kibana_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_elasticsearch_kibana_exposed_to_internet.ec2_instance_port_elasticsearch_kibana_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortElasticsearchKibanaExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance.id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortElasticsearchKibanaExposedToInternetFixer().fix(
|
||||
resource_id=instance.id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_with_public_ip_in_public_subnet_only_5601_port(
|
||||
@@ -440,9 +447,10 @@ class Test_ec2_instance_port_elasticsearch_kibana_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_elasticsearch_kibana_exposed_to_internet.ec2_instance_port_elasticsearch_kibana_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortElasticsearchKibanaExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance.id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortElasticsearchKibanaExposedToInternetFixer().fix(
|
||||
resource_id=instance.id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ def mock_make_api_call_error(self, operation_name, kwarg):
|
||||
return mock_make_api_call(self, operation_name, kwarg)
|
||||
|
||||
|
||||
class Test_ec2_instance_port_ftp_exposed_to_internet_fixer:
|
||||
class TestEc2InstancePortFtpExposedToInternetFixer:
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_with_ip4_and_ip6(self):
|
||||
# Create EC2 Mocked Resources
|
||||
@@ -83,12 +83,13 @@ class Test_ec2_instance_port_ftp_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_ftp_exposed_to_internet.ec2_instance_port_ftp_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortFtpExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortFtpExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_error(self):
|
||||
@@ -166,12 +167,13 @@ class Test_ec2_instance_port_ftp_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_ftp_exposed_to_internet.ec2_instance_port_ftp_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortFtpExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert not fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert not Ec2InstancePortFtpExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_only_with_ip4(self):
|
||||
@@ -229,12 +231,13 @@ class Test_ec2_instance_port_ftp_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_ftp_exposed_to_internet.ec2_instance_port_ftp_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortFtpExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortFtpExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_only_with_ip6(self):
|
||||
@@ -292,12 +295,13 @@ class Test_ec2_instance_port_ftp_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_ftp_exposed_to_internet.ec2_instance_port_ftp_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortFtpExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortFtpExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_public_subnet_only_both_ports(self):
|
||||
@@ -355,9 +359,10 @@ class Test_ec2_instance_port_ftp_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_ftp_exposed_to_internet.ec2_instance_port_ftp_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortFtpExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance.id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortFtpExposedToInternetFixer().fix(
|
||||
resource_id=instance.id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ def mock_make_api_call_error(self, operation_name, kwarg):
|
||||
return mock_make_api_call(self, operation_name, kwarg)
|
||||
|
||||
|
||||
class Test_ec2_instance_port_kafka_exposed_to_internet_fixer:
|
||||
class TestEc2InstancePortKafkaExposedToInternetFixer:
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_with_ip4_and_ip6(self):
|
||||
# Create EC2 Mocked Resources
|
||||
@@ -83,12 +83,13 @@ class Test_ec2_instance_port_kafka_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_kafka_exposed_to_internet.ec2_instance_port_kafka_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortKafkaExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortKafkaExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_error(self):
|
||||
@@ -153,12 +154,13 @@ class Test_ec2_instance_port_kafka_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_kafka_exposed_to_internet.ec2_instance_port_kafka_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortKafkaExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert not fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert not Ec2InstancePortKafkaExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_only_with_ip4(self):
|
||||
@@ -210,12 +212,13 @@ class Test_ec2_instance_port_kafka_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_kafka_exposed_to_internet.ec2_instance_port_kafka_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortKafkaExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortKafkaExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_only_with_ip6(self):
|
||||
@@ -267,12 +270,13 @@ class Test_ec2_instance_port_kafka_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_kafka_exposed_to_internet.ec2_instance_port_kafka_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortKafkaExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortKafkaExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_public_subnet_only_one_port(self):
|
||||
@@ -330,9 +334,10 @@ class Test_ec2_instance_port_kafka_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_kafka_exposed_to_internet.ec2_instance_port_kafka_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortKafkaExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance.id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortKafkaExposedToInternetFixer().fix(
|
||||
resource_id=instance.id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ def mock_make_api_call_error(self, operation_name, kwarg):
|
||||
return mock_make_api_call(self, operation_name, kwarg)
|
||||
|
||||
|
||||
class Test_ec2_instance_port_kerberos_exposed_to_internet_fixer:
|
||||
class TestEc2InstancePortKerberosExposedToInternetFixer:
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_with_ip4_and_ip6(self):
|
||||
# Create EC2 Mocked Resources
|
||||
@@ -83,12 +83,13 @@ class Test_ec2_instance_port_kerberos_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_kerberos_exposed_to_internet.ec2_instance_port_kerberos_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortKerberosExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortKerberosExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_error(self):
|
||||
@@ -166,12 +167,13 @@ class Test_ec2_instance_port_kerberos_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_kerberos_exposed_to_internet.ec2_instance_port_kerberos_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortKerberosExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert not fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert not Ec2InstancePortKerberosExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_only_with_ip4(self):
|
||||
@@ -229,12 +231,13 @@ class Test_ec2_instance_port_kerberos_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_kerberos_exposed_to_internet.ec2_instance_port_kerberos_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortKerberosExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortKerberosExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_only_with_ip6(self):
|
||||
@@ -292,12 +295,13 @@ class Test_ec2_instance_port_kerberos_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_kerberos_exposed_to_internet.ec2_instance_port_kerberos_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortKerberosExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortKerberosExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_public_subnet_only_both_ports(self):
|
||||
@@ -355,9 +359,10 @@ class Test_ec2_instance_port_kerberos_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_kerberos_exposed_to_internet.ec2_instance_port_kerberos_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortKerberosExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance.id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortKerberosExposedToInternetFixer().fix(
|
||||
resource_id=instance.id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ def mock_make_api_call_error(self, operation_name, kwarg):
|
||||
return mock_make_api_call(self, operation_name, kwarg)
|
||||
|
||||
|
||||
class Test_ec2_instance_port_ldap_exposed_to_internet_fixer:
|
||||
class TestEc2InstancePortLdapExposedToInternetFixer:
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_with_ip4_and_ip6(self):
|
||||
# Create EC2 Mocked Resources
|
||||
@@ -47,8 +47,8 @@ class Test_ec2_instance_port_ldap_exposed_to_internet_fixer:
|
||||
},
|
||||
{
|
||||
"IpProtocol": "tcp",
|
||||
"FromPort": 445,
|
||||
"ToPort": 445,
|
||||
"FromPort": 389,
|
||||
"ToPort": 636,
|
||||
"IpRanges": [{"CidrIp": "0.0.0.0/0"}, {"CidrIp": "10.0.0.0/24"}],
|
||||
"Ipv6Ranges": [{"CidrIpv6": "::/0"}, {"CidrIpv6": "2001:db8::/32"}],
|
||||
},
|
||||
@@ -83,12 +83,13 @@ class Test_ec2_instance_port_ldap_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_ldap_exposed_to_internet.ec2_instance_port_ldap_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortLdapExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortLdapExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_error(self):
|
||||
@@ -166,12 +167,13 @@ class Test_ec2_instance_port_ldap_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_ldap_exposed_to_internet.ec2_instance_port_ldap_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortLdapExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert not fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert not Ec2InstancePortLdapExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_only_with_ip4(self):
|
||||
@@ -229,12 +231,13 @@ class Test_ec2_instance_port_ldap_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_ldap_exposed_to_internet.ec2_instance_port_ldap_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortLdapExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortLdapExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_only_with_ip6(self):
|
||||
@@ -292,12 +295,13 @@ class Test_ec2_instance_port_ldap_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_ldap_exposed_to_internet.ec2_instance_port_ldap_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortLdapExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortLdapExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_public_subnet_only_bot_ports(self):
|
||||
@@ -355,9 +359,10 @@ class Test_ec2_instance_port_ldap_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_ldap_exposed_to_internet.ec2_instance_port_ldap_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortLdapExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance.id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortLdapExposedToInternetFixer().fix(
|
||||
resource_id=instance.id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ def mock_make_api_call_error(self, operation_name, kwarg):
|
||||
return mock_make_api_call(self, operation_name, kwarg)
|
||||
|
||||
|
||||
class Test_ec2_instance_port_memcached_exposed_to_internet_fixer:
|
||||
class TestEc2InstancePortMemcachedExposedToInternetFixer:
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_with_ip4_and_ip6(self):
|
||||
# Create EC2 Mocked Resources
|
||||
@@ -83,12 +83,13 @@ class Test_ec2_instance_port_memcached_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_memcached_exposed_to_internet.ec2_instance_port_memcached_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortMemcachedExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortMemcachedExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_error(self):
|
||||
@@ -166,12 +167,13 @@ class Test_ec2_instance_port_memcached_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_memcached_exposed_to_internet.ec2_instance_port_memcached_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortMemcachedExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert not fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert not Ec2InstancePortMemcachedExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_only_with_ip4(self):
|
||||
@@ -223,12 +225,13 @@ class Test_ec2_instance_port_memcached_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_memcached_exposed_to_internet.ec2_instance_port_memcached_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortMemcachedExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortMemcachedExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_only_with_ip6(self):
|
||||
@@ -280,12 +283,13 @@ class Test_ec2_instance_port_memcached_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_memcached_exposed_to_internet.ec2_instance_port_memcached_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortMemcachedExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortMemcachedExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_public_subnet_only_11211_port(self):
|
||||
@@ -343,9 +347,10 @@ class Test_ec2_instance_port_memcached_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_memcached_exposed_to_internet.ec2_instance_port_memcached_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortMemcachedExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance.id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortMemcachedExposedToInternetFixer().fix(
|
||||
resource_id=instance.id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ def mock_make_api_call_error(self, operation_name, kwarg):
|
||||
return mock_make_api_call(self, operation_name, kwarg)
|
||||
|
||||
|
||||
class Test_ec2_instance_port_mongodb_exposed_to_internet_fixer:
|
||||
class TestEc2InstancePortMongodbExposedToInternetFixer:
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_with_ip4_and_ip6(self):
|
||||
# Create EC2 Mocked Resources
|
||||
@@ -83,12 +83,13 @@ class Test_ec2_instance_port_mongodb_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_mongodb_exposed_to_internet.ec2_instance_port_mongodb_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortMongodbExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortMongodbExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_error(self):
|
||||
@@ -166,12 +167,13 @@ class Test_ec2_instance_port_mongodb_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_mongodb_exposed_to_internet.ec2_instance_port_mongodb_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortMongodbExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert not fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert not Ec2InstancePortMongodbExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_only_with_ip4(self):
|
||||
@@ -229,12 +231,13 @@ class Test_ec2_instance_port_mongodb_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_mongodb_exposed_to_internet.ec2_instance_port_mongodb_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortMongodbExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortMongodbExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_private_subnet_only_with_ip6(self):
|
||||
@@ -292,12 +295,13 @@ class Test_ec2_instance_port_mongodb_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_mongodb_exposed_to_internet.ec2_instance_port_mongodb_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortMongodbExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance_id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortMongodbExposedToInternetFixer().fix(
|
||||
resource_id=instance_id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_in_public_subnet_only_27017_port(self):
|
||||
@@ -355,12 +359,13 @@ class Test_ec2_instance_port_mongodb_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_mongodb_exposed_to_internet.ec2_instance_port_mongodb_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortMongodbExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance.id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortMongodbExposedToInternetFixer().fix(
|
||||
resource_id=instance.id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_instance_exposed_port_with_public_ip_in_public_subnet_only_27018_port(
|
||||
@@ -440,9 +445,10 @@ class Test_ec2_instance_port_mongodb_exposed_to_internet_fixer:
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Fixer
|
||||
from prowler.providers.aws.services.ec2.ec2_instance_port_mongodb_exposed_to_internet.ec2_instance_port_mongodb_exposed_to_internet_fixer import (
|
||||
fixer,
|
||||
Ec2InstancePortMongodbExposedToInternetFixer,
|
||||
)
|
||||
|
||||
assert fixer(instance.id, AWS_REGION_EU_WEST_1)
|
||||
assert Ec2InstancePortMongodbExposedToInternetFixer().fix(
|
||||
resource_id=instance.id, region=AWS_REGION_EU_WEST_1
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user