From b7672c39483627322aaed5096e780954de3bc62c Mon Sep 17 00:00:00 2001 From: Al Snow <43523+jasnow@users.noreply.github.com> Date: Sun, 21 Jun 2026 10:36:50 -0400 Subject: [PATCH 1/5] Add desc and title line length checks plus add "|-" to README --- gems/net-imap/CVE-2026-47241.yml | 7 ++++--- gems/nokogiri/GHSA-8678-w3jw-xfc2.yml | 6 ++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/gems/net-imap/CVE-2026-47241.yml b/gems/net-imap/CVE-2026-47241.yml index 6c568e2a77..08da913433 100644 --- a/gems/net-imap/CVE-2026-47241.yml +++ b/gems/net-imap/CVE-2026-47241.yml @@ -84,9 +84,10 @@ description: | commands are issued in a logically coherent order, and for ensuring that commands are only pipelined when it is safe to do so. - Practically, this means that many commands cannot be safely pipelined together, - and user code will often need to wait for state changing commands to successfully - complete before issuing commands that rely on that state change. + Practically, this means that many commands cannot be safely pipelined + together, and user code will often need to wait for state changing + commands to successfully complete before issuing commands that rely on + that state change. cvss_v3: 2.1 cvss_v4: 2.1 patched_versions: diff --git a/gems/nokogiri/GHSA-8678-w3jw-xfc2.yml b/gems/nokogiri/GHSA-8678-w3jw-xfc2.yml index be60e8a6e1..52bffd8f8f 100644 --- a/gems/nokogiri/GHSA-8678-w3jw-xfc2.yml +++ b/gems/nokogiri/GHSA-8678-w3jw-xfc2.yml @@ -47,12 +47,14 @@ description: | ``` ruby # allows resources to be accessed over the network for trusted input - schema = Nokogiri::XML::Schema.new(trusted_schema, Nokogiri::XML::ParseOptions.new.nononet) + schema = Nokogiri::XML::Schema.new(trusted_schema, + Nokogiri::XML::ParseOptions.new.nononet) ``` ### References - - Bypass of: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-vr8q-g5c7-m54m + - Bypass of: + https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-vr8q-g5c7-m54m ### Credit From 7b2e1ac5b624337a2bb56041b7cd85ee66665b01 Mon Sep 17 00:00:00 2001 From: Al Snow <43523+jasnow@users.noreply.github.com> Date: Sun, 21 Jun 2026 10:38:34 -0400 Subject: [PATCH 2/5] Add README.md and spec changes --- README.md | 3 ++- spec/advisory_example.rb | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d5c70932cb..2b029ba187 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,8 @@ patched_versions: * `date` \[Date\] (required): The public disclosure date of the advisory. * `description` \[String\] (required): One or more paragraphs describing the vulnerability. It may contain multiple paragraphs. - * Used `description: |` if it is more than one sentence/line. + * Used `description: |` or `description: |-` if it is more than + one sentence/line. * Line wrap `descriptions:` field at 80. * Do not include "POC", "PoC", or "Proof of Concept" heading sections (any casing) in the `description:` field. diff --git a/spec/advisory_example.rb b/spec/advisory_example.rb index b160580f74..5972b078a0 100644 --- a/spec/advisory_example.rb +++ b/spec/advisory_example.rb @@ -3,6 +3,9 @@ require 'yaml' +MAX_DESC_LEN = 102 +MAX_TITLE_LEN = 154 + shared_examples_for 'Advisory' do |path| advisory = YAML.safe_load_file(path, permitted_classes: [Date]) @@ -133,6 +136,20 @@ it "must not start with or end with additional whitespace" do expect(subject).to_not match(/\A\s|\s\z/) end + + it "has a title <= #{MAX_TITLE_LEN} characters" do + title = advisory["title"] + expect(title).to be_a(String) + + filename_root = File.basename(path, ".yml") + + # 5/28/2026: May 8, 2026 is earliest start date with no failed checks. + start_date = Date.new(2026, 5, 8) + if advisory["date"] >= start_date and !filename_root.start_with?("OSVDB") + expect(title.length).to be <= MAX_TITLE_LEN, + "Title too long (#{title.length} chars): #{title.inspect}" + end + end end describe "date" do @@ -158,6 +175,22 @@ it { expect(subject).to be_kind_of(String) } it { expect(subject).not_to be_empty } + + it "has a description with no line > ${MAX_DESC_LEN} characters" do + description = advisory["description"] + expect(description).to be_a(String) + + filename_root = File.basename(path, ".yml") + + # 5/28/2026: May 8, 2026 is earliest start date with no failed checks. + start_date = Date.new(2026, 5, 8) + if advisory["date"] >= start_date and !filename_root.start_with?("OSVDB") + long_lines = description.split("\n").select { |line| line.length > MAX_DESC_LEN } + expect(long_lines).to be_empty, + "Description has lines > #{MAX_DESC_LEN} chars:\n" + + long_lines.map { |l| " #{l.length} chars: #{l.inspect}" }.join("\n") + end + end end describe "cvss_v2" do From 30667df64ec085a84c8d51e486bb33fb3d0e6bb9 Mon Sep 17 00:00:00 2001 From: Al Snow <43523+jasnow@users.noreply.github.com> Date: Tue, 23 Jun 2026 12:06:59 -0400 Subject: [PATCH 3/5] Delete spec/advisory_example.rb --- spec/advisory_example.rb | 302 --------------------------------------- 1 file changed, 302 deletions(-) delete mode 100644 spec/advisory_example.rb diff --git a/spec/advisory_example.rb b/spec/advisory_example.rb deleted file mode 100644 index 5972b078a0..0000000000 --- a/spec/advisory_example.rb +++ /dev/null @@ -1,302 +0,0 @@ -require 'spec_helper' -require 'versions_example' - -require 'yaml' - -MAX_DESC_LEN = 102 -MAX_TITLE_LEN = 154 - -shared_examples_for 'Advisory' do |path| - advisory = YAML.safe_load_file(path, permitted_classes: [Date]) - - describe path do - let(:filename) { File.basename(path) } - - let(:filename_cve) do - if filename.start_with?('CVE-') - filename.gsub('CVE-','') - end - end - - let(:filename_osvdb) do - if filename.start_with?('OSVDB-') - filename.gsub('OSVDB-','') - end - end - - let(:filename_ghsa) do - if filename.start_with?('GHSA-') - filename.gsub('GHSA-','') - end - end - - it "should be correctly named CVE-XXX or OSVDB-XXX or GHSA-XXX" do - expect(filename).to match( - /\A - (?: - CVE-\d{4}-(?:0\d{3}|[1-9]\d{3,})| - OSVDB-\d+| - GHSA(-[a-z0-9]{4}){3} - )\.yml\z - /x - ) - end - - it "should have CVE or OSVDB or GHSA" do - expect(advisory['cve'] || advisory['osvdb'] || advisory['ghsa']).not_to be_nil - end - - it "should CVE-XXX if cve field has a value" do - if advisory['cve'] - expect(filename).to start_with('CVE-') - elsif advisory['ghsa'] - expect(filename).to start_with('GHSA-') - end - end - - describe "platform" do - subject { advisory['platform'] } - - it "may be nil or a String" do - expect(subject).to be_kind_of(String).or(be_nil) - end - end - - describe "cve" do - subject { advisory['cve'] } - - it "may be nil or a String" do - expect(subject).to be_kind_of(String).or(be_nil) - end - - it "should be id in filename if filename is CVE-XXX" do - if filename_cve - expect(subject).to eq(filename_cve.chomp('.yml')) - end - end - end - - describe "osvdb" do - subject { advisory['osvdb'] } - - it "may be nil or a Integer" do - expect(subject).to be_kind_of(Integer).or(be_nil) - end - - it "should be id in filename if filename is OSVDB-XXX" do - if filename_osvdb - expect(subject).to eq(filename_osvdb.to_i) - end - end - end - - describe "ghsa" do - subject { advisory['ghsa'] } - - it "may be nil or a String" do - expect(subject).to be_kind_of(String).or(be_nil) - end - it "should be id in filename if filename is GHSA-XXX" do - if filename_ghsa - expect(subject).to eq(filename_ghsa.chomp('.yml')) - end - end - end - - describe "url" do - subject { advisory['url'] } - - 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"] - - 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) - if advisory["date"] >= start_date and !filename_root.start_with?("OSVDB") - expect(url).to include(filename_root) - end - end - end - - describe "title" do - subject { advisory['title'] } - - it { expect(subject).to be_kind_of(String) } - it { expect(subject).not_to be_empty } - - it "must be one line" do - expect(subject).to_not include("\n") - end - - it "must not start with or end with additional whitespace" do - expect(subject).to_not match(/\A\s|\s\z/) - end - - it "has a title <= #{MAX_TITLE_LEN} characters" do - title = advisory["title"] - expect(title).to be_a(String) - - filename_root = File.basename(path, ".yml") - - # 5/28/2026: May 8, 2026 is earliest start date with no failed checks. - start_date = Date.new(2026, 5, 8) - if advisory["date"] >= start_date and !filename_root.start_with?("OSVDB") - expect(title.length).to be <= MAX_TITLE_LEN, - "Title too long (#{title.length} chars): #{title.inspect}" - end - end - end - - describe "date" do - subject { advisory['date'] } - - it { expect(subject).to be_kind_of(Date) } - end - - describe "description" do - subject { advisory['description'] } - - it "must not be one line" do - expect(subject).to include("\n") - end - - it "must not have double embbedded newlines" do - expect(subject).to_not include("\\n\\n") - end - - it "must not have PoC sections" do - expect(subject).to_not match(/(#+) *(?:poc\b|proof of concept)/i) - end - - it { expect(subject).to be_kind_of(String) } - it { expect(subject).not_to be_empty } - - it "has a description with no line > ${MAX_DESC_LEN} characters" do - description = advisory["description"] - expect(description).to be_a(String) - - filename_root = File.basename(path, ".yml") - - # 5/28/2026: May 8, 2026 is earliest start date with no failed checks. - start_date = Date.new(2026, 5, 8) - if advisory["date"] >= start_date and !filename_root.start_with?("OSVDB") - long_lines = description.split("\n").select { |line| line.length > MAX_DESC_LEN } - expect(long_lines).to be_empty, - "Description has lines > #{MAX_DESC_LEN} chars:\n" + - long_lines.map { |l| " #{l.length} chars: #{l.inspect}" }.join("\n") - end - end - end - - describe "cvss_v2" do - subject { advisory['cvss_v2'] } - - it "may be nil or a Float" do - expect(subject).to be_kind_of(Float).or(be_nil) - end - - case advisory['cvss_v2'] - when Float - context "when a Float" do - it { expect(subject).to be_between(0.0, 10.0) } - end - end - end - - describe "cvss_v3" do - subject { advisory['cvss_v3'] } - - it "may be nil or a Float" do - expect(subject).to be_kind_of(Float).or(be_nil) - end - - case advisory['cvss_v3'] - when Float - context "when a Float" do - it { expect(subject).to be_between(0.0, 10.0) } - end - end - - if advisory['cvss_v2'] - it "should also provide a cvss_v2 score" do - expect(advisory['cvss_v2']).to_not be_nil - end - end - end - - describe "cvss_v4" do - subject { advisory['cvss_v4'] } - - it "may be nil or a Float" do - expect(subject).to be_kind_of(Float).or(be_nil) - end - - case advisory['cvss_v4'] - when Float - context "when a Float" do - it { expect(subject).to be_between(0.0, 10.0) } - end - end - end - - describe "patched_versions" do - subject { advisory['patched_versions'] } - - it "may be nil or an Array" do - expect(subject).to be_kind_of(Array).or(be_nil) - end - - if advisory['patched_versions'].kind_of?(Array) - include_examples "Versions", advisory['patched_versions'] - end - end - - describe "unaffected_versions" do - subject { advisory['unaffected_versions'] } - - it "may be nil or an Array" do - expect(subject).to be_kind_of(Array).or(be_nil) - end - - if advisory['unaffected_versions'].kind_of?(Array) - include_examples "Versions", advisory['unaffected_versions'] - end - end - - describe "related" do - subject { advisory['related'] } - - it "may be nil or a Hash" do - expect(subject).to be_kind_of(Hash).or(be_nil) - end - - case advisory["related"] - when Hash - advisory["related"].each_pair do |name,values| - describe(name) do - it "should be either a cve, an osvdb, a ghsa, or a url" do - expect(["cve", "osvdb", "ghsa", "url"]).to include(name) - end - - it "should always contain an array" do - expect(values).to be_kind_of(Array) - end - end - end - end - end - - describe "notes" do - subject { advisory['notes'] } - - it "may be nil or a String" do - expect(subject).to be_kind_of(String).or(be_nil) - end - end - end -end From 25702b0a0fd76da6957f4078cee16eb05900cc55 Mon Sep 17 00:00:00 2001 From: Al Snow <43523+jasnow@users.noreply.github.com> Date: Tue, 23 Jun 2026 12:08:36 -0400 Subject: [PATCH 4/5] Delete gems/net-imap/CVE-2026-47241.yml --- gems/net-imap/CVE-2026-47241.yml | 105 ------------------------------- 1 file changed, 105 deletions(-) delete mode 100644 gems/net-imap/CVE-2026-47241.yml diff --git a/gems/net-imap/CVE-2026-47241.yml b/gems/net-imap/CVE-2026-47241.yml deleted file mode 100644 index 08da913433..0000000000 --- a/gems/net-imap/CVE-2026-47241.yml +++ /dev/null @@ -1,105 +0,0 @@ ---- -gem: net-imap -cve: 2026-47241 -ghsa: c4fp-cxrr-mj66 -url: https://www.cve.org/CVERecord?id=CVE-2026-47241 -title: 'Net::IMAP: Denial of Service via incomplete raw argument validation' -date: 2026-06-09 -description: | - - ### Summary - - Several Net::IMAP commands accept a raw string argument which is only - validated to prevent CRLF injection and then sent verbatim. If this - string is derived from user-controlled input, an attacker can force - the next command to be absorbed as a continuation of the first command. - This will cause the first command to eventually fail, but also prevents - it from returning until another command is sent (from another thread). - That other command will not return until the connection is closed. - - ### Details - - `Net::IMAP::RawData` was hardened in v0.6.4, v0.5.14, and v0.4.24 to - reject string arguments that would smuggle an invalid literal-continuation - marker onto the wire (CVE-2026-42257, GHSA-hm49-wcqc-g2xg). But the - trailing-marker check uses an incorrect regex which does not match - `{0}` or `{0+}`, so an attacker-controlled seach `criteria` or fetch - `attr` string ending in `{0}` or `{0+}` passes validation and is sent - verbatim. Since these arguments are sent as the last argument in the - command, they will be followed by CRLF. Although the CRLF was intended - to end the command, the server will interpret it as part of a literal - prefix. This consumes the next command the client puts on the socket - as additional arguments to the current command. - - This affects the following command's arguments: - * `criteria` for `#search` and `#uid_search` - * `search_keys` for `#sort`, `#thread`, `#uid_sort`, and `#uid_thread` - * `attr` for `#fetch` and `#uid_fetch` - - The command which contained the attacker's raw data will not be able - to complete until the _next_ command is issued. If commands are only - sent from single thread, the first command will hang until the connection - times out (most likely by the server closing the connection). - - If a second command is sent _(from another thread)_, this would allow - the server to respond to the first command. This combined command - _will_ be invalid: - * The `{0}\\r\\n` literal prohibits other arguments (such as a quoted - string) from spanning both commands - * It will be sent without the space delimiter which is required - between arguments. - * The second command's tag will not be a valid argument to any of the - vulnerable commands. - - So the server _should_ respond to the first command with a `BAD` response, - which will raise a `BadResponseError`. - - But, since the server never saw a second command, the second command will - never receive a tagged response and the thread that sent it will hang until - the connection is closed. - - ### Impact - - This will result in unexpected crashes and timeouts, which could be used - to create a simple denial of service attack. This attack will present - very similarly to common network issues or server issues which also result - in commands hanging or unexpectedly raising exceptions. By itself, this - does not allow command injection. But the confusion caused by these - errors could lead to other downstream issues, especially in a - multi-threaded environment. - - ### Mitigation - - Update to a patched version of `net-imap` which validates that `RawData` - arguments may not end with literal continuation markers. - If `net-imap` cannot be upgraded:\n* Validate that user input to the - affected command arguments does not end with `\"}\"`. - * Use of `Timeout` or other standard strategies for slow connections - and misbehaving servers will also mitigate the effects of this. - - ### Extra caution is required when issuing commands from multiple threads. - - While `net-imap` does have rudimentary support for issuing commands - from multiple threads, the user is responsible for synchronizing that - commands are issued in a logically coherent order, and for ensuring - that commands are only pipelined when it is safe to do so. - - Practically, this means that many commands cannot be safely pipelined - together, and user code will often need to wait for state changing - commands to successfully complete before issuing commands that rely on - that state change. -cvss_v3: 2.1 -cvss_v4: 2.1 -patched_versions: - - "~> 0.5.15" - - ">= 0.6.4.1" -related: - url: - - https://www.cve.org/CVERecord?id=CVE-2026-47241 - - https://rubygems.org/gems/net-imap/versions/0.6.4.1 - - https://github.com/ruby/net-imap/releases/tag/v0.6.4.1 - - https://github.com/ruby/net-imap/security/advisories/GHSA-c4fp-cxrr-mj66 - - https://github.com/advisories/GHSA-c4fp-cxrr-mj66 -notes: | - - cve is reserved - - cvss_v3 - in GHSA ; No cvss_v2, cvss_v4 values. From 2245d77e06f3411f4b1758a7aca3213ca1230dbd Mon Sep 17 00:00:00 2001 From: Al Snow <43523+jasnow@users.noreply.github.com> Date: Tue, 23 Jun 2026 12:09:06 -0400 Subject: [PATCH 5/5] Delete gems/nokogiri/GHSA-8678-w3jw-xfc2.yml --- gems/nokogiri/GHSA-8678-w3jw-xfc2.yml | 69 --------------------------- 1 file changed, 69 deletions(-) delete mode 100644 gems/nokogiri/GHSA-8678-w3jw-xfc2.yml diff --git a/gems/nokogiri/GHSA-8678-w3jw-xfc2.yml b/gems/nokogiri/GHSA-8678-w3jw-xfc2.yml deleted file mode 100644 index e2071e59a3..0000000000 --- a/gems/nokogiri/GHSA-8678-w3jw-xfc2.yml +++ /dev/null @@ -1,69 +0,0 @@ ---- -gem: nokogiri -platform: jruby -ghsa: 8678-w3jw-xfc2 -url: https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-8678-w3jw-xfc2 -title: 'Nokogiri: XML::Schema on JRuby allows network requests when NONET is set, - bypassing CVE-2020-26247' -date: 2026-06-19 -description: | - ### Summary - - The `NONET` parse option, which Nokogiri turns on by default for - `Nokogiri::XML::Schema` (see - [CVE-2020-26247](https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-vr8q-g5c7-m54m)), - was not correctly enforced on the JRuby implementation. As a result, a schema - parsed with default options could still cause external resources to be fetched - over the network, potentially enabling SSRF or XXE attacks. - - Nokogiri 1.19.4 replaces the scheme denylist with an allowlist. When `NONET` - is enabled, only local resources (a `file:` scheme, or a relative or absolute - path with no scheme) are resolved, and every network scheme is blocked, - case-insensitively. This brings the JRuby behavior in line with CRuby. - - Only the JRuby implementation is affected. CRuby is not affected, because - libxml2's `xmlNoNetExternalEntityLoader` blocks all network schemes at the I/O - layer regardless of scheme or case. - - ### Severity - - The Nokogiri maintainers have evaluated this as low severity (CVSS 2.6, - `CVSS:3.0/AV:N/AC:H/PR:L/UI:R/S:U/C:L/I:N/A:N`). It is a bypass of - CVE-2020-26247, which was scored the same way. - - ### Mitigation - - Upgrade to Nokogiri 1.19.4 or later. - - There are no known workarounds for affected versions. - - This change properly enforces `NONET` on JRuby, which is a breaking change for - any code that (perhaps unknowingly) relied on the previous behavior to load - network resources with default parse options. If you trust your input and want - to allow external resources to be accessed over the network, you can - explicitly disable `NONET`, exactly as documented for CVE-2020-26247: - - 1. Ensure the input is trusted. Do not enable this option for untrusted input. - 2. Pass a `Nokogiri::XML::ParseOptions` with the `NONET` flag turned off: - - ``` ruby - # allows resources to be accessed over the network for trusted input - schema = Nokogiri::XML::Schema.new(trusted_schema, - Nokogiri::XML::ParseOptions.new.nononet) - ``` - - ### References - - - Bypass of: - https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-vr8q-g5c7-m54m - - ### Credit - - This issue was responsibly reported by @bilerden. -cvss_v3: 2.6 -patched_versions: - - ">= 1.19.4" -related: - url: - - https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-8678-w3jw-xfc2 - - https://github.com/advisories/GHSA-8678-w3jw-xfc2