Skip to content
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
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@
{
"command": "devguard.removeGitHooks",
"title": "DevGuard: Removes Pre-Commit-Hooks for git that were previously setup by DevGuard"
},
{
"command": "devguard.generateVEX",
"title": "DevGuard: Generate VEX (Run devguard-scanner SCA)"
}
],
"configuration": {
Expand Down
96 changes: 94 additions & 2 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
//postCommitHooksExists,
} from "./commitHooks";
import { StatusBarCommitHooks } from "./ui/statusBarCommitHooks";
import os from "os";

export interface CommandDeps {
client: DevGuardClient;
Expand Down Expand Up @@ -57,6 +58,9 @@ export function registerCommands(deps: CommandDeps): vscode.Disposable[] {
vscode.commands.registerCommand("devguard.removeGitHooks", () => {
removeGitHooks(deps);
}),
vscode.commands.registerCommand("devguard.generateVEX", () => {
generateVEX(deps);
}),
];
}

Expand Down Expand Up @@ -445,7 +449,7 @@ async function setupGitHooks({
commitHookStatusBar,
}: CommandDeps): Promise<void> {
await setupGitCommitHooks(selection);
if ((await preCommitHooksExists()) /* && (await postCommitHooksExists()) */) {
if (await preCommitHooksExists() /* && (await postCommitHooksExists()) */) {
commitHookStatusBar.setActive(true);
} else {
commitHookStatusBar.setActive(false);
Expand All @@ -459,7 +463,7 @@ async function removeGitHooks({
commitHookStatusBar,
}: CommandDeps): Promise<void> {
await removeExistingGitHooks();
if ((await preCommitHooksExists())/* && (await postCommitHooksExists()) */) {
if (await preCommitHooksExists() /* && (await postCommitHooksExists()) */) {
commitHookStatusBar.setActive(true);
} else {
commitHookStatusBar.setActive(false);
Expand Down Expand Up @@ -512,3 +516,91 @@ function reportError(action: string, err: unknown, logger: Logger): void {
`DevGuard: failed to ${action} (see DevGuard output).`,
);
}

async function generateVEX({
logger,
sbomProvider,
}: CommandDeps): Promise<void> {
const repoPath =
vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? process.cwd();
const outputName = `.devguard-vex-${crypto.randomUUID()}`;

// Token is passed via DEVGUARD_TOKEN env (not argv) so it does not appear in the process list.
const args = [
"run",
"--rm",
"-v",
`${repoPath}:/repo`,
"-v",
`${os.tmpdir()}:${"/tmp"}`,
"ghcr.io/l3montree-dev/devguard/scanner:main",
"devguard-scanner",
"sca",
"--path=/repo",
"--output",
"cyclonedx",
];

logger.info(`Running SCA scan to generate VEX …`);

let output = "";
const capture = (chunk: Buffer): void => {
const text = chunk.toString();
output = text.slice(-8000);
};

const exitCode = await vscode.window.withProgress<number | undefined>(
{
location: vscode.ProgressLocation.Notification,
title: `DevGuard: Generating VEX …`,
cancellable: true,
},
(_progress, cancelToken) =>
new Promise<number | undefined>((resolve) => {
let child: ReturnType<typeof spawn> | undefined;
try {
child = spawn("docker", args);
} catch (err) {
logger.error("could not start the scanner", err);
resolve(undefined);
return;
}
cancelToken.onCancellationRequested(() => child?.kill());
child.stdout?.on("data", capture);
child.stderr?.on("data", capture);
child.on("error", (err) => {
logger.error("scanner process error", err);
resolve(undefined);
});
child.on("close", (code) => resolve(code ?? undefined));
}),
);

if (exitCode === undefined) {
vscode.window.showErrorMessage(
`DevGuard: could not run SCA scan. Install the devguard-scanner CLI or set devguard.scannerPath.`,
);
return;
}
if (exitCode === 0) {
let cdxjson = output;
try {
cdxjson = JSON.stringify(JSON.parse(output), null, 2);
} catch {}
await sbomProvider.open(outputName, cdxjson);
const successMessage = "DevGuard: SCA complete — VEX generated .";
logger.info(successMessage);
vscode.window.showInformationMessage(successMessage);
return;
}
// Since we use the normal sca scan, this error check applies here as well as for generateSBOM()
if (/invalid specification version|not a valid CycloneDX/i.test(output)) {
vscode.window.showErrorMessage(
"DevGuard: the generated VEX used a CycloneDX version the scanner could not read. Rebuild devguard-scanner from source (go install ./cmd/devguard-scanner) or align your trivy version, then retry.",
);
return;
}
vscode.window.showWarningMessage(
`DevGuard: scanner exited with code ${exitCode} (see the DevGuard output). Insights refreshed.`,
);
}