Commit 40c31a56 authored by Savas Vedova's avatar Savas Vedova

Make vulnerability file path linkable

- Update specs
- Add changelog
parent db66f1cf
...@@ -20,7 +20,7 @@ import RemediatedBadge from 'ee/vulnerabilities/components/remediated_badge.vue' ...@@ -20,7 +20,7 @@ import RemediatedBadge from 'ee/vulnerabilities/components/remediated_badge.vue'
import { VULNERABILITY_STATES } from 'ee/vulnerabilities/constants'; import { VULNERABILITY_STATES } from 'ee/vulnerabilities/constants';
import { formatDate } from '~/lib/utils/datetime_utility'; import { formatDate } from '~/lib/utils/datetime_utility';
import { convertToSnakeCase } from '~/lib/utils/text_utility'; import { convertToSnakeCase } from '~/lib/utils/text_utility';
import { s__, __, sprintf } from '~/locale'; import { s__, __ } from '~/locale';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import { VULNERABILITIES_PER_PAGE } from '../store/constants'; import { VULNERABILITIES_PER_PAGE } from '../store/constants';
import IssuesBadge from './issues_badge.vue'; import IssuesBadge from './issues_badge.vue';
...@@ -230,7 +230,7 @@ export default { ...@@ -230,7 +230,7 @@ export default {
} }
if (file && startLine) { if (file && startLine) {
return `${file} ${sprintf(__('(line: %{startLine})'), { startLine })}`; return `${file}:${startLine}`;
} }
if (path) { if (path) {
...@@ -248,6 +248,11 @@ export default { ...@@ -248,6 +248,11 @@ export default {
extraIdentifierCount(identifiers) { extraIdentifierCount(identifiers) {
return identifiers?.length - 1; return identifiers?.length - 1;
}, },
fileUrl(vulnerability) {
const { startLine: start, endLine: end } = vulnerability.location;
const lineNumber = end > start ? `${start}-${end}` : start;
return (vulnerability.location.blobPath || '') + (lineNumber ? `#L${lineNumber}` : '');
},
primaryIdentifier(identifiers) { primaryIdentifier(identifiers) {
return getPrimaryIdentifier(identifiers, 'externalType'); return getPrimaryIdentifier(identifiers, 'externalType');
}, },
...@@ -421,8 +426,10 @@ export default { ...@@ -421,8 +426,10 @@ export default {
<div v-if="shouldShowProjectNamespace"> <div v-if="shouldShowProjectNamespace">
{{ item.project.nameWithNamespace }} {{ item.project.nameWithNamespace }}
</div> </div>
<div v-if="shouldShowVulnerabilityPath(item)" class="monospace"> <div v-if="shouldShowVulnerabilityPath(item)">
<gl-truncate :text="createLocationString(item.location)" position="middle" /> <gl-link :href="fileUrl(item)">
<gl-truncate :text="createLocationString(item.location)" position="middle" />
</gl-link>
</div> </div>
</div> </div>
</template> </template>
......
...@@ -27,13 +27,16 @@ fragment Vulnerability on Vulnerability { ...@@ -27,13 +27,16 @@ fragment Vulnerability on Vulnerability {
image image
} }
... on VulnerabilityLocationDependencyScanning { ... on VulnerabilityLocationDependencyScanning {
blobPath
file file
} }
... on VulnerabilityLocationSast { ... on VulnerabilityLocationSast {
blobPath
file file
startLine startLine
} }
... on VulnerabilityLocationSecretDetection { ... on VulnerabilityLocationSecretDetection {
blobPath
file file
startLine startLine
} }
......
---
title: Make vulnerability file path linkable in the vulnerability list
merge_request: 55356
author:
type: changed
...@@ -58,6 +58,8 @@ export const generateVulnerabilities = () => [ ...@@ -58,6 +58,8 @@ export const generateVulnerabilities = () => [
location: { location: {
file: 'src/main/java/com/gitlab/security_products/tests/App.java', file: 'src/main/java/com/gitlab/security_products/tests/App.java',
startLine: '1337', startLine: '1337',
blobPath:
'/gitlab-org/security-reports2/-/blob/e5c61e4d5d0b8418011171def04ca0aa36532621/src/main/java/com/gitlab/security_products/tests/App.java',
}, },
project: { project: {
nameWithNamespace: 'Administrator / Vulnerability reports', nameWithNamespace: 'Administrator / Vulnerability reports',
......
...@@ -245,7 +245,7 @@ describe('Vulnerability list component', () => { ...@@ -245,7 +245,7 @@ describe('Vulnerability list component', () => {
const cell = findDataCell(`location-${id}`); const cell = findDataCell(`location-${id}`);
expect(cell.text()).toContain(project.nameWithNamespace); expect(cell.text()).toContain(project.nameWithNamespace);
expect(findLocationTextWrapper(cell).props()).toEqual({ expect(findLocationTextWrapper(cell).props()).toEqual({
text: `${location.file} (line: ${location.startLine})`, text: `${location.file}:${location.startLine}`,
position: 'middle', position: 'middle',
}); });
}); });
...@@ -264,7 +264,7 @@ describe('Vulnerability list component', () => { ...@@ -264,7 +264,7 @@ describe('Vulnerability list component', () => {
const { id, project } = newVulnerabilities[4]; const { id, project } = newVulnerabilities[4];
const cellText = findDataCell(`location-${id}`).text(); const cellText = findDataCell(`location-${id}`).text();
expect(cellText).toEqual(project.nameWithNamespace); expect(cellText).toEqual(project.nameWithNamespace);
expect(cellText).not.toContain('(line: '); expect(cellText).not.toContain(':');
}); });
it('should display the vulnerability locations for path', () => { it('should display the vulnerability locations for path', () => {
...@@ -313,11 +313,17 @@ describe('Vulnerability list component', () => { ...@@ -313,11 +313,17 @@ describe('Vulnerability list component', () => {
const cell = findDataCell(`location-${id}`); const cell = findDataCell(`location-${id}`);
expect(cell.text()).not.toContain(project.nameWithNamespace); expect(cell.text()).not.toContain(project.nameWithNamespace);
expect(findLocationTextWrapper(cell).props()).toEqual({ expect(findLocationTextWrapper(cell).props()).toEqual({
text: `${location.file} (line: ${location.startLine})`, text: `${location.file}:${location.startLine}`,
position: 'middle', position: 'middle',
}); });
}); });
it('should make the file path linkable', () => {
const { id, location } = newVulnerabilities[1];
const cell = findDataCell(`location-${id}`);
expect(cell.find('a').attributes('href')).toBe(`${location.blobPath}#L${location.startLine}`);
});
it('should not display the vulnerability group/project locations for code with no line data', () => { it('should not display the vulnerability group/project locations for code with no line data', () => {
const { id, project, location } = newVulnerabilities[2]; const { id, project, location } = newVulnerabilities[2];
const cell = findDataCell(`location-${id}`); const cell = findDataCell(`location-${id}`);
......
...@@ -1017,9 +1017,6 @@ msgstr "" ...@@ -1017,9 +1017,6 @@ msgstr ""
msgid "(deleted)" msgid "(deleted)"
msgstr "" msgstr ""
msgid "(line: %{startLine})"
msgstr ""
msgid "(max size 15 MB)" msgid "(max size 15 MB)"
msgstr "" msgstr ""
......
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