embark-mythx/mythx.js

217 lines
7.4 KiB
JavaScript
Raw Normal View History

2019-04-15 08:11:43 +00:00
require('dotenv').config()
const armlet = require('armlet')
const fs = require('fs')
const mythXUtil = require('./lib/mythXUtil');
const asyncPool = require('tiny-async-pool');
2019-04-22 15:47:01 +00:00
const { MythXIssues, doReport } = require('./lib/issues2eslint');
2019-04-15 08:11:43 +00:00
const defaultAnalyzeRateLimit = 4
async function analyse(contracts, cfg, embark) {
2019-04-15 08:11:43 +00:00
//embark.logger.debug("embark.config", embark.config)
2019-04-22 15:47:01 +00:00
//console.log("embark.logger", embark.logger)
//console.log("JSON.stringify(embark.logger)", JSON.stringify(embark.logger))
//embark.logger.info("typeof embark.logger", typeof embark.logger)
2019-04-15 08:11:43 +00:00
cfg.logger = embark.logger
//embark.logger.info("embark", JSON.stringify(embark))
// Set analysis parameters
const limit = cfg.limit || defaultAnalyzeRateLimit
if (isNaN(limit)) {
2019-04-22 15:47:01 +00:00
embark.logger.info(`limit parameter should be a number; got ${limit}.`)
2019-04-15 08:11:43 +00:00
return 1
}
if (limit < 0 || limit > defaultAnalyzeRateLimit) {
2019-04-22 15:47:01 +00:00
embark.logger.info(`limit should be between 0 and ${defaultAnalyzeRateLimit}; got ${limit}.`)
2019-04-15 08:11:43 +00:00
return 1
}
// Connect to MythX via armlet
const armletClient = new armlet.Client(
{
clientToolName: "embark-mythx",
password: process.env.MYTHX_PASSWORD,
ethAddress: process.env.MYTHX_ETH_ADDRESS,
})
//Check contract names provided in options are respected
2019-04-22 15:47:01 +00:00
//embark.logger.info("contracts", contracts)
//embark.logger.info("cfg.contracts", cfg.contracts)
// Filter contracts based on parameter choice
let toSubmit = {};
if(cfg.contracts) {
toSubmit.sources = contracts.sources
toSubmit.contracts = {}
for (let [filename, contractObjects] of Object.entries(contracts.contracts)) {
for (let [contractName, contract] of Object.entries(contractObjects)) {
if (cfg.contracts.indexOf(contractName) >= 0) {
//console.log("Adding to submit", contractName, contractObjects)
if(!toSubmit.contracts[filename]) {
toSubmit.contracts[filename] = {}
}
toSubmit.contracts[filename][contractName] = contract ;
}
}
}
} else {
toSubmit = contracts
}
//embark.logger.info("toSubmit", toSubmit)
const submitObjects = mythXUtil.buildRequestData(toSubmit)
2019-04-15 08:11:43 +00:00
//return 0
2019-04-22 15:47:01 +00:00
const { objects, errors } = await doAnalysis(armletClient, cfg, submitObjects, null, limit)
2019-04-15 08:11:43 +00:00
//console.log("objects", JSON.stringify(objects))
2019-04-22 15:47:01 +00:00
//embark.logger.info("errors", errors)
2019-04-15 08:11:43 +00:00
2019-04-22 15:47:01 +00:00
const result = doReport(cfg, objects, errors)
//embark.logger.info("result", result)
2019-04-15 08:11:43 +00:00
return result
}
async function getStatus(cfg, embark) {
//embark.logger.debug("embark.config", embark.config)
//console.log("embark.logger", embark.logger)
//console.log("JSON.stringify(embark.logger)", JSON.stringify(embark.logger))
//embark.logger.info("typeof embark.logger", typeof embark.logger)
cfg.logger = embark.logger
//embark.logger.info("embark", JSON.stringify(embark))
// Connect to MythX via armlet
const armletClient = new armlet.Client(
{
clientToolName: "embark-mythx",
password: process.env.MYTHX_PASSWORD,
ethAddress: process.env.MYTHX_ETH_ADDRESS,
})
if (cfg.uuid) {
try {
const results = await armletClient.getIssues(config.uuid);
return ghettoReport(embark.logger.info, results);
} catch (err) {
embark.logger.warn(err);
return 1;
}
}
}
2019-04-15 08:11:43 +00:00
const doAnalysis = async (armletClient, config, contracts, contractNames = null, limit) => {
2019-04-22 15:47:01 +00:00
//config.logger.info("\ncontracts", contracts)
2019-04-15 08:11:43 +00:00
const timeout = (config.timeout || 300) * 1000;
const initialDelay = ('initial-delay' in config) ? config['initial-delay'] * 1000 : undefined;
const cacheLookup = ('cache-lookup' in config) ? config['cache-lookup'] : true;
2019-04-15 08:11:43 +00:00
const results = await asyncPool(limit, contracts, async buildObj => {
const obj = new MythXIssues(buildObj, config);
let analyzeOpts = {
clientToolName: 'embark-mythx',
noCacheLookup: !cacheLookup,
timeout,
initialDelay
};
analyzeOpts.data = mythXUtil.cleanAnalyzeDataEmptyProps(obj.buildObj, config.debug, config.logger.debug);
analyzeOpts.data.analysisMode = analyzeOpts.mode || 'quick';
if (config.debug > 1) {
config.logger.debug("analyzeOpts: " + `${util.inspect(analyzeOpts, {depth: null})}`);
}
// request analysis to armlet.
try {
config.logger.info("Submitting '" + obj.contractName + "' for analysis...")
2019-04-22 15:47:01 +00:00
const armletResult = await armletClient.analyzeWithStatus(analyzeOpts);
//config.logger.info("armletResult", JSON.stringify(armletResult))
const {issues, status} = armletResult
//config.logger.info("issues", issues)
//config.logger.info("status", status)
2019-04-15 08:11:43 +00:00
obj.uuid = status.uuid;
if (config.debug) {
config.logger.debug(`${analyzeOpts.data.contractName}: UUID is ${status.uuid}`);
if (config.debug > 1) {
config.logger.debug("issues: " + `${util.inspect(issues, {depth: null})}`);
config.logger.debug("status: " + `${util.inspect(status, {depth: null})}`);
}
}
if (status.status === 'Error') {
return [status, null];
} else {
obj.setIssues(issues);
}
return [null, obj];
} catch (err) {
let errStr;
if (typeof err === 'string') {
// It is assumed that err should be string here.
errStr = `${err}`;
} else if (typeof err.message === 'string') {
// If err is Error, get message property.
errStr = err.message;
} else {
// If err is unexpected type, coerce err to inspectable format.
// This situation itself is not assumed, but this is for robustness and investigation.
errStr = `${util.inspect(err)}`;
}
// Check error message from armlet to determine if a timeout occurred.
if (errStr.includes('User or default timeout reached after')
|| errStr.includes('Timeout reached after')) {
return [(buildObj.contractName + ": ").yellow + errStr, null];
} else {
return [(buildObj.contractName + ": ").red + errStr, null];
2019-04-22 15:47:01 +00:00
2019-04-15 08:11:43 +00:00
}
}
});
2019-04-22 15:47:01 +00:00
//console.log("results", JSON.stringify(results))
2019-04-15 08:11:43 +00:00
return results.reduce((accum, curr) => {
const [ err, obj ] = curr;
if (err) {
accum.errors.push(err);
} else if (obj) {
accum.objects.push(obj);
}
return accum;
}, { errors: [], objects: [] });
};
function ghettoReport(logger, results) {
let issuesCount = 0;
results.forEach(ele => {
issuesCount += ele.issues.length;
});
if (issuesCount === 0) {
logger('No issues found');
return 0;
}
for (const group of results) {
logger(group.sourceList.join(', ').underline);
for (const issue of group.issues) {
logger(yaml.safeDump(issue, {'skipInvalid': true}));
}
}
return 1;
}
module.exports = {
analyse,
getStatus
}