ruby / webrick

HTTP server toolkit

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

WEBrick 1.4.2 differs from Ruby 2.6.6's WEBrick 1.4.2

headius opened this issue · comments

It appears that changes have been made to the "WEBrick 1.4.2" on the CRuby ruby_2_6 branch that were never released as an update to the webrick gem's 1.4.x versions.

Among the missing changes are the fixes for the "response-splitting" CVEs:

There are no references to 2017-17742 in webrick's master branch, so I am unsure which commit fixes it. The 2019-16254 CVE fixes were merged in #32, which appears to only be in WEBrick 1.6.0.

CRuby 2.6.6 still reports that it ships WEBrick 1.4.2, but it clearly has significant differences from the released gem. A full diff from WEBrick's v1.4.2 tag and CRuby's ruby_2_6 branch is provided below:

https://gist.github.com/headius/cb184868d6d8b709b8a3f62cd0c275eb

As it stands, I do not know what version of WEBrick the copy in CRuby 2.6.6 corresponds to. It appears to be a hybrid of many different patches, but it is clearly *not 1.4.2.

Changes to gemified libraries included in CRuby must be released via the gem or else it is impossible to track the actual released version of these libraries. Users need to know which version of these libraries they are actually running.

In addition, JRuby exclusively uses released gems for WEBrick and other libraries that have been gemified. Because the WEBRick sources have diverged without an updated release, we fail two specs from ruby/spec/security:

6)
WEBrick resists CVE-2017-17742 for a response splitting headers FAILED
Expected "200" == "500"
to be truthy but was false
/Users/headius/projects/jruby/spec/ruby/security/cve_2017_17742_spec.rb:17:in `block in <main>'
org/jruby/RubyBasicObject.java:2695:in `instance_exec'
org/jruby/RubyArray.java:4555:in `all?'
org/jruby/RubyArray.java:1815:in `each'
org/jruby/RubyArray.java:1815:in `each'
/Users/headius/projects/jruby/spec/ruby/security/cve_2017_17742_spec.rb:7:in `<main>'
org/jruby/RubyKernel.java:1078:in `load'
org/jruby/RubyBasicObject.java:2695:in `instance_exec'
org/jruby/RubyArray.java:1815:in `each'
                                                                                             
7)
WEBrick resists CVE-2017-17742 for a response splitting cookie headers FAILED
Expected "200" == "500"
to be truthy but was false
/Users/headius/projects/jruby/spec/ruby/security/cve_2017_17742_spec.rb:30:in `block in <main>'
org/jruby/RubyBasicObject.java:2695:in `instance_exec'
org/jruby/RubyArray.java:4555:in `all?'
org/jruby/RubyArray.java:1815:in `each'
org/jruby/RubyArray.java:1815:in `each'
/Users/headius/projects/jruby/spec/ruby/security/cve_2017_17742_spec.rb:7:in `<main>'
org/jruby/RubyKernel.java:1078:in `load'
org/jruby/RubyBasicObject.java:2695:in `instance_exec'
org/jruby/RubyArray.java:1815:in `each'

These specs fail because the released WEBrick 1.4.2 still ships the vulnerable code.

WEBrick may need a new 1.4.x release that corresponds to the sources in CRuby 2.6.6.

CRuby will need new releases of affected branches that report the correct version of WEBrick that they include.

In the interim, we (JRuby) would appreciate help determining what version of WEBrick to include in our Ruby 2.5 and Ruby 2.6-compatible branches (for JRuby 9.2.12 this week and JRuby 9.3 later this summer).

If I'm reading the sources right, it does not appear that the WEBrick shipped with Ruby 2.5.8 has these CVE patches either.

Ruby 2.5.8 claims to ship WEBrick 1.4.2, but it also has extensive diffs: https://gist.github.com/headius/9e70146870d5e6f329a5857fe91c8948

@hsbt What should we do here? Should we prepare a 1.4.3 release with all expected patches, and make sure Ruby 2.5.9 and 2.6.7 include it? I'm guessing that requires coordination with @unak.

I think on a going-forward basis, we should commit to making sure the webrick gem in Ruby releases matches the same gem you can download from rubygems.org.

On the JRuby side, we have just gone ahead and updated WEBrick in JRuby 9.2.12.0 to use the 1.6.0 release, since no other releases appeared to have all the CVE fixes we needed.

Ack

Should we prepare a 1.4.3 release with all expected patches, and make sure Ruby 2.5.9 and 2.6.7 include it?

I think it's the good plan to resolve the vulnerable code. Can anyone create the patchsets for this? I will coordinate its releases.

@hsbt #53 merges changes to webrick 1.4.2 from Ruby 2.6.6 and adds a few test changes to ensure the tests pass on 2.3-2.6. I think it is suitable to release as 1.4.3, and backport to Ruby 2.5 and 2.6.

I will release 1.4.3 from v1.4 branch soon.

I confirmed the difference between v1.4.2 and v1.4.3 is only version guard for the standalone gem.

$ g d test
diff --git test/webrick/test_httpproxy.rb test/webrick/test_httpproxy.rb
index a9f6f7d610..504eb1f915 100644
--- test/webrick/test_httpproxy.rb
+++ test/webrick/test_httpproxy.rb
@@ -213,7 +213,7 @@ def test_big_bodies
         end
       end
     end
-  end
+  end if RUBY_VERSION >= '2.5'

   def make_certificate(key, cn)
     subject = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=#{cn}")
diff --git test/webrick/test_httpserver.rb test/webrick/test_httpserver.rb
index a6e70da7e8..2e5d44940c 100644
--- test/webrick/test_httpserver.rb
+++ test/webrick/test_httpserver.rb
@@ -253,7 +253,7 @@ def test_callbacks
       server.virtual_host(WEBrick::HTTPServer.new(vhost_config))

       Thread.pass while server.status != :Running
-      sleep 1 if RubyVM::MJIT.enabled? # server.status behaves unexpectedly with --jit-wait
+      sleep 1 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # server.status behaves unexpectedly with --jit-wait
       assert_equal(1, started, log.call)
       assert_equal(0, stopped, log.call)
       assert_equal(0, accepted, log.call)
diff --git test/webrick/test_server.rb test/webrick/test_server.rb
index 5f7f3a0b58..8162a186db 100644
--- test/webrick/test_server.rb
+++ test/webrick/test_server.rb
@@ -65,7 +65,7 @@ def test_callbacks
     }
     TestWEBrick.start_server(Echo, config){|server, addr, port, log|
       true while server.status != :Running
-      sleep 1 if RubyVM::MJIT.enabled? # server.status behaves unexpectedly with --jit-wait
+      sleep 1 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # server.status behaves unexpectedly with --jit-wait
       assert_equal(1, started, log.call)
       assert_equal(0, stopped, log.call)
       assert_equal(0, accepted, log.call)