Skip to content

Commit 0848778

Browse files
committed
src/goVulncheck: add actions to upgrade modules
If fixed version is known, present two options in the webview: go get <the fixed version> go get @latest The webview html page listens the click event, and sends a 'fix' type message to the extension side. The extension then invokes gopls.upgrade_dependency command. Currently, this dependency upgrade command can result in inconsistent go.sum error and users will need to address them separately. Change-Id: Iceef1ce49a7eff57bb65d4d78e1ecab0b3b74f73 Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/412318 TryBot-Result: kokoro <[email protected]> Reviewed-by: Jamal Carvalho <[email protected]> Run-TryBot: Hyang-Ah Hana Kim <[email protected]>
1 parent 78149ef commit 0848778

File tree

5 files changed

+79
-9
lines changed

5 files changed

+79
-9
lines changed

media/vulncheckView.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@
2121
padding-bottom: 0.5em;
2222
}
2323

24+
.vuln-fix:hover,
25+
.vuln-fix:active {
26+
color: var(--vscode-textLink-activeForeground);
27+
}
28+
.vuln-fix {
29+
cursor:pointer;
30+
color: var(--vscode-textLink-foreground);
31+
text-decoration:underline;
32+
}
33+
2434
details summary {
2535
cursor: pointer;
2636
position: relative;

media/vulncheckView.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,18 @@
1616

1717
vulnsContainer.addEventListener('click', (event) => {
1818
let node = event && event.target;
19+
let handled = false;
20+
console.log(`${node.type} ${node.tagName} ${node.className} ${node.id} data:${node.dataset?.target} dir:${node.dataset?.dir}`);
1921
if (node?.tagName === 'A' && node.href) {
2022
// Ask vscode to handle link opening.
2123
vscode.postMessage({ type: 'open', target: node.href });
24+
} else if (node?.tagName === 'SPAN' && node.className === 'vuln-fix' && node.dataset?.target && node.dataset?.dir) {
25+
vscode.postMessage({ type: 'fix', target: node.dataset?.target, dir: node.dataset?.dir });
26+
}
27+
28+
if (handled) {
2229
event.preventDefault();
2330
event.stopPropagation();
24-
return;
2531
}
2632
});
2733

@@ -37,6 +43,13 @@
3743
return 'N/A'
3844
}
3945

46+
function offerUpgrade(/** @type {string} */dir, /** @type {string} */mod, /** @type {string|undefined} */ver) {
47+
if (dir && mod && ver) {
48+
return ` [<span class="vuln-fix" data-target="${mod}@${ver}" data-dir="${dir}">go get</span> | <span class="vuln-fix" data-target="${mod}@latest" data-dir="${dir}">go get latest</span>]`
49+
}
50+
return '';
51+
}
52+
4053
function snapshotContent() {
4154
const res = {
4255
'log': logContainer.innerHTML,
@@ -102,7 +115,7 @@
102115
details.innerHTML = `
103116
<tr><td>Package</td><td>${vuln.PkgPath}</td></tr>
104117
<tr><td>Found in Version</td><td>${moduleVersion(vuln.ModPath, vuln.CurrentVersion)}</td></tr>
105-
<tr><td>Fixed Version</td><td>${moduleVersion(vuln.ModPath, vuln.FixedVersion)}</td></tr>
118+
<tr><td>Fixed Version</td><td>${moduleVersion(vuln.ModPath, vuln.FixedVersion)} ${offerUpgrade(json.Dir, vuln.ModPath, vuln.FixedVersion)}</td></tr>
106119
<tr><td>Affecting</td><td>${vuln.AffectedPkgs?.join('<br>')}</td></tr>
107120
`;
108121
element.appendChild(details);

src/goMain.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<ExtensionA
170170

171171
GoExplorerProvider.setup(ctx);
172172
VulncheckProvider.setup(ctx, goCtx);
173-
VulncheckResultViewProvider.register(ctx);
173+
VulncheckResultViewProvider.register(ctx, goCtx);
174174

175175
registerCommand('go.test.generate.package', goGenerateTests.generateTestCurrentPackage);
176176
registerCommand('go.test.generate.file', goGenerateTests.generateTestCurrentFile);

src/goVulncheck.ts

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,22 @@ import { killProcessTree } from './utils/processUtils';
1414
import * as readline from 'readline';
1515
import { URI } from 'vscode-uri';
1616
import { promisify } from 'util';
17+
import { runGoEnv } from './goModules';
18+
import { ExecuteCommandParams, ExecuteCommandRequest } from 'vscode-languageserver-protocol';
1719

1820
export class VulncheckResultViewProvider implements vscode.CustomTextEditorProvider {
1921
public static readonly viewType = 'vulncheck.view';
2022

21-
public static register({ extensionUri, subscriptions }: vscode.ExtensionContext): VulncheckResultViewProvider {
22-
const provider = new VulncheckResultViewProvider(extensionUri);
23+
public static register(
24+
{ extensionUri, subscriptions }: vscode.ExtensionContext,
25+
goCtx: GoExtensionContext
26+
): VulncheckResultViewProvider {
27+
const provider = new VulncheckResultViewProvider(extensionUri, goCtx);
2328
subscriptions.push(vscode.window.registerCustomEditorProvider(VulncheckResultViewProvider.viewType, provider));
2429
return provider;
2530
}
2631

27-
constructor(private readonly extensionUri: vscode.Uri) {}
32+
constructor(private readonly extensionUri: vscode.Uri, private readonly goCtx: GoExtensionContext) {}
2833

2934
/**
3035
* Called when our custom editor is opened.
@@ -39,7 +44,7 @@ export class VulncheckResultViewProvider implements vscode.CustomTextEditorProvi
3944
webviewPanel.webview.html = this.getHtmlForWebview(webviewPanel.webview);
4045

4146
// Receive message from the webview.
42-
webviewPanel.webview.onDidReceiveMessage(this.handleMessage);
47+
webviewPanel.webview.onDidReceiveMessage(this.handleMessage, this);
4348

4449
function updateWebview() {
4550
webviewPanel.webview.postMessage({ type: 'update', text: document.getText() });
@@ -105,7 +110,7 @@ export class VulncheckResultViewProvider implements vscode.CustomTextEditorProvi
105110
</html>`;
106111
}
107112

108-
private handleMessage(e: { type: string; target?: string }): void {
113+
private async handleMessage(e: { type: string; target?: string; dir?: string }): Promise<void> {
109114
switch (e.type) {
110115
case 'open':
111116
{
@@ -125,6 +130,16 @@ export class VulncheckResultViewProvider implements vscode.CustomTextEditorProvi
125130
}
126131
}
127132
return;
133+
case 'fix':
134+
{
135+
if (!e.target || !e.dir) return;
136+
const modFile = await getGoModFile(vscode.Uri.file(e.dir));
137+
if (modFile) {
138+
await goplsUpgradeDependency(this.goCtx, vscode.Uri.file(modFile), [e.target], false);
139+
// TODO: run go mod tidy?
140+
}
141+
}
142+
return;
128143
case 'snapshot-result':
129144
// response for `snapshot-request`.
130145
return;
@@ -134,6 +149,38 @@ export class VulncheckResultViewProvider implements vscode.CustomTextEditorProvi
134149
}
135150
}
136151

152+
const GOPLS_UPGRADE_DEPENDENCY = 'gopls.upgrade_dependency';
153+
async function goplsUpgradeDependency(
154+
goCtx: GoExtensionContext,
155+
goModFileUri: vscode.Uri,
156+
goCmdArgs: string[],
157+
addRequire: boolean
158+
): Promise<void> {
159+
const { languageClient } = goCtx;
160+
const uri = languageClient?.code2ProtocolConverter.asUri(goModFileUri);
161+
const params: ExecuteCommandParams = {
162+
command: GOPLS_UPGRADE_DEPENDENCY,
163+
arguments: [
164+
{
165+
URI: uri,
166+
GoCmdArgs: goCmdArgs,
167+
AddRequire: addRequire
168+
}
169+
]
170+
};
171+
return await languageClient?.sendRequest(ExecuteCommandRequest.type, params);
172+
}
173+
174+
async function getGoModFile(dir: vscode.Uri): Promise<string | undefined> {
175+
try {
176+
const p = await runGoEnv(dir, ['GOMOD']);
177+
return p['GOMOD'] === '/dev/null' || p['GOMOD'] === 'NUL' ? '' : p['GOMOD'];
178+
} catch (e) {
179+
vscode.window.showErrorMessage(`Failed to find 'go.mod' for ${dir}: ${e}`);
180+
}
181+
return;
182+
}
183+
137184
export class VulncheckProvider {
138185
static scheme = 'govulncheck';
139186
static setup({ subscriptions }: vscode.ExtensionContext, goCtx: GoExtensionContext) {

test/gopls/vulncheck.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ suite('vulncheck result viewer tests', () => {
2121
let provider: goVulncheck.VulncheckResultViewProvider;
2222

2323
setup(() => {
24-
provider = new goVulncheck.VulncheckResultViewProvider(extensionUri);
24+
provider = new goVulncheck.VulncheckResultViewProvider(extensionUri, {});
2525
});
2626

2727
teardown(async () => {

0 commit comments

Comments
 (0)