Skip to content

Commit 638bd90

Browse files
committed
Deep clone fix, Main decluttering
1 parent 9abe6e5 commit 638bd90

File tree

7 files changed

+211
-153
lines changed

7 files changed

+211
-153
lines changed

eburger/main.py

Lines changed: 27 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,26 @@
11
import json
22
import os
3-
from pathlib import Path
4-
import shutil
53
import sys
4+
from pathlib import Path
5+
6+
import eburger.settings as settings
7+
from eburger.serializer import parse_solidity_ast, reduce_json
68
from eburger.utils.cli_args import args
9+
from eburger.utils.compilers import compile_foundry, compile_hardhat, compile_solc
710
from eburger.utils.filesystem import (
811
create_directory_if_not_exists,
9-
create_or_empty_directory,
10-
find_and_read_sol_file,
1112
find_recursive_files_by_patterns,
12-
get_foundry_ast_json,
13-
get_hardhat_ast_json,
14-
get_solidity_version_from_file,
1513
roughly_check_valid_file_path_name,
1614
select_project,
1715
)
1816
from eburger.utils.helpers import (
19-
construct_solc_cmdline,
2017
get_eburger_version,
21-
get_filename_from_path,
22-
is_valid_json,
23-
run_command,
2418
)
2519
from eburger.utils.installers import (
26-
construct_sourceable_nvm_string,
2720
install_foundry_if_not_found,
2821
install_hardhat_if_not_found,
29-
set_solc_compiler_version,
3022
)
3123
from eburger.utils.logger import log
32-
import eburger.settings as settings
33-
from eburger.serializer import parse_solidity_ast, reduce_json
3424
from eburger.utils.outputs import (
3525
calculate_nsloc,
3626
draw_nsloc_table,
@@ -46,15 +36,28 @@ def main():
4636
print(get_eburger_version())
4737
sys.exit(0)
4838

49-
if not args.solidity_file_or_folder and not args.ast_json_file:
39+
if not args.solidity_file_or_folder:
5040
args.solidity_file_or_folder = "."
5141

5242
log("debug", f"Project path: {args.solidity_file_or_folder}")
5343

5444
create_directory_if_not_exists(settings.outputs_dir)
5545
path_type = None
5646

57-
if args.solidity_file_or_folder:
47+
if args.ast_json_file:
48+
filename = args.ast_json_file
49+
filename = filename.replace(".json", "") # Clean possible file extension
50+
filename = filename.replace(
51+
".eburger/", ""
52+
) # Protection against analysis of files inside the output path
53+
with open(args.ast_json_file, "r") as f:
54+
ast_json = json.load(f)
55+
ast_json, src_paths = reduce_json(ast_json)
56+
output_path = settings.outputs_dir / f"{filename}.json"
57+
58+
save_as_json(output_path, ast_json)
59+
60+
elif args.solidity_file_or_folder:
5861
if os.path.isfile(args.solidity_file_or_folder):
5962
log("debug", f"Path type: file")
6063
path_type = "file"
@@ -111,161 +114,40 @@ def main():
111114
)
112115
sys.exit(0)
113116

114-
elif args.ast_json_file:
115-
filename = args.ast_json_file
116-
filename = filename.replace(".json", "") # Clean possible file extension
117-
filename = filename.replace(
118-
".eburger/", ""
119-
) # Protection against analysis of files inside the output path
120-
with open(args.ast_json_file, "r") as f:
121-
ast_json = json.load(f)
122-
ast_json, src_paths = reduce_json(ast_json)
123-
output_path = settings.outputs_dir / f"{filename}.json"
124-
125-
save_as_json(output_path, ast_json)
126-
127117
# NSLOC only mode
128118
if args.nsloc:
129119
draw_nsloc_table()
130120

121+
# Compilation
131122
if path_type is not None:
132-
# Foundry compilation flow
133123
if path_type == "foundry":
134124
log("info", "Foundry project detected, compiling using forge.")
135125
_, forge_full_path_binary_found = install_foundry_if_not_found()
136126

137127
if args.solc_remappings:
138128
log("warning", "Ignoring the -r option in foundry based projects.")
139129

140-
# Call foundry's full path if necessary, otherwise use the bins available through PATH
141-
forge_clean_command = "forge clean"
142-
if forge_full_path_binary_found:
143-
forge_clean_command = (
144-
f"{os.environ.get('HOME')}/.foundry/bin/{forge_clean_command}"
145-
)
146-
147-
run_command(forge_clean_command, directory=args.solidity_file_or_folder)
148-
forge_out_dir = settings.outputs_dir / "forge-output"
149-
create_or_empty_directory(forge_out_dir)
150-
151-
foundry_excluded_dirs = " ".join(
152-
[item for item in settings.excluded_dirs if item != "lib"]
153-
)
154-
# Call foundry's full path if necessary, otherwise use the bins available through PATH
155-
forge_build_command = f"forge build --force --skip {foundry_excluded_dirs} --build-info --build-info-path {forge_out_dir}"
156-
if forge_full_path_binary_found:
157-
forge_build_command = (
158-
f"{os.environ.get('HOME')}/.foundry/bin/{forge_build_command}"
159-
)
160-
161-
run_command(
162-
forge_build_command,
163-
directory=args.solidity_file_or_folder,
164-
live_output=args.debug,
130+
output_filename, ast_json, filename, src_paths = compile_foundry(
131+
forge_full_path_binary_found
165132
)
166-
sample_file_path = find_and_read_sol_file(args.solidity_file_or_folder)
167-
filename, output_filename = get_filename_from_path(sample_file_path)
168-
ast_json = get_foundry_ast_json(forge_out_dir)
169-
ast_json, src_paths = reduce_json(ast_json)
170133
save_as_json(output_filename, ast_json)
171134

172-
# Hardhat compilation flow
173135
elif path_type == "hardhat":
174136
log("info", "Hardhat project detected, compiling using hardhat.")
175137
install_hardhat_if_not_found()
176138

177139
if args.solc_remappings:
178140
log("warning", "Ignoring the -r option in hardhat based projects.")
179141

180-
# try runing npx normally, as a fallback try the construct_sourceable_nvm_string method
181-
# if a user hadn't got npx installed / or it's not on path (meaning it was installed in same run as the analysis)
182-
# it still needs the fallback option
183-
try:
184-
run_command(f"npx hardhat clean", directory=settings.project_root)
185-
run_command(
186-
f"npx hardhat compile --force",
187-
directory=settings.project_root,
188-
live_output=args.debug,
189-
)
190-
except FileNotFoundError:
191-
run_command(
192-
construct_sourceable_nvm_string("npx hardhat clean"),
193-
directory=settings.project_root,
194-
)
195-
run_command(
196-
construct_sourceable_nvm_string("npx hardhat compile --force"),
197-
directory=settings.project_root,
198-
live_output=args.debug,
199-
)
200-
201-
# Copy compilation results to .eburger
202-
expected_hardhat_outfiles = os.path.join(
203-
args.solidity_file_or_folder, "artifacts", "build-info"
204-
)
205-
if not os.path.isdir(expected_hardhat_outfiles):
206-
log(
207-
"error",
208-
f"Hardhat's compilation files were not found in expected location {expected_hardhat_outfiles}",
209-
)
210-
hardhat_out_dir = settings.outputs_dir / "hardhat-output"
211-
if os.path.exists(hardhat_out_dir):
212-
shutil.rmtree(hardhat_out_dir)
213-
shutil.copytree(expected_hardhat_outfiles, hardhat_out_dir)
214-
215-
sample_file_path = find_and_read_sol_file(args.solidity_file_or_folder)
216-
filename, output_filename = get_filename_from_path(sample_file_path)
217-
218-
ast_json = get_hardhat_ast_json(hardhat_out_dir)
219-
ast_json, src_paths = reduce_json(ast_json)
142+
output_filename, ast_json, filename, src_paths = compile_hardhat()
220143
save_as_json(output_filename, ast_json)
221144

222145
# solc compilation flow
223146
elif path_type in ["file", "folder"]:
224-
try:
225-
run_command("solc --version")
226-
run_command("solc-select versions")
227-
except FileNotFoundError:
228-
log(
229-
"info",
230-
"Please ensure solc and solc-select are installed and availble globally, and run again.",
231-
)
232-
sys.exit(0)
233-
234-
sample_file_path = args.solidity_file_or_folder
235-
compilation_source_path = args.solidity_file_or_folder
236-
237-
if path_type == "folder":
238-
sample_file_path = find_and_read_sol_file(args.solidity_file_or_folder)
239-
240-
filename, output_filename = get_filename_from_path(sample_file_path)
147+
output_filename, ast_json, filename, src_paths, solc_compile_res_parsed = (
148+
compile_solc(path_type)
149+
)
241150

242-
if args.solc_compiler_version:
243-
solc_required_version = args.solc_compiler_version
244-
else:
245-
solc_required_version = get_solidity_version_from_file(sample_file_path)
246-
set_solc_compiler_version(solc_required_version)
247-
solc_cmdline = construct_solc_cmdline(path_type, compilation_source_path)
248-
if solc_cmdline is None:
249-
log("error", "Error constructing solc command line")
250-
251-
solc_compile_res, _ = run_command(solc_cmdline, live_output=args.debug)
252-
253-
# We continue as long as solc compiled something
254-
if not is_valid_json(solc_compile_res):
255-
error_string = "Locally installed solc errored out trying to compile the contract. Please review comiler warnings above"
256-
if not args.solc_remappings:
257-
error_string += (
258-
"or see if library remappings (using the -r option) are needed"
259-
)
260-
if not args.solc_compiler_version:
261-
error_string += ", or try specifing the solidity compiler version (using the -s option)"
262-
error_string += "."
263-
log(
264-
"error",
265-
error_string,
266-
)
267-
solc_compile_res_parsed = json.loads("".join(solc_compile_res))
268-
ast_json, src_paths = reduce_json(solc_compile_res_parsed)
269151
save_as_json(output_filename, solc_compile_res_parsed)
270152

271153
# Parse AST

eburger/template_utils.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import copy
12
import re
23
from typing import Union
34

@@ -211,3 +212,12 @@ def function_def_has_following_check_statements(
211212

212213
# If we go through all statements following the target node without finding a validation, return False
213214
return False
215+
216+
217+
def deep_clone_node(node: dict):
218+
"""
219+
Deep clone an AST node for template level manipulations.
220+
221+
:param node: AST Node to clone.
222+
"""
223+
return copy.deepcopy(node)

eburger/templates/missing_reentrancy_guard.yaml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
version: 1.0.5
1+
version: 1.0.6
22
name: "Missing Reentracy Guard"
33
severity: "Low"
44
precision: "Low"
@@ -35,10 +35,11 @@ python: |
3535
function_node_body = function_node.get("body", {})
3636
3737
# Ignore .call usage within the first entries of the function (where reentrancy doesn't affect anything)
38-
if function_node_body.get("statements"):
39-
function_node_body["statements"] = function_node_body["statements"][1:]
38+
function_node_body_clone = deep_clone_node(function_node_body)
39+
if function_node_body_clone.get("statements"):
40+
function_node_body_clone["statements"] = function_node_body["statements"][1:]
4041
41-
call_nodes = get_nodes_by_signature(function_node_body, "function (bytes memory) payable returns (bool,bytes memory)")
42+
call_nodes = get_nodes_by_signature(function_node_body_clone, "function (bytes memory) payable returns (bool,bytes memory)")
4243
4344
if call_nodes:
4445
results.append(function_node)

0 commit comments

Comments
 (0)