Commit a91d82b2 authored by aniket-engg's avatar aniket-engg Committed by Aniket

all modules in ts, tests running partially

parent 5029935a
...@@ -25,11 +25,12 @@ ...@@ -25,11 +25,12 @@
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"lint": "standard", "lint": "standard",
"test": "npm run lint && tape ./test/tests.js" "test": "tsc && npm run lint && tape ./test/tests.js"
}, },
"standard": { "standard": {
"ignore": [ "ignore": [
"node_modules/*" "node_modules/*",
"dist/"
], ],
"parser": "babel-eslint" "parser": "babel-eslint"
}, },
......
...@@ -7,7 +7,7 @@ export class staticAnalysisRunner { ...@@ -7,7 +7,7 @@ export class staticAnalysisRunner {
run (compilationResult, toRun, callback) { run (compilationResult, toRun, callback) {
const modules = toRun.map((i) => { const modules = toRun.map((i) => {
const m = this.modules()[i] const m = this.modules()[i]
return { 'name': m.name, 'mod': new m.Module() } return { 'name': m.name, 'mod': m }
}) })
this.runWithModuleList(compilationResult, modules, callback) this.runWithModuleList(compilationResult, modules, callback)
......
import common from './staticAnalysisCommon' import { isContractDefinition, getStateVariableDeclarationsFormContractNode, isInheritanceSpecifier,
getInheritsFromName, isFunctionDefinition, isModifierDefinition, isModifierInvocation, getContractName,
getFunctionOrModifierDefinitionParameterPart, getType, getDeclaredVariableName, isVariableDeclaration,
getFunctionOrModifierDefinitionReturnParameterPart } from './staticAnalysisCommon'
import { AstWalker } from 'remix-astwalker' import { AstWalker } from 'remix-astwalker'
export class abstractAstView { export default class abstractAstView {
contracts = [] contracts = []
currentContractIndex = null currentContractIndex = null
currentFunctionIndex = null currentFunctionIndex = null
...@@ -46,20 +49,20 @@ export class abstractAstView { ...@@ -46,20 +49,20 @@ export class abstractAstView {
build_visit (relevantNodeFilter) { build_visit (relevantNodeFilter) {
var that = this var that = this
return function (node) { return function (node) {
if (common.isContractDefinition(node)) { if (isContractDefinition(node)) {
that.setCurrentContract(that, { that.setCurrentContract(that, {
node: node, node: node,
functions: [], functions: [],
relevantNodes: [], relevantNodes: [],
modifiers: [], modifiers: [],
inheritsFrom: [], inheritsFrom: [],
stateVariables: common.getStateVariableDeclarationsFormContractNode(node) stateVariables: getStateVariableDeclarationsFormContractNode(node)
}) })
} else if (common.isInheritanceSpecifier(node)) { } else if (isInheritanceSpecifier(node)) {
const currentContract = that.getCurrentContract(that) const currentContract = that.getCurrentContract(that)
const inheritsFromName = common.getInheritsFromName(node) const inheritsFromName = getInheritsFromName(node)
currentContract.inheritsFrom.push(inheritsFromName) currentContract.inheritsFrom.push(inheritsFromName)
} else if (common.isFunctionDefinition(node)) { } else if (isFunctionDefinition(node)) {
that.setCurrentFunction(that, { that.setCurrentFunction(that, {
node: node, node: node,
relevantNodes: [], relevantNodes: [],
...@@ -74,14 +77,14 @@ export class abstractAstView { ...@@ -74,14 +77,14 @@ export class abstractAstView {
that.getCurrentFunction(that).relevantNodes.push(item.node) that.getCurrentFunction(that).relevantNodes.push(item.node)
} }
}) })
} else if (common.isModifierDefinition(node)) { } else if (isModifierDefinition(node)) {
that.setCurrentModifier(that, { that.setCurrentModifier(that, {
node: node, node: node,
relevantNodes: [], relevantNodes: [],
localVariables: that.getLocalVariables(node), localVariables: that.getLocalVariables(node),
parameters: that.getLocalParameters(node) parameters: that.getLocalParameters(node)
}) })
} else if (common.isModifierInvocation(node)) { } else if (isModifierInvocation(node)) {
if (!that.isFunctionNotModifier) throw new Error('abstractAstView.js: Found modifier invocation outside of function scope.') if (!that.isFunctionNotModifier) throw new Error('abstractAstView.js: Found modifier invocation outside of function scope.')
that.getCurrentFunction(that).modifierInvocations.push(node) that.getCurrentFunction(that).modifierInvocations.push(node)
} else if (relevantNodeFilter(node)) { } else if (relevantNodeFilter(node)) {
...@@ -115,7 +118,7 @@ export class abstractAstView { ...@@ -115,7 +118,7 @@ export class abstractAstView {
private resolveStateVariablesInHierarchyForContract (currentContract, contracts) { private resolveStateVariablesInHierarchyForContract (currentContract, contracts) {
currentContract.inheritsFrom.map((inheritsFromName) => { currentContract.inheritsFrom.map((inheritsFromName) => {
// add variables from inherited contracts // add variables from inherited contracts
const inheritsFrom = contracts.find((contract) => common.getContractName(contract.node) === inheritsFromName) const inheritsFrom = contracts.find((contract) => getContractName(contract.node) === inheritsFromName)
if (inheritsFrom) { if (inheritsFrom) {
currentContract.stateVariables = currentContract.stateVariables.concat(inheritsFrom.stateVariables) currentContract.stateVariables = currentContract.stateVariables.concat(inheritsFrom.stateVariables)
} else { } else {
...@@ -125,8 +128,8 @@ export class abstractAstView { ...@@ -125,8 +128,8 @@ export class abstractAstView {
} }
private setCurrentContract (that, contract) { private setCurrentContract (that, contract) {
const name = common.getContractName(contract.node) const name = getContractName(contract.node)
if (that.contracts.map((c) => common.getContractName(c.node)).filter((n) => n === name).length > 0) { if (that.contracts.map((c) => getContractName(c.node)).filter((n) => n === name).length > 0) {
console.log('abstractAstView.js: two or more contracts with the same name dectected, import aliases not supported at the moment') console.log('abstractAstView.js: two or more contracts with the same name dectected, import aliases not supported at the moment')
that.multipleContractsWithSameName = true that.multipleContractsWithSameName = true
} }
...@@ -156,14 +159,14 @@ export class abstractAstView { ...@@ -156,14 +159,14 @@ export class abstractAstView {
} }
private getLocalParameters (funcNode) { private getLocalParameters (funcNode) {
return this.getLocalVariables(common.getFunctionOrModifierDefinitionParameterPart(funcNode)).map(common.getType) return this.getLocalVariables(getFunctionOrModifierDefinitionParameterPart(funcNode)).map(getType)
} }
private getReturnParameters (funcNode) { private getReturnParameters (funcNode) {
return this.getLocalVariables(common.getFunctionOrModifierDefinitionReturnParameterPart(funcNode)).map((n) => { return this.getLocalVariables(getFunctionOrModifierDefinitionReturnParameterPart(funcNode)).map((n) => {
return { return {
type: common.getType(n), type: getType(n),
name: common.getDeclaredVariableName(n) name: getDeclaredVariableName(n)
} }
}) })
} }
...@@ -171,7 +174,7 @@ export class abstractAstView { ...@@ -171,7 +174,7 @@ export class abstractAstView {
private getLocalVariables (funcNode) { private getLocalVariables (funcNode) {
const locals: any[] = [] const locals: any[] = []
new AstWalker().walk(funcNode, {'*': function (node) { new AstWalker().walk(funcNode, {'*': function (node) {
if (common.isVariableDeclaration(node)) locals.push(node) if (isVariableDeclaration(node)) locals.push(node)
return true return true
}}) }})
return locals return locals
......
...@@ -2,7 +2,7 @@ import { default as category } from './categories' ...@@ -2,7 +2,7 @@ import { default as category } from './categories'
import { isSubScopeWithTopLevelUnAssignedBinOp, getUnAssignedTopLevelBinOps } from './staticAnalysisCommon' import { isSubScopeWithTopLevelUnAssignedBinOp, getUnAssignedTopLevelBinOps } from './staticAnalysisCommon'
import { default as algorithm } from './algorithmCategories' import { default as algorithm } from './algorithmCategories'
export class assignAndCompare { export default class assignAndCompare {
warningNodes: any[] = [] warningNodes: any[] = []
name = 'Result not used: ' name = 'Result not used: '
description = 'The result of an operation was not used.' description = 'The result of an operation was not used.'
......
...@@ -2,7 +2,7 @@ import { default as category } from './categories' ...@@ -2,7 +2,7 @@ import { default as category } from './categories'
import { isBlockBlockHashAccess } from './staticAnalysisCommon' import { isBlockBlockHashAccess } from './staticAnalysisCommon'
import { default as algorithm } from './algorithmCategories' import { default as algorithm } from './algorithmCategories'
export class blockBlockhash { export default class blockBlockhash {
warningNodes: any[] = [] warningNodes: any[] = []
name = 'Block.blockhash usage: ' name = 'Block.blockhash usage: '
desc = 'Semantics maybe unclear' desc = 'Semantics maybe unclear'
......
...@@ -2,7 +2,7 @@ import { default as category } from './categories' ...@@ -2,7 +2,7 @@ import { default as category } from './categories'
import { isNowAccess, isBlockTimestampAccess } from './staticAnalysisCommon' import { isNowAccess, isBlockTimestampAccess } from './staticAnalysisCommon'
import { default as algorithm } from './algorithmCategories' import { default as algorithm } from './algorithmCategories'
export class blockTimestamp { export default class blockTimestamp {
warningNowNodes: any[] = [] warningNowNodes: any[] = []
warningblockTimestampNodes: any[] = [] warningblockTimestampNodes: any[] = []
name = 'Block timestamp: ' name = 'Block timestamp: '
......
const name = 'Check effects: '
const desc = 'Avoid potential reentrancy bugs'
const categories = require('./categories')
const common = require('./staticAnalysisCommon')
const fcallGraph = require('./functionCallGraph')
const AbstractAst = require('./abstractAstView')
const algo = require('./algorithmCategories')
function checksEffectsInteraction () {
this.abstractAst = new AbstractAst()
this.visit = this.abstractAst.build_visit(
(node) => common.isInteraction(node) || common.isEffect(node) || common.isLocalCallGraphRelevantNode(node)
)
this.report = this.abstractAst.build_report(report)
}
checksEffectsInteraction.prototype.visit = function () { throw new Error('checksEffectsInteraction.js no visit function set upon construction') }
checksEffectsInteraction.prototype.report = function () { throw new Error('checksEffectsInteraction.js no report function set upon construction') }
function report (contracts, multipleContractsWithSameName) {
const warnings = []
const hasModifiers = contracts.some((item) => item.modifiers.length > 0)
const callGraph = fcallGraph.buildGlobalFuncCallGraph(contracts)
contracts.forEach((contract) => {
contract.functions.forEach((func) => {
func.changesState = checkIfChangesState(common.getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters),
getContext(callGraph, contract, func))
})
contract.functions.forEach((func) => {
if (isPotentialVulnerableFunction(func, getContext(callGraph, contract, func))) {
const funcName = common.getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters)
let comments = (hasModifiers) ? 'Note: Modifiers are currently not considered by this static analysis.' : ''
comments += (multipleContractsWithSameName) ? 'Note: Import aliases are currently not supported by this static analysis.' : ''
warnings.push({
warning: `Potential Violation of Checks-Effects-Interaction pattern in ${funcName}: Could potentially lead to re-entrancy vulnerability. ${comments}`,
location: func.src,
more: 'http://solidity.readthedocs.io/en/develop/security-considerations.html#re-entrancy'
})
}
})
})
return warnings
}
function getContext (callGraph, currentContract, func) {
return { callGraph: callGraph, currentContract: currentContract, stateVariables: getStateVariables(currentContract, func) }
}
function getStateVariables (contract, func) {
return contract.stateVariables.concat(func.localVariables.filter(common.isStorageVariableDeclaration))
}
function isPotentialVulnerableFunction (func, context) {
let isPotentialVulnerable = false
let interaction = false
func.relevantNodes.forEach((node) => {
if (common.isInteraction(node)) {
interaction = true
} else if (interaction && (common.isWriteOnStateVariable(node, context.stateVariables) || isLocalCallWithStateChange(node, context))) {
isPotentialVulnerable = true
}
})
return isPotentialVulnerable
}
function isLocalCallWithStateChange (node, context) {
if (common.isLocalCallGraphRelevantNode(node)) {
const func = fcallGraph.resolveCallGraphSymbol(context.callGraph, common.getFullQualifiedFunctionCallIdent(context.currentContract.node, node))
return !func || (func && func.node.changesState)
}
return false
}
function checkIfChangesState (startFuncName, context) {
return fcallGraph.analyseCallGraph(context.callGraph, startFuncName, context, (node, context) => common.isWriteOnStateVariable(node, context.stateVariables))
}
module.exports = {
name: name,
description: desc,
category: categories.SECURITY,
algorithm: algo.HEURISTIC,
Module: checksEffectsInteraction
}
import { default as category } from './categories'
import { isInteraction, isEffect, isLocalCallGraphRelevantNode, getFullQuallyfiedFuncDefinitionIdent,
isWriteOnStateVariable, isStorageVariableDeclaration, getFullQualifiedFunctionCallIdent } from './staticAnalysisCommon'
import { default as algorithm } from './algorithmCategories'
import { buildGlobalFuncCallGraph, resolveCallGraphSymbol, analyseCallGraph } from './functionCallGraph'
import AbstractAst from './abstractAstView'
export default class checksEffectsInteraction {
name = 'Check effects: '
desc = 'Avoid potential reentrancy bugs'
categories = category.SECURITY
algorithm = algorithm.HEURISTIC
Module = this
abstractAst = new AbstractAst()
visit = this.abstractAst.build_visit((node) => isInteraction(node) || isEffect(node) || isLocalCallGraphRelevantNode(node))
report = this.abstractAst.build_report(this._report)
private _report (contracts, multipleContractsWithSameName) {
const warnings: any = []
const hasModifiers = contracts.some((item) => item.modifiers.length > 0)
const callGraph = buildGlobalFuncCallGraph(contracts)
contracts.forEach((contract) => {
contract.functions.forEach((func) => {
func.changesState = this.checkIfChangesState(
getFullQuallyfiedFuncDefinitionIdent(
contract.node,
func.node,
func.parameters
),
this.getContext(
callGraph,
contract,
func)
)
})
contract.functions.forEach((func) => {
if (this.isPotentialVulnerableFunction(func, this.getContext(callGraph, contract, func))) {
const funcName = getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters)
let comments = (hasModifiers) ? 'Note: Modifiers are currently not considered by this static analysis.' : ''
comments += (multipleContractsWithSameName) ? 'Note: Import aliases are currently not supported by this static analysis.' : ''
warnings.push({
warning: `Potential Violation of Checks-Effects-Interaction pattern in ${funcName}: Could potentially lead to re-entrancy vulnerability. ${comments}`,
location: func.src,
more: 'http://solidity.readthedocs.io/en/develop/security-considerations.html#re-entrancy'
})
}
})
})
return warnings
}
private getContext (callGraph, currentContract, func) {
return { callGraph: callGraph, currentContract: currentContract, stateVariables: this.getStateVariables(currentContract, func) }
}
private getStateVariables (contract, func) {
return contract.stateVariables.concat(func.localVariables.filter(isStorageVariableDeclaration))
}
private isPotentialVulnerableFunction (func, context) {
let isPotentialVulnerable = false
let interaction = false
func.relevantNodes.forEach((node) => {
if (isInteraction(node)) {
interaction = true
} else if (interaction && (isWriteOnStateVariable(node, context.stateVariables) || this.isLocalCallWithStateChange(node, context))) {
isPotentialVulnerable = true
}
})
return isPotentialVulnerable
}
private isLocalCallWithStateChange (node, context) {
if (isLocalCallGraphRelevantNode(node)) {
const func = resolveCallGraphSymbol(context.callGraph, getFullQualifiedFunctionCallIdent(context.currentContract.node, node))
return !func || (func && func.node.changesState)
}
return false
}
private checkIfChangesState (startFuncName, context) {
return analyseCallGraph(context.callGraph, startFuncName, context, (node, context) => isWriteOnStateVariable(node, context.stateVariables))
}
}
const name = 'Constant functions: '
const desc = 'Check for potentially constant functions'
const categories = require('./categories')
const common = require('./staticAnalysisCommon')
const fcallGraph = require('./functionCallGraph')
const AbstractAst = require('./abstractAstView')
const algo = require('./algorithmCategories')
function constantFunctions () {
this.abstractAst = new AbstractAst()
this.visit = this.abstractAst.build_visit(
(node) => common.isLowLevelCall(node) ||
common.isTransfer(node) ||
common.isExternalDirectCall(node) ||
common.isEffect(node) ||
common.isLocalCallGraphRelevantNode(node) ||
common.isInlineAssembly(node) ||
common.isNewExpression(node) ||
common.isSelfdestructCall(node) ||
common.isDeleteUnaryOperation(node)
)
this.report = this.abstractAst.build_report(report)
}
constantFunctions.prototype.visit = function () { throw new Error('constantFunctions.js no visit function set upon construction') }
constantFunctions.prototype.report = function () { throw new Error('constantFunctions.js no report function set upon construction') }
function report (contracts, multipleContractsWithSameName) {
const warnings = []
const hasModifiers = contracts.some((item) => item.modifiers.length > 0)
const callGraph = fcallGraph.buildGlobalFuncCallGraph(contracts)
contracts.forEach((contract) => {
contract.functions.forEach((func) => {
if (common.isPayableFunction(func.node) || common.isConstructor(func.node)) {
func.potentiallyshouldBeConst = false
} else {
func.potentiallyshouldBeConst = checkIfShouldBeConstant(common.getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters),
getContext(callGraph, contract, func))
}
})
contract.functions.filter((func) => common.hasFunctionBody(func.node)).forEach((func) => {
if (common.isConstantFunction(func.node) !== func.potentiallyshouldBeConst) {
const funcName = common.getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters)
let comments = (hasModifiers) ? 'Note: Modifiers are currently not considered by this static analysis.' : ''
comments += (multipleContractsWithSameName) ? 'Note: Import aliases are currently not supported by this static analysis.' : ''
if (func.potentiallyshouldBeConst) {
warnings.push({
warning: `${funcName} : Potentially should be constant but is not. ${comments}`,
location: func.src,
more: 'http://solidity.readthedocs.io/en/develop/contracts.html#constant-functions'
})
} else {
warnings.push({
warning: `${funcName} : Is constant but potentially should not be. ${comments}`,
location: func.src,
more: 'http://solidity.readthedocs.io/en/develop/contracts.html#constant-functions'
})
}
}
})
})
return warnings
}
function getContext (callGraph, currentContract, func) {
return { callGraph: callGraph, currentContract: currentContract, stateVariables: getStateVariables(currentContract, func) }
}
function getStateVariables (contract, func) {
return contract.stateVariables.concat(func.localVariables.filter(common.isStorageVariableDeclaration))
}
function checkIfShouldBeConstant (startFuncName, context) {
return !fcallGraph.analyseCallGraph(context.callGraph, startFuncName, context, isConstBreaker)
}
function isConstBreaker (node, context) {
return common.isWriteOnStateVariable(node, context.stateVariables) ||
common.isLowLevelCall(node) ||
common.isTransfer(node) ||
isCallOnNonConstExternalInterfaceFunction(node, context) ||
common.isCallToNonConstLocalFunction(node) ||
common.isInlineAssembly(node) ||
common.isNewExpression(node) ||
common.isSelfdestructCall(node) ||
common.isDeleteUnaryOperation(node)
}
function isCallOnNonConstExternalInterfaceFunction (node, context) {
if (common.isExternalDirectCall(node)) {
const func = fcallGraph.resolveCallGraphSymbol(context.callGraph, common.getFullQualifiedFunctionCallIdent(context.currentContract, node))
return !func || (func && !common.isConstantFunction(func.node.node))
}
return false
}
module.exports = {
name: name,
description: desc,
category: categories.MISC,
algorithm: algo.HEURISTIC,
Module: constantFunctions
}
import { default as category } from './categories'
import { isLowLevelCall, isTransfer, isExternalDirectCall, isEffect, isLocalCallGraphRelevantNode,
isInlineAssembly, isNewExpression, isSelfdestructCall, isDeleteUnaryOperation, isPayableFunction,
isConstructor, getFullQuallyfiedFuncDefinitionIdent, hasFunctionBody, isConstantFunction, isWriteOnStateVariable,
isStorageVariableDeclaration, isCallToNonConstLocalFunction, getFullQualifiedFunctionCallIdent} from './staticAnalysisCommon'
import { default as algorithm } from './algorithmCategories'
import { buildGlobalFuncCallGraph, resolveCallGraphSymbol, analyseCallGraph } from './functionCallGraph'
import AbstractAst from './abstractAstView'
export default class constantFunctions {
name = 'Constant functions: '
desc = 'Check for potentially constant functions'
categories = category.MISC
algorithm = algorithm.HEURISTIC
Module = this
abstractAst = new AbstractAst()
visit = this.abstractAst.build_visit(
(node) => isLowLevelCall(node) ||
isTransfer(node) ||
isExternalDirectCall(node) ||
isEffect(node) ||
isLocalCallGraphRelevantNode(node) ||
isInlineAssembly(node) ||
isNewExpression(node) ||
isSelfdestructCall(node) ||
isDeleteUnaryOperation(node)
)
report = this.abstractAst.build_report(this._report)
private _report (contracts, multipleContractsWithSameName) {
const warnings: any = []
const hasModifiers = contracts.some((item) => item.modifiers.length > 0)
const callGraph = buildGlobalFuncCallGraph(contracts)
contracts.forEach((contract) => {
contract.functions.forEach((func) => {
if (isPayableFunction(func.node) || isConstructor(func.node)) {
func.potentiallyshouldBeConst = false
} else {
func.potentiallyshouldBeConst = this.checkIfShouldBeConstant(
getFullQuallyfiedFuncDefinitionIdent(
contract.node,
func.node,
func.parameters
),
this.getContext(
callGraph,
contract,
func
)
)
}
})
contract.functions.filter((func) => hasFunctionBody(func.node)).forEach((func) => {
if (isConstantFunction(func.node) !== func.potentiallyshouldBeConst) {
const funcName = getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters)
let comments = (hasModifiers) ? 'Note: Modifiers are currently not considered by this static analysis.' : ''
comments += (multipleContractsWithSameName) ? 'Note: Import aliases are currently not supported by this static analysis.' : ''
if (func.potentiallyshouldBeConst) {
warnings.push({
warning: `${funcName} : Potentially should be constant but is not. ${comments}`,
location: func.src,
more: 'http://solidity.readthedocs.io/en/develop/contracts.html#constant-functions'
})
} else {
warnings.push({
warning: `${funcName} : Is constant but potentially should not be. ${comments}`,
location: func.src,
more: 'http://solidity.readthedocs.io/en/develop/contracts.html#constant-functions'
})
}
}
})
})
return warnings
}
private getContext (callGraph, currentContract, func) {
return { callGraph: callGraph, currentContract: currentContract, stateVariables: this.getStateVariables(currentContract, func) }
}
private getStateVariables (contract, func) {
return contract.stateVariables.concat(func.localVariables.filter(isStorageVariableDeclaration))
}
private checkIfShouldBeConstant (startFuncName, context) {
return !analyseCallGraph(context.callGraph, startFuncName, context, this.isConstBreaker)
}
private isConstBreaker (node, context) {
return isWriteOnStateVariable(node, context.stateVariables) ||
isLowLevelCall(node) ||
isTransfer(node) ||
this.isCallOnNonConstExternalInterfaceFunction(node, context) ||
isCallToNonConstLocalFunction(node) ||
isInlineAssembly(node) ||
isNewExpression(node) ||
isSelfdestructCall(node) ||
isDeleteUnaryOperation(node)
}
private isCallOnNonConstExternalInterfaceFunction (node, context) {
if (isExternalDirectCall(node)) {
const func = resolveCallGraphSymbol(context.callGraph, getFullQualifiedFunctionCallIdent(context.currentContract, node))
return !func || (func && !isConstantFunction(func.node.node))
}
return false
}
}
...@@ -2,7 +2,7 @@ import { default as category } from './categories' ...@@ -2,7 +2,7 @@ import { default as category } from './categories'
import { isDeleteOfDynamicArray } from './staticAnalysisCommon' import { isDeleteOfDynamicArray } from './staticAnalysisCommon'
import { default as algorithm } from './algorithmCategories' import { default as algorithm } from './algorithmCategories'
export class deleteDynamicArrays { export default class deleteDynamicArrays {
rel: any = [] rel: any = []
name = 'Delete on dynamic Array: ' name = 'Delete on dynamic Array: '
desc = 'Use require and appropriately' desc = 'Use require and appropriately'
......
import { default as category } from './categories' import { default as category } from './categories'
import { isDeleteFromDynamicArray, isMappingIndexAccess } from './staticAnalysisCommon' import { isDeleteFromDynamicArray, isMappingIndexAccess } from './staticAnalysisCommon'
export class deleteFromDynamicArray { export default class deleteFromDynamicArray {
relevantNodes: any[] = [] relevantNodes: any[] = []
name = 'Delete from dynamic Array: ' name = 'Delete from dynamic Array: '
desc = 'Using delete on an array leaves a gap' desc = 'Using delete on an array leaves a gap'
......
const name = 'ERC20: '
const desc = 'Decimal should be uint8'
const categories = require('./categories')
const common = require('./staticAnalysisCommon')
const AbstractAst = require('./abstractAstView')
const algo = require('./algorithmCategories')
function erc20Decimals () {
this.abstractAst = new AbstractAst()
this.visit = this.abstractAst.build_visit(
(node) => false
)
this.report = this.abstractAst.build_report(report)
}
erc20Decimals.prototype.visit = function () { throw new Error('erc20Decimals.js no visit function set upon construction') }
erc20Decimals.prototype.report = function () { throw new Error('erc20Decimals.js no report function set upon construction') }
function report (contracts, multipleContractsWithSameName) {
const warnings = []
contracts.forEach((contract) => {
const contractAbiSignatures = contract.functions.map((f) => common.helpers.buildAbiSignature(common.getFunctionDefinitionName(f.node), f.parameters))
if (isERC20(contractAbiSignatures)) {
const decimalsVar = contract.stateVariables.filter((stateVar) => common.getDeclaredVariableName(stateVar) === 'decimals' && (common.getDeclaredVariableType(stateVar) !== 'uint8' || stateVar.attributes.visibility !== 'public'))
const decimalsFun = contract.functions.filter((f) => common.getFunctionDefinitionName(f.node) === 'decimals' &&
(
(f.returns.length === 0 || f.returns.length > 1) ||
(f.returns.length === 1 && (f.returns[0].type !== 'uint8' || f.node.attributes.visibility !== 'public'))
)
)
if (decimalsVar.length > 0 || decimalsFun.length > 0) {
warnings.push({
warning: 'ERC20 Contracts decimals function should have uint8 as return type',
location: null,
more: ' https://eips.ethereum.org/EIPS/eip-20'
})
}
}
})
return warnings
}
function isERC20 (funSignatures) {
return funSignatures.includes('totalSupply()') &&
funSignatures.includes('balanceOf(address)') &&
funSignatures.includes('transfer(address,uint256)') &&
funSignatures.includes('transferFrom(address,address,uint256)') &&
funSignatures.includes('approve(address,uint256)') &&
funSignatures.includes('allowance(address,address)')
}
module.exports = {
name: name,
description: desc,
category: categories.ERC,
algorithm: algo.EXACT,
Module: erc20Decimals
}
import { default as category } from './categories'
import { getFunctionDefinitionName, helpers, getDeclaredVariableName, getDeclaredVariableType } from './staticAnalysisCommon'
import { default as algorithm } from './algorithmCategories'
import AbstractAst from './abstractAstView'
export default class erc20Decimals {
name = 'ERC20: '
desc = 'Decimal should be uint8'
categories = category.ERC
algorithm = algorithm.EXACT
Module = this
abstractAst = new AbstractAst()
visit = this.abstractAst.build_visit((node) => false)
report = this.abstractAst.build_report(this._report)
private _report (contracts, multipleContractsWithSameName) {
const warnings: any = []
contracts.forEach((contract) => {
const contractAbiSignatures = contract.functions.map((f) => helpers.buildAbiSignature(getFunctionDefinitionName(f.node), f.parameters))
if (this.isERC20(contractAbiSignatures)) {
const decimalsVar = contract.stateVariables.filter((stateVar) => getDeclaredVariableName(stateVar) === 'decimals' && (getDeclaredVariableType(stateVar) !== 'uint8' || stateVar.attributes.visibility !== 'public'))
const decimalsFun = contract.functions.filter((f) => getFunctionDefinitionName(f.node) === 'decimals' &&
(
(f.returns.length === 0 || f.returns.length > 1) ||
(f.returns.length === 1 && (f.returns[0].type !== 'uint8' || f.node.attributes.visibility !== 'public'))
)
)
if (decimalsVar.length > 0 || decimalsFun.length > 0) {
warnings.push({
warning: 'ERC20 Contracts decimals function should have uint8 as return type',
location: null,
more: ' https://eips.ethereum.org/EIPS/eip-20'
})
}
}
})
return warnings
}
private isERC20 (funSignatures) {
return funSignatures.includes('totalSupply()') &&
funSignatures.includes('balanceOf(address)') &&
funSignatures.includes('transfer(address,uint256)') &&
funSignatures.includes('transferFrom(address,address,uint256)') &&
funSignatures.includes('approve(address,uint256)') &&
funSignatures.includes('allowance(address,address)')
}
}
import { default as category } from './categories' import { default as category } from './categories'
import { isLoop, isBlock, getLoopBlockStartIndex, isExpressionStatement, isTransfer } from './staticAnalysisCommon' import { isLoop, isBlock, getLoopBlockStartIndex, isExpressionStatement, isTransfer } from './staticAnalysisCommon'
export class etherTransferInLoop { export default class etherTransferInLoop {
relevantNodes: any[] = [] relevantNodes: any[] = []
name = 'Ether transfer in a loop: ' name = 'Ether transfer in a loop: '
desc = 'Avoid transferring Ether to multiple addresses in a loop' desc = 'Avoid transferring Ether to multiple addresses in a loop'
...@@ -12,7 +12,7 @@ export class etherTransferInLoop { ...@@ -12,7 +12,7 @@ export class etherTransferInLoop {
if (isLoop(node)) { if (isLoop(node)) {
let transferNodes = [] let transferNodes = []
const loopBlockStartIndex = getLoopBlockStartIndex(node) const loopBlockStartIndex = getLoopBlockStartIndex(node)
if (isBlock(node.children[loopBlockStartIndex])) { if (loopBlockStartIndex && isBlock(node.children[loopBlockStartIndex])) {
transferNodes = node.children[loopBlockStartIndex].children transferNodes = node.children[loopBlockStartIndex].children
.filter(child => (isExpressionStatement(child) && .filter(child => (isExpressionStatement(child) &&
child.children[0].name === 'FunctionCall' && child.children[0].name === 'FunctionCall' &&
......
import { default as category } from './categories' import { default as category } from './categories'
const { isForLoop, isDynamicArrayLengthAccess, isBinaryOperation } = require('./staticAnalysisCommon') const { isForLoop, isDynamicArrayLengthAccess, isBinaryOperation } = require('./staticAnalysisCommon')
export class forLoopIteratesOverDynamicArray { export default class forLoopIteratesOverDynamicArray {
relevantNodes: any[] = [] relevantNodes: any[] = []
name = 'For loop iterates over dynamic array: ' name = 'For loop iterates over dynamic array: '
desc = 'The number of \'for\' loop iterations depends on dynamic array\'s size' desc = 'The number of \'for\' loop iterations depends on dynamic array\'s size'
......
...@@ -12,7 +12,6 @@ function buildLocalFuncCallGraphInternal (functions, nodeFilter, extractNodeIden ...@@ -12,7 +12,6 @@ function buildLocalFuncCallGraphInternal (functions, nodeFilter, extractNodeIden
callGraph[extractFuncDefIdent(func)] = { node: func, calls: calls } callGraph[extractFuncDefIdent(func)] = { node: func, calls: calls }
}) })
return callGraph return callGraph
} }
...@@ -40,7 +39,7 @@ function buildLocalFuncCallGraphInternal (functions, nodeFilter, extractNodeIden ...@@ -40,7 +39,7 @@ function buildLocalFuncCallGraphInternal (functions, nodeFilter, extractNodeIden
* @contracts {list contracts} Expects as input the contract structure defined in abstractAstView.js * @contracts {list contracts} Expects as input the contract structure defined in abstractAstView.js
* @return {map (string -> Contract Call Graph)} returns map from contract name to contract call graph * @return {map (string -> Contract Call Graph)} returns map from contract name to contract call graph
*/ */
function buildGlobalFuncCallGraph (contracts) { export function buildGlobalFuncCallGraph (contracts) {
const callGraph = {} const callGraph = {}
contracts.forEach((contract) => { contracts.forEach((contract) => {
const filterNodes = (node) => { return common.isLocalCallGraphRelevantNode(node) || common.isExternalDirectCall(node) } const filterNodes = (node) => { return common.isLocalCallGraphRelevantNode(node) || common.isExternalDirectCall(node) }
...@@ -61,7 +60,7 @@ function buildGlobalFuncCallGraph (contracts) { ...@@ -61,7 +60,7 @@ function buildGlobalFuncCallGraph (contracts) {
* @nodeCheck {(ASTNode, context) -> bool} applied on every relevant node in the call graph * @nodeCheck {(ASTNode, context) -> bool} applied on every relevant node in the call graph
* @return {bool} returns map from contract name to contract call graph * @return {bool} returns map from contract name to contract call graph
*/ */
function analyseCallGraph (callGraph, funcName, context, nodeCheck) { export function analyseCallGraph (callGraph, funcName, context, nodeCheck) {
return analyseCallGraphInternal(callGraph, funcName, context, (a, b) => a || b, nodeCheck, {}) return analyseCallGraphInternal(callGraph, funcName, context, (a, b) => a || b, nodeCheck, {})
} }
...@@ -75,7 +74,7 @@ function analyseCallGraphInternal (callGraph, funcName, context, combinator, nod ...@@ -75,7 +74,7 @@ function analyseCallGraphInternal (callGraph, funcName, context, combinator, nod
current.calls.reduce((acc, val) => combinator(acc, analyseCallGraphInternal(callGraph, val, context, combinator, nodeCheck, visited)), false)) current.calls.reduce((acc, val) => combinator(acc, analyseCallGraphInternal(callGraph, val, context, combinator, nodeCheck, visited)), false))
} }
function resolveCallGraphSymbol (callGraph, funcName) { export function resolveCallGraphSymbol (callGraph, funcName) {
return resolveCallGraphSymbolInternal(callGraph, funcName, false) return resolveCallGraphSymbolInternal(callGraph, funcName, false)
} }
...@@ -106,9 +105,3 @@ function resolveCallGraphSymbolInternal (callGraph, funcName, silent) { ...@@ -106,9 +105,3 @@ function resolveCallGraphSymbolInternal (callGraph, funcName, silent) {
if (current === undefined && !silent) console.log(`static analysis functionCallGraph.js: ${funcName} not found in function call graph.`) if (current === undefined && !silent) console.log(`static analysis functionCallGraph.js: ${funcName} not found in function call graph.`)
return current return current
} }
module.exports = {
analyseCallGraph: analyseCallGraph,
buildGlobalFuncCallGraph: buildGlobalFuncCallGraph,
resolveCallGraphSymbol: resolveCallGraphSymbol
}
const name = 'Gas costs: ' import { default as category } from './categories'
const desc = 'Warn if the gas requirements of functions are too high.' import { default as algorithm } from './algorithmCategories'
const categories = require('./categories')
const algo = require('./algorithmCategories') export default class gasCosts {
name = 'Gas costs: '
desc = 'Warn if the gas requirements of functions are too high.'
categories = category.GAS
algorithm = algorithm.EXACT
Module = this
function gasCosts () {
}
/** /**
* call the given @arg cb (function) for all the contracts. Uses last compilation result * call the given @arg cb (function) for all the contracts. Uses last compilation result
...@@ -12,56 +15,49 @@ function gasCosts () { ...@@ -12,56 +15,49 @@ function gasCosts () {
* @param {Function} cb - callback * @param {Function} cb - callback
*/ */
// @TODO has been copied from remix-ide repo ! should fix that soon ! // @TODO has been copied from remix-ide repo ! should fix that soon !
function visitContracts (contracts, cb) { visitContracts (contracts, cb) {
for (let file in contracts) { for (let file in contracts) {
for (let name in contracts[file]) { for (let name in contracts[file]) {
if (cb({ name: name, object: contracts[file][name], file: file })) return if (cb({ name: name, object: contracts[file][name], file: file })) return
}
}
}
gasCosts.prototype.report = function (compilationResults) {
const report = []
visitContracts(compilationResults.contracts, (contract) => {
if (
!contract.object.evm.gasEstimates ||
!contract.object.evm.gasEstimates.external
) {
return
}
const fallback = contract.object.evm.gasEstimates.external['']
if (fallback !== undefined) {
if (fallback === null || fallback >= 2100 || fallback === 'infinite') {
report.push({
warning: `Fallback function of contract ${contract.name} requires too much gas (${fallback}).
If the fallback function requires more than 2300 gas, the contract cannot receive Ether.`
})
} }
} }
}
for (var functionName in contract.object.evm.gasEstimates.external) { report (compilationResults) {
if (functionName === '') { const report: any[] = []
continue this.visitContracts(compilationResults.contracts, (contract) => {
if (
!contract.object.evm.gasEstimates ||
!contract.object.evm.gasEstimates.external
) {
return
} }
const gas = contract.object.evm.gasEstimates.external[functionName] const fallback = contract.object.evm.gasEstimates.external['']
const gasString = gas === null ? 'unknown or not constant' : 'high: ' + gas if (fallback !== undefined) {
if (gas === null || gas >= 3000000 || gas === 'infinite') { if (fallback === null || fallback >= 2100 || fallback === 'infinite') {
report.push({ report.push({
warning: `Gas requirement of function ${contract.name}.${functionName} ${gasString}. warning: `Fallback function of contract ${contract.name} requires too much gas (${fallback}).
If the gas requirement of a function is higher than the block gas limit, it cannot be executed. If the fallback function requires more than 2300 gas, the contract cannot receive Ether.`
Please avoid loops in your functions or actions that modify large areas of storage })
(this includes clearing or copying arrays in storage)` }
})
} }
}
})
return report
}
module.exports = { for (var functionName in contract.object.evm.gasEstimates.external) {
name: name, if (functionName === '') {
description: desc, continue
category: categories.GAS, }
algorithm: algo.EXACT, const gas = contract.object.evm.gasEstimates.external[functionName]
Module: gasCosts const gasString = gas === null ? 'unknown or not constant' : 'high: ' + gas
if (gas === null || gas >= 3000000 || gas === 'infinite') {
report.push({
warning: `Gas requirement of function ${contract.name}.${functionName} ${gasString}.
If the gas requirement of a function is higher than the block gas limit, it cannot be executed.
Please avoid loops in your functions or actions that modify large areas of storage
(this includes clearing or copying arrays in storage)`
})
}
}
})
return report
}
} }
...@@ -2,7 +2,7 @@ import { default as category } from './categories' ...@@ -2,7 +2,7 @@ import { default as category } from './categories'
import { isRequireCall, isAssertCall } from './staticAnalysisCommon' import { isRequireCall, isAssertCall } from './staticAnalysisCommon'
import { default as algorithm } from './algorithmCategories' import { default as algorithm } from './algorithmCategories'
export class guardConditions { export default class guardConditions {
guards: any[] = [] guards: any[] = []
name = 'Guard Conditions: ' name = 'Guard Conditions: '
desc = 'Use require and appropriately' desc = 'Use require and appropriately'
......
...@@ -2,7 +2,7 @@ import { default as category } from './categories' ...@@ -2,7 +2,7 @@ import { default as category } from './categories'
import { isInlineAssembly } from './staticAnalysisCommon' import { isInlineAssembly } from './staticAnalysisCommon'
import { default as algorithm } from './algorithmCategories' import { default as algorithm } from './algorithmCategories'
export class inlineAssembly { export default class inlineAssembly {
inlineAssNodes: any[] = [] inlineAssNodes: any[] = []
name = 'Inline assembly: ' name = 'Inline assembly: '
desc = 'Use of Inline Assembly' desc = 'Use of Inline Assembly'
......
...@@ -2,7 +2,7 @@ import { default as category } from './categories' ...@@ -2,7 +2,7 @@ import { default as category } from './categories'
import { isIntDivision } from './staticAnalysisCommon' import { isIntDivision } from './staticAnalysisCommon'
import { default as algorithm } from './algorithmCategories' import { default as algorithm } from './algorithmCategories'
export class intDivitionTruncate { export default class intDivitionTruncate {
warningNodes: any[] = [] warningNodes: any[] = []
name = 'Data Trucated: ' name = 'Data Trucated: '
desc = 'Division on int/uint values truncates the result.' desc = 'Division on int/uint values truncates the result.'
......
module.exports = [
require('./txOrigin'),
require('./gasCosts'),
require('./thisLocal'),
require('./checksEffectsInteraction'),
require('./constantFunctions'),
require('./similarVariableNames.js'),
require('./inlineAssembly'),
require('./blockTimestamp'),
require('./lowLevelCalls'),
require('./blockBlockhash'),
require('./noReturn'),
require('./selfdestruct'),
require('./guardConditions'),
require('./deleteDynamicArrays'),
require('./assignAndCompare'),
require('./erc20Decimals'),
require('./stringBytesLength'),
require('./deleteFromDynamicArray'),
require('./forLoopIteratesOverDynamicArray')
]
import txOrigin from './txOrigin'
import gasCosts from './gasCosts'
import thisLocal from './thisLocal'
import checksEffectsInteraction from './checksEffectsInteraction'
import constantFunctions from './constantFunctions'
import similarVariableNames from './similarVariableNames'
import inlineAssembly from './inlineAssembly'
import blockTimestamp from './blockTimestamp'
import lowLevelCalls from './lowLevelCalls'
import blockBlockhash from './blockBlockhash'
import noReturn from './noReturn'
import selfdestruct from './selfdestruct'
import guardConditions from './guardConditions'
import deleteDynamicArrays from './deleteDynamicArrays'
import assignAndCompare from './assignAndCompare'
import erc20Decimals from './erc20Decimals'
import stringBytesLength from './stringBytesLength'
import deleteFromDynamicArray from './deleteFromDynamicArray'
import forLoopIteratesOverDynamicArray from './forLoopIteratesOverDynamicArray'
export default [
new txOrigin(),
new gasCosts(),
new thisLocal(),
new checksEffectsInteraction(),
new constantFunctions(),
new similarVariableNames(),
new inlineAssembly(),
new blockTimestamp(),
new lowLevelCalls(),
new blockBlockhash(),
new noReturn(),
new selfdestruct(),
new guardConditions(),
new deleteDynamicArrays(),
new assignAndCompare(),
new erc20Decimals(),
new stringBytesLength(),
new deleteFromDynamicArray(),
new forLoopIteratesOverDynamicArray()
]
...@@ -3,7 +3,7 @@ import { isLowLevelCallInst, isLowLevelCallInst050, isLowLevelCallcodeInst, isLo ...@@ -3,7 +3,7 @@ import { isLowLevelCallInst, isLowLevelCallInst050, isLowLevelCallcodeInst, isLo
isLowLevelSendInst, isLowLevelSendInst050, isLLDelegatecallInst050, lowLevelCallTypes } from './staticAnalysisCommon' isLowLevelSendInst, isLowLevelSendInst050, isLLDelegatecallInst050, lowLevelCallTypes } from './staticAnalysisCommon'
import { default as algorithm } from './algorithmCategories' import { default as algorithm } from './algorithmCategories'
export class lowLevelCalls { export default class lowLevelCalls {
llcNodes: any[] = [] llcNodes: any[] = []
name = 'Low level calls: ' name = 'Low level calls: '
desc = 'Semantics maybe unclear' desc = 'Semantics maybe unclear'
......
const name = 'no return: '
const desc = 'Function with return type is not returning'
const categories = require('./categories')
const common = require('./staticAnalysisCommon')
const AbstractAst = require('./abstractAstView')
const algo = require('./algorithmCategories')
function noReturn () {
this.abstractAst = new AbstractAst()
this.visit = this.abstractAst.build_visit(
(node) => common.isReturn(node) || common.isAssignment(node)
)
this.report = this.abstractAst.build_report(report)
}
noReturn.prototype.visit = function () { throw new Error('noReturn.js no visit function set upon construction') }
noReturn.prototype.report = function () { throw new Error('noReturn.js no report function set upon construction') }
function report (contracts, multipleContractsWithSameName) {
const warnings = []
contracts.forEach((contract) => {
contract.functions.filter((func) => common.hasFunctionBody(func.node)).forEach((func) => {
const funcName = common.getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters)
if (hasNamedAndUnnamedReturns(func)) {
warnings.push({
warning: `${funcName}: Mixing of named and unnamed return parameters is not advised.`,
location: func.src
})
} else if (shouldReturn(func) && !(hasReturnStatement(func) || (hasNamedReturns(func) && hasAssignToAllNamedReturns(func)))) {
warnings.push({
warning: `${funcName}: Defines a return type but never explicitly returns a value.`,
location: func.src
})
}
})
})
return warnings
}
function shouldReturn (func) {
return func.returns.length > 0
}
function hasReturnStatement (func) {
return func.relevantNodes.filter(common.isReturn).length > 0
}
function hasAssignToAllNamedReturns (func) {
const namedReturns = func.returns.filter((n) => n.name.length > 0).map((n) => n.name)
const assignedVars = func.relevantNodes.filter(common.isAssignment).map(common.getEffectedVariableName)
const diff = namedReturns.filter(e => !assignedVars.includes(e))
return diff.length === 0
}
function hasNamedReturns (func) {
return func.returns.filter((n) => n.name.length > 0).length > 0
}
function hasNamedAndUnnamedReturns (func) {
return func.returns.filter((n) => n.name.length === 0).length > 0 &&
hasNamedReturns(func)
}
module.exports = {
name: name,
description: desc,
category: categories.MISC,
algorithm: algo.EXACT,
Module: noReturn
}
import { default as category } from './categories'
import { isReturn, isAssignment, hasFunctionBody, getFullQuallyfiedFuncDefinitionIdent, getEffectedVariableName } from './staticAnalysisCommon'
import { default as algorithm } from './algorithmCategories'
import AbstractAst from './abstractAstView'
export default class noReturn {
name = 'no return: '
desc = 'Function with return type is not returning'
categories = category.MISC
algorithm = algorithm.EXACT
Module = this
abstractAst = new AbstractAst()
visit = this.abstractAst.build_visit(
(node) => isReturn(node) || isAssignment(node)
)
report = this.abstractAst.build_report(this._report)
private _report (contracts, multipleContractsWithSameName) {
const warnings: any[] = []
contracts.forEach((contract) => {
contract.functions.filter((func) => hasFunctionBody(func.node)).forEach((func) => {
const funcName = getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters)
if (this.hasNamedAndUnnamedReturns(func)) {
warnings.push({
warning: `${funcName}: Mixing of named and unnamed return parameters is not advised.`,
location: func.src
})
} else if (this.shouldReturn(func) && !(this.hasReturnStatement(func) || (this.hasNamedReturns(func) && this.hasAssignToAllNamedReturns(func)))) {
warnings.push({
warning: `${funcName}: Defines a return type but never explicitly returns a value.`,
location: func.src
})
}
})
})
return warnings
}
private shouldReturn (func) {
return func.returns.length > 0
}
private hasReturnStatement (func) {
return func.relevantNodes.filter(isReturn).length > 0
}
private hasAssignToAllNamedReturns (func) {
const namedReturns = func.returns.filter((n) => n.name.length > 0).map((n) => n.name)
const assignedVars = func.relevantNodes.filter(isAssignment).map(getEffectedVariableName)
const diff = namedReturns.filter(e => !assignedVars.includes(e))
return diff.length === 0
}
private hasNamedReturns (func) {
return func.returns.filter((n) => n.name.length > 0).length > 0
}
private hasNamedAndUnnamedReturns (func) {
return func.returns.filter((n) => n.name.length === 0).length > 0 &&
this.hasNamedReturns(func)
}
}
const name = 'Selfdestruct: ' import { default as category } from './categories'
const desc = 'Be aware of caller contracts.' import { isStatement, isSelfdestructCall } from './staticAnalysisCommon'
const categories = require('./categories') import { default as algorithm } from './algorithmCategories'
const common = require('./staticAnalysisCommon') import AbstractAst from './abstractAstView'
const AbstractAst = require('./abstractAstView')
const algo = require('./algorithmCategories') export default class selfdestruct {
name = 'Selfdestruct: '
function selfdestruct () { desc = 'Be aware of caller contracts.'
this.abstractAst = new AbstractAst() categories = category.SECURITY
algorithm = algorithm.HEURISTIC
this.visit = this.abstractAst.build_visit( Module = this
(node) => common.isStatement(node) ||
common.isSelfdestructCall(node) abstractAst = new AbstractAst()
visit = this.abstractAst.build_visit(
(node) => isStatement(node) ||
isSelfdestructCall(node)
) )
this.report = this.abstractAst.build_report(report) report = this.abstractAst.build_report(this._report)
} private _report (contracts, multipleContractsWithSameName) {
const warnings: any[] = []
selfdestruct.prototype.visit = function () { throw new Error('selfdestruct.js no visit function set upon construction') }
contracts.forEach((contract) => {
selfdestruct.prototype.report = function () { throw new Error('selfdestruct.js no report function set upon construction') } contract.functions.forEach((func) => {
let hasSelf = false
function report (contracts, multipleContractsWithSameName) { func.relevantNodes.forEach((node) => {
const warnings = [] if (isSelfdestructCall(node)) {
warnings.push({
contracts.forEach((contract) => { warning: 'Use of selfdestruct: can block calling contracts unexpectedly. Be especially careful if this contract is planned to be used by other contracts (i.e. library contracts, interactions). Selfdestruction of the callee contract can leave callers in an inoperable state.',
contract.functions.forEach((func) => { location: node.src,
let hasSelf = false more: 'https://paritytech.io/blog/security-alert.html'
func.relevantNodes.forEach((node) => { })
if (common.isSelfdestructCall(node)) { hasSelf = true
warnings.push({ }
warning: 'Use of selfdestruct: can block calling contracts unexpectedly. Be especially careful if this contract is planned to be used by other contracts (i.e. library contracts, interactions). Selfdestruction of the callee contract can leave callers in an inoperable state.', if (isStatement(node) && hasSelf) {
location: node.src, warnings.push({
more: 'https://paritytech.io/blog/security-alert.html' warning: 'Use of selfdestruct: No code after selfdestruct is executed. Selfdestruct is a terminal.',
}) location: node.src,
hasSelf = true more: 'http://solidity.readthedocs.io/en/develop/introduction-to-smart-contracts.html#self-destruct'
} })
if (common.isStatement(node) && hasSelf) { hasSelf = false
warnings.push({ }
warning: 'Use of selfdestruct: No code after selfdestruct is executed. Selfdestruct is a terminal.', })
location: node.src,
more: 'http://solidity.readthedocs.io/en/develop/introduction-to-smart-contracts.html#self-destruct'
})
hasSelf = false
}
}) })
}) })
})
return warnings
}
module.exports = { return warnings
name: name, }
description: desc,
category: categories.SECURITY,
algorithm: algo.HEURISTIC,
Module: selfdestruct
} }
const name = 'Similar variable names: ' import { default as category } from './categories'
const desc = 'Check if variable names are too similar' import { getDeclaredVariableName, getFullQuallyfiedFuncDefinitionIdent } from './staticAnalysisCommon'
const categories = require('./categories') import { default as algorithm } from './algorithmCategories'
const common = require('./staticAnalysisCommon') import AbstractAst from './abstractAstView'
const AbstractAst = require('./abstractAstView') import { get } from 'fast-levenshtein'
const levenshtein = require('fast-levenshtein') import { util } from 'remix-lib'
const remixLib = require('remix-lib')
const util = remixLib.util
const algo = require('./algorithmCategories')
function similarVariableNames () { export default class similarVariableNames {
this.abstractAst = new AbstractAst() name = 'Similar variable names: '
desc = 'Check if variable names are too similar'
abstractAst = new AbstractAst()
categories = category.MISC
algorithm = algorithm.EXACT
Module = this
this.visit = this.abstractAst.build_visit( visit = this.abstractAst.build_visit(
(node) => false (node) => false
) )
this.report = this.abstractAst.build_report(report) report = this.abstractAst.build_report(this._report)
}
similarVariableNames.prototype.visit = function () { throw new Error('similarVariableNames.js no visit function set upon construction') }
similarVariableNames.prototype.report = function () { throw new Error('similarVariableNames.js no report function set upon construction') }
function report (contracts, multipleContractsWithSameName) { private _report (contracts, multipleContractsWithSameName) {
const warnings = [] const warnings: any[] = []
const hasModifiers = contracts.some((item) => item.modifiers.length > 0) const hasModifiers = contracts.some((item) => item.modifiers.length > 0)
contracts.forEach((contract) => { contracts.forEach((contract) => {
contract.functions.forEach((func) => { contract.functions.forEach((func) => {
const funcName = common.getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters) const funcName = getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters)
let hasModifiersComments = '' let hasModifiersComments = ''
if (hasModifiers) { if (hasModifiers) {
hasModifiersComments = 'Note: Modifiers are currently not considered by this static analysis.' hasModifiersComments = 'Note: Modifiers are currently not considered by this static analysis.'
} }
let multipleContractsWithSameNameComments = '' let multipleContractsWithSameNameComments = ''
if (multipleContractsWithSameName) { if (multipleContractsWithSameName) {
multipleContractsWithSameNameComments = 'Note: Import aliases are currently not supported by this static analysis.' multipleContractsWithSameNameComments = 'Note: Import aliases are currently not supported by this static analysis.'
} }
const vars = getFunctionVariables(contract, func).map(common.getDeclaredVariableName) const vars = this.getFunctionVariables(contract, func).map(getDeclaredVariableName)
findSimilarVarNames(vars).map((sim) => { this.findSimilarVarNames(vars).map((sim) => {
warnings.push({ warnings.push({
warning: `${funcName} : Variables have very similar names ${sim.var1} and ${sim.var2}. ${hasModifiersComments} ${multipleContractsWithSameNameComments}`, warning: `${funcName} : Variables have very similar names ${sim.var1} and ${sim.var2}. ${hasModifiersComments} ${multipleContractsWithSameNameComments}`,
location: func.src location: func.src
})
}) })
}) })
}) })
})
return warnings
}
function findSimilarVarNames (vars) { return warnings
const similar = [] }
const comb = {}
vars.map((varName1) => vars.map((varName2) => {
if (varName1.length > 1 && varName2.length > 1 && varName2 !== varName1 && !isCommonPrefixedVersion(varName1, varName2) && !isCommonNrSuffixVersion(varName1, varName2) && !(comb[varName1 + ';' + varName2] || comb[varName2 + ';' + varName1])) {
comb[varName1 + ';' + varName2] = true
const distance = levenshtein.get(varName1, varName2)
if (distance <= 2) similar.push({ var1: varName1, var2: varName2, distance: distance })
}
}))
return similar
}
function isCommonPrefixedVersion (varName1, varName2) { private findSimilarVarNames (vars) {
return (varName1.startsWith('_') && varName1.slice(1) === varName2) || (varName2.startsWith('_') && varName2.slice(1) === varName1) const similar: any[] = []
} const comb = {}
vars.map((varName1) => vars.map((varName2) => {
if (varName1.length > 1 && varName2.length > 1 && varName2 !== varName1 && !this.isCommonPrefixedVersion(varName1, varName2) && !this.isCommonNrSuffixVersion(varName1, varName2) && !(comb[varName1 + ';' + varName2] || comb[varName2 + ';' + varName1])) {
comb[varName1 + ';' + varName2] = true
const distance = get(varName1, varName2)
if (distance <= 2) similar.push({ var1: varName1, var2: varName2, distance: distance })
}
}))
return similar
}
function isCommonNrSuffixVersion (varName1, varName2) { private isCommonPrefixedVersion (varName1, varName2) {
const ref = '^' + util.escapeRegExp(varName1.slice(0, -1)) + '[0-9]*$' return (varName1.startsWith('_') && varName1.slice(1) === varName2) || (varName2.startsWith('_') && varName2.slice(1) === varName1)
return varName2.match(ref) != null }
}
function getFunctionVariables (contract, func) { private isCommonNrSuffixVersion (varName1, varName2) {
return contract.stateVariables.concat(func.localVariables) const ref = '^' + util.escapeRegExp(varName1.slice(0, -1)) + '[0-9]*$'
} return varName2.match(ref) != null
}
module.exports = { private getFunctionVariables (contract, func) {
name: name, return contract.stateVariables.concat(func.localVariables)
description: desc, }
category: categories.MISC,
algorithm: algo.EXACT,
Module: similarVariableNames
} }
const name = 'String Length: ' import { default as category } from './categories'
const desc = 'Bytes length != String length' import { isStringToBytesConversion, isBytesLengthCheck } from './staticAnalysisCommon'
const categories = require('./categories')
const common = require('./staticAnalysisCommon')
function stringBytesLength () { export default class stringBytesLength {
this.stringToBytesConversions = [] name = 'String Length: '
this.bytesLengthChecks = [] desc = 'Bytes length != String length'
} categories = category.MISC
Module = this
stringToBytesConversions: any[] = []
bytesLengthChecks: any[] = []
stringBytesLength.prototype.visit = function (node) {
if (common.isStringToBytesConversion(node)) this.stringToBytesConversions.push(node)
else if (common.isBytesLengthCheck(node)) this.bytesLengthChecks.push(node)
}
stringBytesLength.prototype.report = function (compilationResults) { visit (node) {
if (this.stringToBytesConversions.length > 0 && this.bytesLengthChecks.length > 0) { if (isStringToBytesConversion(node)) this.stringToBytesConversions.push(node)
return [{ else if (isBytesLengthCheck(node)) this.bytesLengthChecks.push(node)
warning: 'Bytes and string length are not the same since strings are assumed to be UTF-8 encoded (according to the ABI defintion) therefore one character is not nessesarily encoded in one byte of data.',
location: this.bytesLengthChecks[0].src,
more: 'https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI#argument-encoding'
}]
} else {
return []
} }
}
module.exports = { report (compilationResults) {
name: name, if (this.stringToBytesConversions.length > 0 && this.bytesLengthChecks.length > 0) {
description: desc, return [{
category: categories.MISC, warning: 'Bytes and string length are not the same since strings are assumed to be UTF-8 encoded (according to the ABI defintion) therefore one character is not nessesarily encoded in one byte of data.',
Module: stringBytesLength location: this.bytesLengthChecks[0].src,
more: 'https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI#argument-encoding'
}]
} else {
return []
}
}
} }
const name = 'This on local calls: '
const desc = 'Invocation of local functions via this'
const categories = require('./categories')
const common = require('./staticAnalysisCommon')
const algo = require('./algorithmCategories')
function thisLocal () {
this.warningNodes = []
}
thisLocal.prototype.visit = function (node) {
if (common.isThisLocalCall(node)) this.warningNodes.push(node)
}
thisLocal.prototype.report = function (compilationResults) {
return this.warningNodes.map(function (item, i) {
return {
warning: 'Use of "this" for local functions: Never use this to call functions in the same contract, it only consumes more gas than normal local calls.',
location: item.src,
more: 'http://solidity.readthedocs.io/en/develop/control-structures.html#external-function-calls'
}
})
}
module.exports = {
name: name,
description: desc,
category: categories.GAS,
algorithm: algo.EXACT,
Module: thisLocal
}
import { default as category } from './categories'
import { isThisLocalCall } from './staticAnalysisCommon'
import { default as algorithm } from './algorithmCategories'
export default class thisLocal {
warningNodes: any[] = []
name = 'This on local calls: '
desc = 'Invocation of local functions via this'
categories = category.GAS
algorithm = algorithm.EXACT
Module = this
visit (node) {
if (isThisLocalCall(node)) this.warningNodes.push(node)
}
report (compilationResults) {
return this.warningNodes.map(function (item, i) {
return {
warning: 'Use of "this" for local functions: Never use this to call functions in the same contract, it only consumes more gas than normal local calls.',
location: item.src,
more: 'http://solidity.readthedocs.io/en/develop/control-structures.html#external-function-calls'
}
})
}
}
const name = 'Transaction origin: ' import { default as category } from './categories'
const desc = 'Warn if tx.origin is used' import { default as algorithm } from './algorithmCategories'
const categories = require('./categories')
const algo = require('./algorithmCategories')
function txOrigin () { export default class txOrigin {
this.txOriginNodes = [] txOriginNodes: any[] = []
} name = 'Transaction origin: '
desc = 'Warn if tx.origin is used'
txOrigin.prototype.visit = function (node) { categories = category.SECURITY
if (node.name === 'MemberAccess' && algorithm = algorithm.EXACT
node.attributes.member_name === 'origin' && Module = this
(node.attributes.type === 'address' || node.attributes.type === 'address payable') &&
node.children && node.children.length &&
node.children[0].attributes.type === 'tx' &&
node.children[0].attributes.value === 'tx') {
this.txOriginNodes.push(node)
}
}
txOrigin.prototype.report = function () { visit (node) {
return this.txOriginNodes.map((item, i) => { if (node.name === 'MemberAccess' &&
return { node.attributes.member_name === 'origin' &&
warning: `Use of tx.origin: "tx.origin" is useful only in very exceptional cases. (node.attributes.type === 'address' || node.attributes.type === 'address payable') &&
If you use it for authentication, you usually want to replace it by "msg.sender", because otherwise any contract you call can act on your behalf.`, node.children && node.children.length &&
location: item.src node.children[0].attributes.type === 'tx' &&
node.children[0].attributes.value === 'tx') {
this.txOriginNodes.push(node)
} }
}) }
}
module.exports = { report () {
name: name, return this.txOriginNodes.map((item, i) => {
description: desc, return {
category: categories.SECURITY, warning: `Use of tx.origin: "tx.origin" is useful only in very exceptional cases.
algorithm: algo.EXACT, If you use it for authentication, you usually want to replace it by "msg.sender", because otherwise any contract you call can act on your behalf.`,
Module: txOrigin location: item.src
}
})
}
} }
var test = require('tape') var test = require('tape')
var common = require('../../src/solidity-analyzer/modules/staticAnalysisCommon') var common = require('../../dist/src/solidity-analyzer/modules/staticAnalysisCommon')
var { localCall, thisLocalCall, libCall, externalDirect, superLocal, assignment, var { localCall, thisLocalCall, libCall, externalDirect, superLocal, assignment,
inlineAssembly, forLoopNode, whileLoopNode, doWhileLoopNode, stateVariableContractNode, inlineAssembly, forLoopNode, whileLoopNode, doWhileLoopNode, stateVariableContractNode,
functionDefinition, fullyQualifiedFunctionDefinition, selfdestruct, storageVariableNodes, functionDefinition, fullyQualifiedFunctionDefinition, selfdestruct, storageVariableNodes,
...@@ -56,11 +56,11 @@ test('staticAnalysisCommon.helpers.name', function (t) { ...@@ -56,11 +56,11 @@ test('staticAnalysisCommon.helpers.name', function (t) {
var node = { attributes: { value: 'now' } } var node = { attributes: { value: 'now' } }
var node2 = { attributes: { member_name: 'call' } } var node2 = { attributes: { member_name: 'call' } }
t.ok(common.helpers.name(node, 'now'), 'should work for values') t.ok(common.helpers.memName(node, 'now'), 'should work for values')
t.ok(common.helpers.name(node2, 'call'), 'should work for member_name') t.ok(common.helpers.memName(node2, 'call'), 'should work for member_name')
t.ok(common.helpers.name(node2, '.all'), 'regex should work') t.ok(common.helpers.memName(node2, '.all'), 'regex should work')
lowlevelAccessersCommon(t, common.helpers.name, node) lowlevelAccessersCommon(t, common.helpers.memName, node)
}) })
test('staticAnalysisCommon.helpers.operator', function (t) { test('staticAnalysisCommon.helpers.operator', function (t) {
......
var test = require('tape') var test = require('tape')
var remixLib = require('remix-lib') var remixLib = require('remix-lib')
var StatRunner = require('../../src/solidity-analyzer') var StatRunner = require('../../dist/src/solidity-analyzer')
var compilerInput = remixLib.helpers.compiler.compilerInput var compilerInput = remixLib.helpers.compiler.compilerInput
const niv = require('npm-install-version') const niv = require('npm-install-version')
...@@ -51,7 +51,7 @@ testFiles.forEach((fileName) => { ...@@ -51,7 +51,7 @@ testFiles.forEach((fileName) => {
test('Integration test thisLocal.js', function (t) { test('Integration test thisLocal.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/thisLocal') var module = require('../../dist/src/solidity-analyzer/modules/thisLocal')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -90,7 +90,7 @@ test('Integration test thisLocal.js', function (t) { ...@@ -90,7 +90,7 @@ test('Integration test thisLocal.js', function (t) {
test('Integration test checksEffectsInteraction.js', function (t) { test('Integration test checksEffectsInteraction.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/checksEffectsInteraction') var module = require('../../dist/src/solidity-analyzer/modules/checksEffectsInteraction')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 1, 'KingOfTheEtherThrone.sol': 1,
...@@ -129,7 +129,7 @@ test('Integration test checksEffectsInteraction.js', function (t) { ...@@ -129,7 +129,7 @@ test('Integration test checksEffectsInteraction.js', function (t) {
test('Integration test constantFunctions.js', function (t) { test('Integration test constantFunctions.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/constantFunctions') var module = require('../../dist/src/solidity-analyzer/modules/constantFunctions')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -168,7 +168,7 @@ test('Integration test constantFunctions.js', function (t) { ...@@ -168,7 +168,7 @@ test('Integration test constantFunctions.js', function (t) {
test('Integration test inlineAssembly.js', function (t) { test('Integration test inlineAssembly.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/inlineAssembly') var module = require('../../dist/src/solidity-analyzer/modules/inlineAssembly')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -207,7 +207,7 @@ test('Integration test inlineAssembly.js', function (t) { ...@@ -207,7 +207,7 @@ test('Integration test inlineAssembly.js', function (t) {
test('Integration test txOrigin.js', function (t) { test('Integration test txOrigin.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/txOrigin') var module = require('../../dist/src/solidity-analyzer/modules/txOrigin')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -246,7 +246,7 @@ test('Integration test txOrigin.js', function (t) { ...@@ -246,7 +246,7 @@ test('Integration test txOrigin.js', function (t) {
test('Integration test gasCosts.js', function (t) { test('Integration test gasCosts.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/gasCosts') var module = require('../../dist/src/solidity-analyzer/modules/gasCosts')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 2, 'KingOfTheEtherThrone.sol': 2,
...@@ -285,7 +285,7 @@ test('Integration test gasCosts.js', function (t) { ...@@ -285,7 +285,7 @@ test('Integration test gasCosts.js', function (t) {
test('Integration test similarVariableNames.js', function (t) { test('Integration test similarVariableNames.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/similarVariableNames') var module = require('../../dist/src/solidity-analyzer/modules/similarVariableNames')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -324,7 +324,7 @@ test('Integration test similarVariableNames.js', function (t) { ...@@ -324,7 +324,7 @@ test('Integration test similarVariableNames.js', function (t) {
test('Integration test inlineAssembly.js', function (t) { test('Integration test inlineAssembly.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/inlineAssembly') var module = require('../../dist/src/solidity-analyzer/modules/inlineAssembly')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -363,7 +363,7 @@ test('Integration test inlineAssembly.js', function (t) { ...@@ -363,7 +363,7 @@ test('Integration test inlineAssembly.js', function (t) {
test('Integration test blockTimestamp.js', function (t) { test('Integration test blockTimestamp.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/blockTimestamp') var module = require('../../dist/src/solidity-analyzer/modules/blockTimestamp')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 1, 'KingOfTheEtherThrone.sol': 1,
...@@ -402,7 +402,7 @@ test('Integration test blockTimestamp.js', function (t) { ...@@ -402,7 +402,7 @@ test('Integration test blockTimestamp.js', function (t) {
test('Integration test lowLevelCalls.js', function (t) { test('Integration test lowLevelCalls.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/lowLevelCalls') var module = require('../../dist/src/solidity-analyzer/modules/lowLevelCalls')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 1, 'KingOfTheEtherThrone.sol': 1,
...@@ -441,7 +441,7 @@ test('Integration test lowLevelCalls.js', function (t) { ...@@ -441,7 +441,7 @@ test('Integration test lowLevelCalls.js', function (t) {
test('Integration test blockBlockhash.js', function (t) { test('Integration test blockBlockhash.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/blockBlockhash') var module = require('../../dist/src/solidity-analyzer/modules/blockBlockhash')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -480,7 +480,7 @@ test('Integration test blockBlockhash.js', function (t) { ...@@ -480,7 +480,7 @@ test('Integration test blockBlockhash.js', function (t) {
test('Integration test noReturn.js', function (t) { test('Integration test noReturn.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/noReturn') var module = require('../../dist/src/solidity-analyzer/modules/noReturn')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -519,7 +519,7 @@ test('Integration test noReturn.js', function (t) { ...@@ -519,7 +519,7 @@ test('Integration test noReturn.js', function (t) {
test('Integration test selfdestruct.js', function (t) { test('Integration test selfdestruct.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/selfdestruct') var module = require('../../dist/src/solidity-analyzer/modules/selfdestruct')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -558,7 +558,7 @@ test('Integration test selfdestruct.js', function (t) { ...@@ -558,7 +558,7 @@ test('Integration test selfdestruct.js', function (t) {
test('Integration test guardConditions.js', function (t) { test('Integration test guardConditions.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/guardConditions') var module = require('../../dist/src/solidity-analyzer/modules/guardConditions')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -597,7 +597,7 @@ test('Integration test guardConditions.js', function (t) { ...@@ -597,7 +597,7 @@ test('Integration test guardConditions.js', function (t) {
test('Integration test deleteDynamicArrays.js', function (t) { test('Integration test deleteDynamicArrays.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/deleteDynamicArrays') var module = require('../../dist/src/solidity-analyzer/modules/deleteDynamicArrays')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -636,7 +636,7 @@ test('Integration test deleteDynamicArrays.js', function (t) { ...@@ -636,7 +636,7 @@ test('Integration test deleteDynamicArrays.js', function (t) {
test('Integration test deleteFromDynamicArray.js', function (t) { test('Integration test deleteFromDynamicArray.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/deleteFromDynamicArray') var module = require('../../dist/src/solidity-analyzer/modules/deleteFromDynamicArray')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -675,7 +675,7 @@ test('Integration test deleteFromDynamicArray.js', function (t) { ...@@ -675,7 +675,7 @@ test('Integration test deleteFromDynamicArray.js', function (t) {
test('Integration test assignAndCompare.js', function (t) { test('Integration test assignAndCompare.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/assignAndCompare') var module = require('../../dist/src/solidity-analyzer/modules/assignAndCompare')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -714,7 +714,7 @@ test('Integration test assignAndCompare.js', function (t) { ...@@ -714,7 +714,7 @@ test('Integration test assignAndCompare.js', function (t) {
test('Integration test intDivisionTruncate.js', function (t) { test('Integration test intDivisionTruncate.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/intDivisionTruncate') var module = require('../../dist/src/solidity-analyzer/modules/intDivisionTruncate')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -753,7 +753,7 @@ test('Integration test intDivisionTruncate.js', function (t) { ...@@ -753,7 +753,7 @@ test('Integration test intDivisionTruncate.js', function (t) {
test('Integration test erc20Decimal.js', function (t) { test('Integration test erc20Decimal.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/erc20Decimals') var module = require('../../dist/src/solidity-analyzer/modules/erc20Decimals')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -792,7 +792,7 @@ test('Integration test erc20Decimal.js', function (t) { ...@@ -792,7 +792,7 @@ test('Integration test erc20Decimal.js', function (t) {
test('Integration test stringBytesLength.js', function (t) { test('Integration test stringBytesLength.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/stringBytesLength') var module = require('../../dist/src/solidity-analyzer/modules/stringBytesLength')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -831,7 +831,7 @@ test('Integration test stringBytesLength.js', function (t) { ...@@ -831,7 +831,7 @@ test('Integration test stringBytesLength.js', function (t) {
test('Integration test etherTransferInLoop.js', function (t) { test('Integration test etherTransferInLoop.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/etherTransferInLoop') var module = require('../../dist/src/solidity-analyzer/modules/etherTransferInLoop')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -870,7 +870,7 @@ test('Integration test etherTransferInLoop.js', function (t) { ...@@ -870,7 +870,7 @@ test('Integration test etherTransferInLoop.js', function (t) {
test('Integration test forLoopIteratesOverDynamicArray.js', function (t) { test('Integration test forLoopIteratesOverDynamicArray.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/forLoopIteratesOverDynamicArray') var module = require('../../dist/src/solidity-analyzer/modules/forLoopIteratesOverDynamicArray')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
......
var test = require('tape') var test = require('tape')
var remixLib = require('remix-lib') var remixLib = require('remix-lib')
var StatRunner = require('../../src/solidity-analyzer') var StatRunner = require('../../dist/src/solidity-analyzer')
var compilerInput = remixLib.helpers.compiler.compilerInput var compilerInput = remixLib.helpers.compiler.compilerInput
const niv = require('npm-install-version') const niv = require('npm-install-version')
...@@ -51,7 +51,7 @@ testFiles.forEach((fileName) => { ...@@ -51,7 +51,7 @@ testFiles.forEach((fileName) => {
test('Integration test thisLocal.js', function (t) { test('Integration test thisLocal.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/thisLocal') var module = require('../../dist/src/solidity-analyzer/modules/thisLocal')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -90,7 +90,7 @@ test('Integration test thisLocal.js', function (t) { ...@@ -90,7 +90,7 @@ test('Integration test thisLocal.js', function (t) {
test('Integration test checksEffectsInteraction.js', function (t) { test('Integration test checksEffectsInteraction.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/checksEffectsInteraction') var module = require('../../dist/src/solidity-analyzer/modules/checksEffectsInteraction')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 1, 'KingOfTheEtherThrone.sol': 1,
...@@ -129,7 +129,7 @@ test('Integration test checksEffectsInteraction.js', function (t) { ...@@ -129,7 +129,7 @@ test('Integration test checksEffectsInteraction.js', function (t) {
test('Integration test constantFunctions.js', function (t) { test('Integration test constantFunctions.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/constantFunctions') var module = require('../../dist/src/solidity-analyzer/modules/constantFunctions')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -168,7 +168,7 @@ test('Integration test constantFunctions.js', function (t) { ...@@ -168,7 +168,7 @@ test('Integration test constantFunctions.js', function (t) {
test('Integration test inlineAssembly.js', function (t) { test('Integration test inlineAssembly.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/inlineAssembly') var module = require('../../dist/src/solidity-analyzer/modules/inlineAssembly')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -207,7 +207,7 @@ test('Integration test inlineAssembly.js', function (t) { ...@@ -207,7 +207,7 @@ test('Integration test inlineAssembly.js', function (t) {
test('Integration test txOrigin.js', function (t) { test('Integration test txOrigin.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/txOrigin') var module = require('../../dist/src/solidity-analyzer/modules/txOrigin')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -246,7 +246,7 @@ test('Integration test txOrigin.js', function (t) { ...@@ -246,7 +246,7 @@ test('Integration test txOrigin.js', function (t) {
test('Integration test gasCosts.js', function (t) { test('Integration test gasCosts.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/gasCosts') var module = require('../../dist/src/solidity-analyzer/modules/gasCosts')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 2, 'KingOfTheEtherThrone.sol': 2,
...@@ -285,7 +285,7 @@ test('Integration test gasCosts.js', function (t) { ...@@ -285,7 +285,7 @@ test('Integration test gasCosts.js', function (t) {
test('Integration test similarVariableNames.js', function (t) { test('Integration test similarVariableNames.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/similarVariableNames') var module = require('../../dist/src/solidity-analyzer/modules/similarVariableNames')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -324,7 +324,7 @@ test('Integration test similarVariableNames.js', function (t) { ...@@ -324,7 +324,7 @@ test('Integration test similarVariableNames.js', function (t) {
test('Integration test inlineAssembly.js', function (t) { test('Integration test inlineAssembly.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/inlineAssembly') var module = require('../../dist/src/solidity-analyzer/modules/inlineAssembly')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -363,7 +363,7 @@ test('Integration test inlineAssembly.js', function (t) { ...@@ -363,7 +363,7 @@ test('Integration test inlineAssembly.js', function (t) {
test('Integration test blockTimestamp.js', function (t) { test('Integration test blockTimestamp.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/blockTimestamp') var module = require('../../dist/src/solidity-analyzer/modules/blockTimestamp')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 1, 'KingOfTheEtherThrone.sol': 1,
...@@ -402,7 +402,7 @@ test('Integration test blockTimestamp.js', function (t) { ...@@ -402,7 +402,7 @@ test('Integration test blockTimestamp.js', function (t) {
test('Integration test lowLevelCalls.js', function (t) { test('Integration test lowLevelCalls.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/lowLevelCalls') var module = require('../../dist/src/solidity-analyzer/modules/lowLevelCalls')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 1, 'KingOfTheEtherThrone.sol': 1,
...@@ -441,7 +441,7 @@ test('Integration test lowLevelCalls.js', function (t) { ...@@ -441,7 +441,7 @@ test('Integration test lowLevelCalls.js', function (t) {
test('Integration test blockBlockhash.js', function (t) { test('Integration test blockBlockhash.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/blockBlockhash') var module = require('../../dist/src/solidity-analyzer/modules/blockBlockhash')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -484,7 +484,7 @@ test('Integration test blockBlockhash.js', function (t) { ...@@ -484,7 +484,7 @@ test('Integration test blockBlockhash.js', function (t) {
test('Integration test noReturn.js', function (t) { test('Integration test noReturn.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/noReturn') var module = require('../../dist/src/solidity-analyzer/modules/noReturn')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -523,7 +523,7 @@ test('Integration test noReturn.js', function (t) { ...@@ -523,7 +523,7 @@ test('Integration test noReturn.js', function (t) {
test('Integration test selfdestruct.js', function (t) { test('Integration test selfdestruct.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/selfdestruct') var module = require('../../dist/src/solidity-analyzer/modules/selfdestruct')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -562,7 +562,7 @@ test('Integration test selfdestruct.js', function (t) { ...@@ -562,7 +562,7 @@ test('Integration test selfdestruct.js', function (t) {
test('Integration test guardConditions.js', function (t) { test('Integration test guardConditions.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/guardConditions') var module = require('../../dist/src/solidity-analyzer/modules/guardConditions')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -601,7 +601,7 @@ test('Integration test guardConditions.js', function (t) { ...@@ -601,7 +601,7 @@ test('Integration test guardConditions.js', function (t) {
test('Integration test deleteDynamicArrays.js', function (t) { test('Integration test deleteDynamicArrays.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/deleteDynamicArrays') var module = require('../../dist/src/solidity-analyzer/modules/deleteDynamicArrays')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -640,7 +640,7 @@ test('Integration test deleteDynamicArrays.js', function (t) { ...@@ -640,7 +640,7 @@ test('Integration test deleteDynamicArrays.js', function (t) {
test('Integration test deleteFromDynamicArray.js', function (t) { test('Integration test deleteFromDynamicArray.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/deleteFromDynamicArray') var module = require('../../dist/src/solidity-analyzer/modules/deleteFromDynamicArray')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -679,7 +679,7 @@ test('Integration test deleteFromDynamicArray.js', function (t) { ...@@ -679,7 +679,7 @@ test('Integration test deleteFromDynamicArray.js', function (t) {
test('Integration test assignAndCompare.js', function (t) { test('Integration test assignAndCompare.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/assignAndCompare') var module = require('../../dist/src/solidity-analyzer/modules/assignAndCompare')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -718,7 +718,7 @@ test('Integration test assignAndCompare.js', function (t) { ...@@ -718,7 +718,7 @@ test('Integration test assignAndCompare.js', function (t) {
test('Integration test intDivisionTruncate.js', function (t) { test('Integration test intDivisionTruncate.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/intDivisionTruncate') var module = require('../../dist/src/solidity-analyzer/modules/intDivisionTruncate')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -757,7 +757,7 @@ test('Integration test intDivisionTruncate.js', function (t) { ...@@ -757,7 +757,7 @@ test('Integration test intDivisionTruncate.js', function (t) {
test('Integration test erc20Decimal.js', function (t) { test('Integration test erc20Decimal.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/erc20Decimals') var module = require('../../dist/src/solidity-analyzer/modules/erc20Decimals')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -796,7 +796,7 @@ test('Integration test erc20Decimal.js', function (t) { ...@@ -796,7 +796,7 @@ test('Integration test erc20Decimal.js', function (t) {
test('Integration test stringBytesLength.js', function (t) { test('Integration test stringBytesLength.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/stringBytesLength') var module = require('../../dist/src/solidity-analyzer/modules/stringBytesLength')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -835,7 +835,7 @@ test('Integration test stringBytesLength.js', function (t) { ...@@ -835,7 +835,7 @@ test('Integration test stringBytesLength.js', function (t) {
test('Integration test etherTransferInLoop.js', function (t) { test('Integration test etherTransferInLoop.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/etherTransferInLoop') var module = require('../../dist/src/solidity-analyzer/modules/etherTransferInLoop')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
...@@ -874,7 +874,7 @@ test('Integration test etherTransferInLoop.js', function (t) { ...@@ -874,7 +874,7 @@ test('Integration test etherTransferInLoop.js', function (t) {
test('Integration test forLoopIteratesOverDynamicArray.js', function (t) { test('Integration test forLoopIteratesOverDynamicArray.js', function (t) {
t.plan(testFiles.length) t.plan(testFiles.length)
var module = require('../../src/solidity-analyzer/modules/forLoopIteratesOverDynamicArray') var module = require('../../dist/src/solidity-analyzer/modules/forLoopIteratesOverDynamicArray')
var lengthCheck = { var lengthCheck = {
'KingOfTheEtherThrone.sol': 0, 'KingOfTheEtherThrone.sol': 0,
......
var test = require('tape') var test = require('tape')
var remixLib = require('remix-lib') var remixLib = require('remix-lib')
var StatRunner = require('../../src/solidity-analyzer') var StatRunner = require('../../dist/src/solidity-analyzer')
var compilerInput = remixLib.helpers.compiler.compilerInput var compilerInput = remixLib.helpers.compiler.compilerInput
const niv = require('npm-install-version') const niv = require('npm-install-version')
...@@ -22,7 +22,7 @@ test('staticAnalysisIssues.functionParameterPassingError', function (t) { ...@@ -22,7 +22,7 @@ test('staticAnalysisIssues.functionParameterPassingError', function (t) {
t.plan(2) t.plan(2)
var res = compile('functionParameters.sol') var res = compile('functionParameters.sol')
var module = require('../../src/solidity-analyzer/modules/checksEffectsInteraction') var module = require('../../dist/src/solidity-analyzer/modules/checksEffectsInteraction')
var statRunner = new StatRunner() var statRunner = new StatRunner()
......
var test = require('tape') var test = require('tape')
var remixLib = require('remix-lib') var remixLib = require('remix-lib')
var StatRunner = require('../../src/solidity-analyzer') var StatRunner = require('../../dist/src/solidity-analyzer')
var compilerInput = remixLib.helpers.compiler.compilerInput var compilerInput = remixLib.helpers.compiler.compilerInput
const niv = require('npm-install-version') const niv = require('npm-install-version')
...@@ -22,7 +22,7 @@ test('staticAnalysisIssues.functionParameterPassingError', function (t) { ...@@ -22,7 +22,7 @@ test('staticAnalysisIssues.functionParameterPassingError', function (t) {
t.plan(2) t.plan(2)
var res = compile('functionParameters.sol') var res = compile('functionParameters.sol')
var module = require('../../src/solidity-analyzer/modules/checksEffectsInteraction') var module = require('../../dist/src/solidity-analyzer/modules/checksEffectsInteraction')
var statRunner = new StatRunner() var statRunner = new StatRunner()
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
"noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */ "noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */
/* Module Resolution Options */ /* Module Resolution Options */
"baseUrl": "./src", /* Base directory to resolve non-absolute module names. */ "baseUrl": "./src", /* Base directory to resolve non-absolute module names. */
"paths": { "remix-solidity": ["./"] }, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ "paths": { "remix-analyzer": ["./"] }, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
"typeRoots": [ "typeRoots": [
"./@types", "./@types",
"./node_modules/@types" "./node_modules/@types"
......
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