Class: SandboxedErb::Template

Inherits:
Object
  • Object
show all
Defined in:
lib/sandboxed_erb/template.rb

Overview

This class represents a template which can be compiled then run multiple times.

When declaring a template, pass an array of Mixin classes to the contructor to allow the template access to the Mixin methods.

Example module ExampleHelper

def format_date(date, format)
  if format == :short_date
    date.strftime("%d %b %Y %H:%M")
  else
    "unknown format: #{format}"
  end
end

 def current_time
   DateTime.now
 end

end

template = SandboxedErb::Template.new() #the template will now have access to the format_date() and current_time() helper function template.compile(‘the date = <%=format_date(current_time, :short_date)%>’)

Instance Method Summary collapse

Constructor Details

#initialize(mixins = []) ⇒ Template

minins is an array of helper classes which expose methods to the template



53
54
55
# File 'lib/sandboxed_erb/template.rb', line 53

def initialize(mixins = [])
  @mixins = mixins.collect { |clz| "include #{clz.name}"}.join("\n")
end

Instance Method Details

#compile(str_template) ⇒ Object

compile the template

if the template does not compile, false is returned and get_error should be called to get the compile error.



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
# File 'lib/sandboxed_erb/template.rb', line 60

def compile(str_template)
  
  erb_template = compile_erb_template(str_template)
  return false if erb_template.nil?
  #we now have a normal compile erb template (which is just ruby code)
  
  sandboxed_compiled_template = sandbox_code(erb_template)
  puts sandboxed_compiled_template if $DEBUG
  return false if sandboxed_compiled_template.nil?
  
  @clazz_name = "SandboxedErb::TClass#{self.object_id}"
  @file_name = "tclass_#{self.object_id}"
  
  clazz_str = <<-EOF
  class #{@clazz_name} < SandboxedErb::TemplateBase
    #{@mixins}
    def run_internal()
      #{sandboxed_compiled_template}
    end
  end
  #{@clazz_name}.new
  EOF
  
  begin
    @template_runner = eval(clazz_str, nil, @file_name)
  rescue Exception=>e
    @error = "Invalid code generated: #{e.message}"
    return false
  end
  
  true
  
end

#compile_erb_template(str_template) ⇒ Object

:nodoc:



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/sandboxed_erb/template.rb', line 107

def compile_erb_template(str_template) #:nodoc:
  ecompiler = ERB::Compiler.new(nil)
  
  ecompiler.put_cmd = "_erbout.concat"
  ecompiler.insert_cmd = "_erbout.concat"
  
  cmd = []
  cmd.push "_erbout = ''"
  
  ecompiler.pre_cmd = cmd
  
  cmd = []
  cmd.push('_erbout')
  
  ecompiler.post_cmd = cmd
  ecompiler.compile(str_template)
end

#get_errorObject



159
160
161
# File 'lib/sandboxed_erb/template.rb', line 159

def get_error
  @error
end

#run(context, locals) ⇒ Object

run a compiled template

  • context: A map of context objects that will be available to helper functions and instance variables, and available to sandboxed objects through the set_sandbox_context callback.

  • locals: A map of local objects that will be available to the template, and available to sandboxed objects through the set_sandbox_context callback as the :locals entry.

If the template runs successfully, the geneated content is returned. If an error occures, nil is returned and get_error should be called to get the error information.



98
99
100
101
102
103
104
105
# File 'lib/sandboxed_erb/template.rb', line 98

def run(context, locals)
  begin
    @template_runner.run(context, locals)
  rescue Exception=>e
    @error = e.message
    nil
  end
end

#sandbox_code(erb_template) ⇒ Object

:nodoc:



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
# File 'lib/sandboxed_erb/template.rb', line 125

def sandbox_code(erb_template) #:nodoc:
  @error = nil
  tree = nil
  begin
    tree = RubyParser.new.parse erb_template
  rescue Exception=>e
    #the message is pretty useless.. lets eval the code (it wont get executed because of the compile error)
    begin
      #extra bit of caution.. run in $SAFE=4
      t = Thread.new {
          $SAFE = 4
          eval(erb_template, nil, "line")
      }
      t.join
    rescue Exception=>e2
      @error = e2.message
      return nil
    end
    #if we got here then somehow code that would not compile using RubyParser eval'ed ok...
    throw "SYSTEM ERROR: you may be owned! Code that should not be able to compile has run!"
  end
  begin
    context = PartialRuby::PureRubyContext.new
    tree_processor = SandboxedErb::TreeProcessor.new()
    
    tree = tree_processor.process(tree)
    emulationcode = context.emul tree
  rescue Exception=>e
    @error = e.message
    return nil
  end
  emulationcode
end