summaryrefslogtreecommitdiff
path: root/spec/ruby/security
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/security')
-rw-r--r--spec/ruby/security/cve_2010_1330_spec.rb19
-rw-r--r--spec/ruby/security/cve_2011_4815_spec.rb47
-rw-r--r--spec/ruby/security/cve_2013_4164_spec.rb15
-rw-r--r--spec/ruby/security/cve_2018_16396_spec.rb7
-rw-r--r--spec/ruby/security/cve_2018_6914_spec.rb55
-rw-r--r--spec/ruby/security/cve_2018_8778_spec.rb10
-rw-r--r--spec/ruby/security/cve_2018_8779_spec.rb30
-rw-r--r--spec/ruby/security/cve_2018_8780_spec.rb43
-rw-r--r--spec/ruby/security/cve_2019_8321_spec.rb20
-rw-r--r--spec/ruby/security/cve_2019_8322_spec.rb24
-rw-r--r--spec/ruby/security/cve_2019_8323_spec.rb46
-rw-r--r--spec/ruby/security/cve_2019_8325_spec.rb46
-rw-r--r--spec/ruby/security/cve_2020_10663_spec.rb49
-rw-r--r--spec/ruby/security/cve_2024_49761_spec.rb7
14 files changed, 418 insertions, 0 deletions
diff --git a/spec/ruby/security/cve_2010_1330_spec.rb b/spec/ruby/security/cve_2010_1330_spec.rb
new file mode 100644
index 0000000000..8867fb7135
--- /dev/null
+++ b/spec/ruby/security/cve_2010_1330_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../spec_helper'
+
+describe "String#gsub" do
+ it "resists CVE-2010-1330 by raising an exception on invalid UTF-8 bytes" do
+ # This original vulnerability talked about KCODE, which is no longer
+ # used. Instead we are forcing encodings here. But I think the idea is the
+ # same - we want to check that Ruby implementations raise an error on
+ # #gsub on a string in the UTF-8 encoding but with invalid an UTF-8 byte
+ # sequence.
+
+ str = +"\xF6<script>"
+ str.force_encoding Encoding::BINARY
+ str.gsub(/</, "&lt;").should == "\xF6&lt;script>".b
+ str.force_encoding Encoding::UTF_8
+ -> {
+ str.gsub(/</, "&lt;")
+ }.should.raise(ArgumentError, /invalid byte sequence in UTF-8/)
+ end
+end
diff --git a/spec/ruby/security/cve_2011_4815_spec.rb b/spec/ruby/security/cve_2011_4815_spec.rb
new file mode 100644
index 0000000000..554e014a1f
--- /dev/null
+++ b/spec/ruby/security/cve_2011_4815_spec.rb
@@ -0,0 +1,47 @@
+require_relative '../spec_helper'
+
+describe :resists_cve_2011_4815, shared: true do
+ it "resists CVE-2011-4815 by having different hash codes in different processes" do
+ eval("(#{@method}).hash.to_s").should_not == ruby_exe("print (#{@method}).hash")
+ end
+end
+
+describe "Object#hash" do
+ it_behaves_like :resists_cve_2011_4815, 'Object.new'
+end
+
+describe "Integer#hash with a small value" do
+ it_behaves_like :resists_cve_2011_4815, '14'
+end
+
+describe "Integer#hash with a large value" do
+ it_behaves_like :resists_cve_2011_4815, '100000000000000000000000000000'
+end
+
+describe "Float#hash" do
+ it_behaves_like :resists_cve_2011_4815, '3.14'
+end
+
+describe "Rational#hash" do
+ it_behaves_like :resists_cve_2011_4815, 'Rational(1, 2)'
+end
+
+describe "Complex#hash" do
+ it_behaves_like :resists_cve_2011_4815, 'Complex(1, 2)'
+end
+
+describe "String#hash" do
+ it_behaves_like :resists_cve_2011_4815, '"abc"'
+end
+
+describe "Symbol#hash" do
+ it_behaves_like :resists_cve_2011_4815, ':a'
+end
+
+describe "Array#hash" do
+ it_behaves_like :resists_cve_2011_4815, '[1, 2, 3]'
+end
+
+describe "Hash#hash" do
+ it_behaves_like :resists_cve_2011_4815, '{a: 1, b: 2, c: 3}'
+end
diff --git a/spec/ruby/security/cve_2013_4164_spec.rb b/spec/ruby/security/cve_2013_4164_spec.rb
new file mode 100644
index 0000000000..d223b8fd5e
--- /dev/null
+++ b/spec/ruby/security/cve_2013_4164_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../spec_helper'
+
+require 'json'
+
+describe "String#to_f" do
+ it "resists CVE-2013-4164 by converting very long Strings to a Float" do
+ "1.#{'1'*1000000}".to_f.should be_close(1.1111111111111112, TOLERANCE)
+ end
+end
+
+describe "JSON.parse" do
+ it "resists CVE-2013-4164 by converting very long Strings to a Float" do
+ JSON.parse("[1.#{'1'*1000000}]").first.should be_close(1.1111111111111112, TOLERANCE)
+ end
+end
diff --git a/spec/ruby/security/cve_2018_16396_spec.rb b/spec/ruby/security/cve_2018_16396_spec.rb
new file mode 100644
index 0000000000..6f8a5d2f5d
--- /dev/null
+++ b/spec/ruby/security/cve_2018_16396_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../spec_helper'
+
+describe "Array#pack" do
+end
+
+describe "String#unpack" do
+end
diff --git a/spec/ruby/security/cve_2018_6914_spec.rb b/spec/ruby/security/cve_2018_6914_spec.rb
new file mode 100644
index 0000000000..f0aedb0dc6
--- /dev/null
+++ b/spec/ruby/security/cve_2018_6914_spec.rb
@@ -0,0 +1,55 @@
+require_relative '../spec_helper'
+
+require 'tempfile'
+require 'tmpdir'
+
+describe "CVE-2018-6914 is resisted by" do
+ before :each do
+ @tmpdir = ENV['TMPDIR']
+ @dir = tmp("CVE-2018-6914")
+ Dir.mkdir(@dir, 0700)
+ ENV['TMPDIR'] = @dir
+ @dir << '/'
+
+ @tempfile = nil
+ end
+
+ after :each do
+ ENV['TMPDIR'] = @tmpdir
+ @tempfile.close! if @tempfile
+ rm_r @dir
+ end
+
+ it "Tempfile.open by deleting separators" do
+ @tempfile = Tempfile.open(['../', 'foo'])
+ actual = @tempfile.path
+ File.absolute_path(actual).should.start_with?(@dir)
+ end
+
+ it "Tempfile.new by deleting separators" do
+ @tempfile = Tempfile.new('../foo')
+ actual = @tempfile.path
+ File.absolute_path(actual).should.start_with?(@dir)
+ end
+
+ it "Tempfile.create by deleting separators" do
+ actual = Tempfile.create('../foo') do |t|
+ t.path
+ end
+ File.absolute_path(actual).should.start_with?(@dir)
+ end
+
+ it "Dir.mktmpdir by deleting separators" do
+ actual = Dir.mktmpdir('../foo') do |path|
+ path
+ end
+ File.absolute_path(actual).should.start_with?(@dir)
+ end
+
+ it "Dir.mktmpdir with an array by deleting separators" do
+ actual = Dir.mktmpdir(['../', 'foo']) do |path|
+ path
+ end
+ File.absolute_path(actual).should.start_with?(@dir)
+ end
+end
diff --git a/spec/ruby/security/cve_2018_8778_spec.rb b/spec/ruby/security/cve_2018_8778_spec.rb
new file mode 100644
index 0000000000..bbdaac734c
--- /dev/null
+++ b/spec/ruby/security/cve_2018_8778_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../spec_helper'
+
+describe "String#unpack" do
+ it "resists CVE-2018-8778 by raising an exception when a position indicator is larger than a native integer" do
+ pos = (1 << PlatformGuard::POINTER_SIZE) - 99
+ -> {
+ "0123456789".unpack("@#{pos}C10")
+ }.should.raise(RangeError, /pack length too big/)
+ end
+end
diff --git a/spec/ruby/security/cve_2018_8779_spec.rb b/spec/ruby/security/cve_2018_8779_spec.rb
new file mode 100644
index 0000000000..6d573ea7fd
--- /dev/null
+++ b/spec/ruby/security/cve_2018_8779_spec.rb
@@ -0,0 +1,30 @@
+require_relative '../spec_helper'
+
+require 'socket'
+require 'tempfile'
+
+platform_is_not :windows do
+ describe "CVE-2018-8779 is resisted by" do
+ before :each do
+ tmpfile = Tempfile.new("s")
+ @path = tmpfile.path
+ tmpfile.close(true)
+ end
+
+ after :each do
+ File.unlink @path if @path && File.socket?(@path)
+ end
+
+ it "UNIXServer.open by raising an exception when there is a NUL byte" do
+ -> {
+ UNIXServer.open(@path+"\0")
+ }.should.raise(ArgumentError, /(path name|string) contains null byte/)
+ end
+
+ it "UNIXSocket.open by raising an exception when there is a NUL byte" do
+ -> {
+ UNIXSocket.open(@path+"\0")
+ }.should.raise(ArgumentError, /(path name|string) contains null byte/)
+ end
+ end
+end
diff --git a/spec/ruby/security/cve_2018_8780_spec.rb b/spec/ruby/security/cve_2018_8780_spec.rb
new file mode 100644
index 0000000000..dc8c6bcc9b
--- /dev/null
+++ b/spec/ruby/security/cve_2018_8780_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../spec_helper'
+
+describe "CVE-2018-8780 is resisted by" do
+ before :all do
+ @root = File.realpath(tmp(""))
+ end
+
+ it "Dir.glob by raising an exception when there is a NUL byte" do
+ -> {
+ Dir.glob([[@root, File.join(@root, "*")].join("\0")])
+ }.should.raise(ArgumentError, /(path name|string) contains null byte/)
+ end
+
+ it "Dir.entries by raising an exception when there is a NUL byte" do
+ -> {
+ Dir.entries(@root+"\0")
+ }.should.raise(ArgumentError, /(path name|string) contains null byte/)
+ end
+
+ it "Dir.foreach by raising an exception when there is a NUL byte" do
+ -> {
+ Dir.foreach(@root+"\0").to_a
+ }.should.raise(ArgumentError, /(path name|string) contains null byte/)
+ end
+
+ it "Dir.empty? by raising an exception when there is a NUL byte" do
+ -> {
+ Dir.empty?(@root+"\0")
+ }.should.raise(ArgumentError, /(path name|string) contains null byte/)
+ end
+
+ it "Dir.children by raising an exception when there is a NUL byte" do
+ -> {
+ Dir.children(@root+"\0")
+ }.should.raise(ArgumentError, /(path name|string) contains null byte/)
+ end
+
+ it "Dir.each_child by raising an exception when there is a NUL byte" do
+ -> {
+ Dir.each_child(@root+"\0").to_a
+ }.should.raise(ArgumentError, /(path name|string) contains null byte/)
+ end
+end
diff --git a/spec/ruby/security/cve_2019_8321_spec.rb b/spec/ruby/security/cve_2019_8321_spec.rb
new file mode 100644
index 0000000000..a8a86e7d97
--- /dev/null
+++ b/spec/ruby/security/cve_2019_8321_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../spec_helper'
+
+require 'rubygems'
+require 'rubygems/user_interaction'
+
+describe "CVE-2019-8321 is resisted by" do
+ it "sanitising verbose messages" do
+ ui = Class.new {
+ include Gem::UserInteraction
+ }.new
+ ui.should_receive(:say).with(".]2;nyan.")
+ verbose_before = Gem.configuration.verbose
+ begin
+ Gem.configuration.verbose = :really_verbose
+ ui.verbose("\e]2;nyan\a")
+ ensure
+ Gem.configuration.verbose = verbose_before
+ end
+ end
+end
diff --git a/spec/ruby/security/cve_2019_8322_spec.rb b/spec/ruby/security/cve_2019_8322_spec.rb
new file mode 100644
index 0000000000..dbc273f313
--- /dev/null
+++ b/spec/ruby/security/cve_2019_8322_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../spec_helper'
+
+ruby_version_is ""..."4.0" do
+
+ require 'yaml'
+ require 'rubygems'
+ require 'rubygems/safe_yaml'
+ require 'rubygems/commands/owner_command'
+
+ describe "CVE-2019-8322 is resisted by" do
+ it "sanitising owner names" do
+ command = Gem::Commands::OwnerCommand.new
+ def command.rubygems_api_request(*args)
+ Struct.new(:body).new("---\n- email: \"\e]2;nyan\a\"\n handle: handle\n id: id\n")
+ end
+ def command.with_response(response)
+ yield response
+ end
+ command.should_receive(:say).with("Owners for gem: name")
+ command.should_receive(:say).with("- .]2;nyan.")
+ command.show_owners "name"
+ end
+ end
+end
diff --git a/spec/ruby/security/cve_2019_8323_spec.rb b/spec/ruby/security/cve_2019_8323_spec.rb
new file mode 100644
index 0000000000..49a31a6682
--- /dev/null
+++ b/spec/ruby/security/cve_2019_8323_spec.rb
@@ -0,0 +1,46 @@
+require_relative '../spec_helper'
+
+require 'optparse'
+
+require 'rubygems'
+require 'rubygems/gemcutter_utilities'
+
+describe "CVE-2019-8323 is resisted by" do
+ describe "sanitising the body" do
+ it "for success codes" do
+ cutter = Class.new {
+ include Gem::GemcutterUtilities
+ }.new
+ klass = if defined?(Gem::Net::HTTPSuccess)
+ Gem::Net::HTTPSuccess
+ else
+ Net::HTTPSuccess
+ end
+ response = klass.new(nil, nil, nil)
+ def response.body
+ "\e]2;nyan\a"
+ end
+ cutter.should_receive(:say).with(".]2;nyan.")
+ cutter.with_response response
+ end
+
+ it "for error codes" do
+ cutter = Class.new {
+ include Gem::GemcutterUtilities
+ }.new
+ def cutter.terminate_interaction(n)
+ end
+ klass = if defined?(Gem::Net::HTTPNotFound)
+ Gem::Net::HTTPNotFound
+ else
+ Net::HTTPNotFound
+ end
+ response = klass.new(nil, nil, nil)
+ def response.body
+ "\e]2;nyan\a"
+ end
+ cutter.should_receive(:say).with(".]2;nyan.")
+ cutter.with_response response
+ end
+ end
+end
diff --git a/spec/ruby/security/cve_2019_8325_spec.rb b/spec/ruby/security/cve_2019_8325_spec.rb
new file mode 100644
index 0000000000..bbddb3a6ce
--- /dev/null
+++ b/spec/ruby/security/cve_2019_8325_spec.rb
@@ -0,0 +1,46 @@
+require_relative '../spec_helper'
+
+require 'rubygems'
+require 'rubygems/command_manager'
+
+describe "CVE-2019-8325 is resisted by" do
+ describe "sanitising error message components" do
+ before :each do
+ @ui = Gem::SilentUI.new
+ end
+
+ after :each do
+ @ui.close
+ end
+
+ it "for the 'while executing' message" do
+ manager = Gem::CommandManager.new
+ manager.ui = @ui
+ def manager.process_args(args, build_args)
+ raise StandardError, "\e]2;nyan\a"
+ end
+ def manager.terminate_interaction(n)
+ end
+ manager.should_receive(:alert_error).with("While executing gem ... (StandardError)\n .]2;nyan.")
+ manager.run nil, nil
+ end
+
+ it "for the 'invalid option' message" do
+ manager = Gem::CommandManager.new
+ def manager.terminate_interaction(n)
+ end
+ manager.should_receive(:alert_error).with("Invalid option: --.]2;nyan.. See 'gem --help'.")
+ manager.process_args ["--\e]2;nyan\a"], nil
+ end
+
+ it "for the 'loading command' message" do
+ manager = Gem::CommandManager.new
+ manager.ui = @ui
+ def manager.require(x)
+ raise 'foo'
+ end
+ manager.should_receive(:alert_error).with("Loading command: .]2;nyan. (RuntimeError)\n\tfoo")
+ manager.send :load_and_instantiate, "\e]2;nyan\a"
+ end
+ end
+end
diff --git a/spec/ruby/security/cve_2020_10663_spec.rb b/spec/ruby/security/cve_2020_10663_spec.rb
new file mode 100644
index 0000000000..7f42c40742
--- /dev/null
+++ b/spec/ruby/security/cve_2020_10663_spec.rb
@@ -0,0 +1,49 @@
+require_relative '../spec_helper'
+
+ruby_version_is ""..."4.0" do
+ require 'json'
+
+ module JSONSpecs
+ class MyClass
+ def initialize(foo)
+ @foo = foo
+ end
+
+ def self.json_create(hash)
+ new(*hash['args'])
+ end
+
+ def to_json(*args)
+ { 'json_class' => self.class.name, 'args' => [ @foo ] }.to_json(*args)
+ end
+ end
+ end
+
+ guard -> {
+ JSON.const_defined?(:Pure) or
+ version_is(JSON::VERSION, '2.3.0'...'2.11.0')
+ } do
+ describe "CVE-2020-10663 is resisted by" do
+ it "only creating custom objects if passed create_additions: true or using JSON.load" do
+ obj = JSONSpecs::MyClass.new("bar")
+ JSONSpecs::MyClass.should.json_creatable?
+ json = JSON.dump(obj)
+
+ JSON.parse(json, create_additions: true).class.should == JSONSpecs::MyClass
+ JSON(json, create_additions: true).class.should == JSONSpecs::MyClass
+ if version_is(JSON::VERSION, '2.8.0')
+ warning = /\Wcreate_additions:\s*true\W\s+is\s+deprecated/
+ else
+ warning = ''
+ end
+ -> {
+ JSON.load(json).class.should == JSONSpecs::MyClass
+ }.should output_to_fd(warning, STDERR)
+
+ JSON.parse(json).class.should == Hash
+ JSON.parse(json, nil).class.should == Hash
+ JSON(json).class.should == Hash
+ end
+ end
+ end
+end
diff --git a/spec/ruby/security/cve_2024_49761_spec.rb b/spec/ruby/security/cve_2024_49761_spec.rb
new file mode 100644
index 0000000000..c66b438eef
--- /dev/null
+++ b/spec/ruby/security/cve_2024_49761_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../spec_helper'
+
+describe "CVE-2024-49761 is resisted by" do
+ it "the Regexp implementation handling that regular expression in linear time" do
+ Regexp.linear_time?(/&#0*((?:\d+)|(?:x[a-fA-F0-9]+));/).should == true
+ end
+end