fix CVE-2021-33621

(cherry picked from commit 8b60ed7949baf898cbf1e9b616ce5f89608605e7)
This commit is contained in:
sxt1001 2023-01-05 14:40:39 +08:00 committed by openeuler-sync-bot
parent 65e2eecb00
commit fe7798bba3
6 changed files with 451 additions and 1 deletions

View File

@ -0,0 +1,169 @@
From 30107a4797f14227568913499a9a0bb4285de63b Mon Sep 17 00:00:00 2001
From: Nobuyoshi Nakada <nobu@ruby-lang.org>
Date: Tue, 16 Aug 2022 18:36:12 +0900
Subject: [PATCH] Check cookie name/path/domain characters
https://hackerone.com/reports/1204977
---
lib/cgi/cookie.rb | 44 ++++++++++++++++++++-----
test/cgi/test_cgi_cookie.rb | 64 +++++++++++++++++++++++++++++++++++++
2 files changed, 100 insertions(+), 8 deletions(-)
diff --git a/lib/cgi/cookie.rb b/lib/cgi/cookie.rb
index 6b0d89c..4b11a6a 100644
--- a/lib/cgi/cookie.rb
+++ b/lib/cgi/cookie.rb
@@ -40,6 +40,10 @@ class CGI
class Cookie < Array
@@accept_charset="UTF-8" unless defined?(@@accept_charset)
+ TOKEN_RE = %r"\A[[!-~]&&[^()<>@,;:\\\"/?=\[\]{}]]+\z"
+ PATH_VALUE_RE = %r"\A[[ -~]&&[^;]]*\z"
+ DOMAIN_VALUE_RE = %r"\A(?<label>[A-Za-z][-A-Za-z0-9]*[A-Za-z0-9])(?:\.\g<label>)*\z"
+
# Create a new CGI::Cookie object.
#
# :call-seq:
@@ -72,8 +76,8 @@ class CGI
@domain = nil
@expires = nil
if name.kind_of?(String)
- @name = name
- @path = (%r|\A(.*/)| =~ ENV["SCRIPT_NAME"] ? $1 : "")
+ self.name = name
+ self.path = (%r|\A(.*/)| =~ ENV["SCRIPT_NAME"] ? $1 : "")
@secure = false
@httponly = false
return super(value)
@@ -84,11 +88,11 @@ class CGI
raise ArgumentError, "`name' required"
end
- @name = options["name"]
+ self.name = options["name"]
value = Array(options["value"])
# simple support for IE
- @path = options["path"] || (%r|\A(.*/)| =~ ENV["SCRIPT_NAME"] ? $1 : "")
- @domain = options["domain"]
+ self.path = options["path"] || (%r|\A(.*/)| =~ ENV["SCRIPT_NAME"] ? $1 : "")
+ self.domain = options["domain"]
@expires = options["expires"]
@secure = options["secure"] == true
@httponly = options["httponly"] == true
@@ -97,11 +101,35 @@ class CGI
end
# Name of this cookie, as a +String+
- attr_accessor :name
+ attr_reader :name
+ # Set name of this cookie
+ def name=(str)
+ if str and !TOKEN_RE.match?(str)
+ raise ArgumentError, "invalid name: #{str.dump}"
+ end
+ @name = str
+ end
+
# Path for which this cookie applies, as a +String+
- attr_accessor :path
+ attr_reader :path
+ # Set path for which this cookie applies
+ def path=(str)
+ if str and !PATH_VALUE_RE.match?(str)
+ raise ArgumentError, "invalid path: #{str.dump}"
+ end
+ @path = str
+ end
+
# Domain for which this cookie applies, as a +String+
- attr_accessor :domain
+ attr_reader :domain
+ # Set domain for which this cookie applies
+ def domain=(str)
+ if str and ((str = str.b).bytesize > 255 or !DOMAIN_VALUE_RE.match?(str))
+ raise ArgumentError, "invalid domain: #{str.dump}"
+ end
+ @domain = str
+ end
+
# Time at which this cookie expires, as a +Time+
attr_accessor :expires
# True if this cookie is secure; false otherwise
diff --git a/test/cgi/test_cgi_cookie.rb b/test/cgi/test_cgi_cookie.rb
index 985cc0d..2f09d0f 100644
--- a/test/cgi/test_cgi_cookie.rb
+++ b/test/cgi/test_cgi_cookie.rb
@@ -118,6 +118,70 @@ class CGICookieTest < Test::Unit::TestCase
end
+ def test_cgi_cookie_domain_injection_into_name
+ name = "a=b; domain=example.com;"
+ path = "/"
+ domain = "example.jp"
+ assert_raise(ArgumentError) do
+ CGI::Cookie.new('name' => name,
+ 'value' => "value",
+ 'domain' => domain,
+ 'path' => path)
+ end
+ end
+
+
+ def test_cgi_cookie_newline_injection_into_name
+ name = "a=b;\r\nLocation: http://example.com#"
+ path = "/"
+ domain = "example.jp"
+ assert_raise(ArgumentError) do
+ CGI::Cookie.new('name' => name,
+ 'value' => "value",
+ 'domain' => domain,
+ 'path' => path)
+ end
+ end
+
+
+ def test_cgi_cookie_multibyte_injection_into_name
+ name = "a=b;\u3042"
+ path = "/"
+ domain = "example.jp"
+ assert_raise(ArgumentError) do
+ CGI::Cookie.new('name' => name,
+ 'value' => "value",
+ 'domain' => domain,
+ 'path' => path)
+ end
+ end
+
+
+ def test_cgi_cookie_injection_into_path
+ name = "name"
+ path = "/; samesite=none"
+ domain = "example.jp"
+ assert_raise(ArgumentError) do
+ CGI::Cookie.new('name' => name,
+ 'value' => "value",
+ 'domain' => domain,
+ 'path' => path)
+ end
+ end
+
+
+ def test_cgi_cookie_injection_into_domain
+ name = "name"
+ path = "/"
+ domain = "example.jp; samesite=none"
+ assert_raise(ArgumentError) do
+ CGI::Cookie.new('name' => name,
+ 'value' => "value",
+ 'domain' => domain,
+ 'path' => path)
+ end
+ end
+
instance_methods.each do |method|
private method if method =~ /^test_(.*)/ && $1 != ENV['TEST']
--
2.33.0

View File

@ -0,0 +1,135 @@
From 64c5045c0a6b84fdb938a8465a0890e5f7162708 Mon Sep 17 00:00:00 2001
From: Yusuke Endoh <mame@ruby-lang.org>
Date: Tue, 22 Nov 2022 10:49:27 +0900
Subject: [PATCH] Prevent CRLF injection
Throw a RuntimeError if the HTTP response header contains CR or LF to
prevent HTTP response splitting.
https://hackerone.com/reports/1204695
---
lib/cgi/core.rb | 45 +++++++++++++++++++++++--------------
test/cgi/test_cgi_header.rb | 8 +++++++
2 files changed, 36 insertions(+), 17 deletions(-)
diff --git a/lib/cgi/core.rb b/lib/cgi/core.rb
index bec76e0..62e6068 100644
--- a/lib/cgi/core.rb
+++ b/lib/cgi/core.rb
@@ -188,17 +188,28 @@ class CGI
# Using #header with the HTML5 tag maker will create a <header> element.
alias :header :http_header
+ def _no_crlf_check(str)
+ if str
+ str = str.to_s
+ raise "A HTTP status or header field must not include CR and LF" if str =~ /[\r\n]/
+ str
+ else
+ nil
+ end
+ end
+ private :_no_crlf_check
+
def _header_for_string(content_type) #:nodoc:
buf = ''.dup
if nph?()
- buf << "#{$CGI_ENV['SERVER_PROTOCOL'] || 'HTTP/1.0'} 200 OK#{EOL}"
+ buf << "#{_no_crlf_check($CGI_ENV['SERVER_PROTOCOL']) || 'HTTP/1.0'} 200 OK#{EOL}"
buf << "Date: #{CGI.rfc1123_date(Time.now)}#{EOL}"
- buf << "Server: #{$CGI_ENV['SERVER_SOFTWARE']}#{EOL}"
+ buf << "Server: #{_no_crlf_check($CGI_ENV['SERVER_SOFTWARE'])}#{EOL}"
buf << "Connection: close#{EOL}"
end
- buf << "Content-Type: #{content_type}#{EOL}"
+ buf << "Content-Type: #{_no_crlf_check(content_type)}#{EOL}"
if @output_cookies
- @output_cookies.each {|cookie| buf << "Set-Cookie: #{cookie}#{EOL}" }
+ @output_cookies.each {|cookie| buf << "Set-Cookie: #{_no_crlf_check(cookie)}#{EOL}" }
end
return buf
end # _header_for_string
@@ -213,9 +224,9 @@ class CGI
## NPH
options.delete('nph') if defined?(MOD_RUBY)
if options.delete('nph') || nph?()
- protocol = $CGI_ENV['SERVER_PROTOCOL'] || 'HTTP/1.0'
+ protocol = _no_crlf_check($CGI_ENV['SERVER_PROTOCOL']) || 'HTTP/1.0'
status = options.delete('status')
- status = HTTP_STATUS[status] || status || '200 OK'
+ status = HTTP_STATUS[status] || _no_crlf_check(status) || '200 OK'
buf << "#{protocol} #{status}#{EOL}"
buf << "Date: #{CGI.rfc1123_date(Time.now)}#{EOL}"
options['server'] ||= $CGI_ENV['SERVER_SOFTWARE'] || ''
@@ -223,38 +234,38 @@ class CGI
end
## common headers
status = options.delete('status')
- buf << "Status: #{HTTP_STATUS[status] || status}#{EOL}" if status
+ buf << "Status: #{HTTP_STATUS[status] || _no_crlf_check(status)}#{EOL}" if status
server = options.delete('server')
- buf << "Server: #{server}#{EOL}" if server
+ buf << "Server: #{_no_crlf_check(server)}#{EOL}" if server
connection = options.delete('connection')
- buf << "Connection: #{connection}#{EOL}" if connection
+ buf << "Connection: #{_no_crlf_check(connection)}#{EOL}" if connection
type = options.delete('type')
- buf << "Content-Type: #{type}#{EOL}" #if type
+ buf << "Content-Type: #{_no_crlf_check(type)}#{EOL}" #if type
length = options.delete('length')
- buf << "Content-Length: #{length}#{EOL}" if length
+ buf << "Content-Length: #{_no_crlf_check(length)}#{EOL}" if length
language = options.delete('language')
- buf << "Content-Language: #{language}#{EOL}" if language
+ buf << "Content-Language: #{_no_crlf_check(language)}#{EOL}" if language
expires = options.delete('expires')
buf << "Expires: #{CGI.rfc1123_date(expires)}#{EOL}" if expires
## cookie
if cookie = options.delete('cookie')
case cookie
when String, Cookie
- buf << "Set-Cookie: #{cookie}#{EOL}"
+ buf << "Set-Cookie: #{_no_crlf_check(cookie)}#{EOL}"
when Array
arr = cookie
- arr.each {|c| buf << "Set-Cookie: #{c}#{EOL}" }
+ arr.each {|c| buf << "Set-Cookie: #{_no_crlf_check(c)}#{EOL}" }
when Hash
hash = cookie
- hash.each_value {|c| buf << "Set-Cookie: #{c}#{EOL}" }
+ hash.each_value {|c| buf << "Set-Cookie: #{_no_crlf_check(c)}#{EOL}" }
end
end
if @output_cookies
- @output_cookies.each {|c| buf << "Set-Cookie: #{c}#{EOL}" }
+ @output_cookies.each {|c| buf << "Set-Cookie: #{_no_crlf_check(c)}#{EOL}" }
end
## other headers
options.each do |key, value|
- buf << "#{key}: #{value}#{EOL}"
+ buf << "#{_no_crlf_check(key)}: #{_no_crlf_check(value)}#{EOL}"
end
return buf
end # _header_for_hash
diff --git a/test/cgi/test_cgi_header.rb b/test/cgi/test_cgi_header.rb
index bab2d03..ec2f4de 100644
--- a/test/cgi/test_cgi_header.rb
+++ b/test/cgi/test_cgi_header.rb
@@ -176,6 +176,14 @@ class CGIHeaderTest < Test::Unit::TestCase
end
+ def test_cgi_http_header_crlf_injection
+ cgi = CGI.new
+ assert_raise(RuntimeError) { cgi.http_header("text/xhtml\r\nBOO") }
+ assert_raise(RuntimeError) { cgi.http_header("type" => "text/xhtml\r\nBOO") }
+ assert_raise(RuntimeError) { cgi.http_header("status" => "200 OK\r\nBOO") }
+ assert_raise(RuntimeError) { cgi.http_header("location" => "text/xhtml\r\nBOO") }
+ end
+
instance_methods.each do |method|
private method if method =~ /^test_(.*)/ && $1 != ENV['TEST']
--
2.33.0

View File

@ -0,0 +1,39 @@
From 05f0c58048540e868d9bbc6e49151b27e1bc89e9 Mon Sep 17 00:00:00 2001
From: Jean Boussier <jean.boussier@gmail.com>
Date: Wed, 23 Nov 2022 12:10:36 +0100
Subject: [PATCH] Fix test_cgi_cookie_new_with_domain to pass on older rubies
---
test/cgi/test_cgi_cookie.rb | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/test/cgi/test_cgi_cookie.rb b/test/cgi/test_cgi_cookie.rb
index e3ec4be..6d31932 100644
--- a/test/cgi/test_cgi_cookie.rb
+++ b/test/cgi/test_cgi_cookie.rb
@@ -62,18 +62,18 @@ class CGICookieTest < Test::Unit::TestCase
def test_cgi_cookie_new_with_domain
h = {'name'=>'name1', 'value'=>'value1'}
- cookie = CGI::Cookie.new('domain'=>'a.example.com', **h)
+ cookie = CGI::Cookie.new(h.merge('domain'=>'a.example.com'))
assert_equal('a.example.com', cookie.domain)
- cookie = CGI::Cookie.new('domain'=>'1.example.com', **h)
+ cookie = CGI::Cookie.new(h.merge('domain'=>'1.example.com'))
assert_equal('1.example.com', cookie.domain, 'enhanced by RFC 1123')
assert_raise(ArgumentError) {
- CGI::Cookie.new('domain'=>'-a.example.com', **h)
+ CGI::Cookie.new(h.merge('domain'=>'-a.example.com'))
}
assert_raise(ArgumentError) {
- CGI::Cookie.new('domain'=>'a-.example.com', **h)
+ CGI::Cookie.new(h.merge('domain'=>'a-.example.com'))
}
end
--
2.33.0

View File

@ -0,0 +1,44 @@
From 5e09d632f3b56d85b2659ab47d5571ae9e270e10 Mon Sep 17 00:00:00 2001
From: Xenor Chang <tubaxenor@gmail.com>
Date: Mon, 28 Nov 2022 12:34:06 +0800
Subject: [PATCH] Loosen the domain regex to accept '.' (#29)
* Loosen the domain regex to accept '.'
Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
Co-authored-by: Hiroshi SHIBATA <hsbt@ruby-lang.org>
---
lib/cgi/cookie.rb | 2 +-
test/cgi/test_cgi_cookie.rb | 3 +++
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/lib/cgi/cookie.rb b/lib/cgi/cookie.rb
index 1a9c1a8..9498e2f 100644
--- a/lib/cgi/cookie.rb
+++ b/lib/cgi/cookie.rb
@@ -42,7 +42,7 @@ class CGI
TOKEN_RE = %r"\A[[!-~]&&[^()<>@,;:\\\"/?=\[\]{}]]+\z"
PATH_VALUE_RE = %r"\A[[ -~]&&[^;]]*\z"
- DOMAIN_VALUE_RE = %r"\A(?<label>(?!-)[-A-Za-z0-9]+(?<!-))(?:\.\g<label>)*\z"
+ DOMAIN_VALUE_RE = %r"\A\.?(?<label>(?!-)[-A-Za-z0-9]+(?<!-))(?:\.\g<label>)*\z"
# Create a new CGI::Cookie object.
#
diff --git a/test/cgi/test_cgi_cookie.rb b/test/cgi/test_cgi_cookie.rb
index 6d31932..eadae45 100644
--- a/test/cgi/test_cgi_cookie.rb
+++ b/test/cgi/test_cgi_cookie.rb
@@ -65,6 +65,9 @@ class CGICookieTest < Test::Unit::TestCase
cookie = CGI::Cookie.new(h.merge('domain'=>'a.example.com'))
assert_equal('a.example.com', cookie.domain)
+ cookie = CGI::Cookie.new(h.merge('domain'=>'.example.com'))
+ assert_equal('.example.com', cookie.domain)
+
cookie = CGI::Cookie.new(h.merge('domain'=>'1.example.com'))
assert_equal('1.example.com', cookie.domain, 'enhanced by RFC 1123')
--
2.33.0

View File

@ -0,0 +1,55 @@
From b46d41c36380e04f6388970b5ef05c687f4d1819 Mon Sep 17 00:00:00 2001
From: Nobuyoshi Nakada <nobu@ruby-lang.org>
Date: Tue, 22 Nov 2022 11:05:52 +0900
Subject: [PATCH] Relax domain label restrictions
---
lib/cgi/cookie.rb | 2 +-
test/cgi/test_cgi_cookie.rb | 18 ++++++++++++++++++
2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/lib/cgi/cookie.rb b/lib/cgi/cookie.rb
index 4b11a6a..1a9c1a8 100644
--- a/lib/cgi/cookie.rb
+++ b/lib/cgi/cookie.rb
@@ -42,7 +42,7 @@ class CGI
TOKEN_RE = %r"\A[[!-~]&&[^()<>@,;:\\\"/?=\[\]{}]]+\z"
PATH_VALUE_RE = %r"\A[[ -~]&&[^;]]*\z"
- DOMAIN_VALUE_RE = %r"\A(?<label>[A-Za-z][-A-Za-z0-9]*[A-Za-z0-9])(?:\.\g<label>)*\z"
+ DOMAIN_VALUE_RE = %r"\A(?<label>(?!-)[-A-Za-z0-9]+(?<!-))(?:\.\g<label>)*\z"
# Create a new CGI::Cookie object.
#
diff --git a/test/cgi/test_cgi_cookie.rb b/test/cgi/test_cgi_cookie.rb
index 2f09d0f..e3ec4be 100644
--- a/test/cgi/test_cgi_cookie.rb
+++ b/test/cgi/test_cgi_cookie.rb
@@ -60,6 +60,24 @@ class CGICookieTest < Test::Unit::TestCase
end
+ def test_cgi_cookie_new_with_domain
+ h = {'name'=>'name1', 'value'=>'value1'}
+ cookie = CGI::Cookie.new('domain'=>'a.example.com', **h)
+ assert_equal('a.example.com', cookie.domain)
+
+ cookie = CGI::Cookie.new('domain'=>'1.example.com', **h)
+ assert_equal('1.example.com', cookie.domain, 'enhanced by RFC 1123')
+
+ assert_raise(ArgumentError) {
+ CGI::Cookie.new('domain'=>'-a.example.com', **h)
+ }
+
+ assert_raise(ArgumentError) {
+ CGI::Cookie.new('domain'=>'a-.example.com', **h)
+ }
+ end
+
+
def test_cgi_cookie_scriptname
cookie = CGI::Cookie.new('name1', 'value1')
assert_equal('', cookie.path)
--
2.33.0

View File

@ -33,7 +33,7 @@
Name: ruby
Version: %{ruby_version}
Release: 127
Release: 128
Summary: Object-oriented scripting language interpreter
License: (Ruby or BSD) and Public Domain and MIT and CC0 and zlib and UCD
URL: https://www.ruby-lang.org/en/
@ -176,6 +176,11 @@ Patch6004: backport-CVE-2019-19246.patch
Patch6005: backport-CVE-2019-16161.patch
Patch6006: backport-CVE-2019-16162.patch
Patch6007: backport-CVE-2019-16163.patch
Patch6008: backport-0001-CVE-2021-33621.patch
Patch6009: backport-0002-CVE-2021-33621.patch
Patch6010: backport-Relax-domain-label-restrictions.patch
Patch6011: backport-Fix-test_cgi_cookie_new_with_domain-to-pass-on-older.patch
Patch6012: backport-Loosen-the-domain-regex-to-accept-.-29.patch
Provides: %{name}-libs = %{version}-%{release}
Obsoletes: %{name}-libs < %{version}-%{release}
@ -1188,6 +1193,9 @@ make runruby TESTRUN_SCRIPT=%{SOURCE13}
%doc %{gem_dir}/gems/typeprof-%{typeprof_version}/testbed
%changelog
* Thu Jan 05 2023 shixuantong <shixuantong1@huawei.com> - 3.0.3-128
- fix CVE-2021-33621
* Thu Sep 22 2022 shixuantong <shixuantong1@huawei.com> - 3.0.3-127
- remove duplicated bigdecimal.rb file