Skip to content

Require/remove approvals earlier when Scan Result Policy is enforced based on pre-existing vulnerabilities

Why are we doing this work

We want to be able to require/remove approvals when we are checking for pre-existing vulnerabilities. Some customers have configured their pipelines to run security scans in schedule pipelines or with scheduled scan execution policies. We want to give them opportunity to block MR that violates their Scan Result Policies based on pre-existing findings. This issue addresses this problem.

Relevant links

Non-functional requirements

  • Documentation:
  • Feature flag: Add feature flag to be able to enable/disable this behavior (default: false)
  • Performance:
  • Testing:
  • [ ]

Implementation plan

  • backend add new scope scope :without_newly_detected_vulnerability_state, -> { where.not("'newly_detected' = ANY (vulnerability_states)") } to /ee/app/models/concerns/approval_rule_like.rb to get approval rules without newly_detected state in vulnerability_states column,
  • backend in /ee/app/services/ci/sync_reports_to_approval_rules_service.rb add remove_required_approvals_for_scan_finding_with_preexising_findings method to remove required approvals for scan findings with pre-existing finding:
      def remove_required_approvals_for_scan_finding_with_preexising_findings(merge_requests)
        merge_requests.each do |merge_request|
          base_reports = merge_request.latest_pipeline_for_target_branch&.security_reports
          scan_finding_rules = merge_request.approval_rules.scan_finding.without_newly_detected_vulnerability_state
          selected_rules = scan_finding_rules.reject do |rule|
            violates_default_policy?(rule, base_reports)
          end
         scan_finding_rules.remove_required_approved(selected_rules)
        end
      end
  • backend in /ee/app/services/ci/sync_reports_to_approval_rules_service.rb update sync_scan_finding method to initiate remove_required_approvals_for_scan_finding_with_preexising_findings(pipeline.merge_requests_as_head_pipeline.opened) before checking if reports in pipeline are empty or if pipeline is completed,
  • backend in /ee/lib/ee/gitlab/ci/reports/security/reports.rb add preexisting_findings_count method to count findings that are preexisting on target branch:
            def preexisting_findings_count(target_reports, severity_levels, vulnerability_states, report_types)
              pipeline_uuids = target_reports&.unsafe_findings_uuids(severity_levels, report_types).to_a
              count_by_uuid(pipeline_uuids, vulnerability_states)
            end
  • backend in /ee/lib/ee/gitlab/ci/reports/security/reports.rb override violates_default_policy_against? method to include checking if pre-existing vulnerabilities are causing scan result policy to require approval:
            override :violates_default_policy_against?
            def violates_default_policy_against?(target_reports, vulnerabilities_allowed, severity_levels, vulnerability_states, report_types = [])
              return true if !vulnerability_states.include?(ApprovalProjectRule::NEWLY_DETECTED) && preexisting_findings_count(target_reports, severity_levels, vulnerability_states, report_types) > vulnerabilities_allowed
    
              super
            end
  • backend feature flag Add feature flag to be able to enable/disable this feature quickly,

Verification steps

  • Create new project
  • Add .gitlab-ci.yml file that includes detached pipeline:
      container_scanning:
        image: "busybox:latest"
        stage: test
        allow_failure: true
        artifacts:
          reports:
            container_scanning: gl-container-scanning-report.json
          paths: [gl-container-scanning-report.json]
        dependencies: []
        script:
          - wget -O gl-container-scanning-report.json https://gitlab.com/-/snippets/2438327/raw/main/gl-container-scanning-report-1-critical.json?inline=false
        rules:
          - if: $RUN_MANUALY
    
      other_job:
        image: busybox:latest
        stage: test
        script:
          - echo "This is other branch"
  • Add Scan Result Policy (go to Security & Compliance -> Policies), click New Policy, select Scan Result Policies and add new policy and merge created MR:
      name: Enforce check when there is at least 1 critical
      description: ''
      enabled: true
      actions:
      - type: require_approval
        approvals_required: 1
        group_approvers_ids:
        - # add your group ID
      rules:
      - type: scan_finding
        branches: []
        scanners: []
        vulnerabilities_allowed: 0
        severity_levels:
        - critical
        vulnerability_states:
        - detected
  • Run pipeline manually and set RUN_MANUALY variable to "true"
  • Add new MR with some change, unrelated to .gitlab-ci.yml file:
  • Verify if approvals in the MR are required
Edited by Alan (Maciej) Paruszewski