From 8d50aa564a5fefe62355c18a8d57406d890448a5 Mon Sep 17 00:00:00 2001 From: Joseph Izang Date: Mon, 5 Jun 2023 23:23:15 +0100 Subject: [PATCH] addressed comments from @ryestew and @yann300 --- .../src/lib/actions/staticAnalysisActions.ts | 33 +- .../src/lib/components/BasicTitle.tsx | 2 +- .../src/lib/remix-ui-static-analyser.tsx | 331 +++++++++++++----- .../static-analyser/src/staticanalyser.d.ts | 53 +++ .../src/lib/components/Badge.tsx | 6 +- 5 files changed, 312 insertions(+), 113 deletions(-) diff --git a/libs/remix-ui/static-analyser/src/lib/actions/staticAnalysisActions.ts b/libs/remix-ui/static-analyser/src/lib/actions/staticAnalysisActions.ts index d50c830d43..236c158c48 100644 --- a/libs/remix-ui/static-analyser/src/lib/actions/staticAnalysisActions.ts +++ b/libs/remix-ui/static-analyser/src/lib/actions/staticAnalysisActions.ts @@ -1,6 +1,6 @@ import { CompilationResult, SourceWithTarget } from '@remixproject/plugin-api' import React from 'react' //eslint-disable-line -import { AnalysisTab, RemixUiStaticAnalyserReducerActionType, RemixUiStaticAnalyserState, SolHintReport } from '../../staticanalyser' +import { AnalysisTab, RemixUiStaticAnalyserReducerActionType, RemixUiStaticAnalyserState, SolHintReport, SlitherAnalysisResults } from '../../staticanalyser' import { RemixUiStaticAnalyserProps } from '@remix-ui/static-analyser' /** @@ -42,11 +42,11 @@ export const compilation = (analysisModule: AnalysisTab, * @returns {Promise} */ // eslint-disable-next-line @typescript-eslint/no-unused-vars -export async function run (lastCompilationResult, lastCompilationSource, currentFile: string, state: RemixUiStaticAnalyserState, props: RemixUiStaticAnalyserProps, isSupportedVersion, slitherEnabled, categoryIndex: number[], groupedModules, runner, _paq, message, showWarnings, allWarnings: React.RefObject, warningContainer: React.RefObject, calculateWarningStateEntries: (e:[string, any][]) => {length: number, errors: any[] }, warningState, setHints: React.Dispatch>, hints: SolHintReport[]) { +export async function run (lastCompilationResult, lastCompilationSource, currentFile: string, state: RemixUiStaticAnalyserState, props: RemixUiStaticAnalyserProps, isSupportedVersion, showSlither, categoryIndex: number[], groupedModules, runner, _paq, message, showWarnings, allWarnings: React.RefObject, warningContainer: React.RefObject, calculateWarningStateEntries: (e:[string, any][]) => {length: number, errors: any[] }, warningState, setHints: React.Dispatch>, hints: SolHintReport[], setSlitherWarnings: React.Dispatch>, setSsaWarnings: React.Dispatch>) { if (!isSupportedVersion) return if (state.data !== null) { - if (lastCompilationResult && (categoryIndex.length > 0 || slitherEnabled)) { + if (lastCompilationResult && (categoryIndex.length > 0 || showSlither)) { const warningMessage = [] const warningErrors = [] @@ -69,6 +69,7 @@ export async function run (lastCompilationResult, lastCompilationSource, current } }) }) + // iterate over the warnings and create an object for (const item of result.report) { let location: any = {} let locationString = 'not available' @@ -116,16 +117,17 @@ export async function run (lastCompilationResult, lastCompilationSource, current } warningErrors.push(options) warningMessage.push({ msg, options, hasWarning: true, warningModuleName: moduleName }) + setSsaWarnings(warningErrors) } } // Slither Analysis - if (slitherEnabled) { + if (showSlither) { try { const compilerState = await props.analysisModule.call('solidity', 'getCompilerState') const { currentVersion, optimize, evmVersion } = compilerState await props.analysisModule.call('terminal', 'log', { type: 'log', value: '[Slither Analysis]: Running...' }) _paq.push(['trackEvent', 'solidityStaticAnalyzer', 'analyze', 'slitherAnalyzer']) - const result = await props.analysisModule.call('slither', 'analyse', state.file, { currentVersion, optimize, evmVersion }) + const result: SlitherAnalysisResults = await props.analysisModule.call('slither', 'analyse', state.file, { currentVersion, optimize, evmVersion }) if (result.status) { props.analysisModule.call('terminal', 'log', { type: 'log', value: `[Slither Analysis]: Analysis Completed!! ${result.count} warnings found.` }) const report = result.data @@ -138,11 +140,11 @@ export async function run (lastCompilationResult, lastCompilationSource, current let isLibrary = false if (item.sourceMap && item.sourceMap.length) { - let path = item.sourceMap[0].source_mapping.filename_relative + const path = item.sourceMap[0].source_mapping.filename_relative let fileIndex = Object.keys(lastCompilationResult.sources).indexOf(path) if (fileIndex === -1) { - path = await props.analysisModule.call('fileManager', 'getUrlFromPath', path) - fileIndex = Object.keys(lastCompilationResult.sources).indexOf(path.file) + const getpath = await props.analysisModule.call('fileManager', 'getUrlFromPath', path) + fileIndex = Object.keys(lastCompilationResult.sources).indexOf(getpath.file) } if (fileIndex >= 0) { location = { @@ -165,9 +167,9 @@ export async function run (lastCompilationResult, lastCompilationSource, current const {file, provider} = await props.analysisModule.call('fileManager', 'getPathFromUrl', fileName) if (file.startsWith('.deps') || (provider.type === 'localhost' && file.startsWith('localhost/node_modules'))) isLibrary = true } - const msg = message(item.title, item.description, item.more, fileName, locationString) + const msg = message(item.title, item.description, item.more ?? '', fileName, locationString) const options = { - type: 'warning', + type: item.sourceMap[0].type, useSpan: true, errFile: fileName, fileName, @@ -177,11 +179,16 @@ export async function run (lastCompilationResult, lastCompilationSource, current item: { warning: item.description }, name: item.title, locationString, - more: item.more, + more: item.more ?? '', location: location } - warningErrors.push(options) - warningMessage.push({ msg, options, hasWarning: true, warningModuleName: 'Slither Analysis' }) + + const slitherwarnings = [] + setSlitherWarnings((prev) => { + slitherwarnings.push(...prev) + slitherwarnings.push({ msg, options, hasWarning: true, warningModuleName: 'Slither Analysis' }) + return slitherwarnings + }) } showWarnings(warningMessage, 'warningModuleName') } diff --git a/libs/remix-ui/static-analyser/src/lib/components/BasicTitle.tsx b/libs/remix-ui/static-analyser/src/lib/components/BasicTitle.tsx index d047c6578d..c76ff84a13 100644 --- a/libs/remix-ui/static-analyser/src/lib/components/BasicTitle.tsx +++ b/libs/remix-ui/static-analyser/src/lib/components/BasicTitle.tsx @@ -31,7 +31,7 @@ export function calculateWarningStateEntries(entries: [string, any][]) { export function BasicTitle(props: BasicTitleProps) { return ( - Basic{props.warningStateEntries.length > 0 && !props.hideWarnings ? {calculateWarningStateEntries(props.warningStateEntries).length}: null} + Basic{props.warningStateEntries.length > 0 && !props.hideWarnings ? {calculateWarningStateEntries(props.warningStateEntries).length}: null} ) } \ No newline at end of file diff --git a/libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx b/libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx index f50a516710..6f071518bd 100644 --- a/libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx +++ b/libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx @@ -67,20 +67,21 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => { return indexOfCategory } const [basicEnabled, setBasicEnabled] = useState(true) - const [slitherEnabled, setSlitherEnabled] = useState(false) const [solhintEnabled, setSolhintEnabled] = useState(true) // assuming that solhint is always enabled const [showSlither, setShowSlither] = useState(false) const [isSupportedVersion, setIsSupportedVersion] = useState(false) let [showLibsWarning, setShowLibsWarning] = useState(false) // eslint-disable-line prefer-const const [categoryIndex, setCategoryIndex] = useState(groupedModuleIndex(groupedModules)) const [warningState, setWarningState] = useState({}) - const [runButtonTitle, setRunButtonTitle] = useState('Run Static Analysis') const [hideWarnings, setHideWarnings] = useState(false) const [hints, setHints] = useState([]) - + const [slitherWarnings, setSlitherWarnings] = useState([]) + const [ssaWarnings, setSsaWarnings] = useState([]) + const warningContainer = useRef(null) const allWarnings = useRef({}) const [state, dispatch] = useReducer(analysisReducer, initialState) + const [runButtonTitle, setRunButtonTitle] = useState(`Analyse`) /** * Disable static analysis for contracts whose compiler version is @@ -97,7 +98,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => { setRunButtonTitle('Select Solidity compiler version greater than 0.4.12.') } else { setIsSupportedVersion(true) - setRunButtonTitle('Run static analysis') + setRunButtonTitle('Run Analysis') } } @@ -108,10 +109,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => { useEffect(() => { setWarningState({}) const runAnalysis = async () => { - await run(state.data, state.source, state.file, state, props, isSupportedVersion, slitherEnabled, categoryIndex, groupedModules, runner,_paq, message, showWarnings, allWarnings, warningContainer,calculateWarningStateEntries, warningState, setHints, hints) - // // Run solhint - // const hintsResult = await props.analysisModule.call('solhint', 'lint', state.file) - // setHints(hintsResult) + await run(state.data, state.source, state.file, state, props, isSupportedVersion, showSlither, categoryIndex, groupedModules, runner,_paq, message, showWarnings, allWarnings, warningContainer,calculateWarningStateEntries, warningState, setHints, hints, setSlitherWarnings, setSsaWarnings) } if (basicEnabled) { if (state.data !== null) { @@ -136,11 +134,12 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => { // Reset state dispatch({ type: '', payload: initialState }) setHints([]) + setSlitherWarnings([]) + setSsaWarnings([]) // Show 'Enable Slither Analysis' checkbox if (currentWorkspace && currentWorkspace.isLocalhost === true) setShowSlither(true) else { setShowSlither(false) - setSlitherEnabled(false) } }) props.analysisModule.on('manager', 'pluginDeactivated', (plugin) => { @@ -149,17 +148,15 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => { // Reset warning state setWarningState([]) setHints([]) + setSlitherWarnings([]) + setSsaWarnings([]) // Reset badge props.event.trigger('staticAnaysisWarning', []) // Reset state dispatch({ type: '', payload: initialState }) setShowSlither(false) - setSlitherEnabled(false) } }) - const warningResult = calculateWarningStateEntries(Object.entries(warningState)) - props.analysisModule.emit('statusChanged', { key: hints.length+warningResult.length, - title: `${hints.length+warningResult.length} warning${hints.length+warningResult.length === 1 ? '' : 's'}`, type: 'warning'}) // eslint-disable-next-line @typescript-eslint/no-unused-vars props.analysisModule.on('solidity', 'compilerLoaded', async (version: string, license: string) => { setDisableForRun(version) @@ -167,7 +164,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => { return () => { } }, [props]) - const message = (name, warning, more, fileName, locationString) : string => { + const message = (name: string, warning: any, more?: string, fileName?: string, locationString?: string) : string => { return (` ${name} @@ -255,12 +252,14 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => { } const handleSlitherEnabled = async () => { - const slitherStatus = await props.analysisModule.call('manager', 'isActive', 'remixd') - if (slitherEnabled && slitherStatus) { - setSlitherEnabled(false) + const checkRemixd = await !props.analysisModule.call('manager', 'isActive', 'remixd') + if (showSlither && checkRemixd) { setShowSlither(false) + await props.analysisModule.call('manager', 'deactivatePlugin', 'remixd') + await props.analysisModule.call('filePanel', 'setWorkspace', 'default_workspace') } else { - setSlitherEnabled(true) + await props.analysisModule.call('manager', 'activatePlugin', 'remixd') + await props.analysisModule.call('filePanel', 'setWorkspace', { name: 'localhost', isLocalhost: true }, true) setShowSlither(true) } } @@ -362,92 +361,231 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => { } const hintErrors = hints.filter(hint => hint.type === 'error') + const slitherErrors = slitherWarnings.filter(slitherError => slitherError.options.type === 'error') const tabKeys = [ { - tabKey: 'linter', + tabKey: "linter", child: ( <> - {hints.length > 0 && -
-
- - {!hideWarnings ? hints.map((hint, index) => ( -
-
{ - await props.analysisModule.call('editor', 'discardHighlight') - await props.analysisModule.call('editor', 'highlight', { - end: { - line: hint.line, column: hint.column+1 - }, - start: { - line: hint.line, column: hint.column - } - }, state.file, '', { focus: true }) - }}> - {hint.formattedMessage} - {hint.type}
- {`${hint.column}:${hint.line}`} -
-
)) : hintErrors.map((hint, index) => (
-
{ - await props.analysisModule.call('editor', 'discardHighlight') - await props.analysisModule.call('editor', 'highlight', { - end: { - line: hint.line, column: hint.column+1 - }, - start: { - line: hint.line, column: hint.column - } - }, state.file, '', { focus: true }) - }}> - {hint.formattedMessage} - {hint.type}
- {`${hint.column}:${hint.line}`} -
-
)) - } -
-
-
- } + {hints.length > 0 ? ( +
+
+ + {!hideWarnings + ? hints.map((hint, index) => ( +
+
{ + await props.analysisModule.call( + "editor", + "discardHighlight" + ); + await props.analysisModule.call( + "editor", + "highlight", + { + end: { + line: hint.line, + column: hint.column + 1, + }, + start: { + line: hint.line, + column: hint.column, + }, + }, + state.file, + "", + { focus: true } + ); + }} + > + + {hint.formattedMessage} + + {hint.type} +
+ {`${hint.column}:${hint.line}`} +
+
+ )) + : hintErrors.map((hint, index) => ( +
+
{ + await props.analysisModule.call( + "editor", + "discardHighlight" + ); + await props.analysisModule.call( + "editor", + "highlight", + { + end: { + line: hint.line, + column: hint.column + 1, + }, + start: { + line: hint.line, + column: hint.column, + }, + }, + state.file, + "", + { focus: true } + ); + }} + > + + {hint.formattedMessage} + + {hint.type} +
+ {`${hint.column}:${hint.line}`} +
+
+ ))} +
+
+
+ ) : No Results.} ), - title: Linter{hints.length > 0 ? hideWarnings ? {hintErrors.length} : {hints.length} : null} + title: ( + + Linter + {hints.length > 0 ? ( + hideWarnings ? ( + + {hintErrors.length} + + ) : ( + + {hints.length} + + ) + ) : null} + + ), }, { - tabKey: 'basic', - title: , - child: <> - {Object.entries(warningState).length > 0 && -
+ tabKey: "basic", + title: ( + + ), + child: ( + <> + {Object.entries(warningState).length > 0 ? ( +
- { - (Object.entries(warningState).map((element, index) => ( -
- {element[1]['map']((x, i) => ( // eslint-disable-line dot-notation - x.hasWarning && !hideWarnings ? ( // eslint-disable-next-line dot-notation -
- + {Object.entries(warningState).map((element, index) => ( +
+ {element[1]["map"]( + (x,i) => // eslint-disable-line dot-notation + x.hasWarning && !hideWarnings + ? ( // eslint-disable-next-line dot-notation +
+
) : null - ))} - {} -
- ))) - } + )} + {} +
+ ))}
- } - + ) : No Results.} + + ), }, { - tabKey: 'slither', - title: 'Slither', - child:
, - } - ] + tabKey: "slither", + title: ( + + Slither + {slitherWarnings.length > 0 ? ( + hideWarnings ? ( + + {slitherErrors.length} + + ) : ( + + {slitherWarnings.length} + + ) + ) : null} + + ), + child: ( + <> + {slitherWarnings.length > 0 ? ( +
+
+ + {!hideWarnings + ? slitherWarnings.map((warning, index) => ( +
+ +
+ )) + : slitherWarnings.filter(x => x.options.type === 'error').map((warning, index) => ( +
+ +
+ ))} +
+
+
+ ) : No Result} + + ), + }, + ]; const t = Object.entries(warningState) const checkBasicStatus = () => { return Object.values(groupedModules).map((value: any) => { @@ -492,18 +630,18 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => { id="enableSlither" inputType="checkbox" onClick={handleSlitherEnabled} - checked={slitherEnabled} + checked={showSlither} label="Slither" onChange={() => {}} />