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

Fix #27 slither-vscode UI issue #28

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
39 changes: 20 additions & 19 deletions src/detectorFilterTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import * as slitherResults from "./slitherResults";

// Generic tree node implementation.
export class DetectorFilterNode extends vscode.TreeItem {
public readonly detector : slitherResults.SlitherDetector;
public checked : boolean = true;
constructor(detector : slitherResults.SlitherDetector, checked : boolean) {
super(`${detector.check}: ${detector.title}\n${detector.description}\n\nImpact: ${detector.impact}\nConfidence: ${detector.confidence}`, vscode.TreeItemCollapsibleState.None);
public readonly detector: slitherResults.SlitherDetector;
public checked: boolean = true;
constructor(detector: slitherResults.SlitherDetector, checked: boolean) {
super(`${detector.check}`, vscode.TreeItemCollapsibleState.None);
this.tooltip = `${detector.check}: ${detector.title}\n${detector.description}\n\nImpact: ${detector.impact}\nConfidence: ${detector.confidence}`
this.detector = detector;
this.checked = checked;
this.command = {
Expand All @@ -27,10 +28,10 @@ export class DetectorFilterTreeProvider implements vscode.TreeDataProvider<Detec
public readonly onDidChangeTreeData: vscode.Event<any> = this.changeTreeEmitter.event;

// Create a callback function for signaling
public changedEnabledFilters : (() => void)[] = [];
public changedEnabledFilters: (() => void)[] = [];

// A node which is not rendered itself, but contains all nodes which will be shown.
private detectorFilterNodes : DetectorFilterNode[] = [];
private detectorFilterNodes: DetectorFilterNode[] = [];

constructor(private context: vscode.ExtensionContext) {

Expand All @@ -41,12 +42,12 @@ export class DetectorFilterTreeProvider implements vscode.TreeDataProvider<Detec
this.detectorFilterNodes = [];
for (let detector of slither.detectors) {
// Determine if this detector is visible or not
let checked : boolean = true;
let checked: boolean = true;
if (config.userConfiguration.hiddenDetectors) {
checked = config.userConfiguration.hiddenDetectors.indexOf(detector.check) < 0;
}
// Create the node for this detector and add it to the list.
let detectorFilterNode : DetectorFilterNode = new DetectorFilterNode(detector, checked);
let detectorFilterNode: DetectorFilterNode = new DetectorFilterNode(detector, checked);
this.refreshNodeIcon(detectorFilterNode);
this.detectorFilterNodes.push(detectorFilterNode);
}
Expand All @@ -61,15 +62,15 @@ export class DetectorFilterTreeProvider implements vscode.TreeDataProvider<Detec

// If we have callback handlers, fire them all.
if (this.changedEnabledFilters != null) {
for(let callback of this.changedEnabledFilters) {
for (let callback of this.changedEnabledFilters) {
await callback();
}
}
}

private refreshNodeIcon (node :DetectorFilterNode) {
private refreshNodeIcon(node: DetectorFilterNode) {
if (node.checked) {
node.iconPath = {
node.iconPath = {
light: this.context.asAbsolutePath("resources/check-light.svg"),
dark: this.context.asAbsolutePath("resources/check-dark.svg"),
};
Expand All @@ -81,15 +82,15 @@ export class DetectorFilterTreeProvider implements vscode.TreeDataProvider<Detec

public async toggleAll() {
// If we have any items unchecked, we'll check all items. Otherwise we uncheck them all.
let newCheckedState : boolean = false;
for(let node of this.detectorFilterNodes) {
if(!node.checked) {
let newCheckedState: boolean = false;
for (let node of this.detectorFilterNodes) {
if (!node.checked) {
newCheckedState = true;
}
}

// Set the checked state of all items.
for(let node of this.detectorFilterNodes) {
for (let node of this.detectorFilterNodes) {
node.checked = newCheckedState;
this.refreshNodeIcon(node);
}
Expand All @@ -98,12 +99,12 @@ export class DetectorFilterTreeProvider implements vscode.TreeDataProvider<Detec
await this.fireChangedEnabledFilters();
}

public async getHiddenDetectors() : Promise<Set<string>> {
public async getHiddenDetectors(): Promise<Set<string>> {
// Create a new set
let hiddenDetectors : Set<string> = new Set<string>();
let hiddenDetectors: Set<string> = new Set<string>();

// For each hidden detector, add it to the set
for(let node of this.detectorFilterNodes) {
for (let node of this.detectorFilterNodes) {
if (!node.checked) {
hiddenDetectors.add(node.detector.check);
}
Expand All @@ -113,7 +114,7 @@ export class DetectorFilterTreeProvider implements vscode.TreeDataProvider<Detec
return hiddenDetectors;
}

public async clickedNode(node : DetectorFilterNode) {
public async clickedNode(node: DetectorFilterNode) {
// Toggle the checked state
node.checked = !node.checked;
this.refreshNodeIcon(node);
Expand Down
110 changes: 56 additions & 54 deletions src/explorerTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import * as extension from "./extension";

// Generic tree node implementation.
export class ExplorerNode extends vscode.TreeItem {
public nodes : ExplorerNode[];
public readonly originalLabel : string;
public nodes: ExplorerNode[];
public readonly originalLabel: string;
constructor(originalLabel: string, collapsibleState?: vscode.TreeItemCollapsibleState) {
super(originalLabel, collapsibleState);
this.originalLabel = originalLabel;
Expand All @@ -23,21 +23,23 @@ export class ExplorerNode extends vscode.TreeItem {

// A special type of parent node which denotes a type of issue.
export class CheckTypeNode extends ExplorerNode {
public check : string;
constructor(detector : slitherResults.SlitherDetector) {
super(`${detector.check}: ${detector.title}\n${detector.description}\n\nImpact: ${detector.impact}\nConfidence: ${detector.confidence}`, vscode.TreeItemCollapsibleState.Collapsed);
public check: string;
constructor(detector: slitherResults.SlitherDetector) {
super(`${detector.check}`, vscode.TreeItemCollapsibleState.Collapsed);
this.tooltip = `${detector.check}: ${detector.title}\n${detector.description}\n\nImpact: ${detector.impact}\nConfidence: ${detector.confidence}`
this.check = detector.check;
}
}

// A special type of node which denotes an issue.
export class CheckResultNode extends ExplorerNode {
public workspaceFolder : string;
public result : slitherResults.SlitherResult;
public severityNodeParent : ExplorerNode | undefined;
public typeNodeParent : CheckTypeNode | undefined;
constructor(workspaceFolder : string, workspaceResult : slitherResults.SlitherResult) {
super(slitherResults.getSanitizedDescription(workspaceResult), vscode.TreeItemCollapsibleState.None);
public workspaceFolder: string;
public result: slitherResults.SlitherResult;
public severityNodeParent: ExplorerNode | undefined;
public typeNodeParent: CheckTypeNode | undefined;
constructor(workspaceFolder: string, workspaceResult: slitherResults.SlitherResult) {
super(slitherResults.getSanitizedDescription(workspaceResult).split('\n')[0], vscode.TreeItemCollapsibleState.None);
this.tooltip = slitherResults.getSanitizedDescription(workspaceResult);
this.result = workspaceResult;
this.workspaceFolder = workspaceFolder;
this.contextValue = "ExplorerCheckResultNode";
Expand All @@ -51,17 +53,17 @@ export class SlitherExplorer implements vscode.TreeDataProvider<ExplorerNode> {
public readonly onDidChangeTreeData: vscode.Event<any> = this.changeTreeEmitter.event;

// Create our set of hidden detector types
private hiddenDetectors : Set<string> = new Set<string>();
private hiddenDetectors: Set<string> = new Set<string>();

// Nodes which contain check results, or maps to them by name.
private byTypeNode : ExplorerNode = new ExplorerNode("By Type");
private byTypeMap : Map<string, CheckTypeNode> = new Map<string, CheckTypeNode>();
private bySeverityNode : ExplorerNode = new ExplorerNode("By Severity");
private bySeverityMap : Map<string, ExplorerNode> = new Map<string, ExplorerNode>();
private byResultMap : Map<slitherResults.SlitherResult, CheckResultNode> = new Map<slitherResults.SlitherResult, CheckResultNode>();
private byTypeNode: ExplorerNode = new ExplorerNode("By Type");
private byTypeMap: Map<string, CheckTypeNode> = new Map<string, CheckTypeNode>();
private bySeverityNode: ExplorerNode = new ExplorerNode("By Severity");
private bySeverityMap: Map<string, ExplorerNode> = new Map<string, ExplorerNode>();
private byResultMap: Map<slitherResults.SlitherResult, CheckResultNode> = new Map<slitherResults.SlitherResult, CheckResultNode>();

// A node which is not rendered itself, but contains all nodes which will be shown.
private rootNode : ExplorerNode = this.bySeverityNode;
private rootNode: ExplorerNode = this.bySeverityNode;

constructor(private context: vscode.ExtensionContext) {
// Set up the severity nodes with their respective icons.
Expand All @@ -78,44 +80,44 @@ export class SlitherExplorer implements vscode.TreeDataProvider<ExplorerNode> {
this.setIconBySeverity(informationalSeverityNode, <string>informationalSeverityNode.label);

let severityNodes = [highSeverityNode, mediumSeverityNode, lowSeverityNode, informationalSeverityNode];

// Set up the severity node map.
for (let severityNode of severityNodes) {
if(severityNode.label) {
if (severityNode.label) {
this.bySeverityNode.nodes.push(severityNode);
this.bySeverityMap.set(severityNode.label, severityNode);
this.bySeverityMap.set(severityNode.label, severityNode);
}
}

// Subscribe to the detector filter changed event
extension.detectorFilterTreeProvider.changedEnabledFilters.push(async() => {
extension.detectorFilterTreeProvider.changedEnabledFilters.push(async () => {
await this.onDetectorFiltersChanged();
});
}

private setIconBySeverity(node : ExplorerNode, severity : string) {
private setIconBySeverity(node: ExplorerNode, severity: string) {
// Set the node icon according to severity.
switch(severity) {
switch (severity) {
case "High":
node.iconPath = {
node.iconPath = {
light: this.context.asAbsolutePath("resources/severity-high-light.svg"),
dark: this.context.asAbsolutePath("resources/severity-high-dark.svg"),
};
break;
case "Medium":
node.iconPath = {
node.iconPath = {
light: this.context.asAbsolutePath("resources/severity-medium-light.svg"),
dark: this.context.asAbsolutePath("resources/severity-medium-dark.svg"),
};
break;
case "Low":
node.iconPath = {
node.iconPath = {
light: this.context.asAbsolutePath("resources/severity-low-light.svg"),
dark: this.context.asAbsolutePath("resources/severity-low-dark.svg"),
};
break;
case "Informational":
node.iconPath = {
node.iconPath = {
light: this.context.asAbsolutePath("resources/severity-info-light.svg"),
dark: this.context.asAbsolutePath("resources/severity-info-dark.svg"),
};
Expand All @@ -127,22 +129,22 @@ export class SlitherExplorer implements vscode.TreeDataProvider<ExplorerNode> {
// Loop for each check result
for (let [checkResult, checkNode] of this.byResultMap) {
if (checkResult._ext_in_sync) {
checkNode.iconPath = {
checkNode.iconPath = {
light: this.context.asAbsolutePath("resources/explorer-result-blank.svg"),
dark: this.context.asAbsolutePath("resources/explorer-result-blank.svg"),
};
} else {
checkNode.iconPath = {
checkNode.iconPath = {
light: this.context.asAbsolutePath("resources/explorer-result-oos-light.svg"),
dark: this.context.asAbsolutePath("resources/explorer-result-oos-dark.svg"),
};
}
}
}

private async refreshSeverityNodeCounts() : Promise<number> {
private async refreshSeverityNodeCounts(): Promise<number> {
// Refresh our counts for every severity class
let totalCount : number = 0;
let totalCount: number = 0;
for (let severityNode of this.bySeverityNode.nodes) {
let severityIssueCount = this.getFilteredChildren(severityNode.nodes).length;
totalCount += severityIssueCount;
Expand Down Expand Up @@ -180,15 +182,15 @@ export class SlitherExplorer implements vscode.TreeDataProvider<ExplorerNode> {
else {
this.rootNode = this.bySeverityNode;
}

// Fire the event to refresh our tree
this.changeTreeEmitter.fire();
}

public async refreshExplorer(reloadResults : boolean = true, logging : boolean = true) {
public async refreshExplorer(reloadResults: boolean = true, logging: boolean = true) {
// Read our last slither results if the user indicates we wish to.
if (reloadResults) {
let success : boolean = await slither.readResults(false);
let success: boolean = await slither.readResults(false);
if (!success) {
return;
}
Expand Down Expand Up @@ -220,15 +222,15 @@ export class SlitherExplorer implements vscode.TreeDataProvider<ExplorerNode> {
this.byTypeNode.nodes.push(typeNode);
this.byTypeMap.set(detector.check, typeNode);
}

// Loop for each result.
let issueCount : number = 0;
let issueCount: number = 0;
for (let [workspaceFolder, workspaceResults] of slither.results) {
// Organize the workspace results.
workspaceResults.sort((a, b) => (a.description > b.description) ? 1 : -1);

// Loop through all the results.
for(let workspaceResult of workspaceResults) {
for (let workspaceResult of workspaceResults) {
// Add to our issue count
issueCount++;

Expand All @@ -247,7 +249,7 @@ export class SlitherExplorer implements vscode.TreeDataProvider<ExplorerNode> {

// Add our issue by type
let typeNode = this.byTypeMap.get(workspaceResult.check);
if(!typeNode) {
if (!typeNode) {
Logger.error(`Failed to populate results by type with unknown detector type "${workspaceResult.check}"`);
continue;
}
Expand All @@ -274,29 +276,29 @@ export class SlitherExplorer implements vscode.TreeDataProvider<ExplorerNode> {
await extension.diagnosticsProvider.refreshDiagnostics();
}

public async clickedNode(node : ExplorerNode) {
public async clickedNode(node: ExplorerNode) {
// If this is a check result node, go to it.
if (node instanceof CheckResultNode) {
let checkResultNode = node as CheckResultNode;
slitherResults.gotoResultCode(checkResultNode.workspaceFolder, checkResultNode.result);
}
}

public async printDetailedDescription(node : CheckResultNode) {
public async printDetailedDescription(node: CheckResultNode) {

// Obtain the detector for this result
let detector = slither.detectorsByCheck.get(node.result.check);
if(!detector) {
if (!detector) {
Logger.error(`Could not print result information. Detector "${node.result.check}" could not be resolved.`);
return;
}

// Print the header
Logger.log("\u2E3B\u2E3B Detailed Result Output \u2E3B\u2E3B")

// Print our initial message
Logger.log(
`Type: ${detector.title} (${node.result.check})
`Type: ${detector.title} (${node.result.check})
Impact: ${node.result.impact}
Confidence: ${node.result.confidence}
Finding:`);
Expand All @@ -306,10 +308,10 @@ Finding:`);

// Print the recommendation
Logger.log(
`Recommendation: ${detector.recommendation}
`Recommendation: ${detector.recommendation}

Reference: ${detector.wiki_url}`
);
);

// Print the footer
Logger.log("\u2E3B\u2E3B\u2E3B\u2E3B\u2E3B\u2E3B\u2E3B\u2E3B\n");
Expand All @@ -319,7 +321,7 @@ Reference: ${detector.wiki_url}`
return element;
}

public getNodeFromResult(result : slitherResults.SlitherResult) : CheckResultNode | undefined {
public getNodeFromResult(result: slitherResults.SlitherResult): CheckResultNode | undefined {
return this.byResultMap.get(result);
}

Expand All @@ -340,10 +342,10 @@ Reference: ${detector.wiki_url}`
return undefined;
}

private getFilteredChildren(children : ExplorerNode[]) : ExplorerNode[] {
private getFilteredChildren(children: ExplorerNode[]): ExplorerNode[] {
// Using our provided child list, we remove all items which do not conform to the enabled detector list.
let filteredChildren : ExplorerNode[] = [];
for(let childNode of children) {
let filteredChildren: ExplorerNode[] = [];
for (let childNode of children) {
// If this is a result which is hidden, we skip it.
if (childNode instanceof CheckResultNode) {
if (this.hiddenDetectors.has(childNode.result.check)) {
Expand All @@ -358,7 +360,7 @@ Reference: ${detector.wiki_url}`

public getChildren(element?: ExplorerNode): ExplorerNode[] | Thenable<ExplorerNode[]> {
// Create our resulting list
let children : ExplorerNode[] = [];
let children: ExplorerNode[] = [];

// If there is a provided node, return its subnodes.
if (element) {
Expand All @@ -372,7 +374,7 @@ Reference: ${detector.wiki_url}`
if (element && this.rootNode == this.bySeverityNode) {
// We filter the children under each severity node
children = this.getFilteredChildren(children);
} else if(!element && this.rootNode == this.byTypeNode) {
} else if (!element && this.rootNode == this.byTypeNode) {
// We filter all type nodes
children = children.filter(x => !this.hiddenDetectors.has((<CheckTypeNode>x).check) && x.nodes.length > 0);
}
Expand Down
Loading