Skip to content

Commit a77738f

Browse files
authored
Merge pull request #2658 from crytic/dev
Sync Master <> Dev
2 parents 1cf17ec + f6413e6 commit a77738f

18 files changed

+151
-32
lines changed

.github/workflows/publish.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ jobs:
4444
path: dist/
4545

4646
- name: publish
47-
uses: pypa/[email protected].3
47+
uses: pypa/[email protected].4
4848

4949
- name: sign
5050
uses: sigstore/[email protected]

README.md

+15-8
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ python3 -m pip install slither-analyzer
8282
python3 -m pip install --upgrade slither-analyzer
8383
```
8484

85+
### Using Brew
86+
87+
```console
88+
brew install slither-analyzer
89+
```
90+
8591
### Using Git
8692

8793
```bash
@@ -159,7 +165,7 @@ Num | Detector | What it Detects | Impact | Confidence
159165
35 | `locked-ether` | [Contracts that lock ether](https://github.com/crytic/slither/wiki/Detector-Documentation#contracts-that-lock-ether) | Medium | High
160166
36 | `mapping-deletion` | [Deletion on mapping containing a structure](https://github.com/crytic/slither/wiki/Detector-Documentation#deletion-on-mapping-containing-a-structure) | Medium | High
161167
37 | `pyth-deprecated-functions` | [Detect Pyth deprecated functions](https://github.com/crytic/slither/wiki/Detector-Documentation#pyth-deprecated-functions) | Medium | High
162-
38 | `pyth-unchecked-confidence` | [Detect when the confidence level of a Pyth price is not checked](https://github.com/crytic/slither/wiki/Detector-Documentation#pyth-unchecked-confidence) | Medium | High
168+
38 | `pyth-unchecked-confidence` | [Detect when the confidence level of a Pyth price is not checked](https://github.com/crytic/slither/wiki/Detector-Documentation#pyth-unchecked-confidence-level) | Medium | High
163169
39 | `pyth-unchecked-publishtime` | [Detect when the publishTime of a Pyth price is not checked](https://github.com/crytic/slither/wiki/Detector-Documentation#pyth-unchecked-publishtime) | Medium | High
164170
40 | `shadowing-abstract` | [State variables shadowing from abstract contracts](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variable-shadowing-from-abstract-contracts) | Medium | High
165171
41 | `tautological-compare` | [Comparing a variable to itself always returns true or false, depending on comparison](https://github.com/crytic/slither/wiki/Detector-Documentation#tautological-compare) | Medium | High
@@ -179,9 +185,9 @@ Num | Detector | What it Detects | Impact | Confidence
179185
55 | `unchecked-send` | [Unchecked send](https://github.com/crytic/slither/wiki/Detector-Documentation#unchecked-send) | Medium | Medium
180186
56 | `uninitialized-local` | [Uninitialized local variables](https://github.com/crytic/slither/wiki/Detector-Documentation#uninitialized-local-variables) | Medium | Medium
181187
57 | `unused-return` | [Unused return values](https://github.com/crytic/slither/wiki/Detector-Documentation#unused-return) | Medium | Medium
182-
58 | `chainlink-feed-registry` | [Detect when chainlink feed registry is used](https://github.com/crytic/slither/wiki/Detector-Documentation#chainlink-feed-registry) | Low | High
188+
58 | `chainlink-feed-registry` | [Detect when chainlink feed registry is used](https://github.com/crytic/slither/wiki/Detector-Documentation#chainlink-feed-registry-usage) | Low | High
183189
59 | `incorrect-modifier` | [Modifiers that can return the default value](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-modifier) | Low | High
184-
60 | `optimism-deprecation` | [Detect when deprecated Optimism predeploy or function is used.](https://github.com/crytic/slither/wiki/Detector-Documentation#optimism-deprecation) | Low | High
190+
60 | `optimism-deprecation` | [Detect when deprecated Optimism predeploy or function is used.](https://github.com/crytic/slither/wiki/Detector-Documentation#optimism-deprecated-predeploy-or-function) | Low | High
185191
61 | `shadowing-builtin` | [Built-in symbol shadowing](https://github.com/crytic/slither/wiki/Detector-Documentation#builtin-symbol-shadowing) | Low | High
186192
62 | `shadowing-local` | [Local variables shadowing](https://github.com/crytic/slither/wiki/Detector-Documentation#local-variable-shadowing) | Low | High
187193
63 | `uninitialized-fptr-cst` | [Uninitialized function pointer calls in constructors](https://github.com/crytic/slither/wiki/Detector-Documentation#uninitialized-function-pointers-in-constructors) | Low | High
@@ -203,7 +209,7 @@ Num | Detector | What it Detects | Impact | Confidence
203209
79 | `deprecated-standards` | [Deprecated Solidity Standards](https://github.com/crytic/slither/wiki/Detector-Documentation#deprecated-standards) | Informational | High
204210
80 | `erc20-indexed` | [Un-indexed ERC20 event parameters](https://github.com/crytic/slither/wiki/Detector-Documentation#unindexed-erc20-event-parameters) | Informational | High
205211
81 | `function-init-state` | [Function initializing state variables](https://github.com/crytic/slither/wiki/Detector-Documentation#function-initializing-state) | Informational | High
206-
82 | `incorrect-using-for` | [Detects using-for statement usage when no function from a given library matches a given type](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-using-for-usage) | Informational | High
212+
82 | `incorrect-using-for` | [Detects using-for statement usage when no function from a given library matches a given type](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-usage-of-using-for-statement) | Informational | High
207213
83 | `low-level-calls` | [Low level calls](https://github.com/crytic/slither/wiki/Detector-Documentation#low-level-calls) | Informational | High
208214
84 | `missing-inheritance` | [Missing inheritance](https://github.com/crytic/slither/wiki/Detector-Documentation#missing-inheritance) | Informational | High
209215
85 | `naming-convention` | [Conformity to Solidity naming conventions](https://github.com/crytic/slither/wiki/Detector-Documentation#conformance-to-solidity-naming-conventions) | Informational | High
@@ -232,10 +238,11 @@ For more information, see
232238

233239
### Quick Review Printers
234240

235-
* `human-summary`: [Print a human-readable summary of the contracts](https://github.com/crytic/slither/wiki/Printer-documentation#human-summary)
236-
* `inheritance-graph`: [Export the inheritance graph of each contract to a dot file](https://github.com/crytic/slither/wiki/Printer-documentation#inheritance-graph)
237-
* `contract-summary`: [Print a summary of the contracts](https://github.com/crytic/slither/wiki/Printer-documentation#contract-summary)
238-
* `loc`: [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).](https://github.com/crytic/slither/wiki/Printer-documentation#loc)
241+
* `human-summary`: [Print a human-readable summary of the contracts](https://github.com/trailofbits/slither/wiki/Printer-documentation#human-summary)
242+
* `inheritance-graph`: [Export the inheritance graph of each contract to a dot file](https://github.com/trailofbits/slither/wiki/Printer-documentation#inheritance-graph)
243+
* `contract-summary`: [Print a summary of the contracts](https://github.com/trailofbits/slither/wiki/Printer-documentation#contract-summary)
244+
* `loc`: [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).](https://github.com/trailofbits/slither/wiki/Printer-documentation#loc)
245+
* `entry-points`: [Print all the state-changing entry point functions of the contracts](https://github.com/trailofbits/slither/wiki/Printer-documentation#entry-points)
239246

240247
### In-Depth Review Printers
241248

slither/detectors/functions/chainlink_feed_registry.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class ChainlinkFeedRegistry(AbstractDetector):
1515
IMPACT = DetectorClassification.LOW
1616
CONFIDENCE = DetectorClassification.HIGH
1717

18-
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#chainlink-feed-registry"
18+
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#chainlink-feed-registry-usage"
1919

2020
WIKI_TITLE = "Chainlink Feed Registry usage"
2121
WIKI_DESCRIPTION = "Detect when Chainlink Feed Registry is used. At the moment is only available on Ethereum Mainnet."

slither/detectors/functions/optimism_deprecation.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class OptimismDeprecation(AbstractDetector):
1818
IMPACT = DetectorClassification.LOW
1919
CONFIDENCE = DetectorClassification.HIGH
2020

21-
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#optimism-deprecation"
21+
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#optimism-deprecated-predeploy-or-function"
2222

2323
WIKI_TITLE = "Optimism deprecated predeploy or function"
2424
WIKI_DESCRIPTION = "Detect when deprecated Optimism predeploy or function is used."

slither/detectors/statements/incorrect_using_for.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ class IncorrectUsingFor(AbstractDetector):
165165
IMPACT = DetectorClassification.INFORMATIONAL
166166
CONFIDENCE = DetectorClassification.HIGH
167167

168-
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-using-for-usage"
168+
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-usage-of-using-for-statement"
169169

170170
WIKI_TITLE = "Incorrect usage of using-for statement"
171171
WIKI_DESCRIPTION = (

slither/detectors/statements/pyth_unchecked.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ def _detect(self) -> List[Output]:
2424
for contract in self.compilation_unit.contracts_derived:
2525
for target_contract, ir in contract.all_high_level_calls:
2626
if target_contract.name == "IPyth" and ir.function_name in self.PYTH_FUNCTIONS:
27-
# We know for sure the second IR in the node is an Assignment operation of the TMP variable. Example:
27+
# We know for sure the last IR in the node is an Assignment operation of the TMP variable. Example:
2828
# Expression: price = pyth.getEmaPriceNoOlderThan(id,age)
2929
# IRs:
3030
# TMP_0(PythStructs.Price) = HIGH_LEVEL_CALL, dest:pyth(IPyth), function:getEmaPriceNoOlderThan, arguments:['id', 'age']
3131
# price(PythStructs.Price) := TMP_0(PythStructs.Price)
32-
assert isinstance(ir.node.irs[1], Assignment)
33-
return_variable = ir.node.irs[1].lvalue
32+
assert isinstance(ir.node.irs[len(ir.node.irs) - 1], Assignment)
33+
return_variable = ir.node.irs[len(ir.node.irs) - 1].lvalue
3434
checked = False
3535

3636
possible_unchecked_variable_ir = None

slither/detectors/statements/pyth_unchecked_confidence.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class PythUncheckedConfidence(PythUnchecked):
1212
IMPACT = DetectorClassification.MEDIUM
1313
CONFIDENCE = DetectorClassification.HIGH
1414

15-
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#pyth-unchecked-confidence"
15+
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#pyth-unchecked-confidence-level"
1616
WIKI_TITLE = "Pyth unchecked confidence level"
1717
WIKI_DESCRIPTION = "Detect when the confidence level of a Pyth price is not checked"
1818
WIKI_RECOMMENDATION = "Check the confidence level of a Pyth price. Visit https://docs.pyth.network/price-feeds/best-practices#confidence-intervals for more information."

slither/printers/all_printers.py

+1
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@
2525
from .functions.dominator import Dominator
2626
from .summary.martin import Martin
2727
from .summary.cheatcodes import CheatcodePrinter
28+
from .summary.entry_points import PrinterEntryPoints
+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
"""
2+
Module printing all the state-changing entry point functions of the contracts
3+
"""
4+
5+
from slither.printers.abstract_printer import AbstractPrinter
6+
from slither.core.declarations.function_contract import FunctionContract
7+
from slither.utils.colors import Colors
8+
from slither.utils.output import Output
9+
from slither.utils.myprettytable import MyPrettyTable
10+
11+
12+
class PrinterEntryPoints(AbstractPrinter):
13+
14+
ARGUMENT = "entry-points"
15+
HELP = "Print all the state-changing entry point functions of the contracts"
16+
17+
WIKI = "https://github.com/trailofbits/slither/wiki/Printer-documentation#entry-points"
18+
19+
def output(self, _filename) -> Output:
20+
"""
21+
_filename is not used
22+
Args:
23+
_filename(string)
24+
"""
25+
all_contracts = []
26+
27+
for contract in sorted(
28+
(
29+
c
30+
for c in self.contracts
31+
if not c.is_interface
32+
and not c.is_library
33+
and not c.is_abstract
34+
and "lib/" not in c.source_mapping.filename.absolute
35+
and "node_modules/" not in c.source_mapping.filename.absolute
36+
and not any(
37+
mock in c.source_mapping.filename.absolute.lower() for mock in ["mock", "mocks"]
38+
)
39+
),
40+
key=lambda x: x.name,
41+
):
42+
entry_points = [
43+
f
44+
for f in contract.functions
45+
if (
46+
f.visibility in ["public", "external"]
47+
and isinstance(f, FunctionContract)
48+
and not f.is_constructor
49+
and not f.view
50+
and not f.pure
51+
and not f.contract_declarer.is_interface
52+
and not f.contract_declarer.is_library
53+
and not f.is_shadowed
54+
)
55+
]
56+
57+
if not entry_points:
58+
continue
59+
60+
table = MyPrettyTable(["Function", "Modifiers", "Inherited From"])
61+
contract_info = [
62+
f"\nContract {Colors.BOLD}{Colors.YELLOW}{contract.name}{Colors.END}"
63+
f" ({contract.source_mapping})"
64+
]
65+
66+
for f in sorted(
67+
entry_points,
68+
key=lambda x: (x.visibility != "external", x.visibility != "public", x.full_name),
69+
):
70+
modifier_list = [m.name for m in f.modifiers]
71+
if f.payable:
72+
modifier_list.append("payable")
73+
modifiers = ", ".join(modifier_list) if modifier_list else ""
74+
inherited = f"{f.contract_declarer.name}" if f.contract_declarer != contract else ""
75+
76+
name_parts = f.full_name.split("(", 1)
77+
function_name = (
78+
f"{Colors.BOLD}{Colors.RED}{name_parts[0]}{Colors.END}" f"({name_parts[1]}"
79+
)
80+
81+
table.add_row(
82+
[
83+
function_name,
84+
f"{Colors.GREEN}{modifiers}{Colors.END}" if modifiers else "",
85+
f"{Colors.MAGENTA}{inherited}{Colors.END}" if inherited else "",
86+
]
87+
)
88+
89+
contract_info.append(str(table))
90+
all_contracts.append("\n".join(contract_info))
91+
92+
info = "\n".join(all_contracts) if all_contracts else ""
93+
self.info(info)
94+
return self.generate_output(info)

slither/tools/mutator/README.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ options:
3333
--timeout TIMEOUT Set timeout for test command (by default 30 seconds)
3434
--output-dir OUTPUT_DIR
3535
Name of output directory (by default 'mutation_campaign')
36-
-v, --verbose log mutants that are caught as well as those that are uncaught
37-
-vv, --very-verbose log mutants that are caught, uncaught, and fail to compile. And more!
36+
-v, --verbose log mutants that are caught, uncaught, and fail to compile
3837
--mutators-to-run MUTATORS_TO_RUN
3938
mutant generators to run
4039
--contract-names CONTRACT_NAMES

slither/tools/mutator/__main__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def parse_args() -> argparse.Namespace:
7272
parser.add_argument(
7373
"-v",
7474
"--verbose",
75-
help="log mutants that are caught as well as those that are uncaught",
75+
help="log mutants that are caught, uncaught, and fail to compile",
7676
action="store_true",
7777
default=False,
7878
)

slither/tools/mutator/mutators/AOR.py

-9
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@
22
from slither.slithir.operations import Binary, BinaryType
33
from slither.tools.mutator.utils.patch import create_patch_with_line
44
from slither.tools.mutator.mutators.abstract_mutator import AbstractMutator
5-
from slither.core.variables.variable import Variable
65
from slither.core.expressions.unary_operation import UnaryOperation
76
from slither.core.expressions.call_expression import CallExpression
87
from slither.core.expressions.member_access import MemberAccess
9-
from slither.core.expressions.identifier import Identifier
10-
from slither.core.solidity_types.array_type import ArrayType
118

129
arithmetic_operators = [
1310
BinaryType.ADDITION,
@@ -42,9 +39,6 @@ def _mutate(self) -> Dict:
4239
isinstance(ir_expression, CallExpression)
4340
and isinstance(ir_expression.called, MemberAccess)
4441
and ir_expression.called.member_name == "pop"
45-
and isinstance(ir_expression.called.expression, Identifier)
46-
and isinstance(ir_expression.called.expression.value, Variable)
47-
and isinstance(ir_expression.called.expression.value.type, ArrayType)
4842
):
4943
continue
5044

@@ -58,9 +52,6 @@ def _mutate(self) -> Dict:
5852
if isinstance(ir_expression, CallExpression)
5953
and isinstance(ir_expression.called, MemberAccess)
6054
and ir_expression.called.member_name == "push"
61-
and isinstance(ir_expression.called.expression, Identifier)
62-
and isinstance(ir_expression.called.expression.value, Variable)
63-
and isinstance(ir_expression.called.expression.value.type, ArrayType)
6455
else node.irs
6556
)
6657

Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1-
Pyth price conf field is not checked in C.bad(bytes32,uint256) (tests/e2e/detectors/test_data/pyth-unchecked-confidence/0.8.20/pyth_unchecked_confidence.sol#171-175)
2-
- price = pyth.getEmaPriceNoOlderThan(id,age) (tests/e2e/detectors/test_data/pyth-unchecked-confidence/0.8.20/pyth_unchecked_confidence.sol#172)
1+
Pyth price conf field is not checked in C.bad2(C.Data) (tests/e2e/detectors/test_data/pyth-unchecked-confidence/0.8.20/pyth_unchecked_confidence.sol#182-186)
2+
- price = pyth.getEmaPriceNoOlderThan(data.id,data.age) (tests/e2e/detectors/test_data/pyth-unchecked-confidence/0.8.20/pyth_unchecked_confidence.sol#183)
3+
4+
Pyth price conf field is not checked in C.bad(bytes32,uint256) (tests/e2e/detectors/test_data/pyth-unchecked-confidence/0.8.20/pyth_unchecked_confidence.sol#176-180)
5+
- price = pyth.getEmaPriceNoOlderThan(id,age) (tests/e2e/detectors/test_data/pyth-unchecked-confidence/0.8.20/pyth_unchecked_confidence.sol#177)
36

Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1-
Pyth price publishTime field is not checked in C.bad(bytes32) (tests/e2e/detectors/test_data/pyth-unchecked-publishtime/0.8.20/pyth_unchecked_publishtime.sol#171-175)
2-
- price = pyth.getEmaPriceUnsafe(id) (tests/e2e/detectors/test_data/pyth-unchecked-publishtime/0.8.20/pyth_unchecked_publishtime.sol#172)
1+
Pyth price publishTime field is not checked in C.bad(bytes32) (tests/e2e/detectors/test_data/pyth-unchecked-publishtime/0.8.20/pyth_unchecked_publishtime.sol#175-179)
2+
- price = pyth.getEmaPriceUnsafe(id) (tests/e2e/detectors/test_data/pyth-unchecked-publishtime/0.8.20/pyth_unchecked_publishtime.sol#176)
3+
4+
Pyth price publishTime field is not checked in C.bad2(C.Data) (tests/e2e/detectors/test_data/pyth-unchecked-publishtime/0.8.20/pyth_unchecked_publishtime.sol#181-185)
5+
- price = pyth.getEmaPriceUnsafe(data.id) (tests/e2e/detectors/test_data/pyth-unchecked-publishtime/0.8.20/pyth_unchecked_publishtime.sol#182)
36

tests/e2e/detectors/test_data/pyth-unchecked-confidence/0.8.20/pyth_unchecked_confidence.sol

+11
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,11 @@ interface IPyth {
164164
contract C {
165165
IPyth pyth;
166166

167+
struct Data {
168+
bytes32 id;
169+
uint256 age;
170+
}
171+
167172
constructor(IPyth _pyth) {
168173
pyth = _pyth;
169174
}
@@ -174,6 +179,12 @@ contract C {
174179
// Use price
175180
}
176181

182+
function bad2(Data calldata data) public {
183+
PythStructs.Price memory price = pyth.getEmaPriceNoOlderThan(data.id, data.age);
184+
require(price.publishTime > block.timestamp - 120);
185+
// Use price
186+
}
187+
177188
function good(bytes32 id, uint256 age) public {
178189
PythStructs.Price memory price = pyth.getEmaPriceNoOlderThan(id, age);
179190
require(price.conf < 10000);

0 commit comments

Comments
 (0)