Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Desktopslither2 #4925

Merged
merged 10 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions apps/remix-ide/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ import { xtermPlugin } from './app/plugins/electron/xtermPlugin'
import { ripgrepPlugin } from './app/plugins/electron/ripgrepPlugin'
import { compilerLoaderPlugin, compilerLoaderPluginDesktop } from './app/plugins/electron/compilerLoaderPlugin'
import { appUpdaterPlugin } from './app/plugins/electron/appUpdaterPlugin'
import { SlitherHandleDesktop } from './app/plugins/electron/slitherPlugin'
import { SlitherHandle } from './app/files/slither-handle'
import {SolCoder} from './app/plugins/solcoderAI'

const isElectron = require('is-electron')
Expand Down Expand Up @@ -383,6 +385,10 @@ class AppComponent {
const compilerloader = isElectron()? new compilerLoaderPluginDesktop(): new compilerLoaderPlugin()
this.engine.register([compilerloader])

// slither analyzer plugin (remixd / desktop)
const slitherPlugin = isElectron() ? new SlitherHandleDesktop() : new SlitherHandle()
this.engine.register([slitherPlugin])

// LAYOUT & SYSTEM VIEWS
const appPanel = new MainPanel()
Registry.getInstance().put({api: this.mainview, name: 'mainview'})
Expand Down Expand Up @@ -440,7 +446,6 @@ class AppComponent {
filePanel.hardhatHandle,
filePanel.foundryHandle,
filePanel.truffleHandle,
filePanel.slitherHandle,
linkLibraries,
deployLibraries,
openZeppelinProxy,
Expand Down Expand Up @@ -507,7 +512,7 @@ class AppComponent {
await this.appManager.activatePlugin(['solidity-script', 'remix-templates'])

if (isElectron()){
await this.appManager.activatePlugin(['isogit', 'electronconfig', 'electronTemplates', 'xterm', 'ripgrep', 'appUpdater'])
await this.appManager.activatePlugin(['isogit', 'electronconfig', 'electronTemplates', 'xterm', 'ripgrep', 'appUpdater', 'slither'])
}

this.appManager.on(
Expand Down
2 changes: 0 additions & 2 deletions apps/remix-ide/src/app/panels/file-panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {PluginViewWrapper} from '@remix-ui/helper'
const { HardhatHandle } = require('../files/hardhat-handle.js')
const { FoundryHandle } = require('../files/foundry-handle.js')
const { TruffleHandle } = require('../files/truffle-handle.js')
const { SlitherHandle } = require('../files/slither-handle.js')

/*
Overview of APIs:
Expand Down Expand Up @@ -72,7 +71,6 @@ module.exports = class Filepanel extends ViewPlugin {
this.hardhatHandle = new HardhatHandle()
this.foundryHandle = new FoundryHandle()
this.truffleHandle = new TruffleHandle()
this.slitherHandle = new SlitherHandle()
this.contentImport = contentImport
this.workspaces = []
this.appManager = appManager
Expand Down
13 changes: 13 additions & 0 deletions apps/remix-ide/src/app/plugins/electron/slitherPlugin.ts
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']
}
}
2 changes: 1 addition & 1 deletion apps/remix-ide/src/remixAppManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export class RemixAppManager extends PluginManager {
this.pluginsDirectory = 'https://github.com/ethereum/remix-plugins-directory/master/build/metadata.json'
this.pluginLoader = new PluginLoader()
if (Registry.getInstance().get('platform').api.isDesktop()) {
requiredModules = [...requiredModules, 'fs', 'electronTemplates', 'isogit', 'remix-templates', 'electronconfig', 'xterm', 'compilerloader', 'ripgrep']
requiredModules = [...requiredModules, 'fs', 'electronTemplates', 'isogit', 'remix-templates', 'electronconfig', 'xterm', 'compilerloader', 'ripgrep', 'slither']
}
}

Expand Down
3 changes: 3 additions & 0 deletions apps/remixdesktop/src/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ConfigPlugin } from './plugins/configPlugin';
import { TemplatesPlugin } from './plugins/templates';
import { RipgrepPlugin } from './plugins/ripgrepPlugin';
import { CompilerLoaderPlugin } from './plugins/compilerLoader';
import { SlitherPlugin } from './plugins/slitherPlugin';
import { AppUpdaterPlugin } from './plugins/appUpdater';

const engine = new Engine()
Expand All @@ -20,6 +21,7 @@ const configPlugin = new ConfigPlugin()
const templatesPlugin = new TemplatesPlugin()
const ripgrepPlugin = new RipgrepPlugin()
const compilerLoaderPlugin = new CompilerLoaderPlugin()
const slitherPlugin = new SlitherPlugin()
const appUpdaterPlugin = new AppUpdaterPlugin()

engine.register(appManager)
Expand All @@ -30,6 +32,7 @@ engine.register(configPlugin)
engine.register(templatesPlugin)
engine.register(ripgrepPlugin)
engine.register(compilerLoaderPlugin)
engine.register(slitherPlugin)
engine.register(appUpdaterPlugin)

appManager.activatePlugin('electronconfig')
Expand Down
36 changes: 36 additions & 0 deletions apps/remixdesktop/src/lib/remixd.ts
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')
})
}
}
193 changes: 193 additions & 0 deletions apps/remixdesktop/src/lib/slither.ts
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.'))
}
})
})
}
}
24 changes: 24 additions & 0 deletions apps/remixdesktop/src/lib/utils.ts
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 }


34 changes: 34 additions & 0 deletions apps/remixdesktop/src/plugins/slitherPlugin.ts
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);
}
}


Loading
Loading