Files
prowler/prowler/lib/cli/parser.py
T

395 lines
14 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_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,nhn,dashboard} ...",
epilog="""
Available Cloud Providers:
{aws,azure,gcp,kubernetes,m365,nhn}
aws AWS Provider
azure Azure Provider
gcp GCP Provider
kubernetes Kubernetes Provider
github GitHub Provider
m365 Microsoft 365 Provider
nhn NHN Provider (Unofficial)
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"
# 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}")
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-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.cloud/en/latest/tutorials/misc/#custom-checks).",
)
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.cloud/en/latest/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.cloud/en/latest/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.cloud/en/latest/tutorials/integrations/#slack).",
)