Merge commit '693d4d12095ac5a8db0e40594afa840ba6501366' into dev

pull/74/head
Josselin 6 years ago
commit 70e0318b90
  1. 104
      scripts/pretty_print_and_sort_json.py
  2. 38
      scripts/tests_generate_expected_json.sh
  3. 93
      scripts/travis_test.sh
  4. 2
      slither/detectors/functions/arbitrary_send.py
  5. 22
      tests/expected_json/arbitrary_send.arbitrary-send.json
  6. 11
      tests/expected_json/backdoor.backdoor.json
  7. 13
      tests/expected_json/backdoor.suicidal.json
  8. 64
      tests/expected_json/const_state_variables.constable-states.json
  9. 46
      tests/expected_json/external_function.external-function.json
  10. 9
      tests/expected_json/inline_assembly_contract.assembly.json
  11. 16
      tests/expected_json/inline_assembly_library.assembly.json
  12. 16
      tests/expected_json/locked_ether.locked-ether.json
  13. 9
      tests/expected_json/low_level_calls.low-level-calls.json
  14. 131
      tests/expected_json/naming_convention.naming-convention.json
  15. 15
      tests/expected_json/old_solc.sol.json.solc-version.json
  16. 21
      tests/expected_json/pragma.0.4.24.pragma.json
  17. 24
      tests/expected_json/reentrancy.reentrancy.json
  18. 16
      tests/expected_json/tx_origin.tx-origin.json
  19. 86
      tests/expected_json/uninitialized.uninitialized-state.json
  20. 21
      tests/expected_json/uninitialized_storage_pointer.uninitialized-storage.json
  21. 17
      tests/expected_json/unused_state.unused-state.json

@ -0,0 +1,104 @@
#!/usr/bin/python3
'''
the purpose of this file is to sort the json output from the detectors such that
the order is deterministic
- the keys of a json object are sorted
- json objects in a list will be sorted based on the values of their keys
- lists of strings/numbers are sorted
'''
import sys
import json
raw_json_file = sys.argv[1]
pretty_json_file = sys.argv[2]
from collections import OrderedDict
def create_property_val_tuple(d, props_info):
p_names = props_info[0]
p_types = props_info[1]
result = []
for p in p_names:
if not p in d: # not all objects have the same keys
if p_types[p] is 'number':
result.append(0) # to make sorting work
if p_types[p] is 'string':
result.append("") # to make sorting work
else:
result.append(d[p])
return tuple(result)
def get_props_info(list_of_dicts):
found_props = set()
prop_types = dict()
# gather all prop names
for d in list_of_dicts:
for p in d:
found_props.add(p)
# create a copy, since we are gonna possibly remove props
props_whose_value_we_can_sort_on = set(found_props)
# for each object, loop through list of all found property names,
# if the object contains that property, check that it's of type string or number
# if it is, save it's type (used later on for sorting with objects that don't have that property)
# if it's not of type string/number remove it from list of properties to check
# since we cannot sort on non-string/number values
for p in list(found_props):
if p in props_whose_value_we_can_sort_on: # short circuit
for d in list_of_dicts:
if p in props_whose_value_we_can_sort_on: # less shorter short circuit
if p in d:
# we ae only gonna sort key values if they are of type string or number
if not isinstance(d[p], str) and not isinstance(d[p], int):
props_whose_value_we_can_sort_on.remove(p)
# we need to store the type of the value because not each object
# in a list of output objects for 1 detector will have the same
# keys, so if we want to sort based on the values then if a certain object
# does not have a key which another object does have we are gonna
# put in 0 for number and "" for string for that key such that sorting on values
# still works
elif isinstance(d[p], str):
prop_types[p] = 'string'
elif isinstance(d[p], int):
prop_types[p] = 'number'
return (sorted(list(props_whose_value_we_can_sort_on)), prop_types)
def order_by_prop_value(list_of_dicts):
props_info = get_props_info(list_of_dicts)
return sorted(list_of_dicts, key=lambda d: create_property_val_tuple(d, props_info))
def order_dict(d):
result = OrderedDict() # such that we keep the order
for k, v in sorted(d.items()):
if isinstance(v, dict):
result[k] = order_dict(v)
elif type(v) is list:
result[k] = order_list(v)
else: # string/number
result[k] = v
return result
def order_list(l):
# TODO: sometimes slither detectors return a null value in the json output sourceMapping object array
# get rid of those values, it will break sorting (some items are an object, some are null?!)
l = list(filter(None, l))
if not l:
return []
if isinstance(l[0], str): # it's a list of string
return sorted(l)
elif isinstance(l[0], int): # it's a list of numbers
return sorted(l)
elif isinstance(l[0], dict): # it's a list of objects
ordered_by_key = [order_dict(v) for v in l]
ordered_by_val = order_by_prop_value(ordered_by_key)
return ordered_by_val
with open(raw_json_file, 'r') as json_data:
with open(pretty_json_file, 'w') as out_file:
out_file.write(json.dumps(order_list(json.load(json_data)), sort_keys=False, indent=4, separators=(',',': ')))

@ -0,0 +1,38 @@
#!/usr/bin/env bash
DIR="$(cd "$(dirname "$0")" && pwd)"
# generate_expected_json file.sol detectors
generate_expected_json(){
# generate output filename
# e.g. file: uninitialized.sol detector: uninitialized-state
# ---> uninitialized.uninitialized-state.json
output_filename="$(basename $1 .sol).$2.json"
# run slither detector on input file and save output as json
slither "$1" --disable-solc-warnings --detect "$2" --json "$DIR/tmp-gen.json"
# convert json file to pretty print and write to destination folder
python "$DIR/pretty_print_and_sort_json.py" "$DIR/tmp-gen.json" "$DIR/../tests/expected_json/$output_filename"
# remove the raw un-prettified json file
rm "$DIR/tmp-gen.json"
}
generate_expected_json tests/uninitialized.sol "uninitialized-state"
generate_expected_json tests/backdoor.sol "backdoor"
generate_expected_json tests/backdoor.sol "suicidal"
generate_expected_json tests/pragma.0.4.24.sol "pragma"
generate_expected_json tests/old_solc.sol.json "solc-version"
generate_expected_json tests/reentrancy.sol "reentrancy"
generate_expected_json tests/uninitialized_storage_pointer.sol "uninitialized-storage"
generate_expected_json tests/tx_origin.sol "tx-origin"
generate_expected_json tests/unused_state.sol "unused-state"
generate_expected_json tests/locked_ether.sol "locked-ether"
generate_expected_json tests/arbitrary_send.sol "arbitrary-send"
generate_expected_json tests/inline_assembly_contract.sol "assembly"
generate_expected_json tests/inline_assembly_library.sol "assembly"
generate_expected_json tests/low_level_calls.sol "low-level-calls"
generate_expected_json tests/const_state_variables.sol "constable-states"
generate_expected_json tests/external_function.sol "external-function"
generate_expected_json tests/naming_convention.sol "naming-convention"

@ -2,37 +2,80 @@
### Test Detectors ### Test Detectors
# test_slither file.sol detectors number_results DIR="$(cd "$(dirname "$0")" && pwd)"
# test_slither file.sol detectors
test_slither(){ test_slither(){
slither "$1" --disable-solc-warnings --detect "$2"
if [ $? -ne "$3" ]; then expected="$DIR/../tests/expected_json/$(basename $1 .sol).$2.json"
exit 1 actual="$DIR/$(basename $1 .sol).$2.json"
# run slither detector on input file and save output as json
slither "$1" --disable-solc-warnings --detect "$2" --json "$DIR/tmp-test.json"
# convert json file to pretty print and write to destination folder
python "$DIR/pretty_print_and_sort_json.py" "$DIR/tmp-test.json" "$actual"
# remove the raw un-prettified json file
rm "$DIR/tmp-test.json"
result=$(diff "$expected" "$actual")
if [ "$result" != "" ]; then
rm "$actual"
echo ""
echo "failed test of file: $1, detector: $2"
echo ""
echo "$result"
echo ""
exit 1
else
rm "$actual"
fi fi
slither "$1" --disable-solc-warnings --detect "$2" --compact-ast # run slither detector on input file and save output as json
if [ $? -ne "$3" ]; then slither "$1" --disable-solc-warnings --detect "$2" --compact-ast --json "$DIR/tmp-test.json"
exit 1
# convert json file to pretty print and write to destination folder
python "$DIR/pretty_print_and_sort_json.py" "$DIR/tmp-test.json" "$actual"
# remove the raw un-prettified json file
rm "$DIR/tmp-test.json"
result=$(diff "$expected" "$actual")
if [ "$result" != "" ]; then
rm "$actual"
echo ""
echo "failed test of file: $1, detector: $2"
echo ""
echo "$result"
echo ""
exit 1
else
rm "$actual"
fi fi
} }
test_slither tests/uninitialized.sol "uninitialized-state" 4
test_slither tests/backdoor.sol "backdoor" 1 test_slither tests/uninitialized.sol "uninitialized-state"
test_slither tests/backdoor.sol "suicidal" 1 test_slither tests/backdoor.sol "backdoor"
test_slither tests/pragma.0.4.24.sol "pragma" 1 test_slither tests/backdoor.sol "suicidal"
test_slither tests/old_solc.sol.json "solc-version" 1 test_slither tests/pragma.0.4.24.sol "pragma"
test_slither tests/reentrancy.sol "reentrancy" 1 test_slither tests/old_solc.sol.json "solc-version"
test_slither tests/uninitialized_storage_pointer.sol "uninitialized-storage" 1 test_slither tests/reentrancy.sol "reentrancy"
test_slither tests/tx_origin.sol "tx-origin" 2 test_slither tests/uninitialized_storage_pointer.sol "uninitialized-storage"
test_slither tests/unused_state.sol "unused-state" 1 test_slither tests/tx_origin.sol "tx-origin"
test_slither tests/locked_ether.sol "locked-ether" 1 test_slither tests/unused_state.sol "unused-state"
test_slither tests/arbitrary_send.sol "arbitrary-send" 2 test_slither tests/locked_ether.sol "locked-ether"
#test_slither tests/complex_func.sol "complex-function" 3 test_slither tests/arbitrary_send.sol "arbitrary-send"
test_slither tests/inline_assembly_contract.sol "assembly" 1 test_slither tests/inline_assembly_contract.sol "assembly"
test_slither tests/inline_assembly_library.sol "assembly" 2 test_slither tests/inline_assembly_library.sol "assembly"
test_slither tests/low_level_calls.sol "low-level-calls" 1 test_slither tests/low_level_calls.sol "low-level-calls"
test_slither tests/const_state_variables.sol "constable-states" 2 test_slither tests/const_state_variables.sol "constable-states"
test_slither tests/external_function.sol "external-function" 4 test_slither tests/external_function.sol "external-function"
test_slither tests/naming_convention.sol "naming-convention" 12 test_slither tests/naming_convention.sol "naming-convention"
#test_slither tests/complex_func.sol "complex-function"
### Test scripts ### Test scripts

@ -35,7 +35,7 @@ class ArbitrarySend(AbstractDetector):
@staticmethod @staticmethod
def arbitrary_send(func): def arbitrary_send(func):
""" """
""" """
if func.is_protected(): if func.is_protected():
return [] return []

@ -0,0 +1,22 @@
[
{
"calls": [
"msg.sender.send(this.balance)"
],
"contract": "Test",
"filename": "tests/arbitrary_send.sol",
"func": "direct",
"sourceMapping": [],
"vuln": "SuicidalFunc"
},
{
"calls": [
"destination.send(this.balance)"
],
"contract": "Test",
"filename": "tests/arbitrary_send.sol",
"func": "indirect",
"sourceMapping": [],
"vuln": "SuicidalFunc"
}
]

@ -0,0 +1,11 @@
[
{
"contract": "C",
"sourceMapping": {
"filename": "tests/backdoor.sol",
"length": 74,
"start": 42
},
"vuln": "backdoor"
}
]

@ -0,0 +1,13 @@
[
{
"contract": "C",
"filename": "tests/backdoor.sol",
"func": "i_am_a_backdoor",
"sourceMapping": {
"filename": "tests/backdoor.sol",
"length": 74,
"start": 42
},
"vuln": "SuicidalFunc"
}
]

@ -0,0 +1,64 @@
[
{
"contract": "B",
"filename": "tests/const_state_variables.sol",
"sourceMapping": [
{
"filename": "tests/const_state_variables.sol",
"length": 20,
"start": 235
},
{
"filename": "tests/const_state_variables.sol",
"length": 20,
"start": 331
},
{
"filename": "tests/const_state_variables.sol",
"length": 76,
"start": 130
},
{
"filename": "tests/const_state_variables.sol",
"length": 76,
"start": 494
}
],
"unusedVars": [
"myFriendsAddress",
"test",
"text2"
],
"vuln": "ConstStateVariableCandidates"
},
{
"contract": "B",
"filename": "tests/const_state_variables.sol",
"sourceMapping": [
{
"filename": "tests/const_state_variables.sol",
"length": 20,
"start": 235
},
{
"filename": "tests/const_state_variables.sol",
"length": 20,
"start": 331
},
{
"filename": "tests/const_state_variables.sol",
"length": 76,
"start": 130
},
{
"filename": "tests/const_state_variables.sol",
"length": 76,
"start": 494
}
],
"unusedVars": [
"mySistersAddress"
],
"vuln": "ConstStateVariableCandidates"
}
]

@ -0,0 +1,46 @@
[
{
"contract": "ContractWithFunctionNotCalled",
"filename": "tests/external_function.sol",
"func": "funcNotCalled",
"sourceMapping": {
"filename": "tests/external_function.sol",
"length": 40,
"start": 351
},
"vuln": "ExternalFunc"
},
{
"contract": "ContractWithFunctionNotCalled",
"filename": "tests/external_function.sol",
"func": "funcNotCalled2",
"sourceMapping": {
"filename": "tests/external_function.sol",
"length": 41,
"start": 304
},
"vuln": "ExternalFunc"
},
{
"contract": "ContractWithFunctionNotCalled",
"filename": "tests/external_function.sol",
"func": "funcNotCalled3",
"sourceMapping": {
"filename": "tests/external_function.sol",
"length": 41,
"start": 257
},
"vuln": "ExternalFunc"
},
{
"contract": "ContractWithFunctionNotCalled2",
"filename": "tests/external_function.sol",
"func": "funcNotCalled",
"sourceMapping": {
"filename": "tests/external_function.sol",
"length": 304,
"start": 552
},
"vuln": "ExternalFunc"
}
]

@ -0,0 +1,9 @@
[
{
"contract": "GetCode",
"filename": "tests/inline_assembly_contract.sol",
"function_name": "at",
"sourceMapping": [],
"vuln": "Assembly"
}
]

@ -0,0 +1,16 @@
[
{
"contract": "VectorSum",
"filename": "tests/inline_assembly_library.sol",
"function_name": "sumAsm",
"sourceMapping": [],
"vuln": "Assembly"
},
{
"contract": "VectorSum",
"filename": "tests/inline_assembly_library.sol",
"function_name": "sumPureAsm",
"sourceMapping": [],
"vuln": "Assembly"
}
]

@ -0,0 +1,16 @@
[
{
"contract": "OnlyLocked",
"functions_payable": [
"receive"
],
"sourceMapping": [
{
"filename": "tests/locked_ether.sol",
"length": 72,
"start": 46
}
],
"vuln": "LockedEther"
}
]

@ -0,0 +1,9 @@
[
{
"contract": "Sender",
"filename": "tests/low_level_calls.sol",
"function_name": "send",
"sourceMapping": [],
"vuln": "Low level call"
}
]

@ -0,0 +1,131 @@
[
{
"contract": "T",
"filename": "tests/naming_convention.sol",
"sourceMapping": {
"filename": "tests/naming_convention.sol",
"length": 17,
"start": 695
},
"variable": "_myPublicVar",
"vuln": "NamingConvention"
},
{
"contract": "naming",
"filename": "tests/naming_convention.sol",
"sourceMapping": {
"filename": "tests/naming_convention.sol",
"length": 598,
"start": 26
},
"vuln": "NamingConvention"
},
{
"contract": "naming",
"filename": "tests/naming_convention.sol",
"sourceMapping": {
"filename": "tests/naming_convention.sol",
"length": 16,
"start": 183
},
"variable": "Var_One",
"vuln": "NamingConvention"
},
{
"contract": "naming",
"filename": "tests/naming_convention.sol",
"sourceMapping": {
"filename": "tests/naming_convention.sol",
"length": 20,
"start": 227
},
"struct": "test",
"vuln": "NamingConvention"
},
{
"contract": "naming",
"filename": "tests/naming_convention.sol",
"modifier": "CantDo",
"sourceMapping": {
"filename": "tests/naming_convention.sol",
"length": 36,
"start": 545
},
"vuln": "NamingConvention"
},
{
"contract": "naming",
"filename": "tests/naming_convention.sol",
"function": "GetOne",
"sourceMapping": {
"filename": "tests/naming_convention.sol",
"length": 71,
"start": 405
},
"vuln": "NamingConvention"
},
{
"contract": "naming",
"event": "event_",
"filename": "tests/naming_convention.sol",
"sourceMapping": null,
"vuln": "NamingConvention"
},
{
"contract": "naming",
"enum": "numbers",
"filename": "tests/naming_convention.sol",
"sourceMapping": {
"filename": "tests/naming_convention.sol",
"length": 23,
"start": 77
},
"vuln": "NamingConvention"
},
{
"constant": "MY_other_CONSTANT",
"contract": "naming",
"filename": "tests/naming_convention.sol",
"sourceMapping": {
"filename": "tests/naming_convention.sol",
"length": 35,
"start": 141
},
"vuln": "NamingConvention"
},
{
"constant": "l",
"contract": "T",
"filename": "tests/naming_convention.sol",
"sourceMapping": {
"filename": "tests/naming_convention.sol",
"length": 10,
"start": 847
},
"vuln": "NamingConvention"
},
{
"argument": "Number2",
"contract": "naming",
"filename": "tests/naming_convention.sol",
"function": "setInt",
"sourceMapping": {
"filename": "tests/naming_convention.sol",
"length": 12,
"start": 512
},
"vuln": "NamingConvention"
},
{
"argument": "_used",
"contract": "T",
"filename": "tests/naming_convention.sol",
"function": "test",
"sourceMapping": {
"filename": "tests/naming_convention.sol",
"length": 10,
"start": 748
},
"vuln": "NamingConvention"
}
]

@ -0,0 +1,15 @@
[
{
"pragma": [
"0.4.21"
],
"sourceMapping": [
{
"filename": "old_solc.sol",
"length": 23,
"start": 0
}
],
"vuln": "OldPragma"
}
]

@ -0,0 +1,21 @@
[
{
"sourceMapping": [
{
"filename": "tests/pragma.0.4.23.sol",
"length": 24,
"start": 0
},
{
"filename": "tests/pragma.0.4.24.sol",
"length": 23,
"start": 0
}
],
"versions": [
"0.4.24",
"^0.4.23"
],
"vuln": "ConstantPragma"
}
]

@ -0,0 +1,24 @@
[
{
"calls": [
"! (msg.sender.call.value(userBalance[msg.sender])())"
],
"contract": "Reentrancy",
"filename": "tests/reentrancy.sol",
"function_name": "withdrawBalance()",
"send_eth": [
"! (msg.sender.call.value(userBalance[msg.sender])())"
],
"sourceMapping": [
{
"filename": "tests/reentrancy.sol",
"length": 37,
"start": 52
}
],
"varsWritten": [
"userBalance"
],
"vuln": "Reentrancy"
}
]

@ -0,0 +1,16 @@
[
{
"contract": "TxOrigin",
"filename": "tests/tx_origin.sol",
"function_name": "bug0",
"sourceMapping": [],
"vuln": "TxOrigin"
},
{
"contract": "TxOrigin",
"filename": "tests/tx_origin.sol",
"function_name": "bug2",
"sourceMapping": [],
"vuln": "TxOrigin"
}
]

@ -0,0 +1,86 @@
[
{
"contract": "Test",
"filename": "tests/uninitialized.sol",
"functions": [
"use"
],
"sourceMapping": [
{
"filename": "tests/uninitialized.sol",
"length": 34,
"start": 189
},
{
"filename": "tests/uninitialized.sol",
"length": 143,
"start": 356
}
],
"variable": "balances",
"vuln": "UninitializedStateVars"
},
{
"contract": "Test2",
"filename": "tests/uninitialized.sol",
"functions": [
"use"
],
"sourceMapping": [
{
"filename": "tests/uninitialized.sol",
"length": 15,
"start": 695
},
{
"filename": "tests/uninitialized.sol",
"length": 117,
"start": 875
}
],
"variable": "st",
"vuln": "UninitializedStateVars"
},
{
"contract": "Test2",
"filename": "tests/uninitialized.sol",
"functions": [
"init"
],
"sourceMapping": [
{
"filename": "tests/uninitialized.sol",
"length": 6,
"start": 748
},
{
"filename": "tests/uninitialized.sol",
"length": 52,
"start": 817
}
],
"variable": "v",
"vuln": "UninitializedStateVars"
},
{
"contract": "Uninitialized",
"filename": "tests/uninitialized.sol",
"functions": [
"transfer"
],
"sourceMapping": [
{
"filename": "tests/uninitialized.sol",
"length": 19,
"start": 55
},
{
"filename": "tests/uninitialized.sol",
"length": 82,
"start": 81
}
],
"variable": "destination",
"vuln": "UninitializedStateVars"
}
]

@ -0,0 +1,21 @@
[
{
"contract": "Uninitialized",
"filename": "tests/uninitialized_storage_pointer.sol",
"function": "func",
"sourceMapping": [
{
"filename": "tests/uninitialized_storage_pointer.sol",
"length": 9,
"start": 171
},
{
"filename": "tests/uninitialized_storage_pointer.sol",
"length": 138,
"start": 67
}
],
"variable": "st_bug",
"vuln": "UninitializedStorageVars"
}
]

@ -0,0 +1,17 @@
[
{
"contract": "B",
"filename": "tests/unused_state.sol",
"sourceMapping": [
{
"filename": "tests/unused_state.sol",
"length": 14,
"start": 41
}
],
"unusedVars": [
"unused"
],
"vuln": "unusedStateVars"
}
]
Loading…
Cancel
Save