From 64814ebaa2e3ff47ab3aff3010b117fbe07d8d2f Mon Sep 17 00:00:00 2001
From: Rajpreet Singh <63117988+rajpreet-s@users.noreply.github.com>
Date: Mon, 12 Aug 2024 13:52:05 +0530
Subject: [PATCH] 12 hover provider for manifest files (#39)
* refactored
* Added vulnerabilities data in hover menu
* refactored
---
package.json | 4 +-
src/constants/organization.ts | 6 ++-
src/helpers/apiHelper.ts | 4 ++
src/helpers/index.ts | 3 ++
src/helpers/template.ts | 33 ++++++++++++++++
.../manifestDependencyHoverProvider.ts | 38 +++++++++++++------
src/services/dependencyService.ts | 21 +++++++++-
src/services/scanService.ts | 20 +++++++++-
src/types/dependency.ts | 4 +-
src/types/requestParam.ts | 1 +
src/types/vulnerability.ts | 20 ++++++++++
11 files changed, 134 insertions(+), 20 deletions(-)
create mode 100644 src/helpers/template.ts
create mode 100644 src/types/vulnerability.ts
diff --git a/package.json b/package.json
index bb32f6d..1d347cb 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,9 @@
"capabilities": {
"hoverProvider": "true"
},
- "activationEvents": [],
+ "activationEvents": [
+ "*"
+ ],
"main": "./dist/extension.js",
"repository": {
"type": "git",
diff --git a/src/constants/organization.ts b/src/constants/organization.ts
index 77ad3b7..8c04dd5 100644
--- a/src/constants/organization.ts
+++ b/src/constants/organization.ts
@@ -5,7 +5,11 @@ import * as os from "os";
export class Organization {
static readonly name = "debricked";
static readonly apiVersion = "1.0";
- static readonly baseUrl = `https://debricked.com/api/${Organization.apiVersion}/`;
+ static readonly debrickedBaseUrl = "https://debricked.com";
+ static readonly baseUrl = `${Organization.debrickedBaseUrl}/api/${Organization.apiVersion}/`;
+
+ static readonly dependencyUrl = "open/dependencies/get-dependencies-hierarchy";
+ static readonly vulnerableUrl = "open/vulnerabilities/get-vulnerabilities";
static readonly nameCaps = "Debricked";
// Command and OS-specific constants
diff --git a/src/helpers/apiHelper.ts b/src/helpers/apiHelper.ts
index 9030c9d..51d1515 100644
--- a/src/helpers/apiHelper.ts
+++ b/src/helpers/apiHelper.ts
@@ -28,6 +28,10 @@ export class ApiHelper {
params.push(`commitId=${requestParam.commitId}`);
}
+ if (requestParam.dependencyId) {
+ params.push(`dependencyId=${requestParam.dependencyId}`);
+ }
+
if (params.length > 0) {
url += `?${params.join("&")}`;
}
diff --git a/src/helpers/index.ts b/src/helpers/index.ts
index 002eb81..653dbf0 100644
--- a/src/helpers/index.ts
+++ b/src/helpers/index.ts
@@ -18,6 +18,7 @@ import { ErrorHandler } from "./errorHandler";
import { ApiClient } from "./apiClient";
import { GlobalStore } from "./globalStore";
import { Organization } from "../constants";
+import { Template } from "./template";
class IndexHelper {
constructor(
@@ -61,6 +62,7 @@ const installHelper = new InstallHelper(Logger, statusBarMessageHelper, commandH
const fileHelper = new FileHelper(debrickedDataHelper, Logger, globalStore);
const indexHelper = new IndexHelper(debrickedDataHelper, commonHelper, gitHelper);
const showQuickPickHelper = new ShowQuickPickHelper();
+const template = new Template();
export {
authHelper,
@@ -81,4 +83,5 @@ export {
apiClient,
globalStore,
indexHelper,
+ template,
};
diff --git a/src/helpers/template.ts b/src/helpers/template.ts
new file mode 100644
index 0000000..9402306
--- /dev/null
+++ b/src/helpers/template.ts
@@ -0,0 +1,33 @@
+import { Organization } from "../constants";
+import { DependencyVulnerability } from "types/vulnerability";
+import * as vscode from "vscode";
+
+export class Template {
+ constructor() {}
+ public licenseContent(data: string, contents: vscode.MarkdownString) {
+ contents.appendMarkdown(`License: **${data}**`);
+ contents.appendText("\n______________________________\n");
+ }
+
+ public vulnerableContent(data: DependencyVulnerability[], contents: vscode.MarkdownString): void {
+ if (data.length === 0) {
+ contents.appendMarkdown("No vulnerabilities found");
+ return;
+ }
+
+ contents.appendMarkdown(`Vulnerabilities Found: **${data.length}**\n\n`);
+
+ const vulnerabilitiesToShow = data.slice(0, 2);
+ vulnerabilitiesToShow.forEach((vulnerability) => {
+ contents.appendMarkdown(
+ `[**${vulnerability.cveId}**](${Organization.debrickedBaseUrl + vulnerability.name.link})`,
+ );
+
+ if (vulnerability.cvss) {
+ contents.appendMarkdown(` - CVSS: ${vulnerability.cvss.text} (${vulnerability.cvss.type})`);
+ }
+
+ contents.appendMarkdown("\n\n");
+ });
+ }
+}
diff --git a/src/providers/manifestDependencyHoverProvider.ts b/src/providers/manifestDependencyHoverProvider.ts
index c754725..dda5fc7 100644
--- a/src/providers/manifestDependencyHoverProvider.ts
+++ b/src/providers/manifestDependencyHoverProvider.ts
@@ -1,6 +1,8 @@
import * as vscode from "vscode";
import * as path from "path";
-import { globalStore } from "../helpers";
+import { globalStore, template } from "../helpers";
+import { DependencyService } from "services";
+import { DependencyVulnerability } from "types/vulnerability";
export class ManifestDependencyHoverProvider implements vscode.HoverProvider {
private manifestFiles: string[] = [];
@@ -30,22 +32,34 @@ export class ManifestDependencyHoverProvider implements vscode.HoverProvider {
const lineText = document.lineAt(position.line).text;
const dependencyName = this.parseDependencyName(lineText, currentManifestFile);
- if (dependencyName) {
- const depData = globalStore.getDependencyData().get(dependencyName);
- const licenseData = depData?.licenses[0]?.name ?? "License information unavailable";
+ if (!dependencyName) {
+ return null;
+ }
+
+ const depData = globalStore.getDependencyData().get(dependencyName);
+ const licenseData = depData?.licenses[0]?.name ?? "License information unavailable";
+ const vulnerableData = await this.getVulnerableData(depData?.id);
- const contents = new vscode.MarkdownString(
- `Debricked`,
- );
- contents.supportHtml = true;
- contents.isTrusted = true;
+ const contents = this.createMarkdownString();
+ template.licenseContent(licenseData, contents);
+ template.vulnerableContent(vulnerableData, contents);
- const hoverContent = [`**${dependencyName}**`, contents, `License: ${licenseData}`];
+ return new vscode.Hover(contents);
+ }
- return new vscode.Hover(hoverContent);
+ private async getVulnerableData(dependencyId?: number): Promise {
+ if (dependencyId) {
+ return await DependencyService.getVulnerableData(dependencyId);
}
+ return [];
+ }
- return null;
+ private createMarkdownString(): vscode.MarkdownString {
+ const contents = new vscode.MarkdownString();
+ contents.supportHtml = true;
+ contents.isTrusted = true;
+ contents.supportThemeIcons = true;
+ return contents;
}
private parseDependencyName(lineText: string, fileName: string): string | null {
diff --git a/src/services/dependencyService.ts b/src/services/dependencyService.ts
index 6286762..39bdf0e 100644
--- a/src/services/dependencyService.ts
+++ b/src/services/dependencyService.ts
@@ -1,12 +1,14 @@
import { Dependency, DependencyResponse, IndirectDependency } from "types/dependency";
import { apiHelper, globalStore, Logger } from "../helpers";
import { RequestParam } from "../types";
+import { DependencyVulnerabilityWrapper } from "types/vulnerability";
+import { Organization } from "../constants";
export class DependencyService {
static async getDependencyData(repoID: number, commitId: number) {
Logger.logInfo("Started fetching the Dependency Data");
const requestParam: RequestParam = {
- endpoint: "open/dependencies/get-dependencies-hierarchy",
+ endpoint: Organization.dependencyUrl,
repoId: repoID,
commitId: commitId,
};
@@ -24,6 +26,21 @@ export class DependencyService {
});
globalStore.setDependencyData(dependencyMap);
- Logger.logObj(response);
+ }
+
+ static async getVulnerableData(depId: number) {
+ Logger.logInfo("Started fetching the Vulnerable Data");
+ const repoId = await globalStore.getRepoId();
+ const commitId = await globalStore.getCommitId();
+
+ const requestParam: RequestParam = {
+ endpoint: Organization.vulnerableUrl,
+ repoId: repoId,
+ commitId: commitId,
+ dependencyId: depId,
+ };
+ const response: DependencyVulnerabilityWrapper = await apiHelper.get(requestParam);
+ const vulnerableData = response.vulnerabilities;
+ return vulnerableData;
}
}
diff --git a/src/services/scanService.ts b/src/services/scanService.ts
index 0b54734..7242a3f 100644
--- a/src/services/scanService.ts
+++ b/src/services/scanService.ts
@@ -8,10 +8,13 @@ import {
commonHelper,
commandHelper,
authHelper,
+ fileHelper,
} from "../helpers";
import { DebrickedCommands, MessageStatus, Organization } from "../constants/index";
import { DebrickedCommandNode, Flag, RepositoryInfo } from "../types";
import * as vscode from "vscode";
+import * as fs from "fs";
+import { DependencyService } from "./dependencyService";
export class ScanService {
static async scanService() {
@@ -36,7 +39,6 @@ export class ScanService {
await ScanService.handleFlags(command.flags[2], cmdParams, currentRepoData);
await ScanService.handleFlags(command.flags[3], cmdParams, currentRepoData);
await ScanService.handleFlags(command.flags[4], cmdParams, currentRepoData);
- await ScanService.handleFlags(command.flags[4], cmdParams, currentRepoData);
await ScanService.handleFlags(command.global_flags[0], cmdParams, currentRepoData);
}
} else {
@@ -61,7 +63,21 @@ export class ScanService {
},
async (progress) => {
progress.report({ message: "Scanning Manifest Files🚀" });
- await commandHelper.executeAsyncCommand(`${Organization.debrickedCli} ${cmdParams.join(" ")}`);
+ const output = await commandHelper.executeAsyncCommand(
+ `${Organization.debrickedCli} ${cmdParams.join(" ")}`,
+ );
+ if (!output.includes("https://debricked.com/app/en/repository/")) {
+ if (await fs.existsSync(`${Organization.reportsFolderPath}/scan-output.json`)) {
+ await fileHelper.setRepoID();
+
+ const repoId = await globalStore.getRepoId();
+ const commitId = await globalStore.getCommitId();
+
+ await DependencyService.getDependencyData(repoId, commitId);
+ } else {
+ throw new Error("No reports file exists");
+ }
+ }
statusBarMessageHelper.setStatusBarMessage(`Debricked: Scanning Completed $(pass-filled)`, 1000);
},
);
diff --git a/src/types/dependency.ts b/src/types/dependency.ts
index ade8d8f..3135ffb 100644
--- a/src/types/dependency.ts
+++ b/src/types/dependency.ts
@@ -12,13 +12,13 @@ export interface DependencyResponse {
commitName: string;
dependencies: Dependency[];
}
-export interface DependencyName {
+interface DependencyName {
name: string;
shortName: string;
link: string;
}
-export interface DependencyLicense {
+interface DependencyLicense {
name: string;
}
diff --git a/src/types/requestParam.ts b/src/types/requestParam.ts
index fd6e258..955c56e 100644
--- a/src/types/requestParam.ts
+++ b/src/types/requestParam.ts
@@ -4,4 +4,5 @@ export interface RequestParam {
repoId?: number;
endpoint: string;
commitId?: number;
+ dependencyId?: number;
}
diff --git a/src/types/vulnerability.ts b/src/types/vulnerability.ts
new file mode 100644
index 0000000..394fa7e
--- /dev/null
+++ b/src/types/vulnerability.ts
@@ -0,0 +1,20 @@
+export interface DependencyVulnerabilityWrapper {
+ vulnerabilities: DependencyVulnerability[];
+}
+
+export interface DependencyVulnerability {
+ cveId: string;
+ cvss: CVSS;
+ cpeVersions: string[];
+ name: VulnerabilityName
+}
+
+interface CVSS {
+ text: number;
+ type: string;
+}
+
+interface VulnerabilityName {
+ name: string;
+ link: string;
+}
\ No newline at end of file