stratagem 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +99 -0
- data/Rakefile +17 -0
- data/bin/stratagem +10 -0
- data/init.rb +2 -0
- data/lib/bootstrap.rb +31 -0
- data/lib/stratagem/authentication.rb +64 -0
- data/lib/stratagem/auto_mock/aquifer.rb +86 -0
- data/lib/stratagem/auto_mock/factory.rb +213 -0
- data/lib/stratagem/auto_mock/value_generator.rb +174 -0
- data/lib/stratagem/auto_mock.rb +6 -0
- data/lib/stratagem/blocker.rb +16 -0
- data/lib/stratagem/client.rb +32 -0
- data/lib/stratagem/command.rb +13 -0
- data/lib/stratagem/commands/analyze.rb +22 -0
- data/lib/stratagem/commands/base.rb +11 -0
- data/lib/stratagem/commands/devel_crawl.rb +27 -0
- data/lib/stratagem/commands/devel_mock.rb +10 -0
- data/lib/stratagem/commands.rb +7 -0
- data/lib/stratagem/crawler/authentication.rb +109 -0
- data/lib/stratagem/crawler/form.rb +101 -0
- data/lib/stratagem/crawler/html_utils.rb +92 -0
- data/lib/stratagem/crawler/session.rb +296 -0
- data/lib/stratagem/crawler/site_model.rb +138 -0
- data/lib/stratagem/crawler/trace_utils.rb +10 -0
- data/lib/stratagem/crawler.rb +9 -0
- data/lib/stratagem/extensions/class.rb +9 -0
- data/lib/stratagem/extensions/hash.rb +16 -0
- data/lib/stratagem/extensions/module.rb +11 -0
- data/lib/stratagem/extensions/object.rb +15 -0
- data/lib/stratagem/extensions/red_parse.rb +86 -0
- data/lib/stratagem/extensions/string.rb +20 -0
- data/lib/stratagem/extensions.rb +6 -0
- data/lib/stratagem/framework_extensions/controllers/action_controller.rb +10 -0
- data/lib/stratagem/framework_extensions/controllers/action_mailer.rb +12 -0
- data/lib/stratagem/framework_extensions/controllers.rb +5 -0
- data/lib/stratagem/framework_extensions/models/adapters/active_model/detect.rb +7 -0
- data/lib/stratagem/framework_extensions/models/adapters/active_model/extensions.rb +35 -0
- data/lib/stratagem/framework_extensions/models/adapters/active_model/metadata.rb +103 -0
- data/lib/stratagem/framework_extensions/models/adapters/active_model/tracing.rb +50 -0
- data/lib/stratagem/framework_extensions/models/adapters/authlogic/detect.rb +11 -0
- data/lib/stratagem/framework_extensions/models/adapters/authlogic/extensions.rb +10 -0
- data/lib/stratagem/framework_extensions/models/adapters/authlogic/metadata.rb +30 -0
- data/lib/stratagem/framework_extensions/models/adapters/authlogic/tracing.rb +4 -0
- data/lib/stratagem/framework_extensions/models/adapters/common/authentication_metadata.rb +21 -0
- data/lib/stratagem/framework_extensions/models/adapters/restful_authentication/detect.rb +13 -0
- data/lib/stratagem/framework_extensions/models/adapters/restful_authentication/extensions.rb +19 -0
- data/lib/stratagem/framework_extensions/models/adapters/restful_authentication/metadata.rb +30 -0
- data/lib/stratagem/framework_extensions/models/adapters/restful_authentication/tracing.rb +4 -0
- data/lib/stratagem/framework_extensions/models/annotations.rb +79 -0
- data/lib/stratagem/framework_extensions/models/detect.rb +7 -0
- data/lib/stratagem/framework_extensions/models/metadata.rb +85 -0
- data/lib/stratagem/framework_extensions/models/mocking.rb +23 -0
- data/lib/stratagem/framework_extensions/models/tracing.rb +71 -0
- data/lib/stratagem/framework_extensions/models.rb +21 -0
- data/lib/stratagem/framework_extensions/rails.rb +8 -0
- data/lib/stratagem/framework_extensions.rb +6 -0
- data/lib/stratagem/interface/browser.rb +37 -0
- data/lib/stratagem/interface/public/images/backgrounds/content.png +0 -0
- data/lib/stratagem/interface/public/images/backgrounds/shadow.png +0 -0
- data/lib/stratagem/interface/public/javascripts/jquery-1.4.2.min.js +154 -0
- data/lib/stratagem/interface/public/javascripts/stratagem.js +27 -0
- data/lib/stratagem/interface/public/javascripts/stratagem_debug.js +53 -0
- data/lib/stratagem/interface/public/stylesheets/960.css +1 -0
- data/lib/stratagem/interface/public/stylesheets/reset.css +10 -0
- data/lib/stratagem/interface/public/stylesheets/stratagem.css +20 -0
- data/lib/stratagem/interface/public/stylesheets/stratagem_debug.css +20 -0
- data/lib/stratagem/interface/views/debug.haml +43 -0
- data/lib/stratagem/interface/views/index.haml +35 -0
- data/lib/stratagem/labs/auto_mock.rb +7 -0
- data/lib/stratagem/labs/crawler.rb +0 -0
- data/lib/stratagem/logger.rb +46 -0
- data/lib/stratagem/model/application.rb +157 -0
- data/lib/stratagem/model/components/base.rb +55 -0
- data/lib/stratagem/model/components/controller.rb +118 -0
- data/lib/stratagem/model/components/model.rb +170 -0
- data/lib/stratagem/model/components/reference.rb +30 -0
- data/lib/stratagem/model/components/route.rb +53 -0
- data/lib/stratagem/model/components/static_file.rb +18 -0
- data/lib/stratagem/model/components/view.rb +186 -0
- data/lib/stratagem/model/parse_util.rb +61 -0
- data/lib/stratagem/model.rb +12 -0
- data/lib/stratagem/model_builder.rb +146 -0
- data/lib/stratagem/recipes/deploy.rb +30 -0
- data/lib/stratagem/scan/checks/capistrano/secure_deploy.rb +43 -0
- data/lib/stratagem/scan/checks/email_address.rb +15 -0
- data/lib/stratagem/scan/checks/error_pages.rb +25 -0
- data/lib/stratagem/scan/checks/filter_parameter_logging.rb +6 -0
- data/lib/stratagem/scan/checks/mongo_mapper/base.rb +19 -0
- data/lib/stratagem/scan/checks/mongo_mapper/foreign_keys_exposed.rb +32 -0
- data/lib/stratagem/scan/checks/routes.rb +16 -0
- data/lib/stratagem/scan/checks/ssl/secure_login_page.rb +19 -0
- data/lib/stratagem/scan/checks/ssl/secure_login_submit.rb +18 -0
- data/lib/stratagem/scan/result.rb +45 -0
- data/lib/stratagem/scan.rb +19 -0
- data/lib/stratagem/scanner.rb +32 -0
- data/lib/stratagem/site_crawler.rb +47 -0
- data/lib/stratagem/snapshot.rb +33 -0
- data/lib/stratagem.rb +77 -0
- data/lib/tasks/_old_stratagem.rake +99 -0
- data/stratagem.gemspec +56 -0
- metadata +380 -0
@@ -0,0 +1,296 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
require 'action_pack'
|
4
|
+
require 'action_controller'
|
5
|
+
|
6
|
+
class CrawlError < StratagemError; end
|
7
|
+
|
8
|
+
# require 'rack/lint'
|
9
|
+
# module Rack
|
10
|
+
# # Rack::Lint validates your application and the requests and
|
11
|
+
# # responses according to the Rack spec.
|
12
|
+
#
|
13
|
+
# class Lint
|
14
|
+
# alias_method :old_call, :call
|
15
|
+
#
|
16
|
+
# def call(env)
|
17
|
+
# data = env['rack.input']
|
18
|
+
# data.set_encoding(Encoding::ASCII_8BIT) if data.respond_to?(:set_encoding)
|
19
|
+
#
|
20
|
+
# status, headers, body = old_call(env)
|
21
|
+
# body.close # fix for Rack
|
22
|
+
# [status,headers,body]
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
|
27
|
+
PHASES = [:unauthenticated,:authenticated]
|
28
|
+
|
29
|
+
module Stratagem::Crawler::Session
|
30
|
+
include ActionController::Integration::Runner
|
31
|
+
include Stratagem::Crawler::HtmlUtils
|
32
|
+
include Stratagem::Crawler::TraceUtils
|
33
|
+
include Stratagem::Crawler::Authentication
|
34
|
+
|
35
|
+
def log(msg)
|
36
|
+
Stratagem.logger.debug msg
|
37
|
+
end
|
38
|
+
|
39
|
+
def crawler_session(application_model=nil)
|
40
|
+
@model = application_model
|
41
|
+
@model ||= Stratagem::ModelBuilder.new.run
|
42
|
+
@redirect_proc = Proc.new {|redirect_url| handle_redirect(redirect_url) }
|
43
|
+
@parameter_types = {} # routecontainer -> {:route_segment => Model, :route_segment => Model}
|
44
|
+
open_session do |session|
|
45
|
+
@session = session
|
46
|
+
phase(:unauthenticated)
|
47
|
+
yield
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def application_model
|
52
|
+
@model
|
53
|
+
end
|
54
|
+
|
55
|
+
def site_models
|
56
|
+
@site_models ||= {} # builds models of the site for various phases of analysis, see PHASES
|
57
|
+
end
|
58
|
+
|
59
|
+
def site_model
|
60
|
+
raise Stratagem::Crawler::CrawlError.new("Phase not specified") unless @current_model
|
61
|
+
@current_model
|
62
|
+
end
|
63
|
+
|
64
|
+
def phase(new_phase)
|
65
|
+
@current_phase = new_phase
|
66
|
+
@current_model = site_models[@current_phase] ||= Stratagem::Crawler::SiteModel.new
|
67
|
+
end
|
68
|
+
|
69
|
+
def phases
|
70
|
+
site_models
|
71
|
+
end
|
72
|
+
|
73
|
+
def display
|
74
|
+
# print out pages and inbound / outbound links
|
75
|
+
site_model.pages.each do |page|
|
76
|
+
log "Page: #{page.url} - #{page.title} - #{page.response.code}"
|
77
|
+
page.outbound_edges.each do |edge|
|
78
|
+
log "\tout: #{edge.to.url} - #{edge.to.title} - #{edge.to.route}"
|
79
|
+
end
|
80
|
+
page.inbound_edges.each do |edge|
|
81
|
+
log "\tin: #{edge.from.url} - #{edge.from.title}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def crawl
|
87
|
+
# grab all pages independently
|
88
|
+
@model.routes.invalid.each {|route_container|
|
89
|
+
puts "skipping invalid route #{route_container.route.to_s}"
|
90
|
+
}
|
91
|
+
|
92
|
+
authentication_controller = nil
|
93
|
+
if (@current_phase == :authenticated)
|
94
|
+
route = @model.routes.recognize(authentication.login_page)
|
95
|
+
authentication_controller = route.controller
|
96
|
+
end
|
97
|
+
|
98
|
+
@model.routes.each {|route_container|
|
99
|
+
if authentication_controller && route_container.controller && (route_container.controller.klass == authentication_controller.klass)
|
100
|
+
log "Skipping authentication routes #{route_container.route.to_s}"
|
101
|
+
else
|
102
|
+
visit(route_container) unless @model.routes.invalid.include?(route_container)
|
103
|
+
end
|
104
|
+
}
|
105
|
+
|
106
|
+
# establish edges on site graph
|
107
|
+
site_model.pages.each do |page|
|
108
|
+
site_model.add_edge(page, page.redirected_to, :redirect) if page.redirected?
|
109
|
+
end
|
110
|
+
|
111
|
+
site_model
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def visit(route_container)
|
117
|
+
puts "visiting #{route_container.route}"
|
118
|
+
build_urls(route_container).each do |route_info|
|
119
|
+
call_route(route_container, route_info)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def call_route(route, route_info)
|
124
|
+
puts 'CALLING: .'+route_info[:verb].downcase+". - "+route_info[:path]
|
125
|
+
verb = route_info[:verb].downcase
|
126
|
+
verb = 'get' if verb == '' || verb == 'any'
|
127
|
+
case verb
|
128
|
+
when 'get'
|
129
|
+
do_get(route, route_info[:path])
|
130
|
+
puts response.code if response
|
131
|
+
when 'post'
|
132
|
+
when 'put'
|
133
|
+
when 'delete'
|
134
|
+
else
|
135
|
+
raise "Unsupported verb: #{route[:verb]}"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Builds a list of string URLs for a given route. This is done
|
140
|
+
# by replacing :xyz_id segments in the route with known values
|
141
|
+
# from the well
|
142
|
+
def build_urls(route_container)
|
143
|
+
urls = []
|
144
|
+
route = route_container.route
|
145
|
+
parameter_types = (@parameter_types[route_container] ||= resolve_parameter_types(route_container))
|
146
|
+
route_infos, params = build_url(route_container, parameter_types)
|
147
|
+
puts "route: #{route_container.route.to_s} permutations:"
|
148
|
+
route_infos.each do |info|
|
149
|
+
puts "\t#{info[:path]}"
|
150
|
+
end
|
151
|
+
route_infos
|
152
|
+
end
|
153
|
+
|
154
|
+
def url_permutations(meta_segments, segment_stack=[], &block)
|
155
|
+
if (segment_stack.size == meta_segments.size)
|
156
|
+
yield segment_stack
|
157
|
+
else
|
158
|
+
cursor = segment_stack.size
|
159
|
+
options = meta_segments[cursor]
|
160
|
+
if (options.kind_of?(Array))
|
161
|
+
options.each do |option|
|
162
|
+
url_permutations(meta_segments, segment_stack + [option], &block)
|
163
|
+
end
|
164
|
+
else
|
165
|
+
url_permutations(meta_segments, segment_stack + [options], &block)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def build_url(route_container, parameter_types={})
|
171
|
+
params = {}
|
172
|
+
route = route_container.route
|
173
|
+
name = route.to_s
|
174
|
+
verb = route.conditions[:method].to_s
|
175
|
+
|
176
|
+
parameter_types ||= {}
|
177
|
+
|
178
|
+
i = 12345
|
179
|
+
segs = route.segments.inject([]) {|accumulated,segment|
|
180
|
+
s = segment.to_s
|
181
|
+
if (s =~ /^:/)
|
182
|
+
model = parameter_types[s]
|
183
|
+
value = nil
|
184
|
+
if (model)
|
185
|
+
value = (Stratagem::AutoMock::Aquifer.instance.instances_of model).map {|inst|
|
186
|
+
attr_name = s.gsub(/^:/, '').to_sym
|
187
|
+
if inst.methods_include?(attr_name)
|
188
|
+
puts "#{attr_name} is a method on the object"
|
189
|
+
inst.send(attr_name)
|
190
|
+
else
|
191
|
+
puts "#{attr_name} is being mapped to the id on the object"
|
192
|
+
inst.id
|
193
|
+
end
|
194
|
+
}
|
195
|
+
else
|
196
|
+
i += 1
|
197
|
+
value = [i]
|
198
|
+
end
|
199
|
+
accumulated << value
|
200
|
+
params[s] = value
|
201
|
+
else
|
202
|
+
accumulated << s
|
203
|
+
end
|
204
|
+
accumulated
|
205
|
+
}
|
206
|
+
|
207
|
+
routes = []
|
208
|
+
reqs = route.requirements.empty? ? "" : route.requirements.inspect
|
209
|
+
url_permutations(segs) do |segments|
|
210
|
+
path = segments.join('').gsub('(.:format)', '').gsub(/\/$/, '')
|
211
|
+
permutation = {:name => name, :verb => verb, :segs => segs, :reqs => reqs, :path => path}
|
212
|
+
routes << permutation
|
213
|
+
end
|
214
|
+
|
215
|
+
[routes, params]
|
216
|
+
end
|
217
|
+
|
218
|
+
def handle_redirect(redirect_url)
|
219
|
+
existing_pages = site_model.pages_for(response.redirect_url)
|
220
|
+
if (existing_pages.size > 0)
|
221
|
+
existing_pages.first
|
222
|
+
else
|
223
|
+
invocation_delta = model_invocations_for_request do
|
224
|
+
get redirect_url
|
225
|
+
end
|
226
|
+
|
227
|
+
site_model.add(nil, response) {|redirect_url|
|
228
|
+
# TODO - record as bug!
|
229
|
+
puts "recursive redirect #{redirect_url}"
|
230
|
+
}
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def do_get(route,path)
|
235
|
+
begin
|
236
|
+
get path
|
237
|
+
site_model.add(route, response) {|redirect_url| @redirect_proc.call(redirect_url) }
|
238
|
+
rescue
|
239
|
+
puts $!.message
|
240
|
+
puts path
|
241
|
+
#puts $!.backtrace
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def resolve_parameter_types(route_container)
|
246
|
+
puts "resolving parameter types for #{route_container.route}"
|
247
|
+
resolved_parameters = {}
|
248
|
+
route_infos, params = build_url(route_container, resolved_parameters)
|
249
|
+
route_info = route_infos.first
|
250
|
+
unknown_params = params.keys
|
251
|
+
puts "unknown params: #{unknown_params}"
|
252
|
+
progress = nil
|
253
|
+
while ((unknown_params.size > 0) && (progress.nil? || (progress > 0)))
|
254
|
+
progress = 0
|
255
|
+
|
256
|
+
# p route_infos
|
257
|
+
# puts "---"
|
258
|
+
delta = model_invocations_for_request do
|
259
|
+
call_route(route_container, route_info)
|
260
|
+
end
|
261
|
+
|
262
|
+
puts "delta - #{delta.size}"
|
263
|
+
#p delta
|
264
|
+
|
265
|
+
unknown_params.clone.each do |key|
|
266
|
+
value = params[key]
|
267
|
+
value_s = params[key].map {|v| v.to_s }
|
268
|
+
delta.each do |invocation|
|
269
|
+
puts "#{route_info[:path]} - #{invocation.model_class.name} - #{invocation.method} - #{invocation.args.inspect} - #{value_s}"
|
270
|
+
|
271
|
+
# TODO inspect is a hack, refactor
|
272
|
+
if (invocation.args.include?(value.first)) || (invocation.args.inspect.include?('"'+value.first.to_s+'"'))
|
273
|
+
# found match
|
274
|
+
|
275
|
+
puts "\tresolved #{key} to #{invocation.model_class}"
|
276
|
+
unknown_params.delete(key)
|
277
|
+
resolved_parameters[key] = invocation.model_class
|
278
|
+
progress += 1
|
279
|
+
|
280
|
+
break
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
route_infos, params = build_url(route_container, resolved_parameters)
|
286
|
+
route_info = route_infos.first
|
287
|
+
end
|
288
|
+
if (resolved_parameters.size > 0)
|
289
|
+
resolved_parameters
|
290
|
+
else
|
291
|
+
nil
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
|
296
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
module Stratagem::Crawler
|
2
|
+
class SiteModel
|
3
|
+
include Stratagem::Crawler::HtmlUtils
|
4
|
+
|
5
|
+
attr_reader :pages, :edges
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@pages = []
|
9
|
+
@edges = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def export
|
13
|
+
{
|
14
|
+
:pages => @pages.map {|page| page.export },
|
15
|
+
:edges => @edges.map {|edge| edge.export }
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_edge(from,to,type)
|
20
|
+
self.edges << Edge.new(from,to,type)
|
21
|
+
end
|
22
|
+
|
23
|
+
def add(route, response, &block)
|
24
|
+
page = Page.new(self, response, &block)
|
25
|
+
self.pages << page
|
26
|
+
page
|
27
|
+
end
|
28
|
+
|
29
|
+
def pages_for(id)
|
30
|
+
if (id.kind_of?(String))
|
31
|
+
pages.select {|page| page.url == id }
|
32
|
+
else
|
33
|
+
pages.select {|page| page.route == id }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
class Edge
|
40
|
+
# type -> :link, :redirect,
|
41
|
+
attr_accessor :from, :to, :type
|
42
|
+
|
43
|
+
def initialize(from, to, type)
|
44
|
+
@from = from
|
45
|
+
@to = to
|
46
|
+
@type = type
|
47
|
+
end
|
48
|
+
|
49
|
+
def export
|
50
|
+
{
|
51
|
+
:from => from.object_id,
|
52
|
+
:to => to.object_id,
|
53
|
+
:type => type
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class Page
|
59
|
+
include Stratagem::Crawler::HtmlUtils
|
60
|
+
|
61
|
+
attr_reader :response
|
62
|
+
|
63
|
+
attr_accessor :url
|
64
|
+
attr_accessor :path
|
65
|
+
attr_accessor :method
|
66
|
+
attr_accessor :redirected_to
|
67
|
+
attr_accessor :document
|
68
|
+
|
69
|
+
def initialize(site_model, response, &block)
|
70
|
+
@site_model = site_model
|
71
|
+
init(response, &block)
|
72
|
+
end
|
73
|
+
|
74
|
+
def route
|
75
|
+
Stratagem::Model::Application.instance.routes.recognize(self)
|
76
|
+
end
|
77
|
+
|
78
|
+
def export
|
79
|
+
{
|
80
|
+
:external_id => self.object_id,
|
81
|
+
:url => url,
|
82
|
+
:path => path,
|
83
|
+
:method => method,
|
84
|
+
:redirected_to_page_external_id => redirected_to ? redirected_to.object_id : nil,
|
85
|
+
:route_external_id => route.object_id
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
def init(response, &block)
|
90
|
+
@response = response.clone
|
91
|
+
@url = response.request.url
|
92
|
+
@path = response.request.path
|
93
|
+
@method = response.request.method
|
94
|
+
@document = Nokogiri::HTML(response.body)
|
95
|
+
self.redirected_to = block.call(response.redirect_url) if response.redirect?
|
96
|
+
end
|
97
|
+
|
98
|
+
def reload(&block)
|
99
|
+
# TODO - should support all the verbs and params, but
|
100
|
+
# hack together for now to reload the authenticity token
|
101
|
+
response = yield url
|
102
|
+
init(response) {|redirected_to| }
|
103
|
+
end
|
104
|
+
|
105
|
+
def redirected?
|
106
|
+
!self.redirected_to.nil?
|
107
|
+
end
|
108
|
+
|
109
|
+
def forms
|
110
|
+
self.parse_forms(@document)
|
111
|
+
end
|
112
|
+
|
113
|
+
def login_form
|
114
|
+
self.find_login_form(@document)
|
115
|
+
end
|
116
|
+
|
117
|
+
def to_html
|
118
|
+
@document.to_html
|
119
|
+
end
|
120
|
+
|
121
|
+
def outbound_edges(type=nil)
|
122
|
+
@site_model.edges.select {|edge| edge.from == self && (type.nil? || (type == edge.type)) }
|
123
|
+
end
|
124
|
+
|
125
|
+
def inbound_edges(type=nil)
|
126
|
+
@site_model.edges.select {|edge| (edge.to == self) && (type.nil? || (type == edge.type)) }
|
127
|
+
end
|
128
|
+
|
129
|
+
def title
|
130
|
+
unless @title
|
131
|
+
title = (@document/'head title').first
|
132
|
+
@title = title.inner_html if title
|
133
|
+
end
|
134
|
+
@title
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Stratagem::Crawler
|
2
|
+
module TraceUtils
|
3
|
+
def model_invocations_for_request()
|
4
|
+
prior_invocations = ActiveRecord::Base.stratagem.invocations_audit.clone
|
5
|
+
yield
|
6
|
+
post_invocations = ActiveRecord::Base.stratagem.invocations_audit.clone
|
7
|
+
delta = post_invocations - prior_invocations
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Stratagem::Crawler
|
2
|
+
end
|
3
|
+
|
4
|
+
require 'stratagem/crawler/form'
|
5
|
+
require 'stratagem/crawler/trace_utils'
|
6
|
+
require 'stratagem/crawler/html_utils'
|
7
|
+
require 'stratagem/crawler/authentication'
|
8
|
+
require 'stratagem/crawler/session'
|
9
|
+
require 'stratagem/crawler/site_model'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Hash
|
2
|
+
alias_method :ruby_get, :[]
|
3
|
+
attr_reader :hash_reads, :hash_writes
|
4
|
+
|
5
|
+
def [](name)
|
6
|
+
(@hash_reads ||= []) << name if (@auditing)
|
7
|
+
ruby_get name
|
8
|
+
end
|
9
|
+
|
10
|
+
def enable_auditing
|
11
|
+
@auditing = true
|
12
|
+
end
|
13
|
+
|
14
|
+
# def []=
|
15
|
+
# end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Object
|
2
|
+
def methods_include?(name)
|
3
|
+
methods.include?(name.to_sym) || methods.include?(name.to_s)
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.subclasses
|
7
|
+
classes = []
|
8
|
+
ObjectSpace.each_object(Class) do |c|
|
9
|
+
next unless c.superclass == self || c.ancestors.include?(self)
|
10
|
+
next if c == self
|
11
|
+
classes << c
|
12
|
+
end
|
13
|
+
classes
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
class RedParse::Node
|
2
|
+
def talking_about?(model)
|
3
|
+
talking_about = false
|
4
|
+
|
5
|
+
if self.receiver.methods_include?(:name)
|
6
|
+
if ((self.receiver) && self.receiver.name == model.klass.name)
|
7
|
+
# puts "\treceiver name #{self.receiver.name} is equal to #{model.klass.name}"
|
8
|
+
talking_about = true
|
9
|
+
elsif (self.receiver)
|
10
|
+
name = self.receiver.name
|
11
|
+
# if it's lowercase then it's possibly referencing an association
|
12
|
+
if (name.downcase == name)
|
13
|
+
# look through the models to see if one has an association with this name and class
|
14
|
+
Stratagem::Model::Application.instance.models.each do |app_model|
|
15
|
+
talking_about = app_model.association_match?(name, model)
|
16
|
+
break if talking_about
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
talking_about
|
23
|
+
end
|
24
|
+
|
25
|
+
# iterates the node and figures out the class of the leaf that a call is talking about
|
26
|
+
def dereference
|
27
|
+
# recursion is evil
|
28
|
+
path = []
|
29
|
+
current_node = self
|
30
|
+
# puts "current:#{current_node.inspect}"
|
31
|
+
if (current_node.kind_of?(RedParse::VarNode))
|
32
|
+
path << current_node.name if current_node.methods_include?(:name)
|
33
|
+
else
|
34
|
+
while (current_node.methods_include?(:receiver) && (current_node = current_node.receiver) != nil)
|
35
|
+
path << current_node.name if current_node.methods_include?(:name)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
# p path
|
39
|
+
previous = nil
|
40
|
+
path.reverse!
|
41
|
+
path.map! {|element|
|
42
|
+
new_val = element
|
43
|
+
if (element.downcase != element)
|
44
|
+
# class?
|
45
|
+
begin
|
46
|
+
new_val = Class.class_eval(element)
|
47
|
+
rescue
|
48
|
+
logger.error "Unable to load class #{element} in #{path.inspect}"
|
49
|
+
end
|
50
|
+
elsif (element.pluralize == element)
|
51
|
+
# collection?
|
52
|
+
if (previous)
|
53
|
+
if (previous.kind_of?(Class))
|
54
|
+
# look up the loaded model of the class
|
55
|
+
model = Stratagem::Model::Application.instance.models.find {|m| m.klass == previous }
|
56
|
+
if (model)
|
57
|
+
match = previous.reflect_on_all_associations.find {|assoc| assoc.name.to_sym == element.to_sym }
|
58
|
+
if (match)
|
59
|
+
new_val = Class.class_eval(match.class_name)
|
60
|
+
else
|
61
|
+
logger.error "I give up, cannot determine what #{element} is in #{path.inspect}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
else
|
65
|
+
logger.error "Unknown previous type when trying to dereference a collection #{element} in #{path.inspect}"
|
66
|
+
end
|
67
|
+
else
|
68
|
+
logger.error "Unable to determine prior element for #{element} in #{path.inspect}"
|
69
|
+
end
|
70
|
+
elsif previous
|
71
|
+
# call to the class / object in the previous statement
|
72
|
+
if (['find','all','first','last'].include?(element))
|
73
|
+
# then expect to receive an instance of the previous type
|
74
|
+
new_val = previous
|
75
|
+
end
|
76
|
+
end
|
77
|
+
previous = new_val
|
78
|
+
new_val
|
79
|
+
}
|
80
|
+
path.last
|
81
|
+
end
|
82
|
+
|
83
|
+
def logger
|
84
|
+
Rails.logger
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class String
|
2
|
+
def stratagem_strip_erb
|
3
|
+
self.gsub(/^[<%=]+/, '').gsub(/[-%>]+$/,'').strip
|
4
|
+
end
|
5
|
+
|
6
|
+
def stratagem_contains_token?(token)
|
7
|
+
match = false
|
8
|
+
[
|
9
|
+
Regexp.compile("^#{token}$", true),
|
10
|
+
Regexp.compile("^#{token}[^A-Za-z0-9]", true),
|
11
|
+
Regexp.compile("[^A-Za-z0-9]#{token}$", true),
|
12
|
+
].each do |regex|
|
13
|
+
if (regex.match(self))
|
14
|
+
match = true
|
15
|
+
break
|
16
|
+
end
|
17
|
+
end
|
18
|
+
match
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class ActiveRecord::Base
|
2
|
+
class << self
|
3
|
+
def removed_methods=(methods)
|
4
|
+
@@removed_methods = methods
|
5
|
+
end
|
6
|
+
|
7
|
+
def removed_methods
|
8
|
+
@@removed_methods
|
9
|
+
end
|
10
|
+
|
11
|
+
def removed_validators=(validators)
|
12
|
+
@@removed_validators = validators
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
Stratagem::ApplicationExtensions::Models::Annotations.configure(self)
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
module ActiveRecord::Validations::ClassMethods
|
21
|
+
@@removed_validators = []
|
22
|
+
|
23
|
+
instance_methods.each do |m|
|
24
|
+
if (m =~ /^validates_/) && (m !~ /validates_each/)
|
25
|
+
alias_method "old_#{m}", m
|
26
|
+
undef_method m
|
27
|
+
puts "removing validator #{m}"
|
28
|
+
@@removed_validators << m.to_sym
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def removed_validators
|
33
|
+
@@removed_validators
|
34
|
+
end
|
35
|
+
end
|