diff --git a/README.md b/README.md index fc97a27368..b8d291ecaa 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,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/gems/net-imap/CVE-2026-47241.yml b/gems/net-imap/CVE-2026-47241.yml deleted file mode 100644 index 6c568e2a77..0000000000 --- a/gems/net-imap/CVE-2026-47241.yml +++ /dev/null @@ -1,104 +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. diff --git a/gems/nokogiri/GHSA-8678-w3jw-xfc2.yml b/gems/nokogiri/GHSA-8678-w3jw-xfc2.yml deleted file mode 100644 index 95b5ed0132..0000000000 --- a/gems/nokogiri/GHSA-8678-w3jw-xfc2.yml +++ /dev/null @@ -1,67 +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 diff --git a/spec/advisory_example.rb b/spec/advisory_example.rb deleted file mode 100644 index b160580f74..0000000000 --- a/spec/advisory_example.rb +++ /dev/null @@ -1,269 +0,0 @@ -require 'spec_helper' -require 'versions_example' - -require 'yaml' - -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 - 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 } - 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