diff --git a/slither/__main__.py b/slither/__main__.py index db0c543f05..39d387ef20 100644 --- a/slither/__main__.py +++ b/slither/__main__.py @@ -33,6 +33,7 @@ defaults_flag_in_config, output_detectors, output_detectors_json, + output_config_file_section, output_printers, output_printers_json, output_results_to_markdown, @@ -668,6 +669,15 @@ def parse_args( "--wiki-detectors", help=argparse.SUPPRESS, action=OutputWiki, default=False ) + parser.add_argument( + "--wiki-configuration", + "--wiki-config", + help=argparse.SUPPRESS, + action=OutputConfigFileSection, + nargs=0, + default=False, + ) + parser.add_argument( "--list-detectors-json", help=argparse.SUPPRESS, @@ -778,6 +788,18 @@ def __call__( parser.exit() +class OutputConfigFileSection(argparse.Action): + def __call__( + self, + parser: Any, + args: Any, + values: str | Sequence[Any] | None, + option_string: Any = None, + ) -> None: + output_config_file_section() + parser.exit() + + # endregion ################################################################################### ################################################################################### diff --git a/slither/utils/command_line.py b/slither/utils/command_line.py index 3242e3aeb1..7871b2fc32 100644 --- a/slither/utils/command_line.py +++ b/slither/utils/command_line.py @@ -5,6 +5,7 @@ import re import logging from collections import defaultdict +from typing import Any from crytic_compile.cryticparser.defaults import ( DEFAULTS_FLAG_IN_CONFIG as DEFAULTS_FLAG_IN_CONFIG_CRYTIC_COMPILE, @@ -79,6 +80,36 @@ class FailOnLevel(enum.Enum): } +def _format_config_value(value: Any) -> Any: + if isinstance(value, enum.Enum): + return value.value + return value + + +def format_config_file_defaults(defaults: dict[str, Any] | None = None) -> str: + defaults = defaults if defaults is not None else defaults_flag_in_config + serializable_defaults = {key: _format_config_value(value) for key, value in defaults.items()} + return json.dumps(serializable_defaults, indent=4) + + +def output_config_file_section() -> None: + print("### Configuration File") + print() + print( + "Some options can be set through a json configuration file. By default, " + "`slither.config.json` is used if present (it can be changed through " + "`--config-file file.config.json`)." + ) + print() + print("Options passed via the CLI have priority over options set in the configuration file.") + print() + print("The following flags are supported:") + print() + print("```json") + print(format_config_file_defaults()) + print("```") + + def read_config_file(args: argparse.Namespace) -> None: # No config file was provided as an argument if args.config_file is None: diff --git a/tests/unit/utils/test_command_line.py b/tests/unit/utils/test_command_line.py new file mode 100644 index 0000000000..3544381d1b --- /dev/null +++ b/tests/unit/utils/test_command_line.py @@ -0,0 +1,39 @@ +"""Tests for slither/utils/command_line.py""" + +import json + +from slither.utils.command_line import ( + FailOnLevel, + format_config_file_defaults, + output_config_file_section, +) + + +def test_format_config_file_defaults_outputs_valid_json(): + result = format_config_file_defaults( + { + "detectors_to_run": "all", + "fail_on": FailOnLevel.PEDANTIC, + "json": None, + "exclude_dependencies": False, + } + ) + + assert json.loads(result) == { + "detectors_to_run": "all", + "fail_on": "pedantic", + "json": None, + "exclude_dependencies": False, + } + + +def test_output_config_file_section_includes_supported_defaults(capsys): + output_config_file_section() + + captured = capsys.readouterr() + assert "### Configuration File" in captured.out + assert "```json" in captured.out + assert '"exclude_dependencies": false' in captured.out + assert '"exclude_optimization": false' in captured.out + assert '"sarif": null' in captured.out + assert '"json-types": "detectors,printers"' in captured.out