parent
6a55230cae
commit
5029935a0b
@ -1,34 +0,0 @@ |
||||
const name = 'Block.blockhash usage: ' |
||||
const desc = 'Semantics maybe unclear' |
||||
const categories = require('./categories') |
||||
const common = require('./staticAnalysisCommon') |
||||
const algo = require('./algorithmCategories') |
||||
|
||||
function blockBlockhash () { |
||||
this.warningNodes = [] |
||||
} |
||||
|
||||
blockBlockhash.prototype.visit = function (node) { |
||||
if (common.isBlockBlockHashAccess(node)) this.warningNodes.push(node) |
||||
} |
||||
|
||||
blockBlockhash.prototype.report = function (compilationResults) { |
||||
return this.warningNodes.map((item, i) => { |
||||
return { |
||||
warning: `use of "block.blockhash": "block.blockhash" is used to access the last 256 block hashes.
|
||||
A miner computes the block hash by "summing up" the information in the current block mined.
|
||||
By "summing up" the information in a clever way a miner can try to influence the outcome of a transaction in the current block.
|
||||
This is especially easy if there are only a small number of equally likely outcomes.`,
|
||||
location: item.src |
||||
} |
||||
}) |
||||
} |
||||
|
||||
module.exports = { |
||||
name: name, |
||||
description: desc, |
||||
category: categories.SECURITY, |
||||
algorithm: algo.EXACT, |
||||
Module: blockBlockhash |
||||
} |
||||
|
@ -0,0 +1,29 @@ |
||||
import { default as category } from './categories' |
||||
import { isBlockBlockHashAccess } from './staticAnalysisCommon' |
||||
import { default as algorithm } from './algorithmCategories' |
||||
|
||||
export class blockBlockhash { |
||||
warningNodes: any[] = [] |
||||
name = 'Block.blockhash usage: ' |
||||
desc = 'Semantics maybe unclear' |
||||
categories = category.SECURITY |
||||
algorithm = algorithm.EXACT |
||||
Module = this |
||||
|
||||
visit (node) { |
||||
if (isBlockBlockHashAccess(node)) this.warningNodes.push(node) |
||||
} |
||||
|
||||
report (compilationResults) { |
||||
return this.warningNodes.map((item, i) => { |
||||
return { |
||||
warning: `use of "block.blockhash": "block.blockhash" is used to access the last 256 block hashes.
|
||||
A miner computes the block hash by "summing up" the information in the current block mined.
|
||||
By "summing up" the information in a clever way a miner can try to influence the outcome of a transaction in the current block.
|
||||
This is especially easy if there are only a small number of equally likely outcomes.`,
|
||||
location: item.src |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
@ -1,42 +0,0 @@ |
||||
const name = 'Block timestamp: ' |
||||
const desc = 'Semantics maybe unclear' |
||||
const categories = require('./categories') |
||||
const common = require('./staticAnalysisCommon') |
||||
const algo = require('./algorithmCategories') |
||||
|
||||
function blockTimestamp () { |
||||
this.warningNowNodes = [] |
||||
this.warningblockTimestampNodes = [] |
||||
} |
||||
|
||||
blockTimestamp.prototype.visit = function (node) { |
||||
if (common.isNowAccess(node)) this.warningNowNodes.push(node) |
||||
else if (common.isBlockTimestampAccess(node)) this.warningblockTimestampNodes.push(node) |
||||
} |
||||
|
||||
blockTimestamp.prototype.report = function (compilationResults) { |
||||
return this.warningNowNodes.map((item, i) => { |
||||
return { |
||||
warning: `use of "now": "now" does not mean current time. Now is an alias for block.timestamp.
|
||||
Block.timestamp can be influenced by miners to a certain degree, be careful.`,
|
||||
location: item.src, |
||||
more: 'http://solidity.readthedocs.io/en/develop/frequently-asked-questions.html#are-timestamps-now-block-timestamp-reliable' |
||||
} |
||||
}).concat(this.warningblockTimestampNodes.map((item, i) => { |
||||
return { |
||||
warning: `use of "block.timestamp": "block.timestamp" can be influenced by miners to a certain degree.
|
||||
That means that a miner can "choose" the block.timestamp, to a certain degree, to change the outcome of a transaction in the mined block.`,
|
||||
location: item.src, |
||||
more: 'http://solidity.readthedocs.io/en/develop/frequently-asked-questions.html#are-timestamps-now-block-timestamp-reliable' |
||||
} |
||||
})) |
||||
} |
||||
|
||||
module.exports = { |
||||
name: name, |
||||
description: desc, |
||||
category: categories.SECURITY, |
||||
algorithm: algo.EXACT, |
||||
Module: blockTimestamp |
||||
} |
||||
|
@ -0,0 +1,36 @@ |
||||
import { default as category } from './categories' |
||||
import { isNowAccess, isBlockTimestampAccess } from './staticAnalysisCommon' |
||||
import { default as algorithm } from './algorithmCategories' |
||||
|
||||
export class blockTimestamp { |
||||
warningNowNodes: any[] = [] |
||||
warningblockTimestampNodes: any[] = [] |
||||
name = 'Block timestamp: ' |
||||
desc = 'Semantics maybe unclear' |
||||
categories = category.SECURITY |
||||
algorithm = algorithm.EXACT |
||||
Module = this |
||||
|
||||
visit (node) { |
||||
if (isNowAccess(node)) this.warningNowNodes.push(node) |
||||
else if (isBlockTimestampAccess(node)) this.warningblockTimestampNodes.push(node) |
||||
} |
||||
|
||||
report (compilationResults) { |
||||
return this.warningNowNodes.map((item, i) => { |
||||
return { |
||||
warning: `use of "now": "now" does not mean current time. Now is an alias for block.timestamp.
|
||||
Block.timestamp can be influenced by miners to a certain degree, be careful.`,
|
||||
location: item.src, |
||||
more: 'http://solidity.readthedocs.io/en/develop/frequently-asked-questions.html#are-timestamps-now-block-timestamp-reliable' |
||||
} |
||||
}).concat(this.warningblockTimestampNodes.map((item, i) => { |
||||
return { |
||||
warning: `use of "block.timestamp": "block.timestamp" can be influenced by miners to a certain degree.
|
||||
That means that a miner can "choose" the block.timestamp, to a certain degree, to change the outcome of a transaction in the mined block.`,
|
||||
location: item.src, |
||||
more: 'http://solidity.readthedocs.io/en/develop/frequently-asked-questions.html#are-timestamps-now-block-timestamp-reliable' |
||||
} |
||||
})) |
||||
} |
||||
} |
@ -1,4 +1,4 @@ |
||||
module.exports = { |
||||
export default { |
||||
SECURITY: {displayName: 'Security', id: 'SEC'}, |
||||
GAS: {displayName: 'Gas & Economy', id: 'GAS'}, |
||||
MISC: {displayName: 'Miscellaneous', id: 'MISC'}, |
@ -1,31 +0,0 @@ |
||||
const name = 'Delete on dynamic Array: ' |
||||
const desc = 'Use require and appropriately' |
||||
const categories = require('./categories') |
||||
const common = require('./staticAnalysisCommon') |
||||
const algo = require('./algorithmCategories') |
||||
|
||||
function deleteDynamicArrays () { |
||||
this.rel = [] |
||||
} |
||||
|
||||
deleteDynamicArrays.prototype.visit = function (node) { |
||||
if (common.isDeleteOfDynamicArray(node)) this.rel.push(node) |
||||
} |
||||
|
||||
deleteDynamicArrays.prototype.report = function (compilationResults) { |
||||
return this.rel.map((node) => { |
||||
return { |
||||
warning: 'The “delete” operation when applied to a dynamically sized array in Solidity generates code to delete each of the elements contained. If the array is large, this operation can surpass the block gas limit and raise an OOG exception. Also nested dynamically sized objects can produce the same results.', |
||||
location: node.src, |
||||
more: 'http://solidity.readthedocs.io/en/latest/types.html?highlight=array#delete' |
||||
} |
||||
}) |
||||
} |
||||
|
||||
module.exports = { |
||||
name: name, |
||||
description: desc, |
||||
category: categories.GAS, |
||||
algorithm: algo.EXACT, |
||||
Module: deleteDynamicArrays |
||||
} |
@ -0,0 +1,26 @@ |
||||
import { default as category } from './categories' |
||||
import { isDeleteOfDynamicArray } from './staticAnalysisCommon' |
||||
import { default as algorithm } from './algorithmCategories' |
||||
|
||||
export class deleteDynamicArrays { |
||||
rel: any = [] |
||||
name = 'Delete on dynamic Array: ' |
||||
desc = 'Use require and appropriately' |
||||
categories = category.GAS |
||||
algorithm = algorithm.EXACT |
||||
Module = this |
||||
|
||||
visit (node) { |
||||
if (isDeleteOfDynamicArray(node)) this.rel.push(node) |
||||
} |
||||
|
||||
report (compilationResults) { |
||||
return this.rel.map((node) => { |
||||
return { |
||||
warning: 'The “delete” operation when applied to a dynamically sized array in Solidity generates code to delete each of the elements contained. If the array is large, this operation can surpass the block gas limit and raise an OOG exception. Also nested dynamically sized objects can produce the same results.', |
||||
location: node.src, |
||||
more: 'http://solidity.readthedocs.io/en/latest/types.html?highlight=array#delete' |
||||
} |
||||
}) |
||||
} |
||||
} |
@ -1,29 +0,0 @@ |
||||
const name = 'Delete from dynamic Array: ' |
||||
const desc = 'Using delete on an array leaves a gap' |
||||
const categories = require('./categories') |
||||
const common = require('./staticAnalysisCommon') |
||||
|
||||
function deleteFromDynamicArray () { |
||||
this.relevantNodes = [] |
||||
} |
||||
|
||||
deleteFromDynamicArray.prototype.visit = function (node) { |
||||
if (common.isDeleteFromDynamicArray(node) && !common.isMappingIndexAccess(node.children[0])) this.relevantNodes.push(node) |
||||
} |
||||
|
||||
deleteFromDynamicArray.prototype.report = function (compilationResults) { |
||||
return this.relevantNodes.map((node) => { |
||||
return { |
||||
warning: 'Using delete on an array leaves a gap. The length of the array remains the same. If you want to remove the empty position you need to shift items manually and update the length property.\n', |
||||
location: node.src, |
||||
more: 'https://github.com/miguelmota/solidity-idiosyncrasies' |
||||
} |
||||
}) |
||||
} |
||||
|
||||
module.exports = { |
||||
name: name, |
||||
description: desc, |
||||
category: categories.MISC, |
||||
Module: deleteFromDynamicArray |
||||
} |
@ -0,0 +1,24 @@ |
||||
import { default as category } from './categories' |
||||
import { isDeleteFromDynamicArray, isMappingIndexAccess } from './staticAnalysisCommon' |
||||
|
||||
export class deleteFromDynamicArray { |
||||
relevantNodes: any[] = [] |
||||
name = 'Delete from dynamic Array: ' |
||||
desc = 'Using delete on an array leaves a gap' |
||||
categories = category.MISC |
||||
Module = this |
||||
|
||||
visit (node) { |
||||
if (isDeleteFromDynamicArray(node) && !isMappingIndexAccess(node.children[0])) this.relevantNodes.push(node) |
||||
} |
||||
|
||||
report (compilationResults) { |
||||
return this.relevantNodes.map((node) => { |
||||
return { |
||||
warning: 'Using delete on an array leaves a gap. The length of the array remains the same. If you want to remove the empty position you need to shift items manually and update the length property.\n', |
||||
location: node.src, |
||||
more: 'https://github.com/miguelmota/solidity-idiosyncrasies' |
||||
} |
||||
}) |
||||
} |
||||
} |
@ -1,41 +0,0 @@ |
||||
const name = 'Ether transfer in a loop: ' |
||||
const desc = 'Avoid transferring Ether to multiple addresses in a loop' |
||||
const categories = require('./categories') |
||||
const common = require('./staticAnalysisCommon') |
||||
|
||||
function etherTransferInLoop () { |
||||
this.relevantNodes = [] |
||||
} |
||||
|
||||
etherTransferInLoop.prototype.visit = function (node) { |
||||
if (common.isLoop(node)) { |
||||
let transferNodes = [] |
||||
const loopBlockStartIndex = common.getLoopBlockStartIndex(node) |
||||
if (common.isBlock(node.children[loopBlockStartIndex])) { |
||||
transferNodes = node.children[loopBlockStartIndex].children |
||||
.filter(child => (common.isExpressionStatement(child) && |
||||
child.children[0].name === 'FunctionCall' && |
||||
common.isTransfer(child.children[0].children[0]))) |
||||
if (transferNodes.length > 0) { |
||||
this.relevantNodes.push(...transferNodes) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
etherTransferInLoop.prototype.report = function (compilationResults) { |
||||
return this.relevantNodes.map((node) => { |
||||
return { |
||||
warning: 'Ether payout should not be done in a loop: Due to the block gas limit, transactions can only consume a certain amount of gas. The number of iterations in a loop can grow beyond the block gas limit which can cause the complete contract to be stalled at a certain point. If required then make sure that number of iterations are low and you trust each address involved.', |
||||
location: node.src, |
||||
more: 'https://solidity.readthedocs.io/en/latest/security-considerations.html#gas-limit-and-loops' |
||||
} |
||||
}) |
||||
} |
||||
|
||||
module.exports = { |
||||
name: name, |
||||
description: desc, |
||||
category: categories.GAS, |
||||
Module: etherTransferInLoop |
||||
} |
@ -0,0 +1,36 @@ |
||||
import { default as category } from './categories' |
||||
import { isLoop, isBlock, getLoopBlockStartIndex, isExpressionStatement, isTransfer } from './staticAnalysisCommon' |
||||
|
||||
export class etherTransferInLoop { |
||||
relevantNodes: any[] = [] |
||||
name = 'Ether transfer in a loop: ' |
||||
desc = 'Avoid transferring Ether to multiple addresses in a loop' |
||||
category = category.GAS |
||||
Module = this |
||||
|
||||
visit (node) { |
||||
if (isLoop(node)) { |
||||
let transferNodes = [] |
||||
const loopBlockStartIndex = getLoopBlockStartIndex(node) |
||||
if (isBlock(node.children[loopBlockStartIndex])) { |
||||
transferNodes = node.children[loopBlockStartIndex].children |
||||
.filter(child => (isExpressionStatement(child) && |
||||
child.children[0].name === 'FunctionCall' && |
||||
isTransfer(child.children[0].children[0]))) |
||||
if (transferNodes.length > 0) { |
||||
this.relevantNodes.push(...transferNodes) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
report (compilationResults) { |
||||
return this.relevantNodes.map((node) => { |
||||
return { |
||||
warning: 'Ether payout should not be done in a loop: Due to the block gas limit, transactions can only consume a certain amount of gas. The number of iterations in a loop can grow beyond the block gas limit which can cause the complete contract to be stalled at a certain point. If required then make sure that number of iterations are low and you trust each address involved.', |
||||
location: node.src, |
||||
more: 'https://solidity.readthedocs.io/en/latest/security-considerations.html#gas-limit-and-loops' |
||||
} |
||||
}) |
||||
} |
||||
} |
@ -1,40 +0,0 @@ |
||||
const name = 'For loop iterates over dynamic array: ' |
||||
const desc = 'The number of \'for\' loop iterations depends on dynamic array\'s size' |
||||
const categories = require('./categories') |
||||
const { isForLoop, isDynamicArrayLengthAccess, isBinaryOperation } = require('./staticAnalysisCommon') |
||||
|
||||
function forLoopIteratesOverDynamicArray () { |
||||
this.relevantNodes = [] |
||||
} |
||||
|
||||
forLoopIteratesOverDynamicArray.prototype.visit = function (node) { |
||||
if (isForLoop(node)) { |
||||
// Access 'condition' node of 'for' loop statement
|
||||
const forLoopConditionNode = node.children[1] |
||||
// Access right side of condition as its children
|
||||
const conditionChildrenNode = forLoopConditionNode.children[1] |
||||
// Check if it is a binary operation. if yes, check if its children node access length of dynamic array
|
||||
if (isBinaryOperation(conditionChildrenNode) && isDynamicArrayLengthAccess(conditionChildrenNode.children[0])) { |
||||
this.relevantNodes.push(node) |
||||
} else if (isDynamicArrayLengthAccess(conditionChildrenNode)) { // else check if condition node itself access length of dynamic array
|
||||
this.relevantNodes.push(node) |
||||
} |
||||
} |
||||
} |
||||
|
||||
forLoopIteratesOverDynamicArray.prototype.report = function (compilationResults) { |
||||
return this.relevantNodes.map((node) => { |
||||
return { |
||||
warning: 'Loops that do not have a fixed number of iterations, for example, loops that depend on storage values, have to be used carefully: Due to the block gas limit, transactions can only consume a certain amount of gas. The number of iterations in a loop can grow beyond the block gas limit which can cause the complete contract to be stalled at a certain point. Additionally, using unbounded loops incurs in a lot of avoidable gas costs. Carefully test how many items at maximum you can pass to such functions to make it successful.', |
||||
location: node.src, |
||||
more: 'http://solidity.readthedocs.io/en/v0.4.24/security-considerations.html#gas-limit-and-loops' |
||||
} |
||||
}) |
||||
} |
||||
|
||||
module.exports = { |
||||
name: name, |
||||
description: desc, |
||||
category: categories.GAS, |
||||
Module: forLoopIteratesOverDynamicArray |
||||
} |
@ -0,0 +1,35 @@ |
||||
import { default as category } from './categories' |
||||
const { isForLoop, isDynamicArrayLengthAccess, isBinaryOperation } = require('./staticAnalysisCommon') |
||||
|
||||
export class forLoopIteratesOverDynamicArray { |
||||
relevantNodes: any[] = [] |
||||
name = 'For loop iterates over dynamic array: ' |
||||
desc = 'The number of \'for\' loop iterations depends on dynamic array\'s size' |
||||
categories = category.GAS |
||||
Module = this |
||||
|
||||
visit (node) { |
||||
if (isForLoop(node)) { |
||||
// Access 'condition' node of 'for' loop statement
|
||||
const forLoopConditionNode = node.children[1] |
||||
// Access right side of condition as its children
|
||||
const conditionChildrenNode = forLoopConditionNode.children[1] |
||||
// Check if it is a binary operation. if yes, check if its children node access length of dynamic array
|
||||
if (isBinaryOperation(conditionChildrenNode) && isDynamicArrayLengthAccess(conditionChildrenNode.children[0])) { |
||||
this.relevantNodes.push(node) |
||||
} else if (isDynamicArrayLengthAccess(conditionChildrenNode)) { // else check if condition node itself access length of dynamic array
|
||||
this.relevantNodes.push(node) |
||||
} |
||||
} |
||||
} |
||||
|
||||
report (compilationResults) { |
||||
return this.relevantNodes.map((node) => { |
||||
return { |
||||
warning: 'Loops that do not have a fixed number of iterations, for example, loops that depend on storage values, have to be used carefully: Due to the block gas limit, transactions can only consume a certain amount of gas. The number of iterations in a loop can grow beyond the block gas limit which can cause the complete contract to be stalled at a certain point. Additionally, using unbounded loops incurs in a lot of avoidable gas costs. Carefully test how many items at maximum you can pass to such functions to make it successful.', |
||||
location: node.src, |
||||
more: 'http://solidity.readthedocs.io/en/v0.4.24/security-considerations.html#gas-limit-and-loops' |
||||
} |
||||
}) |
||||
} |
||||
} |
@ -1,31 +0,0 @@ |
||||
const name = 'Guard Conditions: ' |
||||
const desc = 'Use require and appropriately' |
||||
const categories = require('./categories') |
||||
const common = require('./staticAnalysisCommon') |
||||
const algo = require('./algorithmCategories') |
||||
|
||||
function guardConditions () { |
||||
this.guards = [] |
||||
} |
||||
|
||||
guardConditions.prototype.visit = function (node) { |
||||
if (common.isRequireCall(node) || common.isAssertCall(node)) this.guards.push(node) |
||||
} |
||||
|
||||
guardConditions.prototype.report = function (compilationResults) { |
||||
if (this.guards.length > 0) { |
||||
return [{ |
||||
warning: 'Use assert(x) if you never ever want x to be false, not in any circumstance (apart from a bug in your code). Use require(x) if x can be false, due to e.g. invalid input or a failing external component.', |
||||
more: 'http://solidity.readthedocs.io/en/develop/control-structures.html#error-handling-assert-require-revert-and-exceptions' |
||||
}] |
||||
} |
||||
return [] |
||||
} |
||||
|
||||
module.exports = { |
||||
name: name, |
||||
description: desc, |
||||
category: categories.MISC, |
||||
algorithm: algo.EXACT, |
||||
Module: guardConditions |
||||
} |
@ -0,0 +1,26 @@ |
||||
import { default as category } from './categories' |
||||
import { isRequireCall, isAssertCall } from './staticAnalysisCommon' |
||||
import { default as algorithm } from './algorithmCategories' |
||||
|
||||
export class guardConditions { |
||||
guards: any[] = [] |
||||
name = 'Guard Conditions: ' |
||||
desc = 'Use require and appropriately' |
||||
categories = category.MISC |
||||
algorithm = algorithm.EXACT |
||||
Module = this |
||||
|
||||
visit (node) { |
||||
if (isRequireCall(node) || isAssertCall(node)) this.guards.push(node) |
||||
} |
||||
|
||||
report (compilationResults) { |
||||
if (this.guards.length > 0) { |
||||
return [{ |
||||
warning: 'Use assert(x) if you never ever want x to be false, not in any circumstance (apart from a bug in your code). Use require(x) if x can be false, due to e.g. invalid input or a failing external component.', |
||||
more: 'http://solidity.readthedocs.io/en/develop/control-structures.html#error-handling-assert-require-revert-and-exceptions' |
||||
}] |
||||
} |
||||
return [] |
||||
} |
||||
} |
@ -1,32 +0,0 @@ |
||||
const name = 'Inline assembly: ' |
||||
const desc = 'Use of Inline Assembly' |
||||
const categories = require('./categories') |
||||
const common = require('./staticAnalysisCommon') |
||||
const algo = require('./algorithmCategories') |
||||
|
||||
function inlineAssembly () { |
||||
this.inlineAssNodes = [] |
||||
} |
||||
|
||||
inlineAssembly.prototype.visit = function (node) { |
||||
if (common.isInlineAssembly(node)) this.inlineAssNodes.push(node) |
||||
} |
||||
|
||||
inlineAssembly.prototype.report = function (compilationResults) { |
||||
return this.inlineAssNodes.map((node) => { |
||||
return { |
||||
warning: `CAUTION: The Contract uses inline assembly, this is only advised in rare cases.
|
||||
Additionally static analysis modules do not parse inline Assembly, this can lead to wrong analysis results.`,
|
||||
location: node.src, |
||||
more: 'http://solidity.readthedocs.io/en/develop/assembly.html#solidity-assembly' |
||||
} |
||||
}) |
||||
} |
||||
|
||||
module.exports = { |
||||
name: name, |
||||
description: desc, |
||||
category: categories.SECURITY, |
||||
algorithm: algo.EXACT, |
||||
Module: inlineAssembly |
||||
} |
@ -0,0 +1,27 @@ |
||||
import { default as category } from './categories' |
||||
import { isInlineAssembly } from './staticAnalysisCommon' |
||||
import { default as algorithm } from './algorithmCategories' |
||||
|
||||
export class inlineAssembly { |
||||
inlineAssNodes: any[] = [] |
||||
name = 'Inline assembly: ' |
||||
desc = 'Use of Inline Assembly' |
||||
categories = category.SECURITY |
||||
algorithm = algorithm.EXACT |
||||
Module = this |
||||
|
||||
visit (node) { |
||||
if (isInlineAssembly(node)) this.inlineAssNodes.push(node) |
||||
} |
||||
|
||||
report (compilationResults) { |
||||
return this.inlineAssNodes.map((node) => { |
||||
return { |
||||
warning: `CAUTION: The Contract uses inline assembly, this is only advised in rare cases.
|
||||
Additionally static analysis modules do not parse inline Assembly, this can lead to wrong analysis results.`,
|
||||
location: node.src, |
||||
more: 'http://solidity.readthedocs.io/en/develop/assembly.html#solidity-assembly' |
||||
} |
||||
}) |
||||
} |
||||
} |
@ -1,30 +0,0 @@ |
||||
const name = 'Data Trucated: ' |
||||
const desc = 'Division on int/uint values truncates the result.' |
||||
const categories = require('./categories') |
||||
const common = require('./staticAnalysisCommon') |
||||
const algo = require('./algorithmCategories') |
||||
|
||||
function intDivitionTruncate () { |
||||
this.warningNodes = [] |
||||
} |
||||
|
||||
intDivitionTruncate.prototype.visit = function (node) { |
||||
if (common.isIntDivision(node)) this.warningNodes.push(node) |
||||
} |
||||
|
||||
intDivitionTruncate.prototype.report = function (compilationResults) { |
||||
return this.warningNodes.map((item, i) => { |
||||
return { |
||||
warning: 'Division of integer values yields an integer value again. That means e.g. 10 / 100 = 0 instead of 0.1 since the result is an integer again. This does not hold for division of (only) literal values since those yield rational constants.', |
||||
location: item.src |
||||
} |
||||
}) |
||||
} |
||||
|
||||
module.exports = { |
||||
name: name, |
||||
description: desc, |
||||
category: categories.MISC, |
||||
algorithm: algo.EXACT, |
||||
Module: intDivitionTruncate |
||||
} |
@ -0,0 +1,25 @@ |
||||
import { default as category } from './categories' |
||||
import { isIntDivision } from './staticAnalysisCommon' |
||||
import { default as algorithm } from './algorithmCategories' |
||||
|
||||
export class intDivitionTruncate { |
||||
warningNodes: any[] = [] |
||||
name = 'Data Trucated: ' |
||||
desc = 'Division on int/uint values truncates the result.' |
||||
categories = category.MISC |
||||
algorithm = algorithm.EXACT |
||||
Module = this |
||||
|
||||
visit (node) { |
||||
if (isIntDivision(node)) this.warningNodes.push(node) |
||||
} |
||||
|
||||
report (compilationResults) { |
||||
return this.warningNodes.map((item, i) => { |
||||
return { |
||||
warning: 'Division of integer values yields an integer value again. That means e.g. 10 / 100 = 0 instead of 0.1 since the result is an integer again. This does not hold for division of (only) literal values since those yield rational constants.', |
||||
location: item.src |
||||
} |
||||
}) |
||||
} |
||||
} |
@ -1,72 +0,0 @@ |
||||
const name = 'Low level calls: ' |
||||
const desc = 'Semantics maybe unclear' |
||||
const categories = require('./categories') |
||||
const common = require('./staticAnalysisCommon') |
||||
const algo = require('./algorithmCategories') |
||||
|
||||
function lowLevelCalls () { |
||||
this.llcNodes = [] |
||||
} |
||||
|
||||
lowLevelCalls.prototype.visit = function (node) { |
||||
if (common.isLowLevelCallInst(node)) { |
||||
this.llcNodes.push({node: node, type: common.lowLevelCallTypes.CALL}) |
||||
} else if (common.isLowLevelCallInst050(node)) { |
||||
this.llcNodes.push({node: node, type: common.lowLevelCallTypes.CALL}) |
||||
} else if (common.isLowLevelCallcodeInst(node)) { |
||||
this.llcNodes.push({node: node, type: common.lowLevelCallTypes.CALLCODE}) |
||||
} else if (common.isLowLevelDelegatecallInst(node)) { |
||||
this.llcNodes.push({node: node, type: common.lowLevelCallTypes.DELEGATECALL}) |
||||
} else if (common.isLowLevelSendInst(node)) { |
||||
this.llcNodes.push({node: node, type: common.lowLevelCallTypes.SEND}) |
||||
} else if (common.isLowLevelSendInst050(node)) { |
||||
this.llcNodes.push({node: node, type: common.lowLevelCallTypes.SEND}) |
||||
} else if (common.isLLDelegatecallInst050(node)) { |
||||
this.llcNodes.push({node: node, type: common.lowLevelCallTypes.DELEGATECALL}) |
||||
} |
||||
} |
||||
|
||||
lowLevelCalls.prototype.report = function (compilationResults) { |
||||
return this.llcNodes.map((item, i) => { |
||||
let text = '' |
||||
let morehref = null |
||||
switch (item.type) { |
||||
case common.lowLevelCallTypes.CALL: |
||||
text = `use of "call": the use of low level "call" should be avoided whenever possible.
|
||||
It can lead to unexpected behavior if return value is not handled properly.
|
||||
Please use Direct Calls via specifying the called contract's interface.` |
||||
morehref = 'http://solidity.readthedocs.io/en/develop/control-structures.html?#external-function-calls' |
||||
// http://solidity.readthedocs.io/en/develop/frequently-asked-questions.html?#why-is-the-low-level-function-call-less-favorable-than-instantiating-a-contract-with-a-variable-contractb-b-and-executing-its-functions-b-dosomething
|
||||
break |
||||
case common.lowLevelCallTypes.CALLCODE: |
||||
text = `use of "callcode": the use of low level "callcode" should be avoided whenever possible.
|
||||
External code that is called can change the state of the calling contract and send ether from the caller's balance.
|
||||
If this is wanted behaviour use the Solidity library feature if possible.` |
||||
morehref = 'http://solidity.readthedocs.io/en/develop/contracts.html#libraries' |
||||
break |
||||
case common.lowLevelCallTypes.DELEGATECALL: |
||||
text = `use of "delegatecall": the use of low level "delegatecall" should be avoided whenever possible.
|
||||
External code that is called can change the state of the calling contract and send ether from the caller's balance.
|
||||
If this is wanted behaviour use the Solidity library feature if possible.` |
||||
morehref = 'http://solidity.readthedocs.io/en/develop/contracts.html#libraries' |
||||
break |
||||
case common.lowLevelCallTypes.SEND: |
||||
text = `use of "send": "send" does not throw an exception when not successful, make sure you deal with the failure case accordingly.
|
||||
Use "transfer" whenever failure of the ether transfer should rollback the whole transaction.
|
||||
Note: if you "send/transfer" ether to a contract the fallback function is called, the callees fallback function is very limited due to the limited amount of gas provided by "send/transfer".
|
||||
No state changes are possible but the callee can log the event or revert the transfer. "send/transfer" is syntactic sugar for a "call" to the fallback function with 2300 gas and a specified ether value.` |
||||
morehref = 'http://solidity.readthedocs.io/en/develop/security-considerations.html#sending-and-receiving-ether' |
||||
break |
||||
} |
||||
return { warning: text, more: morehref, location: item.node.src } |
||||
}) |
||||
} |
||||
|
||||
module.exports = { |
||||
name: name, |
||||
description: desc, |
||||
category: categories.SECURITY, |
||||
algorithm: algo.EXACT, |
||||
Module: lowLevelCalls |
||||
} |
||||
|
@ -0,0 +1,68 @@ |
||||
import { default as category } from './categories' |
||||
import { isLowLevelCallInst, isLowLevelCallInst050, isLowLevelCallcodeInst, isLowLevelDelegatecallInst, |
||||
isLowLevelSendInst, isLowLevelSendInst050, isLLDelegatecallInst050, lowLevelCallTypes } from './staticAnalysisCommon' |
||||
import { default as algorithm } from './algorithmCategories' |
||||
|
||||
export class lowLevelCalls { |
||||
llcNodes: any[] = [] |
||||
name = 'Low level calls: ' |
||||
desc = 'Semantics maybe unclear' |
||||
categories = category.SECURITY |
||||
algorithm = algorithm.EXACT |
||||
Module = this |
||||
|
||||
visit (node) { |
||||
if (isLowLevelCallInst(node)) { |
||||
this.llcNodes.push({node: node, type: lowLevelCallTypes.CALL}) |
||||
} else if (isLowLevelCallInst050(node)) { |
||||
this.llcNodes.push({node: node, type: lowLevelCallTypes.CALL}) |
||||
} else if (isLowLevelCallcodeInst(node)) { |
||||
this.llcNodes.push({node: node, type: lowLevelCallTypes.CALLCODE}) |
||||
} else if (isLowLevelDelegatecallInst(node)) { |
||||
this.llcNodes.push({node: node, type: lowLevelCallTypes.DELEGATECALL}) |
||||
} else if (isLowLevelSendInst(node)) { |
||||
this.llcNodes.push({node: node, type: lowLevelCallTypes.SEND}) |
||||
} else if (isLowLevelSendInst050(node)) { |
||||
this.llcNodes.push({node: node, type: lowLevelCallTypes.SEND}) |
||||
} else if (isLLDelegatecallInst050(node)) { |
||||
this.llcNodes.push({node: node, type: lowLevelCallTypes.DELEGATECALL}) |
||||
} |
||||
} |
||||
|
||||
report (compilationResults) { |
||||
return this.llcNodes.map((item, i) => { |
||||
let text = '' |
||||
let morehref: any = null |
||||
switch (item.type) { |
||||
case lowLevelCallTypes.CALL: |
||||
text = `use of "call": the use of low level "call" should be avoided whenever possible.
|
||||
It can lead to unexpected behavior if return value is not handled properly.
|
||||
Please use Direct Calls via specifying the called contract's interface.` |
||||
morehref = 'http://solidity.readthedocs.io/en/develop/control-structures.html?#external-function-calls' |
||||
// http://solidity.readthedocs.io/en/develop/frequently-asked-questions.html?#why-is-the-low-level-function-call-less-favorable-than-instantiating-a-contract-with-a-variable-contractb-b-and-executing-its-functions-b-dosomething
|
||||
break |
||||
case lowLevelCallTypes.CALLCODE: |
||||
text = `use of "callcode": the use of low level "callcode" should be avoided whenever possible.
|
||||
External code that is called can change the state of the calling contract and send ether from the caller's balance.
|
||||
If this is wanted behaviour use the Solidity library feature if possible.` |
||||
morehref = 'http://solidity.readthedocs.io/en/develop/contracts.html#libraries' |
||||
break |
||||
case lowLevelCallTypes.DELEGATECALL: |
||||
text = `use of "delegatecall": the use of low level "delegatecall" should be avoided whenever possible.
|
||||
External code that is called can change the state of the calling contract and send ether from the caller's balance.
|
||||
If this is wanted behaviour use the Solidity library feature if possible.` |
||||
morehref = 'http://solidity.readthedocs.io/en/develop/contracts.html#libraries' |
||||
break |
||||
case lowLevelCallTypes.SEND: |
||||
text = `use of "send": "send" does not throw an exception when not successful, make sure you deal with the failure case accordingly.
|
||||
Use "transfer" whenever failure of the ether transfer should rollback the whole transaction.
|
||||
Note: if you "send/transfer" ether to a contract the fallback function is called, the callees fallback function is very limited due to the limited amount of gas provided by "send/transfer".
|
||||
No state changes are possible but the callee can log the event or revert the transfer. "send/transfer" is syntactic sugar for a "call" to the fallback function with 2300 gas and a specified ether value.` |
||||
morehref = 'http://solidity.readthedocs.io/en/develop/security-considerations.html#sending-and-receiving-ether' |
||||
break |
||||
} |
||||
return { warning: text, more: morehref, location: item.node.src } |
||||
}) |
||||
} |
||||
} |
||||
|
Loading…
Reference in new issue