github-safegem 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/VERSION.yml +4 -0
- data/bin/safegem +85 -0
- data/lib/safegem/lazy_dir.rb +39 -0
- data/lib/safegem/security.rb +83 -0
- data/lib/safegem.rb +2 -0
- data/test/git_mock +3 -0
- data/test/lazy_dir_test.rb +71 -0
- data/test/safegem_test.rb +263 -0
- data/test/security_test.rb +58 -0
- metadata +64 -0
data/VERSION.yml
ADDED
data/bin/safegem
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'rubygems/specification'
|
7
|
+
require 'sinatra'
|
8
|
+
require 'timeout'
|
9
|
+
require 'yaml'
|
10
|
+
|
11
|
+
post '/' do
|
12
|
+
r, w = IO.pipe
|
13
|
+
|
14
|
+
pid = nil
|
15
|
+
begin
|
16
|
+
repo = params[:repo]
|
17
|
+
data = params[:data]
|
18
|
+
tmpdir = "tmp/#{repo}"
|
19
|
+
spec = nil
|
20
|
+
|
21
|
+
Timeout::timeout(15) do
|
22
|
+
`git clone --depth 1 git://github.com/#{repo} #{tmpdir}`
|
23
|
+
|
24
|
+
pid = fork do
|
25
|
+
begin
|
26
|
+
r.close
|
27
|
+
|
28
|
+
require 'safegem/security'
|
29
|
+
require 'safegem/lazy_dir'
|
30
|
+
Dir.chdir(tmpdir) do
|
31
|
+
thread = Thread.new do
|
32
|
+
eval <<-EOE
|
33
|
+
BEGIN { # First in first out. Get this one exec'ed before the code below.
|
34
|
+
Object.class_eval do
|
35
|
+
remove_const :OrigDir rescue nil
|
36
|
+
OrigDir = Dir
|
37
|
+
remove_const :Dir
|
38
|
+
Dir = LazyDir
|
39
|
+
end
|
40
|
+
$SAFE = 3
|
41
|
+
OrigDir.set_safe_level
|
42
|
+
}
|
43
|
+
BEGIN { # This forces Ruby to ignore nested END {} blocks
|
44
|
+
begin
|
45
|
+
params = tmpdir = data = spec = repo = nil
|
46
|
+
# Pass data out using TLS
|
47
|
+
Thread.current[:spec] = (#{data})
|
48
|
+
ensure
|
49
|
+
Object.class_eval do
|
50
|
+
remove_const :Dir
|
51
|
+
Dir = OrigDir
|
52
|
+
end
|
53
|
+
end
|
54
|
+
}
|
55
|
+
EOE
|
56
|
+
end.join
|
57
|
+
Dir.set_safe_level
|
58
|
+
spec = thread[:spec]
|
59
|
+
spec.rubygems_version = Gem::RubyGemsVersion # make sure validation passes
|
60
|
+
spec.validate
|
61
|
+
end
|
62
|
+
|
63
|
+
w.write YAML.dump(spec)
|
64
|
+
rescue Object
|
65
|
+
puts $!,$@
|
66
|
+
|
67
|
+
w.write "ERROR: #$!"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
w.close
|
71
|
+
|
72
|
+
Process.wait pid
|
73
|
+
r.read
|
74
|
+
end
|
75
|
+
rescue Exception
|
76
|
+
Process.kill 9, pid
|
77
|
+
puts $!,$@
|
78
|
+
|
79
|
+
"ERROR: #$!"
|
80
|
+
ensure
|
81
|
+
`rm -rf #{tmpdir}` if tmpdir
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
Sinatra::Application.run!
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class LazyDir < Array
|
2
|
+
OrigDir = Dir
|
3
|
+
|
4
|
+
def initialize(method, args, block = nil)
|
5
|
+
@method, @args, @block = method, args, block
|
6
|
+
end
|
7
|
+
|
8
|
+
# this method is meant to be called lazily after the $SAFE has reverted to 0
|
9
|
+
def to_a
|
10
|
+
raise SecurityError unless %w([] glob).include? @method
|
11
|
+
files = OrigDir.send(@method, *@args, &@block)
|
12
|
+
|
13
|
+
# only return files within the current directory
|
14
|
+
cur_dir = File.expand_path('.') + File::SEPARATOR
|
15
|
+
files.reject do |f|
|
16
|
+
File.expand_path(f) !~ %r{^#{cur_dir}}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
alias_method :to_ary, :to_a
|
20
|
+
|
21
|
+
def to_yaml(opts = {})
|
22
|
+
to_a.to_yaml(opts)
|
23
|
+
end
|
24
|
+
|
25
|
+
class << self
|
26
|
+
# these methods are meant to be called with tainted data in a $SAFE >= 3
|
27
|
+
%w(glob []).each do |method_name|
|
28
|
+
define_method method_name do |*a|
|
29
|
+
LazyDir.new method_name, a
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def method_missing m, *a, &b
|
34
|
+
OrigDir.send m, *a, &b
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
LazyDir.freeze
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# remove dangerous methods
|
2
|
+
%w(` system exec trap fork callcc binding).each do |method|
|
3
|
+
(class << Kernel; self; end).class_eval do
|
4
|
+
remove_method method rescue nil
|
5
|
+
undef_method method rescue nil
|
6
|
+
define_method(method) {|*a| raise SecurityError }
|
7
|
+
end
|
8
|
+
Object.class_eval do
|
9
|
+
remove_method method rescue nil
|
10
|
+
undef_method method rescue nil
|
11
|
+
define_method(method) {|*a| raise SecurityError }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
Kernel.freeze
|
15
|
+
|
16
|
+
# make sure all string methods which modify self also taint the string
|
17
|
+
class String
|
18
|
+
%w(swapcase! strip! squeeze! reverse! downcase! upcase! delete! slice! replace []= <<).each do |method_name|
|
19
|
+
m = instance_method(method_name)
|
20
|
+
define_method method_name do |*args|
|
21
|
+
begin
|
22
|
+
m.bind(self).call *args
|
23
|
+
ensure
|
24
|
+
self.taint
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
%w(sub! gsub!).each do |method_name|
|
30
|
+
m = instance_method(method_name)
|
31
|
+
|
32
|
+
define_method "__real__#{method_name}" do |b, *a|
|
33
|
+
begin
|
34
|
+
m.bind(self).call(*a, &b)
|
35
|
+
ensure
|
36
|
+
self.taint
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
eval <<-EOF
|
41
|
+
def #{method_name} *a, &b
|
42
|
+
__real__#{method_name}(b, *a)
|
43
|
+
end
|
44
|
+
EOF
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
# Bug in ruby doesn't check taint when an array of globs is passed
|
51
|
+
class << Dir
|
52
|
+
# we need to track $SAFE level manually because define_method captures the $SAFE level
|
53
|
+
# of the current scope, as it would a local varaible, and of course the current scope has a $SAFE of 0
|
54
|
+
@@safe_level = 0
|
55
|
+
|
56
|
+
# since this method is defined with def instead of define_method, $SAFE will be taken from
|
57
|
+
# the calling scope which is what we want
|
58
|
+
def set_safe_level
|
59
|
+
@@safe_level = $SAFE
|
60
|
+
end
|
61
|
+
|
62
|
+
%w([] glob).each do |method_name|
|
63
|
+
m = instance_method method_name
|
64
|
+
define_method method_name do |*args|
|
65
|
+
$SAFE = @@safe_level
|
66
|
+
raise SecurityError if $SAFE >= 3 and args.flatten.any? {|a| a.tainted? }
|
67
|
+
|
68
|
+
m.bind(self).call(*args)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# freeze String so that the taint method can't be redefined
|
74
|
+
String.freeze
|
75
|
+
|
76
|
+
# freeze Dir so that no one can modify the @@safe_level
|
77
|
+
Dir.freeze
|
78
|
+
|
79
|
+
# freeze method classes so someone cant modify them to catch the original methods
|
80
|
+
[Method, UnboundMethod].each {|klass| klass.freeze }
|
81
|
+
|
82
|
+
# disable ObjectSpace so people cant access the original method objects
|
83
|
+
Object.send :remove_const, :ObjectSpace
|
data/lib/safegem.rb
ADDED
data/test/git_mock
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
2
|
+
require 'safegem/lazy_dir'
|
3
|
+
|
4
|
+
require 'test/unit'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
class LazyDirTest < Test::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
FileUtils.mkdir('test_glob_dir')
|
10
|
+
eval %{
|
11
|
+
Object.class_eval do
|
12
|
+
remove_const :Dir
|
13
|
+
remove_const :OrigDir rescue nil
|
14
|
+
end
|
15
|
+
::Dir = LazyDir
|
16
|
+
::OrigDir = LazyDir::OrigDir
|
17
|
+
}
|
18
|
+
%w(a b c d).each {|n| File.open("test_glob_dir/#{n}", 'w'){}}
|
19
|
+
end
|
20
|
+
|
21
|
+
def teardown
|
22
|
+
eval %{
|
23
|
+
Object.class_eval { remove_const :Dir }
|
24
|
+
::Dir = OrigDir
|
25
|
+
}
|
26
|
+
FileUtils.rm_r('test_glob_dir')
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_lazy_glob
|
30
|
+
assert_raises(SecurityError) do
|
31
|
+
Thread.new do
|
32
|
+
$SAFE=4
|
33
|
+
OrigDir['test_glob_dir/*']
|
34
|
+
end.join
|
35
|
+
end
|
36
|
+
|
37
|
+
lazy = Thread.new do
|
38
|
+
$SAFE=4
|
39
|
+
Dir['test_glob_dir/*']
|
40
|
+
end.value
|
41
|
+
|
42
|
+
assert_equal OrigDir['test_glob_dir/*'], lazy.to_a
|
43
|
+
assert_equal OrigDir['test_glob_dir/*'], lazy.to_ary
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_lazy_glob_flags
|
47
|
+
if PLATFORM !~ /darwin/
|
48
|
+
# this will fail on osx because of fs case insensitivity, so don't run in there
|
49
|
+
assert LazyDir.glob('*/A').to_a.empty?
|
50
|
+
end
|
51
|
+
assert_equal ['test_glob_dir/a'], LazyDir.glob('*/A', File::FNM_CASEFOLD).to_a
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_lazy_glob_secure
|
55
|
+
assert LazyDir['/etc/passwd'].to_a.empty?
|
56
|
+
assert LazyDir['../../*'].to_a.empty?
|
57
|
+
|
58
|
+
orig = OrigDir['./**/*'].map {|f| File.expand_path(f) }
|
59
|
+
lazy = LazyDir['../**/*'].to_a.map {|f| File.expand_path(f) }
|
60
|
+
assert_equal orig, lazy
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_lazy_dir_delegates_original_dir_methods
|
64
|
+
assert Dir.pwd
|
65
|
+
dir = 'asfasdfsaf'
|
66
|
+
assert Dir.mkdir(dir)
|
67
|
+
assert File.exist?(dir)
|
68
|
+
assert Dir.rmdir(dir)
|
69
|
+
assert ! File.exist?(dir)
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,263 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'net/http'
|
4
|
+
require 'cgi'
|
5
|
+
require 'fileutils'
|
6
|
+
require 'open4'
|
7
|
+
|
8
|
+
OUTPUT = !!ENV['SERVER_OUTPUT']
|
9
|
+
puts "safegem server output disabled, set SERVER_OUTPUT=1 to enable" if ! OUTPUT
|
10
|
+
|
11
|
+
def mv(a, b)
|
12
|
+
here = File.dirname(__FILE__)
|
13
|
+
FileUtils.mv(File.join(here, a), File.join(here, b))
|
14
|
+
end
|
15
|
+
|
16
|
+
# ensure git_mock is in place before running any of these tests
|
17
|
+
mv('git', 'git_mock') rescue nil
|
18
|
+
|
19
|
+
class SafeGemTest < Test::Unit::TestCase
|
20
|
+
def setup
|
21
|
+
here = File.dirname(__FILE__)
|
22
|
+
|
23
|
+
# put the mock git in place
|
24
|
+
mv('git_mock', 'git')
|
25
|
+
|
26
|
+
# construct the safegem command
|
27
|
+
cmd = "PATH=#{here}:$PATH ruby #{here}/../bin/safegem.rb"
|
28
|
+
cmd += " > /dev/null 2>&1" unless OUTPUT
|
29
|
+
|
30
|
+
# run safegem
|
31
|
+
@pid, _, _, _ = Open4::popen4(cmd)
|
32
|
+
|
33
|
+
# wait for server to start
|
34
|
+
Timeout::timeout(5) do
|
35
|
+
begin
|
36
|
+
TCPSocket.open('localhost', 4567) {}
|
37
|
+
server_started = true
|
38
|
+
rescue Errno::ECONNREFUSED
|
39
|
+
server_started = false
|
40
|
+
sleep 0.1
|
41
|
+
retry
|
42
|
+
end until server_started
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def teardown
|
47
|
+
Process.kill("SIGHUP", @pid)
|
48
|
+
mv('git', 'git_mock')
|
49
|
+
sleep(0.5) # to let sinatra unbind the socket
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_access_to_untainted_locals
|
53
|
+
%w(repo data spec params).each do |v|
|
54
|
+
assert_nil_error v
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_timeout
|
59
|
+
puts "\ntesting 15s timeout"
|
60
|
+
begin
|
61
|
+
timeout(17) do
|
62
|
+
s = req <<-EOS
|
63
|
+
def forever
|
64
|
+
loop{}
|
65
|
+
ensure
|
66
|
+
forever
|
67
|
+
end
|
68
|
+
forever
|
69
|
+
EOS
|
70
|
+
assert_equal "ERROR: execution expired", s
|
71
|
+
end
|
72
|
+
rescue Timeout::Error
|
73
|
+
fail "timed out! no good!"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_legit_gemspec_works
|
78
|
+
gemspec = <<-EOS
|
79
|
+
Gem::Specification.new do |s|
|
80
|
+
s.name = "name"
|
81
|
+
s.description = 'description'
|
82
|
+
s.version = "0.0.9"
|
83
|
+
s.summary = ""
|
84
|
+
s.authors = ["coderrr"]
|
85
|
+
s.files = ['x']
|
86
|
+
end
|
87
|
+
EOS
|
88
|
+
expected_response = <<-EOS
|
89
|
+
--- !ruby/object:Gem::Specification
|
90
|
+
name: name
|
91
|
+
version: !ruby/object:Gem::Version
|
92
|
+
version: 0.0.9
|
93
|
+
platform: ruby
|
94
|
+
authors:
|
95
|
+
- coderrr
|
96
|
+
autorequire:
|
97
|
+
bindir: bin
|
98
|
+
cert_chain: []
|
99
|
+
|
100
|
+
date: 2008-10-31 00:00:00 +07:00
|
101
|
+
default_executable:
|
102
|
+
dependencies: []
|
103
|
+
|
104
|
+
description: description
|
105
|
+
email:
|
106
|
+
executables: []
|
107
|
+
|
108
|
+
extensions: []
|
109
|
+
|
110
|
+
extra_rdoc_files: []
|
111
|
+
|
112
|
+
files:
|
113
|
+
- x
|
114
|
+
has_rdoc: false
|
115
|
+
homepage:
|
116
|
+
post_install_message:
|
117
|
+
rdoc_options: []
|
118
|
+
|
119
|
+
require_paths:
|
120
|
+
- lib
|
121
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: "0"
|
126
|
+
version:
|
127
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: "0"
|
132
|
+
version:
|
133
|
+
requirements: []
|
134
|
+
|
135
|
+
rubyforge_project:
|
136
|
+
rubygems_version: 1.3.0
|
137
|
+
signing_key:
|
138
|
+
specification_version: 2
|
139
|
+
summary: ""
|
140
|
+
test_files: []
|
141
|
+
EOS
|
142
|
+
assert_equal clean_yaml(expected_response), clean_yaml(req(gemspec))
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_gemspec_with_glob_works
|
146
|
+
system("mkdir globdir && cd globdir && touch a.rb b.rb c.txt")
|
147
|
+
gemspec = <<-EOS
|
148
|
+
Gem::Specification.new do |s|
|
149
|
+
s.name = "name"
|
150
|
+
s.description = 'description'
|
151
|
+
s.version = "0.0.9"
|
152
|
+
s.summary = ""
|
153
|
+
s.authors = ["coderrr"]
|
154
|
+
s.files = Dir.glob("globdir/**.rb")
|
155
|
+
s.test_files = Dir["globdir/**"]
|
156
|
+
# make sure array globs work with .glob and make sure glob flags work
|
157
|
+
s.executables = Dir.glob(["globdir/*.TXT", "globdir/*.RB"], File::FNM_CASEFOLD)
|
158
|
+
# make sure array globs work with [] and make sure we cant access files in parent dirs
|
159
|
+
s.extra_rdoc_files = Dir["/etc/*", "globdir"]
|
160
|
+
end
|
161
|
+
EOS
|
162
|
+
expected_response = <<-EOS
|
163
|
+
--- !ruby/object:Gem::Specification
|
164
|
+
name: name
|
165
|
+
version: !ruby/object:Gem::Version
|
166
|
+
version: 0.0.9
|
167
|
+
platform: ruby
|
168
|
+
authors:
|
169
|
+
- coderrr
|
170
|
+
autorequire:
|
171
|
+
bindir: bin
|
172
|
+
cert_chain: []
|
173
|
+
|
174
|
+
|
175
|
+
default_executable:
|
176
|
+
dependencies: []
|
177
|
+
|
178
|
+
description: description
|
179
|
+
email:
|
180
|
+
executables:
|
181
|
+
- globdir/c.txt
|
182
|
+
- globdir/a.rb
|
183
|
+
- globdir/b.rb
|
184
|
+
extensions: []
|
185
|
+
|
186
|
+
extra_rdoc_files:
|
187
|
+
- globdir
|
188
|
+
files:
|
189
|
+
- globdir/a.rb
|
190
|
+
- globdir/b.rb
|
191
|
+
has_rdoc: false
|
192
|
+
homepage:
|
193
|
+
post_install_message:
|
194
|
+
rdoc_options: []
|
195
|
+
|
196
|
+
require_paths:
|
197
|
+
- lib
|
198
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
199
|
+
requirements:
|
200
|
+
- - ">="
|
201
|
+
- !ruby/object:Gem::Version
|
202
|
+
version: "0"
|
203
|
+
version:
|
204
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - ">="
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: "0"
|
209
|
+
version:
|
210
|
+
requirements: []
|
211
|
+
|
212
|
+
rubyforge_project:
|
213
|
+
|
214
|
+
signing_key:
|
215
|
+
specification_version: 2
|
216
|
+
summary: ""
|
217
|
+
test_files:
|
218
|
+
- globdir/a.rb
|
219
|
+
- globdir/b.rb
|
220
|
+
- globdir/c.txt
|
221
|
+
EOS
|
222
|
+
assert_equal clean_yaml(expected_response), clean_yaml(req(gemspec))
|
223
|
+
ensure
|
224
|
+
system("rm -rf globdir")
|
225
|
+
end
|
226
|
+
|
227
|
+
def test_tmpdir_is_destroyed
|
228
|
+
Dir.mkdir('tmp/safegem_test')
|
229
|
+
assert File.exist?('tmp/safegem_test')
|
230
|
+
req('')
|
231
|
+
assert ! File.exist?('tmp/safegem_test')
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_secure_parser_begin
|
235
|
+
resp = req <<-EOS
|
236
|
+
BEGIN {require 'bogus_file'}
|
237
|
+
EOS
|
238
|
+
assert resp.include?('Insecure operation')
|
239
|
+
end
|
240
|
+
|
241
|
+
def test_secure_parser_end
|
242
|
+
resp = req <<-EOS
|
243
|
+
END {fail 'secret exit'}
|
244
|
+
EOS
|
245
|
+
assert !resp.include?('secret exit')
|
246
|
+
end
|
247
|
+
|
248
|
+
private
|
249
|
+
|
250
|
+
def clean_yaml(y)
|
251
|
+
y.strip.gsub(/ *$/m, '').sub(/^date:.+$/,'').sub(/^rubygems_version:.+$/,'')
|
252
|
+
end
|
253
|
+
|
254
|
+
def assert_nil_error(v)
|
255
|
+
assert req("#{v}.abc").include?("undefined method `abc' for nil"), "#{v} was not nil"
|
256
|
+
end
|
257
|
+
|
258
|
+
def req(data)
|
259
|
+
Net::HTTP.start 'localhost', 4567 do |h|
|
260
|
+
h.post('/', "data=#{CGI.escape data}&repo=safegem_test").body
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# I dont use test/unit for this because the security measures screw with it
|
2
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
3
|
+
require 'safegem/security'
|
4
|
+
|
5
|
+
def assert condition, message
|
6
|
+
raise message if ! condition
|
7
|
+
|
8
|
+
print '.'; $stdout.flush
|
9
|
+
end
|
10
|
+
|
11
|
+
def assert_raises error, message, &block
|
12
|
+
begin
|
13
|
+
yield
|
14
|
+
raise message
|
15
|
+
rescue error
|
16
|
+
print '.'; $stdout.flush
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
['class Method', 'class UnboundMethod', 'module Kernel'].each do |klass|
|
21
|
+
assert_raises TypeError, "#{klass} didn't raise" do
|
22
|
+
eval("#{klass}; def x;end; end")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
data = 'echo YOU SHOULDNT SEE THIS!!!!'
|
27
|
+
['system(data)',
|
28
|
+
'exec(data)',
|
29
|
+
'Kernel.send(:exec,data)',
|
30
|
+
'Object.new.exec(data)',
|
31
|
+
'`#{data}`',
|
32
|
+
'Kernel.`(data)',
|
33
|
+
'Kernel.send(:`,data)',
|
34
|
+
'trap(1,lambda{})',
|
35
|
+
'fork{}',
|
36
|
+
'callcc{}',
|
37
|
+
'binding'
|
38
|
+
].each do |danger|
|
39
|
+
assert_raises SecurityError, "#{danger} worked!" do
|
40
|
+
eval danger
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
Thread.new do
|
45
|
+
$SAFE = 3
|
46
|
+
Dir.set_safe_level
|
47
|
+
assert_raises SecurityError, "snuck tainted string past glob" do
|
48
|
+
Dir['**','**']
|
49
|
+
Dir.glob(['**', '**'])
|
50
|
+
end
|
51
|
+
end.join
|
52
|
+
Dir.set_safe_level
|
53
|
+
Dir['**'.taint]
|
54
|
+
|
55
|
+
dirs = Dir['/**']
|
56
|
+
assert(4 == (dirs & %w(/usr /bin /home /sbin)).size, 'glob doesnt work')
|
57
|
+
|
58
|
+
puts
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: github-safegem
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- PJ Hyett
|
8
|
+
- Tom Preston-Werner
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2009-02-10 00:00:00 -08:00
|
14
|
+
default_executable: safegem
|
15
|
+
dependencies: []
|
16
|
+
|
17
|
+
description: GitHub's safe gem eval web service
|
18
|
+
email: tom@mojombo.com
|
19
|
+
executables:
|
20
|
+
- safegem
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files: []
|
24
|
+
|
25
|
+
files:
|
26
|
+
- VERSION.yml
|
27
|
+
- bin/safegem
|
28
|
+
- lib/safegem
|
29
|
+
- lib/safegem/lazy_dir.rb
|
30
|
+
- lib/safegem/security.rb
|
31
|
+
- lib/safegem.rb
|
32
|
+
- test/git_mock
|
33
|
+
- test/lazy_dir_test.rb
|
34
|
+
- test/safegem_test.rb
|
35
|
+
- test/security_test.rb
|
36
|
+
has_rdoc: true
|
37
|
+
homepage: http://github.com/github/safegem
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options:
|
40
|
+
- --inline-source
|
41
|
+
- --charset=UTF-8
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: "0"
|
49
|
+
version:
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
version:
|
56
|
+
requirements: []
|
57
|
+
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 1.2.0
|
60
|
+
signing_key:
|
61
|
+
specification_version: 2
|
62
|
+
summary: GitHub's safe gem eval web service
|
63
|
+
test_files: []
|
64
|
+
|