Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Metrics/BlockLength:
- 'spec/cvss3/cvss3_spec.rb'
- 'spec/cvss31/cvss31_spec.rb'
- 'spec/cvss40/cvss40_spec.rb'
- 'spec/cvss_metric_spec.rb'

Style/IfUnlessModifier:
Exclude:
Expand Down
4 changes: 2 additions & 2 deletions lib/cvss_suite/cvss_31_and_before.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ def valid?
# Returns the Overall Score of the CVSS vector.
def overall_score
check_validity
return temporal_score if @temporal.valid? && !@environmental.valid?
return environmental_score if @environmental.valid?
return temporal_score if @temporal.explicitly_provided? && !@environmental.explicitly_provided?
return environmental_score if @environmental.explicitly_provided?

base_score
end
Expand Down
11 changes: 11 additions & 0 deletions lib/cvss_suite/cvss_metric.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ def valid?
true
end

##
# Returns if any property in this metric was explicitly provided
# (i.e., set to a value other than the default 'X' or 'ND').
def explicitly_provided?
@properties.any? do |property|
property.valid? &&
property.selected_value[:abbreviation] != 'X' &&
property.selected_value[:abbreviation] != 'ND'
end
end

##
# Returns number of properties for this metric.
def count
Expand Down
39 changes: 39 additions & 0 deletions spec/cvss3/cvss3_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@
CvssSuite.new('CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H/E:U/RL:T/RC:U/CR:L/IR:L/AR:H/MAV:P/MAC:H/MPR:H/MUI:R/MS:C/MC:H/MI:H/MA:H') # rubocop:disable Layout/LineLength
end

# Base-only vectors with Scope: Changed (issue #58)
let(:valid_cvss3_base_only_scope_changed1) { CvssSuite.new('CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H') }
let(:valid_cvss3_base_only_scope_changed2) { CvssSuite.new('CVSS:3.0/AV:P/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H') }
let(:valid_cvss3_base_only_scope_changed3) { CvssSuite.new('CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H') }
# Temporal-only with Scope: Changed (issue #58)
let(:valid_cvss3_temporal_only_scope_changed) do
CvssSuite.new('CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H/E:H/RL:T/RC:C')
end

let(:invalid_cvss3_with_version) { CvssSuite.new('CVSS:3.0/AV:L/AC:') }
let(:invalid_cvss3_not_defined) { CvssSuite.new('CVSS:3.0/AV:X/AC:H/PR:L/UI:R/S:U/C:L/I:N/A:H') }
let(:invalid_cvss3_missing_metric) { CvssSuite.new('CVSS:3.0/AV:L/AC:H/PR:L/UI:R/S:U/C:L/I:L') }
Expand Down Expand Up @@ -141,6 +150,36 @@
it_behaves_like 'a valid cvss vector', 3.0, 10.0, 6.05, 3.89, 8.1, 5.5, 5.5, 'Medium'
end

# Issue #58: overall_score should equal base_score for base-only vectors with Scope: Changed
describe 'base-only vectors with Scope: Changed (issue #58)' do
it 'CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H overall_score equals base_score (9.6)' do
cvss = valid_cvss3_base_only_scope_changed1
expect(cvss.overall_score).to eql(cvss.base_score)
expect(cvss.base_score).to eql(9.6)
end

it 'CVSS:3.0/AV:P/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H overall_score equals base_score (7.6)' do
cvss = valid_cvss3_base_only_scope_changed2
expect(cvss.overall_score).to eql(cvss.base_score)
expect(cvss.base_score).to eql(7.6)
end

it 'CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H overall_score equals base_score (9.0)' do
cvss = valid_cvss3_base_only_scope_changed3
expect(cvss.overall_score).to eql(cvss.base_score)
expect(cvss.base_score).to eql(9.0)
end
end

# Issue #58: overall_score should equal temporal_score when only temporal metrics are provided
describe 'temporal-only vector with Scope: Changed (issue #58)' do
it 'CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H/E:H/RL:T/RC:C overall_score equals temporal_score (9.3)' do
cvss = valid_cvss3_temporal_only_scope_changed
expect(cvss.overall_score).to eql(cvss.temporal_score)
expect(cvss.temporal_score).to eql(9.3)
end
end

describe 'invalid cvss3' do
subject { invalid_cvss3_with_version }

Expand Down
86 changes: 86 additions & 0 deletions spec/cvss31/cvss31_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,26 @@
let(:valid_cvss31_temporal_environmental_modified_confidentiality_high) do
CvssSuite.new('CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H/E:U/RL:T/RC:U/CR:L/IR:L/AR:H/MAV:P/MAC:H/MPR:H/MUI:R/MS:C/MC:H/MI:H/MA:H') # rubocop:disable Layout/LineLength
end
# Base-only vectors with Scope: Changed (issue #58)
let(:valid_cvss31_base_only_scope_changed1) { CvssSuite.new('CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H') }
let(:valid_cvss31_base_only_scope_changed2) { CvssSuite.new('CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H') }
let(:valid_cvss31_base_only_scope_changed3) { CvssSuite.new('CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H') }
let(:valid_cvss31_base_only_scope_changed4) { CvssSuite.new('CVSS:3.1/AV:A/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H') }
let(:valid_cvss31_base_only_scope_changed5) { CvssSuite.new('CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H') }
# Base-only with Scope: Unchanged (sanity check — should also return base_score)
let(:valid_cvss31_base_only_scope_unchanged) { CvssSuite.new('CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H') }
# Temporal-only with Scope: Changed (issue #58)
let(:valid_cvss31_temporal_only_scope_changed1) do
CvssSuite.new('CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H/E:H/RL:T/RC:C')
end
let(:valid_cvss31_temporal_only_scope_changed2) do
CvssSuite.new('CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H/E:P/RL:O/RC:R')
end
# Environmental-only with Scope: Changed (issue #58)
let(:valid_cvss31_env_only_scope_changed) do
CvssSuite.new('CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H/CR:L/IR:M/AR:H/MAV:N/MAC:H/MPR:N/MUI:R/MS:U/MC:N/MI:L/MA:H') # rubocop:disable Layout/LineLength
end

let(:invalid_cvss31_with_version) { CvssSuite.new('CVSS:3.1/AV:L/AC:') }
let(:invalid_cvss31_not_defined) { CvssSuite.new('CVSS:3.1/AV:X/AC:H/PR:L/UI:R/S:U/C:L/I:N/A:H') }
let(:invalid_cvss31_missing_metric) { CvssSuite.new('CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:U/C:L/I:L') }
Expand Down Expand Up @@ -139,6 +159,72 @@
it_behaves_like 'a valid cvss vector', 3.1, 10.0, 6.05, 3.89, 8.1, 5.6, 5.6, 'Medium'
end

# Issue #58: overall_score should equal base_score for base-only vectors with Scope: Changed
describe 'base-only vectors with Scope: Changed (issue #58)' do
it 'CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H overall_score equals base_score (9.6)' do
cvss = valid_cvss31_base_only_scope_changed1
expect(cvss.overall_score).to eql(cvss.base_score)
expect(cvss.base_score).to eql(9.6)
end

it 'CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H overall_score equals base_score (7.6)' do
cvss = valid_cvss31_base_only_scope_changed2
expect(cvss.overall_score).to eql(cvss.base_score)
expect(cvss.base_score).to eql(7.6)
end

it 'CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H overall_score equals base_score (9.0)' do
cvss = valid_cvss31_base_only_scope_changed3
expect(cvss.overall_score).to eql(cvss.base_score)
expect(cvss.base_score).to eql(9.0)
end

it 'CVSS:3.1/AV:A/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H overall_score equals base_score (8.8)' do
cvss = valid_cvss31_base_only_scope_changed4
expect(cvss.overall_score).to eql(cvss.base_score)
expect(cvss.base_score).to eql(8.8)
end

it 'CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H overall_score equals base_score (9.0)' do
cvss = valid_cvss31_base_only_scope_changed5
expect(cvss.overall_score).to eql(cvss.base_score)
expect(cvss.base_score).to eql(9.0)
end
end

# Issue #58: overall_score should equal base_score for base-only S:U vectors too
describe 'base-only vector with Scope: Unchanged (issue #58 sanity check)' do
it 'CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H overall_score equals base_score (8.8)' do
cvss = valid_cvss31_base_only_scope_unchanged
expect(cvss.overall_score).to eql(cvss.base_score)
expect(cvss.base_score).to eql(8.8)
end
end

# Issue #58: overall_score should equal temporal_score when only temporal metrics are provided
describe 'temporal-only vectors with Scope: Changed (issue #58)' do
it 'CVSS:3.1/.../E:H/RL:T/RC:C overall_score equals temporal_score (9.3)' do
cvss = valid_cvss31_temporal_only_scope_changed1
expect(cvss.overall_score).to eql(cvss.temporal_score)
expect(cvss.temporal_score).to eql(9.3)
end

it 'CVSS:3.1/.../E:P/RL:O/RC:R overall_score equals temporal_score (8.3)' do
cvss = valid_cvss31_temporal_only_scope_changed2
expect(cvss.overall_score).to eql(cvss.temporal_score)
expect(cvss.temporal_score).to eql(8.3)
end
end

# Issue #58: overall_score should equal environmental_score when only env metrics are provided
describe 'environmental-only vector with Scope: Changed (issue #58)' do
it 'overall_score equals environmental_score (7.3)' do
cvss = valid_cvss31_env_only_scope_changed
expect(cvss.overall_score).to eql(cvss.environmental_score)
expect(cvss.environmental_score).to eql(7.3)
end
end

describe 'invalid cvss31' do
subject { invalid_cvss31_with_version }

Expand Down
106 changes: 106 additions & 0 deletions spec/cvss_metric_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# CVSS-Suite, a Ruby gem to manage the CVSS vector
#
# This work is licensed under the terms of the MIT license.
# See the LICENSE.md file in the top-level directory.

require_relative 'spec_helper'

describe CvssSuite::CvssMetric, '#explicitly_provided?' do
context 'with a CVSS 3.1 base-only vector' do
let(:cvss) { CvssSuite.new('CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H') }

it 'base metric is explicitly provided' do
expect(cvss.base.explicitly_provided?).to be true
end

it 'temporal metric is not explicitly provided' do
expect(cvss.temporal.explicitly_provided?).to be false
end

it 'environmental metric is not explicitly provided' do
expect(cvss.environmental.explicitly_provided?).to be false
end
end

context 'with a CVSS 3.1 vector including temporal metrics' do
let(:cvss) { CvssSuite.new('CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H/E:H/RL:T/RC:C') }

it 'temporal metric is explicitly provided' do
expect(cvss.temporal.explicitly_provided?).to be true
end

it 'environmental metric is not explicitly provided' do
expect(cvss.environmental.explicitly_provided?).to be false
end
end

context 'with a CVSS 3.1 vector including environmental metrics' do
let(:cvss) do
CvssSuite.new('CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H/CR:L/IR:M/AR:H/MAV:N/MAC:H/MPR:N/MUI:R/MS:U/MC:N/MI:L/MA:H') # rubocop:disable Layout/LineLength
end

it 'temporal metric is not explicitly provided' do
expect(cvss.temporal.explicitly_provided?).to be false
end

it 'environmental metric is explicitly provided' do
expect(cvss.environmental.explicitly_provided?).to be true
end
end

context 'with a CVSS 3.1 vector including temporal and environmental metrics' do
let(:cvss) do
CvssSuite.new('CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:C/C:L/I:L/A:L/E:P/RL:W/RC:R/CR:L/IR:M/AR:H/MAV:N/MAC:H/MPR:N/MUI:R/MS:C/MC:N/MI:L/MA:H') # rubocop:disable Layout/LineLength
end

it 'temporal metric is explicitly provided' do
expect(cvss.temporal.explicitly_provided?).to be true
end

it 'environmental metric is explicitly provided' do
expect(cvss.environmental.explicitly_provided?).to be true
end
end

context 'with a CVSS 3.1 vector with all environmental modifiers set to X' do
let(:cvss) do
CvssSuite.new('CVSS:3.1/AV:P/AC:L/PR:H/UI:N/S:C/C:N/I:L/A:H/E:X/RL:T/RC:C/CR:M/IR:L/AR:H/MAV:X/MAC:X/MPR:X/MUI:X/MS:X/MC:X/MI:X/MA:X') # rubocop:disable Layout/LineLength
end

it 'temporal metric is explicitly provided (RL:T, RC:C are non-default)' do
expect(cvss.temporal.explicitly_provided?).to be true
end

it 'environmental metric is explicitly provided (CR:M, IR:L, AR:H are non-default)' do
expect(cvss.environmental.explicitly_provided?).to be true
end
end

context 'with a CVSS 2 base-only vector' do
let(:cvss) { CvssSuite.new('AV:N/AC:L/Au:N/C:P/I:P/A:P') }

it 'base metric is explicitly provided' do
expect(cvss.base.explicitly_provided?).to be true
end

it 'temporal metric is not explicitly provided' do
expect(cvss.temporal.explicitly_provided?).to be false
end

it 'environmental metric is not explicitly provided' do
expect(cvss.environmental.explicitly_provided?).to be false
end
end

context 'with a CVSS 3.0 base-only vector' do
let(:cvss) { CvssSuite.new('CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H') }

it 'temporal metric is not explicitly provided' do
expect(cvss.temporal.explicitly_provided?).to be false
end

it 'environmental metric is not explicitly provided' do
expect(cvss.environmental.explicitly_provided?).to be false
end
end
end
Loading