GitHub Security Advisory: GHSA-7fc5-f82f-cx69
Package: net-imap (RUBYGEMS)
Severity: MODERATE (CVSS 6.5)
CWE: CWE-400: Uncontrolled Resource Consumption, CWE-405: Asymmetric Resource Consumption (Amplification), CWE-409: Improper Handling of Highly Compressed Data (Data Amplification), CWE-770: Allocation of Resources Without Limits or Throttling, CWE-789: Memory Allocation with Excessive Size Value
Vulnerable versions: >= 0.4.0, < 0.4.19
Patched in: 0.4.19

Summary: Possible DoS by memory exhaustion in net-imap

Details:
### Summary
There is a possibility for denial of service by memory exhaustion in `net-imap`'s response parser.  At any time while the client is connected, a malicious server can send  can send highly compressed `uid-set` data which is automatically read by the client's receiver thread.  The response parser uses `Range#to_a` to convert the `uid-set` data into arrays of integers, with no limitation on the expanded size of the ranges.

### Details
IMAP's `uid-set` and `sequence-set` formats can compress ranges of numbers, for example: `"1,2,3,4,5"` and `"1:5"` both represent the same set.  When `Net::IMAP::ResponseParser` receives `APPENDUID` or `COPYUID` response codes, it expands each `uid-set` into an array of integers.  On a 64 bit system, these arrays will expand to 8 bytes for each number in the set.  A malicious IMAP server may send specially crafted `APPENDUID` or `COPYUID` responses with very large `uid-set` ranges.

The `Net::IMAP` client parses each server response in a separate thread, as soon as each responses is received from the server.  This attack works even when the client does not handle the `APPENDUID` or `COPYUID` responses.

Malicious inputs:

```ruby
# 40 bytes expands to ~1.6GB:
"* OK [COPYUID 1 1:99999999 1:99999999]\r\n"

# Worst *valid* input scenario (using uint32 max),
# 44 bytes expands to 64GiB:
"* OK [COPYUID 1 1:[PII_REDACTED] 1:[PII_REDACTED]]\r\n"

# Numbers must be non-zero uint32, but this isn't validated.  Arrays larger than
# UINT32_MAX can be created.  For example, the following would theoretically
# expand to almost 800 exabytes:
"* OK [COPYUID 1 1:[PII_REDACTED][PII_REDACTED] 1:[PII_REDACTED][PII_REDACTED]]\r\n"
```

Simple way to test this:
```ruby
require "net/imap"

def test(size)
  input = "A004 OK [COPYUID 1 1:#{size} 1:#{size}] too large?\r\n"
  parser = Net::IMAP::ResponseParser.new
  parser.parse input
end

test(99_999_999)
```

### Fixes

#### Preferred Fix, minor API changes
Upgrade to v0.4.19, v0.5.6, or higher, and configure:
```ruby
# globally
Net::IMAP.config.parser_use_deprecated_uidplus_data = false
# per-client
imap = Net::IMAP.new(hostname, ssl: true,
                               parser_use_deprecated_uidplus_data: false)
imap.config.parser_use_deprecated_uidplus_data = false
```

This replaces `UIDPlusData` with `AppendUIDData` and `CopyUIDData`.  These classes store their UIDs as `Net::IMAP::SequenceSet` objects (_not_ expanded into arrays of integers).  Code that does not handle `APPENDUID` or `COPYUID` responses will not notice any difference.  Code that does handle these responses _may_ need to be updated.  See the documentation for [UIDPlusData](https://ruby.github.io/net-imap/Net/IMAP/UIDPlusData.html), [AppendUIDData](https://ruby.github.io/net-imap/Net/IMAP/AppendUIDData.html) and [CopyUIDData](https://ruby.github.io/net-imap/Net/IMAP/CopyUIDData.html).

For v0.3.8, this option is not available.
For v0.4.19, the default value is `true`.
For v0.5.6, the default value is `:up_to_max_size`.
For v0.6.0, the