243 lines
7.2 KiB
Diff
243 lines
7.2 KiB
Diff
From 20814a27978725dfcbce7f1b149d4cb44e634653 Mon Sep 17 00:00:00 2001
|
|
From: nick evans <nick@rubinick.dev>
|
|
Date: Wed, 22 Apr 2026 11:25:06 -0400
|
|
Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=8D=92=20edit=20ca72ac45:=20=E2=99=BB?=
|
|
=?UTF-8?q?=EF=B8=8F=20Extract=20superclass=20for=20(internal)=20command?=
|
|
=?UTF-8?q?=20data?=
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
Unlike the cherry-picked commit (ca72ac45), this _only_ makes
|
|
`CommandData` the superclass for `Literal` and `Atom`. Because those
|
|
are the classes that will be modified by later cherry-picked commits.
|
|
This allows those other commits to merge more cleanly, and work with
|
|
fewer modifications.
|
|
---
|
|
lib/net/imap.rb | 44 ++++++++++++++++++++++++++++----------------
|
|
1 file changed, 28 insertions(+), 16 deletions(-)
|
|
|
|
diff --git a/lib/net/imap.rb b/lib/net/imap.rb
|
|
index 692d6e297a..ba44d21956 100644
|
|
--- a/lib/net/imap.rb
|
|
+++ b/lib/net/imap.rb
|
|
@@ -1566,22 +1566,43 @@ def start_tls_session(params = {})
|
|
end
|
|
end
|
|
|
|
- class RawData # :nodoc:
|
|
+ # simplistic emulation of CommandData = Data.define(:data)
|
|
+ class CommandData
|
|
+ class << self
|
|
+ def new(arg = nil, data: arg) super(data: data) end
|
|
+ alias :[] :new
|
|
+ end
|
|
+
|
|
+ def initialize(data:)
|
|
+ @data = data
|
|
+ freeze
|
|
+ end
|
|
+
|
|
+ attr_reader :data
|
|
+
|
|
+ def to_h(&block) block ? to_h.to_h(&block) : { data: data } end
|
|
+ def ==(other) self.class === other && to_h == other.to_h end
|
|
+ def eql?(other) self.class === other && to_h.eql?(other.to_h) end
|
|
+
|
|
+ # following class definition goes beyond the basic Data.define(:data)
|
|
+ ##
|
|
+
|
|
def send_data(imap, tag)
|
|
- imap.send(:put_string, @data)
|
|
+ raise NoMethodError, "#{self.class} must implement #{__method__}"
|
|
end
|
|
|
|
def validate
|
|
end
|
|
+ end
|
|
|
|
- private
|
|
|
|
- def initialize(data)
|
|
- @data = data
|
|
+ class RawData < CommandData # :nodoc:
|
|
+ def send_data(imap, tag)
|
|
+ imap.send(:put_string, @data)
|
|
end
|
|
end
|
|
|
|
- class Atom # :nodoc:
|
|
+ class Atom < CommandData # :nodoc:
|
|
def send_data(imap, tag)
|
|
imap.send(:put_string, @data)
|
|
end
|
|
@@ -1611,19 +1632,10 @@ def initialize(data)
|
|
end
|
|
end
|
|
|
|
- class Literal # :nodoc:
|
|
+ class Literal < CommandData # :nodoc:
|
|
def send_data(imap, tag)
|
|
imap.send(:send_literal, @data, tag)
|
|
end
|
|
-
|
|
- def validate
|
|
- end
|
|
-
|
|
- private
|
|
-
|
|
- def initialize(data)
|
|
- @data = data
|
|
- end
|
|
end
|
|
|
|
class MessageSet # :nodoc:
|
|
|
|
From 5c6f3ce364478fd8fde9fe8c9fc47cad34ac4ba7 Mon Sep 17 00:00:00 2001
|
|
From: nick evans <nick@rubinick.dev>
|
|
Date: Thu, 19 Feb 2026 15:06:08 -0500
|
|
Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=8D=92=20pick=209db3e9d60:=20?=
|
|
=?UTF-8?q?=F0=9F=A5=85=20Strictly=20validate=20symbol=20(\flag)=20argumen?=
|
|
=?UTF-8?q?ts=20[backports=20#657]?=
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
Flags should not allow `atom-specials`.
|
|
|
|
Previously, no validation was done on symbol data. Sending atom or flag
|
|
args which contain atom specials could lead to various errors.
|
|
|
|
Although this could theoretically include injection attacks, this is not
|
|
considered to be a critical vulnerability in `net-imap`, for the
|
|
following reason: Valid "system flag" inputs are restricted to an
|
|
enumerated set of RFC-defined flag types. User-defined "keyword" flags
|
|
are sent as atoms, not flags, which use string inputs (strings which
|
|
can't be sent as an atom will be quoted or sent as a literal). `\Seen`
|
|
as a flag (symbol argument) is semantically different from `Seen` as a
|
|
keyword (string argument). So there is no scenario where it is
|
|
appropriate to call `#to_sym` on unvetted user input. Any code which
|
|
calls `#to_sym` indiscriminately on user-input is already buggy.
|
|
|
|
Nevertheless, users should reasonably be able to rely on `net-imap` to
|
|
do very basic input validation on its basic input types.
|
|
|
|
===
|
|
|
|
Tests were not backported, they use a much newer style that does not
|
|
make sense to backport, when we can run the tests elsewhere instead of
|
|
trying to essentially write some portions anew.
|
|
---
|
|
lib/net/imap.rb | 32 ++++++++++++++++++++++----------
|
|
1 file changed, 22 insertions(+), 10 deletions(-)
|
|
|
|
diff --git a/lib/net/imap.rb b/lib/net/imap.rb
|
|
index ba44d21956..4354731f13 100644
|
|
--- a/lib/net/imap.rb
|
|
+++ b/lib/net/imap.rb
|
|
@@ -1344,6 +1344,7 @@ def validate_data(data)
|
|
end
|
|
when Time
|
|
when Symbol
|
|
+ Flag.validate(data)
|
|
else
|
|
data.validate
|
|
end
|
|
@@ -1362,7 +1363,7 @@ def send_data(data, tag = nil)
|
|
when Time
|
|
send_time_data(data)
|
|
when Symbol
|
|
- send_symbol_data(data)
|
|
+ Flag[data].send_data(self, tag)
|
|
else
|
|
data.send_data(self, tag)
|
|
end
|
|
@@ -1432,10 +1433,6 @@ def send_time_data(time)
|
|
put_string(s)
|
|
end
|
|
|
|
- def send_symbol_data(symbol)
|
|
- put_string("\\" + symbol.to_s)
|
|
- end
|
|
-
|
|
def search_internal(cmd, keys, charset)
|
|
if keys.instance_of?(String)
|
|
keys = [RawData.new(keys)]
|
|
@@ -1587,6 +1584,12 @@ def eql?(other) self.class === other && to_h.eql?(other.to_h) end
|
|
# following class definition goes beyond the basic Data.define(:data)
|
|
##
|
|
|
|
+ def self.validate(*args)
|
|
+ data = new(*args)
|
|
+ data.validate
|
|
+ data
|
|
+ end
|
|
+
|
|
def send_data(imap, tag)
|
|
raise NoMethodError, "#{self.class} must implement #{__method__}"
|
|
end
|
|
@@ -1603,17 +1606,26 @@ def send_data(imap, tag)
|
|
end
|
|
|
|
class Atom < CommandData # :nodoc:
|
|
- def send_data(imap, tag)
|
|
- imap.send(:put_string, @data)
|
|
+ def initialize(**)
|
|
+ super
|
|
+ validate
|
|
end
|
|
|
|
def validate
|
|
+ data.to_s.ascii_only? \
|
|
+ or raise DataFormatError, "#{self.class} must be ASCII only"
|
|
+ data.match?(ResponseParser::Patterns::ATOM_SPECIALS) \
|
|
+ and raise DataFormatError, "#{self.class} must not contain atom-specials"
|
|
end
|
|
|
|
- private
|
|
+ def send_data(imap, tag)
|
|
+ imap.send(:put_string, data.to_s)
|
|
+ end
|
|
+ end
|
|
|
|
- def initialize(data)
|
|
- @data = data
|
|
+ class Flag < Atom # :nodoc:
|
|
+ def send_data(imap, tag)
|
|
+ imap.send(:put_string, "\\#{data}")
|
|
end
|
|
end
|
|
|
|
|
|
From 1f8a99c432f748dfccb275cc696d8ac58c2255e6 Mon Sep 17 00:00:00 2001
|
|
From: Jarek Prokop <jprokop@redhat.com>
|
|
Date: Tue, 9 Jun 2026 18:14:51 +0200
|
|
Subject: [PATCH 3/3] Cherry-pick ATOM_SPECIALS for validation regex.
|
|
|
|
The previous commit relies on ResponseParsers::Patterns::ATOM_SPECIALS
|
|
to be available. We could either hardcode the regex in the line where
|
|
the matching happens, but instead have it live in its own constant for
|
|
easier reference and less edits to the actual commit backport.
|
|
|
|
The line ported from:
|
|
https://github.com/ruby/net-imap/commit/92db350b24c388d2a2104f36cac9caa49a1044df
|
|
---
|
|
lib/net/imap.rb | 4 ++++
|
|
1 file changed, 4 insertions(+)
|
|
|
|
diff --git a/lib/net/imap.rb b/lib/net/imap.rb
|
|
index 4354731f13..2af43806bb 100644
|
|
--- a/lib/net/imap.rb
|
|
+++ b/lib/net/imap.rb
|
|
@@ -2218,6 +2218,10 @@ def multipart?
|
|
end
|
|
|
|
class ResponseParser # :nodoc:
|
|
+ module Patterns
|
|
+ ATOM_SPECIALS = /[(){ \x00-\x1f\x7f%*"\\\]]/n
|
|
+ end
|
|
+
|
|
def initialize
|
|
@str = nil
|
|
@pos = nil
|