diff options
Diffstat (limited to 'spec/lib/gitlab/ci/parsers')
-rw-r--r-- | spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb | 73 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/parsers/sbom/validators/cyclonedx_schema_validator_spec.rb | 268 |
2 files changed, 177 insertions, 164 deletions
diff --git a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb index a331af9a9ac..9c8402faf77 100644 --- a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb +++ b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb @@ -33,35 +33,27 @@ RSpec.describe Gitlab::Ci::Parsers::Sbom::Cyclonedx, feature_category: :dependen allow(SecureRandom).to receive(:uuid).and_return(uuid) end - context 'when report JSON is invalid' do - let(:raw_report_data) { '{ ' } + context 'when report is invalid' do + 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:")) + 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 + expect { parse! }.not_to raise_error + end 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] } - 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") - 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! + parse! + end end end @@ -109,25 +101,26 @@ RSpec.describe Gitlab::Ci::Parsers::Sbom::Cyclonedx, feature_category: :dependen it 'adds each component, ignoring unused attributes' do expect(report).to receive(:add_component) - .with( - an_object_having_attributes( - name: "activesupport", - version: "5.1.4", - component_type: "library", - purl: an_object_having_attributes(type: "gem") - ) - ) + .with( + an_object_having_attributes( + name: "activesupport", + version: "5.1.4", + component_type: "library", + purl: an_object_having_attributes(type: "gem") + ) + ) expect(report).to receive(:add_component) - .with( - an_object_having_attributes( - name: "byebug", - version: "10.0.0", - component_type: "library", - purl: an_object_having_attributes(type: "gem") - ) - ) + .with( + an_object_having_attributes( + name: "byebug", + version: "10.0.0", + component_type: "library", + purl: an_object_having_attributes(type: "gem") + ) + ) expect(report).to receive(:add_component) - .with(an_object_having_attributes(name: "minimal-component", version: nil, component_type: "library")) + .with(an_object_having_attributes(name: "minimal-component", version: nil, + component_type: "library")) parse! 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 index acb7c122bcd..9422290761d 100644 --- 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 @@ -4,160 +4,116 @@ require "spec_helper" RSpec.describe Gitlab::Ci::Parsers::Sbom::Validators::CyclonedxSchemaValidator, feature_category: :dependency_management 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 + let(:report_data) do { "bomFormat" => "CycloneDX", - "specVersion" => "1.4", + "specVersion" => spec_version, "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 + subject(:validator) { described_class.new(report_data) } - it { is_expected.to be_valid } + shared_examples 'a validator that performs the expected validations' do + let(:required_attributes) do + { + "bomFormat" => "CycloneDX", + "specVersion" => spec_version, + "version" => 1 + } 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 + context "with minimally valid report" do + let(:report_data) { required_attributes } it { is_expected.to be_valid } end - context 'when components have licenses' do - let(:components) do - [ - { - "type" => "library", - "name" => "activesupport", - "version" => "5.1.4", - "licenses" => [ - { "license" => { "id" => "MIT" } } - ] - } - ] - end + context "when report has components" do + let(:report_data) { required_attributes.merge({ "components" => components }) } - it { is_expected.to be_valid } - end - - context 'when components have a signature' do - let(:components) do - [ - { - "type" => "library", - "name" => "activesupport", - "version" => "5.1.4", - "signature" => { - "algorithm" => "ES256", - "publicKey" => { - "kty" => "EC", - "crv" => "P-256", - "x" => "6BKxpty8cI-exDzCkh-goU6dXq3MbcY0cd1LaAxiNrU", - "y" => "mCbcvUzm44j3Lt2b5BPyQloQ91tf2D2V-gzeUxWaUdg" - }, - "value" => "ybT1qz5zHNi4Ndc6y7Zhamuf51IqXkPkZwjH1XcC-KSuBiaQplTw6Jasf2MbCLg3CF7PAdnMO__WSLwvI5r2jA" + 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 + ] + end - context "when components are not valid" do - let(:components) do - [ - { "type" => "foo" }, - { "name" => "activesupport" } - ] + it { is_expected.to be_valid } end - it { is_expected.not_to be_valid } - - it "outputs errors for each validation failure" do - expect(validator.errors).to match_array( + context "when components have versions" do + let(:components) do [ - "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 + { + "type" => "library", + "name" => "activesupport", + "version" => "5.1.4" + }, + { + "type" => "library", + "name" => "byebug", + "version" => "10.0.0" + } + ] + end - let(:report_data) { required_attributes.merge({ "metadata" => metadata }) } + it { is_expected.to be_valid } + end - it { is_expected.to be_valid } + context 'when components have licenses' do + let(:components) do + [ + { + "type" => "library", + "name" => "activesupport", + "version" => "5.1.4", + "licenses" => [ + { "license" => { "id" => "MIT" } } + ] + } + ] + end - context "when metadata has properties" do - before do - metadata.merge!({ "properties" => properties }) + it { is_expected.to be_valid } end - context "when properties are valid" do - let(:properties) do + context 'when components have a signature' do + let(:components) do [ - { "name" => "gitlab:dependency_scanning:input_file", "value" => "Gemfile.lock" }, - { "name" => "gitlab:dependency_scanning:package_manager", "value" => "bundler" } + { + "type" => "library", + "name" => "activesupport", + "version" => "5.1.4", + "signature" => { + "algorithm" => "ES256", + "publicKey" => { + "kty" => "EC", + "crv" => "P-256", + "x" => "6BKxpty8cI-exDzCkh-goU6dXq3MbcY0cd1LaAxiNrU", + "y" => "mCbcvUzm44j3Lt2b5BPyQloQ91tf2D2V-gzeUxWaUdg" + }, + "value" => "ybT1qz5zHNi4Ndc6y7Zhamuf51IqXkPkZwjH1XcC-KSuBiaQplTw6Jasf2MbCLg3CF7PAdnMO__WSLwvI5r2jA" + } + } ] end it { is_expected.to be_valid } end - context "when properties are invalid" do - let(:properties) do + context "when components are not valid" do + let(:components) do [ - { "name" => ["gitlab:meta:schema_version"], "value" => 1 } + { "type" => "foo" }, + { "name" => "activesupport" } ] end @@ -166,11 +122,75 @@ RSpec.describe Gitlab::Ci::Parsers::Sbom::Validators::CyclonedxSchemaValidator, 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" + "property '/components/0' is missing required keys: name", + a_string_starting_with("property '/components/0/type' is not one of:"), + "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 + + context 'when spec version is supported' do + where(:spec_version) { %w[1.4 1.5] } + + with_them do + it_behaves_like 'a validator that performs the expected validations' + end + end + + context 'when spec version is not supported' do + let(:spec_version) { '1.3' } + + it { is_expected.not_to be_valid } end end |