mcpField
P
prowler-sdk-check

prowler-sdk-check

github.com/prowler-cloudSecurity15.1k66 installsUpdated today
Repository

About prowler-sdk-check

prowler-sdk-check is a skill for adding security checks to the Prowler SDK. It guides contributors through check structure, metadata, implementation, local verification, and provider-specific handling of findings.

Key features

  1. 01Define check files and directory structure
  2. 02Create metadata using required fields
  3. 03Implement PASS, FAIL, and MANUAL outcomes
  4. 04Handle provider permission and availability errors
  5. 05Verify checks with Prowler CLI commands

Use cases

  1. 01Add a check for a cloud provider service
  2. 02Confirm a check's metadata and severity
  3. 03Test behavior when scan data is unavailable
  4. 04Run a new check locally

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/prowler-cloud/prowler --skill prowler-sdk-check

After installing, type/prowler-sdk-checkin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

assets/
aws_check.py
aws_metadata.json
azure_check.py
azure_metadata.json
gcp_check.py
gcp_metadata.json
references/
metadata-docs.md

Details

From the project README

Check Structure

prowler/providers/{provider}/services/{service}/{check_name}/
├── __init__.py
├── {check_name}.py
└── {check_name}.metadata.json

Step-by-Step Creation Process

1. Prerequisites
  • Verify check doesn't exist: Search prowler/providers/{provider}/services/{service}/
  • Ensure provider and service exist - create them first if not
  • Confirm service has required methods - may need to add/modify service methods to get data
2. Create Check Files
mkdir -p prowler/providers/{provider}/services/{service}/{check_name}
touch prowler/providers/{provider}/services/{service}/{check_name}/__init__.py
touch prowler/providers/{provider}/services/{service}/{check_name}/{check_name}.py
touch prowler/providers/{provider}/services/{service}/{check_name}/{check_name}.metadata.json
3. Implement Check Logic
from prowler.lib.check.models import Check, Check_Report_{Provider}
from prowler.providers.{provider}.services.{service}.{service}_client import {service}_client

class {check_name}(Check):
    """Ensure that {resource} meets {security_requirement}."""
    def execute(self) -> list[Check_Report_{Provider}]:
        """Execute the check logic.

        Returns:
            A list of reports containing the result of the check.
        """
        findings = []
        for resource in {service}_client.{resources}:
            report = Check_Report_{Provider}(metadata=self.metadata(), resource=resource)
            report.status = "PASS" if resource.is_compliant else "FAIL"
            report.status_extended = f"Resource {resource.name} compliance status."
            findings.append(report)
        return findings
4. Create Metadata File

See complete schema below and assets/ folder for complete templates. For detailed field documentation, see references/metadata-docs.md.

5. Verify Check Detection
uv run python prowler-cli.py {provider} --list-checks | grep {check_name}
6. Run Check Locally
uv run python prowler-cli.py {provider} --log-level ERROR --verbose --check {check_name}
7. Create Tests

See prowler-test-sdk skill for test patterns (PASS, FAIL, no resources, error handling).


Check Naming Convention

{service}_{resource}_{security_control}

Examples:

  • ec2_instance_public_ip_disabled
  • s3_bucket_encryption_enabled
  • iam_user_mfa_enabled

Metadata Schema (COMPLETE)

{
  "Provider": "aws",
  "CheckID": "{check_name}",
  "CheckTitle": "Human-readable title",
  "CheckType": [
    "Software and Configuration Checks/AWS Security Best Practices",
    "Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices"
  ],
  "ServiceName": "{service}",
  "SubServiceName": "",
  "ResourceIdTemplate": "",
  "Severity": "low|medium|high|critical",
  "ResourceType": "AwsEc2Instance|Other",
  "ResourceGroup": "security|compute|storage|network",
  "Description": "**Bold resource name**. Detailed explanation of what this check evaluates and why it matters.",
  "Risk": "What happens if non-compliant. Explain attack vectors, data exposure risks, compliance impact.",
  "RelatedUrl": "",
  "AdditionalURLs": [
    "https://docs.aws.amazon.com/..."
  ],
  "Remediation": {
    "Code": {
      "CLI": "aws {service} {command} --option value",
      "NativeIaC": "```yaml\nResources:\n  Resource:\n    Type: AWS::{Service}::{Resource}\n    Properties:\n      Key: value  # This line fixes the issue\n```",
      "Other": "1. Console steps\n2. Step by step",
      "Terraform": "```hcl\nresource \"aws_{service}_{resource}\" \"example\" {\n  key = \"value\"  # This line fixes the issue\n}\n```"
    },
    "Recommendation": {
      "Text": "Detailed recommendation for remediation.",
      "Url": "https://hub.prowler.com/check/{check_name}"
    }
  },
  "Categories": [
    "identity-access",
    "encryption",
    "logging",
    "forensics-ready",
    "internet-exposed",
    "trust-boundaries"
  ],
  "DependsOn": [],
  "RelatedTo": [],
  "Notes": ""
}
Required Fields
FieldDescription
ProviderProvider name: aws, azure, gcp, kubernetes, github, m365
CheckIDMust match class name and folder name
CheckTitleHuman-readable title
Severitylow, medium, high, critical
ServiceNameService being checked
DescriptionWhat the check evaluates
RiskSecurity impact of non-compliance
Remediation.Code.CLICLI fix command
Remediation.Recommendation.TextHow to fix
Severity Guidelines
SeverityWhen to Use
criticalDirect data exposure, RCE, privilege escalation
highSignificant security risk, compliance violation
mediumDefense-in-depth, best practice
lowInformational, minor hardening

Check Report Statuses

StatusWhen to Use
PASSResource is compliant
FAILResource is non-compliant
MANUALRequires human verification, or the data needed to evaluate the resource could not be retrieved
Permission / availability errors are NOT findings

Never set FAIL because an API call failed (missing permission or scope, API not enabled, feature not licensed, data unavailable). That is a scan-configuration problem, not a security issue, and it surfaces as a misleading high-severity finding.

  • Service: log the error and expose it distinctly from an empty result (None instead of [], an *_error attribute, or a *_lookup_failed set). Only treat real access errors this way; a 404/not-found usually means "not configured" and IS a legitimate FAIL, and a definitively disabled API is a legitimate FAIL when the API's activation is itself the audited control (e.g. GCP Access Approval).
  • Check: emit ONE tenant/account/project/subscription-level MANUAL finding (not one per resource) whose status_extended says the check cannot be evaluated and names the required permission/API/license.
  • Do not touch report.check_metadata.Severity to hide it.
if <service>_client.<data> is None:
    report = CheckReport<Provider>(metadata=self.metadata(), resource={})
    report.resource_name = "<Tenant-level resource>"
    report.resource_id = "<stable-id>"
    report.status = "MANUAL"
    report.status_extended = "Cannot evaluate <requirement>: <data> could not be retrieved. Verify that <permission> is granted to the scanning identity."
    return [report]

Common Patterns

AWS Check with Regional Resources
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.s3.s3_client import s3_client

class s3_bucket_encryption_enabled(Check):
    def execute(self) -> list[Check_Report_AWS]:
        findings = []
        for bucket in s3_client.buckets.values():
            report = Check_Report_AWS(metadata=self.metadata(), resource=bucket)
            if bucket.encryption:
                report.status = "PASS"
                report.status_extended = f"S3 bucket {bucket.name} has encryption enabled."
            else:
                report.status = "FAIL"
                report.status_extended = f"S3 bucket {bucket.name} does not have encryption enabled."
            findings.append(report)
        return findings
Check with Multiple Conditions
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.ec2.ec2_client import ec2_client

class ec2_instance_hardened(Check):
    def execute(self) -> list[Check_Report_AWS]:
        findings = []
        for instance in ec2_client.instances:
            report = Check_Report_AWS(metadata=self.metadata(), resource=instance)

            issues = []
            if instance.public_ip:
                issues.append("has public IP")
            if not instance.metadata_options.http_tokens == "required":
                issues.append("IMDSv2 not enforced")

            if issues:
                report.status = "FAIL"
                report.status_extended = f"Instance {instance.id} {', '.join(issues)}."
            else:
                report.status = "PASS"
                report.status_extended = f"Instance {instance.id} is properly hardened."

            findings.append(report)
        return findings

Commands

# Verify detection
uv run python prowler-cli.py {provider} --list-checks | grep {check_name}

# Run check
uv run python prowler-cli.py {provider} --log-level ERROR --verbose --check {check_name}

# Run with specific profile/credentials
uv run python prowler-cli.py aws --profile myprofile --check {check_name}

# Run multiple checks
uv run python prowler-cli.py {provider} --check {check1} {check2} {check3}

Resources

  • Templates: See assets/ for complete check and metadata templates (AWS, Azure, GCP)
  • Documentation: See references/metadata-docs.md for official Prowler Developer Guide links

Comments

Markdown supported