Merge pull request #1060 from ethereum/fixStaticAnalysisO.5

Fix static analysis 0.5
pull/5370/head
yann300 6 years ago committed by GitHub
commit 561ae7002c
  1. 6
      remix-analyzer/package.json
  2. 6
      remix-analyzer/src/solidity-analyzer/modules/lowLevelCalls.js
  3. 61
      remix-analyzer/src/solidity-analyzer/modules/staticAnalysisCommon.js
  4. 2
      remix-analyzer/src/solidity-analyzer/modules/txOrigin.js
  5. 9
      remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.4.24.js
  6. 866
      remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.5.0.js
  7. 37
      remix-analyzer/test/analysis/staticAnalysisIssues-test-0.4.24.js
  8. 7
      remix-analyzer/test/analysis/staticAnalysisIssues-test-0.5.0.js
  9. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/ERC20.sol
  10. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/KingOfTheEtherThrone.sol
  11. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/assembly.sol
  12. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/ballot.sol
  13. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/ballot_reentrant.sol
  14. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/ballot_withoutWarnings.sol
  15. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/blockLevelCompare.sol
  16. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/cross_contract.sol
  17. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/ctor.sol
  18. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/deleteDynamicArray.sol
  19. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/deleteFromDynamicArray.sol
  20. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/forLoopIteratesOverDynamicArray.sol
  21. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/forgottenReturn.sol
  22. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/functionParameters.sol
  23. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/globals.sol
  24. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/inheritance.sol
  25. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/intDivisionTruncate.sol
  26. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/library.sol
  27. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/modifier1.sol
  28. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/modifier2.sol
  29. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/notReentrant.sol
  30. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/reentrant.sol
  31. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/selfdestruct.sol
  32. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/stringBytesLength.sol
  33. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/structReentrant.sol
  34. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/thisLocal.sol
  35. 0
      remix-analyzer/test/analysis/test-contracts/solidity-v0.4.24/transfer.sol
  36. 46
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/ERC20.sol
  37. 23
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/KingOfTheEtherThrone.sol
  38. 30
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/assembly.sol
  39. 145
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/ballot.sol
  40. 111
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/ballot_reentrant.sol
  41. 31
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/ballot_withoutWarnings.sol
  42. 45
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/blockLevelCompare.sol
  43. 19
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/cross_contract.sol
  44. 8
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/ctor.sol
  45. 37
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/deleteDynamicArray.sol
  46. 22
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/deleteFromDynamicArray.sol
  47. 15
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/forLoopIteratesOverDynamicArray.sol
  48. 13
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/forgottenReturn.sol
  49. 16
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/functionParameters.sol
  50. 57
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/globals.sol
  51. 41
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/inheritance.sol
  52. 38
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/intDivisionTruncate.sol
  53. 54
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/library.sol
  54. 17
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/modifier1.sol
  55. 28
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/modifier2.sol
  56. 13
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/notReentrant.sol
  57. 49
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/reentrant.sol
  58. 14
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/selfdestruct.sol
  59. 10
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/stringBytesLength.sol
  60. 36
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/structReentrant.sol
  61. 16
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/thisLocal.sol
  62. 7
      remix-analyzer/test/analysis/test-contracts/solidity-v0.5/transfer.sol
  63. 9
      remix-analyzer/test/tests.js
  64. 2
      remix-debug/package.json
  65. 2
      remix-solidity/package.json

@ -22,7 +22,6 @@
"babel-plugin-transform-object-assign": "^6.22.0", "babel-plugin-transform-object-assign": "^6.22.0",
"babel-preset-es2015": "^6.24.0", "babel-preset-es2015": "^6.24.0",
"remix-lib": "0.3.11", "remix-lib": "0.3.11",
"solc": "^0.4.24",
"standard": "^7.0.1", "standard": "^7.0.1",
"tape": "^4.6.0" "tape": "^4.6.0"
}, },
@ -38,5 +37,8 @@
}, },
"author": "Remix Team", "author": "Remix Team",
"license": "MIT", "license": "MIT",
"homepage": "https://github.com/ethereum/remix#readme" "homepage": "https://github.com/ethereum/remix#readme",
"devDependencies": {
"npm-install-version": "^6.0.2"
}
} }

@ -11,12 +11,18 @@ function lowLevelCalls () {
lowLevelCalls.prototype.visit = function (node) { lowLevelCalls.prototype.visit = function (node) {
if (common.isLowLevelCallInst(node)) { if (common.isLowLevelCallInst(node)) {
this.llcNodes.push({node: node, type: common.lowLevelCallTypes.CALL}) this.llcNodes.push({node: node, type: common.lowLevelCallTypes.CALL})
} else if (common.isLowLevelCallInst050(node)) {
this.llcNodes.push({node: node, type: common.lowLevelCallTypes.CALL})
} else if (common.isLowLevelCallcodeInst(node)) { } else if (common.isLowLevelCallcodeInst(node)) {
this.llcNodes.push({node: node, type: common.lowLevelCallTypes.CALLCODE}) this.llcNodes.push({node: node, type: common.lowLevelCallTypes.CALLCODE})
} else if (common.isLowLevelDelegatecallInst(node)) { } else if (common.isLowLevelDelegatecallInst(node)) {
this.llcNodes.push({node: node, type: common.lowLevelCallTypes.DELEGATECALL}) this.llcNodes.push({node: node, type: common.lowLevelCallTypes.DELEGATECALL})
} else if (common.isLowLevelSendInst(node)) { } else if (common.isLowLevelSendInst(node)) {
this.llcNodes.push({node: node, type: common.lowLevelCallTypes.SEND}) this.llcNodes.push({node: node, type: common.lowLevelCallTypes.SEND})
} else if (common.isLowLevelSendInst050(node)) {
this.llcNodes.push({node: node, type: common.lowLevelCallTypes.SEND})
} else if (common.isLLDelegatecallInst050(node)) {
this.llcNodes.push({node: node, type: common.lowLevelCallTypes.DELEGATECALL})
} }
} }

@ -33,6 +33,7 @@ var basicTypes = {
UINT: 'uint256', UINT: 'uint256',
BOOL: 'bool', BOOL: 'bool',
ADDRESS: 'address', ADDRESS: 'address',
PAYABLE_ADDRESS: 'address payable',
BYTES32: 'bytes32', BYTES32: 'bytes32',
STRING_MEM: 'string memory', STRING_MEM: 'string memory',
BYTES_MEM: 'bytes memory', BYTES_MEM: 'bytes memory',
@ -52,7 +53,9 @@ var basicRegex = {
var basicFunctionTypes = { var basicFunctionTypes = {
SEND: buildFunctionSignature([basicTypes.UINT], [basicTypes.BOOL], false), SEND: buildFunctionSignature([basicTypes.UINT], [basicTypes.BOOL], false),
CALL: buildFunctionSignature([], [basicTypes.BOOL], true), CALL: buildFunctionSignature([], [basicTypes.BOOL], true),
'CALL-v0.5': buildFunctionSignature([basicTypes.BYTES_MEM], [basicTypes.BOOL, basicTypes.BYTES_MEM], true),
DELEGATECALL: buildFunctionSignature([], [basicTypes.BOOL], false), DELEGATECALL: buildFunctionSignature([], [basicTypes.BOOL], false),
'DELEGATECALL-v0.5': buildFunctionSignature([basicTypes.BYTES_MEM], [basicTypes.BOOL, basicTypes.BYTES_MEM], false),
TRANSFER: buildFunctionSignature([basicTypes.UINT], [], false) TRANSFER: buildFunctionSignature([basicTypes.UINT], [], false)
} }
@ -65,6 +68,7 @@ var builtinFunctions = {
'addmod(uint256,uint256,uint256)': true, 'addmod(uint256,uint256,uint256)': true,
'mulmod(uint256,uint256,uint256)': true, 'mulmod(uint256,uint256,uint256)': true,
'selfdestruct(address)': true, 'selfdestruct(address)': true,
'selfdestruct(address payable)': true,
'revert()': true, 'revert()': true,
'revert(string memory)': true, 'revert(string memory)': true,
'assert(bool)': true, 'assert(bool)': true,
@ -77,8 +81,10 @@ var builtinFunctions = {
var lowLevelCallTypes = { var lowLevelCallTypes = {
CALL: { ident: 'call', type: basicFunctionTypes.CALL }, CALL: { ident: 'call', type: basicFunctionTypes.CALL },
'CALL-v0.5': { ident: 'call', type: basicFunctionTypes['CALL-v0.5'] },
CALLCODE: { ident: 'callcode', type: basicFunctionTypes.CALL }, CALLCODE: { ident: 'callcode', type: basicFunctionTypes.CALL },
DELEGATECALL: { ident: 'delegatecall', type: basicFunctionTypes.DELEGATECALL }, DELEGATECALL: { ident: 'delegatecall', type: basicFunctionTypes.DELEGATECALL },
'DELEGATECALL-v0.5': { ident: 'delegatecall', type: basicFunctionTypes['DELEGATECALL-v0.5'] },
SEND: { ident: 'send', type: basicFunctionTypes.SEND }, SEND: { ident: 'send', type: basicFunctionTypes.SEND },
TRANSFER: { ident: 'transfer', type: basicFunctionTypes.TRANSFER } TRANSFER: { ident: 'transfer', type: basicFunctionTypes.TRANSFER }
} }
@ -588,7 +594,7 @@ function isStorageVariableDeclaration (node) {
* @return {bool} * @return {bool}
*/ */
function isInteraction (node) { function isInteraction (node) {
return isLLCall(node) || isLLSend(node) || isExternalDirectCall(node) || isTransfer(node) return isLLCall(node) || isLLSend(node) || isExternalDirectCall(node) || isTransfer(node) || isLLCall050(node) || isLLSend050(node)
} }
/** /**
@ -833,11 +839,25 @@ function isLowLevelCall (node) {
return isLLCall(node) || return isLLCall(node) ||
isLLCallcode(node) || isLLCallcode(node) ||
isLLDelegatecall(node) || isLLDelegatecall(node) ||
isLLSend(node) isLLSend(node) ||
isLLSend050(node) ||
isLLCall050(node) ||
isLLDelegatecall050(node)
} }
/** /**
* True if low level send * True if low level send (solidity >= 0.5)
* @node {ASTNode} some AstNode
* @return {bool}
*/
function isLLSend050 (node) {
return isMemberAccess(node,
exactMatch(util.escapeRegExp(lowLevelCallTypes.SEND.type)),
undefined, exactMatch(basicTypes.PAYABLE_ADDRESS), exactMatch(lowLevelCallTypes.SEND.ident))
}
/**
* True if low level send (solidity < 0.5)
* @node {ASTNode} some AstNode * @node {ASTNode} some AstNode
* @return {bool} * @return {bool}
*/ */
@ -858,6 +878,17 @@ function isLLCall (node) {
undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes.CALL.ident)) undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes.CALL.ident))
} }
/**
* True if low level payable call (solidity >= 0.5)
* @node {ASTNode} some AstNode
* @return {bool}
*/
function isLLCall050 (node) {
return isMemberAccess(node,
exactMatch(util.escapeRegExp(lowLevelCallTypes['CALL-v0.5'].type)),
undefined, exactMatch(basicTypes.PAYABLE_ADDRESS), exactMatch(lowLevelCallTypes['CALL-v0.5'].ident))
}
/** /**
* True if low level callcode * True if low level callcode
* @node {ASTNode} some AstNode * @node {ASTNode} some AstNode
@ -880,6 +911,17 @@ function isLLDelegatecall (node) {
undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes.DELEGATECALL.ident)) undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes.DELEGATECALL.ident))
} }
/**
* True if low level delegatecall (solidity >= 0.5)
* @node {ASTNode} some AstNode
* @return {bool}
*/
function isLLDelegatecall050 (node) {
return isMemberAccess(node,
exactMatch(util.escapeRegExp(lowLevelCallTypes['DELEGATECALL-v0.5'].type)),
undefined, matches(basicTypes.PAYABLE_ADDRESS, basicTypes.ADDRESS), exactMatch(lowLevelCallTypes['DELEGATECALL-v0.5'].ident))
}
/** /**
* True if transfer call * True if transfer call
* @node {ASTNode} some AstNode * @node {ASTNode} some AstNode
@ -888,7 +930,7 @@ function isLLDelegatecall (node) {
function isTransfer (node) { function isTransfer (node) {
return isMemberAccess(node, return isMemberAccess(node,
exactMatch(util.escapeRegExp(lowLevelCallTypes.TRANSFER.type)), exactMatch(util.escapeRegExp(lowLevelCallTypes.TRANSFER.type)),
undefined, exactMatch(basicTypes.ADDRESS), exactMatch(lowLevelCallTypes.TRANSFER.ident)) undefined, matches(basicTypes.ADDRESS, basicTypes.PAYABLE_ADDRESS), exactMatch(lowLevelCallTypes.TRANSFER.ident))
} }
function isStringToBytesConversion (node) { function isStringToBytesConversion (node) {
@ -962,6 +1004,14 @@ function exactMatch (regexStr) {
return '^' + regexStr + '$' return '^' + regexStr + '$'
} }
function matches () {
var args = []
for (var k = 0; k < arguments.length; k++) {
args.push(arguments[k])
}
return '(' + args.join('|') + ')'
}
/** /**
* Builds an function signature as used in the AST of the solc-json AST * Builds an function signature as used in the AST of the solc-json AST
* @param {Array} paramTypes * @param {Array} paramTypes
@ -1048,6 +1098,9 @@ module.exports = {
isTransfer: isTransfer, isTransfer: isTransfer,
isLowLevelCall: isLowLevelCall, isLowLevelCall: isLowLevelCall,
isLowLevelCallInst: isLLCall, isLowLevelCallInst: isLLCall,
isLowLevelCallInst050: isLLCall050,
isLowLevelSendInst050: isLLSend050,
isLLDelegatecallInst050: isLLDelegatecall050,
isLowLevelCallcodeInst: isLLCallcode, isLowLevelCallcodeInst: isLLCallcode,
isLowLevelDelegatecallInst: isLLDelegatecall, isLowLevelDelegatecallInst: isLLDelegatecall,
isLowLevelSendInst: isLLSend, isLowLevelSendInst: isLLSend,

@ -10,7 +10,7 @@ function txOrigin () {
txOrigin.prototype.visit = function (node) { txOrigin.prototype.visit = function (node) {
if (node.name === 'MemberAccess' && if (node.name === 'MemberAccess' &&
node.attributes.member_name === 'origin' && node.attributes.member_name === 'origin' &&
node.attributes.type === 'address' && (node.attributes.type === 'address' || node.attributes.type === 'address payable') &&
node.children && node.children.length && node.children && node.children.length &&
node.children[0].attributes.type === 'tx' && node.children[0].attributes.type === 'tx' &&
node.children[0].attributes.value === 'tx') { node.children[0].attributes.value === 'tx') {

@ -4,10 +4,13 @@ var remixLib = require('remix-lib')
var StatRunner = require('../../src/solidity-analyzer') var StatRunner = require('../../src/solidity-analyzer')
var compilerInput = remixLib.helpers.compiler.compilerInput var compilerInput = remixLib.helpers.compiler.compilerInput
var compiler = require('solc') const niv = require('npm-install-version')
niv.install('solc@0.4.24')
var compiler = niv.require('solc@0.4.24')
var fs = require('fs') var fs = require('fs')
var path = require('path') var path = require('path')
var folder = 'solidity-v0.4.24'
var testFiles = [ var testFiles = [
'KingOfTheEtherThrone.sol', 'KingOfTheEtherThrone.sol',
@ -40,8 +43,8 @@ var testFiles = [
var testFileAsts = {} var testFileAsts = {}
testFiles.forEach((fileName) => { testFiles.forEach((fileName) => {
var content = fs.readFileSync(path.join(__dirname, 'test-contracts', fileName), 'utf8') var content = fs.readFileSync(path.join(__dirname, 'test-contracts/' + folder, fileName), 'utf8')
testFileAsts[fileName] = JSON.parse(compiler.compile(compilerInput(content))) testFileAsts[fileName] = JSON.parse(compiler.compileStandardWrapper(compilerInput(content)))
}) })
test('Integration test thisLocal.js', function (t) { test('Integration test thisLocal.js', function (t) {

@ -0,0 +1,866 @@
var test = require('tape')
var remixLib = require('remix-lib')
var StatRunner = require('../../src/solidity-analyzer')
var compilerInput = remixLib.helpers.compiler.compilerInput
const niv = require('npm-install-version')
niv.install('solc@0.5.0')
var compiler = niv.require('solc@0.5.0')
var fs = require('fs')
var path = require('path')
var folder = 'solidity-v0.5'
var testFiles = [
'KingOfTheEtherThrone.sol',
'assembly.sol',
'ballot.sol',
'ballot_reentrant.sol',
'ballot_withoutWarnings.sol',
'cross_contract.sol',
'inheritance.sol',
'modifier1.sol',
'modifier2.sol',
'notReentrant.sol',
'structReentrant.sol',
'thisLocal.sol',
'globals.sol',
'library.sol',
'transfer.sol',
'ctor.sol',
'forgottenReturn.sol',
'selfdestruct.sol',
'deleteDynamicArray.sol',
'deleteFromDynamicArray.sol',
'blockLevelCompare.sol',
'intDivisionTruncate.sol',
'ERC20.sol',
'stringBytesLength.sol',
'forLoopIteratesOverDynamicArray.sol'
]
var testFileAsts = {}
testFiles.forEach((fileName) => {
var content = fs.readFileSync(path.join(__dirname, 'test-contracts/' + folder, fileName), 'utf8')
testFileAsts[fileName] = JSON.parse(compiler.compile(compilerInput(content)))
})
test('Integration test thisLocal.js', function (t) {
t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/thisLocal')
var lengthCheck = {
'KingOfTheEtherThrone.sol': 0,
'assembly.sol': 0,
'ballot.sol': 0,
'ballot_reentrant.sol': 1,
'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': 1,
'globals.sol': 0,
'library.sol': 0,
'transfer.sol': 0,
'ctor.sol': 0,
'forgottenReturn.sol': 0,
'selfdestruct.sol': 0,
'deleteDynamicArray.sol': 0,
'deleteFromDynamicArray.sol': 0,
'blockLevelCompare.sol': 0,
'intDivisionTruncate.sol': 0,
'ERC20.sol': 0,
'stringBytesLength.sol': 0,
'forLoopIteratesOverDynamicArray.sol': 0
}
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('../../src/solidity-analyzer/modules/checksEffectsInteraction')
var lengthCheck = {
'KingOfTheEtherThrone.sol': 1,
'assembly.sol': 1,
'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,
'globals.sol': 1,
'library.sol': 1,
'transfer.sol': 1,
'ctor.sol': 0,
'forgottenReturn.sol': 0,
'selfdestruct.sol': 0,
'deleteDynamicArray.sol': 0,
'deleteFromDynamicArray.sol': 0,
'blockLevelCompare.sol': 0,
'intDivisionTruncate.sol': 0,
'ERC20.sol': 0,
'stringBytesLength.sol': 0,
'forLoopIteratesOverDynamicArray.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 constantFunctions.js', function (t) {
t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/constantFunctions')
var lengthCheck = {
'KingOfTheEtherThrone.sol': 0,
'assembly.sol': 0,
'ballot.sol': 0,
'ballot_reentrant.sol': 0,
'ballot_withoutWarnings.sol': 0,
'cross_contract.sol': 0,
'inheritance.sol': 0,
'modifier1.sol': 1,
'modifier2.sol': 0,
'notReentrant.sol': 0,
'structReentrant.sol': 1,
'thisLocal.sol': 1,
'globals.sol': 0,
'library.sol': 3,
'transfer.sol': 0,
'ctor.sol': 0,
'forgottenReturn.sol': 0,
'selfdestruct.sol': 0,
'deleteDynamicArray.sol': 0,
'deleteFromDynamicArray.sol': 0,
'blockLevelCompare.sol': 0,
'intDivisionTruncate.sol': 0,
'ERC20.sol': 0,
'stringBytesLength.sol': 0,
'forLoopIteratesOverDynamicArray.sol': 0
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of constant warnings`)
})
})
test('Integration test inlineAssembly.js', function (t) {
t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/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,
'globals.sol': 0,
'library.sol': 0,
'transfer.sol': 0,
'ctor.sol': 0,
'forgottenReturn.sol': 0,
'selfdestruct.sol': 0,
'deleteDynamicArray.sol': 0,
'deleteFromDynamicArray.sol': 0,
'blockLevelCompare.sol': 0,
'intDivisionTruncate.sol': 0,
'ERC20.sol': 0,
'stringBytesLength.sol': 0,
'forLoopIteratesOverDynamicArray.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('../../src/solidity-analyzer/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,
'globals.sol': 1,
'library.sol': 0,
'transfer.sol': 0,
'ctor.sol': 0,
'forgottenReturn.sol': 0,
'selfdestruct.sol': 0,
'deleteDynamicArray.sol': 0,
'deleteFromDynamicArray.sol': 0,
'blockLevelCompare.sol': 0,
'intDivisionTruncate.sol': 0,
'ERC20.sol': 0,
'stringBytesLength.sol': 0,
'forLoopIteratesOverDynamicArray.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('../../src/solidity-analyzer/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': 1,
'globals.sol': 1,
'library.sol': 1,
'transfer.sol': 1,
'ctor.sol': 0,
'forgottenReturn.sol': 3,
'selfdestruct.sol': 0,
'deleteDynamicArray.sol': 2,
'deleteFromDynamicArray.sol': 1,
'blockLevelCompare.sol': 1,
'intDivisionTruncate.sol': 1,
'ERC20.sol': 2,
'stringBytesLength.sol': 1,
'forLoopIteratesOverDynamicArray.sol': 1
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of gasCost warnings`)
})
})
test('Integration test similarVariableNames.js', function (t) {
t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/similarVariableNames')
var lengthCheck = {
'KingOfTheEtherThrone.sol': 0,
'assembly.sol': 0,
'ballot.sol': 2,
'ballot_reentrant.sol': 11,
'ballot_withoutWarnings.sol': 0,
'cross_contract.sol': 0,
'inheritance.sol': 0,
'modifier1.sol': 0,
'modifier2.sol': 0,
'notReentrant.sol': 1,
'structReentrant.sol': 0,
'thisLocal.sol': 0,
'globals.sol': 0,
'library.sol': 0,
'transfer.sol': 0,
'ctor.sol': 1,
'forgottenReturn.sol': 0,
'selfdestruct.sol': 0,
'deleteDynamicArray.sol': 1,
'deleteFromDynamicArray.sol': 0,
'blockLevelCompare.sol': 0,
'intDivisionTruncate.sol': 0,
'ERC20.sol': 0,
'stringBytesLength.sol': 0,
'forLoopIteratesOverDynamicArray.sol': 0
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of similarVariableNames warnings`)
})
})
test('Integration test inlineAssembly.js', function (t) {
t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/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,
'globals.sol': 0,
'library.sol': 0,
'transfer.sol': 0,
'ctor.sol': 0,
'forgottenReturn.sol': 0,
'selfdestruct.sol': 0,
'deleteDynamicArray.sol': 0,
'deleteFromDynamicArray.sol': 0,
'blockLevelCompare.sol': 0,
'intDivisionTruncate.sol': 0,
'ERC20.sol': 0,
'stringBytesLength.sol': 0,
'forLoopIteratesOverDynamicArray.sol': 0
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of inlineAssembly warnings`)
})
})
test('Integration test blockTimestamp.js', function (t) {
t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/blockTimestamp')
var lengthCheck = {
'KingOfTheEtherThrone.sol': 1,
'assembly.sol': 0,
'ballot.sol': 0,
'ballot_reentrant.sol': 3,
'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,
'globals.sol': 2,
'library.sol': 0,
'transfer.sol': 0,
'ctor.sol': 0,
'forgottenReturn.sol': 0,
'selfdestruct.sol': 0,
'deleteDynamicArray.sol': 0,
'deleteFromDynamicArray.sol': 0,
'blockLevelCompare.sol': 0,
'intDivisionTruncate.sol': 0,
'ERC20.sol': 0,
'stringBytesLength.sol': 0,
'forLoopIteratesOverDynamicArray.sol': 0
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of blockTimestamp warnings`)
})
})
test('Integration test lowLevelCalls.js', function (t) {
t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/lowLevelCalls')
var lengthCheck = {
'KingOfTheEtherThrone.sol': 1,
'assembly.sol': 1,
'ballot.sol': 0,
'ballot_reentrant.sol': 7,
'ballot_withoutWarnings.sol': 0,
'cross_contract.sol': 1,
'inheritance.sol': 1,
'modifier1.sol': 0,
'modifier2.sol': 0,
'notReentrant.sol': 1,
'structReentrant.sol': 1,
'thisLocal.sol': 2,
'globals.sol': 1,
'library.sol': 1,
'transfer.sol': 0,
'ctor.sol': 0,
'forgottenReturn.sol': 0,
'selfdestruct.sol': 0,
'deleteDynamicArray.sol': 0,
'deleteFromDynamicArray.sol': 0,
'blockLevelCompare.sol': 0,
'intDivisionTruncate.sol': 0,
'ERC20.sol': 0,
'stringBytesLength.sol': 0,
'forLoopIteratesOverDynamicArray.sol': 0
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of lowLevelCalls warnings`)
})
})
test('Integration test blockBlockhash.js', function (t) {
t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/blockBlockhash')
var lengthCheck = {
'KingOfTheEtherThrone.sol': 0,
'assembly.sol': 0,
'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,
'globals.sol': 0, // was 1 !! @TODO
'library.sol': 0,
'transfer.sol': 0,
'ctor.sol': 0,
'forgottenReturn.sol': 0,
'selfdestruct.sol': 0,
'deleteDynamicArray.sol': 0,
'deleteFromDynamicArray.sol': 0,
'blockLevelCompare.sol': 0,
'intDivisionTruncate.sol': 0,
'ERC20.sol': 0,
'stringBytesLength.sol': 0,
'forLoopIteratesOverDynamicArray.sol': 0
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of blockBlockhash warnings`)
})
})
/*
! No return gives compilation error with solidity 0.5.0
test('Integration test noReturn.js', function (t) {
t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/noReturn')
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': 1,
'modifier2.sol': 0,
'notReentrant.sol': 0,
'structReentrant.sol': 0,
'thisLocal.sol': 1,
'globals.sol': 0,
'library.sol': 0,
'transfer.sol': 0,
'ctor.sol': 0,
'forgottenReturn.sol': 1,
'selfdestruct.sol': 0,
'deleteDynamicArray.sol': 0,
'deleteFromDynamicArray.sol': 0,
'blockLevelCompare.sol': 0,
'intDivisionTruncate.sol': 0,
'ERC20.sol': 0,
'stringBytesLength.sol': 0,
'forLoopIteratesOverDynamicArray.sol': 0
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of noReturn warnings`)
})
})
*/
test('Integration test selfdestruct.js', function (t) {
t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/selfdestruct')
var lengthCheck = {
'KingOfTheEtherThrone.sol': 0,
'assembly.sol': 0,
'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,
'globals.sol': 2,
'library.sol': 0,
'transfer.sol': 0,
'ctor.sol': 0,
'forgottenReturn.sol': 0,
'selfdestruct.sol': 3,
'deleteDynamicArray.sol': 0,
'deleteFromDynamicArray.sol': 0,
'blockLevelCompare.sol': 0,
'ERC20.sol': 0,
'intDivisionTruncate.sol': 5,
'stringBytesLength.sol': 0,
'forLoopIteratesOverDynamicArray.sol': 0
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of selfdestruct warnings`)
})
})
test('Integration test guardConditions.js', function (t) {
t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/guardConditions')
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,
'globals.sol': 1,
'library.sol': 0,
'transfer.sol': 0,
'ctor.sol': 0,
'forgottenReturn.sol': 0,
'selfdestruct.sol': 0,
'deleteDynamicArray.sol': 1,
'deleteFromDynamicArray.sol': 0,
'blockLevelCompare.sol': 0,
'intDivisionTruncate.sol': 1,
'ERC20.sol': 0,
'stringBytesLength.sol': 0,
'forLoopIteratesOverDynamicArray.sol': 0
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of guardCondition warnings`)
})
})
test('Integration test deleteDynamicArrays.js', function (t) {
t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/deleteDynamicArrays')
var lengthCheck = {
'KingOfTheEtherThrone.sol': 0,
'assembly.sol': 0,
'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,
'globals.sol': 0,
'library.sol': 0,
'transfer.sol': 0,
'ctor.sol': 0,
'forgottenReturn.sol': 0,
'selfdestruct.sol': 0,
'deleteDynamicArray.sol': 2,
'deleteFromDynamicArray.sol': 0,
'blockLevelCompare.sol': 0,
'intDivisionTruncate.sol': 0,
'ERC20.sol': 0,
'stringBytesLength.sol': 0,
'forLoopIteratesOverDynamicArray.sol': 0
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of deleteDynamicArrays warnings`)
})
})
test('Integration test deleteFromDynamicArray.js', function (t) {
t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/deleteFromDynamicArray')
var lengthCheck = {
'KingOfTheEtherThrone.sol': 0,
'assembly.sol': 0,
'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,
'globals.sol': 0,
'library.sol': 0,
'transfer.sol': 0,
'ctor.sol': 0,
'forgottenReturn.sol': 0,
'selfdestruct.sol': 0,
'deleteDynamicArray.sol': 0,
'deleteFromDynamicArray.sol': 1,
'blockLevelCompare.sol': 0,
'intDivisionTruncate.sol': 0,
'ERC20.sol': 0,
'stringBytesLength.sol': 0,
'forLoopIteratesOverDynamicArray.sol': 0
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of deleteFromDynamicArray warnings`)
})
})
test('Integration test assignAndCompare.js', function (t) {
t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/assignAndCompare')
var lengthCheck = {
'KingOfTheEtherThrone.sol': 0,
'assembly.sol': 0,
'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,
'globals.sol': 0,
'library.sol': 0,
'transfer.sol': 0,
'ctor.sol': 0,
'forgottenReturn.sol': 0,
'selfdestruct.sol': 0,
'deleteDynamicArray.sol': 0,
'deleteFromDynamicArray.sol': 0,
'blockLevelCompare.sol': 8,
'intDivisionTruncate.sol': 0,
'ERC20.sol': 0,
'stringBytesLength.sol': 0,
'forLoopIteratesOverDynamicArray.sol': 0
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of assignAndCompare warnings`)
})
})
test('Integration test intDivisionTruncate.js', function (t) {
t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/intDivisionTruncate')
var lengthCheck = {
'KingOfTheEtherThrone.sol': 0,
'assembly.sol': 0,
'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,
'globals.sol': 0,
'library.sol': 0,
'transfer.sol': 0,
'ctor.sol': 0,
'forgottenReturn.sol': 0,
'selfdestruct.sol': 0,
'deleteDynamicArray.sol': 0,
'deleteFromDynamicArray.sol': 0,
'blockLevelCompare.sol': 0,
'intDivisionTruncate.sol': 2,
'ERC20.sol': 0,
'stringBytesLength.sol': 0,
'forLoopIteratesOverDynamicArray.sol': 0
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of intDivisionTruncate warnings`)
})
})
test('Integration test erc20Decimal.js', function (t) {
t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/erc20Decimals')
var lengthCheck = {
'KingOfTheEtherThrone.sol': 0,
'assembly.sol': 0,
'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,
'globals.sol': 0,
'library.sol': 0,
'transfer.sol': 0,
'ctor.sol': 0,
'forgottenReturn.sol': 0,
'selfdestruct.sol': 0,
'deleteDynamicArray.sol': 0,
'deleteFromDynamicArray.sol': 0,
'blockLevelCompare.sol': 0,
'intDivisionTruncate.sol': 0,
'ERC20.sol': 1,
'stringBytesLength.sol': 0,
'forLoopIteratesOverDynamicArray.sol': 0
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of erc20Decimals warnings`)
})
})
test('Integration test stringBytesLength.js', function (t) {
t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/stringBytesLength')
var lengthCheck = {
'KingOfTheEtherThrone.sol': 0,
'assembly.sol': 0,
'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,
'globals.sol': 0,
'library.sol': 0,
'transfer.sol': 0,
'ctor.sol': 0,
'forgottenReturn.sol': 0,
'selfdestruct.sol': 0,
'deleteDynamicArray.sol': 0,
'deleteFromDynamicArray.sol': 0,
'blockLevelCompare.sol': 0,
'intDivisionTruncate.sol': 0,
'ERC20.sol': 0,
'stringBytesLength.sol': 1,
'forLoopIteratesOverDynamicArray.sol': 0
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of stringBytesLength warnings`)
})
})
test('Integration test forLoopIteratesOverDynamicArray.js', function (t) {
t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/forLoopIteratesOverDynamicArray')
var lengthCheck = {
'KingOfTheEtherThrone.sol': 0,
'assembly.sol': 0,
'ballot.sol': 2,
'ballot_reentrant.sol': 1,
'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,
'globals.sol': 0,
'library.sol': 0,
'transfer.sol': 0,
'ctor.sol': 0,
'forgottenReturn.sol': 0,
'selfdestruct.sol': 0,
'deleteDynamicArray.sol': 0,
'deleteFromDynamicArray.sol': 0,
'blockLevelCompare.sol': 0,
'intDivisionTruncate.sol': 0,
'ERC20.sol': 0,
'stringBytesLength.sol': 0,
'forLoopIteratesOverDynamicArray.sol': 1
}
runModuleOnFiles(module, t, (file, report) => {
t.equal(report.length, lengthCheck[file], `${file} has right amount of forLoopIteratesOverDynamicArray 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) => {
let report = reports[0].report
if (report.some((x) => x['warning'].includes('INTERNAL ERROR'))) {
t.comment('Error while executing Module: ' + JSON.stringify(report))
}
cb(fileName, report)
})
})
}

@ -0,0 +1,37 @@
var test = require('tape')
var remixLib = require('remix-lib')
var StatRunner = require('../../src/solidity-analyzer')
var compilerInput = remixLib.helpers.compiler.compilerInput
const niv = require('npm-install-version')
niv.install('solc@0.4.24')
var compiler = niv.require('solc@0.4.24')
var fs = require('fs')
var path = require('path')
var folder = 'solidity-v0.4.24'
function compile (fileName) {
var content = fs.readFileSync(path.join(__dirname, 'test-contracts/' + folder, fileName), 'utf8')
return JSON.parse(compiler.compileStandardWrapper(compilerInput(content)))
}
test('staticAnalysisIssues.functionParameterPassingError', function (t) {
// https://github.com/ethereum/remix-ide/issues/889#issuecomment-351746474
t.plan(2)
var res = compile('functionParameters.sol')
var module = require('../../src/solidity-analyzer/modules/checksEffectsInteraction')
var statRunner = new StatRunner()
t.doesNotThrow(() => {
statRunner.runWithModuleList(res, [{ name: module.name, mod: new module.Module() }], (reports) => {
})
}, true, 'Analysis should not throw')
statRunner.runWithModuleList(res, [{ name: module.name, mod: new module.Module() }], (reports) => {
t.ok(!reports.some((mod) => mod.report.some((rep) => rep.warning.includes('INTERNAL ERROR')), 'Should not have internal errors'))
})
})

@ -4,13 +4,16 @@ var remixLib = require('remix-lib')
var StatRunner = require('../../src/solidity-analyzer') var StatRunner = require('../../src/solidity-analyzer')
var compilerInput = remixLib.helpers.compiler.compilerInput var compilerInput = remixLib.helpers.compiler.compilerInput
var compiler = require('solc') const niv = require('npm-install-version')
niv.install('solc@0.5.0')
var compiler = niv.require('solc@0.5.0')
var fs = require('fs') var fs = require('fs')
var path = require('path') var path = require('path')
var folder = 'solidity-v0.5'
function compile (fileName) { function compile (fileName) {
var content = fs.readFileSync(path.join(__dirname, 'test-contracts', fileName), 'utf8') var content = fs.readFileSync(path.join(__dirname, 'test-contracts/' + folder, fileName), 'utf8')
return JSON.parse(compiler.compile(compilerInput(content))) return JSON.parse(compiler.compile(compilerInput(content)))
} }

@ -0,0 +1,46 @@
pragma solidity >=0.4.9 <0.6.0;
contract EIP20 {
uint public decimals = 12;
// optional
function name() public pure returns (string memory) {
return "MYTOKEN";
}
// optional
function symbol() public pure returns (string memory) {
return "MT";
}
// optional
//function decimals() internal pure returns (uint8) {
// return 12;
//}
function totalSupply() public pure returns (uint256) {
return 12000;
}
function balanceOf(address _owner) public pure returns (uint256) {
return 0;
}
function transfer(address _to, uint256 _value) public pure returns (bool success) {
return true;
}
function transferFrom(address _from, address _to, uint256 _value) public pure returns (bool) {
return true;
}
function approve(address _spender, uint256 _value) public pure returns (bool) {
return true;
}
function allowance(address _owner, address _spender) public pure returns (uint256) {
return 0;
}
}

@ -0,0 +1,23 @@
// return value send
contract KingOfTheEtherThrone{
struct Monarch {
// address of the king .
address payable ethAddr ;
string name ;
// how much he pays to previous king
uint claimPrice ;
uint coronationTimestamp;
}
Monarch public currentMonarch ;
function claimThrone ( string memory name ) public {
address wizardAddress;
uint compensation = 100;
uint valuePaid = 10;
if ( currentMonarch.ethAddr != wizardAddress )
if (currentMonarch.ethAddr.send( compensation )) revert();
currentMonarch = Monarch(msg.sender,name,valuePaid,block.timestamp);
}
}

@ -0,0 +1,30 @@
pragma solidity >=0.4.9 <0.6.0;
contract test {
address owner;
function at(address _addr) public returns (bytes memory o_code) {
assert(_addr != address(0x0));
assembly {
// retrieve the size of the code, this needs assembly
let size := extcodesize(_addr)
// allocate output byte array - this could also be done without assembly
// by using o_code = new bytes(size)
o_code := mload(0x40)
// new "memory end" including padding
mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
// store length in memory
mstore(o_code, size)
// actually retrieve the code, this needs assembly
extcodecopy(_addr, add(o_code, 0x20), 0, size)
}
}
function bla() public {
require(tx.origin == owner);
msg.sender.send(19);
assembly {
}
}
}

@ -0,0 +1,145 @@
pragma solidity >=0.4.9 <0.6.0;
/// @title Voting with delegation.
contract Ballot {
// This declares a new complex type which will
// be used for variables later.
// It will represent a single voter.
struct Voter {
uint weight; // weight is accumulated by delegation
bool voted; // if true, that person already voted
address delegate; // person delegated to
uint vote; // index of the voted proposal
}
// This is a type for a single proposal.
struct Proposal
{
bytes32 name; // short name (up to 32 bytes)
uint voteCount; // number of accumulated votes
}
address public chairperson;
// This declares a state variable that
// stores a `Voter` struct for each possible address.
mapping(address => Voter) public voters;
// A dynamically-sized array of `Proposal` structs.
Proposal[] public proposals;
/// Create a new ballot to choose one of `proposalNames`.
constructor(bytes32[] memory proposalNames) public {
chairperson = msg.sender;
voters[chairperson].weight = 1;
// For each of the provided proposal names,
// create a new proposal object and add it
// to the end of the array.
for (uint i = 0; i < proposalNames.length; i++) {
// `Proposal({...})` creates a temporary
// Proposal object and `proposals.push(...)`
// appends it to the end of `proposals`.
proposals.push(Proposal({
name: proposalNames[i],
voteCount: 0
}));
}
}
// Give `voter` the right to vote on this ballot.
// May only be called by `chairperson`.
function giveRightToVote(address voter) public {
if (msg.sender != chairperson || voters[voter].voted) {
// `throw` terminates and reverts all changes to
// the state and to Ether balances. It is often
// a good idea to use this if functions are
// called incorrectly. But watch out, this
// will also consume all provided gas.
revert();
}
voters[voter].weight = 1;
}
/// Delegate your vote to the voter `to`.
function delegate(address to) public {
// assigns reference
Voter memory sender = voters[msg.sender];
if (sender.voted)
revert();
// Forward the delegation as long as
// `to` also delegated.
// In general, such loops are very dangerous,
// because if they run too long, they might
// need more gas than is available in a block.
// In this case, the delegation will not be executed,
// but in other situations, such loops might
// cause a contract to get "stuck" completely.
while (
voters[to].delegate != address(0) &&
voters[to].delegate != msg.sender
) {
to = voters[to].delegate;
}
// We found a loop in the delegation, not allowed.
if (to == msg.sender) {
revert();
}
// Since `sender` is a reference, this
// modifies `voters[msg.sender].voted`
sender.voted = true;
sender.delegate = to;
Voter memory delegate = voters[to];
if (delegate.voted) {
// If the delegate already voted,
// directly add to the number of votes
proposals[delegate.vote].voteCount += sender.weight;
} else {
// If the delegate did not vote yet,
// add to her weight.
delegate.weight += sender.weight;
}
}
/// Give your vote (including votes delegated to you)
/// to proposal `proposals[proposal].name`.
function vote(uint proposal) public {
Voter memory sender = voters[msg.sender];
if (sender.voted)
revert();
sender.voted = true;
sender.vote = proposal;
// If `proposal` is out of the range of the array,
// this will throw automatically and revert all
// changes.
proposals[proposal].voteCount += sender.weight;
}
/// @dev Computes the winning proposal taking all
/// previous votes into account.
function winningProposal() view public
returns (uint winningProposal)
{
uint winningVoteCount = 0;
for (uint p = 0; p < proposals.length; p++) {
if (proposals[p].voteCount > winningVoteCount) {
winningVoteCount = proposals[p].voteCount;
winningProposal = p;
}
}
}
// Calls winningProposal() function to get the index
// of the winner contained in the proposals array and then
// returns the name of the winner
function winnerName() view public
returns (bytes32 winnerName)
{
winnerName = proposals[winningProposal()].name;
}
}

@ -0,0 +1,111 @@
pragma solidity >=0.4.9 <0.6.0;
contract InfoFeed {
function info() public payable returns (uint ret);
function call1(uint a) public payable returns (bool);
}
contract Ballot {
InfoFeed feed;
struct Voter {
uint weight;
bool voted;
uint8 vote;
address delegate;
}
struct Proposal {
uint voteCount;
}
address chairperson;
mapping(address => Voter) voters;
Proposal[] proposals;
function send1(address a) public {
giveRightToVote(a,a);
}
/// Create a new ballot with $(_numProposals) different proposals.
constructor(uint8 _numProposals) public {
address payable d;
(bool success, bytes memory data) = d.delegatecall.gas(800)(abi.encodeWithSignature('function_name', 'arg1', 'arg2'));
if(!success) revert();
(bool success2, bytes memory data2) = d.delegatecall.gas(800)(abi.encodeWithSignature('function_name', 'arg1', 'arg2'));
if(!success2) revert();
(bool success3, bytes memory data3) = d.call.value(10).gas(800)(abi.encodeWithSignature('function_name', 'arg1', 'arg2'));
if(!success3) revert();
(bool success4, bytes memory data4) = d.call.value(10).gas(800)(abi.encodeWithSignature('function_name', 'arg1', 'arg2'));
if(!success4) revert();
address payable o = msg.sender;
if(!o.send(1 wei)) revert();
(bool success5, bytes memory data5) = d.call(abi.encodeWithSignature('function_name', 'arg1', 'arg2'));
if(!success5) revert();
uint a = now;
uint c = block.timestamp;
if(block.timestamp < 100){}
chairperson = msg.sender;
voters[chairperson].weight = 1;
proposals.length = _numProposals;
if(!d.send(1 wei)) revert();
feed.info.value(10).gas(800)();
feed.call1(1);
this.send1(d);
}
/// Give $(voter) the right to vote on this ballot.
/// May only be called by $(chairperson).
function giveRightToVote(address voter, address b) public payable returns (bool){
if (msg.sender != chairperson || voters[voter].voted) return false;
voters[voter].weight = 1;
return true;
}
/// Delegate your vote to the voter $(to).
function delegate(address to) public {
Voter memory sender = voters[msg.sender]; // assigns reference
if (sender.voted) return;
while (voters[to].delegate != address(0) && voters[to].delegate != msg.sender)
to = voters[to].delegate;
if (to == msg.sender) return;
sender.voted = true;
sender.delegate = to;
Voter memory delegate = voters[to];
if (delegate.voted)
proposals[delegate.vote].voteCount += sender.weight;
else
delegate.weight += sender.weight;
}
/// Give a single vote to proposal $(proposal).
function vote(uint8 proposal) public {
Voter memory sender = voters[msg.sender];
if (sender.voted || proposal >= proposals.length) return;
sender.voted = true;
sender.vote = proposal;
proposals[proposal].voteCount += sender.weight;
}
function winningProposal() view public returns (uint8 winningProposal) {
uint256 winningVoteCount = 0;
for (uint8 proposal = 0; proposal < proposals.length; proposal++)
if (proposals[proposal].voteCount > winningVoteCount) {
winningVoteCount = proposals[proposal].voteCount;
winningProposal = proposal;
}
}
}

@ -0,0 +1,31 @@
pragma solidity >=0.4.9 <0.6.0;
/// @title Voting with delegation.
contract Ballot {
struct Proposal
{
bytes32 name; // short name (up to 32 bytes)
uint voteCount; // number of accumulated votes
}
// A dynamically-sized array of `Proposal` structs.
Proposal[] public proposals;
/// @dev Computes the winning proposal taking all
/// previous votes into account.
function winningProposal() public view
returns (uint winningProposal)
{
winningProposal = 0;
}
// Calls winningProposal() function to get the index
// of the winner contained in the proposals array and then
// returns the name of the winner
function winnerName() public view
returns (bytes32 winnerName)
{
winnerName = proposals[winningProposal()].name;
}
}

@ -0,0 +1,45 @@
pragma solidity >=0.4.9 <0.6.0;
contract grr {
bool breaker;
function() external {
uint a = 1;
string memory sig = "withdraw()";
uint b = 3;
bytes4 selector = bytes4(keccak256(abi.encodePacked(sig)));
abi.encode(a,b);
abi.encodePacked(a,b);
a = -b;
a == b;
if(a == b) {
abi.encodeWithSelector(selector, a, b);
abi.encodeWithSignature(sig, a, b);
}
if(b < 4) { a == b; }
if(b > 4) b == a;
while(true) a == b;
for(int i = 0; i < 3; i++) b == a;
while(false) {
int c = 3;
uint(c) + a;
c == 5;
}
a + b;
breaker = false;
}
}

@ -0,0 +1,19 @@
pragma solidity >=0.4.9 <0.6.0;
contract a {
uint x;
function foo() public {
x++;
}
}
contract b {
a x;
function bar() public {
address payable a;
a.send(100 wei);
x.foo();
}
}

@ -0,0 +1,8 @@
contract c {
uint x;
uint x_abc;
constructor(uint _x, uint _abc) public {
x=_x;
x_abc=_abc;
}
}

@ -0,0 +1,37 @@
pragma solidity >=0.4.9 <0.6.0;
contract arr {
uint[] users;
bytes access_rights_per_user;
uint user_index;
address owner;
string grr = "message";
uint[100] last_100_users;
constructor(address owner1) public {
owner = owner1;
user_index = 0;
}
function addUser(uint id, byte rights) public{
users[user_index] = id;
last_100_users[user_index % 100] = id;
access_rights_per_user[user_index] = rights;
user_index++;
}
function resetState() public{
require(msg.sender == owner, grr);
delete users;
delete access_rights_per_user;
delete last_100_users;
}
function bla(string memory bal) public {
grr = bal;
}
}

@ -0,0 +1,22 @@
pragma solidity >=0.4.9 <0.6.0;
contract arr {
uint[] array = [1,2,3];
function removeAtIndex() public returns (uint[] memory) {
delete array[1];
return array;
}
// TODO: deleteFromDynamicArray should not generate warnings if array item is shifted and removed
/* function safeRemoveAtIndex(uint index) returns (uint[] memory) {
if (index >= array.length) return;
for (uint i = index; i < array.length-1; i++) {
array[i] = array[i+1];
}
delete array[array.length-1];
array.length--;
return array;
} */
}

@ -0,0 +1,15 @@
pragma solidity >=0.4.9 <0.6.0;
contract forLoopArr {
uint[] array;
constructor(uint[] memory _array) public {
array = _array;
}
function shiftArrItem(uint index) public returns(uint[] memory) {
// TODO: for (uint i = index; i < array.length-1; i++) should also generate warning
for (uint i = index; i < array.length; i++) {
array[i] = array[i+1];
}
return array;
}
}

@ -0,0 +1,13 @@
contract Sheep {
string public name;
string public dna;
bool g = true;
constructor(string memory _name, string memory _dna) public {
name = _name;
dna = _dna;
}
function geneticallyEngineer(string memory _dna) public returns (bool) {
dna = _dna;
}
}

@ -0,0 +1,16 @@
pragma solidity >=0.4.9 <0.6.0;
contract B {
function plus(uint a, uint b) pure internal returns (uint) {
return a + b;
}
function eval(function (uint, uint) pure internal returns (uint) f, uint x, uint y) pure internal returns (uint) {
return f(x, y);
}
function calc(uint x, uint y) pure public returns (uint) {
return eval(plus, x, y);
// return plus(x, y);
}
}

@ -0,0 +1,57 @@
pragma solidity >=0.4.9 <0.6.0;
contract bla {
uint brr;
function duper() public {
brr++;
}
}
contract a is bla {
function blub() public {
brr++;
}
function r () public payable {
address payable a;
bytes32 hash;
uint8 v;
bytes32 r;
bytes32 s;
blockhash(1);
block.coinbase;
block.difficulty;
block.gaslimit;
block.number;
block.timestamp;
msg.data;
gasleft();
msg.sender;
msg.value;
now;
tx.gasprice;
tx.origin;
// assert(1 == 2);
// require(1 == 1);
keccak256(abi.encodePacked(a));
sha256(abi.encodePacked(a));
ripemd160(abi.encodePacked(a));
ecrecover(hash, v, r, s);
addmod(1, 2, 2);
mulmod(4,4,12);
a.balance;
blub();
a.send(a.balance);
super.duper();
//a.transfer(a.balance);
selfdestruct(a);
//revert();
assert(a.balance == 0);
}
}

@ -0,0 +1,41 @@
pragma solidity >=0.4.9 <0.6.0;
contract r {
function s() public view {}
}
contract a is r {
uint x = 1;
function getX() public view returns (uint) {
return x;
}
}
contract b is a {
uint y = 2;
uint x = 3;
function getY(uint z, bool r) public returns (uint) {
return y++;
}
function getY(string storage n) internal view returns (uint) { return 10; }
}
contract c is b {
string x;
function d() public returns (uint a, uint b) {
//d();
//sha3("bla");
address payable o = msg.sender;
o.call.gas(200000).value(address(this).balance)(abi.encode("pay()"));
//x++;
getY(x);
a = getX() + getY(1, false);
b = getX() + getY(x);
}
}

@ -0,0 +1,38 @@
pragma solidity >=0.4.9 <0.6.0;
contract CharityCampaign {
mapping (address => uint) contributions;
int128 feePercentage;
uint p2;
address payable processor;
address payable beneficiary;
constructor(address payable _beneficiary, int128 _feePercentage) public {
processor = msg.sender;
beneficiary = _beneficiary;
feePercentage = _feePercentage;
}
function contribute() payable public returns (uint feeCollected) {
uint fee = msg.value * uint256(feePercentage / 100);
fee = msg.value * (p2 / 100);
contributions[msg.sender] = msg.value - fee;
processor.transfer(fee);
return fee;
}
function endCampaign() public returns (bool) {
require(msg.sender == processor || msg.sender == beneficiary);
selfdestruct(beneficiary);
return true;
}
// FALSE POSITIVE FOR SELFDESTRUCT TERMINAL
function endAmbiguous() public {
if(msg.sender == address(0x0)) {
selfdestruct(beneficiary);
} else {
selfdestruct(processor);
}
}
}

@ -0,0 +1,54 @@
pragma solidity >=0.4.9 <0.6.0;
library Set {
// We define a new struct datatype that will be used to
// hold its data in the calling contract.
struct Data { mapping(uint => bool) flags; }
// Note that the first parameter is of type "storage
// reference" and thus only its storage address and not
// its contents is passed as part of the call. This is a
// special feature of library functions. It is idiomatic
// to call the first parameter 'self', if the function can
// be seen as a method of that object.
function insert(Data storage self, uint value) public
returns (bool)
{
if (self.flags[value])
return false; // already there
self.flags[value] = true;
return true;
}
function remove(Data storage self, uint value) public
returns (bool)
{
if (!self.flags[value])
return false; // not there
self.flags[value] = false;
return true;
}
function contains(Data storage self, uint value) public
returns (bool)
{
return self.flags[value];
}
}
contract C {
Set.Data knownValues;
function register(uint value) public {
// The library functions can be called without a
// specific instance of the library, since the
// "instance" will be the current contract.
address payable a;
a.send(10 wei);
if (!Set.insert(knownValues, value))
revert();
}
// In this contract, we can also directly access knownValues.flags, if we want.
}

@ -0,0 +1,17 @@
pragma solidity >=0.4.9 <0.6.0;
contract test {
address owner;
modifier onlyOwner {
uint a = 0;
if (msg.sender != owner)
revert();
_;
}
function b(address a) public onlyOwner returns (bool) {
return true;
}
}

@ -0,0 +1,28 @@
pragma solidity >=0.4.9 <0.6.0;
contract owned {
uint r=0;
modifier ntimes(uint n) {
for(uint i=0;i<n-1;i++){
_;
}
_;
}
modifier plus100 {
uint bla=1;
r+=100;
_;
}
function a() ntimes(10) plus100 public payable returns (uint) {
{
uint bla=5;
r += bla;
}
return r;
}
}

@ -0,0 +1,13 @@
pragma solidity >=0.4.9 <0.6.0;
contract Fund {
/// Mapping of ether shares of the contract.
mapping(address => uint) shares;
/// Withdraw your share.
function withdraw() public {
uint share = shares[msg.sender];
shares[msg.sender] = 0;
if (!msg.sender.send(share))
revert();
}
}

@ -0,0 +1,49 @@
pragma solidity >=0.4.9 <0.6.0;
contract InfoFeed {
uint c;
function info() constant returns (uint ret) {return c;}
function call1(uint a) constant returns (bool) {return true;}
}
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
contract Fund {
/// Mapping of ether shares of the contract.
//mapping(address => uint) shares;
/// Withdraw your share.
uint c = 0;
function withdraw() constant {
InfoFeed f;
//shares[msg.sender] /= 1;
f.info();
//if (msg.sender.send(shares[msg.sender])) throw;
// shares[msg.sender] = 0;
b(true, false);
//shares[msg.sender]++;
//c++;
}
mapping(address => uint) shares;
function b(bool a, bool b) returns (bool) {
mapping(address => uint) c = shares;
c[msg.sender] = 0;
//f();
//withdraw();
//shares[msg.sender]++;
//c++;
return true;
}
function f() {
c++;
withdraw();
}
}

@ -0,0 +1,14 @@
contract sd {
uint x = 0;
function() external payable { }
function c () public {
selfdestruct(address(0xdeadbeef));
}
function b () public {
selfdestruct(address(0xdeadbeef));
x = 1;
}
}

@ -0,0 +1,10 @@
pragma solidity >=0.4.9 <0.6.0;
contract bytesString {
function length(string memory a) public pure returns(uint) {
bytes memory x = bytes(a);
return x.length;
}
}

@ -0,0 +1,36 @@
pragma solidity >=0.4.9 <0.6.0;
contract Ballot {
struct Voter {
uint weight;
bool voted;
uint8 vote;
address delegate;
baz foo;
}
struct baz{
uint bar;
}
mapping(address => Voter) voters;
/// Create a new ballot with $(_numProposals) different proposals.
function bla(address payable a) public {
Voter storage x = voters[a];
if (!a.send(10))
revert();
//voters[a] = Voter(10,true,1,a);
//x.foo.bar *= 100;
bli(x);
}
//function bla(){}
function bli(Voter storage x) private {
x.foo.bar++;
}
}

@ -0,0 +1,16 @@
pragma solidity >=0.4.9 <0.6.0;
contract test {
function () external {
address payable x;
this.b(x);
x.call('something');
x.send(1 wei);
}
function b(address a) public returns (bool) {
}
}

@ -0,0 +1,7 @@
contract c {
uint x;
function f(address payable r) public payable {
r.transfer(1);
x = 2;
}
}

@ -1,4 +1,7 @@
require('./analysis/staticAnalysisCommon-test.js') require('./analysis/staticAnalysisCommon-test.js')
require('./analysis/staticAnalysisIntegration-test.js')
require('./analysis/staticAnalysisIssues-test.js') require('./analysis/staticAnalysisIntegration-test-0.4.24.js')
require('./analysis/staticAnalysisIssues-test-0.4.24.js')
require('./analysis/staticAnalysisIntegration-test-0.5.0.js')
require('./analysis/staticAnalysisIssues-test-0.5.0.js')

@ -23,7 +23,7 @@
"fast-async": "^6.1.2", "fast-async": "^6.1.2",
"notify-error": "^1.2.0", "notify-error": "^1.2.0",
"npm-run-all": "^4.1.2", "npm-run-all": "^4.1.2",
"remix-lib": "^0.3.11", "remix-lib": "0.3.11",
"solc": "^0.5.0" "solc": "^0.5.0"
}, },
"devDependencies": { "devDependencies": {

@ -22,7 +22,7 @@
"ethereumjs-vm": "^2.3.3", "ethereumjs-vm": "^2.3.3",
"fast-async": "^6.1.2", "fast-async": "^6.1.2",
"npm-run-all": "^4.0.2", "npm-run-all": "^4.0.2",
"remix-lib": "^0.3.11", "remix-lib": "0.3.11",
"solc": "^0.5.0", "solc": "^0.5.0",
"standard": "^7.0.1", "standard": "^7.0.1",
"tape": "^4.6.0", "tape": "^4.6.0",

Loading…
Cancel
Save