Allow root-relative URLs in vulnerability modal

- isSafeUrl utility now considers root-relative URLs safe
- `file` field in modal data now points to blob_path if available
parent 0f2d1809
const isAbsoluteOrRootRelative = url => /^(https?:)?\//.test(url);
/**
* Checks if the provided URL is a safe URL (absolute http(s) URL)
*
......@@ -7,7 +9,7 @@
export default url => {
let parsedUrl;
if (!(url.startsWith('https:') || url.startsWith('http:'))) {
if (!isAbsoluteOrRootRelative(url)) {
return false;
}
......
......@@ -108,6 +108,10 @@ export default {
Object.keys(data).forEach(key => {
if (data[key].value && data[key].value.length) {
result[key] = data[key];
if (key === 'file' && this.vulnerability.blob_path) {
result[key].isLink = true;
result[key].url = this.vulnerability.blob_path;
}
}
});
......
---
title: Make root relative URLs clickable in vulnerability modal
merge_request: 9767
author:
type: fixed
......@@ -10,7 +10,9 @@ describe('isSafeUrl', () => {
'https://192.168.1.1',
];
const relativeUrls = ['./relative/link', '/relative/link', '../relative/link'];
const rootRelativeUrls = ['/relative/link'];
const relativeUrls = ['./relative/link', '../relative/link'];
const urlsWithoutHost = ['http://', 'https://', 'https:https:https:'];
......@@ -31,17 +33,22 @@ describe('isSafeUrl', () => {
'\\u006A\\u0061\\u0076\\u0061\\u0073\\u0063\\u0072\\u0069\\u0070\\u0074\\u003A\\u0061\\u006C\\u0065\\u0072\\u0074\\u0028\\u0027\\u0058\\u0053\\u0053\\u0027\\u0029',
];
const safeUrls = [...absoluteUrls, ...rootRelativeUrls];
const unsafeUrls = [
...relativeUrls,
...urlsWithoutHost,
...nonHttpUrls,
...encodedJavaScriptUrls,
];
describe('with URL constructor support', () => {
it.each(absoluteUrls)('returns true for %s', url => {
it.each(safeUrls)('returns true for %s', url => {
expect(isSafeURL(url)).toBe(true);
});
it.each([...relativeUrls, ...urlsWithoutHost, ...nonHttpUrls, ...encodedJavaScriptUrls])(
'returns false for %s',
url => {
expect(isSafeURL(url)).toBe(false);
},
);
it.each(unsafeUrls)('returns false for %s', url => {
expect(isSafeURL(url)).toBe(false);
});
});
describe('without URL constructor support', () => {
......@@ -51,15 +58,12 @@ describe('isSafeUrl', () => {
});
});
it.each(absoluteUrls)('returns true for %s', url => {
it.each(safeUrls)('returns true for %s', url => {
expect(isSafeURL(url)).toBe(true);
});
it.each([...relativeUrls, ...urlsWithoutHost, ...nonHttpUrls, ...encodedJavaScriptUrls])(
'returns false for %s',
url => {
expect(isSafeURL(url)).toBe(false);
},
);
it.each(unsafeUrls)('returns false for %s', url => {
expect(isSafeURL(url)).toBe(false);
});
});
});
......@@ -97,18 +97,42 @@ describe('Security Reports modal', () => {
});
describe('Vulnerability Details', () => {
it('is rendered', () => {
const blobPath = '/group/project/blob/1ab2c3d4e5/some/file.path#L0-0';
const namespaceValue = 'foobar';
const fileValue = '/some/file.path';
beforeEach(() => {
const props = {
modal: createState().modal,
};
props.modal.data.namespace.value = 'foobar';
props.modal.vulnerability.blob_path = blobPath;
props.modal.data.namespace.value = namespaceValue;
props.modal.data.file.value = fileValue;
vm = mountComponent(Component, props);
});
it('is rendered', () => {
const vulnerabilityDetails = vm.$el.querySelector('.js-vulnerability-details');
expect(vulnerabilityDetails).not.toBeNull();
expect(vulnerabilityDetails.textContent).toContain('foobar');
});
it('computes valued fields properly', () => {
expect(vm.valuedFields).toMatchObject({
file: {
value: fileValue,
url: blobPath,
isLink: true,
text: 'File',
},
namespace: {
value: namespaceValue,
text: 'Namespace',
isLink: false,
},
});
});
});
describe('Solution Card', () => {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment