From 56ef103d0e19fe7b5b8253d4353642f7c22c1a0d Mon Sep 17 00:00:00 2001 From: Ram Bhushan Mishra Date: Mon, 2 Mar 2026 12:56:58 +0530 Subject: [PATCH 1/6] CVSS v4 Support --- lib/vrt.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vrt.rb b/lib/vrt.rb index 7bec939..482b640 100644 --- a/lib/vrt.rb +++ b/lib/vrt.rb @@ -19,7 +19,7 @@ module VRT 'name' => 'Other', 'priority' => nil, 'type' => 'category' }.freeze - MAPPINGS = %i[cvss_v3 remediation_advice cwe].freeze + MAPPINGS = %i[cvss_v3 cvss_v4 remediation_advice cwe].freeze @version_json = {} @last_update = {} From a88c7097eb5f6fdabc7198c7ce64ff2c0bd32ea4 Mon Sep 17 00:00:00 2001 From: Ram Bhushan Mishra Date: Mon, 2 Mar 2026 12:57:40 +0530 Subject: [PATCH 2/6] Specs & ChangeLog --- CHANGELOG.md | 2 + spec/sample_vrt/2.0/mappings/cvss_v4.json | 78 ++++++++++ .../999.999/mappings/cvss_v4/cvss_v4.json | 141 ++++++++++++++++++ spec/vrt/mappings_spec.rb | 68 +++++++++ spec/vrt/node_spec.rb | 8 + 5 files changed, 297 insertions(+) create mode 100644 spec/sample_vrt/2.0/mappings/cvss_v4.json create mode 100644 spec/sample_vrt/999.999/mappings/cvss_v4/cvss_v4.json diff --git a/CHANGELOG.md b/CHANGELOG.md index b45e962..5e5bca8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ### Added - Find the [Secure Code Warrior](https://www.securecodewarrior.com/) remediation training associated with a VRT node +- CVSS v4 mapping support: `cvss_v4` in `VRT::MAPPINGS` and `node.mappings[:cvss_v4]` when mapping data is present +- Specs for cvss_v4 mappings (mapping file loading, `#get` for leaf/internal/root nodes, default, and node `#mappings`) ### Changed diff --git a/spec/sample_vrt/2.0/mappings/cvss_v4.json b/spec/sample_vrt/2.0/mappings/cvss_v4.json new file mode 100644 index 0000000..885f049 --- /dev/null +++ b/spec/sample_vrt/2.0/mappings/cvss_v4.json @@ -0,0 +1,78 @@ +{ + "metadata": { + "default": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + "content": [ + { + "id": "server_security_misconfiguration", + "cvss_v4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N", + "children": [ + { + "id": "unsafe_cross_origin_resource_sharing", + "cvss_v4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "ssl_attack_breach_poodle_etc", + "cvss_v4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "using_default_credentials", + "cvss_v4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "misconfigured_dns", + "cvss_v4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:L/SC:N/SI:N/SA:N" + } + ] + }, + { + "id": "server_side_injection", + "children": [ + { + "id": "file_inclusion", + "cvss_v4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:L/VA:L/SC:N/SI:N/SA:N" + }, + { + "id": "remote_code_execution_rce", + "cvss_v4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N" + }, + { + "id": "sql_injection", + "cvss_v4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N" + } + ] + }, + { + "id": "unvalidated_redirects_and_forwards", + "cvss_v4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N", + "children": [ + { + "id": "open_redirect", + "children": [ + { + "id": "get_based", + "cvss_v4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N" + } + ] + } + ] + }, + { + "id": "dumb_v2_category", + "cvss_v4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N" + }, + { + "id": "insecure_data_storage", + "cvss_v4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "broken_authentication_and_session_management", + "children": [ + { + "id": "authentication_bypass", + "cvss_v4": "CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N" + } + ] + } + ] +} diff --git a/spec/sample_vrt/999.999/mappings/cvss_v4/cvss_v4.json b/spec/sample_vrt/999.999/mappings/cvss_v4/cvss_v4.json new file mode 100644 index 0000000..2697ff1 --- /dev/null +++ b/spec/sample_vrt/999.999/mappings/cvss_v4/cvss_v4.json @@ -0,0 +1,141 @@ +{ + "metadata": { + "default": "CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + "content": [ + { + "id": "server_security_misconfiguration", + "cvss_v4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N", + "children": [ + { + "id": "unsafe_cross_origin_resource_sharing", + "cvss_v4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "ssl_attack_breach_poodle_etc", + "cvss_v4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "using_default_credentials", + "cvss_v4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "misconfigured_dns", + "cvss_v4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:H/VA:L/SC:N/SI:N/SA:N" + } + ] + }, + { + "id": "server_side_injection", + "children": [ + { + "id": "file_inclusion", + "cvss_v4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:L/VA:L/SC:N/SI:N/SA:N" + }, + { + "id": "remote_code_execution_rce", + "cvss_v4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N" + }, + { + "id": "sql_injection", + "cvss_v4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N" + } + ] + }, + { + "id": "unvalidated_redirects_and_forwards", + "cvss_v4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N", + "children": [ + { + "id": "open_redirect", + "children": [ + { + "id": "get_based", + "cvss_v4": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N" + } + ] + } + ] + }, + { + "id": "broken_authentication_and_session_management", + "cvss_v4": "CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "insecure_direct_object_references_idor", + "cvss_v4": "CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "sensitive_data_exposure", + "cvss_v4": "CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "cross_site_scripting_xss", + "cvss_v4": "CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "missing_function_level_access_control", + "cvss_v4": "CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "cross_site_request_forgery_csrf", + "cvss_v4": "CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "application_level_denial_of_service_dos", + "cvss_v4": "CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "external_behavior", + "cvss_v4": "CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "insufficient_security_configurability", + "cvss_v4": "CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "using_components_with_known_vulnerabilities", + "cvss_v4": "CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "insecure_data_storage", + "cvss_v4": "CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "lack_of_binary_hardening", + "cvss_v4": "CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "insecure_data_transport", + "cvss_v4": "CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "insecure_os_firmware", + "cvss_v4": "CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "broken_cryptography", + "cvss_v4": "CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "privacy_concerns", + "cvss_v4": "CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "network_security_misconfiguration", + "cvss_v4": "CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "mobile_security_misconfiguration", + "cvss_v4": "CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "client_side_injection", + "cvss_v4": "CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N" + }, + { + "id": "vrt_category_only_in_test", + "cvss_v4": "CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N" + } + ] +} diff --git a/spec/vrt/mappings_spec.rb b/spec/vrt/mappings_spec.rb index 673b49f..1e4e863 100644 --- a/spec/vrt/mappings_spec.rb +++ b/spec/vrt/mappings_spec.rb @@ -15,6 +15,12 @@ it { is_expected.not_to be nil } end + context 'when cvss_v4 mapping is nested under a subdirectory' do + let(:scheme) { :cvss_v4 } + + it { is_expected.not_to be nil } + end + context 'when mapping is in flat directory' do let(:scheme) { :cwe } @@ -196,4 +202,66 @@ end end end + + describe 'cvss_v4_mapping' do + let(:cvss_v4) { described_class.new(:cvss_v4) } + + describe '#get' do + subject { cvss_v4.get(id_list, version) } + + context 'when cvss_v4 on leaf node' do + let(:id_list) { %i[unvalidated_redirects_and_forwards open_redirect get_based] } + let(:version) { '999.999' } + + it 'returns the full CVSS v4 vector string' do + is_expected.to eq('CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N') + end + end + + context 'when cvss_v4 on internal node' do + let(:id_list) { %i[server_security_misconfiguration unsafe_cross_origin_resource_sharing] } + let(:version) { '999.999' } + + it 'returns the most specific cvss_v4 value' do + is_expected.to eq('CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N') + end + end + + context 'when cvss_v4 on root node with children' do + let(:id_list) { %i[server_security_misconfiguration] } + let(:version) { '999.999' } + + it 'returns the category-level cvss_v4 value' do + is_expected.to eq('CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N') + end + end + + context 'when cvss_v4 on root node without children' do + let(:id_list) { %i[insecure_data_storage] } + let(:version) { '999.999' } + + it 'returns the node cvss_v4 value' do + is_expected.to eq('CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N') + end + end + + context 'when other' do + let(:id_list) { %i[other] } + let(:version) { '999.999' } + + it 'returns the default from metadata' do + is_expected.to eq('CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:N/VI:N/VA:N/SC:N/SI:N/SA:N') + end + end + + context 'when using version 2.0 flat mapping' do + let(:id_list) { %i[unvalidated_redirects_and_forwards open_redirect get_based] } + let(:version) { '2.0' } + + it 'returns the cvss_v4 value for the leaf node' do + is_expected.to eq('CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N') + end + end + end + end end diff --git a/spec/vrt/node_spec.rb b/spec/vrt/node_spec.rb index 24b6cdc..25669c0 100644 --- a/spec/vrt/node_spec.rb +++ b/spec/vrt/node_spec.rb @@ -55,6 +55,14 @@ end end + context 'cvss_v4' do + it 'has the expected cvss_v4 full vector string' do + expect(mappings).to include( + cvss_v4: 'CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N' + ) + end + end + context 'remediation_advice' do let(:id) { 'server_security_misconfiguration.unsafe_cross_origin_resource_sharing' } From f27d6e580d8829bab058495d29f4533574c93d88 Mon Sep 17 00:00:00 2001 From: Ram Bhushan Mishra Date: Tue, 3 Mar 2026 10:50:58 +0530 Subject: [PATCH 3/6] ChangeLog Entry --- CHANGELOG.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e5bca8..a68df07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,13 +9,18 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ### Added - Find the [Secure Code Warrior](https://www.securecodewarrior.com/) remediation training associated with a VRT node -- CVSS v4 mapping support: `cvss_v4` in `VRT::MAPPINGS` and `node.mappings[:cvss_v4]` when mapping data is present -- Specs for cvss_v4 mappings (mapping file loading, `#get` for leaf/internal/root nodes, default, and node `#mappings`) ### Changed ### Removed +## [v0.13.7](https://github.com/bugcrowd/vrt-ruby/compare/v0.13.6...v0.13.7) - 2026-03-05 + +### Added + +- CVSS v4 mapping support: `cvss_v4` in `VRT::MAPPINGS` and `node.mappings[:cvss_v4]` when mapping data is present +- Specs for cvss_v4 mappings (mapping file loading, `#get` for leaf/internal/root nodes, default, and node `#mappings`) + ## [v0.13.6](https://github.com/bugcrowd/vrt-ruby/compare/v0.13.5...v0.13.6) - 2025-08-19 ### Added From 55998247faf1bea5b6ce498e11b7ff6413132450 Mon Sep 17 00:00:00 2001 From: Ram Bhushan Mishra Date: Fri, 6 Mar 2026 14:41:35 +0530 Subject: [PATCH 4/6] Updated Release Date --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a68df07..766be2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ### Removed -## [v0.13.7](https://github.com/bugcrowd/vrt-ruby/compare/v0.13.6...v0.13.7) - 2026-03-05 +## [v0.13.7](https://github.com/bugcrowd/vrt-ruby/compare/v0.13.6...v0.13.7) - 2026-03-09 ### Added From 9b915958fa5cde15879628baee728226105283f9 Mon Sep 17 00:00:00 2001 From: Ram Bhushan Mishra Date: Fri, 6 Mar 2026 15:06:20 +0530 Subject: [PATCH 5/6] Git Submodules --- .gitmodules | 3 +++ lib/data/1.18 | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/data/1.18 diff --git a/.gitmodules b/.gitmodules index ae952bf..ab1e1f6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -70,3 +70,6 @@ [submodule "lib/data/1.17"] path = lib/data/1.17 url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git +[submodule "lib/data/1.18"] + path = lib/data/1.18 + url = git@github.com:bugcrowd/vulnerability-rating-taxonomy.git diff --git a/lib/data/1.18 b/lib/data/1.18 new file mode 160000 index 0000000..5a74938 --- /dev/null +++ b/lib/data/1.18 @@ -0,0 +1 @@ +Subproject commit 5a7493842f95221d5d36a03a37139d387c92ece3 From 35a8ec92b43568c5a6a79bcfa9ba41c395b524a1 Mon Sep 17 00:00:00 2001 From: Ram Bhushan Mishra Date: Fri, 6 Mar 2026 15:09:25 +0530 Subject: [PATCH 6/6] Updated Version --- lib/vrt/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vrt/version.rb b/lib/vrt/version.rb index 4c77c48..f5a1620 100644 --- a/lib/vrt/version.rb +++ b/lib/vrt/version.rb @@ -1,3 +1,3 @@ module Vrt - VERSION = '0.13.6'.freeze + VERSION = '0.13.7'.freeze end