Buglist check script supports json paths

This commit is contained in:
Leonardo Alt 2018-08-21 16:09:53 +02:00
parent c57a60833d
commit 9927964d21
5 changed files with 200 additions and 54 deletions

View File

@ -195,6 +195,12 @@ jobs:
TERM: xterm TERM: xterm
steps: steps:
- checkout - checkout
- run:
name: JS deps
command: |
npm install download
npm install JSONPath
npm install mktemp
- run: - run:
name: Test buglist name: Test buglist
command: ./test/buglistTests.js command: ./test/buglistTests.js

View File

@ -5,7 +5,8 @@
"description": "If a struct is used in an event, the address of the struct is logged instead of the actual data.", "description": "If a struct is used in an event, the address of the struct is logged instead of the actual data.",
"introduced": "0.4.17", "introduced": "0.4.17",
"fixed": "0.5.0", "fixed": "0.5.0",
"severity": "very low" "severity": "very low",
"check": {"ast-compact-json-path": "$..[?(@.nodeType === 'EventDefinition')]..[?(@.nodeType === 'UserDefinedTypeName' && @.typeDescriptions.typeString.startsWith('struct'))]"}
}, },
{ {
"name": "NestedArrayFunctionCallDecoder", "name": "NestedArrayFunctionCallDecoder",

View File

@ -57,13 +57,18 @@ conditions
means that the optimizer has to be switched on to enable the bug. means that the optimizer has to be switched on to enable the bug.
If no conditions are given, assume that the bug is present. If no conditions are given, assume that the bug is present.
check check
This field contains JavaScript regular expressions that are to be matched This field contains different checks that report whether the smart contract
against the source code ("source-regex") to find out if the contains the bug or not. The first type of check are Javascript regular
smart contract contains the bug or not. If there is no match, expressions that are to be matched against the source code ("source-regex")
then the bug is very likely not present. If there is a match, if the bug is present. If there is no match, then the bug is very likely
the bug might be present. For improved accuracy, the regular not present. If there is a match, the bug might be present. For improved
expression should be applied to the source code after stripping accuracy, the checks should be applied to the source code after stripping
comments. comments.
The second type of check are patterns to be checked on the compact AST of
the Solidity program ("ast-compact-json-path"). The specified search query
is a `JsonPath <https://github.com/json-path/JsonPath>`_ expression.
If at least one path of the Solidity AST matches the query, the bug is
likely present.
.. literalinclude:: bugs.json .. literalinclude:: bugs.json
:language: js :language: js

View File

@ -2,6 +2,11 @@
"use strict"; "use strict";
var util = require('util')
var exec = util.promisify(require('child_process').exec)
var mktemp = require('mktemp');
var download = require('download')
var JSONPath = require('JSONPath')
var fs = require('fs') var fs = require('fs')
var bugs = JSON.parse(fs.readFileSync(__dirname + '/../docs/bugs.json', 'utf8')) var bugs = JSON.parse(fs.readFileSync(__dirname + '/../docs/bugs.json', 'utf8'))
@ -19,6 +24,10 @@ var tests = fs.readFileSync(__dirname + '/buglist_test_vectors.md', 'utf8')
var testVectorParser = /\s*#\s+(\S+)\s+## buggy\n([^#]*)## fine\n([^#]*)/g var testVectorParser = /\s*#\s+(\S+)\s+## buggy\n([^#]*)## fine\n([^#]*)/g
runTests()
async function runTests()
{
var result; var result;
while ((result = testVectorParser.exec(tests)) !== null) while ((result = testVectorParser.exec(tests)) !== null)
{ {
@ -27,19 +36,99 @@ while ((result = testVectorParser.exec(tests)) !== null)
var fine = result[3].split('\n--\n') var fine = result[3].split('\n--\n')
console.log("Testing " + name + " with " + buggy.length + " buggy and " + fine.length + " fine instances") console.log("Testing " + name + " with " + buggy.length + " buggy and " + fine.length + " fine instances")
var regex = RegExp(bugsByName[name].check['regex-source']) try {
await checkRegex(name, buggy, fine)
await checkJSONPath(name, buggy, fine)
} catch (err) {
console.error("Error: " + err)
}
}
}
function checkRegex(name, buggy, fine)
{
return new Promise(function(resolve, reject) {
var regexStr = bugsByName[name].check['regex-source']
if (regexStr !== undefined)
{
var regex = RegExp(regexStr)
for (var i in buggy) for (var i in buggy)
{ {
if (!regex.exec(buggy[i])) if (!regex.exec(buggy[i]))
{ {
throw "Bug " + name + ": Buggy source does not match: " + buggy[i] reject("Bug " + name + ": Buggy source does not match: " + buggy[i])
} }
} }
for (var i in fine) for (var i in fine)
{ {
if (regex.exec(fine[i])) if (regex.exec(fine[i]))
{ {
throw "Bug " + name + ": Non-buggy source matches: " + fine[i] reject("Bug " + name + ": Non-buggy source matches: " + fine[i])
} }
} }
} }
resolve()
})
}
async function checkJSONPath(name, buggy, fine)
{
var jsonPath = bugsByName[name].check['ast-compact-json-path']
if (jsonPath !== undefined)
{
var url = "http://github.com/ethereum/solidity/releases/download/v" + bugsByName[name].introduced + "/solc-static-linux"
try {
var tmpdir = await mktemp.createDir('XXXXX')
var binary = tmpdir + "/solc-static-linux"
await download(url, tmpdir)
exec("chmod +x " + binary)
for (var i in buggy)
{
var result = await checkJsonPathTest(buggy[i], tmpdir, binary, jsonPath, i)
if (!result)
throw "Bug " + name + ": Buggy source does not contain path: " + buggy[i]
}
for (var i in fine)
{
var result = await checkJsonPathTest(fine[i], tmpdir, binary, jsonPath, i + buggy.length)
if (result)
throw "Bug " + name + ": Non-buggy source contains path: " + fine[i]
}
exec("rm -r " + tmpdir)
} catch (err) {
throw err
}
}
}
function checkJsonPathTest(code, tmpdir, binary, query, idx) {
return new Promise(function(resolve, reject) {
var solFile = tmpdir + "/jsonPath" + idx + ".sol"
var astFile = tmpdir + "/ast" + idx + ".json"
writeFilePromise(solFile, code)
.then(() => {
return exec(binary + " --ast-compact-json " + solFile + " > " + astFile)
})
.then(() => {
var jsonRE = /(\{[\s\S]*\})/
var ast = JSON.parse(jsonRE.exec(fs.readFileSync(astFile, 'utf8'))[0])
var result = JSONPath({json: ast, path: query})
if (result.length > 0)
resolve(true)
else
resolve(false)
})
.catch((err) => {
reject(err)
})
})
}
function writeFilePromise(filename, data) {
return new Promise(function(resolve, reject) {
fs.writeFile(filename, data, 'utf8', function(err) {
if (err) reject(err)
else resolve(data)
})
})
}

View File

@ -67,3 +67,48 @@ function f() m(uint[2][2]) { }
-- --
function f() returns (uint, uint) { uint[2][2] memory x; } function f() returns (uint, uint) { uint[2][2] memory x; }
# EventStructWrongData
## buggy
pragma experimental ABIEncoderV2;
contract C
{
struct S { uint x; }
event E(S);
event F(S);
enum A { B, C }
event G(A);
function f(S s);
}
--
pragma experimental ABIEncoderV2;
contract C
{
struct S { uint x; }
event E(S indexed);
event F(uint, S, bool);
}
## fine
pragma experimental ABIEncoderV2;
contract C
{
struct S { uint x; }
enum A { B, C }
event G(A);
}
--
pragma experimental ABIEncoderV2;
contract C
{
struct S { uint x; }
function f(S s);
S s1;
}