Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
9a1203dfe8
commit
3d680c141e
|
@ -499,6 +499,8 @@
|
||||||
- 1
|
- 1
|
||||||
- - upload_checksum
|
- - upload_checksum
|
||||||
- 1
|
- 1
|
||||||
|
- - vulnerabilities_mark_dropped_as_resolved
|
||||||
|
- 1
|
||||||
- - vulnerabilities_statistics_adjustment
|
- - vulnerabilities_statistics_adjustment
|
||||||
- 1
|
- 1
|
||||||
- - vulnerability_exports_export
|
- - vulnerability_exports_export
|
||||||
|
|
|
@ -434,10 +434,29 @@ The value of the `category` field matches the report type:
|
||||||
- `sast`
|
- `sast`
|
||||||
- `dast`
|
- `dast`
|
||||||
|
|
||||||
##### Scanner
|
##### Scan
|
||||||
|
|
||||||
The `scanner` field is an object that embeds a human-readable `name` and a technical `id`.
|
The `scan` field is an object that embeds meta information about the scan itself: the `analyzer`
|
||||||
The `id` should not collide with any other scanner another integrator would provide.
|
and `scanner` that performed the scan, the `start_time` and `end_time` the scan executed,
|
||||||
|
and `status` of the scan (either "success" or "failure").
|
||||||
|
|
||||||
|
Both the `analyzer` and `scanner` fields are objects that embeds a human-readable `name` and a technical `id`.
|
||||||
|
The `id` should not collide with any other analyzers or scanners another integrator would provide.
|
||||||
|
|
||||||
|
##### Scan Primary Identifiers
|
||||||
|
|
||||||
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368284) in GitLab 15.4 [with a flag](../../administration/feature_flags.md) named `sec_mark_dropped_findings_as_resolved`. Disabled by default.
|
||||||
|
|
||||||
|
The `scan.primary_identifiers` field is an optional field containing an array of
|
||||||
|
[primary identifiers](../../user/application_security/terminology/index.md#primary-identifier)).
|
||||||
|
This is an exhaustive list of all rulesets for which the analyzer performed the scan.
|
||||||
|
|
||||||
|
Even when the [`Vulnerabilities`](#vulnerabilities) array for a given scan may be empty, this optional field
|
||||||
|
should contain the complete list of potential identifiers in order to inform the Rails application of which
|
||||||
|
rules were executed.
|
||||||
|
|
||||||
|
When populated, the Rails application will automatically resolve previously detected vulnerabilities as no
|
||||||
|
longer relevant when their primary identifier is not included.
|
||||||
|
|
||||||
##### Name, message, and description
|
##### Name, message, and description
|
||||||
|
|
||||||
|
|
|
@ -200,7 +200,22 @@ module Gitlab
|
||||||
external_id: scanner_data['id'],
|
external_id: scanner_data['id'],
|
||||||
name: scanner_data['name'],
|
name: scanner_data['name'],
|
||||||
vendor: scanner_data.dig('vendor', 'name'),
|
vendor: scanner_data.dig('vendor', 'name'),
|
||||||
version: scanner_data.dig('version')))
|
version: scanner_data.dig('version'),
|
||||||
|
primary_identifiers: create_scan_primary_identifiers))
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: primary_identifiers should be initialized on the
|
||||||
|
# scan itself but we do not currently parse scans through `MergeReportsService`
|
||||||
|
def create_scan_primary_identifiers
|
||||||
|
return unless scan_data.is_a?(Hash) && scan_data.dig('primary_identifiers')
|
||||||
|
|
||||||
|
scan_data.dig('primary_identifiers').map do |identifier|
|
||||||
|
::Gitlab::Ci::Reports::Security::Identifier.new(
|
||||||
|
external_type: identifier['type'],
|
||||||
|
external_id: identifier['value'],
|
||||||
|
name: identifier['name'],
|
||||||
|
url: identifier['url'])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_identifiers(identifiers)
|
def create_identifiers(identifiers)
|
||||||
|
|
|
@ -69,6 +69,10 @@ module Gitlab
|
||||||
replace_with!(::Security::MergeReportsService.new(self, other).execute)
|
replace_with!(::Security::MergeReportsService.new(self, other).execute)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def primary_identifiers
|
||||||
|
scanners.values.flat_map(&:primary_identifiers).compact
|
||||||
|
end
|
||||||
|
|
||||||
def primary_scanner
|
def primary_scanner
|
||||||
scanners.first&.second
|
scanners.first&.second
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,15 +16,16 @@ module Gitlab
|
||||||
"semgrep" => 2
|
"semgrep" => 2
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
attr_accessor :external_id, :name, :vendor, :version
|
attr_accessor :external_id, :name, :vendor, :version, :primary_identifiers
|
||||||
|
|
||||||
alias_method :key, :external_id
|
alias_method :key, :external_id
|
||||||
|
|
||||||
def initialize(external_id:, name:, vendor:, version:)
|
def initialize(external_id:, name:, vendor:, version:, primary_identifiers: nil)
|
||||||
@external_id = external_id
|
@external_id = external_id
|
||||||
@name = name
|
@name = name
|
||||||
@vendor = vendor
|
@vendor = vendor
|
||||||
@version = version
|
@version = version
|
||||||
|
@primary_identifiers = primary_identifiers
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_hash
|
def to_hash
|
||||||
|
|
|
@ -352,6 +352,18 @@ FactoryBot.define do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Equivalent Semgrep report for combined :sast_bandit and :sast_gosec reports.
|
||||||
|
# This report includes signature tracking.
|
||||||
|
trait :sast_semgrep_for_multiple_findings do
|
||||||
|
file_type { :sast }
|
||||||
|
file_format { :raw }
|
||||||
|
|
||||||
|
after(:build) do |artifact, _|
|
||||||
|
artifact.file = fixture_file_upload(
|
||||||
|
Rails.root.join('spec/fixtures/security_reports/master/gl-sast-report-semgrep-for-multiple-findings.json'), 'application/json')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
trait :common_security_report do
|
trait :common_security_report do
|
||||||
file_format { :raw }
|
file_format { :raw }
|
||||||
file_type { :dependency_scanning }
|
file_type { :dependency_scanning }
|
||||||
|
|
|
@ -62,6 +62,13 @@
|
||||||
},
|
},
|
||||||
"version": "0.82.0"
|
"version": "0.82.0"
|
||||||
},
|
},
|
||||||
|
"primary_identifiers": [
|
||||||
|
{
|
||||||
|
"type": "semgrep_id",
|
||||||
|
"name": "gosec.G106-1",
|
||||||
|
"value": "gosec.G106-1"
|
||||||
|
}
|
||||||
|
],
|
||||||
"type": "sast",
|
"type": "sast",
|
||||||
"start_time": "2022-03-15T20:36:58",
|
"start_time": "2022-03-15T20:36:58",
|
||||||
"end_time": "2022-03-15T20:37:05",
|
"end_time": "2022-03-15T20:37:05",
|
||||||
|
|
134
spec/fixtures/security_reports/master/gl-sast-report-semgrep-for-multiple-findings.json
vendored
Normal file
134
spec/fixtures/security_reports/master/gl-sast-report-semgrep-for-multiple-findings.json
vendored
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
{
|
||||||
|
"version": "14.0.4",
|
||||||
|
"vulnerabilities": [
|
||||||
|
{
|
||||||
|
"id": "985a5666dcae22adef5ac12f8a8a2dacf9b9b481ae5d87cd0ac1712b0fd64864",
|
||||||
|
"category": "sast",
|
||||||
|
"message": "Deserialization of Untrusted Data",
|
||||||
|
"description": "Avoid using `load()`. `PyYAML.load` can create arbitrary Python\nobjects. A malicious actor could exploit this to run arbitrary\ncode. Use `safe_load()` instead.\n",
|
||||||
|
"cve": "",
|
||||||
|
"severity": "Critical",
|
||||||
|
"scanner": {
|
||||||
|
"id": "semgrep",
|
||||||
|
"name": "Semgrep"
|
||||||
|
},
|
||||||
|
"location": {
|
||||||
|
"file": "app/app.py",
|
||||||
|
"start_line": 39
|
||||||
|
},
|
||||||
|
"identifiers": [
|
||||||
|
{
|
||||||
|
"type": "semgrep_id",
|
||||||
|
"name": "bandit.B506",
|
||||||
|
"value": "bandit.B506",
|
||||||
|
"url": "https://semgrep.dev/r/gitlab.bandit.B506"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "cwe",
|
||||||
|
"name": "CWE-502",
|
||||||
|
"value": "502",
|
||||||
|
"url": "https://cwe.mitre.org/data/definitions/502.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "bandit_test_id",
|
||||||
|
"name": "Bandit Test ID B506",
|
||||||
|
"value": "B506"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tracking": {
|
||||||
|
"type": "source",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"file": "app/app.py",
|
||||||
|
"line_start": 39,
|
||||||
|
"line_end": 39,
|
||||||
|
"signatures": [
|
||||||
|
{
|
||||||
|
"algorithm": "scope_offset",
|
||||||
|
"value": "app/app.py|yaml_hammer[0]:13"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "79f6537b7ec83c7717f5bd1a4f12645916caafefe2e4359148d889855505aa67",
|
||||||
|
"category": "sast",
|
||||||
|
"message": "Key Exchange without Entity Authentication",
|
||||||
|
"description": "Audit the use of ssh.InsecureIgnoreHostKey\n",
|
||||||
|
"cve": "",
|
||||||
|
"severity": "Medium",
|
||||||
|
"scanner": {
|
||||||
|
"id": "semgrep",
|
||||||
|
"name": "Semgrep"
|
||||||
|
},
|
||||||
|
"location": {
|
||||||
|
"file": "og.go",
|
||||||
|
"start_line": 8
|
||||||
|
},
|
||||||
|
"identifiers": [
|
||||||
|
{
|
||||||
|
"type": "semgrep_id",
|
||||||
|
"name": "gosec.G106-1",
|
||||||
|
"value": "gosec.G106-1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "cwe",
|
||||||
|
"name": "CWE-322",
|
||||||
|
"value": "322",
|
||||||
|
"url": "https://cwe.mitre.org/data/definitions/322.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "gosec_rule_id",
|
||||||
|
"name": "Gosec Rule ID G106",
|
||||||
|
"value": "G106"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tracking": {
|
||||||
|
"type": "source",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"file": "og.go",
|
||||||
|
"line_start": 8,
|
||||||
|
"line_end": 8,
|
||||||
|
"signatures": [
|
||||||
|
{
|
||||||
|
"algorithm": "scope_offset",
|
||||||
|
"value": "og.go|foo[0]:1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"scan": {
|
||||||
|
"scanner": {
|
||||||
|
"id": "semgrep",
|
||||||
|
"name": "Semgrep",
|
||||||
|
"url": "https://github.com/returntocorp/semgrep",
|
||||||
|
"vendor": {
|
||||||
|
"name": "GitLab"
|
||||||
|
},
|
||||||
|
"version": "0.82.0"
|
||||||
|
},
|
||||||
|
"primary_identifiers": [
|
||||||
|
{
|
||||||
|
"type": "semgrep_id",
|
||||||
|
"name": "bandit.B506",
|
||||||
|
"value": "bandit.B506",
|
||||||
|
"url": "https://semgrep.dev/r/gitlab.bandit.B506"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "semgrep_id",
|
||||||
|
"name": "gosec.G106-1",
|
||||||
|
"value": "gosec.G106-1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "sast",
|
||||||
|
"start_time": "2022-03-15T20:36:58",
|
||||||
|
"end_time": "2022-03-15T20:37:05",
|
||||||
|
"status": "success"
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,9 +11,12 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Sast do
|
||||||
let(:created_at) { 2.weeks.ago }
|
let(:created_at) { 2.weeks.ago }
|
||||||
|
|
||||||
context "when passing valid report" do
|
context "when passing valid report" do
|
||||||
where(:report_format, :report_version, :scanner_length, :finding_length, :identifier_length, :file_path, :line) do
|
# rubocop: disable Layout/LineLength
|
||||||
:sast | '14.0.0' | 1 | 5 | 6 | 'groovy/src/main/java/com/gitlab/security_products/tests/App.groovy' | 47
|
where(:report_format, :report_version, :scanner_length, :finding_length, :identifier_length, :file_path, :start_line, :end_line, :primary_identifiers_length) do
|
||||||
|
:sast | '14.0.0' | 1 | 5 | 6 | 'groovy/src/main/java/com/gitlab/security_products/tests/App.groovy' | 47 | 47 | nil
|
||||||
|
:sast_semgrep_for_multiple_findings | '14.0.4' | 1 | 2 | 6 | 'app/app.py' | 39 | nil | 2
|
||||||
end
|
end
|
||||||
|
# rubocop: enable Layout/LineLength
|
||||||
|
|
||||||
with_them do
|
with_them do
|
||||||
let(:report) do
|
let(:report) do
|
||||||
|
@ -34,6 +37,12 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Sast do
|
||||||
expect(report.findings.length).to eq(finding_length)
|
expect(report.findings.length).to eq(finding_length)
|
||||||
expect(report.identifiers.length).to eq(identifier_length)
|
expect(report.identifiers.length).to eq(identifier_length)
|
||||||
expect(report.scanners.length).to eq(scanner_length)
|
expect(report.scanners.length).to eq(scanner_length)
|
||||||
|
|
||||||
|
if primary_identifiers_length
|
||||||
|
expect(
|
||||||
|
report.scanners.each_value.first.primary_identifiers.length
|
||||||
|
).to eq(primary_identifiers_length)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'generates expected location' do
|
it 'generates expected location' do
|
||||||
|
@ -42,8 +51,8 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Sast do
|
||||||
expect(location).to be_a(::Gitlab::Ci::Reports::Security::Locations::Sast)
|
expect(location).to be_a(::Gitlab::Ci::Reports::Security::Locations::Sast)
|
||||||
expect(location).to have_attributes(
|
expect(location).to have_attributes(
|
||||||
file_path: file_path,
|
file_path: file_path,
|
||||||
end_line: line,
|
end_line: end_line,
|
||||||
start_line: line
|
start_line: start_line
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -140,6 +140,24 @@ RSpec.describe Gitlab::Ci::Reports::Security::Report do
|
||||||
it { is_expected.to eq(scanner_1) }
|
it { is_expected.to eq(scanner_1) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#primary_identifiers' do
|
||||||
|
it 'returns matching identifiers' do
|
||||||
|
scanner_with_identifiers = create(
|
||||||
|
:ci_reports_security_scanner,
|
||||||
|
external_id: 'external_id_1',
|
||||||
|
primary_identifiers: [create(:ci_reports_security_identifier, external_id: 'other_id', name: 'other_scanner')]
|
||||||
|
)
|
||||||
|
scanner_without_identifiers = create(
|
||||||
|
:ci_reports_security_scanner,
|
||||||
|
external_id: 'external_id_2')
|
||||||
|
|
||||||
|
report.add_scanner(scanner_with_identifiers)
|
||||||
|
report.add_scanner(scanner_without_identifiers)
|
||||||
|
|
||||||
|
expect(report.primary_identifiers).to eq(scanner_with_identifiers.primary_identifiers)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe '#add_error' do
|
describe '#add_error' do
|
||||||
context 'when the message is not given' do
|
context 'when the message is not given' do
|
||||||
it 'adds a new error to report with the generic error message' do
|
it 'adds a new error to report with the generic error message' do
|
||||||
|
|
Loading…
Reference in New Issue