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

Protect against scanning unintended directories #40

Merged
merged 3 commits into from
Oct 5, 2020
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
54 changes: 49 additions & 5 deletions src/GrypeExtension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import { VulnerabilityReportSerializer } from "./ui/VulnerabilityReportSerialize
import { ExecutableNotFoundError } from "./executable/ExecutableNotFoundError";
import { ExitCodeNonZeroError } from "./executable/ExitCodeNonZeroError";
import { StatusBarQuickPick } from "./ui/StatusBarQuickPick";
import { NoWorkspaceFolderError } from "./NoWorkspaceFolderError";
import { MultipleWorkspaceFoldersError } from "./MultipleWorkspaceFoldersError";
import { RootDirectoryScanError } from "./executable/RootDirectoryScanError";

export default class GrypeExtension {
private static readonly isAutomaticScanningEnabledKey =
Expand Down Expand Up @@ -80,6 +83,17 @@ export default class GrypeExtension {
return this.scanReport ? true : false;
}

private get directory(): string {
const { workspace } = vscode;

if (workspace.workspaceFolders && workspace.workspaceFolders.length === 1) {
const workspaceFolder = workspace.workspaceFolders[0];
return workspaceFolder.uri.fsPath;
}

return "";
}

private async initializeWatcher(): Promise<void> {
if (!this.watcher && this.grype) {
const patterns = await this.grype.globPatterns();
Expand Down Expand Up @@ -261,19 +275,43 @@ export default class GrypeExtension {
this.context.subscriptions.push(disposable);
}

private checkForNoWorkspaceFolders(): void {
const { workspace } = vscode;

if (
!workspace.workspaceFolders ||
workspace.workspaceFolders.length === 0
) {
throw new NoWorkspaceFolderError();
}
}

private checkForMultipleWorkspaceFolders(): void {
const { workspace } = vscode;

if (workspace.workspaceFolders && workspace.workspaceFolders.length > 1) {
throw new MultipleWorkspaceFoldersError(workspace.workspaceFolders);
}
}

private async scanWorkspace(): Promise<void> {
const root = vscode.workspace.rootPath;
if (!root) {
console.error("no workspace path defined");
try {
this.checkForNoWorkspaceFolders();
this.checkForMultipleWorkspaceFolders();
} catch (e) {
console.error(
`A scan was requested but scanning is not possible: ${e.message}`
);

this.statusBar.showError();
return;
}

if (this.grype) {
this.statusBar.showScanning();

// TODO: Catch errors and notify user of unsuccessful scan somehow
try {
this.scanReport = await this.grype.scan(root);
this.scanReport = await this.grype.scan(this.directory);
} catch (err) {
this.handleScanError(err);
return;
Expand All @@ -295,6 +333,7 @@ export default class GrypeExtension {

private handleScanError(err: Error): void {
this.statusBar.showError();
console.error(err.message);

if (err instanceof ExecutableNotFoundError) {
vscode.window.showErrorMessage(
Expand All @@ -312,6 +351,11 @@ export default class GrypeExtension {
return;
}

if (err instanceof RootDirectoryScanError) {
// No need to show an error message, this is clearly an unsupported scenario.
return;
}

vscode.window.showErrorMessage(
`Unable to run scan due to unexpected error: ${err}`
);
Expand Down
12 changes: 12 additions & 0 deletions src/MultipleWorkspaceFoldersError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { WorkspaceFolder } from "vscode";

export class MultipleWorkspaceFoldersError extends Error {
public readonly folders: ReadonlyArray<WorkspaceFolder>;

constructor(folders: ReadonlyArray<WorkspaceFolder>) {
const message = "Scanning multi-root workspaces is not yet supported.";
super(message);

this.folders = folders;
}
}
6 changes: 6 additions & 0 deletions src/NoWorkspaceFolderError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export class NoWorkspaceFolderError extends Error {
constructor() {
const message = "No workspace folder was found.";
super(message);
}
}
7 changes: 6 additions & 1 deletion src/executable/Grype.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import path = require("path");
import { IGrypeFinding } from "../IGrypeFinding";
import { ExecutableNotFoundError } from "./ExecutableNotFoundError";
import { ExitCodeNonZeroError } from "./ExitCodeNonZeroError";
import { RootDirectoryScanError } from "./RootDirectoryScanError";

interface IProcessResult {
stdout: string;
Expand Down Expand Up @@ -59,7 +60,11 @@ export class Grype {
}

public async scan(directory: string): Promise<IGrypeFinding[]> {
console.log("scanning...");
if (path.resolve(directory) === "/") {
throw new RootDirectoryScanError();
}

console.log(`scanning ${directory}...`);

await this.updateDb();
const result = await this.run(
Expand Down
6 changes: 6 additions & 0 deletions src/executable/RootDirectoryScanError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export class RootDirectoryScanError extends Error {
constructor() {
const message = "Scanning the root directory ('/') is not supported.";
super(message);
}
}