Files
prowler/docs/tutorials/fixer.md
T
2025-07-15 12:32:58 +02:00

6.9 KiB
Raw Blame History

Prowler Fixers (remediations)

Prowler supports automated remediation ("fixers") for certain findings. This system is extensible and provider-agnostic, allowing you to implement fixers for AWS, Azure, GCP, and M365 using a unified interface.


Overview

  • Fixers are Python classes that encapsulate the logic to remediate a failed check.
  • Each provider has its own base fixer class, inheriting from a common abstract base (Fixer).
  • Fixers are automatically discovered and invoked by Prowler when the --fixer flag is used.

???+ note Right now, fixers are only available through the CLI.


How to Use Fixers

To run fixers for failed findings:

prowler <provider> -c <check_id_1> <check_id_2> ... --fixer

To list all available fixers for a provider:

prowler <provider> --list-fixers

Note: Some fixers may incur additional costs (e.g., enabling certain cloud services like Access Analyzer, GuardDuty, and SecurityHub in AWS).


Fixer Class Structure

Base Class

All fixers inherit from the abstract Fixer class (prowler/lib/fix/fixer.py). This class defines the required interface and common logic.

Key methods and properties:

  • __init__(description, cost_impact=False, cost_description=None): Sets metadata for the fixer.
  • _get_fixer_info(): Returns a dictionary with fixer metadata.
  • fix(finding=None, **kwargs): Abstract method. Must be implemented by each fixer to perform the remediation.
  • get_fixer_for_finding(finding): Factory method to dynamically load the correct fixer for a finding.
  • run_fixer(findings): Runs the fixer(s) for one or more findings.

Provider-Specific Base Classes

Each provider extends the base class to add provider-specific logic and metadata:

  • AWS: AWSFixer (prowler/providers/aws/lib/fix/fixer.py)
  • Azure: AzureFixer (prowler/providers/azure/lib/fix/fixer.py)
  • GCP: GCPFixer (prowler/providers/gcp/lib/fix/fixer.py)
  • M365: M365Fixer (prowler/providers/m365/lib/fix/fixer.py)

These classes may add fields such as required permissions, IAM policies, or provider-specific client handling.


Writing a Fixer

1. Location and Naming

  • Place your fixer in the checks directory, named <check_id>_fixer.py.
  • The fixer class should be named in PascalCase, matching the check ID, ending with Fixer. Example: For ec2_ebs_default_encryption, use Ec2EbsDefaultEncryptionFixer.

2. Class Definition

  • Inherit from the providers base fixer class.
  • Implement the fix() method. This method receives a finding and/or keyword arguments and must return True if the remediation was successful, False otherwise.

Example (AWS):

from prowler.providers.aws.lib.fix.fixer import AWSFixer

class Ec2EbsDefaultEncryptionFixer(AWSFixer):
    def __init__(self):
        super().__init__(
            description="Enable EBS encryption by default in a region.",
            service="ec2",
            iam_policy_required={
                "Action": ["ec2:EnableEbsEncryptionByDefault"],
                "Resource": "*"
            }
        )

    def fix(self, finding=None, **kwargs):
        # Remediation logic here
        return True

Example (Azure):

from prowler.providers.azure.lib.fix.fixer import AzureFixer

class AppFunctionFtpsDeploymentDisabledFixer(AzureFixer):
    def __init__(self):
        super().__init__(
            description="Disable FTP/FTPS deployments for Azure Functions.",
            service="app",
            permissions_required={
                "actions": [
                    "Microsoft.Web/sites/write",
                    "Microsoft.Web/sites/config/write"
                ]
            }
        )

    def fix(self, finding=None, **kwargs):
        # Remediation logic here
        return True

Example (GCP):

from prowler.providers.gcp.lib.fix.fixer import GCPFixer

class ComputeInstancePublicIPFixer(GCPFixer):
    def __init__(self):
        super().__init__(
            description="Remove public IP from Compute Engine instance.",
            service="compute",
            iam_policy_required={
                "roles": ["roles/compute.instanceAdmin.v1"]
            }
        )

    def fix(self, finding=None, **kwargs):
        # Remediation logic here
        return True

Example (M365):

from prowler.providers.m365.lib.fix.fixer import M365Fixer

class AppFunctionFtpsDeploymentDisabledFixer(M365Fixer):
    def __init__(self):
        super().__init__(
            description="Disable FTP/FTPS deployments for Azure Functions.",
            service="app",
            permissions_required={
                "actions": [
                    "Microsoft.Web/sites/write",
                    "Microsoft.Web/sites/config/write"
                ]
            }
        )

    def fix(self, finding=None, **kwargs):
        # Remediation logic here
        return True

Fixer info

Each fixer should provide:

  • description: What the fixer does.
  • cost_impact: Whether the remediation may incur costs.
  • cost_description: Details about potential costs (if any).

For some providers, there will be additional information that needs to be added to the fixer info, like:

  • service: The cloud service affected.
  • permissions/IAM policy required: The minimum permissions needed for the fixer to work.

In order to get the fixer info, you can use the flag --fixer-info. And it will print the fixer info in a pretty format.


Fixer Config File

Some fixers support configurable parameters. You can use the default config file at prowler/config/fixer_config.yaml or provide your own with --fixer-config.

Example YAML:

aws:
  ec2_ebs_default_encryption: {}
  iam_password_policy:
    MinimumPasswordLength: 14
    RequireSymbols: True
    # ...
azure:
  app_function_ftps_deployment_disabled:
    ftps_state: "Disabled"

Best Practices

  • Always document the permissions required for your fixer.
  • Handle exceptions gracefully and log errors.
  • Return True only if the remediation was actually successful.
  • Use the providers client libraries and follow their best practices for API calls.

Troubleshooting

  • If a fixer is not available for a check, Prowler will print a warning.
  • If a fixer fails due to missing permissions, check the required IAM roles or permissions and update your execution identity accordingly.
  • Use the --list-fixers flag to see all available fixers for your provider.

Extending to New Providers

To add support for a new provider:

  1. Implement a new base fixer class inheriting from Fixer.
  2. Place it in the appropriate provider directory.
  3. Follow the same structure for check-specific fixers.

For more details, see the code in prowler/lib/fix/fixer.py and the provider-specific fixer base classes.