From 89e3356b79d19991e01bf5e79b66b8fff8fc105f Mon Sep 17 00:00:00 2001 From: Al Snow <43523+jasnow@users.noreply.github.com> Date: Sun, 24 May 2026 13:28:54 -0400 Subject: [PATCH 1/6] new check: url: == basename(filename) --- CONTRIBUTING.md | 3 ++- spec/advisory_example.rb | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 72681b560e..1c779cd67c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,7 +9,8 @@ * Please see the [README](README.md#schema) for more documentation on the YAML Schema. * Prior to submitting a pull request, run the tests: - +* Advisory filename and the root of the `url:` field must + be equal `(as of 5/10/2026).` ``` bundle install bundle exec rspec diff --git a/spec/advisory_example.rb b/spec/advisory_example.rb index 6097bbfd4a..3c43ddf838 100644 --- a/spec/advisory_example.rb +++ b/spec/advisory_example.rb @@ -106,6 +106,24 @@ it { expect(subject).to be_kind_of(String) } it { expect(subject).to_not match(%r{\Ahttp(s)?://osvdb\.org}) } it { expect(subject).not_to be_empty } + + it "has a filename that matches the root of the url field" do + url = advisory["url"] + + # Extract last path segment from URL + url_root = File.basename(URI.parse(url).path) + + # Extract filename without extension + filename_root = File.basename(path, ".yml") + + # 5/24/2026: May 9, 2026 is earliest start date with no failed checks. + start_date = Date.new(2026, 5, 9) + # Skip advisories older than start_date and old OSVDB advisories. + if advisory["date"] >= start_date and !filename_root.start_with?("OSVDB") + expect(filename_root).to eq(url_root), + "Expected filename '#{filename_root}' DOES NOT to match URL root '#{url_root}'" + end + end end describe "title" do From 50ce20346f6bee1b2f95b914bdfca14f53c4e933 Mon Sep 17 00:00:00 2001 From: Al Snow <43523+jasnow@users.noreply.github.com> Date: Wed, 27 May 2026 10:16:41 -0400 Subject: [PATCH 2/6] Removed both strings form line 124 expect stmt --- spec/advisory_example.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/advisory_example.rb b/spec/advisory_example.rb index 3c43ddf838..0cc806cafd 100644 --- a/spec/advisory_example.rb +++ b/spec/advisory_example.rb @@ -121,7 +121,7 @@ # Skip advisories older than start_date and old OSVDB advisories. if advisory["date"] >= start_date and !filename_root.start_with?("OSVDB") expect(filename_root).to eq(url_root), - "Expected filename '#{filename_root}' DOES NOT to match URL root '#{url_root}'" + "Expected filename DOES NOT to match URL root" end end end From b9e375080f1f463d4a50bdbc10a1ebea653d50fe Mon Sep 17 00:00:00 2001 From: Al Snow <43523+jasnow@users.noreply.github.com> Date: Wed, 27 May 2026 10:21:44 -0400 Subject: [PATCH 3/6] Update URL matching expectation in advisory example --- spec/advisory_example.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/spec/advisory_example.rb b/spec/advisory_example.rb index 0cc806cafd..d0ff3ce76f 100644 --- a/spec/advisory_example.rb +++ b/spec/advisory_example.rb @@ -110,9 +110,6 @@ it "has a filename that matches the root of the url field" do url = advisory["url"] - # Extract last path segment from URL - url_root = File.basename(URI.parse(url).path) - # Extract filename without extension filename_root = File.basename(path, ".yml") @@ -120,7 +117,7 @@ start_date = Date.new(2026, 5, 9) # Skip advisories older than start_date and old OSVDB advisories. if advisory["date"] >= start_date and !filename_root.start_with?("OSVDB") - expect(filename_root).to eq(url_root), + expect(url).to include(filename_root), "Expected filename DOES NOT to match URL root" end end From ee50a18a12fdd84f722e32539ee47c03003ae79b Mon Sep 17 00:00:00 2001 From: Al Snow <43523+jasnow@users.noreply.github.com> Date: Wed, 27 May 2026 10:36:57 -0400 Subject: [PATCH 4/6] Changed prinary URL to fix PR#1069 CI failures --- gems/faraday/CVE-2026-33637.yml | 45 +++++++++++++++++++++++++ gems/jwt/CVE-2026-45363.yml | 58 +++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 gems/faraday/CVE-2026-33637.yml create mode 100644 gems/jwt/CVE-2026-45363.yml diff --git a/gems/faraday/CVE-2026-33637.yml b/gems/faraday/CVE-2026-33637.yml new file mode 100644 index 0000000000..26357acfa9 --- /dev/null +++ b/gems/faraday/CVE-2026-33637.yml @@ -0,0 +1,45 @@ +--- +gem: faraday +cve: 2026-33637 +ghsa: 5rv5-xj5j-3484 +url: https://nvd.nist.gov/vuln/detail/CVE-2026-33637 +title: Faraday has a possible incomplete fix for GHSA-33mh-2634-fwr2 - + protocol-relative URI objects still bypass host scoping +date: 2026-05-18 +description: | + ## Summary + + `Faraday::Connection#build_exclusive_url` still allows protocol-relative + host override when the request target is provided as a `URI` object + instead of a `String`. This bypasses the February 2026 fix for + `GHSA-33mh-2634-fwr2` and can redirect a request built from a fixed-base + `Faraday::Connection` to an attacker-controlled host while preserving + connection-scoped headers such as `Authorization`. + + ## Supporting Materials + + - Existing advisory for the original string-based issue: GHSA-33mh-2634-fwr2 + - Existing CVE for the original string-based issue: CVE-2026-25765 + - Existing regression tests for the string-only fix: + - spec/faraday/connection_spec.rb:314-345 + - Existing test proving supported URI request input: + - spec/faraday/request_spec.rb:26-31 + + ## Impact + + The direct consequence is off-host request forgery from code paths + that believe they are constrained to a fixed base URL. If the + connection carries default headers or query parameters, those + values are forwarded to the attacker-selected host. +cvss_v3: 6.5 +unaffected_versions: + - "< 2.0.0" +patched_versions: + - ">= 2.14.2" +related: + url: + - https://nvd.nist.gov/vuln/detail/CVE-2026-33637 + - https://github.com/lostisland/faraday/releases/tag/v2.14.2 + - https://github.com/lostisland/faraday/security/advisories/GHSA-5rv5-xj5j-3484 + - https://github.com/advisories/GHSA-33mh-2634-fwr2 + - https://github.com/advisories/GHSA-5rv5-xj5j-3484 diff --git a/gems/jwt/CVE-2026-45363.yml b/gems/jwt/CVE-2026-45363.yml new file mode 100644 index 0000000000..1504ff58da --- /dev/null +++ b/gems/jwt/CVE-2026-45363.yml @@ -0,0 +1,58 @@ +--- +gem: jwt +cve: 2026-45363 +ghsa: c32j-vqhx-rx3x +url: https://www.cve.org/CVERecord?id=CVE-2026-45363 +title: 'ruby-jwt: Empty-key HMAC bypass; cross-language sibling of CVE-2026-44351' +date: 2026-05-18 +description: | + `JWT.decode(token, '', true, algorithm: 'HS256')` accepts an + attacker-forged token. `OpenSSL::HMAC.digest('SHA256', '', payload)` + returns a valid digest under an empty key, and no + `raise InvalidKeyError if key.empty?` precondition exists in the HMAC + algorithm. + + ``` + JWT.decode(token, "", true, algorithm: 'HS256') + -> JWA::Hmac.verify(verification_key: "", ...) + -> OpenSSL::HMAC.digest('SHA256', "", signing_input) == signature + ``` + + The same path is reached when a keyfinder block or key_finder: argument + returns "", nil, or an array containing nil for an unknown key. + JWT::Decode#find_key only rejects literal nil and empty arrays, and + JWT::JWA::Hmac silently coerces nil to "" (signing_key ||= '') before + signing. + + ``` + JWT.decode(token, nil, true, algorithms: ['HS256']) { |_h| "" } + -> find_key returns "" # "" && !Array("").empty? == true + -> JWA::Hmac.verify(verification_key: "", ...) + -> verifies + ``` + + Common application patterns that produce the unsafe value: + `redis.get("kid:#{kid}").to_s`, ORM string columns with `default: ''`, + `ENV['SECRET'] || ''`, `Hash.new('')` lookups, `[primary, fallback]` + where fallback may be nil. Applications passing a non-empty static + `key:`, or whose keyfinder returns nil / raises on miss, are not + affected. + + The existing `enforce_hmac_key_length` option would block this but + defaults to false. On OpenSSL ≥ 3.5 the empty-key HMAC.digest call no + longer raises, so the OpenSSL-3.0 rescue in JWA::Hmac#sign does not + fire. + + Affects HS256/HS384/HS512 via both JWT.decode (positional key and block + keyfinder) and `JWT::EncodedToken#verify_signature!(key_finder:)`. +cvss_v3: 7.4 +patched_versions: + - "~> 2.10.3" + - ">= 3.2.0" +related: + url: + - https://www.cve.org/CVERecord?id=CVE-2026-45363 + - https://github.com/jwt/ruby-jwt/security/advisories/GHSA-c32j-vqhx-rx3x + - https://github.com/jwt/ruby-jwt/commit/db560b769a07bd9724e77ff505011ac01872106f + - https://github.com/jwt/ruby-jwt/releases/tag/v3.2.0 + - https://github.com/advisories/GHSA-c32j-vqhx-rx3x From e2110c59c27a9cd486545635e54d058461792232 Mon Sep 17 00:00:00 2001 From: Al Snow <43523+jasnow@users.noreply.github.com> Date: Wed, 27 May 2026 10:42:47 -0400 Subject: [PATCH 5/6] Reworded expect error string --- spec/advisory_example.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/advisory_example.rb b/spec/advisory_example.rb index d0ff3ce76f..9ec5601b1f 100644 --- a/spec/advisory_example.rb +++ b/spec/advisory_example.rb @@ -118,7 +118,7 @@ # Skip advisories older than start_date and old OSVDB advisories. if advisory["date"] >= start_date and !filename_root.start_with?("OSVDB") expect(url).to include(filename_root), - "Expected filename DOES NOT to match URL root" + "Expected base filename DOES NOT include URL" end end end From 981e87b705d02a80a9f308881b26fe886937e3d1 Mon Sep 17 00:00:00 2001 From: Al Snow <43523+jasnow@users.noreply.github.com> Date: Wed, 27 May 2026 12:23:45 -0400 Subject: [PATCH 6/6] deleted 2 comments + expect failed string as requested --- spec/advisory_example.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/spec/advisory_example.rb b/spec/advisory_example.rb index 9ec5601b1f..eaedbf136d 100644 --- a/spec/advisory_example.rb +++ b/spec/advisory_example.rb @@ -110,15 +110,12 @@ it "has a filename that matches the root of the url field" do url = advisory["url"] - # Extract filename without extension filename_root = File.basename(path, ".yml") # 5/24/2026: May 9, 2026 is earliest start date with no failed checks. start_date = Date.new(2026, 5, 9) - # Skip advisories older than start_date and old OSVDB advisories. if advisory["date"] >= start_date and !filename_root.start_with?("OSVDB") - expect(url).to include(filename_root), - "Expected base filename DOES NOT include URL" + expect(url).to include(filename_root) end end end