diff --git a/app/assets/javascripts/security_configuration/utils.js b/app/assets/javascripts/security_configuration/utils.js index ec6b93c6193061a7597d549388d32c67626b622d..47231497b8f4a2b9690818e86ef202b13a02db41 100644 --- a/app/assets/javascripts/security_configuration/utils.js +++ b/app/assets/javascripts/security_configuration/utils.js @@ -1,4 +1,5 @@ import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; +import { SCANNER_NAMES_MAP } from '~/security_configuration/components/constants'; export const augmentFeatures = (securityFeatures, complianceFeatures, features = []) => { const featuresByType = features.reduce((acc, feature) => { @@ -24,3 +25,13 @@ export const augmentFeatures = (securityFeatures, complianceFeatures, features = augmentedComplianceFeatures: complianceFeatures.map((feature) => augmentFeature(feature)), }; }; + +/** + * Converts a list of security scanner IDs (such as SAST_IAC) into a list of their translated + * names defined in the SCANNER_NAMES_MAP constant (eg. IaC Scanning). + * + * @param {String[]} scannerNames + * @returns {String[]} + */ +export const translateScannerNames = (scannerNames = []) => + scannerNames.map((scannerName) => SCANNER_NAMES_MAP[scannerName] || scannerName); diff --git a/ee/app/assets/javascripts/security_dashboard/components/project/project_vulnerabilities.vue b/ee/app/assets/javascripts/security_dashboard/components/project/project_vulnerabilities.vue index 9df8e06da48a611c896c1275cd7e64a295948307..ddee4d3f36aa46912da1cf73787302579b8a1a3d 100644 --- a/ee/app/assets/javascripts/security_dashboard/components/project/project_vulnerabilities.vue +++ b/ee/app/assets/javascripts/security_dashboard/components/project/project_vulnerabilities.vue @@ -9,7 +9,7 @@ import { preparePageInfo } from 'ee/security_dashboard/helpers'; import { VULNERABILITIES_PER_PAGE } from 'ee/security_dashboard/store/constants'; import { parseBoolean } from '~/lib/utils/common_utils'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; -import { SCANNER_NAMES_MAP } from '~/security_configuration/components/constants'; +import { translateScannerNames } from '~/security_configuration/utils'; import VulnerabilityList from '../shared/vulnerability_list.vue'; import SecurityScannerAlert from './security_scanner_alert.vue'; @@ -92,12 +92,11 @@ export default { }, update({ project = {} }) { const { available = [], enabled = [], pipelineRun = [] } = project?.securityScanners || {}; - const translateScannerName = (scannerName) => SCANNER_NAMES_MAP[scannerName] || scannerName; return { - available: available.map(translateScannerName), - enabled: enabled.map(translateScannerName), - pipelineRun: pipelineRun.map(translateScannerName), + available: translateScannerNames(available), + enabled: translateScannerNames(enabled), + pipelineRun: translateScannerNames(pipelineRun), }; }, }, diff --git a/ee/app/assets/javascripts/security_dashboard/components/project/project_vulnerability_report.vue b/ee/app/assets/javascripts/security_dashboard/components/project/project_vulnerability_report.vue index 69e8a78e00514db71f150d7c366ce668a55249c6..1cb69901e3e3518dca94e309e275b59128504b6a 100644 --- a/ee/app/assets/javascripts/security_dashboard/components/project/project_vulnerability_report.vue +++ b/ee/app/assets/javascripts/security_dashboard/components/project/project_vulnerability_report.vue @@ -4,7 +4,7 @@ import { difference } from 'lodash'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; import { parseBoolean } from '~/lib/utils/common_utils'; -import { SCANNER_NAMES_MAP } from '~/security_configuration/components/constants'; +import { translateScannerNames } from '~/security_configuration/utils'; import ReportNotConfiguredProject from '../shared/empty_states/report_not_configured_project.vue'; import VulnerabilityReportTabs from '../shared/vulnerability_report/vulnerability_report_tabs.vue'; import projectVulnerabilitiesQuery from '../../graphql/queries/project_vulnerabilities.query.graphql'; @@ -40,12 +40,11 @@ export default { }, update({ project = {} }) { const { available = [], enabled = [], pipelineRun = [] } = project?.securityScanners || {}; - const translateScannerName = (scannerName) => SCANNER_NAMES_MAP[scannerName] || scannerName; return { - available: available.map(translateScannerName), - enabled: enabled.map(translateScannerName), - pipelineRun: pipelineRun.map(translateScannerName), + available: translateScannerNames(available), + enabled: translateScannerNames(enabled), + pipelineRun: translateScannerNames(pipelineRun), }; }, }, diff --git a/spec/frontend/security_configuration/utils_spec.js b/spec/frontend/security_configuration/utils_spec.js index eaed4532baaa3ec1a74c3ae95c25a50324a01a56..241e69204d20ba2b4ac5765f82f4472756eb6ca8 100644 --- a/spec/frontend/security_configuration/utils_spec.js +++ b/spec/frontend/security_configuration/utils_spec.js @@ -1,101 +1,120 @@ -import { augmentFeatures } from '~/security_configuration/utils'; - -const mockSecurityFeatures = [ - { - name: 'SAST', - type: 'SAST', - }, -]; - -const mockComplianceFeatures = [ - { - name: 'LICENSE_COMPLIANCE', - type: 'LICENSE_COMPLIANCE', - }, -]; - -const mockFeaturesWithSecondary = [ - { - name: 'DAST', - type: 'DAST', - secondary: { - type: 'DAST PROFILES', - name: 'DAST PROFILES', +import { augmentFeatures, translateScannerNames } from '~/security_configuration/utils'; +import { SCANNER_NAMES_MAP } from '~/security_configuration/components/constants'; + +describe('augmentFeatures', () => { + const mockSecurityFeatures = [ + { + name: 'SAST', + type: 'SAST', }, - }, -]; - -const mockInvalidCustomFeature = [ - { - foo: 'bar', - }, -]; - -const mockValidCustomFeature = [ - { - name: 'SAST', - type: 'SAST', - customField: 'customvalue', - }, -]; - -const mockValidCustomFeatureSnakeCase = [ - { - name: 'SAST', - type: 'SAST', - custom_field: 'customvalue', - }, -]; - -const expectedOutputDefault = { - augmentedSecurityFeatures: mockSecurityFeatures, - augmentedComplianceFeatures: mockComplianceFeatures, -}; - -const expectedOutputSecondary = { - augmentedSecurityFeatures: mockSecurityFeatures, - augmentedComplianceFeatures: mockFeaturesWithSecondary, -}; - -const expectedOutputCustomFeature = { - augmentedSecurityFeatures: mockValidCustomFeature, - augmentedComplianceFeatures: mockComplianceFeatures, -}; - -describe('returns an object with augmentedSecurityFeatures and augmentedComplianceFeatures when', () => { - it('given an empty array', () => { - expect(augmentFeatures(mockSecurityFeatures, mockComplianceFeatures, [])).toEqual( - expectedOutputDefault, - ); + ]; + + const mockComplianceFeatures = [ + { + name: 'LICENSE_COMPLIANCE', + type: 'LICENSE_COMPLIANCE', + }, + ]; + + const mockFeaturesWithSecondary = [ + { + name: 'DAST', + type: 'DAST', + secondary: { + type: 'DAST PROFILES', + name: 'DAST PROFILES', + }, + }, + ]; + + const mockInvalidCustomFeature = [ + { + foo: 'bar', + }, + ]; + + const mockValidCustomFeature = [ + { + name: 'SAST', + type: 'SAST', + customField: 'customvalue', + }, + ]; + + const mockValidCustomFeatureSnakeCase = [ + { + name: 'SAST', + type: 'SAST', + custom_field: 'customvalue', + }, + ]; + + const expectedOutputDefault = { + augmentedSecurityFeatures: mockSecurityFeatures, + augmentedComplianceFeatures: mockComplianceFeatures, + }; + + const expectedOutputSecondary = { + augmentedSecurityFeatures: mockSecurityFeatures, + augmentedComplianceFeatures: mockFeaturesWithSecondary, + }; + + const expectedOutputCustomFeature = { + augmentedSecurityFeatures: mockValidCustomFeature, + augmentedComplianceFeatures: mockComplianceFeatures, + }; + + describe('returns an object with augmentedSecurityFeatures and augmentedComplianceFeatures when', () => { + it('given an empty array', () => { + expect(augmentFeatures(mockSecurityFeatures, mockComplianceFeatures, [])).toEqual( + expectedOutputDefault, + ); + }); + + it('given an invalid populated array', () => { + expect( + augmentFeatures(mockSecurityFeatures, mockComplianceFeatures, mockInvalidCustomFeature), + ).toEqual(expectedOutputDefault); + }); + + it('features have secondary key', () => { + expect(augmentFeatures(mockSecurityFeatures, mockFeaturesWithSecondary, [])).toEqual( + expectedOutputSecondary, + ); + }); + + it('given a valid populated array', () => { + expect( + augmentFeatures(mockSecurityFeatures, mockComplianceFeatures, mockValidCustomFeature), + ).toEqual(expectedOutputCustomFeature); + }); }); - it('given an invalid populated array', () => { - expect( - augmentFeatures(mockSecurityFeatures, mockComplianceFeatures, mockInvalidCustomFeature), - ).toEqual(expectedOutputDefault); + describe('returns an object with camelcased keys', () => { + it('given a customfeature in snakecase', () => { + expect( + augmentFeatures( + mockSecurityFeatures, + mockComplianceFeatures, + mockValidCustomFeatureSnakeCase, + ), + ).toEqual(expectedOutputCustomFeature); + }); }); +}); - it('features have secondary key', () => { - expect(augmentFeatures(mockSecurityFeatures, mockFeaturesWithSecondary, [])).toEqual( - expectedOutputSecondary, - ); +describe('translateScannerNames', () => { + it.each(['', undefined, null, 1, 'UNKNOWN_SCANNER_KEY'])('returns %p as is', (key) => { + expect(translateScannerNames([key])).toEqual([key]); }); - it('given a valid populated array', () => { - expect( - augmentFeatures(mockSecurityFeatures, mockComplianceFeatures, mockValidCustomFeature), - ).toEqual(expectedOutputCustomFeature); + it('returns an empty array if no input is provided', () => { + expect(translateScannerNames([])).toEqual([]); }); -}); -describe('returns an object with camelcased keys', () => { - it('given a customfeature in snakecase', () => { - expect( - augmentFeatures( - mockSecurityFeatures, - mockComplianceFeatures, - mockValidCustomFeatureSnakeCase, - ), - ).toEqual(expectedOutputCustomFeature); + it('returns translated scanner names', () => { + expect(translateScannerNames(Object.keys(SCANNER_NAMES_MAP))).toEqual( + Object.values(SCANNER_NAMES_MAP), + ); }); });