Skip to content

Commit

Permalink
prevent starting href template with dangerous protocols
Browse files Browse the repository at this point in the history
  • Loading branch information
samisayegh committed Nov 3, 2020
1 parent 6eff507 commit 2d55a52
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 12 deletions.
3 changes: 2 additions & 1 deletion src/ui/ResultLink/ResultLink.ts
Expand Up @@ -487,7 +487,8 @@ export class ResultLink extends Component {

private getResultUri(): string {
if (this.options.hrefTemplate) {
return StringUtils.buildStringTemplateFromResult(this.options.hrefTemplate, this.result);
const uri = StringUtils.buildStringTemplateFromResult(this.options.hrefTemplate, this.result);
return this.filterProtocol(uri);
}

if (this.options.field == undefined && this.options.openInOutlook) {
Expand Down
29 changes: 18 additions & 11 deletions unitTests/ui/ResultLinkTest.ts
Expand Up @@ -126,49 +126,56 @@ export function ResultLinkTest() {
});

describe('exposes hrefTemplate', () => {
it('when the href starts with a protocol that enables XSS, it returns an empty string', () => {
const hrefTemplate = 'javascript:alert(1)';
test = Mock.optionsResultComponentSetup<ResultLink, IResultLinkOptions>(ResultLink, { hrefTemplate: hrefTemplate }, fakeResult);
test.cmp.openLinkInNewWindow();
expect(window.open).toHaveBeenCalledWith('', jasmine.anything());
});

it('should not modify the href template if there are no field specified', () => {
let hrefTemplate = 'test';
let hrefTemplate = 'http://test';
test = Mock.optionsResultComponentSetup<ResultLink, IResultLinkOptions>(ResultLink, { hrefTemplate: hrefTemplate }, fakeResult);
test.cmp.openLinkInNewWindow();
expect(window.open).toHaveBeenCalledWith(hrefTemplate, jasmine.anything());
});

it('should replace fields in the href template by the results equivalent', () => {
let hrefTemplate = '${title}';
let hrefTemplate = 'http://${title}';
test = Mock.optionsResultComponentSetup<ResultLink, IResultLinkOptions>(ResultLink, { hrefTemplate: hrefTemplate }, fakeResult);
test.cmp.openLinkInNewWindow();
expect(window.open).toHaveBeenCalledWith(fakeResult.title, jasmine.anything());
expect(window.open).toHaveBeenCalledWith(`http://${fakeResult.title}`, jasmine.anything());
});

it('should support nested values in result', () => {
let hrefTemplate = '${raw.number}';
let hrefTemplate = 'http://${raw.number}';
test = Mock.optionsResultComponentSetup<ResultLink, IResultLinkOptions>(ResultLink, { hrefTemplate: hrefTemplate }, fakeResult);
test.cmp.openLinkInNewWindow();
expect(window.open).toHaveBeenCalledWith(fakeResult.raw['number'].toString(), jasmine.anything());
expect(window.open).toHaveBeenCalledWith(`http://${fakeResult.raw['number'].toString()}`, jasmine.anything());
});

it('should not parse standalone accolades', () => {
let hrefTemplate = '${raw.number}{test}';
let hrefTemplate = 'http://${raw.number}{test}';
test = Mock.optionsResultComponentSetup<ResultLink, IResultLinkOptions>(ResultLink, { hrefTemplate: hrefTemplate }, fakeResult);
test.cmp.openLinkInNewWindow();
expect(window.open).toHaveBeenCalledWith(fakeResult.raw['number'] + '{test}', jasmine.anything());
expect(window.open).toHaveBeenCalledWith(`http://${fakeResult.raw['number']}{test}`, jasmine.anything());
});

it('should support external fields', () => {
window['Coveo']['test'] = 'testExternal';
let hrefTemplate = '${Coveo.test}';
let hrefTemplate = 'http://${Coveo.test}';
test = Mock.optionsResultComponentSetup<ResultLink, IResultLinkOptions>(ResultLink, { hrefTemplate: hrefTemplate }, fakeResult);
test.cmp.openLinkInNewWindow();
expect(window.open).toHaveBeenCalledWith('testExternal', jasmine.anything());
expect(window.open).toHaveBeenCalledWith('http://testExternal', jasmine.anything());
window['Coveo']['test'] = undefined;
});

it('should support nested external fields with more than 2 keys', () => {
window['Coveo']['test'] = { key: 'testExternal' };
let hrefTemplate = '${Coveo.test.key}';
let hrefTemplate = 'http://${Coveo.test.key}';
test = Mock.optionsResultComponentSetup<ResultLink, IResultLinkOptions>(ResultLink, { hrefTemplate: hrefTemplate }, fakeResult);
test.cmp.openLinkInNewWindow();
expect(window.open).toHaveBeenCalledWith('testExternal', jasmine.anything());
expect(window.open).toHaveBeenCalledWith('http://testExternal', jasmine.anything());
window['Coveo']['test'] = undefined;
});
});
Expand Down

0 comments on commit 2d55a52

Please sign in to comment.