Comments (8)
Thanks for the contribution @ktb88!
Indeed the grammar is not well defined.
More I think about it, more I think that complex pragma declarations should be flag as bad. If someone declares
pragma solidity >=0.4.23<=0.6.0>=0.4.22>=0.4.24 <=0.5.2>0.4.1
, something is wrong.
We could parse the pragma through a simple regular expression, and classify as an issue if it does not match. So instead of 'old Solidity version', we could look for 'incorrect Solidity version', which would include
- old version
- non-trivial pragma
- lack of pragma
By including the lack of pragma in the check, we can avoid checking the version of the solc binary (as the detector will only not complain if a recent version of Solidity is used, the compiler must be at least as recent as this version)
A pragma should be declared as
- constant
- greater than / lesser than (including strictly)
- a simple interval
Any thoughts?
from slither.
I thought this issue more carefully, this is not that easy to resolve because the versioning issues are not depend on pragma syntax but solc binary (--solc option or default solc).
One straightforward way to handle this issue is that every detectors have their own affected version list (solc binary version). But I think this way is hard to maintain to refresh all the detectors on every version update of solc.
So, I've researching and testing for versioning issue especially slither detectors and if I found good way (reduce false alarm, easy maintain), then I'll put the result in here.
from slither.
I wrote simple check code that validates pragma syntax and find possible old version (target_version) can be compiled.
But as I mentioned this approach is quite ambiguous because the pragma syntax is very very flexible. (even official document is not well-documented, https://solidity.readthedocs.io/en/v0.5.1/miscellaneous.html#language-grammar)
I think finding use of old solc is important because many people may easily missed, but detector like below has many problems. So, I think checking the linked solc binary version of slither is the best way to detect this one.
import re
# < 0.4.23
target_version = [0, 4, 23-1]
safe = [
"pragma solidity ^0.4.23;",
"pragma solidity >0.4.23;",
"pragma solidity >=0.4.23;",
"pragma solidity <0.5.0;",
"pragma solidity <=0.5.1 ;",
"pragma solidity >0.4.23 <0.6.0;",
"pragma solidity <0.6.0 >0.4.23;",
"pragma solidity >=0.4.23 <0.6.0;",
"pragma solidity <0.6.0 >=0.4.23;",
"pragma solidity >0.4.23 <=0.6.0;",
"pragma solidity <=0.6.0 >0.4.23;",
"pragma solidity <=0.6.0 >=0.4.23;",
"pragma \tsolidity >=0.4.25 <0.6.0\t>=0.4.24;"
]
unsafe = [
"pragma solidity >=0.4.22<=0.6.0;",
"pragma solidity ~0.4.0;",
"pragma solidity >=0.4.0;",
"pragma solidity >=0.4.23<=0.6.0>=0.4.22>=0.4.24;",
"pragma solidity >=0.4.23<=0.6.0>=0.4.22>=0.4.24 <=0.5.2>0.4.1;",
]
err = [
"pragma solidity ^0.4.1 ^0.5.0;",
"pragma solidity ~0.4.1 ~0.5.0;"
]
test = safe + unsafe + err
regex_group = re.compile(r"""
pragma\s+solidity\s+ # pragma solidity
(
(?P<group1> # index 0: group 1 pattern (^ or ~)
[\^\~]\s*
(\d+.\d+.\d+)
)\s*;|
(?P<group2> # group 2 pattern (>=? or <=? or both)
(
(>=?|<=?)\s*
(\d+.\d+.\d+)\s*
){1,}
)\s*;
)
""", re.VERBOSE)
for _ in test:
m_group = regex_group.match(_)
if m_group is None:
print "[?] pragma syntax not found -> {}".format(_)
continue
group = m_group.group("group1") or m_group.group("group2") or None
versions = []
if group is not None:
group = group.replace(" ","")
if group[0] in ['^', '~']:
versions += [[">=", map(int, group[1:].split("."))]]
else:
regex_version = re.compile("((>=?)(\d+.\d+.\d+))")
versions = [[ex[1], map(int, ex[2].split("."))] for ex in regex_version.findall(group)]
old_pragma = [eval("{}{}{}".format(target_version, v[0], v[1])) for v in versions]
if any(old_pragma):
print "[!] Possible Old Solc -> {}".format(_)
else:
print "[-] Safe -> {}".format(_)
result
[-] Safe -> pragma solidity ^0.4.23;
[-] Safe -> pragma solidity >0.4.23;
[-] Safe -> pragma solidity >=0.4.23;
[-] Safe -> pragma solidity <0.5.0;
[-] Safe -> pragma solidity <=0.5.1 ;
[-] Safe -> pragma solidity >0.4.23 <0.6.0;
[-] Safe -> pragma solidity <0.6.0 >0.4.23;
[-] Safe -> pragma solidity >=0.4.23 <0.6.0;
[-] Safe -> pragma solidity <0.6.0 >=0.4.23;
[-] Safe -> pragma solidity >0.4.23 <=0.6.0;
[-] Safe -> pragma solidity <=0.6.0 >0.4.23;
[-] Safe -> pragma solidity <=0.6.0 >=0.4.23;
[-] Safe -> pragma solidity >=0.4.25 <0.6.0 >=0.4.24;
[!] Possible Old Solc -> pragma solidity >=0.4.22<=0.6.0;
[!] Possible Old Solc -> pragma solidity ~0.4.0;
[!] Possible Old Solc -> pragma solidity >=0.4.0;
[!] Possible Old Solc -> pragma solidity >=0.4.23<=0.6.0>=0.4.22>=0.4.24;
[!] Possible Old Solc -> pragma solidity >=0.4.23<=0.6.0>=0.4.22>=0.4.24 <=0.5.2>0.4.1;
[?] pragma syntax not found -> pragma solidity ^0.4.1 ^0.5.0;
[?] pragma syntax not found -> pragma solidity ~0.4.1 ~0.5.0;
from slither.
this contract has no pragma declaration.
-> https://etherscan.io/address/0xe8780b48bdb05f928697a5e8155f672ed91462f7#code
It could be "lack of pragma".
from slither.
@montyly yes, you're right. I've just focused on the crazy syntax but that should be flagged as bad in practice. I think that your categorization is enough to resolve the issue. (its clear not complex, reduce false alarm, easy to maintain) great!!!
from slither.
@montyly I've updated above check code from python2 to python3 and fix some errors, but still remain some ambiguous cases
import re
def _check_version(version):
regex_group = re.compile(r"""
pragma\s+solidity\s+ # pragma solidity
(
(?P<group1> # group 1 pattern
[\^\~]?\s*
(\d+.\d+.\d+)
)\s*;|
(?P<group2> # group 2 pattern
(
(>=?|<=?)\s*
(\d+.\d+.\d+)\s*
){1,}
)\s*;
)
""", re.VERBOSE)
m_group = regex_group.match(version)
if m_group is None:
# lack of pragma
return "lack of pragma"
group = m_group.group("group1")
versions = []
# group 1 pattern
# x.y.z, ^x.y.z ~x.y.z
if group:
group = group.replace(" ","")
if group[0] in ['^', '~']:
versions = list(map(int, group[1:].split(".")))
elif group[0] in "0123456789":
versions = list(map(int, group.split(".")))
else:
return "non-trivial pragma"
if [*versions] < stable_version:
return "old version pragma"
return "safe pragma"
# group 2 pattern
# (single range): <x.y.z, <=x.y.z, >x.y.z, >=x.y.z
# (multi range): >x.y.z <x'.y'.z', >=x.y.z <=x'.y'.z', ...
group = m_group.group("group2")
group = group.replace(" ","")
regex_version = re.compile("(>=?|<=?)\s*(\d+.\d+.\d+)\s*")
res_ex = regex_version.findall(group)
if len(res_ex) == 0:
return "lack of pragma"
versions = list((ex[0], list(map(int, ex[1].split(".")))) for ex in res_ex)
if len(versions) > 2:
return "non-trivial pragma"
chk_pragma = [eval("{}{}{}".format(stable_version, ver[0], ver[1])) for ver in versions]
if all(chk_pragma):
return "safe pragma"
return "non-trivial pragma"
stable_version = [0, 5, 2]
safe = [
"pragma solidity 0.5.2;",
"pragma solidity ^0.5.2;",
"pragma solidity ~0.5.2;",
"pragma solidity >0.5.1;",
"pragma solidity >=0.5.2;",
"pragma solidity <0.5.3;",
"pragma solidity <=0.5.2;",
]
ambiguous = [
"pragma solidity <0.6.0;", # really safe?
"pragma solidity >0.3.0;", # really safe?
"pragma solidity >0.4.23 <0.6.0;",
"pragma solidity <0.6.0 >0.4.23;",
"pragma solidity >=0.4.23 <0.6.0;",
"pragma solidity <0.6.0 >=0.4.25;",
"pragma solidity >0.4.23 <=0.6.0;",
"pragma solidity <=0.6.0 >0.4.23;",
"pragma solidity <=0.6.0 >=0.4.24;",
]
non_trivial = [
"pragma solidity <0.5.0;",
"pragma solidity <=0.5.1 ;",
"pragma \tsolidity >=0.4.25 <0.6.0\t>=0.4.24;",
"pragma solidity >=0.4.23<=0.6.0>=0.4.22>=0.4.24;",
"pragma solidity >=0.4.23<=0.6.0>=0.4.22>=0.4.24 <=0.5.2>0.4.1;",
"pragma solidity <0.4.23;",
"pragma solidity <=0.4.23;",
]
lack_of_pragma = [
"pragma solidity ^0.4.1 ^0.5.0;",
"pragma solidity ~0.4.1 ~0.5.0;",
]
old_solc = [
"pragma solidity 0.4.23;",
"pragma solidity ^0.4.23;",
"pragma solidity ~0.4.23;",
]
test = safe + ambiguous + non_trivial + lack_of_pragma + old_solc
for t in test:
print ("[*] {} -> {}".format(_check_version(t), t))
result
[*] safe pragma -> pragma solidity 0.5.2;
[*] safe pragma -> pragma solidity ^0.5.2;
[*] safe pragma -> pragma solidity ~0.5.2;
[*] safe pragma -> pragma solidity >0.5.1;
[*] safe pragma -> pragma solidity >=0.5.2;
[*] safe pragma -> pragma solidity <0.5.3;
[*] safe pragma -> pragma solidity <=0.5.2;
[*] safe pragma -> pragma solidity <0.6.0;
[*] safe pragma -> pragma solidity >0.3.0;
[*] safe pragma -> pragma solidity >0.4.23 <0.6.0;
[*] safe pragma -> pragma solidity <0.6.0 >0.4.23;
[*] safe pragma -> pragma solidity >=0.4.23 <0.6.0;
[*] safe pragma -> pragma solidity <0.6.0 >=0.4.25;
[*] safe pragma -> pragma solidity >0.4.23 <=0.6.0;
[*] safe pragma -> pragma solidity <=0.6.0 >0.4.23;
[*] safe pragma -> pragma solidity <=0.6.0 >=0.4.24;
[*] non-trivial pragma -> pragma solidity <0.5.0;
[*] non-trivial pragma -> pragma solidity <=0.5.1 ;
[*] non-trivial pragma -> pragma solidity >=0.4.25 <0.6.0 >=0.4.24;
[*] non-trivial pragma -> pragma solidity >=0.4.23<=0.6.0>=0.4.22>=0.4.24;
[*] non-trivial pragma -> pragma solidity >=0.4.23<=0.6.0>=0.4.22>=0.4.24 <=0.5.2>0.4.1;
[*] non-trivial pragma -> pragma solidity <0.4.23;
[*] non-trivial pragma -> pragma solidity <=0.4.23;
[*] lack of pragma -> pragma solidity ^0.4.1 ^0.5.0;
[*] lack of pragma -> pragma solidity ~0.4.1 ~0.5.0;
[*] old version pragma -> pragma solidity 0.4.23;
[*] old version pragma -> pragma solidity ^0.4.23;
[*] old version pragma -> pragma solidity ~0.4.23;
Is this result right direction as you mentioned? and i'm sry this thread may bothers you. If you okay then I'll make a PR for this.
from slither.
Hey @ktb88, are you still working on this? Otherwise, I will fix the detector based on our discussion.
Your fix as a PR is definitely welcome!
from slither.
Fixed in #129
from slither.
Related Issues (20)
- [Bug-Candidate]: variable headStart is 1 slot(s) too deep inside the stack HOT 2
- Add Support For Top level EventDefinition HOT 1
- slithIR wiki page links are broken HOT 1
- [Bug-Candidate]: --disable-color ignored, printer produces colored outputs HOT 2
- [Bug-Candidate]: Wrong library/high level call.
- Add support for filtering printer output HOT 6
- Include _get_source_code into Function class
- [False-Positive]:`msg.value` in a loop when `msg.value` is not transferred HOT 1
- [Question] A representation of "not used" returned variables HOT 2
- ModuleNotFoundError: No module named '_pysha3' HOT 1
- [Bug-Candidate]: File not found. Searched the following locations: "" HOT 4
- Small issue in documenting "variable-scope" detector HOT 4
- May I ask if Slither has the following features HOT 2
- The following issues occurred while running the slither
- Unable to run slicer in ide HOT 2
- [Bug]: Generated string of ir of assignment operation HOT 1
- [False-Positive]: Weak PRNG Usage HOT 3
- [Bug-Candidate]: Slither installed but running through issues HOT 1
- [question] module not found error when running HOT 5
- Detector Request: Implicit Typecast HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from slither.