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.



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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
# File 'lib/ppr/safer_generator.rb', line 60

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
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
# 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
    # 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