embark-mythx/mythx.js

152 lines
5.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');
const { MythXIssues } = require('./lib/issues2eslint');
const defaultAnalyzeRateLimit = 4
module.exports = async function analyse(cfg, embark) {
//embark.logger.debug("embark.config", embark.config)
cfg.logger = embark.logger
//embark.logger.info("embark", JSON.stringify(embark))
// Set analysis parameters
const limit = cfg.limit || defaultAnalyzeRateLimit
if (isNaN(limit)) {
console.log(`limit parameter should be a number; got ${limit}.`)
return 1
}
if (limit < 0 || limit > defaultAnalyzeRateLimit) {
console.log(`limit should be between 0 and ${defaultAnalyzeRateLimit}; got ${limit}.`)
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,
})
//TODO: Check contract names provided in options are respected
//const contractNames = cfg._.length > 1 ? cfg._.slice(1, cfg._.length) : null
// Collect contracts ---
// Extract list of contracts passed in cli to verify
// Get list of JSON smart contract files from build directory
console.log("embark.config.embarkConfig.generationDir", embark.config.buildDir)
const contractFiles = mythXUtil.getContractFiles(embark.config.buildDir + "/contracts")
//embark.logger.debug("contractFiles", contractFiles)
// Parse contracts
let contractObjects = contractFiles.map(filename => {
const jsonFile = fs.readFileSync(filename, 'utf8')
//console.log("contract object", jsonFile)
return JSON.parse(jsonFile)
})
console.log("contractObjects", contractObjects)
//TODO: Possibly need to rewrite the contract objects for MythX to understand
const submitObjects = mythXUtil.buildRequestData(contractObjects)
const { objects, errors } = await doAnalysis(armletClient, cfg, submitObjects, limit)
console.log("objects", JSON.stringify(objects))
console.log("errors", errors)
const result = mythXUtil.doReport(cfg, objects, errors)
console.log("result", result)
return result
}
const doAnalysis = async (armletClient, config, contracts, contractNames = null, limit) => {
//console.log("contracts", contracts)
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;
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 {
console.log("analyzeOpts", JSON.stringify(analyzeOpts))
const {issues, status} = await armletClient.analyzeWithStatus(analyzeOpts);
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];
}
}
});
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: [] });
};