-
Notifications
You must be signed in to change notification settings - Fork 919
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4925 from ethereum/desktopslither2
Desktopslither2
- Loading branch information
Showing
13 changed files
with
423 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { ElectronPlugin } from '@remixproject/engine-electron'; | ||
|
||
export class SlitherHandleDesktop extends ElectronPlugin { | ||
constructor() { | ||
super({ | ||
displayName: 'slither', | ||
name: 'slither', | ||
description: 'electron slither', | ||
methods: ['analyse'] | ||
}) | ||
this.methods = ['analyse'] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { ElectronBasePluginClient } from "@remixproject/plugin-electron"; | ||
import { Profile } from "@remixproject/plugin-utils"; | ||
|
||
export class ElectronBasePluginRemixdClient extends ElectronBasePluginClient { | ||
log: (...message: any) => void | ||
error: (...message: any) => void | ||
|
||
currentSharedFolder: string = '' | ||
constructor(webContentsId: number, profile: Profile) { | ||
super(webContentsId, profile); | ||
this.log = (...message: any) => { | ||
for(const m of message) { | ||
this.call('terminal', 'log', { | ||
type: 'log', | ||
value: m | ||
}) | ||
} | ||
} | ||
this.error = (...message: any) => { | ||
for(const m of message) { | ||
this.call('terminal', 'log', { | ||
type: 'error', | ||
value: m | ||
}) | ||
} | ||
} | ||
|
||
|
||
this.onload(async () => { | ||
this.on('fs' as any, 'workingDirChanged', async (path: string) => { | ||
this.currentSharedFolder = path | ||
}) | ||
this.currentSharedFolder = await this.call('fs' as any, 'getWorkingDir') | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
import { existsSync, readFileSync, readdirSync, unlinkSync } from 'fs' | ||
import * as utils from './utils' | ||
const { spawn, execSync } = require('child_process') // eslint-disable-line | ||
export interface OutputStandard { | ||
description: string | ||
title: string | ||
confidence: string | ||
severity: string | ||
sourceMap: any | ||
category?: string | ||
reference?: string | ||
example?: any | ||
[key: string]: any | ||
} | ||
|
||
export const SlitherClientMixin = (Base) => class extends Base { | ||
methods: Array<string> | ||
currentSharedFolder: string | ||
|
||
constructor(...args: any[]) { | ||
super(...args); // Ensure the parent constructor is called | ||
} | ||
|
||
|
||
log(...message: any) { | ||
if (this.log) { | ||
this.log(...message) | ||
} else { | ||
console.log(...message) | ||
} | ||
} | ||
|
||
error(...message: any) { | ||
if (this.error) { | ||
this.error(...message) | ||
} else { | ||
console.error(...message) | ||
} | ||
} | ||
|
||
|
||
mapNpmDepsDir(list) { | ||
const remixNpmDepsPath = utils.absolutePath('.deps/npm', this.currentSharedFolder) | ||
const localNpmDepsPath = utils.absolutePath('node_modules', this.currentSharedFolder) | ||
const npmDepsExists = existsSync(remixNpmDepsPath) | ||
const nodeModulesExists = existsSync(localNpmDepsPath) | ||
let isLocalDep = false | ||
let isRemixDep = false | ||
let allowPathString = '' | ||
let remapString = '' | ||
|
||
for (const e of list) { | ||
const importPath = e.replace(/import ['"]/g, '').trim() | ||
const packageName = importPath.split('/')[0] | ||
if (nodeModulesExists && readdirSync(localNpmDepsPath).includes(packageName)) { | ||
isLocalDep = true | ||
remapString += `${packageName}=./node_modules/${packageName} ` | ||
} else if (npmDepsExists && readdirSync(remixNpmDepsPath).includes(packageName)) { | ||
isRemixDep = true | ||
remapString += `${packageName}=./.deps/npm/${packageName} ` | ||
} | ||
} | ||
if (isLocalDep) allowPathString += './node_modules,' | ||
if (isRemixDep) allowPathString += './.deps/npm,' | ||
|
||
return { remapString, allowPathString } | ||
} | ||
|
||
transform(detectors: Record<string, any>[]): OutputStandard[] { | ||
const standardReport: OutputStandard[] = [] | ||
for (const e of detectors) { | ||
const obj = {} as OutputStandard | ||
obj.description = e.description | ||
obj.title = e.check | ||
obj.confidence = e.confidence | ||
obj.severity = e.impact | ||
obj.sourceMap = e.elements.map((element) => { | ||
delete element.source_mapping.filename_used | ||
delete element.source_mapping.filename_absolute | ||
return element | ||
}) | ||
standardReport.push(obj) | ||
} | ||
return standardReport | ||
} | ||
|
||
analyse(filePath: string, compilerConfig: Record<string, any>) { | ||
return new Promise((resolve, reject) => { | ||
const options = { cwd: this.currentSharedFolder, shell: true } | ||
|
||
const { currentVersion, optimize, evmVersion } = compilerConfig | ||
if (currentVersion && currentVersion.includes('+commit')) { | ||
// Get compiler version with commit id e.g: 0.8.2+commit.661d110 | ||
const versionString: string = currentVersion.substring(0, currentVersion.indexOf('+commit') + 16) | ||
this.log(`[Slither Analysis]: Compiler version is ${versionString}`) | ||
let solcOutput: Buffer | ||
// Check solc current installed version | ||
try { | ||
solcOutput = execSync('solc --version', options) | ||
} catch (err) { | ||
this.error(err) | ||
reject(new Error('Error in running solc command')) | ||
} | ||
if (!solcOutput.toString().includes(versionString)) { | ||
this.log('[Slither Analysis]: Compiler version is different from installed solc version') | ||
// Get compiler version without commit id e.g: 0.8.2 | ||
const version: string = versionString.substring(0, versionString.indexOf('+commit')) | ||
// List solc versions installed using solc-select | ||
try { | ||
const solcSelectInstalledVersions: Buffer = execSync('solc-select versions', options) | ||
// Check if required version is already installed | ||
if (!solcSelectInstalledVersions.toString().includes(version)) { | ||
this.log(`[Slither Analysis]: Installing ${version} using solc-select`) | ||
// Install required version | ||
execSync(`solc-select install ${version}`, options) | ||
} | ||
this.log(`[Slither Analysis]: Setting ${version} as current solc version using solc-select`) | ||
// Set solc current version as required version | ||
execSync(`solc-select use ${version}`, options) | ||
} catch (err) { | ||
this.error(err) | ||
reject(new Error('Error in running solc-select command')) | ||
} | ||
} else this.log('[Slither Analysis]: Compiler version is same as installed solc version') | ||
} | ||
// Allow paths and set solc remapping for import URLs | ||
const fileContent = readFileSync(utils.absolutePath(filePath, this.currentSharedFolder), 'utf8') | ||
const importsArr = fileContent.match(/import ['"][^.|..](.+?)['"];/g) | ||
let remaps = '' | ||
if (importsArr?.length) { | ||
const { remapString } = this.mapNpmDepsDir(importsArr) | ||
remaps = remapString.trim() | ||
} | ||
const optimizeOption: string = optimize ? '--optimize' : '' | ||
const evmOption: string = evmVersion ? `--evm-version ${evmVersion}` : '' | ||
let solcArgs = '' | ||
if (optimizeOption) { | ||
solcArgs += optimizeOption + ' ' | ||
} | ||
if (evmOption) { | ||
if (!solcArgs.endsWith(' ')) solcArgs += ' ' | ||
solcArgs += evmOption | ||
} | ||
if (solcArgs) { | ||
solcArgs = `--solc-args "${solcArgs.trimStart()}"` | ||
} | ||
const solcRemaps = remaps ? `--solc-remaps "${remaps}"` : '' | ||
|
||
const outputFile = 'remix-slither-report.json' | ||
try { | ||
// We don't keep the previous analysis | ||
const outputFilePath = utils.absolutePath(outputFile, this.currentSharedFolder) | ||
if (existsSync(outputFilePath)) unlinkSync(outputFilePath) | ||
} catch (e) { | ||
this.error('unable to remove the output file') | ||
this.error(e.message) | ||
} | ||
const cmd = `slither ${filePath} ${solcArgs} ${solcRemaps} --json ${outputFile}` | ||
this.log('[Slither Analysis]: Running Slither...') | ||
// Added `stdio: 'ignore'` as for contract with NPM imports analysis which is exported in 'stderr' | ||
// get too big and hangs the process. We process analysis from the report file only | ||
const child = spawn(cmd, { cwd: this.currentSharedFolder, shell: true, stdio: 'ignore' }) | ||
|
||
const response = {} | ||
child.on('close', () => { | ||
const outputFileAbsPath: string = utils.absolutePath(outputFile, this.currentSharedFolder) | ||
// Check if slither report file exists | ||
if (existsSync(outputFileAbsPath)) { | ||
let report = readFileSync(outputFileAbsPath, 'utf8') | ||
report = JSON.parse(report) | ||
if (report['success']) { | ||
response['status'] = true | ||
if (!report['results'] || !report['results'].detectors || !report['results'].detectors.length) { | ||
response['count'] = 0 | ||
} else { | ||
const { detectors } = report['results'] | ||
response['count'] = detectors.length | ||
response['data'] = this.transform(detectors) | ||
} | ||
|
||
resolve(response) | ||
} else { | ||
this.log(report['error']) | ||
reject(new Error('Error in running Slither Analysis.')) | ||
} | ||
} else { | ||
this.error('Error in generating Slither Analysis Report. Make sure Slither is properly installed.') | ||
reject(new Error('Error in generating Slither Analysis Report. Make sure Slither is properly installed.')) | ||
} | ||
}) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import * as pathModule from 'path' | ||
/** | ||
* returns the absolute path of the given @arg path | ||
* | ||
* @param {String} path - relative path (Unix style which is the one used by Remix IDE) | ||
* @param {String} sharedFolder - absolute shared path. platform dependent representation. | ||
* @return {String} platform dependent absolute path (/home/user1/.../... for unix, c:\user\...\... for windows) | ||
*/ | ||
function absolutePath (path: string, sharedFolder:string): string { | ||
path = normalizePath(path) | ||
path = pathModule.resolve(sharedFolder, path) | ||
return path | ||
} | ||
function normalizePath (path) { | ||
if (path === '/') path = './' | ||
if (process.platform === 'win32') { | ||
return path.replace(/\//g, '\\') | ||
} | ||
return path | ||
} | ||
|
||
export { absolutePath } | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { Profile } from "@remixproject/plugin-utils"; | ||
import { ElectronBasePlugin, ElectronBasePluginClient } from "@remixproject/plugin-electron" | ||
|
||
import { ElectronBasePluginRemixdClient } from "../lib/remixd" | ||
import { SlitherClientMixin } from "../lib/slither"; | ||
const profile: Profile = { | ||
name: 'slither', | ||
displayName: 'electron slither', | ||
description: 'electron slither', | ||
} | ||
|
||
export class SlitherPlugin extends ElectronBasePlugin { | ||
clients: any [] | ||
constructor() { | ||
super(profile, clientProfile, SlitherClientMixin(SlitherPluginClient)) | ||
this.methods = [...super.methods] | ||
} | ||
} | ||
|
||
const clientProfile: Profile = { | ||
name: 'slither', | ||
displayName: 'electron slither', | ||
description: 'electron slither', | ||
methods: ['analyse'] | ||
} | ||
|
||
|
||
class SlitherPluginClient extends ElectronBasePluginRemixdClient { | ||
constructor(webContentsId: number, profile: Profile) { | ||
super(webContentsId, profile); | ||
} | ||
} | ||
|
||
|
Oops, something went wrong.