RSS

Patching gems for security vulnerabilities with gem-patch

gem-patch is a RubyGems plugin that helps you to patch gems. You can use it to apply security fixes or cherry-pick commits you want to apply to your .gem files. I use it to test whether the upstream commits containing vulnerability fixes apply cleanly on older gem releases so I can prepare fixed builds of those gems in Fedora. Here is how one can do that.

As an example let’s look on recent upstream patches for CVE-2015-3226 and CVE-2015-3227. The patches from upstream are commits for ActiveSupport version 4.2.1 (the latest release in a given supported branch). That make sense. But you might be on different version and you might be in a position when updating all related Rails gems is not a way to go. That’s also the case of Fedora 22 which features ActiveSupport 4.2.0.

The original purpose of gem-patch is to create (or apply) patches with ease. Let’s look how I test whether the released patches work with 4.2.0:

$ gem patch activesupport-4.2.0.gem -c./test rubygem-activesupport-4.2.2-CVE-2015-3226.patch \
 -p2 --dry-run --verbose

Here is a partial output:

Applying patch rubygem-activesupport-4.2.2-CVE-2015-3226.patch
Path to the patch to apply: /home/strzibny/fedora/rubygem-activesupport/rubygem-activesupport-4.2.2-CVE-2015-3226.patch
Hmm...  Looks like a unified diff to me...
The text leading up to this was:
--------------------------
|From ca91e927557906592f39ad5c07da25eefa9d8e61 Mon Sep 17 00:00:00 2001
|From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?=
| <rafaelmfranca@gmail.com>
|Date: Mon, 15 Jun 2015 15:23:01 -0300
|Subject: [PATCH] Escape HTML entities in JSON keys
|
|Fixes CVE-2015-3226
|---
| activesupport/lib/active_support/json/encoding.rb | 4 ++++
| activesupport/test/json/encoding_test.rb          | 7 +++++++
| 2 files changed, 11 insertions(+)
|
|diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb
|index c0ac5af..fdd63d9 100644
|--- a/activesupport/lib/active_support/json/encoding.rb
|+++ b/activesupport/lib/active_support/json/encoding.rb
--------------------------
patching file lib/active_support/json/encoding.rb
Using Plan A...
Hunk #1 succeeded at 58.
Hmm...  The next patch looks like a unified diff to me...
The text leading up to this was:
--------------------------
|diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb
|index 7e976aa..0cbe2da 100644
|--- a/activesupport/test/json/encoding_test.rb
|+++ b/activesupport/test/json/encoding_test.rb
--------------------------
patching file test/json/encoding_test.rb
Using Plan A...
Hunk #1 succeeded at 143 (offset -3 lines).
Hmm...  Ignoring the trailing garbage.
done
  Successfully built RubyGem
  Name: activesupport
  Version: 4.2.0
  File: activesupport-4.2.0.gem
Succesfully patched with rubygem-activesupport-4.2.2-CVE-2015-3226.patch

Nice, it works. And we also see how to apply the patch cleanly (which is important if you want to make sure that any additional files are created by patch program). For that we just change the line 146 to 143:

From ca91e927557906592f39ad5c07da25eefa9d8e61 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?=
 <rafaelmfranca@gmail.com>
Date: Mon, 15 Jun 2015 15:23:01 -0300
Subject: [PATCH] Escape HTML entities in JSON keys

Fixes CVE-2015-3226
---
 activesupport/lib/active_support/json/encoding.rb | 4 ++++
 activesupport/test/json/encoding_test.rb          | 7 +++++++
 2 files changed, 11 insertions(+)

diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb
index c0ac5af..fdd63d9 100644

...

diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb
index 7e976aa..0cbe2da 100644
--- a/activesupport/test/json/encoding_test.rb
+++ b/activesupport/test/json/encoding_test.rb
@@ -143,6 +143,13 @@ class TestJSONEncoding < ActiveSupport::TestCase
     assert_equal %({\"a\":\"b\",\"c\":\"d\"}), sorted_json(ActiveSupport::JSON.encode(:a => :b, :c => :d))
   end

+  def test_hash_keys_encoding
+    ActiveSupport.escape_html_entities_in_json = true
+    assert_equal "{\"\\u003c\\u003e\":\"\\u003c\\u003e\"}", ActiveSupport::JSON.encode("<>" => "<>")
+  ensure
+    ActiveSupport.escape_html_entities_in_json = false
+  end
+
   def test_utf8_string_encoded_properly
     result = ActiveSupport::JSON.encode('€2.99')
     assert_equal '"€2.99"', result
--

We can run gem patch again and see that the patch applied cleanly.

...
|diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb
|index 7e976aa..0cbe2da 100644
|--- a/activesupport/test/json/encoding_test.rb
|+++ b/activesupport/test/json/encoding_test.rb
--------------------------
patching file test/json/encoding_test.rb
Using Plan A...
Hunk #1 succeeded at 143.
Hmm...  Ignoring the trailing garbage.
done
  Successfully built RubyGem
  Name: activesupport
  Version: 4.2.0
  File: activesupport-4.2.0.gem
Succesfully patched with rubygem-activesupport-4.2.2-CVE-2015-3226.patch

But there was one more vulnerability reported in the announcement. To apply both at the same time we just add it as another argument:

$ gem patch activesupport-4.2.0.gem -ctest -p2 --dry-run --verbose \
  rubygem-activesupport-4.2.2-CVE-2015-3226.patch rubygem-activesupport-4.2.2-CVE-2015-3227.patch

That’s it. We can remove the --dry-run option now if we want to patch the given .gem file or include our fixed patches in the build process (specfiles in case of Fedora builds).

← THIS CHRISTMAS

I am writing an introductory book on web application deployment. Networking, processes, systemd, backups, and all your usual suspects.

Open →