This repository has been archived by the owner on Nov 1, 2017. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 15
/
security.rb
79 lines (67 loc) · 2.13 KB
/
security.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# remove dangerous methods
%w(` system exec trap fork callcc binding).each do |method|
(class << Kernel; self; end).class_eval do
remove_method method rescue nil
undef_method method rescue nil
define_method(method) {|*a| raise SecurityError }
end
Object.class_eval do
remove_method method rescue nil
undef_method method rescue nil
define_method(method) {|*a| raise SecurityError }
end
end
Kernel.freeze
# disable ObjectSpace
Object.send :remove_const, :ObjectSpace
# make sure all string methods which modify self also taint the string
class String
%w(swapcase! strip! squeeze! reverse! downcase! upcase! delete! slice! replace []= <<).each do |method_name|
m = instance_method(method_name)
define_method method_name do |*args|
begin
m.bind(self).call *args
ensure
self.taint
end
end
end
%w(sub! gsub!).each do |method_name|
m = instance_method(method_name)
define_method "__real__#{method_name}" do |b, *a|
begin
m.bind(self).call(*a, &b)
ensure
self.taint
end
end
eval <<-EOF
def #{method_name} *a, &b
__real__#{method_name}(b, *a)
end
EOF
end
end
# Bug in ruby doesn't check taint when an array of globs is passed
class << Dir
# we need to track $SAFE level manually because define_method captures the $SAFE level
# of the current scope, as it would a local varaible, and of course the current scope has a $SAFE of 0
@@safe_level = 0
# since this method is defined with def instead of define_method, $SAFE will be taken from
# the calling scope which is what we want
def set_safe_level
@@safe_level = $SAFE
end
%w([] glob).each do |method_name|
m = instance_method method_name
define_method method_name do |*args|
$SAFE = @@safe_level
raise SecurityError if $SAFE >= 3 and args.flatten.any? {|a| a.tainted? }
m.bind(self).call(*args)
end
end
end
# freeze Dir so that no one can modify the @@safe_level
Dir.freeze
# freeze method classes so someone cant modify them to catch the original methods
[Method, UnboundMethod].each {|klass| klass.freeze }