add analyze with slither

pull/3181/head
yann300 2 years ago
parent 6578135447
commit 29f4bd72c6
  1. 3
      apps/remix-ide/src/app/plugins/solhint.ts
  2. 4
      apps/remix-ide/src/app/tabs/compile-tab.js
  3. 80
      apps/solidity-compiler/src/app/compiler-api.ts
  4. 3
      libs/remix-lib/src/types/ICompilerApi.ts
  5. 10
      libs/remix-ui/renderer/src/lib/renderer.tsx
  6. 45
      libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx
  7. 18
      libs/remix-ui/solidity-compiler/src/lib/solidity-compiler.tsx

@ -60,9 +60,8 @@ export class Solhint extends Plugin {
return reports.map((report: Report) => { return reports.map((report: Report) => {
return { return {
severity: severity[report.severity] || 'error',
formattedMessage: `${report.message}\n${report.fix ? report.fix : ''}`, formattedMessage: `${report.message}\n${report.fix ? report.fix : ''}`,
type: report.ruleId, type: severity[report.severity] || 'error',
column: report.column, column: report.column,
line: report.line - 1 line: report.line - 1
} }

@ -70,6 +70,10 @@ class CompileTab extends CompilerApiMixin(ViewPlugin) { // implements ICompilerA
this.renderComponent() this.renderComponent()
} }
onSlitherFinished () {
this.renderComponent()
}
onFileClosed () { onFileClosed () {
this.renderComponent() this.renderComponent()
} }

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { compile, helper } from '@remix-project/remix-solidity' import { compile, helper, CompilerAbstract } from '@remix-project/remix-solidity'
import { CompileTabLogic, parseContracts } from '@remix-ui/solidity-compiler' // eslint-disable-line import { CompileTabLogic, parseContracts } from '@remix-ui/solidity-compiler' // eslint-disable-line
import type { ConfigurationSettings, CompileErrors, CompileError } from '@remix-project/remix-lib-ts' import type { ConfigurationSettings, CompileErrors, CompileError } from '@remix-project/remix-lib-ts'
@ -14,6 +14,7 @@ export const CompilerApiMixin = (Base) => class extends Base {
} }
compileErrors: CompileErrors compileErrors: CompileErrors
linterErrors: CompileError[] linterErrors: CompileError[]
slitherErrors: CompileError[]
compileTabLogic: CompileTabLogic compileTabLogic: CompileTabLogic
configurationSettings: ConfigurationSettings configurationSettings: ConfigurationSettings
@ -23,6 +24,7 @@ export const CompilerApiMixin = (Base) => class extends Base {
onFileRemoved: (path: string) => void onFileRemoved: (path: string) => void
onNoFileSelected: () => void onNoFileSelected: () => void
onLintingFinished: () => void onLintingFinished: () => void
onSlitherFinished: () => void
onCompilationFinished: (compilationDetails: { contractMap: { file: string } | Record<string, any>, contractsDetails: Record<string, any> }) => void onCompilationFinished: (compilationDetails: { contractMap: { file: string } | Record<string, any>, contractsDetails: Record<string, any> }) => void
onSessionSwitched: () => void onSessionSwitched: () => void
onContentChanged: () => void onContentChanged: () => void
@ -52,6 +54,7 @@ export const CompilerApiMixin = (Base) => class extends Base {
errors: [] errors: []
} }
this.linterErrors = [] this.linterErrors = []
this.slitherErrors = []
this.compiledFileName = '' this.compiledFileName = ''
this.currentFile = '' this.currentFile = ''
} }
@ -113,6 +116,80 @@ export const CompilerApiMixin = (Base) => class extends Base {
this.onLintingFinished() this.onLintingFinished()
} }
async runSlither () {
this.slitherErrors = []
const conf = {
currentVersion: this.compiler.state.currentVersion,
optimize: this.compiler.state.optimize,
evmVersion: this.compiler.state.evmVersion
}
const result = await this.call('slither', 'analyse', this.currentFile, conf)
if (!result.status) {
this.call('notification', 'toast', 'slither analysis failed.')
return
}
const lastCompilationResult: CompilerAbstract = await this.call('compilerArtefacts', 'get', '__last')
const data = lastCompilationResult.getData()
const sourceCode = lastCompilationResult.getSourceCode()
const report = result.data
const mapType = {
'informational': 'info',
'low': 'warning',
'medium': 'error',
'high': 'error',
'optimization': 'info'
}
for (const item of report) {
console.log(item)
const type = mapType[item.severity.toLowerCase()] ? mapType[item.severity.toLowerCase()] : 'error'
if (item.sourceMap.length > 0) {
let location: any = {}
let path = item.sourceMap[0].source_mapping.filename_relative
let fileIndex = Object.keys(data.sources).indexOf(path)
if (fileIndex === -1) {
path = await this.call('fileManager', 'getUrlFromPath', path)
fileIndex = Object.keys(data.sources).indexOf(path.file)
}
if (fileIndex >= 0) {
location = {
start: item.sourceMap[0].source_mapping.start,
length: item.sourceMap[0].source_mapping.length
}
location = await this.call('offsetToLineColumnConverter', 'offsetToLineColumn',
location,
fileIndex,
sourceCode.sources,
data.sources
)
const msg = `${item.title} : ${path} - ${item.description} - ${item.more ? item.more : ''}`
const fileName = Object.keys(data.sources)[fileIndex]
this.slitherErrors.push({
column: location.start.column,
line: location.start.line,
file: fileName,
formattedMessage: msg,
type: type
})
} else {
const msg = `${item.title} : ${item.description} - ${item.more}`
this.slitherErrors.push({
column: -1,
line: -1,
file: null,
formattedMessage: msg,
type: type
})
}
}
}
this.onSlitherFinished()
}
compileWithHardhat (configFile) { compileWithHardhat (configFile) {
return this.call('hardhat', 'compile', configFile) return this.call('hardhat', 'compile', configFile)
} }
@ -291,6 +368,7 @@ export const CompilerApiMixin = (Base) => class extends Base {
this.data.eventHandlers.onCompilationFinished = async (success, data, source, input, version) => { this.data.eventHandlers.onCompilationFinished = async (success, data, source, input, version) => {
this.compileErrors = data this.compileErrors = data
this.linterErrors = [] this.linterErrors = []
this.slitherErrors = []
if (success) { if (success) {
// forwarding the event to the appManager infra // forwarding the event to the appManager infra
this.emit('compilationFinished', source.target, source, 'soljson', data, input, version) this.emit('compilationFinished', source.target, source, 'soljson', data, input, version)

@ -9,6 +9,7 @@ export interface ICompilerApi {
} }
compileErrors: CompileErrors compileErrors: CompileErrors
linterErrors: CompileError[] linterErrors: CompileError[]
slitherErrors: CompileError[]
compileTabLogic: any compileTabLogic: any
configurationSettings: ConfigurationSettings configurationSettings: ConfigurationSettings
@ -29,6 +30,7 @@ export interface ICompilerApi {
onFileRemoved: (path: string) => void onFileRemoved: (path: string) => void
onNoFileSelected: () => void onNoFileSelected: () => void
onLintingFinished: () => void onLintingFinished: () => void
onSlitherFinished: () => void
onCompilationFinished: (contractsDetails: any, contractMap: any) => void onCompilationFinished: (contractsDetails: any, contractMap: any) => void
onSessionSwitched: () => void onSessionSwitched: () => void
onContentChanged: () => void onContentChanged: () => void
@ -42,6 +44,7 @@ export interface ICompilerApi {
saveCurrentFile: () => void saveCurrentFile: () => void
runScriptAfterCompilation: (fileName: string) => void, runScriptAfterCompilation: (fileName: string) => void,
runLinter: (fileName: string) => void, runLinter: (fileName: string) => void,
runSlither: () => void,
logToTerminal: (log: terminalLog) => void logToTerminal: (log: terminalLog) => void

@ -10,6 +10,12 @@ interface RendererProps {
errFile?: string errFile?: string
} }
const classType = {
'error': 'alert alert-danger',
'warning': 'alert alert-warning',
'info': 'alert alert-info'
}
export const Renderer = ({ message, opt = {}, plugin, errColumn, errLine, errFile }: RendererProps) => { export const Renderer = ({ message, opt = {}, plugin, errColumn, errLine, errFile }: RendererProps) => {
const [messageText, setMessageText] = useState(null) const [messageText, setMessageText] = useState(null)
const [editorOptions, setEditorOptions] = useState({ const [editorOptions, setEditorOptions] = useState({
@ -17,7 +23,7 @@ export const Renderer = ({ message, opt = {}, plugin, errColumn, errLine, errFil
type: '', type: '',
errFile: '' errFile: ''
}) })
const [classList, setClassList] = useState(opt.type === 'error' ? 'alert alert-danger' : 'alert alert-warning') const [classList, setClassList] = useState('alert alert-warning')
const [close, setClose] = useState(false) const [close, setClose] = useState(false)
useEffect(() => { useEffect(() => {
@ -48,7 +54,7 @@ export const Renderer = ({ message, opt = {}, plugin, errColumn, errLine, errFil
setMessageText(text) setMessageText(text)
setEditorOptions(opt) setEditorOptions(opt)
setClose(close !== undefined ? close : false) setClose(close !== undefined ? close : false)
setClassList(opt.type === 'error' ? 'alert alert-danger' : 'alert alert-warning') setClassList(classType[opt.type] ? classType[opt.type] : 'alert alert-warning')
}, [message, opt]) }, [message, opt])

@ -536,6 +536,13 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
api.runLinter(currentFile) api.runLinter(currentFile)
} }
const slither = () => {
const currentFile = api.currentFile
if (!isSolFileSelected()) return
api.runSlither()
}
const _updateVersionSelector = (version, customUrl = '') => { const _updateVersionSelector = (version, customUrl = '') => {
// update selectedversion of previous one got filtered out // update selectedversion of previous one got filtered out
let selectedVersion = version let selectedVersion = version
@ -1038,26 +1045,42 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
<CustomTooltip <CustomTooltip
placement="right" placement="right"
tooltipId="overlay-tooltip-compile-run" tooltipId="overlay-tooltip-compile-run"
tooltipText={<div className="text-left">Lint the current file</div>} tooltipText={<div className="text-left">Analyze with Solhint</div>}
> >
<span> <span>
<FormattedMessage id='solidity.lint' defaultMessage='Lint' /> <FormattedMessage id='solidity.lint' defaultMessage='Lint' />
<span className="ml-1"> <span className="ml-1">
{typeof state.compiledFileName === 'string' Analyze with Solhint
? extractNameFromKey(state.compiledFileName) ||
`<${intl.formatMessage({
id: 'solidity.noFileSelected',
defaultMessage: 'no file selected',
})}>`
: `<${intl.formatMessage({
id: 'solidity.noFileSelected',
defaultMessage: 'no file selected',
})}>`}
</span> </span>
</span> </span>
</CustomTooltip> </CustomTooltip>
</button> </button>
</div> </div>
<div className='d-flex align-items-center'>
<button
id="slitherButton"
data-id="compilerContainerLinter"
className="btn btn-secondary btn-block d-block w-100 text-break remixui_soliditySlitherButton d-inline-block remixui_disabled mb-1 mt-3"
onClick={slither}
disabled={(configFilePath === '' && state.useFileConfiguration) || disableCompileButton}
>
<CustomTooltip
placement="right"
tooltipId="overlay-tooltip-compile-run"
tooltipText={<div className="text-left">Analyze with Slither</div>}
>
<span>
<FormattedMessage id='solidity.lint' defaultMessage='Lint' />
<span className="ml-1">
Analyze with Slither
</span>
</span>
</CustomTooltip>
</button>
</div>
</div> </div>
</article> </article>
</section> </section>

@ -37,6 +37,7 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
const [hideWarnings, setHideWarnings] = useState<boolean>(false) const [hideWarnings, setHideWarnings] = useState<boolean>(false)
const [compileErrors, setCompileErrors] = useState<Record<string, CompileErrors>>({ [currentFile]: api.compileErrors }) const [compileErrors, setCompileErrors] = useState<Record<string, CompileErrors>>({ [currentFile]: api.compileErrors })
const [linterErrors, setLinterErrors] = useState<Record<string, Array<CompileError>>>({ [currentFile]: api.linterErrors }) const [linterErrors, setLinterErrors] = useState<Record<string, Array<CompileError>>>({ [currentFile]: api.linterErrors })
const [slitherErrors, setSlitherErrors] = useState<Record<string, Array<CompileError>>>({ [currentFile]: api.slitherErrors })
const [badgeStatus, setBadgeStatus] = useState<Record<string, { key: string, title?: string, type?: string }>>({}) const [badgeStatus, setBadgeStatus] = useState<Record<string, { key: string, title?: string, type?: string }>>({})
const [contractsFile, setContractsFile] = useState<ContractsFile>({}) const [contractsFile, setContractsFile] = useState<ContractsFile>({})
@ -104,6 +105,7 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
setContractsFile({ ...contractsFile, [target]: { contractList, contractsDetails } }) setContractsFile({ ...contractsFile, [target]: { contractList, contractsDetails } })
setCompileErrors({ ...compileErrors, [currentFile]: api.compileErrors }) setCompileErrors({ ...compileErrors, [currentFile]: api.compileErrors })
setLinterErrors({ [currentFile]: api.linterErrors }) setLinterErrors({ [currentFile]: api.linterErrors })
setSlitherErrors({ [currentFile]: api.slitherErrors })
} }
api.onLintingFinished = () => { api.onLintingFinished = () => {
@ -111,10 +113,16 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
setLinterErrors({ [currentFile]: api.linterErrors }) setLinterErrors({ [currentFile]: api.linterErrors })
} }
api.onSlitherFinished = () => {
setCompileErrors({} as Record<string, CompileErrors>)
setSlitherErrors({ [currentFile]: api.slitherErrors })
}
api.onFileClosed = (name) => { api.onFileClosed = (name) => {
if (name === currentFile) { if (name === currentFile) {
setCompileErrors({ ...compileErrors, [currentFile]: {} as CompileErrors }) setCompileErrors({ ...compileErrors, [currentFile]: {} as CompileErrors })
setLinterErrors({ ...linterErrors, [currentFile]: [] as CompileError[] }) setLinterErrors({ ...linterErrors, [currentFile]: [] as CompileError[] })
setSlitherErrors({ ...slitherErrors, [currentFile]: [] as CompileError[] })
setBadgeStatus({ ...badgeStatus, [currentFile]: { key: 'none' } }) setBadgeStatus({ ...badgeStatus, [currentFile]: { key: 'none' } })
} }
} }
@ -214,7 +222,15 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
linterErrors[currentFile] && linterErrors[currentFile] &&
<div className="remixui_errorBlobs p-4" data-id="linterErrors"> <div className="remixui_errorBlobs p-4" data-id="linterErrors">
{ {
linterErrors[currentFile].map((err, index) => <Renderer key={index} message={err.formattedMessage} plugin={api} errColumn={err.column} errLine={err.line} errFile={currentFile} opt={{ close: false, useSpan: true, type: err.severity, errorType: err.type }}></Renderer>) linterErrors[currentFile].map((err, index) => <Renderer key={index} message={err.formattedMessage} plugin={api} errColumn={err.column} errLine={err.line} errFile={currentFile} opt={{ close: false, useSpan: true, type: err.type }}></Renderer>)
}
</div>
}
{
slitherErrors[currentFile] &&
<div className="remixui_errorBlobs p-4" data-id="slitherErrors">
{
slitherErrors[currentFile].map((err, index) => <Renderer key={index} message={err.formattedMessage} plugin={api} errColumn={err.column} errLine={err.line} errFile={currentFile} opt={{ close: false, useSpan: true, type: err.type }}></Renderer>)
} }
</div> </div>
} }

Loading…
Cancel
Save