blob: 46699e5fd90a519360b3ce2737253c55a20ed32e [file] [log] [blame]
/*---------------------------------------------------------
* Copyright 2022 The Go Authors. All rights reserved.
* Licensed under the MIT License. See LICENSE in the project root for license information.
*--------------------------------------------------------*/
import assert from 'assert';
import path = require('path');
import vscode = require('vscode');
import { extensionId } from '../../src/const';
import goVulncheck = require('../../src/goVulncheck');
suite('vulncheck result viewer tests', () => {
const webviewId = 'vulncheck';
const extensionUri = vscode.extensions.getExtension(extensionId)!.extensionUri;
const fixtureDir = path.join(__dirname, '..', '..', '..', 'test', 'testdata', 'vuln');
const disposables: vscode.Disposable[] = [];
function _register<T extends vscode.Disposable>(disposable: T) {
disposables.push(disposable);
return disposable;
}
let provider: goVulncheck.VulncheckResultViewProvider;
setup(() => {
provider = new goVulncheck.VulncheckResultViewProvider(extensionUri, {});
});
teardown(async () => {
await vscode.commands.executeCommand('workbench.action.closeAllEditors');
vscode.Disposable.from(...disposables).dispose();
});
test('populates webview', async () => {
const doTest = async (tag: string) => {
const webviewPanel = _register(
vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, {})
);
const source = path.join(fixtureDir, 'test.vulncheck.json');
const doc = await vscode.workspace.openTextDocument(source);
console.timeLog(tag, 'opened document');
const canceller = new vscode.CancellationTokenSource();
_register(canceller);
const watcher = getMessage<{ type: string; target?: string }>(webviewPanel);
await provider.resolveCustomTextEditor(doc, webviewPanel, canceller.token);
console.timeLog(tag, 'resolved custom text editor');
webviewPanel.reveal();
// Trigger snapshotContent that sends `snapshot-result` as a result.
webviewPanel.webview.postMessage({ type: 'snapshot-request' });
console.timeLog(tag, 'posted snapshot-request');
const res = await watcher;
console.timeLog(tag, 'received message');
assert.deepStrictEqual(res.type, 'snapshot-result', `want snapshot-result, got ${JSON.stringify(res)}`);
// res.target type is defined in vulncheckView.js.
const { log = '', vulns = '', unaffecting = '' } = JSON.parse(res.target ?? '{}');
assert(
log.includes('1 known vulnerabilities'),
`expected "1 known vulnerabilities", got ${JSON.stringify(res.target)}`
);
assert(
vulns.includes('GO-2021-0113') &&
vulns.includes('<td>Affecting</td><td>github.com/golang/vscode-go/test/testdata/vuln</td>'),
`expected "Affecting" section, got ${JSON.stringify(res.target)}`
);
// Unaffecting vulnerability's ID is reported.
assert(
unaffecting.includes('GO-2021-0000') && unaffecting.includes('golang.org/x/text'),
`expected reports about unaffecting vulns, got ${JSON.stringify(res.target)}`
);
};
try {
console.time('populates-webview');
await doTest('populates-webview');
} catch (e) {
console.timeLog('populates-webview', `error thrown: ${e}`);
throw e;
} finally {
console.timeEnd('populates-webview');
}
}).timeout(5_000);
test('handles empty input', async () => {
const webviewPanel = _register(
vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, {})
);
// Empty doc.
const doc = await vscode.workspace.openTextDocument(
vscode.Uri.file('bogus.vulncheck.json').with({ scheme: 'untitled' })
);
const canceller = new vscode.CancellationTokenSource();
_register(canceller);
const watcher = getMessage<{ type: string; target?: string }>(webviewPanel);
await provider.resolveCustomTextEditor(doc, webviewPanel, canceller.token);
webviewPanel.reveal();
// Trigger snapshotContent that sends `snapshot-result` as a result.
webviewPanel.webview.postMessage({ type: 'snapshot-request' });
const res = await watcher;
assert.deepStrictEqual(res.type, 'snapshot-result', `want snapshot-result, got ${JSON.stringify(res)}`);
const { log = '', vulns = '', unaffecting = '' } = JSON.parse(res.target ?? '{}');
assert(!log && !vulns && !unaffecting, res.target);
});
// TODO: test corrupted/incomplete json file handling.
});
function getMessage<R = { type: string; target?: string }>(webview: vscode.WebviewPanel): Promise<R> {
return new Promise<R>((resolve) => {
const sub = webview.webview.onDidReceiveMessage((message) => {
sub.dispose();
resolve(message);
});
});
}
suite('fillAffectedPkgs', () => {
test('compute from the first call stack entry', async () => {
const data = JSON.parse(`{
"Vuln": [{
"CallStacks": [
[
{
"Name": "github.com/golang/vscode-go/test/testdata/vuln.main",
"URI": "file:///vuln/test.go",
"Pos": { "line": 9, "character": 0 }
},
{
"Name": "golang.org/x/text/language.Parse",
"URI": "file:///foo/bar.go",
"Pos": { "line": 227, "character": 0 }
}
]
]}]}`);
goVulncheck.fillAffectedPkgs(data.Vuln);
assert.deepStrictEqual(data.Vuln[0].AffectedPkgs, ['github.com/golang/vscode-go/test/testdata/vuln']);
});
test('callstacks missing', async () => {
const data = JSON.parse('{ "Vuln": [{}] }');
goVulncheck.fillAffectedPkgs(data.Vuln);
assert.deepStrictEqual(data.Vuln[0].AffectedPkgs, []);
});
test('callstacks empty', async () => {
const data = JSON.parse('{ "Vuln": [{"CallStacks": []}] }');
goVulncheck.fillAffectedPkgs(data.Vuln);
assert.deepStrictEqual(data.Vuln[0].AffectedPkgs, []);
});
test('first call stack entry is missing Name', async () => {
const data = JSON.parse(`{
"Vuln": [{ "CallStacks": [ [ { "URI": "file:///vuln/test.go" } ] ]}]}`);
goVulncheck.fillAffectedPkgs(data.Vuln);
assert.deepStrictEqual(data.Vuln[0].AffectedPkgs, []);
});
});