From 24abff0576a79671c277ecd6c932b7d3254920a0 Mon Sep 17 00:00:00 2001
From: Mark Florian <mflorian@gitlab.com>
Date: Tue, 9 Mar 2021 16:25:52 +0000
Subject: [PATCH] Add pinning tests for current implementation

This adds a temporary [pinning test][1] to record the current behaviour
of the Ultimate version of the Security Configuration page, so as to
support the refactoring work in the next commit.

Once the work has been done, these tests can be removed, having served
their purpose.

[1]: https://web.archive.org/web/20170826101146/http://wiki.c2.com/?PinningTests
---
 .../components/configuration_table.vue        | 10 +-
 ...res_constants.js => scanners_constants.js} | 61 +++++++-----
 .../components/upgrade.vue                    |  2 +-
 .../vue_shared/security_reports/constants.js  |  2 +-
 ...security-configuration-ui-strings-ssot.yml |  5 +
 .../security_configuration/components/app.vue | 97 +++++-------------
 .../components/configuration_table.vue        | 98 +++++++++++++++++++
 .../security_configuration/index.js           |  2 -
 .../security/configuration_presenter.rb       | 52 +---------
 .../components/app_spec.js                    | 55 +++++------
 .../components/configuration_table_spec.js    | 75 ++++++++++++++
 .../components/helpers.js                     |  7 +-
 .../security/configuration_presenter_spec.rb  |  5 +-
 .../configuration_table_spec.js               | 18 ++--
 .../security_configuration/upgrade_spec.js    |  2 +-
 15 files changed, 290 insertions(+), 201 deletions(-)
 rename app/assets/javascripts/security_configuration/components/{features_constants.js => scanners_constants.js} (66%)
 create mode 100644 changelogs/unreleased/323376-security-configuration-ui-strings-ssot.yml
 create mode 100644 ee/app/assets/javascripts/security_configuration/components/configuration_table.vue
 create mode 100644 ee/spec/frontend/security_configuration/components/configuration_table_spec.js

diff --git a/app/assets/javascripts/security_configuration/components/configuration_table.vue b/app/assets/javascripts/security_configuration/components/configuration_table.vue
index d1ac7190c37d8..168d158a7863f 100644
--- a/app/assets/javascripts/security_configuration/components/configuration_table.vue
+++ b/app/assets/javascripts/security_configuration/components/configuration_table.vue
@@ -1,5 +1,5 @@
 <script>
-import { GlLink, GlSprintf, GlTable, GlAlert } from '@gitlab/ui';
+import { GlLink, GlTable, GlAlert } from '@gitlab/ui';
 import { s__, sprintf } from '~/locale';
 import {
   REPORT_TYPE_SAST,
@@ -8,10 +8,11 @@ import {
   REPORT_TYPE_DEPENDENCY_SCANNING,
   REPORT_TYPE_CONTAINER_SCANNING,
   REPORT_TYPE_COVERAGE_FUZZING,
+  REPORT_TYPE_API_FUZZING,
   REPORT_TYPE_LICENSE_COMPLIANCE,
 } from '~/vue_shared/security_reports/constants';
-import { features } from './features_constants';
 import ManageSast from './manage_sast.vue';
+import { scanners } from './scanners_constants';
 import Upgrade from './upgrade.vue';
 
 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}`;
 export default {
   components: {
     GlLink,
-    GlSprintf,
     GlTable,
     GlAlert,
   },
   data: () => ({
-    features,
     errorMessage: '',
   }),
   methods: {
@@ -45,6 +44,7 @@ export default {
         [REPORT_TYPE_DEPENDENCY_SCANNING]: Upgrade,
         [REPORT_TYPE_CONTAINER_SCANNING]: Upgrade,
         [REPORT_TYPE_COVERAGE_FUZZING]: Upgrade,
+        [REPORT_TYPE_API_FUZZING]: Upgrade,
         [REPORT_TYPE_LICENSE_COMPLIANCE]: Upgrade,
       };
 
@@ -64,7 +64,7 @@ export default {
         thClass,
       },
     ],
-    items: features,
+    items: scanners,
   },
 };
 </script>
diff --git a/app/assets/javascripts/security_configuration/components/features_constants.js b/app/assets/javascripts/security_configuration/components/scanners_constants.js
similarity index 66%
rename from app/assets/javascripts/security_configuration/components/features_constants.js
rename to app/assets/javascripts/security_configuration/components/scanners_constants.js
index c0eef0611a08b..9846df0b4bf72 100644
--- a/app/assets/javascripts/security_configuration/components/features_constants.js
+++ b/app/assets/javascripts/security_configuration/components/scanners_constants.js
@@ -1,5 +1,5 @@
 import { helpPagePath } from '~/helpers/help_page_helper';
-import { s__ } from '~/locale';
+import { __, s__ } from '~/locale';
 
 import {
   REPORT_TYPE_SAST,
@@ -9,58 +9,65 @@ import {
   REPORT_TYPE_DEPENDENCY_SCANNING,
   REPORT_TYPE_CONTAINER_SCANNING,
   REPORT_TYPE_COVERAGE_FUZZING,
+  REPORT_TYPE_API_FUZZING,
   REPORT_TYPE_LICENSE_COMPLIANCE,
 } from '~/vue_shared/security_reports/constants';
 
 /**
  * Translations & helpPagePaths for Static Security Configuration Page
  */
-export const SAST_NAME = s__('Static Application Security Testing (SAST)');
-export const SAST_DESCRIPTION = s__('Analyze your source code for known vulnerabilities.');
+export const SAST_NAME = __('Static Application Security Testing (SAST)');
+export const SAST_DESCRIPTION = __('Analyze your source code for known vulnerabilities.');
 export const SAST_HELP_PATH = helpPagePath('user/application_security/sast/index');
 
-export const DAST_NAME = s__('Dynamic Application Security Testing (DAST)');
-export const DAST_DESCRIPTION = s__('Analyze a review version of your web application.');
+export const DAST_NAME = __('Dynamic Application Security Testing (DAST)');
+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_PROFILES_NAME = s__('DAST Scans');
-export const DAST_PROFILES_DESCRIPTION = s__('Analyze a review version of your web application.');
+export const DAST_PROFILES_NAME = __('DAST Scans');
+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 SECRET_DETECTION_NAME = s__('Secret Detection');
-export const SECRET_DETECTION_DESCRIPTION = s__(
+export const SECRET_DETECTION_NAME = __('Secret Detection');
+export const SECRET_DETECTION_DESCRIPTION = __(
   'Analyze your source code and git history for secrets.',
 );
 export const SECRET_DETECTION_HELP_PATH = helpPagePath(
   'user/application_security/secret_detection/index',
 );
 
-export const DEPENDENCY_SCANNING_NAME = s__('Dependency Scanning');
-export const DEPENDENCY_SCANNING_DESCRIPTION = s__(
+export const DEPENDENCY_SCANNING_NAME = __('Dependency Scanning');
+export const DEPENDENCY_SCANNING_DESCRIPTION = __(
   'Analyze your dependencies for known vulnerabilities.',
 );
 export const DEPENDENCY_SCANNING_HELP_PATH = helpPagePath(
   'user/application_security/dependency_scanning/index',
 );
 
-export const CONTAINER_SCANNING_NAME = s__('Container Scanning');
-export const CONTAINER_SCANNING_DESCRIPTION = s__(
+export const CONTAINER_SCANNING_NAME = __('Container Scanning');
+export const CONTAINER_SCANNING_DESCRIPTION = __(
   'Check your Docker images for known vulnerabilities.',
 );
 export const CONTAINER_SCANNING_HELP_PATH = helpPagePath(
   'user/application_security/container_scanning/index',
 );
 
-export const COVERAGE_FUZZING_NAME = s__('Coverage Fuzzing');
-export const COVERAGE_FUZZING_DESCRIPTION = s__(
+export const COVERAGE_FUZZING_NAME = __('Coverage Fuzzing');
+export const COVERAGE_FUZZING_DESCRIPTION = __(
   'Find bugs in your code with coverage-guided fuzzing.',
 );
 export const COVERAGE_FUZZING_HELP_PATH = helpPagePath(
   'user/application_security/coverage_fuzzing/index',
 );
 
-export const LICENSE_COMPLIANCE_NAME = s__('License Compliance');
-export const LICENSE_COMPLIANCE_DESCRIPTION = s__(
+export const API_FUZZING_NAME = __('API Fuzzing');
+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.',
 );
 export const LICENSE_COMPLIANCE_HELP_PATH = helpPagePath(
@@ -71,7 +78,7 @@ export const UPGRADE_CTA = s__(
   'SecurityConfiguration|Available with %{linkStart}upgrade or free trial%{linkEnd}',
 );
 
-export const features = [
+export const scanners = [
   {
     name: SAST_NAME,
     description: SAST_DESCRIPTION,
@@ -90,12 +97,6 @@ export const features = [
     helpPath: DAST_PROFILES_HELP_PATH,
     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,
     description: DEPENDENCY_SCANNING_DESCRIPTION,
@@ -108,12 +109,24 @@ export const features = [
     helpPath: CONTAINER_SCANNING_HELP_PATH,
     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,
     description: COVERAGE_FUZZING_DESCRIPTION,
     helpPath: COVERAGE_FUZZING_HELP_PATH,
     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,
     description: LICENSE_COMPLIANCE_DESCRIPTION,
diff --git a/app/assets/javascripts/security_configuration/components/upgrade.vue b/app/assets/javascripts/security_configuration/components/upgrade.vue
index 04f3763d5daf2..518eb57731d05 100644
--- a/app/assets/javascripts/security_configuration/components/upgrade.vue
+++ b/app/assets/javascripts/security_configuration/components/upgrade.vue
@@ -1,6 +1,6 @@
 <script>
 import { GlLink, GlSprintf } from '@gitlab/ui';
-import { UPGRADE_CTA } from './features_constants';
+import { UPGRADE_CTA } from './scanners_constants';
 
 export default {
   components: {
diff --git a/app/assets/javascripts/vue_shared/security_reports/constants.js b/app/assets/javascripts/vue_shared/security_reports/constants.js
index 56a8853412d29..1cdcf87097f1f 100644
--- a/app/assets/javascripts/vue_shared/security_reports/constants.js
+++ b/app/assets/javascripts/vue_shared/security_reports/constants.js
@@ -23,7 +23,7 @@ export const REPORT_TYPE_SECRET_DETECTION = 'secret_detection';
 export const REPORT_TYPE_DEPENDENCY_SCANNING = 'dependency_scanning';
 export const REPORT_TYPE_CONTAINER_SCANNING = 'container_scanning';
 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';
 
 /**
diff --git a/changelogs/unreleased/323376-security-configuration-ui-strings-ssot.yml b/changelogs/unreleased/323376-security-configuration-ui-strings-ssot.yml
new file mode 100644
index 0000000000000..5b901a6e15853
--- /dev/null
+++ b/changelogs/unreleased/323376-security-configuration-ui-strings-ssot.yml
@@ -0,0 +1,5 @@
+---
+title: Add API Fuzzing to Security Configuration page, and re-order scanners
+merge_request: 56022
+author:
+type: changed
diff --git a/ee/app/assets/javascripts/security_configuration/components/app.vue b/ee/app/assets/javascripts/security_configuration/components/app.vue
index a67f6efdacd82..d410b29f933ee 100644
--- a/ee/app/assets/javascripts/security_configuration/components/app.vue
+++ b/ee/app/assets/javascripts/security_configuration/components/app.vue
@@ -1,23 +1,21 @@
 <script>
-import { GlAlert, GlLink, GlSprintf, GlTable } from '@gitlab/ui';
+import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui';
 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 glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
 import AutoFixSettings from './auto_fix_settings.vue';
-import FeatureStatus from './feature_status.vue';
-import ManageFeature from './manage_feature.vue';
+import ConfigurationTable from './configuration_table.vue';
 
 export default {
   components: {
     GlAlert,
     GlLink,
     GlSprintf,
-    GlTable,
     AutoFixSettings,
     LocalStorageSync,
-    FeatureStatus,
-    ManageFeature,
+    ConfigurationTable,
   },
   mixins: [glFeatureFlagsMixin()],
   props: {
@@ -26,10 +24,6 @@ export default {
       required: false,
       default: false,
     },
-    helpPagePath: {
-      type: String,
-      required: true,
-    },
     autoDevopsHelpPagePath: {
       type: String,
       required: true,
@@ -84,28 +78,6 @@ export default {
     devopsUrl() {
       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() {
       return Boolean(
         !parseBoolean(this.autoDevopsAlertDismissed) &&
@@ -114,16 +86,26 @@ export default {
           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: {
     dismissAutoDevopsAlert() {
       this.autoDevopsAlertDismissed = 'true';
     },
-    getFeatureDocumentationLinkLabel(item) {
-      return sprintf(s__('SecurityConfiguration|Feature documentation for %{featureName}'), {
-        featureName: item.name,
-      });
-    },
   },
   autoDevopsAlertMessage: s__(`
     SecurityConfiguration|You can quickly enable all security scanning tools by
@@ -166,40 +148,13 @@ export default {
       </gl-sprintf>
     </gl-alert>
 
-    <gl-table
-      ref="securityControlTable"
-      :items="features"
-      :fields="fields"
-      stacked="md"
-      :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>
+    <configuration-table
+      :features="featuresForDisplay"
+      :auto-devops-enabled="autoDevopsEnabled"
+      :gitlab-ci-present="gitlabCiPresent"
+      :gitlab-ci-history-path="gitlabCiHistoryPath"
+    />
 
-      <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" />
   </article>
 </template>
diff --git a/ee/app/assets/javascripts/security_configuration/components/configuration_table.vue b/ee/app/assets/javascripts/security_configuration/components/configuration_table.vue
new file mode 100644
index 0000000000000..8696d1a1de447
--- /dev/null
+++ b/ee/app/assets/javascripts/security_configuration/components/configuration_table.vue
@@ -0,0 +1,98 @@
+<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>
diff --git a/ee/app/assets/javascripts/security_configuration/index.js b/ee/app/assets/javascripts/security_configuration/index.js
index 7c5c26621a1c1..ab15b0309d179 100644
--- a/ee/app/assets/javascripts/security_configuration/index.js
+++ b/ee/app/assets/javascripts/security_configuration/index.js
@@ -11,7 +11,6 @@ export const initSecurityConfiguration = (el) => {
     autoDevopsHelpPagePath,
     autoDevopsPath,
     features,
-    helpPagePath,
     latestPipelinePath,
     autoFixEnabled,
     autoFixHelpPath,
@@ -33,7 +32,6 @@ export const initSecurityConfiguration = (el) => {
           autoDevopsHelpPagePath,
           autoDevopsPath,
           features: JSON.parse(features),
-          helpPagePath,
           latestPipelinePath,
           ...parseBooleanDataAttributes(el, [
             'autoDevopsEnabled',
diff --git a/ee/app/presenters/projects/security/configuration_presenter.rb b/ee/app/presenters/projects/security/configuration_presenter.rb
index 121e17373d3fa..723ec9cf106f8 100644
--- a/ee/app/presenters/projects/security/configuration_presenter.rb
+++ b/ee/app/presenters/projects/security/configuration_presenter.rb
@@ -9,46 +9,6 @@ class ConfigurationPresenter < Gitlab::View::Presenter::Delegated
 
       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
         {
           auto_devops_enabled: auto_devops_source?,
@@ -115,8 +75,9 @@ def latest_pipeline_path
       end
 
       # 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)
-        index = scans.index { |scan| scan[:name] == localized_scan_names[:dast] }
+        index = scans.index { |scan| scan[:type] == :dast }
 
         unless index.nil?
           scans.insert(index + 1, scan(:dast_profiles, configured: true, status: s_('SecurityConfiguration|Available for on-demand DAST')))
@@ -130,10 +91,7 @@ def scan(type, configured: false, status:)
           type: type,
           configured: configured,
           status: status,
-          description: self.class.localized_scan_descriptions[type],
-          link: help_page_path(SCAN_DOCS[type]),
-          configuration_path: configuration_path(type),
-          name: localized_scan_names[type]
+          configuration_path: configuration_path(type)
         }
       end
 
@@ -141,10 +99,6 @@ def scan_types
         ::Security::SecurityJobsFinder.allowed_job_types + ::Security::LicenseComplianceJobsFinder.allowed_job_types
       end
 
-      def localized_scan_names
-        @localized_scan_names ||= self.class.localized_scan_names
-      end
-
       def project_settings
         project.security_setting
       end
diff --git a/ee/spec/frontend/security_configuration/components/app_spec.js b/ee/spec/frontend/security_configuration/components/app_spec.js
index 43ab9ef1afb68..cebba404035ea 100644
--- a/ee/spec/frontend/security_configuration/components/app_spec.js
+++ b/ee/spec/frontend/security_configuration/components/app_spec.js
@@ -2,10 +2,10 @@ import { GlAlert, GlLink } from '@gitlab/ui';
 import { mount } from '@vue/test-utils';
 import { merge } from 'lodash';
 import SecurityConfigurationApp from 'ee/security_configuration/components/app.vue';
-import FeatureStatus from 'ee/security_configuration/components/feature_status.vue';
-import ManageFeature from 'ee/security_configuration/components/manage_feature.vue';
+import ConfigurationTable from 'ee/security_configuration/components/configuration_table.vue';
 import { useLocalStorageSpy } from 'helpers/local_storage_helper';
 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 { generateFeatures } from './helpers';
 
@@ -31,7 +31,6 @@ describe('Security Configuration App', () => {
         {
           stubs: {
             ...stubChildren(SecurityConfigurationApp),
-            GlTable: false,
             GlSprintf: false,
           },
           propsData,
@@ -51,13 +50,8 @@ describe('Security Configuration App', () => {
   });
 
   const getPipelinesLink = () => wrapper.find({ ref: 'pipelinesLink' });
-  const getFeaturesTable = () => wrapper.find({ ref: 'securityControlTable' });
-  const getFeaturesRows = () => getFeaturesTable().findAll('tbody tr');
+  const getConfigurationTable = () => wrapper.find(ConfigurationTable);
   const getAlert = () => wrapper.find(GlAlert);
-  const getRowCells = (row) => {
-    const [feature, status, manage] = row.findAll('td').wrappers;
-    return { feature, status, manage };
-  };
 
   describe('header', () => {
     it.each`
@@ -161,30 +155,31 @@ describe('Security Configuration App', () => {
   });
 
   describe('features table', () => {
-    it('passes the expected data to the GlTable', () => {
-      const features = generateFeatures(5);
+    it('passes the expected features to the configuration table', () => {
+      const features = generateFeatures(scanners.length);
 
       createComponent({ propsData: { features } });
-
-      expect(getFeaturesTable().classes('b-table-stacked-md')).toBeTruthy();
-      const rows = getFeaturesRows();
-      expect(rows).toHaveLength(5);
-
-      for (let i = 0; i < features.length; i += 1) {
-        const { feature, status, manage } = getRowCells(rows.at(i));
-        expect(feature.text()).toMatch(features[i].name);
-        expect(feature.text()).toMatch(features[i].description);
-        expect(status.find(FeatureStatus).props()).toEqual({
-          feature: features[i],
-          gitlabCiPresent: propsData.gitlabCiPresent,
-          gitlabCiHistoryPath: propsData.gitlabCiHistoryPath,
+      const table = getConfigurationTable();
+      const receivedFeatures = table.props('features');
+
+      scanners.forEach((scanner, i) => {
+        expect(receivedFeatures[i]).toMatchObject({
+          ...features[i],
+          name: scanner.name,
+          description: scanner.description,
+          helpPath: scanner.helpPath,
         });
-        expect(manage.find(ManageFeature).props()).toEqual({
-          feature: features[i],
-          autoDevopsEnabled: propsData.autoDevopsEnabled,
-        });
-        expect(feature.find(GlLink).props('href')).toBe(features[i].href);
-      }
+      });
+    });
+
+    it('passes the expected props data to the configuration table', () => {
+      createComponent();
+
+      expect(getConfigurationTable().props()).toMatchObject({
+        autoDevopsEnabled: propsData.autoDevopsEnabled,
+        gitlabCiPresent: propsData.gitlabCiPresent,
+        gitlabCiHistoryPath: propsData.gitlabCiHistoryPath,
+      });
     });
   });
 });
diff --git a/ee/spec/frontend/security_configuration/components/configuration_table_spec.js b/ee/spec/frontend/security_configuration/components/configuration_table_spec.js
new file mode 100644
index 0000000000000..41a7cba5c1261
--- /dev/null
+++ b/ee/spec/frontend/security_configuration/components/configuration_table_spec.js
@@ -0,0 +1,75 @@
+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);
+  });
+});
diff --git a/ee/spec/frontend/security_configuration/components/helpers.js b/ee/spec/frontend/security_configuration/components/helpers.js
index 7de2b971dcefa..ec0db18a36a92 100644
--- a/ee/spec/frontend/security_configuration/components/helpers.js
+++ b/ee/spec/frontend/security_configuration/components/helpers.js
@@ -1,9 +1,8 @@
+import { scanners } from '~/security_configuration/components/scanners_constants';
+
 export const generateFeatures = (n, overrides = {}) => {
   return [...Array(n).keys()].map((i) => ({
-    type: `scan-type-${i}`,
-    name: `name-feature-${i}`,
-    description: `description-feature-${i}`,
-    link: `link-feature-${i}`,
+    type: scanners[i % scanners.length].type,
     configuration_path: i % 2 ? `configuration_path-${i}` : null,
     configured: i % 2 === 0,
     status: i % 2 === 0 ? 'Enabled' : 'Not enabled',
diff --git a/ee/spec/presenters/projects/security/configuration_presenter_spec.rb b/ee/spec/presenters/projects/security/configuration_presenter_spec.rb
index f9a35ddef9ac1..c3dd8500ac77f 100644
--- a/ee/spec/presenters/projects/security/configuration_presenter_spec.rb
+++ b/ee/spec/presenters/projects/security/configuration_presenter_spec.rb
@@ -263,10 +263,7 @@ def security_scan(type, configured:, auto_dev_ops_enabled: false)
       "type" => type.to_s,
       "configured" => configured,
       "status" => status_str,
-      "description" => described_class.localized_scan_descriptions[type],
-      "link" => help_page_path(described_class::SCAN_DOCS[type]),
-      "configuration_path" => configuration_path,
-      "name" => described_class.localized_scan_names[type]
+      "configuration_path" => configuration_path
     }
   end
 
diff --git a/spec/frontend/security_configuration/configuration_table_spec.js b/spec/frontend/security_configuration/configuration_table_spec.js
index a9d9a0dbf1a37..b8a574dc4e0c7 100644
--- a/spec/frontend/security_configuration/configuration_table_spec.js
+++ b/spec/frontend/security_configuration/configuration_table_spec.js
@@ -1,7 +1,7 @@
 import { mount } from '@vue/test-utils';
 import { extendedWrapper } from 'helpers/vue_test_utils_helper';
 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 {
   REPORT_TYPE_SAST,
@@ -25,20 +25,20 @@ describe('Configuration Table Component', () => {
     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', () => {
-      expect(wrapper.text()).toContain(feature.name);
-      expect(wrapper.text()).toContain(feature.description);
-      if (feature.type === REPORT_TYPE_SAST) {
-        expect(wrapper.findByTestId(feature.type).text()).toBe('Configure via Merge Request');
-      } else if (feature.type !== REPORT_TYPE_SECRET_DETECTION) {
-        expect(wrapper.findByTestId(feature.type).text()).toMatchInterpolatedText(UPGRADE_CTA);
+      expect(wrapper.text()).toContain(scanner.name);
+      expect(wrapper.text()).toContain(scanner.description);
+      if (scanner.type === REPORT_TYPE_SAST) {
+        expect(wrapper.findByTestId(scanner.type).text()).toBe('Configure via Merge Request');
+      } else if (scanner.type !== REPORT_TYPE_SECRET_DETECTION) {
+        expect(wrapper.findByTestId(scanner.type).text()).toMatchInterpolatedText(UPGRADE_CTA);
       }
     });
 
     it('should show expected help link', () => {
       const helpLink = findHelpLinks().at(i);
-      expect(helpLink.attributes('href')).toBe(feature.helpPath);
+      expect(helpLink.attributes('href')).toBe(scanner.helpPath);
     });
   });
 });
diff --git a/spec/frontend/security_configuration/upgrade_spec.js b/spec/frontend/security_configuration/upgrade_spec.js
index b516b926dc513..1f0cc795fc527 100644
--- a/spec/frontend/security_configuration/upgrade_spec.js
+++ b/spec/frontend/security_configuration/upgrade_spec.js
@@ -1,5 +1,5 @@
 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';
 
 const TEST_URL = 'http://www.example.test';
-- 
GitLab