Commit 7096ea8d authored by Brandon Labuschagne's avatar Brandon Labuschagne

Merge branch '323376-security-configuration-ui-strings-ssot' into 'master'

Move Security Configuration UI strings to frontend

See merge request gitlab-org/gitlab!56022
parents 02fc8956 24abff05
<script> <script>
import { GlLink, GlSprintf, GlTable, GlAlert } from '@gitlab/ui'; import { GlLink, GlTable, GlAlert } from '@gitlab/ui';
import { s__, sprintf } from '~/locale'; import { s__, sprintf } from '~/locale';
import { import {
REPORT_TYPE_SAST, REPORT_TYPE_SAST,
...@@ -8,10 +8,11 @@ import { ...@@ -8,10 +8,11 @@ import {
REPORT_TYPE_DEPENDENCY_SCANNING, REPORT_TYPE_DEPENDENCY_SCANNING,
REPORT_TYPE_CONTAINER_SCANNING, REPORT_TYPE_CONTAINER_SCANNING,
REPORT_TYPE_COVERAGE_FUZZING, REPORT_TYPE_COVERAGE_FUZZING,
REPORT_TYPE_API_FUZZING,
REPORT_TYPE_LICENSE_COMPLIANCE, REPORT_TYPE_LICENSE_COMPLIANCE,
} from '~/vue_shared/security_reports/constants'; } from '~/vue_shared/security_reports/constants';
import { features } from './features_constants';
import ManageSast from './manage_sast.vue'; import ManageSast from './manage_sast.vue';
import { scanners } from './scanners_constants';
import Upgrade from './upgrade.vue'; import Upgrade from './upgrade.vue';
const borderClasses = 'gl-border-b-1! gl-border-b-solid! gl-border-gray-100!'; const borderClasses = 'gl-border-b-1! gl-border-b-solid! gl-border-gray-100!';
...@@ -20,12 +21,10 @@ const thClass = `gl-text-gray-900 gl-bg-transparent! ${borderClasses}`; ...@@ -20,12 +21,10 @@ const thClass = `gl-text-gray-900 gl-bg-transparent! ${borderClasses}`;
export default { export default {
components: { components: {
GlLink, GlLink,
GlSprintf,
GlTable, GlTable,
GlAlert, GlAlert,
}, },
data: () => ({ data: () => ({
features,
errorMessage: '', errorMessage: '',
}), }),
methods: { methods: {
...@@ -45,6 +44,7 @@ export default { ...@@ -45,6 +44,7 @@ export default {
[REPORT_TYPE_DEPENDENCY_SCANNING]: Upgrade, [REPORT_TYPE_DEPENDENCY_SCANNING]: Upgrade,
[REPORT_TYPE_CONTAINER_SCANNING]: Upgrade, [REPORT_TYPE_CONTAINER_SCANNING]: Upgrade,
[REPORT_TYPE_COVERAGE_FUZZING]: Upgrade, [REPORT_TYPE_COVERAGE_FUZZING]: Upgrade,
[REPORT_TYPE_API_FUZZING]: Upgrade,
[REPORT_TYPE_LICENSE_COMPLIANCE]: Upgrade, [REPORT_TYPE_LICENSE_COMPLIANCE]: Upgrade,
}; };
...@@ -64,7 +64,7 @@ export default { ...@@ -64,7 +64,7 @@ export default {
thClass, thClass,
}, },
], ],
items: features, items: scanners,
}, },
}; };
</script> </script>
......
import { helpPagePath } from '~/helpers/help_page_helper'; import { helpPagePath } from '~/helpers/help_page_helper';
import { s__ } from '~/locale'; import { __, s__ } from '~/locale';
import { import {
REPORT_TYPE_SAST, REPORT_TYPE_SAST,
...@@ -9,58 +9,65 @@ import { ...@@ -9,58 +9,65 @@ import {
REPORT_TYPE_DEPENDENCY_SCANNING, REPORT_TYPE_DEPENDENCY_SCANNING,
REPORT_TYPE_CONTAINER_SCANNING, REPORT_TYPE_CONTAINER_SCANNING,
REPORT_TYPE_COVERAGE_FUZZING, REPORT_TYPE_COVERAGE_FUZZING,
REPORT_TYPE_API_FUZZING,
REPORT_TYPE_LICENSE_COMPLIANCE, REPORT_TYPE_LICENSE_COMPLIANCE,
} from '~/vue_shared/security_reports/constants'; } from '~/vue_shared/security_reports/constants';
/** /**
* Translations & helpPagePaths for Static Security Configuration Page * Translations & helpPagePaths for Static Security Configuration Page
*/ */
export const SAST_NAME = s__('Static Application Security Testing (SAST)'); export const SAST_NAME = __('Static Application Security Testing (SAST)');
export const SAST_DESCRIPTION = s__('Analyze your source code for known vulnerabilities.'); export const SAST_DESCRIPTION = __('Analyze your source code for known vulnerabilities.');
export const SAST_HELP_PATH = helpPagePath('user/application_security/sast/index'); export const SAST_HELP_PATH = helpPagePath('user/application_security/sast/index');
export const DAST_NAME = s__('Dynamic Application Security Testing (DAST)'); export const DAST_NAME = __('Dynamic Application Security Testing (DAST)');
export const DAST_DESCRIPTION = s__('Analyze a review version of your web application.'); export const DAST_DESCRIPTION = __('Analyze a review version of your web application.');
export const DAST_HELP_PATH = helpPagePath('user/application_security/dast/index'); export const DAST_HELP_PATH = helpPagePath('user/application_security/dast/index');
export const DAST_PROFILES_NAME = s__('DAST Scans'); export const DAST_PROFILES_NAME = __('DAST Scans');
export const DAST_PROFILES_DESCRIPTION = s__('Analyze a review version of your web application.'); export const DAST_PROFILES_DESCRIPTION = __(
'Saved scan settings and target site settings which are reusable.',
);
export const DAST_PROFILES_HELP_PATH = helpPagePath('user/application_security/dast/index'); export const DAST_PROFILES_HELP_PATH = helpPagePath('user/application_security/dast/index');
export const SECRET_DETECTION_NAME = s__('Secret Detection'); export const SECRET_DETECTION_NAME = __('Secret Detection');
export const SECRET_DETECTION_DESCRIPTION = s__( export const SECRET_DETECTION_DESCRIPTION = __(
'Analyze your source code and git history for secrets.', 'Analyze your source code and git history for secrets.',
); );
export const SECRET_DETECTION_HELP_PATH = helpPagePath( export const SECRET_DETECTION_HELP_PATH = helpPagePath(
'user/application_security/secret_detection/index', 'user/application_security/secret_detection/index',
); );
export const DEPENDENCY_SCANNING_NAME = s__('Dependency Scanning'); export const DEPENDENCY_SCANNING_NAME = __('Dependency Scanning');
export const DEPENDENCY_SCANNING_DESCRIPTION = s__( export const DEPENDENCY_SCANNING_DESCRIPTION = __(
'Analyze your dependencies for known vulnerabilities.', 'Analyze your dependencies for known vulnerabilities.',
); );
export const DEPENDENCY_SCANNING_HELP_PATH = helpPagePath( export const DEPENDENCY_SCANNING_HELP_PATH = helpPagePath(
'user/application_security/dependency_scanning/index', 'user/application_security/dependency_scanning/index',
); );
export const CONTAINER_SCANNING_NAME = s__('Container Scanning'); export const CONTAINER_SCANNING_NAME = __('Container Scanning');
export const CONTAINER_SCANNING_DESCRIPTION = s__( export const CONTAINER_SCANNING_DESCRIPTION = __(
'Check your Docker images for known vulnerabilities.', 'Check your Docker images for known vulnerabilities.',
); );
export const CONTAINER_SCANNING_HELP_PATH = helpPagePath( export const CONTAINER_SCANNING_HELP_PATH = helpPagePath(
'user/application_security/container_scanning/index', 'user/application_security/container_scanning/index',
); );
export const COVERAGE_FUZZING_NAME = s__('Coverage Fuzzing'); export const COVERAGE_FUZZING_NAME = __('Coverage Fuzzing');
export const COVERAGE_FUZZING_DESCRIPTION = s__( export const COVERAGE_FUZZING_DESCRIPTION = __(
'Find bugs in your code with coverage-guided fuzzing.', 'Find bugs in your code with coverage-guided fuzzing.',
); );
export const COVERAGE_FUZZING_HELP_PATH = helpPagePath( export const COVERAGE_FUZZING_HELP_PATH = helpPagePath(
'user/application_security/coverage_fuzzing/index', 'user/application_security/coverage_fuzzing/index',
); );
export const LICENSE_COMPLIANCE_NAME = s__('License Compliance'); export const API_FUZZING_NAME = __('API Fuzzing');
export const LICENSE_COMPLIANCE_DESCRIPTION = s__( export const API_FUZZING_DESCRIPTION = __('Find bugs in your code with API fuzzing.');
export const API_FUZZING_HELP_PATH = helpPagePath('user/application_security/api_fuzzing/index');
export const LICENSE_COMPLIANCE_NAME = __('License Compliance');
export const LICENSE_COMPLIANCE_DESCRIPTION = __(
'Search your project dependencies for their licenses and apply policies.', 'Search your project dependencies for their licenses and apply policies.',
); );
export const LICENSE_COMPLIANCE_HELP_PATH = helpPagePath( export const LICENSE_COMPLIANCE_HELP_PATH = helpPagePath(
...@@ -71,7 +78,7 @@ export const UPGRADE_CTA = s__( ...@@ -71,7 +78,7 @@ export const UPGRADE_CTA = s__(
'SecurityConfiguration|Available with %{linkStart}upgrade or free trial%{linkEnd}', 'SecurityConfiguration|Available with %{linkStart}upgrade or free trial%{linkEnd}',
); );
export const features = [ export const scanners = [
{ {
name: SAST_NAME, name: SAST_NAME,
description: SAST_DESCRIPTION, description: SAST_DESCRIPTION,
...@@ -90,12 +97,6 @@ export const features = [ ...@@ -90,12 +97,6 @@ export const features = [
helpPath: DAST_PROFILES_HELP_PATH, helpPath: DAST_PROFILES_HELP_PATH,
type: REPORT_TYPE_DAST_PROFILES, type: REPORT_TYPE_DAST_PROFILES,
}, },
{
name: SECRET_DETECTION_NAME,
description: SECRET_DETECTION_DESCRIPTION,
helpPath: SECRET_DETECTION_HELP_PATH,
type: REPORT_TYPE_SECRET_DETECTION,
},
{ {
name: DEPENDENCY_SCANNING_NAME, name: DEPENDENCY_SCANNING_NAME,
description: DEPENDENCY_SCANNING_DESCRIPTION, description: DEPENDENCY_SCANNING_DESCRIPTION,
...@@ -108,12 +109,24 @@ export const features = [ ...@@ -108,12 +109,24 @@ export const features = [
helpPath: CONTAINER_SCANNING_HELP_PATH, helpPath: CONTAINER_SCANNING_HELP_PATH,
type: REPORT_TYPE_CONTAINER_SCANNING, type: REPORT_TYPE_CONTAINER_SCANNING,
}, },
{
name: SECRET_DETECTION_NAME,
description: SECRET_DETECTION_DESCRIPTION,
helpPath: SECRET_DETECTION_HELP_PATH,
type: REPORT_TYPE_SECRET_DETECTION,
},
{ {
name: COVERAGE_FUZZING_NAME, name: COVERAGE_FUZZING_NAME,
description: COVERAGE_FUZZING_DESCRIPTION, description: COVERAGE_FUZZING_DESCRIPTION,
helpPath: COVERAGE_FUZZING_HELP_PATH, helpPath: COVERAGE_FUZZING_HELP_PATH,
type: REPORT_TYPE_COVERAGE_FUZZING, type: REPORT_TYPE_COVERAGE_FUZZING,
}, },
{
name: API_FUZZING_NAME,
description: API_FUZZING_DESCRIPTION,
helpPath: API_FUZZING_HELP_PATH,
type: REPORT_TYPE_API_FUZZING,
},
{ {
name: LICENSE_COMPLIANCE_NAME, name: LICENSE_COMPLIANCE_NAME,
description: LICENSE_COMPLIANCE_DESCRIPTION, description: LICENSE_COMPLIANCE_DESCRIPTION,
......
<script> <script>
import { GlLink, GlSprintf } from '@gitlab/ui'; import { GlLink, GlSprintf } from '@gitlab/ui';
import { UPGRADE_CTA } from './features_constants'; import { UPGRADE_CTA } from './scanners_constants';
export default { export default {
components: { components: {
......
...@@ -23,7 +23,7 @@ export const REPORT_TYPE_SECRET_DETECTION = 'secret_detection'; ...@@ -23,7 +23,7 @@ export const REPORT_TYPE_SECRET_DETECTION = 'secret_detection';
export const REPORT_TYPE_DEPENDENCY_SCANNING = 'dependency_scanning'; export const REPORT_TYPE_DEPENDENCY_SCANNING = 'dependency_scanning';
export const REPORT_TYPE_CONTAINER_SCANNING = 'container_scanning'; export const REPORT_TYPE_CONTAINER_SCANNING = 'container_scanning';
export const REPORT_TYPE_COVERAGE_FUZZING = 'coverage_fuzzing'; export const REPORT_TYPE_COVERAGE_FUZZING = 'coverage_fuzzing';
export const REPORT_TYPE_LICENSE_COMPLIANCE = 'license_compliance'; export const REPORT_TYPE_LICENSE_COMPLIANCE = 'license_scanning';
export const REPORT_TYPE_API_FUZZING = 'api_fuzzing'; export const REPORT_TYPE_API_FUZZING = 'api_fuzzing';
/** /**
......
---
title: Add API Fuzzing to Security Configuration page, and re-order scanners
merge_request: 56022
author:
type: changed
<script> <script>
import { GlAlert, GlLink, GlSprintf, GlTable } from '@gitlab/ui'; import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui';
import { parseBoolean } from '~/lib/utils/common_utils'; import { parseBoolean } from '~/lib/utils/common_utils';
import { sprintf, s__, __ } from '~/locale'; import { s__, __ } from '~/locale';
import { scanners } from '~/security_configuration/components/scanners_constants';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import AutoFixSettings from './auto_fix_settings.vue'; import AutoFixSettings from './auto_fix_settings.vue';
import FeatureStatus from './feature_status.vue'; import ConfigurationTable from './configuration_table.vue';
import ManageFeature from './manage_feature.vue';
export default { export default {
components: { components: {
GlAlert, GlAlert,
GlLink, GlLink,
GlSprintf, GlSprintf,
GlTable,
AutoFixSettings, AutoFixSettings,
LocalStorageSync, LocalStorageSync,
FeatureStatus, ConfigurationTable,
ManageFeature,
}, },
mixins: [glFeatureFlagsMixin()], mixins: [glFeatureFlagsMixin()],
props: { props: {
...@@ -26,10 +24,6 @@ export default { ...@@ -26,10 +24,6 @@ export default {
required: false, required: false,
default: false, default: false,
}, },
helpPagePath: {
type: String,
required: true,
},
autoDevopsHelpPagePath: { autoDevopsHelpPagePath: {
type: String, type: String,
required: true, required: true,
...@@ -84,28 +78,6 @@ export default { ...@@ -84,28 +78,6 @@ export default {
devopsUrl() { devopsUrl() {
return this.autoDevopsEnabled ? this.autoDevopsHelpPagePath : this.latestPipelinePath; return this.autoDevopsEnabled ? this.autoDevopsHelpPagePath : this.latestPipelinePath;
}, },
fields() {
const borderClasses = 'gl-border-b-1! gl-border-b-solid! gl-border-gray-100!';
const thClass = `gl-text-gray-900 gl-bg-transparent! ${borderClasses}`;
return [
{
key: 'feature',
label: s__('SecurityConfiguration|Security Control'),
thClass,
},
{
key: 'status',
label: s__('SecurityConfiguration|Status'),
thClass,
},
{
key: 'manage',
label: s__('SecurityConfiguration|Manage'),
thClass,
},
];
},
shouldShowAutoDevopsAlert() { shouldShowAutoDevopsAlert() {
return Boolean( return Boolean(
!parseBoolean(this.autoDevopsAlertDismissed) && !parseBoolean(this.autoDevopsAlertDismissed) &&
...@@ -114,16 +86,26 @@ export default { ...@@ -114,16 +86,26 @@ export default {
this.canEnableAutoDevops, this.canEnableAutoDevops,
); );
}, },
featuresForDisplay() {
const featuresByType = this.features.reduce((acc, feature) => {
acc[feature.type] = feature;
return acc;
}, {});
return scanners.map((scanner) => {
const feature = featuresByType[scanner.type] ?? {};
return {
...feature,
...scanner,
};
});
},
}, },
methods: { methods: {
dismissAutoDevopsAlert() { dismissAutoDevopsAlert() {
this.autoDevopsAlertDismissed = 'true'; this.autoDevopsAlertDismissed = 'true';
}, },
getFeatureDocumentationLinkLabel(item) {
return sprintf(s__('SecurityConfiguration|Feature documentation for %{featureName}'), {
featureName: item.name,
});
},
}, },
autoDevopsAlertMessage: s__(` autoDevopsAlertMessage: s__(`
SecurityConfiguration|You can quickly enable all security scanning tools by SecurityConfiguration|You can quickly enable all security scanning tools by
...@@ -166,40 +148,13 @@ export default { ...@@ -166,40 +148,13 @@ export default {
</gl-sprintf> </gl-sprintf>
</gl-alert> </gl-alert>
<gl-table <configuration-table
ref="securityControlTable" :features="featuresForDisplay"
:items="features" :auto-devops-enabled="autoDevopsEnabled"
:fields="fields" :gitlab-ci-present="gitlabCiPresent"
stacked="md" :gitlab-ci-history-path="gitlabCiHistoryPath"
:tbody-tr-attr="{ 'data-testid': 'security-scanner-row' }" />
>
<template #cell(feature)="{ item }">
<div class="gl-text-gray-900">{{ item.name }}</div>
<div>
{{ item.description }}
<gl-link
target="_blank"
:href="item.link"
:aria-label="getFeatureDocumentationLinkLabel(item)"
data-testid="docsLink"
>
{{ s__('SecurityConfiguration|More information') }}
</gl-link>
</div>
</template>
<template #cell(status)="{ item }">
<feature-status
:feature="item"
:gitlab-ci-present="gitlabCiPresent"
:gitlab-ci-history-path="gitlabCiHistoryPath"
/>
</template>
<template #cell(manage)="{ item }">
<manage-feature :feature="item" :auto-devops-enabled="autoDevopsEnabled" />
</template>
</gl-table>
<auto-fix-settings v-if="glFeatures.securityAutoFix" v-bind="autoFixSettingsProps" /> <auto-fix-settings v-if="glFeatures.securityAutoFix" v-bind="autoFixSettingsProps" />
</article> </article>
</template> </template>
<script>
import { GlLink, GlTable } from '@gitlab/ui';
import { s__, sprintf } from '~/locale';
import FeatureStatus from './feature_status.vue';
import ManageFeature from './manage_feature.vue';
const borderClasses = 'gl-border-b-1! gl-border-b-solid! gl-border-gray-100!';
const thClass = `gl-text-gray-900 gl-bg-transparent! ${borderClasses}`;
export default {
components: {
GlLink,
GlTable,
FeatureStatus,
ManageFeature,
},
props: {
features: {
type: Array,
required: true,
},
autoDevopsEnabled: {
type: Boolean,
required: false,
default: false,
},
gitlabCiPresent: {
type: Boolean,
required: false,
default: false,
},
gitlabCiHistoryPath: {
type: String,
required: false,
default: '',
},
},
methods: {
getFeatureDocumentationLinkLabel(item) {
return sprintf(s__('SecurityConfiguration|Feature documentation for %{featureName}'), {
featureName: item.name,
});
},
},
fields: [
{
key: 'description',
label: s__('SecurityConfiguration|Security Control'),
thClass,
},
{
key: 'status',
label: s__('SecurityConfiguration|Status'),
thClass,
},
{
key: 'manage',
label: s__('SecurityConfiguration|Manage'),
thClass,
},
],
};
</script>
<template>
<gl-table
:items="features"
:fields="$options.fields"
stacked="md"
:tbody-tr-attr="{ 'data-testid': 'security-scanner-row' }"
>
<template #cell(description)="{ item }">
<div class="gl-text-gray-900">{{ item.name }}</div>
<div>
{{ item.description }}
<gl-link
target="_blank"
:href="item.helpPath"
:aria-label="getFeatureDocumentationLinkLabel(item)"
>
{{ s__('SecurityConfiguration|More information') }}
</gl-link>
</div>
</template>
<template #cell(status)="{ item }">
<feature-status
:feature="item"
:gitlab-ci-present="gitlabCiPresent"
:gitlab-ci-history-path="gitlabCiHistoryPath"
/>
</template>
<template #cell(manage)="{ item }">
<manage-feature :feature="item" :auto-devops-enabled="autoDevopsEnabled" />
</template>
</gl-table>
</template>
...@@ -11,7 +11,6 @@ export const initSecurityConfiguration = (el) => { ...@@ -11,7 +11,6 @@ export const initSecurityConfiguration = (el) => {
autoDevopsHelpPagePath, autoDevopsHelpPagePath,
autoDevopsPath, autoDevopsPath,
features, features,
helpPagePath,
latestPipelinePath, latestPipelinePath,
autoFixEnabled, autoFixEnabled,
autoFixHelpPath, autoFixHelpPath,
...@@ -33,7 +32,6 @@ export const initSecurityConfiguration = (el) => { ...@@ -33,7 +32,6 @@ export const initSecurityConfiguration = (el) => {
autoDevopsHelpPagePath, autoDevopsHelpPagePath,
autoDevopsPath, autoDevopsPath,
features: JSON.parse(features), features: JSON.parse(features),
helpPagePath,
latestPipelinePath, latestPipelinePath,
...parseBooleanDataAttributes(el, [ ...parseBooleanDataAttributes(el, [
'autoDevopsEnabled', 'autoDevopsEnabled',
......
...@@ -9,46 +9,6 @@ module Projects ...@@ -9,46 +9,6 @@ module Projects
presents :project presents :project
SCAN_DOCS = {
container_scanning: 'user/application_security/container_scanning/index',
dast: 'user/application_security/dast/index',
dast_profiles: 'user/application_security/dast/index',
dependency_scanning: 'user/application_security/dependency_scanning/index',
license_scanning: 'user/compliance/license_compliance/index',
sast: 'user/application_security/sast/index',
secret_detection: 'user/application_security/secret_detection/index',
coverage_fuzzing: 'user/application_security/coverage_fuzzing/index',
api_fuzzing: 'user/application_security/api_fuzzing/index'
}.freeze
def self.localized_scan_descriptions
{
container_scanning: _('Check your Docker images for known vulnerabilities.'),
dast: _('Analyze a review version of your web application.'),
dast_profiles: _('Saved scan settings and target site settings which are reusable.'),
dependency_scanning: _('Analyze your dependencies for known vulnerabilities.'),
license_scanning: _('Search your project dependencies for their licenses and apply policies.'),
sast: _('Analyze your source code for known vulnerabilities.'),
secret_detection: _('Analyze your source code and git history for secrets.'),
coverage_fuzzing: _('Find bugs in your code with coverage-guided fuzzing.'),
api_fuzzing: _('Find bugs in your code with API fuzzing.')
}.freeze
end
def self.localized_scan_names
{
container_scanning: _('Container Scanning'),
dast: _('Dynamic Application Security Testing (DAST)'),
dast_profiles: _('DAST Scans'),
dependency_scanning: _('Dependency Scanning'),
license_scanning: _('License Compliance'),
sast: _('Static Application Security Testing (SAST)'),
secret_detection: _('Secret Detection'),
coverage_fuzzing: _('Coverage Fuzzing'),
api_fuzzing: _('API Fuzzing')
}.freeze
end
def to_h def to_h
{ {
auto_devops_enabled: auto_devops_source?, auto_devops_enabled: auto_devops_source?,
...@@ -115,8 +75,9 @@ module Projects ...@@ -115,8 +75,9 @@ module Projects
end end
# DAST On-demand scans is a static (non job) entry. Add it manually following DAST # DAST On-demand scans is a static (non job) entry. Add it manually following DAST
# TODO: remove as part of https://gitlab.com/gitlab-org/gitlab/-/issues/323375
def dast_profiles_insert(scans) def dast_profiles_insert(scans)
index = scans.index { |scan| scan[:name] == localized_scan_names[:dast] } index = scans.index { |scan| scan[:type] == :dast }
unless index.nil? unless index.nil?
scans.insert(index + 1, scan(:dast_profiles, configured: true, status: s_('SecurityConfiguration|Available for on-demand DAST'))) scans.insert(index + 1, scan(:dast_profiles, configured: true, status: s_('SecurityConfiguration|Available for on-demand DAST')))
...@@ -130,10 +91,7 @@ module Projects ...@@ -130,10 +91,7 @@ module Projects
type: type, type: type,
configured: configured, configured: configured,
status: status, status: status,
description: self.class.localized_scan_descriptions[type], configuration_path: configuration_path(type)
link: help_page_path(SCAN_DOCS[type]),
configuration_path: configuration_path(type),
name: localized_scan_names[type]
} }
end end
...@@ -141,10 +99,6 @@ module Projects ...@@ -141,10 +99,6 @@ module Projects
::Security::SecurityJobsFinder.allowed_job_types + ::Security::LicenseComplianceJobsFinder.allowed_job_types ::Security::SecurityJobsFinder.allowed_job_types + ::Security::LicenseComplianceJobsFinder.allowed_job_types
end end
def localized_scan_names
@localized_scan_names ||= self.class.localized_scan_names
end
def project_settings def project_settings
project.security_setting project.security_setting
end end
......
...@@ -2,10 +2,10 @@ import { GlAlert, GlLink } from '@gitlab/ui'; ...@@ -2,10 +2,10 @@ import { GlAlert, GlLink } from '@gitlab/ui';
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { merge } from 'lodash'; import { merge } from 'lodash';
import SecurityConfigurationApp from 'ee/security_configuration/components/app.vue'; import SecurityConfigurationApp from 'ee/security_configuration/components/app.vue';
import FeatureStatus from 'ee/security_configuration/components/feature_status.vue'; import ConfigurationTable from 'ee/security_configuration/components/configuration_table.vue';
import ManageFeature from 'ee/security_configuration/components/manage_feature.vue';
import { useLocalStorageSpy } from 'helpers/local_storage_helper'; import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import stubChildren from 'helpers/stub_children'; import stubChildren from 'helpers/stub_children';
import { scanners } from '~/security_configuration/components/scanners_constants';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import { generateFeatures } from './helpers'; import { generateFeatures } from './helpers';
...@@ -31,7 +31,6 @@ describe('Security Configuration App', () => { ...@@ -31,7 +31,6 @@ describe('Security Configuration App', () => {
{ {
stubs: { stubs: {
...stubChildren(SecurityConfigurationApp), ...stubChildren(SecurityConfigurationApp),
GlTable: false,
GlSprintf: false, GlSprintf: false,
}, },
propsData, propsData,
...@@ -51,13 +50,8 @@ describe('Security Configuration App', () => { ...@@ -51,13 +50,8 @@ describe('Security Configuration App', () => {
}); });
const getPipelinesLink = () => wrapper.find({ ref: 'pipelinesLink' }); const getPipelinesLink = () => wrapper.find({ ref: 'pipelinesLink' });
const getFeaturesTable = () => wrapper.find({ ref: 'securityControlTable' }); const getConfigurationTable = () => wrapper.find(ConfigurationTable);
const getFeaturesRows = () => getFeaturesTable().findAll('tbody tr');
const getAlert = () => wrapper.find(GlAlert); const getAlert = () => wrapper.find(GlAlert);
const getRowCells = (row) => {
const [feature, status, manage] = row.findAll('td').wrappers;
return { feature, status, manage };
};
describe('header', () => { describe('header', () => {
it.each` it.each`
...@@ -161,30 +155,31 @@ describe('Security Configuration App', () => { ...@@ -161,30 +155,31 @@ describe('Security Configuration App', () => {
}); });
describe('features table', () => { describe('features table', () => {
it('passes the expected data to the GlTable', () => { it('passes the expected features to the configuration table', () => {
const features = generateFeatures(5); const features = generateFeatures(scanners.length);
createComponent({ propsData: { features } }); createComponent({ propsData: { features } });
const table = getConfigurationTable();
expect(getFeaturesTable().classes('b-table-stacked-md')).toBeTruthy(); const receivedFeatures = table.props('features');
const rows = getFeaturesRows();
expect(rows).toHaveLength(5); scanners.forEach((scanner, i) => {
expect(receivedFeatures[i]).toMatchObject({
for (let i = 0; i < features.length; i += 1) { ...features[i],
const { feature, status, manage } = getRowCells(rows.at(i)); name: scanner.name,
expect(feature.text()).toMatch(features[i].name); description: scanner.description,
expect(feature.text()).toMatch(features[i].description); helpPath: scanner.helpPath,
expect(status.find(FeatureStatus).props()).toEqual({
feature: features[i],
gitlabCiPresent: propsData.gitlabCiPresent,
gitlabCiHistoryPath: propsData.gitlabCiHistoryPath,
}); });
expect(manage.find(ManageFeature).props()).toEqual({ });
feature: features[i], });
autoDevopsEnabled: propsData.autoDevopsEnabled,
}); it('passes the expected props data to the configuration table', () => {
expect(feature.find(GlLink).props('href')).toBe(features[i].href); createComponent();
}
expect(getConfigurationTable().props()).toMatchObject({
autoDevopsEnabled: propsData.autoDevopsEnabled,
gitlabCiPresent: propsData.gitlabCiPresent,
gitlabCiHistoryPath: propsData.gitlabCiHistoryPath,
});
}); });
}); });
}); });
import { GlLink } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import ConfigurationTable from 'ee/security_configuration/components/configuration_table.vue';
import FeatureStatus from 'ee/security_configuration/components/feature_status.vue';
import ManageFeature from 'ee/security_configuration/components/manage_feature.vue';
import stubChildren from 'helpers/stub_children';
import { generateFeatures } from './helpers';
const propsData = {
features: [],
autoDevopsEnabled: false,
gitlabCiPresent: false,
gitlabCiHistoryPath: '/ci/history',
};
describe('ConfigurationTable component', () => {
let wrapper;
const mockFeatures = [
...generateFeatures(1, {
name: 'foo',
description: 'Foo description',
helpPath: '/help/foo',
}),
...generateFeatures(1, {
name: 'bar',
description: 'Bar description',
helpPath: '/help/bar',
}),
];
const createComponent = (props) => {
wrapper = mount(ConfigurationTable, {
stubs: {
...stubChildren(ConfigurationTable),
GlTable: false,
},
propsData: {
...propsData,
...props,
},
});
};
const getRows = () => wrapper.findAll('tbody tr');
const getRowCells = (row) => {
const [description, status, manage] = row.findAll('td').wrappers;
return { description, status, manage };
};
afterEach(() => {
wrapper.destroy();
});
it.each(mockFeatures)('renders the feature %p correctly', (feature) => {
createComponent({ features: [feature] });
expect(wrapper.classes('b-table-stacked-md')).toBeTruthy();
const rows = getRows();
expect(rows).toHaveLength(1);
const { description, status, manage } = getRowCells(rows.at(0));
expect(description.text()).toMatch(feature.name);
expect(description.text()).toMatch(feature.description);
expect(status.find(FeatureStatus).props()).toEqual({
feature,
gitlabCiPresent: propsData.gitlabCiPresent,
gitlabCiHistoryPath: propsData.gitlabCiHistoryPath,
});
expect(manage.find(ManageFeature).props()).toEqual({
feature,
autoDevopsEnabled: propsData.autoDevopsEnabled,
});
expect(description.find(GlLink).attributes('href')).toBe(feature.helpPath);
});
});
import { scanners } from '~/security_configuration/components/scanners_constants';
export const generateFeatures = (n, overrides = {}) => { export const generateFeatures = (n, overrides = {}) => {
return [...Array(n).keys()].map((i) => ({ return [...Array(n).keys()].map((i) => ({
type: `scan-type-${i}`, type: scanners[i % scanners.length].type,
name: `name-feature-${i}`,
description: `description-feature-${i}`,
link: `link-feature-${i}`,
configuration_path: i % 2 ? `configuration_path-${i}` : null, configuration_path: i % 2 ? `configuration_path-${i}` : null,
configured: i % 2 === 0, configured: i % 2 === 0,
status: i % 2 === 0 ? 'Enabled' : 'Not enabled', status: i % 2 === 0 ? 'Enabled' : 'Not enabled',
......
...@@ -263,10 +263,7 @@ RSpec.describe Projects::Security::ConfigurationPresenter do ...@@ -263,10 +263,7 @@ RSpec.describe Projects::Security::ConfigurationPresenter do
"type" => type.to_s, "type" => type.to_s,
"configured" => configured, "configured" => configured,
"status" => status_str, "status" => status_str,
"description" => described_class.localized_scan_descriptions[type], "configuration_path" => configuration_path
"link" => help_page_path(described_class::SCAN_DOCS[type]),
"configuration_path" => configuration_path,
"name" => described_class.localized_scan_names[type]
} }
end end
......
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import ConfigurationTable from '~/security_configuration/components/configuration_table.vue'; import ConfigurationTable from '~/security_configuration/components/configuration_table.vue';
import { features, UPGRADE_CTA } from '~/security_configuration/components/features_constants'; import { scanners, UPGRADE_CTA } from '~/security_configuration/components/scanners_constants';
import { import {
REPORT_TYPE_SAST, REPORT_TYPE_SAST,
...@@ -25,20 +25,20 @@ describe('Configuration Table Component', () => { ...@@ -25,20 +25,20 @@ describe('Configuration Table Component', () => {
createComponent(); createComponent();
}); });
describe.each(features.map((feature, i) => [feature, i]))('given feature %s', (feature, i) => { describe.each(scanners.map((scanner, i) => [scanner, i]))('given scanner %s', (scanner, i) => {
it('should match strings', () => { it('should match strings', () => {
expect(wrapper.text()).toContain(feature.name); expect(wrapper.text()).toContain(scanner.name);
expect(wrapper.text()).toContain(feature.description); expect(wrapper.text()).toContain(scanner.description);
if (feature.type === REPORT_TYPE_SAST) { if (scanner.type === REPORT_TYPE_SAST) {
expect(wrapper.findByTestId(feature.type).text()).toBe('Configure via Merge Request'); expect(wrapper.findByTestId(scanner.type).text()).toBe('Configure via Merge Request');
} else if (feature.type !== REPORT_TYPE_SECRET_DETECTION) { } else if (scanner.type !== REPORT_TYPE_SECRET_DETECTION) {
expect(wrapper.findByTestId(feature.type).text()).toMatchInterpolatedText(UPGRADE_CTA); expect(wrapper.findByTestId(scanner.type).text()).toMatchInterpolatedText(UPGRADE_CTA);
} }
}); });
it('should show expected help link', () => { it('should show expected help link', () => {
const helpLink = findHelpLinks().at(i); const helpLink = findHelpLinks().at(i);
expect(helpLink.attributes('href')).toBe(feature.helpPath); expect(helpLink.attributes('href')).toBe(scanner.helpPath);
}); });
}); });
}); });
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { UPGRADE_CTA } from '~/security_configuration/components/features_constants'; import { UPGRADE_CTA } from '~/security_configuration/components/scanners_constants';
import Upgrade from '~/security_configuration/components/upgrade.vue'; import Upgrade from '~/security_configuration/components/upgrade.vue';
const TEST_URL = 'http://www.example.test'; const TEST_URL = 'http://www.example.test';
......
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