Class: Metasploit::Model::Spec::Template

Inherits:
Base
  • Object
show all
Extended by:
ActiveModel::Callbacks, ActiveSupport::Autoload
Includes:
ActiveModel::Validations::Callbacks
Defined in:
app/models/metasploit/model/spec/template.rb

Overview

Processes '.rb.erb' templates to create Module::Ancestor#contents that contain the same metadata as the Module::Ancestor, Module::Class, Module::Instance and associations for those contents. This ensures that when the Module::Ancestor#contents are loaded in metasploit-framework, the same metadata instances are derived from the contents ensuring idempotency of the contents and metadata parsing loop.

Direct Known Subclasses

Module::Ancestor::Spec::Template

Defined Under Namespace

Modules: Write

Constant Summary collapse

BACKTRACE_FILE_REGEXP =

Regular expression to parse file from backtrace line

/(?<file>.*):\d+:in .*/
EXPLICIT_TRIM_MODE =

Trim mode for ERB templates so that lines using <%- -%> will be trimmed of new lines

'-'
EXTENSION =

File extension for templates.

'.rb.erb'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

#initialize, #valid!

Constructor Details

This class inherits a constructor from Metasploit::Model::Base

Instance Attribute Details

#destination_pathnameString

The pathname where to #write the template results.

Returns:

  • (String)


36
37
38
# File 'app/models/metasploit/model/spec/template.rb', line 36

def destination_pathname
  @destination_pathname
end

#localsHash{Symbol => Object}

Local variables to exposed to partials.

Returns:

  • (Hash{Symbol => Object})


42
43
44
# File 'app/models/metasploit/model/spec/template.rb', line 42

def locals
  @locals
end

#overwriteBoolean

Whether to overwrite a pre-existing file.

Returns:

  • (Boolean)


48
49
50
# File 'app/models/metasploit/model/spec/template.rb', line 48

def overwrite
  @overwrite
end

#search_pathnamesArray<Pathname>

Pathnames to search for partials. First item is search first, etc.

Returns:

  • (Array<Pathname>)


54
55
56
# File 'app/models/metasploit/model/spec/template.rb', line 54

def search_pathnames
  @search_pathnames
end

#source_relative_nameString

Name of template under #search_pathnames without EXTENSION similar to how to refer to partials.

Returns:

  • (String)


60
61
62
# File 'app/models/metasploit/model/spec/template.rb', line 60

def source_relative_name
  @source_relative_name
end

Class Method Details

.rootPathname

The root of all relative #search_pathnames. By changing root you can use your own set of templates.

Returns:

  • (Pathname)

    Defaults to 'spec/support/templates/metasploit/model' under Metasploit::Model.root.



202
203
204
# File 'app/models/metasploit/model/spec/template.rb', line 202

def root
  @@root ||= Metasploit::Model::Engine.root.join('spec', 'support', 'templates', 'metasploit', 'model')
end

.root=(root) ⇒ Pathname

Sets the root pathname for all #search_pathnames, including those on subclasses.

Parameters:

  • root (Pathname)

Returns:

  • (Pathname)


210
211
212
# File 'app/models/metasploit/model/spec/template.rb', line 210

def root=(root)
  @@root = root
end

Instance Method Details

#find_pathname(relative_path) ⇒ Pathname? (private)

Finds relative_path under #search_pathnames

Returns:

  • (Pathname)

    if relative_path exists under a search path.

  • (nil)

    if relative_path does not exist under a search path.



253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'app/models/metasploit/model/spec/template.rb', line 253

def find_pathname(relative_path)
  found_pathname = nil

  search_pathnames.each do |search_pathname|
    real_pathname = search_pathname.join(relative_path)

    if real_pathname.exist?
      found_pathname = real_pathname
      break
    end
  end

  found_pathname
end

#partial_pathname(partial_relative_path) ⇒ Pathname?

Converts the partial_relative_path to a real (absolute) pathname that can be passed to #result by finding the corresponding file in the #search_pathnames.

Parameters:

  • partial_relative_path (String)

    partial path name as used in Rails, so no search_pathname prefix and no '_' or file extension in the basename.

Returns:

  • (Pathname)

    if relative_path exists under a search path.

  • (nil)

    if relative_path does not exist under a search path.



99
100
101
102
103
104
105
106
107
# File 'app/models/metasploit/model/spec/template.rb', line 99

def partial_pathname(partial_relative_path)
  partial_relative_pathname = Pathname.new(partial_relative_path)
  relative_directory = partial_relative_pathname.dirname
  raw_basename = partial_relative_pathname.basename
  partial_basename = "_#{raw_basename}#{EXTENSION}"
  relative_pathname = relative_directory.join(partial_basename)

  find_pathname(relative_pathname)
end

#render(partial_relative_path, options = {}) ⇒ String?

Renders partial in templates.

Parameters:

  • partial_relative_path (String)

    relative path to partial without extension and no leading '_' to match Rails convention.

  • options (Hash{Symbol => Object}) (defaults to: {})

Options Hash (options):

  • :locals (Hash{Symbol => Object})

    Maps name of locals to their value for this result. Can override #locals.

Returns:

  • (String, nil)

    result of rendering partial.



116
117
118
119
# File 'app/models/metasploit/model/spec/template.rb', line 116

def render(partial_relative_path, options={})
  pathname = partial_pathname(partial_relative_path)
  result(pathname, options)
end

#render_superString?

Renders the super template of the current template by searching for the template of the same name under super #search_pathnames (those that have a higher index).

Returns:

  • (String, nil)

    result of rendering super partial.

Raises:

  • (IOError)

    if super template can't be found on super search pathnames.

  • (RegexpError)

    if file can't be parsed from backtrace.



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
# File 'app/models/metasploit/model/spec/template.rb', line 127

def render_super
  super_pathname = nil
  backtrace = caller
  match = BACKTRACE_FILE_REGEXP.match(backtrace[0])

  if match
    real_path = match[:file]
    current_search_pathname_found = false
    relative_pathname = nil

    search_pathnames.each do |search_pathname|
      # find the current index
      unless current_search_pathname_found
        if real_path.starts_with?(search_pathname.to_path)
          current_search_pathname_found = true

          real_pathname = Pathname.new(real_path)
          relative_pathname = real_pathname.relative_path_from(search_pathname)
        end
        # then switch to finding the next (super) index
      else
        real_pathname = search_pathname.join(relative_pathname)

        if real_pathname.exist?
          super_pathname = real_pathname
          break
        end
      end
    end

    unless super_pathname
      raise IOError, "Couldn't find super template"
    end
  else
    raise RegexpError, "Can't parse file from backtrace to determine current search path"
  end

  result(super_pathname)
end

#result(pathname, options = {}) ⇒ Object

Note:

Must be an instance method

Parameters:

  • pathname (Pathname)

    pathname to template

  • options (Hash{Symbol => Object}) (defaults to: {})

Options Hash (options):

  • :locals (Hash{Symbol => Object})

    Maps name of locals to their value for this result. Can override #locals.



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'app/models/metasploit/model/spec/template.rb', line 172

def result(pathname, options={})
  options.assert_valid_keys(:locals)

  if pathname
    content = pathname.read
    safe_level = nil
    template = ERB.new content, safe_level, EXPLICIT_TRIM_MODE
    template.filename = pathname.to_path

    erb_binding = binding.dup
    locals = self.locals || {}
    result_locals = options[:locals] || {}
    merged_locals = locals.merge(result_locals)

    merged_locals.each do |name, value|
      erb_binding.eval("#{name} = nil; lambda { |value| #{name} = value }").call(value)
    end

    # use current binding to allow templates to call {#render} and then use {#method_missing} to allow access to
    # locals
    template.result(erb_binding)
  else
    ''
  end
end

#search_real_pathnamesvoid (private)

This method returns an undefined value.

Makes sure all #search_pathnames are real (absolute). Relative pathnames are resolved against root.



271
272
273
274
275
276
277
278
279
# File 'app/models/metasploit/model/spec/template.rb', line 271

def search_real_pathnames
  search_pathnames.collect! { |search_pathname|
    if search_pathname.relative?
      self.class.root.join(search_pathname)
    else
      search_pathname
    end
  }
end

#source_pathnamePathname?

Converts #source_relative_name to a real (absolute) pathname.

Returns:

  • (Pathname)

    if relative_path exists under a search path.

  • (nil)

    if relative_path does not exist under a search path.



218
219
220
221
222
223
224
225
# File 'app/models/metasploit/model/spec/template.rb', line 218

def source_pathname
  unless instance_variable_defined? :@source_pathname
    relative_path = "#{source_relative_name}#{EXTENSION}"
    @source_pathname = find_pathname(relative_path)
  end

  @source_pathname
end

#writevoid

This method returns an undefined value.

Writes result of template to #destination_pathname.

Raises:



232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'app/models/metasploit/model/spec/template.rb', line 232

def write
  unless overwrite
    Metasploit::Model::Spec::PathnameCollision.check!(destination_pathname)
  end

  result = self.result(source_pathname)

  # make directory
  destination_pathname.parent.mkpath

  destination_pathname.open('wb') do |f|
    f.write(result)
  end
end