summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/ci/parsers
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-08-18 08:17:02 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-08-18 08:17:02 +0000
commitb39512ed755239198a9c294b6a45e65c05900235 (patch)
treed234a3efade1de67c46b9e5a38ce813627726aa7 /spec/lib/gitlab/ci/parsers
parentd31474cf3b17ece37939d20082b07f6657cc79a9 (diff)
downloadgitlab-ce-b39512ed755239198a9c294b6a45e65c05900235.tar.gz
Add latest changes from gitlab-org/gitlab@15-3-stable-eev15.3.0-rc42
Diffstat (limited to 'spec/lib/gitlab/ci/parsers')
-rw-r--r--spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb88
-rw-r--r--spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb135
-rw-r--r--spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb40
-rw-r--r--spec/lib/gitlab/ci/parsers/sbom/validators/cyclonedx_schema_validator_spec.rb132
-rw-r--r--spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb599
5 files changed, 633 insertions, 361 deletions
diff --git a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb
new file mode 100644
index 00000000000..c99cfa94aa6
--- /dev/null
+++ b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Parsers::Sbom::CyclonedxProperties do
+ subject(:parse_source) { described_class.parse_source(properties) }
+
+ context 'when properties are nil' do
+ let(:properties) { nil }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when report does not have gitlab properties' do
+ let(:properties) { ['name' => 'foo', 'value' => 'bar'] }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when schema_version is missing' do
+ let(:properties) do
+ [
+ { 'name' => 'gitlab:dependency_scanning:dependency_file', 'value' => 'package-lock.json' },
+ { 'name' => 'gitlab:dependency_scanning:package_manager_name', 'value' => 'npm' },
+ { 'name' => 'gitlab:dependency_scanning:language', 'value' => 'JavaScript' }
+ ]
+ end
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when schema version is unsupported' do
+ let(:properties) do
+ [
+ { 'name' => 'gitlab:meta:schema_version', 'value' => '2' },
+ { 'name' => 'gitlab:dependency_scanning:dependency_file', 'value' => 'package-lock.json' },
+ { 'name' => 'gitlab:dependency_scanning:package_manager_name', 'value' => 'npm' },
+ { 'name' => 'gitlab:dependency_scanning:language', 'value' => 'JavaScript' }
+ ]
+ end
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when no dependency_scanning properties are present' do
+ let(:properties) do
+ [
+ { 'name' => 'gitlab:meta:schema_version', 'value' => '1' }
+ ]
+ end
+
+ it 'does not call dependency_scanning parser' do
+ expect(Gitlab::Ci::Parsers::Sbom::Source::DependencyScanning).not_to receive(:parse_source)
+
+ parse_source
+ end
+ end
+
+ context 'when dependency_scanning properties are present' do
+ let(:properties) do
+ [
+ { 'name' => 'gitlab:meta:schema_version', 'value' => '1' },
+ { 'name' => 'gitlab:dependency_scanning:category', 'value' => 'development' },
+ { 'name' => 'gitlab:dependency_scanning:input_file:path', 'value' => 'package-lock.json' },
+ { 'name' => 'gitlab:dependency_scanning:source_file:path', 'value' => 'package.json' },
+ { 'name' => 'gitlab:dependency_scanning:package_manager:name', 'value' => 'npm' },
+ { 'name' => 'gitlab:dependency_scanning:language:name', 'value' => 'JavaScript' },
+ { 'name' => 'gitlab:dependency_scanning:unsupported_property', 'value' => 'Should be ignored' }
+ ]
+ end
+
+ let(:expected_input) do
+ {
+ 'category' => 'development',
+ 'input_file' => { 'path' => 'package-lock.json' },
+ 'source_file' => { 'path' => 'package.json' },
+ 'package_manager' => { 'name' => 'npm' },
+ 'language' => { 'name' => 'JavaScript' }
+ }
+ end
+
+ it 'passes only supported properties to the dependency scanning parser' do
+ expect(Gitlab::Ci::Parsers::Sbom::Source::DependencyScanning).to receive(:source).with(expected_input)
+
+ parse_source
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb
new file mode 100644
index 00000000000..431fe9f3591
--- /dev/null
+++ b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb
@@ -0,0 +1,135 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Parsers::Sbom::Cyclonedx do
+ let(:report) { instance_double('Gitlab::Ci::Reports::Sbom::Report') }
+ let(:report_data) { base_report_data }
+ let(:raw_report_data) { report_data.to_json }
+ let(:report_valid?) { true }
+ let(:validator_errors) { [] }
+ let(:properties_parser) { class_double('Gitlab::Ci::Parsers::Sbom::CyclonedxProperties') }
+
+ let(:base_report_data) do
+ {
+ 'bomFormat' => 'CycloneDX',
+ 'specVersion' => '1.4',
+ 'version' => 1
+ }
+ end
+
+ subject(:parse!) { described_class.new.parse!(raw_report_data, report) }
+
+ before do
+ allow_next_instance_of(Gitlab::Ci::Parsers::Sbom::Validators::CyclonedxSchemaValidator) do |validator|
+ allow(validator).to receive(:valid?).and_return(report_valid?)
+ allow(validator).to receive(:errors).and_return(validator_errors)
+ end
+
+ allow(properties_parser).to receive(:parse_source)
+ stub_const('Gitlab::Ci::Parsers::Sbom::CyclonedxProperties', properties_parser)
+ end
+
+ context 'when report JSON is invalid' do
+ let(:raw_report_data) { '{ ' }
+
+ it 'handles errors and adds them to the report' do
+ expect(report).to receive(:add_error).with(a_string_including("Report JSON is invalid:"))
+
+ expect { parse! }.not_to raise_error
+ end
+ end
+
+ context 'when report uses an unsupported spec version' do
+ let(:report_data) { base_report_data.merge({ 'specVersion' => '1.3' }) }
+
+ it 'reports unsupported version as an error' do
+ expect(report).to receive(:add_error).with("Unsupported CycloneDX spec version. Must be one of: 1.4")
+
+ parse!
+ end
+ end
+
+ context 'when report does not conform to the CycloneDX schema' do
+ let(:report_valid?) { false }
+ let(:validator_errors) { %w[error1 error2] }
+
+ it 'reports all errors returned by the validator' do
+ expect(report).to receive(:add_error).with("error1")
+ expect(report).to receive(:add_error).with("error2")
+
+ parse!
+ end
+ end
+
+ context 'when cyclonedx report has no components' do
+ it 'skips component processing' do
+ expect(report).not_to receive(:add_component)
+
+ parse!
+ end
+ end
+
+ context 'when report has components' do
+ let(:report_data) { base_report_data.merge({ 'components' => components }) }
+ let(:components) do
+ [
+ {
+ "name" => "activesupport",
+ "version" => "5.1.4",
+ "purl" => "pkg:gem/activesupport@5.1.4",
+ "type" => "library",
+ "bom-ref" => "pkg:gem/activesupport@5.1.4"
+ },
+ {
+ "name" => "byebug",
+ "version" => "10.0.0",
+ "purl" => "pkg:gem/byebug@10.0.0",
+ "type" => "library",
+ "bom-ref" => "pkg:gem/byebug@10.0.0"
+ },
+ {
+ "name" => "minimal-component",
+ "type" => "library"
+ },
+ {
+ # Should be skipped
+ "name" => "unrecognized-type",
+ "type" => "unknown"
+ }
+ ]
+ end
+
+ it 'adds each component, ignoring unused attributes' do
+ expect(report).to receive(:add_component)
+ .with({ "name" => "activesupport", "version" => "5.1.4", "type" => "library" })
+ expect(report).to receive(:add_component)
+ .with({ "name" => "byebug", "version" => "10.0.0", "type" => "library" })
+ expect(report).to receive(:add_component)
+ .with({ "name" => "minimal-component", "type" => "library" })
+
+ parse!
+ end
+ end
+
+ context 'when report has metadata properties' do
+ let(:report_data) { base_report_data.merge({ 'metadata' => { 'properties' => properties } }) }
+
+ let(:properties) do
+ [
+ { 'name' => 'gitlab:meta:schema_version', 'value' => '1' },
+ { 'name' => 'gitlab:dependency_scanning:category', 'value' => 'development' },
+ { 'name' => 'gitlab:dependency_scanning:input_file:path', 'value' => 'package-lock.json' },
+ { 'name' => 'gitlab:dependency_scanning:source_file:path', 'value' => 'package.json' },
+ { 'name' => 'gitlab:dependency_scanning:package_manager:name', 'value' => 'npm' },
+ { 'name' => 'gitlab:dependency_scanning:language:name', 'value' => 'JavaScript' }
+ ]
+ end
+
+ it 'passes them to the properties parser' do
+ expect(properties_parser).to receive(:parse_source).with(properties)
+
+ parse!
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb
new file mode 100644
index 00000000000..30114b17cac
--- /dev/null
+++ b/spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Parsers::Sbom::Source::DependencyScanning do
+ subject { described_class.source(property_data) }
+
+ context 'when all property data is present' do
+ let(:property_data) do
+ {
+ 'category' => 'development',
+ 'input_file' => { 'path' => 'package-lock.json' },
+ 'source_file' => { 'path' => 'package.json' },
+ 'package_manager' => { 'name' => 'npm' },
+ 'language' => { 'name' => 'JavaScript' }
+ }
+ end
+
+ it 'returns expected source data' do
+ is_expected.to eq({
+ 'type' => :dependency_scanning,
+ 'data' => property_data,
+ 'fingerprint' => '4dbcb747e6f0fb3ed4f48d96b777f1d64acdf43e459fdfefad404e55c004a188'
+ })
+ end
+ end
+
+ context 'when required properties are missing' do
+ let(:property_data) do
+ {
+ 'category' => 'development',
+ 'source_file' => { 'path' => 'package.json' },
+ 'package_manager' => { 'name' => 'npm' },
+ 'language' => { 'name' => 'JavaScript' }
+ }
+ end
+
+ it { is_expected.to be_nil }
+ end
+end
diff --git a/spec/lib/gitlab/ci/parsers/sbom/validators/cyclonedx_schema_validator_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/validators/cyclonedx_schema_validator_spec.rb
new file mode 100644
index 00000000000..c54a3268bbe
--- /dev/null
+++ b/spec/lib/gitlab/ci/parsers/sbom/validators/cyclonedx_schema_validator_spec.rb
@@ -0,0 +1,132 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe Gitlab::Ci::Parsers::Sbom::Validators::CyclonedxSchemaValidator do
+ # Reports should be valid or invalid according to the specification at
+ # https://cyclonedx.org/docs/1.4/json/
+
+ subject(:validator) { described_class.new(report_data) }
+
+ let_it_be(:required_attributes) do
+ {
+ "bomFormat" => "CycloneDX",
+ "specVersion" => "1.4",
+ "version" => 1
+ }
+ end
+
+ context "with minimally valid report" do
+ let_it_be(:report_data) { required_attributes }
+
+ it { is_expected.to be_valid }
+ end
+
+ context "when report has components" do
+ let(:report_data) { required_attributes.merge({ "components" => components }) }
+
+ context "with minimally valid components" do
+ let(:components) do
+ [
+ {
+ "type" => "library",
+ "name" => "activesupport"
+ },
+ {
+ "type" => "library",
+ "name" => "byebug"
+ }
+ ]
+ end
+
+ it { is_expected.to be_valid }
+ end
+
+ context "when components have versions" do
+ let(:components) do
+ [
+ {
+ "type" => "library",
+ "name" => "activesupport",
+ "version" => "5.1.4"
+ },
+ {
+ "type" => "library",
+ "name" => "byebug",
+ "version" => "10.0.0"
+ }
+ ]
+ end
+
+ it { is_expected.to be_valid }
+ end
+
+ context "when components are not valid" do
+ let(:components) do
+ [
+ { "type" => "foo" },
+ { "name" => "activesupport" }
+ ]
+ end
+
+ it { is_expected.not_to be_valid }
+
+ it "outputs errors for each validation failure" do
+ expect(validator.errors).to match_array([
+ "property '/components/0' is missing required keys: name",
+ "property '/components/0/type' is not one of: [\"application\", \"framework\"," \
+ " \"library\", \"container\", \"operating-system\", \"device\", \"firmware\", \"file\"]",
+ "property '/components/1' is missing required keys: type"
+ ])
+ end
+ end
+ end
+
+ context "when report has metadata" do
+ let(:metadata) do
+ {
+ "timestamp" => "2022-02-23T08:02:39Z",
+ "tools" => [{ "vendor" => "GitLab", "name" => "Gemnasium", "version" => "2.34.0" }],
+ "authors" => [{ "name" => "GitLab", "email" => "support@gitlab.com" }]
+ }
+ end
+
+ let(:report_data) { required_attributes.merge({ "metadata" => metadata }) }
+
+ it { is_expected.to be_valid }
+
+ context "when metadata has properties" do
+ before do
+ metadata.merge!({ "properties" => properties })
+ end
+
+ context "when properties are valid" do
+ let(:properties) do
+ [
+ { "name" => "gitlab:dependency_scanning:input_file", "value" => "Gemfile.lock" },
+ { "name" => "gitlab:dependency_scanning:package_manager", "value" => "bundler" }
+ ]
+ end
+
+ it { is_expected.to be_valid }
+ end
+
+ context "when properties are invalid" do
+ let(:properties) do
+ [
+ { "name" => ["gitlab:meta:schema_version"], "value" => 1 }
+ ]
+ end
+
+ it { is_expected.not_to be_valid }
+
+ it "outputs errors for each validation failure" do
+ expect(validator.errors).to match_array([
+ "property '/metadata/properties/0/name' is not of type: string",
+ "property '/metadata/properties/0/value' is not of type: string"
+ ])
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb b/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb
index d06077d69b6..7828aa99f6a 100644
--- a/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb
@@ -6,6 +6,10 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let_it_be(:project) { create(:project) }
let(:supported_dast_versions) { described_class::SUPPORTED_VERSIONS[:dast].join(', ') }
+ let(:deprecated_schema_version_message) {}
+ let(:missing_schema_version_message) do
+ "Report version not provided, dast report type supports versions: #{supported_dast_versions}"
+ end
let(:scanner) do
{
@@ -24,7 +28,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
expect(described_class::SUPPORTED_VERSIONS.keys).to eq(described_class::DEPRECATED_VERSIONS.keys)
end
- context 'when a schema JSON file exists for a particular report type version' do
+ context 'when all files under schema path are explicitly listed' do
# We only care about the part that comes before report-format.json
# https://rubular.com/r/N8Juz7r8hYDYgD
filename_regex = /(?<report_type>[-\w]*)\-report-format.json/
@@ -38,7 +42,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
matches = filename_regex.match(file)
report_type = matches[:report_type].tr("-", "_").to_sym
- it "#{report_type} #{version} is in the constant" do
+ it "#{report_type} #{version}" do
expect(described_class::SUPPORTED_VERSIONS[report_type]).to include(version)
end
end
@@ -64,11 +68,54 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
describe '#valid?' do
subject { validator.valid? }
+ context 'when given a supported MAJOR.MINOR schema version' do
+ let(:report_type) { :dast }
+ let(:report_version) do
+ latest_vendored_version = described_class::SUPPORTED_VERSIONS[report_type].last.split(".")
+ (latest_vendored_version[0...2] << "34").join(".")
+ end
+
+ context 'and the report is valid' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'and the report is invalid' do
+ let(:report_data) do
+ {
+ 'version' => report_version
+ }
+ end
+
+ it { is_expected.to be_falsey }
+
+ it 'logs related information' do
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ message: "security report schema validation problem",
+ security_report_type: report_type,
+ security_report_version: report_version,
+ project_id: project.id,
+ security_report_failure: 'schema_validation_fails',
+ security_report_scanner_id: 'gemnasium',
+ security_report_scanner_version: '2.1.0'
+ )
+
+ subject
+ end
+ end
+ end
+
context 'when given a supported schema version' do
let(:report_type) { :dast }
let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
- context 'when the report is valid' do
+ context 'and the report is valid' do
let(:report_data) do
{
'version' => report_version,
@@ -79,7 +126,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
it { is_expected.to be_truthy }
end
- context 'when the report is invalid' do
+ context 'and the report is invalid' do
let(:report_data) do
{
'version' => report_version
@@ -118,7 +165,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
stub_const("#{described_class}::DEPRECATED_VERSIONS", deprecations_hash)
end
- context 'when the report passes schema validation' do
+ context 'and the report passes schema validation' do
let(:report_data) do
{
'version' => '10.0.0',
@@ -143,34 +190,14 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
end
end
- context 'when the report does not pass schema validation' do
- context 'when enforce_security_report_validation is enabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: true)
- end
-
- let(:report_data) do
- {
- 'version' => 'V2.7.0'
- }
- end
-
- it { is_expected.to be_falsey }
+ context 'and the report does not pass schema validation' do
+ let(:report_data) do
+ {
+ 'version' => 'V2.7.0'
+ }
end
- context 'when enforce_security_report_validation is disabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: false)
- end
-
- let(:report_data) do
- {
- 'version' => 'V2.7.0'
- }
- end
-
- it { is_expected.to be_truthy }
- end
+ it { is_expected.to be_falsey }
end
end
@@ -178,20 +205,40 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:report_type) { :dast }
let(:report_version) { "12.37.0" }
- context 'when enforce_security_report_validation is enabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: true)
+ context 'and the report is valid' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
end
- context 'when the report is valid' do
- let(:report_data) do
- {
- 'version' => report_version,
- 'vulnerabilities' => []
- }
- end
+ it { is_expected.to be_falsey }
+
+ it 'logs related information' do
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ message: "security report schema validation problem",
+ security_report_type: report_type,
+ security_report_version: report_version,
+ project_id: project.id,
+ security_report_failure: 'using_unsupported_schema_version',
+ security_report_scanner_id: 'gemnasium',
+ security_report_scanner_version: '2.1.0'
+ )
- it { is_expected.to be_falsey }
+ subject
+ end
+ end
+
+ context 'and the report is invalid' do
+ let(:report_data) do
+ {
+ 'version' => report_version
+ }
+ end
+
+ context 'and scanner information is empty' do
+ let(:scanner) { {} }
it 'logs related information' do
expect(Gitlab::AppLogger).to receive(:info).with(
@@ -199,79 +246,26 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
security_report_type: report_type,
security_report_version: report_version,
project_id: project.id,
+ security_report_failure: 'schema_validation_fails',
+ security_report_scanner_id: nil,
+ security_report_scanner_version: nil
+ )
+
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ message: "security report schema validation problem",
+ security_report_type: report_type,
+ security_report_version: report_version,
+ project_id: project.id,
security_report_failure: 'using_unsupported_schema_version',
- security_report_scanner_id: 'gemnasium',
- security_report_scanner_version: '2.1.0'
+ security_report_scanner_id: nil,
+ security_report_scanner_version: nil
)
subject
end
end
- context 'when the report is invalid' do
- let(:report_data) do
- {
- 'version' => report_version
- }
- end
-
- context 'when scanner information is empty' do
- let(:scanner) { {} }
-
- it 'logs related information' do
- expect(Gitlab::AppLogger).to receive(:info).with(
- message: "security report schema validation problem",
- security_report_type: report_type,
- security_report_version: report_version,
- project_id: project.id,
- security_report_failure: 'schema_validation_fails',
- security_report_scanner_id: nil,
- security_report_scanner_version: nil
- )
-
- expect(Gitlab::AppLogger).to receive(:info).with(
- message: "security report schema validation problem",
- security_report_type: report_type,
- security_report_version: report_version,
- project_id: project.id,
- security_report_failure: 'using_unsupported_schema_version',
- security_report_scanner_id: nil,
- security_report_scanner_version: nil
- )
-
- subject
- end
- end
-
- it { is_expected.to be_falsey }
- end
- end
-
- context 'when enforce_security_report_validation is disabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: false)
- end
-
- context 'when the report is valid' do
- let(:report_data) do
- {
- 'version' => report_version,
- 'vulnerabilities' => []
- }
- end
-
- it { is_expected.to be_truthy }
- end
-
- context 'when the report is invalid' do
- let(:report_data) do
- {
- 'version' => report_version
- }
- end
-
- it { is_expected.to be_truthy }
- end
+ it { is_expected.to be_falsey }
end
end
@@ -284,19 +278,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
- before do
- stub_feature_flags(enforce_security_report_validation: true)
- end
-
it { is_expected.to be_falsey }
-
- context 'when enforce_security_report_validation is disabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: false)
- end
-
- it { is_expected.to be_truthy }
- end
end
end
@@ -307,7 +289,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:report_type) { :dast }
let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
- context 'when the report is valid' do
+ context 'and the report is valid' do
let(:report_data) do
{
'version' => report_version,
@@ -318,34 +300,20 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
it { is_expected.to be_empty }
end
- context 'when the report is invalid' do
+ context 'and the report is invalid' do
let(:report_data) do
{
'version' => report_version
}
end
- context 'when enforce_security_report_validation is enabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: project)
- end
-
- let(:expected_errors) do
- [
- 'root is missing required keys: vulnerabilities'
- ]
- end
-
- it { is_expected.to match_array(expected_errors) }
+ let(:expected_errors) do
+ [
+ 'root is missing required keys: vulnerabilities'
+ ]
end
- context 'when enforce_security_report_validation is disabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: false)
- end
-
- it { is_expected.to be_empty }
- end
+ it { is_expected.to match_array(expected_errors) }
end
end
@@ -363,7 +331,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
stub_const("#{described_class}::DEPRECATED_VERSIONS", deprecations_hash)
end
- context 'when the report passes schema validation' do
+ context 'and the report passes schema validation' do
let(:report_data) do
{
'version' => '10.0.0',
@@ -374,119 +342,77 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
it { is_expected.to be_empty }
end
- context 'when the report does not pass schema validation' do
- context 'when enforce_security_report_validation is enabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: true)
- end
-
- let(:report_data) do
- {
- 'version' => 'V2.7.0'
- }
- end
-
- let(:expected_errors) do
- [
- "property '/version' does not match pattern: ^[0-9]+\\.[0-9]+\\.[0-9]+$",
- "root is missing required keys: vulnerabilities"
- ]
- end
-
- it { is_expected.to match_array(expected_errors) }
+ context 'and the report does not pass schema validation' do
+ let(:report_data) do
+ {
+ 'version' => 'V2.7.0'
+ }
end
- context 'when enforce_security_report_validation is disabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: false)
- end
-
- let(:report_data) do
- {
- 'version' => 'V2.7.0'
- }
- end
-
- it { is_expected.to be_empty }
+ let(:expected_errors) do
+ [
+ "property '/version' does not match pattern: ^[0-9]+\\.[0-9]+\\.[0-9]+$",
+ "root is missing required keys: vulnerabilities"
+ ]
end
+
+ it { is_expected.to match_array(expected_errors) }
end
end
context 'when given an unsupported schema version' do
let(:report_type) { :dast }
let(:report_version) { "12.37.0" }
+ let(:expected_unsupported_message) do
+ "Version #{report_version} for report type #{report_type} is unsupported, supported versions for this report type are: "\
+ "#{supported_dast_versions}. GitLab will attempt to validate this report against the earliest supported "\
+ "versions of this report type, to show all the errors but will not ingest the report"
+ end
- context 'when enforce_security_report_validation is enabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: true)
+ context 'and the report is valid' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
end
- context 'when the report is valid' do
- let(:report_data) do
- {
- 'version' => report_version,
- 'vulnerabilities' => []
- }
- end
-
- let(:expected_errors) do
- [
- "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: #{supported_dast_versions}"
- ]
- end
-
- it { is_expected.to match_array(expected_errors) }
+ let(:expected_errors) do
+ [
+ expected_unsupported_message
+ ]
end
- context 'when the report is invalid' do
- let(:report_data) do
- {
- 'version' => report_version
- }
- end
-
- let(:expected_errors) do
- [
- "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: #{supported_dast_versions}",
- "root is missing required keys: vulnerabilities"
- ]
- end
-
- it { is_expected.to match_array(expected_errors) }
- end
+ it { is_expected.to match_array(expected_errors) }
end
- context 'when enforce_security_report_validation is disabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: false)
+ context 'and the report is invalid' do
+ let(:report_data) do
+ {
+ 'version' => report_version
+ }
end
- context 'when the report is valid' do
- let(:report_data) do
- {
- 'version' => report_version,
- 'vulnerabilities' => []
- }
- end
-
- it { is_expected.to be_empty }
+ let(:expected_errors) do
+ [
+ expected_unsupported_message,
+ "root is missing required keys: vulnerabilities"
+ ]
end
- context 'when the report is invalid' do
- let(:report_data) do
- {
- 'version' => report_version
- }
- end
-
- it { is_expected.to be_empty }
- end
+ it { is_expected.to match_array(expected_errors) }
end
end
context 'when not given a schema version' do
let(:report_type) { :dast }
let(:report_version) { nil }
+ let(:expected_missing_version_message) do
+ "Report version not provided, #{report_type} report type supports versions: #{supported_dast_versions}. GitLab "\
+ "will attempt to validate this report against the earliest supported versions of this report type, to show all "\
+ "the errors but will not ingest the report"
+ end
+
let(:report_data) do
{
'vulnerabilities' => []
@@ -496,19 +422,11 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:expected_errors) do
[
"root is missing required keys: version",
- "Report version not provided, dast report type supports versions: #{supported_dast_versions}"
+ expected_missing_version_message
]
end
it { is_expected.to match_array(expected_errors) }
-
- context 'when enforce_security_report_validation is disabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: false)
- end
-
- it { is_expected.to be_empty }
- end
end
end
@@ -519,7 +437,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:report_type) { :dast }
let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
- context 'when the report is valid' do
+ context 'and the report is valid' do
let(:report_data) do
{
'version' => report_version,
@@ -530,7 +448,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
it { is_expected.to be_empty }
end
- context 'when the report is invalid' do
+ context 'and the report is invalid' do
let(:report_data) do
{
'version' => report_version
@@ -550,9 +468,14 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
end
let(:report_version) { described_class::DEPRECATED_VERSIONS[report_type].last }
+ let(:expected_deprecation_message) do
+ "Version #{report_version} for report type #{report_type} has been deprecated, supported versions for this "\
+ "report type are: #{supported_dast_versions}. GitLab will attempt to parse and ingest this report if valid."
+ end
+
let(:expected_deprecation_warnings) do
[
- "Version V2.7.0 for report type dast has been deprecated, supported versions for this report type are: #{supported_dast_versions}"
+ expected_deprecation_message
]
end
@@ -560,7 +483,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
stub_const("#{described_class}::DEPRECATED_VERSIONS", deprecations_hash)
end
- context 'when the report passes schema validation' do
+ context 'and the report passes schema validation' do
let(:report_data) do
{
'version' => report_version,
@@ -571,7 +494,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
it { is_expected.to match_array(expected_deprecation_warnings) }
end
- context 'when the report does not pass schema validation' do
+ context 'and the report does not pass schema validation' do
let(:report_data) do
{
'version' => 'V2.7.0'
@@ -600,11 +523,27 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
describe '#warnings' do
subject { validator.warnings }
- context 'when given a supported schema version' do
+ context 'when given a supported MAJOR.MINOR schema version' do
let(:report_type) { :dast }
- let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
+ let(:report_version) do
+ latest_vendored_version = described_class::SUPPORTED_VERSIONS[report_type].last.split(".")
+ (latest_vendored_version[0...2] << "34").join(".")
+ end
+
+ let(:latest_patch_version) do
+ ::Security::ReportSchemaVersionMatcher.new(
+ report_declared_version: report_version,
+ supported_versions: described_class::SUPPORTED_VERSIONS[report_type]
+ ).call
+ end
+
+ let(:message) do
+ "This report uses a supported MAJOR.MINOR schema version but the PATCH version doesn't match"\
+ " any vendored schema version. Validation will be attempted against version"\
+ " #{latest_patch_version}"
+ end
- context 'when the report is valid' do
+ context 'and the report is valid' do
let(:report_data) do
{
'version' => report_version,
@@ -612,37 +551,57 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
- it { is_expected.to be_empty }
+ it { is_expected.to match_array([message]) }
end
- context 'when the report is invalid' do
+ context 'and the report is invalid' do
let(:report_data) do
{
'version' => report_version
}
end
- context 'when enforce_security_report_validation is enabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: project)
- end
+ it { is_expected.to match_array([message]) }
+
+ it 'logs related information' do
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ message: "security report schema validation problem",
+ security_report_type: report_type,
+ security_report_version: report_version,
+ project_id: project.id,
+ security_report_failure: 'schema_validation_fails',
+ security_report_scanner_id: 'gemnasium',
+ security_report_scanner_version: '2.1.0'
+ )
- it { is_expected.to be_empty }
+ subject
end
+ end
+ end
- context 'when enforce_security_report_validation is disabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: false)
- end
+ context 'when given a supported schema version' do
+ let(:report_type) { :dast }
+ let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
- let(:expected_warnings) do
- [
- 'root is missing required keys: vulnerabilities'
- ]
- end
+ context 'and the report is valid' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
+ end
+
+ it { is_expected.to be_empty }
+ end
- it { is_expected.to match_array(expected_warnings) }
+ context 'and the report is invalid' do
+ let(:report_data) do
+ {
+ 'version' => report_version
+ }
end
+
+ it { is_expected.to be_empty }
end
end
@@ -660,7 +619,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
stub_const("#{described_class}::DEPRECATED_VERSIONS", deprecations_hash)
end
- context 'when the report passes schema validation' do
+ context 'and the report passes schema validation' do
let(:report_data) do
{
'vulnerabilities' => []
@@ -670,35 +629,14 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
it { is_expected.to be_empty }
end
- context 'when the report does not pass schema validation' do
+ context 'and the report does not pass schema validation' do
let(:report_data) do
{
'version' => 'V2.7.0'
}
end
- context 'when enforce_security_report_validation is enabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: true)
- end
-
- it { is_expected.to be_empty }
- end
-
- context 'when enforce_security_report_validation is disabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: false)
- end
-
- let(:expected_warnings) do
- [
- "property '/version' does not match pattern: ^[0-9]+\\.[0-9]+\\.[0-9]+$",
- "root is missing required keys: vulnerabilities"
- ]
- end
-
- it { is_expected.to match_array(expected_warnings) }
- end
+ it { is_expected.to be_empty }
end
end
@@ -706,71 +644,25 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:report_type) { :dast }
let(:report_version) { "12.37.0" }
- context 'when enforce_security_report_validation is enabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: true)
- end
-
- context 'when the report is valid' do
- let(:report_data) do
- {
- 'version' => report_version,
- 'vulnerabilities' => []
- }
- end
-
- it { is_expected.to be_empty }
+ context 'and the report is valid' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
end
- context 'when the report is invalid' do
- let(:report_data) do
- {
- 'version' => report_version
- }
- end
-
- it { is_expected.to be_empty }
- end
+ it { is_expected.to be_empty }
end
- context 'when enforce_security_report_validation is disabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: false)
- end
-
- context 'when the report is valid' do
- let(:report_data) do
- {
- 'version' => report_version,
- 'vulnerabilities' => []
- }
- end
-
- let(:expected_warnings) do
- [
- "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: #{supported_dast_versions}"
- ]
- end
-
- it { is_expected.to match_array(expected_warnings) }
+ context 'and the report is invalid' do
+ let(:report_data) do
+ {
+ 'version' => report_version
+ }
end
- context 'when the report is invalid' do
- let(:report_data) do
- {
- 'version' => report_version
- }
- end
-
- let(:expected_warnings) do
- [
- "Version 12.37.0 for report type dast is unsupported, supported versions for this report type are: #{supported_dast_versions}",
- "root is missing required keys: vulnerabilities"
- ]
- end
-
- it { is_expected.to match_array(expected_warnings) }
- end
+ it { is_expected.to be_empty }
end
end
@@ -784,21 +676,6 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
end
it { is_expected.to be_empty }
-
- context 'when enforce_security_report_validation is disabled' do
- before do
- stub_feature_flags(enforce_security_report_validation: false)
- end
-
- let(:expected_warnings) do
- [
- "root is missing required keys: version",
- "Report version not provided, dast report type supports versions: #{supported_dast_versions}"
- ]
- end
-
- it { is_expected.to match_array(expected_warnings) }
- end
end
end
end