Commit 44327da2 authored by Michael Fröwis's avatar Michael Fröwis Committed by chriseth

Static Analysis: integration tests

parent d68814df
'use strict'
/**
* Crawl the given AST through the function walk(ast, callback)
*/
function AstWalker () {
}
/**
* visit all the AST nodes
*
* @param {Object} ast - AST node
* @param {Object or Function} callback - if (Function) the function will be called for every node.
* - if (Object) callback[<Node Type>] will be called for
* every node of type <Node Type>. callback["*"] will be called fo all other nodes.
* in each case, if the callback returns false it does not descend into children.
* If no callback for the current type, children are visited.
*/
AstWalker.prototype.walk = function (ast, callback) {
if (callback instanceof Function) {
callback = {'*': callback}
}
if (!('*' in callback)) {
callback['*'] = function () { return true }
}
if (manageCallBack(ast, callback) && ast.children && ast.children.length > 0) {
for (var k in ast.children) {
var child = ast.children[k]
this.walk(child, callback)
}
}
}
/**
* walk the given @astList
*
* @param {Object} sourcesList - sources list (containing root AST node)
* @param {Function} - callback used by AstWalker to compute response
*/
AstWalker.prototype.walkAstList = function (sourcesList, callback) {
var walker = new AstWalker()
for (var k in sourcesList) {
walker.walk(sourcesList[k].AST, callback)
}
}
function manageCallBack (node, callback) {
if (node.name in callback) {
return callback[node.name](node)
} else {
return callback['*'](node)
}
}
module.exports = AstWalker
var common = require('./staticAnalysisCommon') var common = require('./staticAnalysisCommon')
var AstWalker = require('ethereum-remix').util.AstWalker // var AstWalker = require('ethereum-remix').util.AstWalker
var AstWalker = require('../astWalker')
function abstractAstView () { function abstractAstView () {
this.contracts = null this.contracts = null
......
...@@ -19,6 +19,8 @@ checksEffectsInteraction.prototype.report = function (compilationResults) { ...@@ -19,6 +19,8 @@ checksEffectsInteraction.prototype.report = function (compilationResults) {
var cg = callGraph.buildGlobalFuncCallGraph(this.contracts) var cg = callGraph.buildGlobalFuncCallGraph(this.contracts)
if (this.contracts.some((item) => item.modifiers.length > 0)) this.warning.push({ warning: `Modifiers are currently not supported by this analysis.` })
this.contracts.forEach((contract) => { this.contracts.forEach((contract) => {
contract.functions.forEach((func) => { contract.functions.forEach((func) => {
func.changesState = checkIfChangesState(common.getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters), func.changesState = checkIfChangesState(common.getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters),
......
...@@ -19,6 +19,8 @@ constantFunctions.prototype.report = function (compilationResults) { ...@@ -19,6 +19,8 @@ constantFunctions.prototype.report = function (compilationResults) {
var cg = callGraph.buildGlobalFuncCallGraph(this.contracts) var cg = callGraph.buildGlobalFuncCallGraph(this.contracts)
if (this.contracts.some((item) => item.modifiers.length > 0)) this.warning.push({ warning: `Modifiers are currently not supported by this analysis.` })
this.contracts.forEach((contract) => { this.contracts.forEach((contract) => {
if (!common.isFullyImplementedContract(contract.node)) return if (!common.isFullyImplementedContract(contract.node)) return
......
...@@ -5,4 +5,7 @@ module.exports = [ ...@@ -5,4 +5,7 @@ module.exports = [
require('./checksEffectsInteraction'), require('./checksEffectsInteraction'),
require('./constantFunctions'), require('./constantFunctions'),
require('./inlineAssembly') require('./inlineAssembly')
// require('./blockTimestamp'),
// require('./lowLevelCalls'),
// require('./blockBlockhash')
] ]
...@@ -226,18 +226,21 @@ function isExternalDirectCall (node) { ...@@ -226,18 +226,21 @@ function isExternalDirectCall (node) {
return isMemberAccess(node, basicRegex.EXTERNALFUNCTIONTYPE, undefined, basicRegex.CONTRACTTYPE, undefined) && !isThisLocalCall(node) return isMemberAccess(node, basicRegex.EXTERNALFUNCTIONTYPE, undefined, basicRegex.CONTRACTTYPE, undefined) && !isThisLocalCall(node)
} }
// usage of now special variable
function isNowAccess (node) { function isNowAccess (node) {
return nodeType(node, exactMatch(nodeTypes.IDENTIFIER)) && return nodeType(node, exactMatch(nodeTypes.IDENTIFIER)) &&
expressionType(node, exactMatch(basicTypes.UINT)) && expressionType(node, exactMatch(basicTypes.UINT)) &&
name(node, exactMatch('now')) name(node, exactMatch('now'))
} }
// usage of block timestamp
function isBlockTimestampAccess (node) { function isBlockTimestampAccess (node) {
return isSpecialVariableAccess(node, specialVariables.BLOCKTIMESTAMP) return isSpecialVariableAccess(node, specialVariables.BLOCKTIMESTAMP)
} }
// usage of block timestamp
function isBlockBlockHashAccess (node) {
return isSpecialVariableAccess(node, specialVariables.BLOCKHASH)
}
function isThisLocalCall (node) { function isThisLocalCall (node) {
return isMemberAccess(node, basicRegex.FUNCTIONTYPE, exactMatch('this'), basicRegex.CONTRACTTYPE, undefined) return isMemberAccess(node, basicRegex.FUNCTIONTYPE, exactMatch('this'), basicRegex.CONTRACTTYPE, undefined)
} }
...@@ -382,6 +385,7 @@ module.exports = { ...@@ -382,6 +385,7 @@ module.exports = {
isEffect: isEffect, isEffect: isEffect,
isNowAccess: isNowAccess, isNowAccess: isNowAccess,
isBlockTimestampAccess: isBlockTimestampAccess, isBlockTimestampAccess: isBlockTimestampAccess,
isBlockBlockHashAccess: isBlockBlockHashAccess,
isThisLocalCall: isThisLocalCall, isThisLocalCall: isThisLocalCall,
isLocalCall: isLocalCall, isLocalCall: isLocalCall,
isWriteOnStateVariable: isWriteOnStateVariable, isWriteOnStateVariable: isWriteOnStateVariable,
......
'use strict' 'use strict'
var AstWalker = require('ethereum-remix').util.AstWalker // var AstWalker = require('ethereum-remix').util.AstWalker
var AstWalker = require('./astWalker')
var list = require('./modules/list') var list = require('./modules/list')
function staticAnalysisRunner () { function staticAnalysisRunner () {
......
...@@ -1593,6 +1593,15 @@ test('staticAnalysisCommon.isBlockTimestampAccess', function (t) { ...@@ -1593,6 +1593,15 @@ test('staticAnalysisCommon.isBlockTimestampAccess', function (t) {
t.notOk(common.isNowAccess(node), 'is now used should not work') t.notOk(common.isNowAccess(node), 'is now used should not work')
}) })
test('staticAnalysisCommon.isBlockBlockhashAccess', function (t) {
t.plan(4)
var node = { name: 'MemberAccess', children: [{attributes: { value: 'block', type: 'block' }}], attributes: { value: 'blockhash', type: 'bytes32' } }
t.notOk(common.isThisLocalCall(node), 'is this.local_method() used should not work')
t.notOk(common.isBlockTimestampAccess(node), 'is block.timestamp used should not work')
t.ok(common.isBlockBlockHashAccess(node), 'blockhash should work')
t.notOk(common.isNowAccess(node), 'is now used should not work')
})
test('staticAnalysisCommon.isThisLocalCall', function (t) { test('staticAnalysisCommon.isThisLocalCall', function (t) {
t.plan(3) t.plan(3)
var node = { name: 'MemberAccess', children: [{attributes: { value: 'this', type: 'contract test' }}], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } } var node = { name: 'MemberAccess', children: [{attributes: { value: 'this', type: 'contract test' }}], attributes: { value: 'b', type: 'function (bytes32,address) returns (bool)' } }
......
// var test = require('tape') var test = require('tape')
// var common = require('../../babelify-src/app/staticanalysis/modules/staticAnalysisCommon') var StatRunner = require('../../babelify-src/app/staticanalysis/staticAnalysisRunner')
// var StatRunner = require('../../babelify-src/app/staticanalysis/staticAnalysisRunner') // const util = require('util')
// var utils = require('../../babelify-src/app/utils')
// var Compiler = require('../../babelify-src/app/compiler') var solc = require('solc')
// var fs = require('fs') var fs = require('fs')
// var path = require('path') var path = require('path')
// var testFiles = [ var testFiles = [
// '/test-contracts/KingOfTheEtherThrone.sol', 'KingOfTheEtherThrone.sol',
// '/test-contracts/assembly.sol', 'assembly.sol',
// '/test-contracts/ballot.sol', 'ballot.sol',
// '/test-contracts/ballot_reentrant.sol', 'ballot_reentrant.sol',
// '/test-contracts/ballot_withoutWarning.sol', 'ballot_withoutWarnings.sol',
// '/test-contracts/cross_contract.sol', 'cross_contract.sol',
// '/test-contracts/inheritance.sol', 'inheritance.sol',
// '/test-contracts/notReentrant.sol', 'modifier1.sol',
// '/test-contracts/structReentrant.sol', 'modifier2.sol',
// '/test-contracts/thisLocal.sol', 'notReentrant.sol',
// '/test-contracts/modifier1.sol', 'structReentrant.sol',
// '/test-contracts/modifier2.sol' 'thisLocal.sol'
// ] ]
// test('thisLocal.js', function (t) { var testFileAsts = {}
// t.plan(0)
testFiles.forEach((fileName) => {
// var module = require('../../babelify-src/app/staticanalysis/modules/thisLocal') var contents = fs.readFileSync(path.join(__dirname, 'test-contracts', fileName), 'utf8')
testFileAsts[fileName] = solc.compile(contents, 0)
// runModuleOnFiles(module, t) })
// })
test('Integration test thisLocal.js', function (t) {
// function runModuleOnFiles (module, t) { t.plan(testFiles.length)
// var fakeImport = function (url, cb) { cb('Not implemented') }
// var compiler = new Compiler(fakeImport) var module = require('../../babelify-src/app/staticanalysis/modules/thisLocal')
// var statRunner = new StatRunner()
var lengthCheck = {
// testFiles.map((fileName) => { 'KingOfTheEtherThrone.sol': 0,
// var contents = fs.readFileSync(path.join(__dirname, fileName), 'utf8') 'assembly.sol': 0,
// var compres = compiler.compile({ 'test': contents }, 'test') 'ballot.sol': 0,
'ballot_reentrant.sol': 1,
// statRunner.runWithModuleList(compres, [{ name: module.name, mod: new module.Module() }], (reports) => { 'ballot_withoutWarnings.sol': 0,
// reports.map((r) => t.comment(r.warning)) 'cross_contract.sol': 0,
// }) 'inheritance.sol': 0,
// }) 'modifier1.sol': 0,
// } 'modifier2.sol': 0,
'notReentrant.sol': 0,
'structReentrant.sol': 0,
'thisLocal.sol': 1
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of this local warnings`)
})
})
test('Integration test checksEffectsInteraction.js', function (t) {
t.plan(testFiles.length)
var module = require('../../babelify-src/app/staticanalysis/modules/checksEffectsInteraction')
var lengthCheck = {
'KingOfTheEtherThrone.sol': 1,
'assembly.sol': 0,
'ballot.sol': 0,
'ballot_reentrant.sol': 1,
'ballot_withoutWarnings.sol': 0,
'cross_contract.sol': 0,
'inheritance.sol': 1,
'modifier1.sol': 0,
'modifier2.sol': 0,
'notReentrant.sol': 0,
'structReentrant.sol': 1,
'thisLocal.sol': 0
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of checks-effects-interaction warnings`)
})
})
test('Integration test constatnFunctions.js', function (t) {
t.plan(testFiles.length)
var module = require('../../babelify-src/app/staticanalysis/modules/constantFunctions')
var lengthCheck = {
'KingOfTheEtherThrone.sol': 0,
'assembly.sol': 0,
'ballot.sol': 0,
'ballot_reentrant.sol': 0,
'ballot_withoutWarnings.sol': 0,
'cross_contract.sol': 1,
'inheritance.sol': 0,
'modifier1.sol': 1,
'modifier2.sol': 0,
'notReentrant.sol': 0,
'structReentrant.sol': 0,
'thisLocal.sol': 1
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of constant warnings`)
})
})
test('Integration test constantFunctions.js', function (t) {
t.plan(testFiles.length)
var module = require('../../babelify-src/app/staticanalysis/modules/inlineAssembly')
var lengthCheck = {
'KingOfTheEtherThrone.sol': 0,
'assembly.sol': 2,
'ballot.sol': 0,
'ballot_reentrant.sol': 0,
'ballot_withoutWarnings.sol': 0,
'cross_contract.sol': 0,
'inheritance.sol': 0,
'modifier1.sol': 0,
'modifier2.sol': 0,
'notReentrant.sol': 0,
'structReentrant.sol': 0,
'thisLocal.sol': 0
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of inline assembly warnings`)
})
})
test('Integration test txOrigin.js', function (t) {
t.plan(testFiles.length)
var module = require('../../babelify-src/app/staticanalysis/modules/txOrigin')
var lengthCheck = {
'KingOfTheEtherThrone.sol': 0,
'assembly.sol': 1,
'ballot.sol': 0,
'ballot_reentrant.sol': 0,
'ballot_withoutWarnings.sol': 0,
'cross_contract.sol': 0,
'inheritance.sol': 0,
'modifier1.sol': 0,
'modifier2.sol': 0,
'notReentrant.sol': 0,
'structReentrant.sol': 0,
'thisLocal.sol': 0
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of tx.origin warnings`)
})
})
test('Integration test gasCosts.js', function (t) {
t.plan(testFiles.length)
var module = require('../../babelify-src/app/staticanalysis/modules/gasCosts')
var lengthCheck = {
'KingOfTheEtherThrone.sol': 2,
'assembly.sol': 2,
'ballot.sol': 3,
'ballot_reentrant.sol': 2,
'ballot_withoutWarnings.sol': 0,
'cross_contract.sol': 1,
'inheritance.sol': 1,
'modifier1.sol': 0,
'modifier2.sol': 1,
'notReentrant.sol': 1,
'structReentrant.sol': 1,
'thisLocal.sol': 2
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of gasCost warnings`)
})
})
// #################### Helpers
function runModuleOnFiles (module, t, cb) {
var statRunner = new StatRunner()
testFiles.forEach((fileName) => {
statRunner.runWithModuleList(testFileAsts[fileName], [{ name: module.name, mod: new module.Module() }], (reports) => {
cb(fileName, reports[0].report)
})
})
}
pragma solidity ^0.4.9; pragma solidity ^0.4.9;
contract test { contract test {
address owner;
function at(address _addr) returns (bytes o_code) { function at(address _addr) returns (bytes o_code) {
assembly { assembly {
// retrieve the size of the code, this needs assembly // retrieve the size of the code, this needs assembly
...@@ -18,6 +20,7 @@ pragma solidity ^0.4.9; ...@@ -18,6 +20,7 @@ pragma solidity ^0.4.9;
} }
function bla() { function bla() {
if(tx.origin == owner)
msg.sender.send(19); msg.sender.send(19);
assembly { assembly {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment