Skip to content

Commit ea2a051

Browse files
committed
Fix Go to Super Implementation hover link not clickable (#4438)
The command-link sanitization added in fixJdtSchemeHoverLinks stripped all [label](command:...) links, including the trusted ones the extension contributes (e.g. Go to Super Implementation). Skip sanitizing trusted contributed content; only untrusted server Javadoc is sanitized. Add unit tests covering the hover link sanitization.
1 parent 6109b9a commit ea2a051

3 files changed

Lines changed: 45 additions & 2 deletions

File tree

src/hoverAction.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class JavaHoverProvider implements HoverProvider {
7474
}
7575

7676
const contributed = new MarkdownString(contributedCommands.map((command) => this.convertCommandToMarkdown(command)).join(' | '));
77-
contributed.isTrusted = true;
77+
contributed.isTrusted = { enabledCommands: contributedCommands.map((command) => command.command) };
7878
let contents: MarkdownString[] = [ contributed ];
7979
let range;
8080
if (serverHover && serverHover.contents) {

src/providerDispatcher.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,13 @@ export function fixJdtSchemeHoverLinks(hover: Hover): Hover {
211211
const newContents: (MarkedString | MarkdownString)[] = [];
212212
for (const content of hover.contents) {
213213
if (content instanceof MarkdownString) {
214-
newContents.push(fixJdtLinksInDocumentation(content));
214+
// Skip our own trusted contributed commands (e.g. "Go to Super Implementation");
215+
// only sanitize untrusted server-provided Javadoc.
216+
if (content.isTrusted) {
217+
newContents.push(content);
218+
} else {
219+
newContents.push(fixJdtLinksInDocumentation(content));
220+
}
215221
} else {
216222
newContents.push(content);
217223
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use strict';
2+
3+
import * as assert from 'assert';
4+
import { Hover, MarkdownString } from 'vscode';
5+
import { fixJdtSchemeHoverLinks } from '../../src/providerDispatcher';
6+
7+
suite('Hover Links Test', () => {
8+
9+
test('trusted contributed command links are preserved (super implementation)', () => {
10+
const contributed = new MarkdownString('[Go to Super Implementation](command:java.action.navigateToSuperImplementation?%5B%5D)');
11+
contributed.isTrusted = { enabledCommands: ['java.action.navigateToSuperImplementation'] };
12+
const hover = new Hover([contributed]);
13+
14+
const fixed = fixJdtSchemeHoverLinks(hover);
15+
const value = (fixed.contents[0] as MarkdownString).value;
16+
assert.ok(value.includes('(command:java.action.navigateToSuperImplementation'), 'contributed command link should not be sanitized');
17+
});
18+
19+
test('untrusted server command links are sanitized', () => {
20+
const javadoc = new MarkdownString('[click here](command:evil.command?param=true)');
21+
const hover = new Hover([javadoc]);
22+
23+
const fixed = fixJdtSchemeHoverLinks(hover);
24+
const value = (fixed.contents[0] as MarkdownString).value;
25+
assert.strictEqual(value, 'click here', 'server command link should be stripped to its label');
26+
});
27+
28+
test('jdt:// links are converted to command links', () => {
29+
const javadoc = new MarkdownString('[String](jdt://contents/Foo.class)');
30+
const hover = new Hover([javadoc]);
31+
32+
const fixed = fixJdtSchemeHoverLinks(hover);
33+
const value = (fixed.contents[0] as MarkdownString).value;
34+
assert.ok(value.includes('(command:'), 'jdt link should be converted to a command link');
35+
assert.ok(!value.includes('jdt://'), 'jdt scheme should be replaced');
36+
});
37+
});

0 commit comments

Comments
 (0)