Update security-triage-automation tool to close linked issues for resolved vulnerabilities

Proposal

When a vulnerability is no longer detected in a project, the status of the vulnerability finding remains unchanged, and any linked issues remain open. To clean this up, we periodically run the security-triage-automation (sta) tool to resolve-and-close all no-longer-detected vulnerabilities.

This sta tool loops through all vulnerabilities for a project whose state is CONFIRMED or DETECTED and:

This process worked fine until the introduction of the Auto resolve vulnerabilities security policy 3 months ago, which executes as the GitLab Security Policy Bot user and periodically loops through all no-longer-detected vulnerabilities and automatically resolves the vulnerability and changes its state to RESOLVED, but does not close linked issues.

Since the sta tool currently only resolves and closes CONFIRMED,DETECTED vulnerabilities, it now always ends up processing 0 vulnerabilities because the GitLab Security Policy Bot has already changed the state of all the vulnerabilities to RESOLVED. However, as mentioned previously, the GitLab Security Policy Bot does not close linked issues, which means we have a large number of vulnerability issues that remain open, as discussed here.

The purpose of this issue is to create an automated process for closing these linked vulnerability issues.

Possible solutions

The best solution to this issue would be to update the GitLab Security Policy Bot to close linked issues when it resolves a vulnerability, however, this is the most complicated approach and the issue tracking this work has yet to be started: Provide option for closing linked issues to Vul... (#545683) • Unassigned • Backlog.

An easier approach would be to implement the following items:

  1. Update the sta tool to modify the conditional that skips resolved vulnerabilities.

    If a vulnerability has been resolved, instead of immediately returning, we want to close the linked issues.

  2. Update the way the sta tool is invoked so instead of only operating on vulnerabilities whose states are CONFIRMED,DETECTED, we operate on vulnerabilities whose state is RESOLVED.

    However, there's a gotcha here. Because there's a massive number of RESOLVED vulnerabilities, processing all of them takes an hour for gemnasium:

    $ git clone git@gitlab.com:gitlab-org/secure/tools/security-triage-automation.git && cd security-triage-automation
    $ ./main.rb --resolve-and-close --dry-run --custom-labels="group::composition analysis,Category:Software Composition Analysis" --project-path=gitlab-org/security-products/analyzers/gemnasium --log-level=debug --states=RESOLVED
    
    2025-05-21 18:49:06.742310 I Automator -- Notice: Dry run enabled, performing a trial run with no changes made.
    2025-05-21 18:49:06.742335 I Automator -- Resolving all no-longer-found vulnerabilities updated on or after 1975-05-21 and closing their linked issues.
    2025-05-21 18:49:06.742341 I Automator -- States: ["RESOLVED"]
    <snip>
    2025-05-21 19:48:03.437604 I Automator -- Number of records processed: 5331   

    The majority of the time is spent fetching NVD data because we're forced to wait 0.6 seconds between each call to the NVD API, to avoid being throttled.

    Solution 1

    We need to fetch NVD data when creating vulnerability issues, however, it's not necessary when simply resolving and closing issues, so we can decrease our processing time substantially by removing the call to fetch NVD data in this case.

    This will cut the processing time down from 1 hour to 8 minutes:

    2025-05-22 10:57:32.351837 I Automator -- Notice: Dry run enabled, performing a trial run with no changes made.
    <snip>
    2025-05-22 11:05:11.324341 I Automator -- Number of records processed: 5331

    This is the approach we'll implement.

    Solution 2

    Another solution is to add an --after=DATE flag that only processes vulnerabilities whose updated_at date is after this date. In other words, it'll stop processing vulnerabilities as soon as it encounters one whose updated_at date is before the --after date.

    For example, if we pass --after=2024-11-22, and a vulnerability is encountered with an updated_date equal to 2024-10-31, we'll stop processing at that point, because it's older than the provided --after date 2024-11-22.

    This works because this problem only started happening 3 months ago, so we can limit the number of vulnerabilities we need to process by using this flag. This will also avoid needing to fetch every vulnerability using the GraphQL endpoint, thereby reducing resources.

    This is the approach we'll implement. see this explanation to understand why we can't implement solution 2.

Implementation Plan

Implement Solution 1 above.

  1. Update the sta tool to modify the conditional that skips resolved vulnerabilities.

    If a vulnerability has been resolved, instead of immediately returning, we want to close the linked issues.

  2. Remove the call to instantiate an NVD object Vulnerability.new is called from Vulnerability.resolve_and_close!

  3. Bump the version of the sta tool in the CHANGELOG.md and release a new version.

  4. Bump the version of the sta tool in the release project to point to the version released in step 3 above.

    We also need to update the way the sta tool is invoked so instead of only operating on vulnerabilities whose states are CONFIRMED,DETECTED, we operate on vulnerabilities whose state is RESOLVED, to ensure we process and close all the issues that the GitLab Security Policy Bot left open.

Edited by Adam Cohen