From 48fc49a4bec59a81a50f3e05c6a723e797efaeaf Mon Sep 17 00:00:00 2001 From: Simone Date: Fri, 14 Jun 2024 12:03:32 +0200 Subject: [PATCH 1/3] Fix for dynamic array operations --- slither/tools/mutator/mutators/AOR.py | 39 ++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/slither/tools/mutator/mutators/AOR.py b/slither/tools/mutator/mutators/AOR.py index 0bf0fb2a2..f86e7d3d4 100644 --- a/slither/tools/mutator/mutators/AOR.py +++ b/slither/tools/mutator/mutators/AOR.py @@ -2,7 +2,12 @@ from typing import Dict from slither.slithir.operations import Binary, BinaryType from slither.tools.mutator.utils.patch import create_patch_with_line from slither.tools.mutator.mutators.abstract_mutator import AbstractMutator +from slither.core.variables.variable import Variable from slither.core.expressions.unary_operation import UnaryOperation +from slither.core.expressions.call_expression import CallExpression +from slither.core.expressions.member_access import MemberAccess +from slither.core.expressions.identifier import Identifier +from slither.core.solidity_types.array_type import ArrayType arithmetic_operators = [ BinaryType.ADDITION, @@ -27,7 +32,39 @@ class AOR(AbstractMutator): # pylint: disable=too-few-public-methods ir_expression = node.expression except: # pylint: disable=bare-except continue - for ir in node.irs: + + # Special cases handling .push and .pop on dynamic arrays. + # The IR for these operations has a binary operation due to internal conversion + # (see convert_to_push and convert_to_pop in slithir/convert.py) + # however it's not present in the source code and should not be mutated. + # pylint: disable=too-many-boolean-expressions + if ( + isinstance(ir_expression, CallExpression) + and isinstance(ir_expression.called, MemberAccess) + and ir_expression.called.member_name == "pop" + and isinstance(ir_expression.called.expression, Identifier) + and isinstance(ir_expression.called.expression.value, Variable) + and isinstance(ir_expression.called.expression.value.type, ArrayType) + ): + continue + + # For a .push instruction we skip the last 6 IR operations + # because they are fixed based on the internal conversion to the IR + # while we need to look at the preceding instructions because + # they might contain Binary IR to be mutated. + # For example for a.push(3+x) it's correct to mutate 3+x. + irs = ( + node.irs[:-6] + if isinstance(ir_expression, CallExpression) + and isinstance(ir_expression.called, MemberAccess) + and ir_expression.called.member_name == "push" + and isinstance(ir_expression.called.expression, Identifier) + and isinstance(ir_expression.called.expression.value, Variable) + and isinstance(ir_expression.called.expression.value.type, ArrayType) + else node.irs + ) + + for ir in irs: if isinstance(ir, Binary) and ir.type in arithmetic_operators: if isinstance(ir_expression, UnaryOperation): continue From a22ae18171e54d7396a1b1cf5718c1506ad6a6e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 22:26:17 +0000 Subject: [PATCH 2/3] Bump sigstore/gh-action-sigstore-python from 2.1.1 to 3.0.0 Bumps [sigstore/gh-action-sigstore-python](https://github.com/sigstore/gh-action-sigstore-python) from 2.1.1 to 3.0.0. - [Release notes](https://github.com/sigstore/gh-action-sigstore-python/releases) - [Changelog](https://github.com/sigstore/gh-action-sigstore-python/blob/main/CHANGELOG.md) - [Commits](https://github.com/sigstore/gh-action-sigstore-python/compare/v2.1.1...v3.0.0) --- updated-dependencies: - dependency-name: sigstore/gh-action-sigstore-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c5ec555ce..0a0f04f2b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -47,7 +47,7 @@ jobs: uses: pypa/gh-action-pypi-publish@v1.9.0 - name: sign - uses: sigstore/gh-action-sigstore-python@v2.1.1 + uses: sigstore/gh-action-sigstore-python@v3.0.0 with: inputs: ./dist/*.tar.gz ./dist/*.whl release-signing-artifacts: true From 48815192f25d9ab8916393a57d2b6d98e3b067e6 Mon Sep 17 00:00:00 2001 From: dm Date: Thu, 18 Jul 2024 16:59:02 +0200 Subject: [PATCH 3/3] feat: make tables fit within terminal by default (#2426) This PR adds a `max_width` parameter to MyPrettyTable to restrict the maximum width of its underlying table. The value can be an integer, "max" (detect automatically the correct width) or None (no width limit) * Add a new parameter `max_width` to MyPrettyTable to enhance its display for CLI usage * Fix the description of the Loc printer, and adjust the maximal width. * bump prettytable * default all prettytable's to fit within max terminal width --------- Co-authored-by: alpharush <0xalpharush@protonmail.com> --- setup.py | 2 +- slither/printers/summary/loc.py | 4 ++-- slither/utils/command_line.py | 4 +++- slither/utils/myprettytable.py | 26 ++++++++++++++++++++++---- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/setup.py b/setup.py index a669b82a3..3b361d605 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ setup( python_requires=">=3.8", install_requires=[ "packaging", - "prettytable>=3.3.0", + "prettytable>=3.10.2", "pycryptodome>=3.4.6", "crytic-compile>=0.3.7,<0.4.0", # "crytic-compile@git+https://github.com/crytic/crytic-compile.git@master#egg=crytic-compile", diff --git a/slither/printers/summary/loc.py b/slither/printers/summary/loc.py index 35bb20fc4..886803e8e 100644 --- a/slither/printers/summary/loc.py +++ b/slither/printers/summary/loc.py @@ -17,8 +17,8 @@ from slither.utils.output import Output class LocPrinter(AbstractPrinter): ARGUMENT = "loc" - HELP = """Count the total number lines of code (LOC), source lines of code (SLOC), \ - and comment lines of code (CLOC) found in source files (SRC), dependencies (DEP), \ + HELP = """Count the total number lines of code (LOC), source lines of code (SLOC), + and comment lines of code (CLOC) found in source files (SRC), dependencies (DEP), and test files (TEST).""" WIKI = "https://github.com/trailofbits/slither/wiki/Printer-documentation#loc" diff --git a/slither/utils/command_line.py b/slither/utils/command_line.py index f5b9ab452..b8888cb85 100644 --- a/slither/utils/command_line.py +++ b/slither/utils/command_line.py @@ -360,8 +360,10 @@ def output_printers(printer_classes: List[Type[AbstractPrinter]]) -> None: printers_list = sorted(printers_list, key=lambda element: (element[0])) idx = 1 for (argument, help_info) in printers_list: - table.add_row([str(idx), argument, help_info]) + # Clean multi line HELP info + table.add_row([str(idx), argument, " ".join(x.strip() for x in help_info.splitlines())]) idx = idx + 1 + print(table) diff --git a/slither/utils/myprettytable.py b/slither/utils/myprettytable.py index b33fb9c5f..ac666a501 100644 --- a/slither/utils/myprettytable.py +++ b/slither/utils/myprettytable.py @@ -1,3 +1,4 @@ +from shutil import get_terminal_size from typing import List, Dict, Union from prettytable import PrettyTable @@ -7,7 +8,12 @@ from slither.utils.colors import Colors class MyPrettyTable: - def __init__(self, field_names: List[str], pretty_align: bool = True): # TODO: True by default? + def __init__( + self, + field_names: List[str], + pretty_align: bool = True, + max_width: Union[int, None] = "max", # Default value is "max" + ): self._field_names = field_names self._rows: List = [] self._options: Dict = {} @@ -19,6 +25,17 @@ class MyPrettyTable: else: self._options["set_alignment"] = [] + self.max_width = None + if max_width == "max": + # We use (0,0) as a fallback to detect if we are not attached to a terminal + # In this case, we fall back to the default behavior (i.e. printing as much as possible) + terminal_column = get_terminal_size((0, 0)).columns + if terminal_column != 0: + # We reduce slightly the max-width to take into account inconsistencies in terminals + self.max_width = terminal_column - 3 + else: + self.max_width = max_width + def add_row(self, row: List[Union[str, List[str]]]) -> None: self._rows.append(row) @@ -28,6 +45,9 @@ class MyPrettyTable: else: table = PrettyTable(self._field_names) + if self.max_width is not None: + table.max_table_width = self.max_width + for row in self._rows: table.add_row(row) if len(self._options["set_alignment"]): @@ -63,7 +83,5 @@ def make_pretty_table( table_row = [row] + [body[row][key] for key in headers[1:]] table.add_row(table_row) if totals: - table.add_row( - [total_header] + [sum([body[row][key] for row in body]) for key in headers[1:]] - ) + table.add_row([total_header] + [sum(body[row][key] for row in body) for key in headers[1:]]) return table