From 31fa63e2579be600f337fe5a23c5cf6e530fbd05 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 3 Nov 2016 16:02:40 +0100 Subject: [PATCH 1/4] add staticAnalysis runner, viewer --- .../staticanalysis/staticAnalysisRunner.js | 30 +++++++ src/app/staticanalysis/staticAnalysisView.js | 79 +++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 src/app/staticanalysis/staticAnalysisRunner.js create mode 100644 src/app/staticanalysis/staticAnalysisView.js diff --git a/src/app/staticanalysis/staticAnalysisRunner.js b/src/app/staticanalysis/staticAnalysisRunner.js new file mode 100644 index 0000000000..f7edbd8a5d --- /dev/null +++ b/src/app/staticanalysis/staticAnalysisRunner.js @@ -0,0 +1,30 @@ +'use strict' +var AstWalker = require('ethereum-remix').util.AstWalker +var list = require('./modules/list') + +function staticAnalysisRunner () { +} + +staticAnalysisRunner.prototype.run = function (ast, toRun, callback) { + var walker = new AstWalker() + for (var k in ast) { + walker.walk(ast[k].AST, {'*': function (node) { + toRun.map(function (item, i) { + item.visit(node) + }) + return true + }}) + } + + var reports = [] + toRun.map(function (item, i) { + reports.push(item.report()) + }) + callback(reports) +} + +staticAnalysisRunner.prototype.modules = function () { + return list +} + +module.exports = staticAnalysisRunner diff --git a/src/app/staticanalysis/staticAnalysisView.js b/src/app/staticanalysis/staticAnalysisView.js new file mode 100644 index 0000000000..1d891e28b6 --- /dev/null +++ b/src/app/staticanalysis/staticAnalysisView.js @@ -0,0 +1,79 @@ +'use strict' +var StaticAnalysisRunner = require('./staticAnalysisRunner.js') +var yo = require('yo-yo') +var $ = require('jquery') + +function staticAnalysisView (compiler, renderer) { + this.view = null + this.renderer = renderer + this.runner = new StaticAnalysisRunner() + this.modulesView = renderModules(this.runner.modules()) + this.lastASTs = null + var self = this + compiler.event.register('compilationFinished', function (success, data, source) { + self.lastASTs = null + if (success) { + self.lastASTs = data.sources + } + }) +} + +staticAnalysisView.prototype.render = function () { + var self = this + var view = yo`
+ Static Analysis +
Select analyser to run against current compiled contracts
+ ${this.modulesView} +
+ +
+
+
` + if (!this.view) { + this.view = view + } + return view +} + +staticAnalysisView.prototype.selectedModules = function () { + if (!this.view) { + return [] + } + var selected = this.view.querySelectorAll('[name="staticanalysismodule"]') + var toRun = [] + for (var i = 0; i < selected.length; i++) { + var el = selected[i] + if (el.checked) { + var analyser = this.runner.modules()[el.attributes['index'].value] + toRun.push(new analyser.Module()) + } + } + return toRun +} + +staticAnalysisView.prototype.run = function () { + if (!this.view) { + return + } + var selected = this.selectedModules() + var warningContainer = $('#staticanalysisresult') + warningContainer.html('') + if (this.lastASTs) { + var self = this + this.runner.run(this.lastASTs, selected, function (results) { + results.map(function (item, i) { + self.renderer.error(item.name + ':\n\n' + item.report, warningContainer, null, 'warning') + }) + }) + } else { + warningContainer.html('No compiled AST available') + } +} + +function renderModules (modules) { + return modules.map(function (item, i) { + return yo`
${item.name} (${item.description})
` + }) +} + +module.exports = staticAnalysisView From 9bde45fd63dfdb37dba1d848150547cb13e28909 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 3 Nov 2016 16:03:46 +0100 Subject: [PATCH 2/4] add modules list and sample module --- src/app/staticanalysis/modules/list.js | 3 ++ src/app/staticanalysis/modules/txOrigin.js | 34 ++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 src/app/staticanalysis/modules/list.js create mode 100644 src/app/staticanalysis/modules/txOrigin.js diff --git a/src/app/staticanalysis/modules/list.js b/src/app/staticanalysis/modules/list.js new file mode 100644 index 0000000000..ddd37957f7 --- /dev/null +++ b/src/app/staticanalysis/modules/list.js @@ -0,0 +1,3 @@ +module.exports = [ + require('./txOrigin') +] diff --git a/src/app/staticanalysis/modules/txOrigin.js b/src/app/staticanalysis/modules/txOrigin.js new file mode 100644 index 0000000000..ded49f2b02 --- /dev/null +++ b/src/app/staticanalysis/modules/txOrigin.js @@ -0,0 +1,34 @@ +var name = 'tx origin' +var desc = 'warn if tx.origin is used' + +function txOrigin () { + this.txOriginNode = [] +} + +txOrigin.prototype.visit = function (node) { + if (node.name === 'MemberAccess' && + node.attributes.member_name === 'origin' && + node.attributes.type === 'address' && + node.children && node.children.length && + node.children[0].attributes.type === 'tx' && + node.children[0].attributes.value === 'tx') { + this.txOriginNode.push(node) + } +} + +txOrigin.prototype.report = function (node) { + var report = this.txOriginNode.length + ' use of tx.origin\n' + this.txOriginNode.map(function (item, i) { + report += item.src + '\n' + }) + return { + name: name, + report: report + } +} + +module.exports = { + name: name, + description: desc, + Module: txOrigin +} From 264163403ea0ffd9642b583cac35a575afe9d5e6 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 3 Nov 2016 16:04:52 +0100 Subject: [PATCH 3/4] integrate to app, renderer --- assets/css/browser-solidity.css | 17 +++++++++++++++++ index.html | 3 +++ src/app.js | 6 +++++- src/app/renderer.js | 16 +++++++++------- src/app/staticanalysis/staticAnalysisView.js | 2 +- 5 files changed, 35 insertions(+), 9 deletions(-) diff --git a/assets/css/browser-solidity.css b/assets/css/browser-solidity.css index f0d2740fa9..89e800df62 100644 --- a/assets/css/browser-solidity.css +++ b/assets/css/browser-solidity.css @@ -228,6 +228,10 @@ body { display: block; } +#header #optionViews.staticanalysisView #staticanalysisView { + display: block; +} + #header #optionViews.txView input, #header #optionViews.txView select { max-width: 13em; @@ -283,6 +287,19 @@ body { cursor: pointer; } +#staticanalysisView button { + background-color: #C6CFF7; + font-size: 12px; + padding: 0.25em; + margin-bottom: .5em; + color: black; + border:0 none; + border-radius: 3px; + width: 8em; + margin-right: 1em; + cursor: pointer; +} + #header .origin, #header #executionContext { display: block; diff --git a/index.html b/index.html index 3a47efc214..cb5b0ee2bd 100644 --- a/index.html +++ b/index.html @@ -61,6 +61,7 @@
  • +
  • Solidity realtime compiler and runtime @@ -123,6 +124,8 @@
    +
    +

    This tab provides support for formal verification of Solidity contracts.
    This feature is still in development and thus also not yet well documented, diff --git a/src/app.js b/src/app.js index a806b350af..9e7e92ec43 100644 --- a/src/app.js +++ b/src/app.js @@ -19,6 +19,7 @@ var UniversalDApp = require('./universal-dapp.js') var Debugger = require('./app/debugger') var FormalVerification = require('./app/formalVerification') var EventManager = require('./lib/eventManager') +var StaticAnalysis = require('./app/staticanalysis/staticAnalysisView') // The event listener needs to be registered as early as possible, because the // parent will send the message upon the "load" event. @@ -444,6 +445,9 @@ var run = function () { var renderer = new Renderer(editor, executionContext.web3(), updateFiles, udapp, executionContext, formalVerification.event, compiler.event) // eslint-disable-line + var staticanalysis = new StaticAnalysis(compiler, renderer) + $('#staticanalysisView').append(staticanalysis.render()) + var autoCompile = document.querySelector('#autoCompile').checked document.querySelector('#autoCompile').addEventListener('change', function () { @@ -609,7 +613,7 @@ var run = function () { selectedVersion = queryParams.get().version } - loadVersion(selectedVersion) + loadVersion(selectedVersion) }).fail(function (xhr, text, err) { // loading failed for some reason, fall back to local compiler $('#versionSelector').append(new Option('latest local version', 'builtin')) diff --git a/src/app/renderer.js b/src/app/renderer.js index 2e81d1cfa2..1e0464faa0 100644 --- a/src/app/renderer.js +++ b/src/app/renderer.js @@ -35,9 +35,11 @@ function Renderer (editor, web3, updateFiles, udapp, executionContext, formalVer }) } -Renderer.prototype.error = function (message, container, noAnnotations) { +Renderer.prototype.error = function (message, container, noAnnotations, type) { var self = this - var type = utils.errortype(message) + if (!type) { + type = utils.errortype(message) + } var $pre = $('

    ').text(message)
       var $error = $('
    ').prepend($pre) if (container === undefined) { @@ -66,12 +68,12 @@ Renderer.prototype.error = function (message, container, noAnnotations) { } self.editor.handleErrorClick(errLine, errCol) }) - $error.find('.close').click(function (ev) { - ev.preventDefault() - $error.remove() - return false - }) } + $error.find('.close').click(function (ev) { + ev.preventDefault() + $error.remove() + return false + }) } Renderer.prototype.contracts = function (data, source) { diff --git a/src/app/staticanalysis/staticAnalysisView.js b/src/app/staticanalysis/staticAnalysisView.js index 1d891e28b6..b6be01dc18 100644 --- a/src/app/staticanalysis/staticAnalysisView.js +++ b/src/app/staticanalysis/staticAnalysisView.js @@ -57,7 +57,7 @@ staticAnalysisView.prototype.run = function () { } var selected = this.selectedModules() var warningContainer = $('#staticanalysisresult') - warningContainer.html('') + warningContainer.empty() if (this.lastASTs) { var self = this this.runner.run(this.lastASTs, selected, function (results) { From 6f3c6342c7d7fd9e5e78ac999261700bbb5b9713 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 7 Nov 2016 14:57:03 +0100 Subject: [PATCH 4/4] add test --- src/app.js | 2 +- test-browser/tests/staticanalysis.js | 36 ++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 test-browser/tests/staticanalysis.js diff --git a/src/app.js b/src/app.js index 9e7e92ec43..da1b234e69 100644 --- a/src/app.js +++ b/src/app.js @@ -613,7 +613,7 @@ var run = function () { selectedVersion = queryParams.get().version } - loadVersion(selectedVersion) + loadVersion(selectedVersion) }).fail(function (xhr, text, err) { // loading failed for some reason, fall back to local compiler $('#versionSelector').append(new Option('latest local version', 'builtin')) diff --git a/test-browser/tests/staticanalysis.js b/test-browser/tests/staticanalysis.js new file mode 100644 index 0000000000..12c6144b79 --- /dev/null +++ b/test-browser/tests/staticanalysis.js @@ -0,0 +1,36 @@ +'use strict' +var contractHelper = require('../helpers/contracts') +var init = require('../helpers/init') +var sauce = require('./sauce') + +var sources = { + 'sources': { + 'Untitled': `contract test1 { address test = tx.origin; } contract test2 {}` + } +} + +module.exports = { + before: function (browser, done) { + init(browser, done) + }, + '@sources': function () { + return sources + }, + 'Static Analysis': function (browser) { + runTests(browser) + }, + tearDown: sauce +} + +function runTests (browser) { + browser + .waitForElementVisible('.newFile', 10000) + contractHelper.testContracts(browser, sources.sources.Untitled, ['test1', 'test2'], function () { + browser + .click('.staticanalysisView') + .click('#staticanalysisView button') + .waitForElementPresent('#staticanalysisresult .warning') + .assert.containsText('#staticanalysisresult .warning pre', '1 use of tx.origin') + .end() + }) +}