Scan result policy blocking MR when vulnerabilities do not match the fix_available criteria defined in the policy

Summary

With testing newly introduced scan result policy filters to support age based filtering of detected vulnerabilities, I've noticed some unexpected behavior. An MR is being blocked when it should not be.

Steps to reproduce

  1. I started by ensuring I could implement dependency scanning and detect results using a typical policy that blocks MRs based on the severity match (Critical, High, Medium). This I used as a baseline to ensure I could produce a High and Medium finding based on this guide. It took some time to properly produce the results, so days passed to have enough time to get this test in place, during which it's possible these findings were detected on some non-default branches.

  2. Next, I created policies using the age filter:

Policy yaml
type: scan_result_policy
name: Block DS Vulnerabilities (Reduced Noise)
description: >-
  - Block MRs with Critical severity vulns

  - Set an SLA of 30 days for High severity vulns

  - Set an SLA of 60 days for Medium severity vulns

  - Ignore false positives and dependency scanning findings with no fix
  available
enabled: true
rules:
  - type: scan_finding
    scanners:
      - dependency_scanning
    vulnerabilities_allowed: 0
    severity_levels:
      - critical
    vulnerability_states: []
    branch_type: default
    vulnerability_attributes:
      false_positive: false
      fix_available: false
  - type: scan_finding
    scanners:
      - dependency_scanning
    vulnerabilities_allowed: 0
    severity_levels:
      - high
    vulnerability_states:
      - detected
      - confirmed
      - dismissed
      - resolved
    branch_type: default
    vulnerability_age:
      operator: greater_than
      value: 1
      interval: day
    vulnerability_attributes:
      fix_available: false
      false_positive: false
  - type: scan_finding
    scanners:
      - dependency_scanning
    vulnerabilities_allowed: 0
    severity_levels:
      - medium
    vulnerability_states:
      - detected
      - confirmed
      - dismissed
      - resolved
    branch_type: default
    vulnerability_age:
      operator: greater_than
      value: 2
      interval: day
    vulnerability_attributes:
      fix_available: false
      false_positive: false
actions:
  - type: require_approval
    approvals_required: 1
    role_approvers:
      - developer
      - maintainer
      - owner
  1. In my scenario, I first set the policy to 30 days for High severity and 60 days for Medium. The results in the MR were as expected - a High and Medium severity finding were detected but approval was NOT required.

image

  1. I then narrowed the criteria down to 1 and 2 days respectively so I could demo this more quickly and observe the policy behaving properly. However, when I reran the job on the MR, it completed and showed a policy violation, that one of these findings must have a detected date greater than 1 or 2 days. This is not expected as I have yet to merge these changes into the default branch in any experiments. It's possible that these findings have been detected on a non-default branch more than 1 or 2 days prior to the day I tested and observed this behavior. However, the Vulnerability report does not show the detected CVEs as appearing on the default branch. We may want to clarify what exactly the age criteria is based on. Below is the current Vuln Report:

If we are setting the age SLA based on the datetime at which the vulnerability is first Detected on the default branch (which is expected), I would also expect this vuln to appear in the Vulnerability Report 🤔"

  1. Note that I enforce Dependency Scanning to run using an SEP triggered on * branches.

Example Project

MR: https://gitlab.com/haven-group/development-projects/project-a/-/merge_requests/19#53585a096fe3a3b96321113856ae62602995a33b

What is the current bug behavior?

Described above.

What is the expected correct behavior?

The MR should not be blocked unless we can see a vulnerability appearing on the default/target branch and the detected date is greater than the specified policy vulnerability age.

Relevant logs and/or screenshots

Output of checks

Results of GitLab environment info

Expand for output related to GitLab environment info
(For installations with omnibus-gitlab package run and paste the output of:
`sudo gitlab-rake gitlab:env:info`)

(For installations from source run and paste the output of:
`sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)

Results of GitLab application Check

Expand for output related to the GitLab application check

(For installations with omnibus-gitlab package run and paste the output of: sudo gitlab-rake gitlab:check SANITIZE=true)

(For installations from source run and paste the output of: sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true)

(we will only investigate if the tests are passing)

Implementation Plan

  • backend Update Security::Finding fix_available and no_fix_available scopes to consider finding_data->solution
    scope :fix_available, -> do
      where(
        "jsonb_array_length(finding_data -> 'remediation_byte_offsets')::bigint > 0
        OR finding_data -> 'solution' IS NOT NULL"
      )
    end

    scope :no_fix_available, -> do
      where(
        "finding_data -> 'remediation_byte_offsets' IS NULL
        OR jsonb_array_length(finding_data -> 'remediation_byte_offsets')::bigint <= 0
        OR finding_data -> 'solution' IS NULL"
      )
    end

Verification Steps

  • Create a project with container scanning on a vulnerable image. Eg .gitlab-ci.yml:
include:
  - template: Security/Container-Scanning.gitlab-ci.yml

container_scanning:
  variables:
    CS_IMAGE: 'citizenstig/dvwa:latest'
  • Create a scan result policy to require approval when a fix is available
type: scan_result_policy
name: Fix Available
description: ''
enabled: true
rules:
  - type: scan_finding
    scanners: []
    vulnerabilities_allowed: 0
    severity_levels: []
    vulnerability_states:
      - detected
      - confirmed
      - dismissed
      - resolved
    branch_type: protected
    vulnerability_attributes:
      fix_available: true
actions:
  - type: require_approval
    approvals_required: 1
    group_approvers_ids:
      - <>
  • Create a MR that updates README and verify that the approval is required
Edited by Sashi Kumar Kumaresan