Files
prowler/prowler/lib/cli/parser.py
Sergio Garcia dbdce98cf2 feat(alibaba): add Alibaba Cloud provider (#9329)
Co-authored-by: pedrooot <pedromarting3@gmail.com>
Co-authored-by: HugoPBrito <hugopbrit@gmail.com>
2025-12-03 11:47:55 -05:00

415 lines
15 KiB
Python

import argparse
import sys
from argparse import RawTextHelpFormatter
from dashboard.lib.arguments.arguments import init_dashboard_parser
from prowler.config.config import (
available_compliance_frameworks,
available_output_formats,
check_current_version,
default_config_file_path,
default_fixer_config_file_path,
default_output_directory,
)
from prowler.lib.check.models import Severity
from prowler.lib.outputs.common import Status
from prowler.providers.common.arguments import (
init_providers_parser,
validate_asff_usage,
validate_provider_arguments,
)
class ProwlerArgumentParser:
# Set the default parser
def __init__(self):
# CLI Arguments
self.parser = argparse.ArgumentParser(
prog="prowler",
formatter_class=RawTextHelpFormatter,
usage="prowler [-h] [--version] {aws,azure,gcp,kubernetes,m365,github,nhn,mongodbatlas,oraclecloud,alibabacloud,dashboard,iac} ...",
epilog="""
Available Cloud Providers:
{aws,azure,gcp,kubernetes,m365,github,iac,llm,nhn,mongodbatlas,oraclecloud,alibabacloud}
aws AWS Provider
azure Azure Provider
gcp GCP Provider
kubernetes Kubernetes Provider
m365 Microsoft 365 Provider
github GitHub Provider
oraclecloud Oracle Cloud Infrastructure Provider
alibabacloud Alibaba Cloud Provider
iac IaC Provider (Beta)
llm LLM Provider (Beta)
nhn NHN Provider (Unofficial)
mongodbatlas MongoDB Atlas Provider (Beta)
Available components:
dashboard Local dashboard
To see the different available options on a specific component, run:
prowler {provider|dashboard} -h|--help
Detailed documentation at https://docs.prowler.com
""",
)
# Default
self.parser.add_argument(
"--version",
"-v",
action="store_true",
help="show Prowler version",
)
# Common arguments parser
self.common_providers_parser = argparse.ArgumentParser(add_help=False)
# Providers Parser
self.subparsers = self.parser.add_subparsers(
title="Available Cloud Providers", dest="provider", help=argparse.SUPPRESS
)
self.__init_outputs_parser__()
self.__init_logging_parser__()
self.__init_checks_parser__()
self.__init_exclude_checks_parser__()
self.__init_list_checks_parser__()
self.__init_mutelist_parser__()
self.__init_config_parser__()
self.__init_custom_checks_metadata_parser__()
self.__init_third_party_integrations_parser__()
# Init Providers Arguments
init_providers_parser(self)
# Dashboard Parser
init_dashboard_parser(self)
def parse(self, args=None) -> argparse.Namespace:
"""
parse is a wrapper to call parse_args() and do some validation
"""
# We can override sys.argv
if args:
sys.argv = args
if len(sys.argv) == 2 and sys.argv[1] in ("-v", "--version"):
print(check_current_version())
sys.exit(0)
# Set AWS as the default provider if no provider is supplied
if len(sys.argv) == 1:
sys.argv = self.__set_default_provider__(sys.argv)
# Help and Version flags cannot set a default provider
if (
len(sys.argv) >= 2
and (sys.argv[1] not in ("-h", "--help"))
and (sys.argv[1] not in ("-v", "--version"))
):
# Since the provider is always the second argument, we are checking if
# a flag, starting by "-", is supplied
if "-" in sys.argv[1]:
sys.argv = self.__set_default_provider__(sys.argv)
# Provider aliases mapping
# Microsoft 365
elif sys.argv[1] == "microsoft365":
sys.argv[1] = "m365"
# Oracle Cloud Infrastructure
elif sys.argv[1] == "oci":
sys.argv[1] = "oraclecloud"
# Parse arguments
args = self.parser.parse_args()
# A provider is always required
if not args.provider:
self.parser.error(
"A provider/component is required to see its specific help options."
)
# Only Logging Configuration
if args.provider != "dashboard" and (args.only_logs or args.list_checks_json):
args.no_banner = True
# Extra validation for provider arguments
valid, message = validate_provider_arguments(args)
if not valid:
self.parser.error(f"{args.provider}: {message}")
asff_is_valid, asff_error = validate_asff_usage(
args.provider, getattr(args, "output_formats", None)
)
if not asff_is_valid:
self.parser.error(asff_error)
return args
def __set_default_provider__(self, args: list) -> list:
default_args = [args[0]]
provider = "aws"
default_args.append(provider)
default_args.extend(args[1:])
# Save the arguments with the default provider included
return default_args
def __init_outputs_parser__(self):
# Outputs
common_outputs_parser = self.common_providers_parser.add_argument_group(
"Outputs"
)
common_outputs_parser.add_argument(
"--status",
nargs="+",
help=f"Filter by the status of the findings {[status.value for status in Status]}",
choices=[status.value for status in Status],
)
common_outputs_parser.add_argument(
"--output-formats",
"--output-modes",
"-M",
nargs="+",
help="Output modes, by default csv and json-oscf are saved. When using AWS Security Hub integration, json-asff output is also saved.",
default=["csv", "json-ocsf", "html"],
choices=available_output_formats,
)
common_outputs_parser.add_argument(
"--output-filename",
"-F",
nargs="?",
help="Custom output report name without the file extension, if not specified will use default output/prowler-output-ACCOUNT_NUM-OUTPUT_DATE.format",
)
common_outputs_parser.add_argument(
"--output-directory",
"-o",
nargs="?",
help="Custom output directory, by default the folder where Prowler is stored",
default=default_output_directory,
)
common_outputs_parser.add_argument(
"--verbose",
action="store_true",
help="Runs showing all checks executed and results",
)
common_outputs_parser.add_argument(
"--ignore-exit-code-3",
"-z",
action="store_true",
help="Failed checks do not trigger exit code 3",
)
common_outputs_parser.add_argument(
"--no-banner", "-b", action="store_true", help="Hide Prowler banner"
)
common_outputs_parser.add_argument(
"--no-color",
action="store_true",
help="Disable color codes in output",
)
common_outputs_parser.add_argument(
"--unix-timestamp",
action="store_true",
default=False,
help="Set the output timestamp format as unix timestamps instead of iso format timestamps (default mode).",
)
def __init_logging_parser__(self):
# Logging Options
# Both options can be combined to only report to file some log level
common_logging_parser = self.common_providers_parser.add_argument_group(
"Logging"
)
common_logging_parser.add_argument(
"--log-level",
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
default="CRITICAL",
help="Select Log Level",
)
common_logging_parser.add_argument(
"--log-file",
nargs="?",
help="Set log file name",
)
common_logging_parser.add_argument(
"--only-logs",
action="store_true",
help="Print only Prowler logs by the stdout. This option sets --no-banner.",
)
def __init_exclude_checks_parser__(self):
# Exclude checks options
exclude_checks_parser = self.common_providers_parser.add_argument_group(
"Exclude checks/services to run"
)
exclude_checks_parser.add_argument(
"--excluded-check",
"--excluded-checks",
"-e",
nargs="+",
help="Checks to exclude",
)
exclude_checks_parser.add_argument(
"--excluded-checks-file",
nargs="?",
help="JSON file containing the checks to be excluded. See config/checklist_example.json",
)
exclude_checks_parser.add_argument(
"--excluded-service",
"--excluded-services",
nargs="+",
help="Services to exclude",
)
def __init_checks_parser__(self):
# Set checks to execute
common_checks_parser = self.common_providers_parser.add_argument_group(
"Specify checks/services to run"
)
# The following arguments needs to be set exclusivelly
group = common_checks_parser.add_mutually_exclusive_group()
group.add_argument(
"--check",
"--checks",
"-c",
nargs="+",
help="List of checks to be executed.",
)
group.add_argument(
"--checks-file",
"-C",
nargs="?",
help="JSON file containing the checks to be executed. See config/checklist_example.json",
)
group.add_argument(
"--service",
"--services",
"-s",
nargs="+",
help="List of services to be executed.",
)
common_checks_parser.add_argument(
"--severity",
"--severities",
nargs="+",
help=f"Severities to be executed {[severity.value for severity in Severity]}",
choices=[severity.value for severity in Severity],
)
group.add_argument(
"--compliance",
nargs="+",
help="Compliance Framework to check against for. The format should be the following: framework_version_provider (e.g.: cis_3.0_aws)",
choices=available_compliance_frameworks,
)
group.add_argument(
"--category",
"--categories",
nargs="+",
help="List of categories to be executed.",
default=[],
# TODO: Pending validate choices
)
common_checks_parser.add_argument(
"--checks-folder",
"-x",
nargs="?",
help="Specify external directory with custom checks (each check must have a folder with the required files, see more in https://docs.prowler.com/user-guide/cli/tutorials/misc#custom-checks-in-prowler).",
)
def __init_list_checks_parser__(self):
# List checks options
list_checks_parser = self.common_providers_parser.add_argument_group(
"List checks/services/categories/compliance-framework checks"
)
list_group = list_checks_parser.add_mutually_exclusive_group()
list_group.add_argument(
"--list-checks", "-l", action="store_true", help="List checks"
)
list_group.add_argument(
"--list-checks-json",
action="store_true",
help="Output a list of checks in json format to use with --checks-file option",
)
list_group.add_argument(
"--list-services",
action="store_true",
help="List covered services by given provider",
)
list_group.add_argument(
"--list-compliance",
"--list-compliances",
action="store_true",
help="List all available compliance frameworks",
)
list_group.add_argument(
"--list-compliance-requirements",
nargs="+",
help="List requirements and checks per compliance framework",
choices=available_compliance_frameworks,
)
list_group.add_argument(
"--list-categories",
action="store_true",
help="List the available check's categories",
)
list_group.add_argument(
"--list-fixer",
"--list-fixers",
"--list-remediations",
action="store_true",
help="List fixers available for the provider",
)
def __init_mutelist_parser__(self):
mutelist_subparser = self.common_providers_parser.add_argument_group("Mutelist")
mutelist_subparser.add_argument(
"--mutelist-file",
"-w",
nargs="?",
help="Path for mutelist YAML file. See example prowler/config/<provider>_mutelist.yaml for reference and format. For AWS provider, it also accepts AWS DynamoDB Table, Lambda ARNs or S3 URIs, see more in https://docs.prowler.com/user-guide/cli/tutorials/mutelist",
)
def __init_config_parser__(self):
config_parser = self.common_providers_parser.add_argument_group("Configuration")
config_parser.add_argument(
"--config-file",
nargs="?",
default=default_config_file_path,
help="Set configuration file path",
)
config_parser.add_argument(
"--fixer-config",
nargs="?",
default=default_fixer_config_file_path,
help="Set configuration fixer file path",
)
def __init_custom_checks_metadata_parser__(self):
# CustomChecksMetadata
custom_checks_metadata_subparser = (
self.common_providers_parser.add_argument_group("Custom Checks Metadata")
)
custom_checks_metadata_subparser.add_argument(
"--custom-checks-metadata-file",
nargs="?",
default=None,
help="Path for the custom checks metadata YAML file. See example prowler/config/custom_checks_metadata_example.yaml for reference and format. See more in https://docs.prowler.com/user-guide/cli/tutorials/custom-checks-metadata/",
)
def __init_third_party_integrations_parser__(self):
third_party_subparser = self.common_providers_parser.add_argument_group(
"3rd Party Integrations"
)
third_party_subparser.add_argument(
"--shodan",
"-N",
nargs="?",
default=None,
metavar="SHODAN_API_KEY",
help="Check if any public IPs in your Cloud environments are exposed in Shodan.",
)
third_party_subparser.add_argument(
"--slack",
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.com/user-guide/cli/tutorials/integrations#configuration-of-the-integration-with-slack/).",
)