diff --git a/HISTORY.md b/HISTORY.md index a25c2ea..a7d2b86 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,10 @@ # History +0.12.1 (2024-03-07) +-------------------- +- Change `fuzz run` command behavior on contract targets absence (now it will emit warning instead of an error) +- Update documentation + 0.12.0 (2024-03-04) -------------------- - Analytics collection support diff --git a/README.md b/README.md index e87a53b..0af120f 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ Table of Contents - [Foundry Tests](#foundry-tests) - [Configuration](#configuration) - [Commands](#commands) + - [Product Analytics Collection](#product-analytics-collection) ## Installing The Diligence Fuzzing CLI runs on Python 3.7+, including PyPy. @@ -244,5 +245,8 @@ The `fuzz` CLI tool provides the following commands: Each command serves a specific purpose in the fuzzing process, and they can be used together to configure and execute fuzzing campaigns. For more information on each command, consult the corresponding documentation. +## Product Analytics Collection +This tool collects usage data to help us understand how the tool is being used and how we can improve it, but you can opt out of this data collection at any time. +Please have a look at the [Product Analytics Collection](docs/analytics.md) documentation for more information. * Free software: Apache 2 license diff --git a/docs/analytics.md b/docs/analytics.md new file mode 100644 index 0000000..6ba08e8 --- /dev/null +++ b/docs/analytics.md @@ -0,0 +1,77 @@ +# Product Analytics Collection and Crash Reporting +This document describes how we collect and use [analytics](#analytics-report) and [crash data](#crash-report) in Fuzzing CLI. + +## [Privacy Policy](#privacy-policy) +We take your privacy seriously. We do not collect any personal or sensitive information (except the ones you provide to us in the course of using the tool, e.g. your API key, contracts' source code). For more information, please read our [Privacy Policy](https://consensys.io/diligence/privacy-policy/). + +## [Analytics Report](#analytics-report) +### Overview +Fuzzing CLI collects usage data to help us understand how the tool is being used and how we can improve it. We use this data to prioritize features and improvements. We do not collect any personal or sensitive information (except the ones you provide to us in the course of using the tool, e.g. your API key, contracts' source code). Here's what we collect: +- `deviceId` - a unique identifier for the current device (UUID v4) +- `sessionId` - a unique identifier for the current session (UUID v4) +- `system` - Name of the operating system (e.g. "Linux", "Windows", "Darwin") +- `release` - Version of the operating system (e.g. "23.0.0") +- `machine` - Machine type (e.g. "x86_64", "arm64") +- `pythonVersion` - Version of Python (e.g. "3.8.10") +- `pythonImplementation` - e.g. "CPython", "PyPy" +- `fuzzingCliVersion` - version of the tool +- `rpcNodeKind` - kind of the local rpc node (e.g. "truffle", "anvil", "ganache") +- `rpcNodeVersion` - version of the local rpc node +- `ciMode` - whether the tool is running in CI mode +- `userId` - a user identifier from the API key, if the user has provided it +- `functionCalls` - a list of function calls with the following fields: + - `functionName` - the name of the function (e.g. "run", "arm") + - `context` - additional context for the function call (e.g. parameters) + - `result` - the result of the function call (e.g. "success", "exception") + - `duration` - the duration of the command in milliseconds + - `errorType` - the error type (*if the function call resulted in an exception*) + - `errorMessage` - the error message (*if the function call resulted in an exception*) + - `stackTrace` - the stack trace (*if the function call resulted in an exception*) + +### Opting Out +You can opt out of analytics following ways: +1. At first run of the tool, you will be asked whether you want to opt out of analytics + ```bash + > Hey there! 👋 Mind if we collect some usage analytics? + It helps us improve and make the experience better for you and others. 🚀. + (You can revoke the consent at any time later using `fuzz config set no-product-analytics`) [Y/n]: n + ``` +2. By running `fuzz config set no-product-analytics` command +3. By setting `FUZZ_ALLOW_ANALYTICS` environment variable to `false`
+If you opt out, we will not collect any usage data from your device.
+> Note: [CI mode](configuration.md#general-configuration-options) will opt in to analytics by default, but you can opt out using the last two methods. + +## [Crash Report](#crash-report) +### Overview +Fuzzing CLI collects crash data to help us understand and fix issues. We use this data to prioritize bug fixes. We do not collect any personal or sensitive information (except the ones you provide to us in the course of using the tool, e.g. your API key, contracts' source code). Here's what we collect: +- `deviceId` - a unique identifier for the current device (UUID v4) +- `sessionId` - a unique identifier for the current session (UUID v4) +- `system` - Name of the operating system (e.g. "Linux", "Windows", "Darwin") +- `release` - Version of the operating system (e.g. "23.0.0") +- `machine` - Machine type (e.g. "x86_64", "arm64") +- `pythonVersion` - Version of Python (e.g. "3.8.10") +- `pythonImplementation` - e.g. "CPython", "PyPy" +- `fuzzingCliVersion` - version of the tool +- `rpcNodeKind` - kind of the local rpc node (e.g. "truffle", "anvil", "ganache") +- `rpcNodeVersion` - version of the local rpc node +- `ciMode` - whether the tool is running in CI mode +- `userId` - a user identifier from the API key, if the user has provided it +- `context` - additional context for the function call (e.g. parameters) +- `errorType` - the error type (*if the function call resulted in an exception*) +- `errorMessage` - the error message (*if the function call resulted in an exception*) +- `errorCulprit` - the error culprit (*if the function call resulted in an exception*) +- `stackTrace` - the stack trace (*if the function call resulted in an exception*) +- `stackFrames` - the stack frames object (from python's traceback module) (*if the function call resulted in an exception*) + +### Opting Out +You can opt out of crash data collection following ways: +1. At an every event of a crash, you will be asked whether you want to send the crash report. If you choose not to send the crash report, we will not collect any crash data from your device. + ```bash + > Oops! 🙊 Something didn't go as planned. Please see details below for more information: + : + Do you want to report this error? [Y/n]: n + ``` +2. By setting `FUZZ_REPORT_CRASHES` environment variable to `false`. Setting this environment variable will prevent the tool from asking you to send the crash report at every event of a crash. + +> Note: [CI mode](configuration.md#general-configuration-options) will opt in to crash data collection by default, but you can opt out using the last method. + diff --git a/docs/configuration.md b/docs/configuration.md index 766af8e..9a34384 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -14,31 +14,40 @@ Each source has a different priority, based on the order in which they appear in 3. `.env` file 4. YAML config file -## Fuzzing Configuration Options -| Variable | Environment Variable | Default Value | Type | Description | -|-----------------------------------|---------------------------------------|-------------------------------------|-----------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------| -| `ide` | `FUZZ_IDE` | `None` | Optional `str` | The IDE that the project is using (e.g., `truffle`, `hardhat`). Usually, detected automatically | -| `build_directory` | `FUZZ_BUILD_DIRECTORY` | `None` | Optional `Path` | The path to the build directory of the project. | -| `sources_directory` | `FUZZ_SOURCES_DIRECTORY` | `None` | Optional `Path` | The path to the sources directory of the project. | -| `key` | `FUZZ_API_KEY` | `None` | Optional `str` | The API key used to submit campaigns to the Diligence Fuzzing API. | -| `smart_mode` | `FUZZ_SMART_MODE` | `False` | `bool` | Whether to use smart mode for the fuzzing campaign. | -| `project` | `FUZZ_PROJECT` | `None` | Optional `str` | The name of the project to add submitted campaigns to | -| `corpus_target` | `FUZZ_CORPUS_TARGET` | `None` | Optional `str` | The name of the corpus target to be used in the Diligence Fuzzing API. | -| `number_of_cores` | `FUZZ_NUMBER_OF_CORES` | `1` | `int` | The number of CPU cores to use for fuzzing. | -| `time_limit` | `FUZZ_TIME_LIMIT` | `None` | Optional `str` | The time limit for each individual fuzzing job (e.g., `10m`, `1h`, `30s`). | -| `targets` | `FUZZ_TARGETS` | `None` | Optional `List[str]` | A list of Solidity files to be fuzzed. | -| `deployed_contract_address` | `FUZZ_DEPLOYED_CONTRACT_ADDRESS` | `None` | Optional `str` | The address of the deployed contract to be used in the fuzzing campaign. | -| `additional_contracts_addresses` | `FUZZ_ADDITIONAL_CONTRACTS_ADDRESSES` | `None` | Optional `Union[List[str], str]` | A list of additional contract addresses to be used in the fuzzing campaign (could be a string with comma-separated addresses) | -| `rpc_url` | `FUZZ_RPC_URL` | `"http://localhost:8545"` | `str` | The URL of the RPC node where the contract are deployed. | -| `campaign_name_prefix` | `FUZZ_CAMPAIGN_NAME_PREFIX` | `"untitled"` | `str` | The prefix to use for the name of the fuzzing campaign. | -| `map_to_original_source` | `FUZZ_MAP_TO_ORIGINAL_SOURCE` | `False` | `bool` | Whether to map the generated inputs to the original source code. | -| `enable_cheat_codes` | `FUZZ_ENABLE_CHEAT_CODES` | `None` | Optional `bool` | Whether to enable cheat codes for the fuzzing campaign (`True` by default for foundry tests campaigns) | -| `chain_id` | `FUZZ_CHAIN_ID` | `None` | Optional `str` | The chain ID for the blockchain. | -| `incremental` | `FUZZ_INCREMENTAL` | `False` | `bool` | Whether to use incremental mode for the fuzzing campaign. | -| `truffle_executable_path` | `FUZZ_TRUFFLE_EXECUTABLE_PATH` | `truffle` | Optional `str` | The path to the Truffle executable (for projects using the Truffle) | -| `dry_run` | `FUZZ_DRY_RUN` | `False` | `bool` | Whether to run the fuzzer in dry run mode when the campaign isn't submitted but the payload is outputted. | - -## Arming Configuration Options +## [General Configuration Options](#general-configuration-options) +| Variable | Environment Variable | Default Value | Type | Description | +|-------------------|------------------------|---------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `ci_mode` | `FUZZ_CI_MODE` | `False` | `bool` | CI mode toggle. In CI mode (i.e. `ci_mode = true`) any interactive prompts (to fix something or change something dynamically) will be disabled and default actions will be performed | +| `report_crashes` | `FUZZ_REPORT_CRASHES` | `True` | `bool` | Switch to allow/disallow fuzzing-cli to send crash reports. | +| `allow_analytics` | `FUZZ_ALLOW_ANALYTICS` | `True` | `bool` | Switch to allow/disallow fuzzing-cli to send product usage analytics. Can also be turned off using `fuzz config set no-product-analytics` or on a prompt upon first campaign submission | + +## [Fuzzing Configuration Options](#fuzzing-configuration-options) +| Variable | Environment Variable | Default Value | Type | Description | +|----------------------------------|---------------------------------------|---------------------------|----------------------------------|-------------------------------------------------------------------------------------------------------------------------------| +| `ide` | `FUZZ_IDE` | `None` | Optional `str` | The IDE that the project is using (e.g., `truffle`, `hardhat`). Usually, detected automatically | +| `build_directory` | `FUZZ_BUILD_DIRECTORY` | `None` | Optional `Path` | The path to the build directory of the project. | +| `sources_directory` | `FUZZ_SOURCES_DIRECTORY` | `None` | Optional `Path` | The path to the sources directory of the project. | +| `key` | `FUZZ_API_KEY` | `None` | Optional `str` | The API key used to submit campaigns to the Diligence Fuzzing API. | +| `smart_mode` | `FUZZ_SMART_MODE` | `False` | `bool` | Whether to use smart mode for the fuzzing campaign. | +| `project` | `FUZZ_PROJECT` | `None` | Optional `str` | The name of the project to add submitted campaigns to | +| `corpus_target` | `FUZZ_CORPUS_TARGET` | `None` | Optional `str` | The name of the corpus target to be used in the Diligence Fuzzing API. | +| `number_of_cores` | `FUZZ_NUMBER_OF_CORES` | `1` | `int` | The number of CPU cores to use for fuzzing. | +| `time_limit` | `FUZZ_TIME_LIMIT` | `None` | Optional `str` | The time limit for each individual fuzzing job (e.g., `10m`, `1h`, `30s`). | +| `targets` | `FUZZ_TARGETS` | `None` | Optional `List[str]` | A list of Solidity files to be fuzzed. | +| `deployed_contract_address` | `FUZZ_DEPLOYED_CONTRACT_ADDRESS` | `None` | Optional `str` | The address of the deployed contract to be used in the fuzzing campaign. | +| `additional_contracts_addresses` | `FUZZ_ADDITIONAL_CONTRACTS_ADDRESSES` | `None` | Optional `Union[List[str], str]` | A list of additional contract addresses to be used in the fuzzing campaign (could be a string with comma-separated addresses) | +| `rpc_url` | `FUZZ_RPC_URL` | `"http://localhost:8545"` | `str` | The URL of the RPC node where the contract are deployed. | +| `campaign_name_prefix` | `FUZZ_CAMPAIGN_NAME_PREFIX` | `"untitled"` | `str` | The prefix to use for the name of the fuzzing campaign. | +| `map_to_original_source` | `FUZZ_MAP_TO_ORIGINAL_SOURCE` | `False` | `bool` | Whether to map the generated inputs to the original source code. | +| `enable_cheat_codes` | `FUZZ_ENABLE_CHEAT_CODES` | `None` | Optional `bool` | Whether to enable cheat codes for the fuzzing campaign (`True` by default for foundry tests campaigns) | +| `chain_id` | `FUZZ_CHAIN_ID` | `None` | Optional `str` | The chain ID for the blockchain. | +| `incremental` | `FUZZ_INCREMENTAL` | `False` | `bool` | Whether to use incremental mode for the fuzzing campaign. | +| `truffle_executable_path` | `FUZZ_TRUFFLE_EXECUTABLE_PATH` | `truffle` | Optional `str` | The path to the Truffle executable (for projects using the Truffle) | +| `dry_run` | `FUZZ_DRY_RUN` | `False` | `bool` | Whether to run the fuzzer in dry run mode when the campaign isn't submitted but the payload is outputted. | +| `max_sequence_length` | `FUZZ_MAX_SEQUENCE_LENGTH` | `None` | Optional `int` | Max sequence length (fuzzer parameter) | +| `ignore_code_hash` | `FUZZ_IGNORE_CODE_HASH` | `None` | Optional `bool` | Ignore code hash (fuzzer parameter) | + +## [Arming Configuration Options](#arming-configuration-options) | Variable | Environment Variable | Default Value | Type | Description | |------------------|--------------------------|---------------|----------------------|---------------------------------------------------------------------------------------------------------------| | `solc_version` | `ANALYZE_SOLC_VERSION` | `None` | Optional `str` | The version of the Solidity compiler to use for analysis. If not specified, the default version will be used. | @@ -47,7 +56,7 @@ Each source has a different priority, based on the order in which they appear in | `no_assert` | `ANALYZE_NO_ASSERT` | `True` | `bool` | If True, assertions will not be checked. | | `assert_` | `ANALYZE_ASSERT` | `False` | `bool` | If True, assertions will be checked. | -## Sources +## [Sources](#sources) ### YAML config file YAML config file are a convenient way to store configuration parameters in a file. They can be provided as an argument to the `fuzz` command using the `-c` option. For example: diff --git a/fuzzing_cli/__init__.py b/fuzzing_cli/__init__.py index 953801f..c5544bc 100644 --- a/fuzzing_cli/__init__.py +++ b/fuzzing_cli/__init__.py @@ -4,4 +4,4 @@ __author__ = """Dominik Muhs""" __email__ = "dominik.muhs@consensys.net" -__version__ = "0.12.0" +__version__ = "0.12.1" diff --git a/fuzzing_cli/fuzz/ide/generic.py b/fuzzing_cli/fuzz/ide/generic.py index 23a2162..e1c7996 100644 --- a/fuzzing_cli/fuzz/ide/generic.py +++ b/fuzzing_cli/fuzz/ide/generic.py @@ -152,9 +152,8 @@ def get_metadata_hash(deployed_bytecode) -> Optional[str]: return None except Exception as e: LOGGER.debug( - f'Exception decoding metadata from the bytecode. Encoded metadata "{encoded_metadata}"' + f'Exception decoding metadata from the bytecode. Encoded metadata "{encoded_metadata}". Exception: {e}' ) - LOGGER.error(e) return None def get_contract(self, deployed_bytecode: str) -> Optional[Contract]: diff --git a/fuzzing_cli/fuzz/run.py b/fuzzing_cli/fuzz/run.py index 2c1de7c..34d40e9 100644 --- a/fuzzing_cli/fuzz/run.py +++ b/fuzzing_cli/fuzz/run.py @@ -34,6 +34,7 @@ def handle_validation_errors( :param corpus_repo: Corpus repository :param prompt: Whether to prompt the user for automatic fixes + :param smart_mode: Whether to automatically apply fixes without prompting :return: List of suggested fixes """ suggested_fixes = [] @@ -101,7 +102,8 @@ def handle_validation_errors( } ) continue - raise ClickException(error_message) + click.secho(error_message) + continue if validation_error["type"] == "source_target_not_set": data = "\n".join( diff --git a/setup.cfg b/setup.cfg index 33c7caa..8e6e862 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,5 @@ [bumpversion] -current_version = 0.12.0 - +current_version = 0.12.1 commit = True tag = True diff --git a/setup.py b/setup.py index 15652c4..4e766a6 100644 --- a/setup.py +++ b/setup.py @@ -56,6 +56,6 @@ test_suite="tests", tests_require=test_requirements, url="https://github.com/ConsenSys/diligence-fuzzing", - version="0.12.0", + version="0.12.1", zip_safe=False, ) diff --git a/tests/test_get_corpus.py b/tests/test_get_corpus.py index df4b85e..5630d44 100644 --- a/tests/test_get_corpus.py +++ b/tests/test_get_corpus.py @@ -334,27 +334,27 @@ def test_contract_target_not_set( prompt if with_prompt else None, cmd_result, "y" if auto_fix else "n", - error=True if not auto_fix else False, + error=False, ) == result.output ) - if not with_prompt or not auto_fix: - assert result.exit_code == 1 - assert start_faas_campaign_mock.called is False - else: - assert result.exit_code == 0 - assert start_faas_campaign_mock.called is True - payload = start_faas_campaign_mock.call_args[0][0] - assert ( - payload["corpus"]["address-under-test"] - == "0x07d9fb5736cd151c8561798dfbda5dbcf54cb9e6" - ) - assert payload["corpus"]["other-addresses-under-test"] == [ - "0x1672fb2eb51789abd1a9f2fe83d69c6f4c883065", - "0x6a432c13a2e980a78f941c136ec804e7cb67e0d9", - "0x6bcb21de38753e485f7678c7ada2a63f688b8579", - ] + assert result.exit_code == 0 + assert start_faas_campaign_mock.called is True + payload = start_faas_campaign_mock.call_args[0][0] + assert ( + payload["corpus"]["address-under-test"] + == "0x07d9fb5736cd151c8561798dfbda5dbcf54cb9e6" + ) + + expected = [ + "0x1672fb2eb51789abd1a9f2fe83d69c6f4c883065", + "0x6a432c13a2e980a78f941c136ec804e7cb67e0d9", + ] + if auto_fix: + expected.append("0x6bcb21de38753e485f7678c7ada2a63f688b8579") + + assert payload["corpus"]["other-addresses-under-test"] == expected @pytest.mark.parametrize(*TESTS_PARAMETRIZATION) diff --git a/tests/test_tracing.py b/tests/test_tracing.py index 2deb649..da4595a 100644 --- a/tests/test_tracing.py +++ b/tests/test_tracing.py @@ -247,7 +247,6 @@ def test_report_crash( "rpcNodeVersion": "test/0.0.1", "system": platform.system(), } - import json assert_is_equal( list(crash_report_post_data.keys()),