safe_intern 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c83f69632b7a1ac53c90593819f74884dd14d723
4
+ data.tar.gz: 239e8d074b08c60a7959c48f7e87eb7e2628e333
5
+ SHA512:
6
+ metadata.gz: 5901bf0d86098b00d30bcb688d72dd4c14757e7b2105a8c56ab06b8776536fd9b7a3b4b12fe72eac9b1b19052fd55736a2f8fb553f1e6cf20bd55597e885f9f6
7
+ data.tar.gz: 10f41a368bb3f9078c9dde6e8abc5def0439a24b93062f39d22472504c62b77913cc4c1d5caa53866630d3c11466e831ed7065e7ef6c3bd8537f702131744d20
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ group :development,:test do
4
+ gem 'rake-compiler'
5
+ gem 'rake'
6
+ gem 'rspec'
7
+ gem 'rubocop'
8
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Jan Rusnacko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,116 @@
1
+ # SafeIntern
2
+
3
+ Safe implemetation of String#intern and String#to_sym methods.
4
+
5
+ ## Description
6
+
7
+ One of the common security issues of Ruby applications is calling `intern` or
8
+ `to_sym` on strings from untrusted source. Since symbols are not garbage
9
+ collected in Ruby, yet take up some memory, this allows attacker to mount
10
+ Denial of Service attack. Just by feeding the application with large number of
11
+ strings and creating large number of symbols, memory consumption of the Ruby
12
+ process would grow till machine runs out of memory.
13
+
14
+ One approach to solving this problem is to prevent calls to .intern on
15
+ untrusted strings by whitelisting expected inputs:
16
+
17
+ whitelist(untrusted_string).to_sym
18
+
19
+ This gets tedious very quickly - developer has to know all of the permitted
20
+ values beforehand and has to maintain the list.
21
+
22
+ Another approach is to allow conversion from string to symbol, but prevent
23
+ allocation of new (previously unseen) symbol. This works most of the time since
24
+ all useful symbols are already allocated in application when its source is
25
+ parsed.
26
+
27
+ ## How it works
28
+
29
+ Symbols are stored in a structure `global_symbols` which maintains mapping
30
+ between frozen strings that are the "name" of symbol and numeric IDs, which
31
+ are used by Ruby interpreter. All currently allocated Symbols can be listed
32
+ by calling `Symbol.all_symbols`.
33
+
34
+ Starting from Ruby 2.0, API provides `rb_check_id` function, which allows to
35
+ query `global_symbols` for particular symbol without allocating it. This is
36
+ much more efficient than doing something like
37
+
38
+ Symbol.all_symbols.map(&:to_s).include?(untrusted_string)
39
+
40
+ With the ability to query for known symbols, `intern` and `to_sym` methods can
41
+ be patched. There are two possible behaviours when called on unknown string:
42
+
43
+ * return nil
44
+ * raise exception
45
+
46
+ This gem provides implementation of both in `SafeIntern::ExceptionPatch` and
47
+ `SafeIntern::NilPatch` modules.
48
+
49
+ ## Usage
50
+
51
+ 1. Install using gem command:
52
+
53
+ $ gem install safe_intern
54
+
55
+ or include in Gemfile:
56
+
57
+ gem 'safe_intern'
58
+
59
+ 2. Patch the String:
60
+
61
+ require 'safe_intern/string'
62
+
63
+ This will patch `String#intern` and `String#to_sym` methods to throw
64
+ exception when new Symbol would be allocated. To return nil instead, patch
65
+ the String with
66
+
67
+ require 'safe_intern'
68
+
69
+ class ::String
70
+ prepend SafeIntern::NilPatch
71
+ end
72
+
73
+ Prepending the `SafeIntern::NilPatch` module is important, since including it
74
+ would result in the wrong order of method lookup.
75
+
76
+ Only particular strings can be patched too:
77
+
78
+ require 'safe_intern'
79
+
80
+ unstrusted_string.extend(SafeIntern::ExceptionPatch)
81
+
82
+ Calling `.to_sym` and `.intern` on unstrusted string that would result in
83
+ new symbol allocation returns nil or throws exception:
84
+
85
+ > untrusted_string.extend(SafeIntern::NilPatch)
86
+ => "does_not_exist"
87
+ > untrusted_string.intern
88
+ => nil
89
+ > untrusted_string.extend(SafeIntern::ExceptionPatch)
90
+ => "does_not_exist"
91
+ > untrusted_string.intern
92
+ UnsafeInternException: UnsafeInternException
93
+ from /home/jrusnack/.gem/ruby/gems/safe_intern-1.0.0/lib/safe_intern/exception_patch.rb:7:in `intern'
94
+ from (irb):4
95
+ from /usr/bin/irb:12:in `<main>'
96
+
97
+ When String class is patched, creating new symbol from trusted string is
98
+ possible with
99
+
100
+ trusted_string.intern(:allow_unsafe)
101
+
102
+ ## Requirements
103
+ * [Ruby] >= 2.0.0
104
+
105
+ ## See also
106
+
107
+ Similar functionality is provided by [Symbol Lookup] gem. Read about addition
108
+ of new method to query for already allocated Symbols in [Issue 7854].
109
+
110
+ ## License
111
+ [SafeIntern] is released under the MIT License.
112
+
113
+ [Ruby]: http://www.ruby-lang.org
114
+ [Symbol Lookup]: https://github.com/phluid61/symbol_lookup-gem
115
+ [Issue 7854]: https://bugs.ruby-lang.org/issues/7854
116
+ [SafeIntern] https://github.com/jrusnack/safe_intern
data/Rakefile ADDED
@@ -0,0 +1,31 @@
1
+ require 'rspec/core/rake_task'
2
+ require 'rake/extensiontask'
3
+ require 'rubocop/rake_task'
4
+ require 'benchmark'
5
+ $:.unshift File.expand_path('../lib', __FILE__)
6
+ require 'safe_intern'
7
+
8
+ RSpec::Core::RakeTask.new
9
+
10
+ Rake::ExtensionTask.new "symbol_defined" do |ext|
11
+ end
12
+
13
+ Rubocop::RakeTask.new
14
+
15
+ desc 'Benchmark implemented methods'
16
+ task :benchmark do
17
+ i = 2000000
18
+
19
+ Benchmark.bm(22) do |bm|
20
+ string = 'String'
21
+ bm.report("unpatched:") { i.times do string.intern end }
22
+
23
+ str = string.clone.extend(SafeIntern::NilPatch)
24
+ bm.report("Nil patch:") { i.times do str.intern end }
25
+ bm.report("Nil patch(allow):") { i.times do str.intern(:allow_unsafe) end }
26
+
27
+ str = string.clone.extend(SafeIntern::ExceptionPatch)
28
+ bm.report("Exception patch:") { i.times do str.intern end }
29
+ bm.report("Exception patch(allow):") { i.times do str.intern(:allow_unsafe) end }
30
+ end
31
+ end
@@ -0,0 +1,8 @@
1
+ # Copyright (C) 2014 Jan Rusnacko
2
+ #
3
+ # This copyrighted material is made available to anyone wishing to use,
4
+ # modify, copy, or redistribute it subject to the terms and conditions of the
5
+ # MIT license.
6
+
7
+ require 'mkmf'
8
+ create_makefile('symbol_defined')
@@ -0,0 +1,26 @@
1
+ /* Copyright (C) 2014 Jan Rusnacko
2
+ *
3
+ * This copyrighted material is made available to anyone wishing to use,
4
+ * modify, copy, or redistribute it subject to the terms and conditions of the
5
+ * MIT license.
6
+ */
7
+
8
+ #include "ruby.h"
9
+
10
+ VALUE
11
+ symbol_defined(VALUE self, VALUE str)
12
+ {
13
+ if(rb_check_id(&str)) {
14
+ return Qtrue;
15
+ } else {
16
+ return Qfalse;
17
+ }
18
+ }
19
+
20
+ VALUE cSafeIntern;
21
+
22
+ void Init_symbol_defined()
23
+ {
24
+ cSafeIntern = rb_define_module("SafeIntern");
25
+ rb_define_module_function(cSafeIntern, "symbol_defined?", symbol_defined, 1);
26
+ }
@@ -0,0 +1,25 @@
1
+ # Copyright (C) 2014 Jan Rusnacko
2
+ #
3
+ # This copyrighted material is made available to anyone wishing to use,
4
+ # modify, copy, or redistribute it subject to the terms and conditions of the
5
+ # MIT license.
6
+
7
+ module SafeIntern
8
+ module ExceptionPatch
9
+ def intern(allow_unsafe = nil)
10
+ if allow_unsafe == :allow_unsafe || SafeIntern.symbol_defined?(self)
11
+ super()
12
+ else
13
+ fail UnsafeInternException
14
+ end
15
+ end
16
+
17
+ def to_sym(allow_unsafe = nil)
18
+ if allow_unsafe == :allow_unsafe || SafeIntern.symbol_defined?(self)
19
+ super()
20
+ else
21
+ fail UnsafeInternException
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ # Copyright (C) 2014 Jan Rusnacko
2
+ #
3
+ # This copyrighted material is made available to anyone wishing to use,
4
+ # modify, copy, or redistribute it subject to the terms and conditions of the
5
+ # MIT license.
6
+
7
+ module SafeIntern
8
+ module NilPatch
9
+ def intern(allow_unsafe = nil)
10
+ if allow_unsafe == :allow_unsafe || SafeIntern.symbol_defined?(self)
11
+ super()
12
+ else
13
+ nil
14
+ end
15
+ end
16
+
17
+ def to_sym(allow_unsafe = nil)
18
+ if allow_unsafe == :allow_unsafe || SafeIntern.symbol_defined?(self)
19
+ super()
20
+ else
21
+ nil
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,16 @@
1
+ # Copyright (C) 2014 Jan Rusnacko
2
+ #
3
+ # This copyrighted material is made available to anyone wishing to use,
4
+ # modify, copy, or redistribute it subject to the terms and conditions of the
5
+ # MIT license.
6
+
7
+ require 'safe_intern'
8
+
9
+ # Patch String class to throw exception when new Symbol is created by intern
10
+ # or to_sym methods.
11
+ #
12
+ # Usage: require 'safe_intern/string'
13
+ #
14
+ class ::String
15
+ prepend SafeIntern::ExceptionPatch
16
+ end
@@ -0,0 +1,7 @@
1
+ # Copyright (C) 2014 Jan Rusnacko
2
+ #
3
+ # This copyrighted material is made available to anyone wishing to use,
4
+ # modify, copy, or redistribute it subject to the terms and conditions of the
5
+ # MIT license.
6
+
7
+ class UnsafeInternException < RuntimeError; end
@@ -0,0 +1,10 @@
1
+ # Copyright (C) 2014 Jan Rusnacko
2
+ #
3
+ # This copyrighted material is made available to anyone wishing to use,
4
+ # modify, copy, or redistribute it subject to the terms and conditions of the
5
+ # MIT license.
6
+
7
+ require 'symbol_defined'
8
+ require 'safe_intern/exception_patch'
9
+ require 'safe_intern/nil_patch'
10
+ require 'safe_intern/unsafe_intern_exception'
@@ -0,0 +1,14 @@
1
+ Gem::Specification.new do |gem|
2
+ gem.name = 'safe_intern'
3
+ gem.version = '1.0.0'
4
+ gem.date = '2014-03-20'
5
+ gem.summary = 'Safe String#intern'
6
+ gem.description = 'Safe implementation of String#intern'
7
+ gem.authors = ["Jan Rusnacko"]
8
+ gem.email = 'rusnackoj@gmail.com'
9
+ gem.files = `git ls-files`.split($/)
10
+ gem.extensions = 'ext/symbol_defined/extconf.rb'
11
+ gem.required_ruby_version = ['>= 2.0']
12
+ gem.homepage = 'https://github.com/jrusnack/safe_intern'
13
+ gem.license = 'MIT'
14
+ end
@@ -0,0 +1,17 @@
1
+ # Copyright (C) 2014 Jan Rusnacko
2
+ #
3
+ # This copyrighted material is made available to anyone wishing to use,
4
+ # modify, copy, or redistribute it subject to the terms and conditions of the
5
+ # MIT license.
6
+
7
+ require 'spec_helper'
8
+
9
+ describe SafeIntern::ExceptionPatch do
10
+ context 'intern' do
11
+ it_behaves_like 'safe-intern', SafeIntern::ExceptionPatch, :intern
12
+ end
13
+
14
+ context 'to_sym' do
15
+ it_behaves_like 'safe-intern', SafeIntern::ExceptionPatch, :to_sym
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # Copyright (C) 2014 Jan Rusnacko
2
+ #
3
+ # This copyrighted material is made available to anyone wishing to use,
4
+ # modify, copy, or redistribute it subject to the terms and conditions of the
5
+ # MIT license.
6
+
7
+ require 'spec_helper'
8
+
9
+ describe SafeIntern::NilPatch do
10
+ context '#intern' do
11
+ it_behaves_like 'safe-intern', SafeIntern::NilPatch, :intern
12
+ end
13
+
14
+ context '#to_sym' do
15
+ it_behaves_like 'safe-intern', SafeIntern::NilPatch, :to_sym
16
+ end
17
+ end
@@ -0,0 +1,29 @@
1
+ # Copyright (C) 2014 Jan Rusnacko
2
+ #
3
+ # This copyrighted material is made available to anyone wishing to use,
4
+ # modify, copy, or redistribute it subject to the terms and conditions of the
5
+ # MIT license.
6
+
7
+ shared_examples 'safe-intern' do |patch, method|
8
+ it 'should convert to Symbol if it already exists' do
9
+ 'Object'.extend(patch).send(method).should be_eql(:Object)
10
+ end
11
+
12
+ it 'should not create new Symbol if it does not already exist' do
13
+ case patch.name
14
+ when 'SafeIntern::NilPatch'
15
+ 'DoesNotExist'.extend(patch).send(method).should be_nil
16
+ when 'SafeIntern::ExceptionPatch'
17
+ expect { 'DoesNotExist'.extend(patch).send(method) }.to raise_error
18
+ else
19
+ fail
20
+ end
21
+ Symbol.all_symbols.map(&:to_s).should_not include('DoesNotExist')
22
+ end
23
+
24
+ it 'should accept :allow_unsafe as optional parameter' do
25
+ rnd = rand(100_000).to_s
26
+ Symbol.all_symbols.map(&:to_s).should_not include(rnd)
27
+ rnd.extend(patch).send(method, :allow_unsafe).should be_eql(rnd.to_sym)
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ # Copyright (C) 2014 Jan Rusnacko
2
+ #
3
+ # This copyrighted material is made available to anyone wishing to use,
4
+ # modify, copy, or redistribute it subject to the terms and conditions of the
5
+ # MIT license.
6
+
7
+ require 'safe_intern'
8
+ require 'safe_intern_helper'
9
+
10
+ RSpec.configure do |config|
11
+ config.color_enabled = true
12
+ config.formatter = :documentation
13
+ end
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: safe_intern
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jan Rusnacko
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-20 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Safe implementation of String#intern
14
+ email: rusnackoj@gmail.com
15
+ executables: []
16
+ extensions:
17
+ - ext/symbol_defined/extconf.rb
18
+ extra_rdoc_files: []
19
+ files:
20
+ - Gemfile
21
+ - LICENSE
22
+ - README.md
23
+ - Rakefile
24
+ - ext/symbol_defined/extconf.rb
25
+ - ext/symbol_defined/symbol_defined.c
26
+ - lib/safe_intern.rb
27
+ - lib/safe_intern/exception_patch.rb
28
+ - lib/safe_intern/nil_patch.rb
29
+ - lib/safe_intern/string.rb
30
+ - lib/safe_intern/unsafe_intern_exception.rb
31
+ - safe_intern.gemspec
32
+ - spec/safe_intern/exception_patch_spec.rb
33
+ - spec/safe_intern/nil_patch_spec.rb
34
+ - spec/safe_intern_helper.rb
35
+ - spec/spec_helper.rb
36
+ homepage: https://github.com/jrusnack/safe_intern
37
+ licenses:
38
+ - MIT
39
+ metadata: {}
40
+ post_install_message:
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '2.0'
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ requirements: []
55
+ rubyforge_project:
56
+ rubygems_version: 2.2.2
57
+ signing_key:
58
+ specification_version: 4
59
+ summary: Safe String#intern
60
+ test_files: []