diff --git a/app/assets/javascripts/security_configuration/components/training_provider_list.vue b/app/assets/javascripts/security_configuration/components/training_provider_list.vue
index 6dae8e50908f2792ab2718efd2652a42031e4295..578d7c8a18c33fb5990dd3aa991b948c10016880 100644
--- a/app/assets/javascripts/security_configuration/components/training_provider_list.vue
+++ b/app/assets/javascripts/security_configuration/components/training_provider_list.vue
@@ -247,6 +247,8 @@ export default {
               :label="__('Training mode')"
               label-position="hidden"
               :disabled="!securityTrainingEnabled"
+              data-qa-selector="security_training_toggle"
+              :data-qa-training-provider="provider.name"
               @change="toggleProvider(provider)"
             />
             <div v-if="$options.TEMP_PROVIDER_LOGOS[provider.name]" class="gl-ml-4">
diff --git a/ee/app/assets/javascripts/vulnerabilities/components/vulnerability_training.vue b/ee/app/assets/javascripts/vulnerabilities/components/vulnerability_training.vue
index d3f11702f20bb33a1b9d42f4be169ce95b52858c..2ea2307619104ccbcc60331428f5205de1ade510 100644
--- a/ee/app/assets/javascripts/vulnerabilities/components/vulnerability_training.vue
+++ b/ee/app/assets/javascripts/vulnerabilities/components/vulnerability_training.vue
@@ -187,9 +187,19 @@ export default {
               role="presentation"
             ></div>
           </span>
-          <span class="gl-font-weight-bold gl-font-base">{{ name }}</span>
+          <span
+            class="gl-font-weight-bold gl-font-base"
+            data-qa-selector="security_training_text"
+            >{{ name }}</span
+          >
         </div>
-        <gl-link :href="url" target="_blank" @click="clickTrainingLink(name, identifier)">
+        <gl-link
+          :href="url"
+          target="_blank"
+          data-qa-selector="security_training_link"
+          :data-qa-training-name="name"
+          @click="clickTrainingLink(name, identifier)"
+        >
           {{ $options.i18n.viewTraining }}
           <gl-icon class="gl-ml-2" name="external-link" :size="12" />
         </gl-link>
diff --git a/qa/qa/ee/fixtures/secure_premade_reports/gl-sast-report.json b/qa/qa/ee/fixtures/secure_premade_reports/gl-sast-report.json
index 4ad2e9b1b82bc53437cde182232e03924f4ade7b..d03494eeaf4eae91e1de64519037fecd4f11f386 100644
--- a/qa/qa/ee/fixtures/secure_premade_reports/gl-sast-report.json
+++ b/qa/qa/ee/fixtures/secure_premade_reports/gl-sast-report.json
@@ -148,6 +148,62 @@
         }
       ]
     },
+    {
+      "id": "7e126e060d3d0b5f0b11506528c82fa40f5d144d85b2460ab01c44c7c9043be7",
+      "category": "sast",
+      "message": "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')",
+      "description": "Detected possible formatted SQL query. Use parameterized queries instead.\n",
+      "cve": "semgrep_id:bandit.B608:304:304",
+      "severity": "Medium",
+      "scanner": {
+        "id": "semgrep",
+        "name": "Semgrep"
+      },
+      "location": {
+        "file": "django/contrib/gis/db/backends/postgis/operations.py",
+        "start_line": 304
+      },
+      "identifiers": [
+        {
+          "type": "semgrep_id",
+          "name": "bandit.B608",
+          "value": "bandit.B608",
+          "url": "https://semgrep.dev/r/gitlab.bandit.B608"
+        },
+        {
+          "type": "cwe",
+          "name": "CWE-89",
+          "value": "89",
+          "url": "https://cwe.mitre.org/data/definitions/89.html"
+        },
+        {
+          "type": "owasp",
+          "name": "A1:2017 - Injection",
+          "value": "A1:2017"
+        },
+        {
+          "type": "bandit_test_id",
+          "name": "Bandit Test ID B608",
+          "value": "B608"
+        }
+      ],
+      "tracking": {
+        "type": "source",
+        "items": [
+          {
+            "file": "django/contrib/gis/db/backends/postgis/operations.py",
+            "line_start": 304,
+            "line_end": 304,
+            "signatures": [
+              {
+                "algorithm": "scope_offset",
+                "value": "django/contrib/gis/db/backends/postgis/operations.py|PostGISOperations[0]|_get_postgis_func[0]:6"
+              }
+            ]
+          }
+        ]
+      }
+    },
     {
       "id": "47f7fccbb39495c68dac599833fa631a5c043025e8a50fc036f86bafde7kl090",
       "category": "sast",
@@ -171,6 +227,12 @@
           "name": "Find Security Bugs-CIPHER_INTEGRITY",
           "value": "CIPHER_INTEGRITY",
           "url": "https://find-sec-bugs.github.io/bugs.htm#CIPHER_INTEGRITY"
+        },
+        {
+          "type": "cwe",
+          "name": "CWE-353",
+          "value": "353",
+          "url": "https://cwe.mitre.org/data/definitions/353.html"
         }
       ],
       "priority": "Medium",
diff --git a/qa/qa/ee/page/project/secure/security_dashboard.rb b/qa/qa/ee/page/project/secure/security_dashboard.rb
index 9475bc954e8c97b97c375ae7b1ea923945d4d88e..45f18e96212c866e9d42e994de1b7a748fbe839f 100644
--- a/qa/qa/ee/page/project/secure/security_dashboard.rb
+++ b/qa/qa/ee/page/project/secure/security_dashboard.rb
@@ -66,7 +66,7 @@ def export_vulnerabilities_to_csv
             end
 
             def wait_for_vuln_report_to_load
-              wait_until(max_duration: 10, sleep_interval: 2, message: "Vulnerability report not loaded yet") do
+              wait_until(max_duration: 20, sleep_interval: 2, message: "Vulnerability report not loaded yet") do
                 has_element?(:vulnerability_report_header)
               end
             end
diff --git a/qa/qa/ee/page/project/secure/vulnerability_details.rb b/qa/qa/ee/page/project/secure/vulnerability_details.rb
index ea00884712a34fdf6d3a9d626e7c6084f9eb19c0..b0dbb174d7e0c7fe80e5da0eb40bc8822b29a205 100644
--- a/qa/qa/ee/page/project/secure/vulnerability_details.rb
+++ b/qa/qa/ee/page/project/secure/vulnerability_details.rb
@@ -39,6 +39,11 @@ class VulnerabilityDetails < QA::Page::Base
               element :jira_issue_link
             end
 
+            view 'ee/app/assets/javascripts/vulnerabilities/components/vulnerability_training.vue' do
+              element :security_training_text
+              element :security_training_link
+            end
+
             def has_component?(component_name:)
               has_element?(component_name.to_sym)
             end
@@ -47,6 +52,15 @@ def has_vulnerability_title?(title:)
               has_element?(:vulnerability_title, text: title)
             end
 
+            def security_training_present?(training_name:)
+              has_element?(:security_training_text, text: training_name)
+            end
+
+            def training_link_present?(training_name:, url:)
+              element = find_element(:security_training_link, training_name: training_name)
+              element["href"].include?(url)
+            end
+
             def has_vulnerability_description?(description:)
               has_element?(:vulnerability_description, text: description)
             end
@@ -75,6 +89,10 @@ def choose_dismissal_reason
               find_element(:dismissal_reason_dropdown).find('li', text: reasons.sample).click
             end
 
+            def training_header_present?
+              has_css?('h3', text: 'Training')
+            end
+
             def has_vulnerability_status?(status)
               has_element?(:vulnerability_status_dropdown, text: "#{status.capitalize}")
             end
diff --git a/qa/qa/ee/page/project/secure/vulnerability_security_training.rb b/qa/qa/ee/page/project/secure/vulnerability_security_training.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a66933625fc781c1b59d66cd7b8f1a4d821ff6e9
--- /dev/null
+++ b/qa/qa/ee/page/project/secure/vulnerability_security_training.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module QA
+  module EE
+    module Page
+      module Project
+        module Secure
+          class VulnerabilitySecurityTraining < QA::Page::Base
+            view 'app/assets/javascripts/security_configuration/components/training_provider_list.vue' do
+              element :security_training_toggle
+            end
+
+            def toggle_training_provider(name, toggle_to = "on")
+              within_element(:security_training_toggle, training_provider: name) do
+                toggle = find('button.gl-toggle')
+                checked = toggle[:class].include?('is-checked')
+                toggle.click if (checked && toggle_to == "off") || (!checked && toggle_to == "on")
+              end
+            end
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/qa/qa/specs/features/ee/browser_ui/10_govern/create_merge_request_with_secure_spec.rb b/qa/qa/specs/features/ee/browser_ui/10_govern/create_merge_request_with_secure_spec.rb
index 6f40383c0b1f384667ea26edce2da52bdf627c45..2e61aae4c8979dfde729c62023c0cff6e3bcdcac 100644
--- a/qa/qa/specs/features/ee/browser_ui/10_govern/create_merge_request_with_secure_spec.rb
+++ b/qa/qa/specs/features/ee/browser_ui/10_govern/create_merge_request_with_secure_spec.rb
@@ -3,7 +3,7 @@
 module QA
   RSpec.describe 'Govern', :runner, product_group: :threat_insights do
     describe 'Security Reports in a Merge Request Widget' do
-      let(:sast_vuln_count) { 6 }
+      let(:sast_vuln_count) { 7 }
       let(:dependency_scan_vuln_count) { 4 }
       let(:container_scan_vuln_count) { 8 }
       let(:vuln_name) { "Regular Expression Denial of Service in debug" }
diff --git a/qa/qa/specs/features/ee/browser_ui/10_govern/vulnerability_security_training_spec.rb b/qa/qa/specs/features/ee/browser_ui/10_govern/vulnerability_security_training_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..be7de24107a572695bd50d9b751eaaab00160562
--- /dev/null
+++ b/qa/qa/specs/features/ee/browser_ui/10_govern/vulnerability_security_training_spec.rb
@@ -0,0 +1,99 @@
+# frozen_string_literal: true
+
+module QA
+  RSpec.describe 'Govern', :runner, product_group: :threat_insights do
+    describe 'Vulnerability Report Security Training' do
+      let(:vuln_name) { "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')" }
+
+      let(:training_providers) { ["Kontra", "Secure Code Warrior"] }
+      let(:secure_code_warrior_url) { "portal.securecodewarrior.com" }
+      let(:kontra_url) { "application.security/gitlab/free-application-security-training" }
+      let(:secure_code_warrior_text) { training_providers[1] }
+      let(:kontra_text) { training_providers[0] }
+      let!(:project) do
+        Resource::Project.fabricate_via_api! do |project|
+          project.name = 'vulnerability-report-security-training'
+          project.description = 'To Test integration with security training in vulnerability report'
+        end
+      end
+
+      let!(:artefacts_directory) do
+        Pathname.new(File.join(EE::Runtime::Path.fixtures_path, 'secure_premade_reports'))
+      end
+
+      let!(:runner) do
+        Resource::ProjectRunner.fabricate_via_api! do |runner|
+          runner.project = project
+          runner.name = "runner-for-#{project.name}"
+          runner.tags = ['secure_report']
+        end
+      end
+
+      let!(:repository) do
+        Resource::Repository::Commit.fabricate_via_api! do |commit|
+          commit.project = project
+          commit.commit_message = 'Add report files and .gitlab-ci.yml'
+          commit.add_directory(artefacts_directory)
+        end
+      end
+
+      before do
+        Flow::Login.sign_in
+        project.visit!
+      end
+
+      after do
+        runner.remove_via_api!
+        project.remove_via_api!
+      end
+
+      it 'shows security training section for supported vulnerabilities when the setting is toggled ON',
+        testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/411764' do
+        # Enable two security training providers
+        toggle_training_providers("on")
+
+        visit_vulnerability(vuln_name)
+
+        QA::EE::Page::Project::Secure::VulnerabilityDetails.perform do |vulnerability_details|
+          aggregate_failures "testing vulnerability details" do
+            expect(vulnerability_details.security_training_present?(training_name: secure_code_warrior_text)).to be true
+            expect(vulnerability_details.training_link_present?(training_name: secure_code_warrior_text,
+              url: secure_code_warrior_url)).to be true
+
+            expect(vulnerability_details.security_training_present?(training_name: kontra_text)).to be true
+            expect(vulnerability_details.training_link_present?(training_name: kontra_text,
+              url: kontra_url)).to be true
+          end
+        end
+      end
+
+      it 'does not show security training section in vulnerability details when the setting is turned OFF',
+        testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/412398' do
+        toggle_training_providers("off")
+        visit_vulnerability(vuln_name)
+
+        expect(QA::EE::Page::Project::Secure::VulnerabilityDetails.perform(&:training_header_present?)).to be false
+      end
+
+      def toggle_training_providers(toggle_to = "on")
+        Page::Project::Menu.perform(&:go_to_security_configuration)
+        click_link("Vulnerability Management")
+        QA::EE::Page::Project::Secure::VulnerabilitySecurityTraining.perform do |security_training|
+          training_providers.each do |provider|
+            security_training.toggle_training_provider(provider, toggle_to)
+          end
+        end
+      end
+
+      def visit_vulnerability(vulnerability_name)
+        Page::Project::Menu.perform(&:go_to_vulnerability_report)
+        QA::EE::Page::Project::Secure::SecurityDashboard.perform do |security_dashboard|
+          security_dashboard.wait_for_vuln_report_to_load
+          expect(security_dashboard).to have_vulnerability(description: vulnerability_name)
+
+          security_dashboard.click_vulnerability(description: vulnerability_name)
+        end
+      end
+    end
+  end
+end