doc/user/application_security/detect/vulnerability_deduplication.md
{{< details >}}
{{< /details >}}
When a pipeline contains jobs that produce multiple security reports of the same type, it is possible that the same vulnerability is present in multiple reports. This duplication is common when different scanners are used to increase coverage, but can also exist in a single report. Vulnerability deduplication automatically consolidates duplicate vulnerabilities across scans, helping you focus on unique vulnerabilities while maintaining full scanning coverage.
The logic for deduplicating vulnerabilities varies depending on the scan type:
The scan type must match because each can have its own definition for the location of a vulnerability. For example, static analyzers are able to locate a file path and line number, whereas a container scanning analyzer uses the image name instead.
When comparing identifiers, GitLab does not compare CWE and WASC during deduplication because
they are "type identifiers" and are used to classify groups of vulnerabilities. Including these
identifiers would result in many vulnerabilities being incorrectly considered duplicates. Two vulnerabilities are
considered unique if none of their identifiers match.
In a set of duplicated vulnerabilities, the first occurrence of a vulnerability is kept and the remaining are skipped. Security reports are processed in alphabetical file path order, and vulnerabilities are processed sequentially in the order they appear in a report.
For scan types that can run more than one GitLab analyzer, such as SAST, scanner priority also determines which vulnerability is kept. When two GitLab analyzers detect the same vulnerability, the result from the higher-priority analyzer is kept. This is how GitLab maintains continuity when one analyzer replaces another. For example, results from the Gemnasium analyzer take priority over results from a deprecated analyzer that covered the same dependencies.
GitLab deduplicates vulnerabilities at more than one stage:
GitLab never deduplicates vulnerabilities across different scan types. For more information, see scan type.
Deduplication and cross-pipeline tracking depend on the primary identifier remaining stable across scans. The primary identifier is the first entry in a vulnerability's identifiers.
If a scanner reports a different primary identifier for the same vulnerability in a later run, GitLab cannot match the new result to the existing one. The existing vulnerability is marked as no longer detected, and the new result is recorded as a separate vulnerability.
Third-party scanners should use a stable rule key as the primary identifier, such as a SonarQube rule key. A primary identifier that changes between runs, such as a value generated for each scan, breaks tracking and produces duplicate vulnerabilities.
The location used for deduplication is dependent on the scan type.
registry.gitlab.com/group-name/project-name/image1:12345019:libcrypto3registry.gitlab.com/group-name/project-name/image1:libcrypto3registry.gitlab.com/group-name/project-name/image1:v19202021:libcrypto3registry.gitlab.com/group-name/project-name/image1:libcrypto3When security scanners analyze your code, they sometimes report the same vulnerability multiple times, especially when code is refactored or moved around. Advanced vulnerability tracking uses a smart deduplication system to recognize when these are actually the same issue, not new ones.
Imagine you have a security issue in a function. If a developer refactors the code and moves that function to a different line, the scanner might report it as a new vulnerability. Without deduplication, you'd see duplicate alerts for the same problem, making it harder to track what you actually need to fix.
When using scope-offset signatures, GitLab creates a unique "fingerprint" for each vulnerability using the following information:
This combination creates a signature that stays the same even when code moves around, as long as it stays within the same scope.
Scope-offset tracking applies to GitLab SAST analyzers, which include the required tracking data in their reports. This tracking data is specific to the GitLab report format, so third-party SARIF uploads cannot provide it. Third-party SARIF uploads instead use a location fingerprint based on the file path and line numbers. With this fingerprint, a vulnerability can be recorded as a new finding when unrelated code changes shift its line numbers.
Say you have this Ruby code:
class OuterClass
class InnerClassA
def function_A(x)
puts "calling call1"
call1(x) # ← Vulnerability found here on line 5
end
call2("calling call 2")
end
end
The scanner finds a vulnerability on line 5. GitLab needs to figure out whether the vulnerability is in OuterClass, InnerClassA, or function_A?
The scanner calculates which scope is the best fit by measuring the distance from the vulnerability to the beginning and to the end of each scope:
OuterClass (lines 1-9): Distance = (5-1) + (9-5) = 8InnerClassA (lines 2-8): Distance = (5-2) + (8-5) = 6function_A (lines 3-6): Distance = (5-3) + (6-5) = 3The smallest distance wins, so GitLab identifies function_A as the scope.
GitLab creates a signature like lib/outer_class.rb|OuterClass[0]|InnerClassA[0]|function_A[0]:2
to identify the location of the vulnerability. If the function or class that contains the vulnerability is moved
to a different location within its parent scope, the vulnerability will not be reintroduced.
However, if OuterClass is renamed the scope is different and a new vulnerability is created.
Here are some examples of how vulnerability deduplication behaves.
dependency_scanningadc83b19e793491b1c6ea0fd8b46cd9f32e592fccontainer_scanningadc83b19e793491b1c6ea0fd8b46cd9f32e592fcsastadc83b19e793491b1c6ea0fd8b46cd9f32e592fcsastadc83b19e793491b1c6ea0fd8b46cd9f32e592fcCWE identifiers are ignored.container_scanningadc83b19e793491b1c6ea0fd8b46cd9f32e592fccontainer_scanningadc83b19e793491b1c6ea0fd8b46cd9f32e592fc