stratagem 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. data/Manifest +16 -6
  2. data/Rakefile +8 -1
  3. data/lib/generators/stratagem/install/install_base.rb +13 -3
  4. data/lib/generators/stratagem/install/install_generator.rb +1 -1
  5. data/lib/stratagem.rb +42 -18
  6. data/lib/stratagem/authentication.rb +2 -5
  7. data/lib/stratagem/auto_mock.rb +1 -0
  8. data/lib/stratagem/auto_mock/aquifer.rb +49 -26
  9. data/lib/stratagem/auto_mock/factory.rb +1 -6
  10. data/lib/stratagem/auto_mock/user_loader.rb +38 -0
  11. data/lib/stratagem/client.rb +15 -4
  12. data/lib/stratagem/configuration/auth_auth.rb +19 -0
  13. data/lib/stratagem/configuration/core.rb +20 -0
  14. data/lib/stratagem/crawler/authentication.rb +17 -12
  15. data/lib/stratagem/crawler/authentication/automated.rb +40 -0
  16. data/lib/stratagem/crawler/authentication/base.rb +140 -0
  17. data/lib/stratagem/crawler/authentication/configured.rb +27 -0
  18. data/lib/stratagem/crawler/parameter_resolver.rb +12 -8
  19. data/lib/stratagem/crawler/route_invoker.rb +10 -13
  20. data/lib/stratagem/crawler/session.rb +14 -2
  21. data/lib/stratagem/crawler/site_model.rb +4 -173
  22. data/lib/stratagem/crawler/site_model/edge.rb +20 -0
  23. data/lib/stratagem/crawler/site_model/page.rb +121 -0
  24. data/lib/stratagem/crawler/site_model/page_set.rb +58 -0
  25. data/lib/stratagem/instrumentation/models.rb +3 -14
  26. data/lib/stratagem/instrumentation/models/annotations.rb +39 -5
  27. data/lib/stratagem/instrumentation/models/authentication.rb +0 -1
  28. data/lib/stratagem/instrumentation/models/authentication/authlogic/detect.rb +1 -0
  29. data/lib/stratagem/instrumentation/models/authentication/devise/detect.rb +1 -1
  30. data/lib/stratagem/instrumentation/models/authentication/devise/instrumentation.rb +0 -4
  31. data/lib/stratagem/instrumentation/models/metadata.rb +23 -1
  32. data/lib/stratagem/instrumentation/models/persistence.rb +3 -4
  33. data/lib/stratagem/instrumentation/models/persistence/active_record/metadata.rb +2 -2
  34. data/lib/stratagem/interface/browser.rb +9 -3
  35. data/lib/stratagem/interface/public/javascripts/stratagem.js +14 -12
  36. data/lib/stratagem/interface/views/index.haml +3 -3
  37. data/lib/stratagem/logger.rb +28 -2
  38. data/lib/stratagem/model.rb +6 -0
  39. data/lib/stratagem/model/application.rb +21 -134
  40. data/lib/stratagem/model/components/base.rb +1 -4
  41. data/lib/stratagem/model/components/controller.rb +1 -2
  42. data/lib/stratagem/model/components/model.rb +15 -15
  43. data/lib/stratagem/model/components/route.rb +3 -2
  44. data/lib/stratagem/model/components/view.rb +0 -1
  45. data/lib/stratagem/model/containers/base.rb +60 -0
  46. data/lib/stratagem/model/containers/gem.rb +25 -0
  47. data/lib/stratagem/model/containers/plugin.rb +11 -0
  48. data/lib/stratagem/model/containers/route.rb +19 -0
  49. data/lib/stratagem/model/parse_util.rb +3 -3
  50. data/lib/stratagem/model_builder.rb +1 -4
  51. data/lib/stratagem/rack_hack.rb +15 -0
  52. data/lib/stratagem/site_crawler.rb +5 -4
  53. data/lib/stratagem/snapshot.rb +5 -7
  54. data/spec/stratagem/configuration_spec.rb +32 -0
  55. data/stratagem.gemspec +5 -8
  56. data/templates/install/environments/stratagem.rb.erb +31 -2
  57. data/templates/install/script/stratagem +16 -0
  58. data/templates/install/tasks/stratagem.rake +2 -2
  59. metadata +36 -65
  60. data/bin/stratagem +0 -58
  61. data/lib/stratagem/scan.rb +0 -19
  62. data/lib/stratagem/scan/checks/email_address.rb +0 -15
  63. data/lib/stratagem/scan/checks/error_pages.rb +0 -25
  64. data/lib/stratagem/scan/result.rb +0 -45
  65. data/lib/stratagem/scanner.rb +0 -32
@@ -1,25 +1,14 @@
1
1
  module Stratagem::Instrumentation::Models; end
2
2
  module Stratagem::Instrumentation::Models::Adapters; end
3
3
 
4
+ require 'stratagem/instrumentation/models/association'
5
+
4
6
  require 'stratagem/instrumentation/models/mocking'
5
7
  require 'stratagem/instrumentation/models/metadata'
6
8
  require 'stratagem/instrumentation/models/tracing'
7
9
  require 'stratagem/instrumentation/models/annotations'
8
10
  require 'stratagem/instrumentation/models/detect'
9
11
 
10
- require 'stratagem/instrumentation/models/persistence'
11
12
  require 'stratagem/instrumentation/models/authentication'
13
+ require 'stratagem/instrumentation/models/persistence'
12
14
  require 'stratagem/instrumentation/models/support_libraries'
13
-
14
- # base = File.join(File.dirname(__FILE__), 'models', 'persistence', 'util')
15
- # Dir.entries(base).select {|s| s =~ /\.rb$/}.each {|helper|
16
- # require File.join(base, helper.gsub(/\.rb/, ''))
17
- # }
18
- #
19
- # base = File.join(File.dirname(__FILE__), 'models', 'persistence')
20
- # Dir.entries(base).select {|s| s !~ /^\./ && s != 'util' }.each {|adapter_dir|
21
- # require File.join(base, adapter_dir, 'detect')
22
- # require File.join(base, adapter_dir, 'tracing')
23
- # require File.join(base, adapter_dir, 'metadata')
24
- # require File.join(base, adapter_dir, 'extensions')
25
- # }
@@ -12,6 +12,36 @@ module Stratagem::Instrumentation::Models
12
12
  def method_missing(method, *args, &block)
13
13
  @object.class.stratagem.send(method, *args, &block)
14
14
  end
15
+
16
+ # objects that are related to this object
17
+ def related_objects(collection_size_limit=5000)
18
+ traverse_objects(collection_size_limit)
19
+ end
20
+
21
+ def traverse_objects(collection_size_limit, collection=[], class_chain=[])
22
+ return if collection.size >= collection_size_limit
23
+
24
+ unless collection.include?(@object)
25
+ collection << @object
26
+ relations(:has_many).each do |relation|
27
+ puts "relation #{relation.name} - #{collection.size} - #{relation.klass}"
28
+ if (relation.klass && !class_chain.include?(relation.klass))
29
+ class_chain << relation.klass
30
+ begin
31
+ related = @object.send(relation.name)
32
+ if (related.kind_of?(Array))
33
+ related.each {|r| r.stratagem.traverse_objects(collection_size_limit, collection, class_chain) }
34
+ elsif (!related.nil?)
35
+ related.stratagem.traverse_objects(collection_size_limit, collection, class_chain)
36
+ end
37
+ rescue
38
+ Stratagem.logger.error($!)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ collection
44
+ end
15
45
  end
16
46
 
17
47
  class Annotations
@@ -43,8 +73,12 @@ module Stratagem::Instrumentation::Models
43
73
  # connect the adapters
44
74
  detect_adapters(model).each {|adapter|
45
75
  if adapter.detector.supports?(model)
46
- puts "#{model.name} supports #{adapter.tracing.name}"
47
- model.send(:include, adapter.tracing)
76
+ begin
77
+ puts "#{model.name} supports #{adapter.tracing.name}"
78
+ model.send(:include, adapter.tracing)
79
+ rescue
80
+ Rails.logger.error($!)
81
+ end
48
82
  end
49
83
  }
50
84
  end
@@ -75,14 +109,14 @@ module Stratagem::Instrumentation::Models
75
109
  end
76
110
 
77
111
  def initialize(model)
78
- puts "initializing stratagem for #{model.name} - #{self.class.name}"
112
+ puts "initializing stratagem for #{model.name}"
79
113
  @model = model
80
114
  self.class.detect_adapters(model).each do |adapter|
81
115
  if adapter.detector.supports?(model)
82
116
  # puts "\t#{model.name} supports #{adapter.detector.name}"
83
117
  instrument_model(adapter)
84
- # else
85
- # puts "#{model.name} does not support #{adapter.detector.name}"
118
+ else
119
+ # puts "#{model.name} does not support #{adapter.detector.name}"
86
120
  end
87
121
  end
88
122
  rescue
@@ -4,4 +4,3 @@ pattern = File.join(File.dirname(__FILE__), 'authentication', '**', '*.rb')
4
4
  Dir.glob(pattern).each {|filename|
5
5
  require filename
6
6
  }
7
-
@@ -2,6 +2,7 @@ module Stratagem::Instrumentation::Models::Authentication::Authlogic
2
2
  class Detect < Stratagem::Instrumentation::Models::Detect
3
3
  def self.supports?(model)
4
4
  begin
5
+ # puts "AUTHLOGIC MODEL #{model.name} - #{model.ancestors.include?(::Authlogic::ActsAsAuthentic::MagicColumns::Methods)}"
5
6
  model.ancestors.include?(::Authlogic::ActsAsAuthentic::MagicColumns::Methods)
6
7
  rescue
7
8
  false
@@ -2,7 +2,7 @@ module Stratagem::Instrumentation::Models::Authentication::Devise
2
2
  class Detect < Stratagem::Instrumentation::Models::Detect
3
3
  def self.supports?(model)
4
4
  begin
5
- model.ancestors.find {|a| a.name.include? 'Devise::Models' } != nil
5
+ model.ancestors.find {|a| a.name && a.name.include?('Devise::Models') } != nil
6
6
  rescue
7
7
  false
8
8
  end
@@ -8,10 +8,6 @@ module Stratagem::Instrumentation::Models::Authentication::Devise
8
8
  record.active = true
9
9
  end
10
10
  }
11
-
12
- after_save {|record|
13
- p record
14
- }
15
11
  end
16
12
  end
17
13
  end
@@ -25,6 +25,10 @@ module Stratagem::Instrumentation::Models
25
25
 
26
26
  # Convenience methods
27
27
 
28
+ def connected_classes
29
+ traverse_graph(model) - [model]
30
+ end
31
+
28
32
  def subclasses?
29
33
  model.sg_subclasses().size > 0
30
34
  end
@@ -61,7 +65,9 @@ module Stratagem::Instrumentation::Models
61
65
  end
62
66
 
63
67
  def callbacks
64
- adapters.select {|a| a.detector.supports?(model) }.map {|a| a.metadata }
68
+ adapters.select {|a|
69
+ a.detector.supports?(model)
70
+ }.map {|a| a.metadata }
65
71
  end
66
72
 
67
73
  def validators
@@ -71,6 +77,7 @@ module Stratagem::Instrumentation::Models
71
77
  private
72
78
 
73
79
  def run_callbacks(method, *args)
80
+ puts "#{self.class.name} model" if (method == :attribute_names)
74
81
  results = callbacks.inject([]) {|memory,callback|
75
82
  begin
76
83
  memory << callback.send(method, *args) if callback.methods_include?(method)
@@ -82,5 +89,20 @@ module Stratagem::Instrumentation::Models
82
89
  results.flatten.compact.uniq
83
90
  end
84
91
 
92
+
93
+ private
94
+
95
+ # see connected_classes
96
+ def traverse_graph(application_class, class_chain=[])
97
+ class_chain << application_class
98
+ application_class.stratagem.relations.each do |relation|
99
+ traverse_graph(relation.from_class, class_chain) unless class_chain.include?(relation.from_class)
100
+ traverse_graph(relation.klass, class_chain) unless class_chain.include?(relation.klass)
101
+ end
102
+ puts class_chain.map {|c| c.name }.join(" -> ")
103
+ class_chain
104
+ end
105
+
106
+
85
107
  end
86
108
  end
@@ -2,8 +2,7 @@ module Stratagem::Instrumentation::Models::Persistence; end
2
2
 
3
3
  base = File.join(File.dirname(__FILE__), 'persistence')
4
4
  Dir.entries(base).select {|s| s !~ /^\./ && s != 'util' }.each {|adapter_dir|
5
- require File.join(base, adapter_dir, 'detect')
6
- require File.join(base, adapter_dir, 'tracing')
7
- require File.join(base, adapter_dir, 'metadata')
8
- require File.join(base, adapter_dir, 'extensions')
5
+ ['detect', 'tracing', 'metadata', 'extensions'].each do |extension|
6
+ require File.join(base, adapter_dir, extension) if File.exists?(File.join(base, adapter_dir, extension+'.rb'))
7
+ end
9
8
  }
@@ -7,7 +7,7 @@ module Stratagem::Instrumentation::Models::Persistence::ActiveRecord
7
7
  begin
8
8
  @instance = @model.new unless (@model == ActiveRecord::Base)
9
9
  rescue
10
- puts "ERROR: #{@model.name} could not be instantiated: #{$!.message}"
10
+ puts "ERROR: #{@model} could not be instantiated: #{$!.message}"
11
11
  end
12
12
  end
13
13
 
@@ -23,7 +23,7 @@ module Stratagem::Instrumentation::Models::Persistence::ActiveRecord
23
23
  begin
24
24
  Stratagem::Instrumentation::Models::Association.new(model, a.name.to_sym, a.association_foreign_key.to_sym, klass, a.macro, a.options)
25
25
  rescue
26
- puts "ERROR: #{$!.message}"
26
+ puts "METADATA ERROR: #{$!.message}"
27
27
  end
28
28
  }.compact
29
29
  end
@@ -18,7 +18,7 @@ get '/' do
18
18
  end
19
19
 
20
20
  get '/credentials' do
21
- Stratagem::Authentication.instance.store_credentials(params[:account], params[:api_key], params[:project])
21
+ Stratagem::Authentication.instance.store_credentials(params[:api_key], params[:project])
22
22
  redirect '/'
23
23
  end
24
24
 
@@ -27,9 +27,15 @@ get '/logs' do
27
27
  logger = Stratagem.logger
28
28
  logs = [logger.pop]
29
29
  logs << logger.pop until logger.empty?
30
- logs.to_json
30
+ logs.map {|log|
31
+ {
32
+ :phase => log.phase,
33
+ :details => log.details,
34
+ :level => log.level
35
+ }
36
+ }.to_json
31
37
  else
32
- ""
38
+ null
33
39
  end
34
40
  end
35
41
 
@@ -2,24 +2,26 @@ $(document).ready(function() {
2
2
  var currentPhase = null,
3
3
  $phases = $("li");
4
4
 
5
+ function changePhase(name) {
6
+ $phases.removeClass("active");
7
+ $("#"+name).addClass("active");
8
+ if (name == "complete") {
9
+ window.setTimeout(function() { window.location = $("#completeUrl").attr("href"); }, 1000);
10
+ }
11
+ };
12
+
5
13
  window.setInterval(function() {
6
14
  $.getJSON('/logs', function(data) {
7
15
  var logs = $("#logs .modeling_application ul");
8
- if (data == null)
9
- window.location = $("#completeUrl").attr("href");
16
+ if (data == null) {
17
+ changePhase("complete");
18
+ }
10
19
 
11
20
  $.each(data, function(idx, val) {
12
- var phase = val[0];
13
- var ts = val[1];
14
-
21
+ var phase = val['phase'];
22
+
15
23
  if (phase != currentPhase) {
16
- $phases.removeClass("active");
17
- $("#"+phase).addClass("active");
18
-
19
- if (phase == "complete")
20
- window.setTimeout(function() {
21
- window.location = $("#completeUrl").attr("href");
22
- }, 1000);
24
+ changePhase(phase);
23
25
  }
24
26
  });
25
27
  });
@@ -1,4 +1,4 @@
1
- - phases = ['modeling application', 'mocking models', 'traversing site', 'vulnerability scanning']
1
+ - phases = ['modeling application', 'mocking models', 'traversing site', 'exporting']
2
2
 
3
3
  %html
4
4
  %head
@@ -26,9 +26,9 @@
26
26
  %li{:id => "#{phases[2].gsub(/\s/, '_')}"}
27
27
  crawling your website
28
28
  %li{:id => "#{phases[3].gsub(/\s/, '_')}"}
29
- scanning model for vulnerabilities
29
+ exporting security snapshot
30
30
  %li#complete
31
- analysis complete
31
+ complete
32
32
  %li{:style => "display:none"}
33
33
  %a{:id => "completeUrl", :href => Stratagem::Authentication.instance.project_url}
34
34
 
@@ -7,9 +7,15 @@ module Stratagem
7
7
 
8
8
  MESSAGE_QUEUE = []
9
9
 
10
- Message = Struct.new(:phase, :timestamp, :details)
10
+ Message = Struct.new(:phase, :timestamp, :details, :level)
11
11
  @@blocker = Blocker.new()
12
12
 
13
+ attr_reader :errors
14
+
15
+ def initialize
16
+ @errors = []
17
+ end
18
+
13
19
  def phase(phase)
14
20
  @phase = phase
15
21
  end
@@ -18,8 +24,17 @@ module Stratagem
18
24
  add(Message.new(@phase, Time.now, message))
19
25
  end
20
26
 
27
+ def error(exception)
28
+ puts exception.message
29
+ puts exception.backtrace
30
+ @errors << create_error(exception)
31
+ end
32
+
21
33
  def fatal(exception)
22
-
34
+ puts exception.message
35
+ puts exception.backtrace
36
+ @errors << create_error(exception)
37
+ # add(Message.new(@phase, Time.now, $!.message))
23
38
  end
24
39
 
25
40
  def pop
@@ -33,6 +48,17 @@ module Stratagem
33
48
 
34
49
  private
35
50
 
51
+ def create_error(exception)
52
+ if (exception.kind_of?(Exception))
53
+ { :message => exception.message, :backtrace => exception.backtrace.slice(0,50) }
54
+ elsif (e.kind_of?(String))
55
+ { :message => exception, :backtrace => [] }
56
+ else
57
+ puts "ERROR: unknown error type #{e.class.name}"
58
+ nil
59
+ end
60
+ end
61
+
36
62
  def add(obj)
37
63
  puts obj.details
38
64
  MESSAGE_QUEUE << obj
@@ -3,6 +3,12 @@ end
3
3
 
4
4
  require 'stratagem/model/application'
5
5
  require 'stratagem/model/parse_util'
6
+
7
+ require 'stratagem/model/containers/base'
8
+ require 'stratagem/model/containers/gem'
9
+ require 'stratagem/model/containers/route'
10
+ require 'stratagem/model/containers/plugin'
11
+
6
12
  require 'stratagem/model/components/base'
7
13
  require 'stratagem/model/components/reference'
8
14
  require 'stratagem/model/components/model'
@@ -16,13 +16,13 @@ module Stratagem::Model
16
16
 
17
17
  def initialize
18
18
  log "initializing application model"
19
- @models = ComponentContainer.new self
20
- @controllers = ComponentContainer.new self
21
- @routes = RouteContainer.new self
22
- @views = ComponentContainer.new self
23
- @static_files = ComponentContainer.new self
24
- @gems = GemContainer.new self
25
- @plugins = PluginContainer.new self
19
+ @models = Stratagem::Model::Containers::Base.new self
20
+ @controllers = Stratagem::Model::Containers::Base.new self
21
+ @routes = Stratagem::Model::Containers::Route.new self
22
+ @views = Stratagem::Model::Containers::Base.new self
23
+ @static_files = Stratagem::Model::Containers::Base.new self
24
+ @gems = Stratagem::Model::Containers::Gem.new self
25
+ @plugins = Stratagem::Model::Containers::Plugin.new self
26
26
  end
27
27
 
28
28
  def log(msg)
@@ -31,7 +31,7 @@ module Stratagem::Model
31
31
 
32
32
  def export
33
33
  puts "exporting site model"
34
- puts "references:"
34
+ puts "\tmapping #{Stratagem::Instrumentation::Models::Tracing.invocations_audit.size} references"
35
35
  references = []
36
36
  begin
37
37
  references = Stratagem::Instrumentation::Models::Tracing.invocations_audit.uniq.map {|ia| ia.to_reference.export }.uniq
@@ -39,28 +39,30 @@ module Stratagem::Model
39
39
  puts $!.message
40
40
  puts $!.backtrace
41
41
  end
42
- puts "exporting hash"
42
+
43
+ puts "\tmapping #{Stratagem.logger.errors.size} errors"
44
+ errors = Stratagem.logger.errors.compact.uniq
43
45
  begin
44
- # references = @controllers.map {|c| c.references }.flatten.map {|r| r.export }.uniq
45
46
  h = {
46
47
  :rails_version => rails_version,
47
48
  :rails_environment => Rails.env,
48
49
  :rails_root => Rails.root.to_s,
49
- :models => @models.export,
50
- :controllers => @controllers.export,
51
- :routes => @routes.export,
52
- :views => @views.export,
53
- :gems => @gems.export,
54
50
  :plugins => @plugins.export,
55
- :site_model => crawler ? crawler.export : nil,
56
- :references => references
51
+ :scanning_exceptions_attributes => errors,
52
+
53
+ :models_attributes => @models.export,
54
+ :controllers_attributes => @controllers.export,
55
+ :routes_attributes => @routes.export,
56
+ :views_attributes => @views.export,
57
+ :gems => @gems.export,
58
+ :references_attributes => references,
59
+ :site_attributes => crawler ? crawler.export : {},
57
60
  }
58
61
  rescue
59
62
  puts $!.message
60
63
  puts $!.backtrace
61
64
  end
62
- puts "hash generated"
63
- puts h.to_json
65
+ puts "Sending snapshot of #{h.to_json.size} bytes"
64
66
  h
65
67
  end
66
68
 
@@ -69,120 +71,5 @@ module Stratagem::Model
69
71
  end
70
72
  end
71
73
 
72
- protected
73
-
74
- class GemContainer
75
- include Enumerable
76
-
77
- def initialize(app_model)
78
- @app_model = app_model
79
- @gems = Gem.loaded_specs
80
- end
81
-
82
- def names
83
- @gems.map {|g| g[0] }
84
- end
85
-
86
- def export(options=nil)
87
- @gems.map {|g|
88
- name, spec = g
89
- [name, {:version => spec.version.version}]
90
- }
91
- end
92
-
93
- def each
94
- @gems.each {|spec| yield spec }
95
- end
96
- end
97
-
98
- class ComponentContainer
99
- include Enumerable
100
-
101
- attr_reader :invalid, :missing, :parse_trees, :components, :errors
102
-
103
- def initialize(app_model)
104
- @app_model = app_model
105
- @components = Set.new()
106
- @parse_trees = {}
107
- @invalid = []
108
- @missing = {}
109
- end
110
-
111
- def export(options=nil)
112
- {
113
- :components => @components.to_a.map {|c| c.export }.compact,
114
- :invalid => @invalid.map {|c| c.export }.compact
115
- }
116
- end
117
-
118
- def find
119
- @components.find{|component| yield component }
120
- end
121
-
122
- def clear
123
- @components.clear
124
- end
125
-
126
- def size
127
- @components.size
128
- end
129
-
130
- def -(other)
131
- @components-other
132
- end
133
-
134
- def each
135
- @components.each {|e| yield e }
136
- end
137
-
138
- def map
139
- @components.map {|e| yield e }
140
- end
141
-
142
- def << (component)
143
- if (component.kind_of?(Array))
144
- component.each {|e|
145
- @components << e
146
- e.app_model = @app_model if e.methods_include?(:app_model=)
147
- }
148
- elsif (component.kind_of?(Exception))
149
- errors << component
150
- else
151
- @components << component
152
- component.app_model = @app_model if component.methods_include?(:app_model=)
153
- end
154
- end
155
- end
156
-
157
- class RouteContainer < ComponentContainer
158
- def recognize(page, method = :get)
159
- path = nil
160
- if (page.kind_of?(Stratagem::Crawler::Page))
161
- method = page.method
162
- path = page.path
163
- else
164
- path = page
165
- end
166
-
167
- unless path.nil?
168
- # path = path.gsub('http://www.example.com', '')
169
- route = self.find {|r| r.responds_to?(path, method) }
170
- puts "route: #{route.path}" if route
171
- route
172
- else
173
- nil
174
- end
175
- end
176
- end
177
-
178
- class PluginContainer < ComponentContainer
179
- def names
180
- components.map {|plugin| plugin.name }
181
- end
182
-
183
- def export(options=nil)
184
- names
185
- end
186
- end
187
74
  end
188
75