Class: SaferGenerator

Inherits:
Object
  • Object
show all
Defined in:
lib/ppr/safer_generator.rb

Overview

Tool for executing in a safer sandbox a proc that generates a string into a stream.

Defined Under Namespace

Classes: SaferException

Constant Summary collapse

DANGER_CONSTANTS =

The list of dangerous constants of Object.

[ :File, :IO, :Dir ]
DANGER_METHODS =

The list of dangerous methods of Kernel

[ :system, :`, :open ]

Instance Method Summary collapse

Constructor Details

#initialize(*black_list) ⇒ SaferGenerator

Creates a new safe context with while removing Kernel methods and constants from black_list in addition to the default dangerous ones.


26
27
28
29
30
31
32
33
34
35
# File 'lib/ppr/safer_generator.rb', line 26

def initialize(*black_list)
    # Set the black list of methods.
    @black_methods = black_list.select do |symbol|
        symbol.to_s[0].match(/[a-z_]/)
    end
    # Set the black list of constants.
    @black_constants = black_list.select do |symbol|
        symbol.to_s[0].match(/[A-Z]/)
    end
end

Instance Method Details

#run(stream = nil, &block) ⇒ Object

Executes block in a safe context for generating text into a stream.

If no stream is given, returns the result as a string instead.


85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/ppr/safer_generator.rb', line 85

def run(stream = nil, &block)
    unless stream
        # No stream given
        to_return = true
        stream = StringIO.new("")
    end
    # Creates the pipe for communicating with the block.
    rd,wr = IO.pipe
    # # Creates a process for executing the block.
    # pid = fork
    # if pid then
    #     # This is the parent: waits for the block execution result.
    #     # No need to write on the pipe. close it.
    #     wr.close
    #     # Read the result of the process and send it to stream
    #     until rd.eof?
    #         stream << rd.read
    #     end
    #     # No more need of rd.
    #     rd.close
    #     # Wait the end of the child process
    #     Process.wait(pid)
    #     # Where there a trouble?
    #     unless $?.exited? then
    #         # pid did not exit, internal error.
    #         raise "*Internal error*: safer process #{pid} did not exit."
    #     end
    #     if $?.exitstatus !=0 then
    #         # Reconstruct the exception from the stream, the exit
    #         # status is the number of line to use.
    #         e0 = Marshal.load( stream.string.each_line.
    #                            to_a[-$?.exitstatus..-1].join )
    #         # Then resend the eception encapsulated into another one
    #         # telling the safer process failed.
    #         begin
    #             raise e0
    #         rescue Exception => e1
    #             raise SaferException.new("*Error*: exception occured in safer process #{pid}.")
    #         end
    #     end
    # else
    #     # This is the child: enter in safe mode and execute the block.
    #     # No need to write on the pipe. close it.
    #     rd.close
    #     # Secure.
    #     secure
    #     # Execute the block.
    #     begin
    #         block.call(wr)
    #     rescue Exception => e
    #         # The exception is serialized and passed to the main process
    #         # through the pipe.
    #         e = Marshal.dump(e)
    #         wr << "\n" << e 
    #         # The exit status is the number of line of the serialized
    #         # exception.
    #         exit!(e.each_line.count)
    #     end
    #     # No more need of wr.
    #     wr.close
    #     # End the process without any error.
    #     exit!(0)
    # end
    # 
    # # Is there a string to return?
    # if to_return then
    #     return stream.string
    # else
    #     return nil
    # end

    # Secure.
    secure
    trouble = nil
    # Execute the block.
    begin
        block.call(wr)
    rescue Exception => e
        trouble = e
    end
    # No more need of wr.
    wr.close

    # Unsecure and process the result.
    unsecure
    # Read the result of the process and send it to stream
    until rd.eof?
        stream << rd.read
    end
    # No more need of rd.
    rd.close
    if trouble then
        begin
            raise trouble
        rescue Exception => e1
            raise SaferException.new("*Error*: exception occured in safe mode.")
        end
    end
      
    # Is there a string to return?
    if to_return then
        return stream.string
    else
        return nil
    end
end

#secureObject

Strips all the Kernel methods and constants appart from the elements of the white list. Also strip Object from dangerous methods and constants apart from the elements of the white list.


41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/ppr/safer_generator.rb', line 41

def secure
    # Gather the methods to strip.
    methods = DANGER_METHODS + @black_methods
    # Gather the constants to strip.
    constants = DANGER_CONSTANTS + @black_constants
    # Save the dangerous methods in a private safe.
    @safe_of_methods = {}
    methods.each do |meth|
        @safe_of_methods[meth]=method(meth)
    end
    # Save the dangerous constants in a private safe.
    @safe_of_constants = {}
    constants.each do |cst|
        @safe_of_constants[cst] = Object.send(:const_get,cst)
    end
    # Strip the dangerous methods.
    methods.each do |meth|
        Kernel.send(:undef_method,meth)
    end
    # Strip the dangerous constants from Object.
    constants.each do |cst|
        Object.send(:remove_const,cst)
    end
end

#unsecureObject

Restores all the stripped Kernel methods and constants appart from the elements of the white list. Also strip Object from dangerous methods and constants apart from the elements of the white list.


70
71
72
73
74
75
76
77
78
79
# File 'lib/ppr/safer_generator.rb', line 70

def unsecure
    # Restores the dangerous methods in a private safe.
    @safe_of_methods.each do |(name,pr)|
        Kernel.send(:define_method,name,&pr)
    end
    # Restors the dangerous constants in a private safe.
    @safe_of_constants.each do |(name,cst)|
        Object.const_set(name,cst)
    end
end