diff --git a/docs/src/Usage.md b/docs/src/Usage.md index 7b9738362d..41225647c8 100644 --- a/docs/src/Usage.md +++ b/docs/src/Usage.md @@ -126,36 +126,89 @@ Some options can be set through a json configuration file. By default, `slither. Options passed via the CLI have priority over options set in the configuration file. -The following flags are supported: +Run `slither --list-config` to generate the supported configuration keys and their default values from the current Slither and `crytic-compile` defaults: -```sh +```json { + "brownie_ignore_compile": false, + "buidler_cache_directory": "cache", + "buidler_ignore_compile": false, + "buidler_skip_directory_name_fix": false, + "codex": false, + "codex_contracts": "all", + "codex_log": false, + "codex_max_tokens": 300, + "codex_model": "text-davinci-003", + "codex_temperature": 0, + "compile_custom_build": null, + "compile_force_framework": null, + "compile_libraries": null, + "compile_remove_metadata": false, + "dapp_ignore_compile": false, + "detectors_to_exclude": null, + "detectors_to_include": null, "detectors_to_run": "all", - "printers_to_run": None, - "detectors_to_exclude": None, - "detectors_to_include": None, - "exclude_dependencies": False, - "exclude_informational": False, - "exclude_optimization": False, - "exclude_low": False, - "exclude_medium": False, - "exclude_high": False, - "fail_on": FailOnLevel.PEDANTIC, - "json": None, - "sarif": None, - "disable_color": False, - "filter_paths": None, - "include_paths": None, - "generate_patches": False, - "skip_assembly": False, - "legacy_ast": False, - "zip": None, - "zip_type": "lzma", - "show_ignored_findings": False, + "disable_color": false, + "embark_ignore_compile": false, + "embark_overwrite_config": false, + "etherlime_compile_arguments": null, + "etherlime_ignore_compile": false, + "etherscan_api_key": null, + "etherscan_export_directory": "etherscan-contracts", + "etherscan_only_bytecode": false, + "etherscan_only_source_code": false, + "exclude_dependencies": false, + "exclude_high": false, + "exclude_informational": false, + "exclude_location": false, + "exclude_low": false, + "exclude_medium": false, + "exclude_optimization": false, + "export_dir": "crytic-export", + "fail_on": "pedantic", + "filter_paths": null, + "foundry_compile_all": false, + "foundry_ignore_compile": false, + "foundry_out_directory": null, + "generate_patches": false, + "hardhat_artifacts_directory": null, + "hardhat_cache_directory": null, + "hardhat_ignore_compile": false, + "ignore_compile": false, + "include_paths": null, + "json": null, + "json-types": "detectors,printers", + "legacy_ast": false, + "no_fail": false, + "npx_disable": false, + "printers_to_run": null, + "sarif": null, "sarif_input": "export.sarif", "sarif_triage": "export.sarif.sarifexplorer", + "show_ignored_findings": false, + "skip_assembly": false, + "skip_clean": false, + "solc": "solc", + "solc_args": null, + "solc_disable_warnings": false, + "solc_force_legacy_json": false, + "solc_remaps": null, + "solc_solcs_bin": null, + "solc_solcs_select": null, + "solc_standard_json": false, + "solc_working_dir": null, "triage_database": "slither.db.json", + "truffle_build_directory": "build/contracts", + "truffle_ignore_compile": false, + "truffle_overwrite_config": false, + "truffle_overwrite_version": null, + "truffle_version": null, + "waffle_config_file": null, + "waffle_ignore_compile": false, + "warn_unused_ignores": false, + "zip": null, + "zip_type": "lzma" } ``` -For flags related to the compilation, see the [`crytic-compile` configuration](https://github.com/crytic/crytic-compile/blob/master/crytic_compile/cryticparser/defaults.py) +For details about the compilation-related flags, see the [`crytic-compile` configuration](https://github.com/crytic/crytic-compile/blob/master/crytic_compile/cryticparser/defaults.py). diff --git a/slither/__main__.py b/slither/__main__.py index db0c543f05..8674ff08ac 100644 --- a/slither/__main__.py +++ b/slither/__main__.py @@ -33,6 +33,7 @@ defaults_flag_in_config, output_detectors, output_detectors_json, + output_config_json, output_printers, output_printers_json, output_results_to_markdown, @@ -614,6 +615,14 @@ def parse_args( default=None, ) + group_misc.add_argument( + "--list-config", + help="List supported configuration file options with their default values", + action=ListConfig, + nargs=0, + default=False, + ) + group_misc.add_argument( "--change-line-prefix", help="Change the line prefix (default #) for the displayed source codes (i.e. file.sol#1).", @@ -743,6 +752,12 @@ def __call__(self, parser: Any, *args: Any, **kwargs: Any) -> None: parser.exit() +class ListConfig(argparse.Action): + def __call__(self, parser: Any, *args: Any, **kwargs: Any) -> None: + print(json.dumps(output_config_json(), indent=4)) + parser.exit() + + class ListPrinters(argparse.Action): def __call__(self, parser: Any, *args: Any, **kwargs: Any) -> None: _, printers = get_detectors_and_printers() diff --git a/slither/utils/command_line.py b/slither/utils/command_line.py index 3242e3aeb1..5029521a24 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,19 @@ class FailOnLevel(enum.Enum): } +def _convert_config_default(value: Any) -> Any: + if isinstance(value, enum.Enum): + return value.value + return value + + +def output_config_json() -> dict[str, Any]: + return { + key: _convert_config_default(value) + for key, value in sorted(defaults_flag_in_config.items()) + } + + def read_config_file(args: argparse.Namespace) -> None: # No config file was provided as an argument if args.config_file is None: @@ -99,8 +113,10 @@ def read_config_file(args: argparse.Namespace) -> None: yellow(f"{args.config_file} has an unknown key: {key} : {elem}") ) continue - if getattr(args, key) == defaults_flag_in_config[key]: - setattr(args, key, elem) + arg_key = key.replace("-", "_") + current_value = getattr(args, arg_key, defaults_flag_in_config[key]) + if current_value == defaults_flag_in_config[key]: + setattr(args, arg_key, elem) except json.decoder.JSONDecodeError as e: logger.error(red(f"Impossible to read {args.config_file}, please check the file {e}")) else: diff --git a/tests/unit/utils/test_command_line.py b/tests/unit/utils/test_command_line.py new file mode 100644 index 0000000000..84c6c285a9 --- /dev/null +++ b/tests/unit/utils/test_command_line.py @@ -0,0 +1,48 @@ +import argparse + +from slither.utils.command_line import ( + defaults_flag_in_config, + output_config_json, + read_config_file, +) + + +def test_output_config_json_serializes_defaults() -> None: + config = output_config_json() + + assert config["fail_on"] == "pedantic" + assert config["json-types"] == "detectors,printers" + assert config.keys() == defaults_flag_in_config.keys() + + +def test_read_config_file_supports_hyphenated_keys(tmp_path) -> None: + config_file = tmp_path / "slither.config.json" + config_file.write_text('{"json-types": "detectors"}', encoding="utf8") + args = argparse.Namespace( + config_file=str(config_file), + json_types=defaults_flag_in_config["json-types"], + ) + + read_config_file(args) + + assert args.json_types == "detectors" + + +def test_read_config_file_preserves_cli_override_for_hyphenated_keys(tmp_path) -> None: + config_file = tmp_path / "slither.config.json" + config_file.write_text('{"json-types": "detectors"}', encoding="utf8") + args = argparse.Namespace(config_file=str(config_file), json_types="printers") + + read_config_file(args) + + assert args.json_types == "printers" + + +def test_read_config_file_accepts_supported_key_absent_from_namespace(tmp_path) -> None: + config_file = tmp_path / "slither.config.json" + config_file.write_text('{"codex": true}', encoding="utf8") + args = argparse.Namespace(config_file=str(config_file)) + + read_config_file(args) + + assert args.codex is True