aboutsummaryrefslogtreecommitdiffstats
path: root/test/buglistTests.js
blob: f24f0cb689a24bcea4f2fdd87b434e826a589f95 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#!/usr/bin/env node

"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 bugs = JSON.parse(fs.readFileSync(__dirname + '/../docs/bugs.json', 'utf8'))

var bugsByName = {}
for (var i in bugs)
{
    if (bugs[i].name in bugsByName)
    {
        throw "Duplicate bug name: " + bugs[i].name
    }
    bugsByName[bugs[i].name] = bugs[i]
}

var tests = fs.readFileSync(__dirname + '/buglist_test_vectors.md', 'utf8')

var testVectorParser = /\s*#\s+(\S+)\s+## buggy\n([^#]*)## fine\n([^#]*)/g

runTests()

async function runTests()
{
    var result;
    while ((result = testVectorParser.exec(tests)) !== null)
    {
        var name = result[1]
        var buggy = result[2].split('\n--\n')
        var fine = result[3].split('\n--\n')
        console.log("Testing " + name + " with " + buggy.length + " buggy and " + fine.length + " fine instances")

        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)
            {
                if (!regex.exec(buggy[i]))
                {
                    reject("Bug " + name + ": Buggy source does not match: " + buggy[i])
                }
            }
            for (var i in fine)
            {
                if (regex.exec(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)
        })
    })
}