Commit a7b48a1e authored by Martin Wortschack's avatar Martin Wortschack

Merge branch '210327-add-line-number' into 'master'

Add line number to SAST and Secret Detection

Closes #210327

See merge request gitlab-org/gitlab!35536
parents e3d3fc24 5412e7bf
<script> <script>
import { s__, __ } from '~/locale'; import { s__, __, sprintf } from '~/locale';
import { GlEmptyState, GlFormCheckbox, GlLink, GlSkeletonLoading, GlTable } from '@gitlab/ui'; import { GlEmptyState, GlFormCheckbox, GlLink, GlSkeletonLoading, GlTable } from '@gitlab/ui';
import RemediatedBadge from './remediated_badge.vue'; import RemediatedBadge from './remediated_badge.vue';
import getPrimaryIdentifier from 'ee/vue_shared/security_reports/store/utils/get_primary_identifier'; import getPrimaryIdentifier from 'ee/vue_shared/security_reports/store/utils/get_primary_identifier';
...@@ -148,6 +148,19 @@ export default { ...@@ -148,6 +148,19 @@ export default {
}, },
}, },
methods: { methods: {
createLocationString(location) {
const { image, file, startLine } = location;
if (image) {
return image;
}
if (file && startLine) {
return `${file} ${sprintf(__('(line: %{startLine})'), { startLine })}`;
}
return file;
},
deselectAllVulnerabilities() { deselectAllVulnerabilities() {
this.selectedVulnerabilities = {}; this.selectedVulnerabilities = {};
}, },
...@@ -263,7 +276,7 @@ export default { ...@@ -263,7 +276,7 @@ export default {
{{ item.project.nameWithNamespace }} {{ item.project.nameWithNamespace }}
</div> </div>
<div v-if="shouldShowVulnerabilityPath(item)" class="monospace"> <div v-if="shouldShowVulnerabilityPath(item)" class="monospace">
{{ item.location.image || item.location.file }} {{ createLocationString(item.location) }}
</div> </div>
</div> </div>
<remediated-badge v-if="item.resolved_on_default_branch" class="ml-2" /> <remediated-badge v-if="item.resolved_on_default_branch" class="ml-2" />
......
...@@ -28,9 +28,11 @@ fragment Vulnerability on Vulnerability { ...@@ -28,9 +28,11 @@ fragment Vulnerability on Vulnerability {
} }
... on VulnerabilityLocationSast { ... on VulnerabilityLocationSast {
file file
startLine
} }
... on VulnerabilityLocationSecretDetection { ... on VulnerabilityLocationSecretDetection {
file file
startLine
} }
} }
project { project {
......
---
title: Add line number to SAST and Secret Detection
merge_request: 35536
author:
type: changed
...@@ -37,6 +37,7 @@ export const generateVulnerabilities = () => [ ...@@ -37,6 +37,7 @@ export const generateVulnerabilities = () => [
reportType: 'DEPENDENCY_SCANNING', reportType: 'DEPENDENCY_SCANNING',
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',
}, },
project: { project: {
nameWithNamespace: 'Administrator / Vulnerability reports', nameWithNamespace: 'Administrator / Vulnerability reports',
...@@ -49,7 +50,7 @@ export const generateVulnerabilities = () => [ ...@@ -49,7 +50,7 @@ export const generateVulnerabilities = () => [
state: 'opened', state: 'opened',
reportType: 'CUSTOM_SCANNER_WITHOUT_TRANSLATION', reportType: 'CUSTOM_SCANNER_WITHOUT_TRANSLATION',
location: { location: {
file: 'yarn.lock', file: 'src/main/java/com/gitlab/security_products/tests/App.java',
}, },
project: { project: {
nameWithNamespace: 'Mixed Vulnerabilities / Dependency List Test 01', nameWithNamespace: 'Mixed Vulnerabilities / Dependency List Test 01',
...@@ -64,7 +65,17 @@ export const generateVulnerabilities = () => [ ...@@ -64,7 +65,17 @@ export const generateVulnerabilities = () => [
file: 'yarn.lock', file: 'yarn.lock',
}, },
project: { project: {
nameWithNamespace: 'Mixed Vulnerabilities / Dependency List Test 01', nameWithNamespace: 'Mixed Vulnerabilities / Rails App',
},
},
{
id: 'id_4',
title: 'Vulnerability 4',
severity: 'critical',
state: 'dismissed',
location: {},
project: {
nameWithNamespace: 'Administrator / Security reports',
}, },
}, },
]; ];
......
...@@ -35,6 +35,7 @@ describe('Vulnerability list component', () => { ...@@ -35,6 +35,7 @@ describe('Vulnerability list component', () => {
const findRowVulnerabilityCommentIcon = row => findRow(row).find(VulnerabilityCommentIcon); const findRowVulnerabilityCommentIcon = row => findRow(row).find(VulnerabilityCommentIcon);
const findDataCell = label => wrapper.find(`[data-testid="${label}"]`); const findDataCell = label => wrapper.find(`[data-testid="${label}"]`);
const findDataCells = label => wrapper.findAll(`[data-testid="${label}"]`); const findDataCells = label => wrapper.findAll(`[data-testid="${label}"]`);
const findCellText = label => findDataCell(label).text();
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
...@@ -116,19 +117,35 @@ describe('Vulnerability list component', () => { ...@@ -116,19 +117,35 @@ describe('Vulnerability list component', () => {
}); });
}); });
it('should display the vulnerability locations', () => { it('should display the vulnerability locations for images', () => {
expect(findDataCell(`location-${newVulnerabilities[0].id}`).text()).toContain( const { id, project, location } = newVulnerabilities[0];
'Administrator / Security reports', const cellText = findCellText(`location-${id}`);
); expect(cellText).toContain(project.nameWithNamespace);
expect(findDataCell(`location-${newVulnerabilities[0].id}`).text()).toContain( expect(cellText).toContain(location.image);
'registry.gitlab.com/groulot/container-scanning-test/master:5f21de6956aee99ddb68ae49498662d9872f50ff', expect(cellText).not.toContain('(line: ');
); });
expect(findDataCell(`location-${newVulnerabilities[1].id}`).text()).toContain(
'Administrator / Vulnerability reports', it('should display the vulnerability locations for code', () => {
); const { id, project, location } = newVulnerabilities[1];
expect(findDataCell(`location-${newVulnerabilities[1].id}`).text()).toContain( const cellText = findCellText(`location-${id}`);
'src/main/java/com/gitlab/security_products/tests/App.java', expect(cellText).toContain(project.nameWithNamespace);
); expect(cellText).toContain(location.file);
expect(cellText).toContain(`(line: ${location.startLine})`);
});
it('should display the vulnerability locations for code with no line data', () => {
const { id, project, location } = newVulnerabilities[2];
const cellText = findCellText(`location-${id}`);
expect(cellText).toContain(project.nameWithNamespace);
expect(cellText).toContain(location.file);
expect(cellText).not.toContain('(line: ');
});
it('should not display the vulnerability locations for vulnerabilities without a location', () => {
const { id, project } = newVulnerabilities[4];
const cellText = findCellText(`location-${id}`);
expect(cellText).toEqual(project.nameWithNamespace);
expect(cellText).not.toContain('(line: ');
}); });
it('should not display the vulnerability identifier', () => { it('should not display the vulnerability identifier', () => {
...@@ -140,30 +157,6 @@ describe('Vulnerability list component', () => { ...@@ -140,30 +157,6 @@ describe('Vulnerability list component', () => {
const cell = findDataCell('vulnerability-report-type'); const cell = findDataCell('vulnerability-report-type');
expect(cell.exists()).toBe(false); expect(cell.exists()).toBe(false);
}); });
it('should not display the vulnerability locations', () => {
const vulnerabilityWithoutLocation = [
{
id: 'id_0',
title: 'Vulnerability 1',
severity: 'critical',
state: 'dismissed',
location: {},
project: {
nameWithNamespace: 'Administrator / Security reports',
},
},
];
wrapper = createWrapper({
props: { vulnerabilities: vulnerabilityWithoutLocation, shouldShowProjectNamespace: true },
});
expect(findDataCell(`location-${vulnerabilityWithoutLocation[0].id}`).text()).toContain(
'Administrator / Security reports',
);
expect(
findDataCell(`location-${vulnerabilityWithoutLocation[0].id}`).findAll('div').length,
).toBe(2);
});
}); });
describe('when displayed on a project level dashboard', () => { describe('when displayed on a project level dashboard', () => {
...@@ -179,19 +172,25 @@ describe('Vulnerability list component', () => { ...@@ -179,19 +172,25 @@ describe('Vulnerability list component', () => {
}); });
}); });
it('should not display the vulnerability locations', () => { it('should not display the vulnerability group/project locations for images', () => {
expect(findDataCell(`location-${newVulnerabilities[0].id}`).text()).not.toContain( const { id, project, location } = newVulnerabilities[0];
'Administrator / Security reports', const cellText = findCellText(`location-${id}`);
); expect(cellText).not.toContain(project.nameWithNamespace);
expect(findDataCell(`location-${newVulnerabilities[0].id}`).text()).toContain( expect(cellText).toEqual(location.image);
'registry.gitlab.com/groulot/container-scanning-test/master:5f21de6956aee99ddb68ae49498662d9872f50ff', });
);
expect(findDataCell(`location-${newVulnerabilities[1].id}`).text()).not.toContain( it('should display the vulnerability locations for code', () => {
'Administrator / Vulnerability reports', const { id, project, location } = newVulnerabilities[1];
); const cellText = findCellText(`location-${id}`);
expect(findDataCell(`location-${newVulnerabilities[1].id}`).text()).toContain( expect(cellText).not.toContain(project.nameWithNamespace);
'src/main/java/com/gitlab/security_products/tests/App.java', expect(cellText).toEqual(`${location.file} (line: ${location.startLine})`);
); });
it('should not display the vulnerability group/project locations for code with no line data', () => {
const { id, project, location } = newVulnerabilities[2];
const cellText = findCellText(`location-${id}`);
expect(cellText).not.toContain(project.nameWithNamespace);
expect(cellText).toEqual(location.file);
}); });
it('should correctly render the identifier', () => { it('should correctly render the identifier', () => {
......
...@@ -752,6 +752,9 @@ msgstr "" ...@@ -752,6 +752,9 @@ msgstr ""
msgid "(external source)" msgid "(external source)"
msgstr "" msgstr ""
msgid "(line: %{startLine})"
msgstr ""
msgid "(removed)" msgid "(removed)"
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