Git Product home page Git Product logo

Comments (8)

montyly avatar montyly commented on May 22, 2024 2

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.

ktb88 avatar ktb88 commented on May 22, 2024 1

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.

ktb88 avatar ktb88 commented on May 22, 2024 1

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.

ktb88 avatar ktb88 commented on May 22, 2024 1

this contract has no pragma declaration.
-> https://etherscan.io/address/0xe8780b48bdb05f928697a5e8155f672ed91462f7#code

It could be "lack of pragma".

from slither.

ktb88 avatar ktb88 commented on May 22, 2024

@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.

ktb88 avatar ktb88 commented on May 22, 2024

@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.

montyly avatar montyly commented on May 22, 2024

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.

montyly avatar montyly commented on May 22, 2024

Fixed in #129

from slither.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.