stratagem 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,38 @@
1
+ # Locates an instance of a user from configured credentials
2
+
3
+ module Stratagem::AutoMock
4
+ module UserLoader
5
+ def load_user_from_configuration(credentials)
6
+ possible_matches = []
7
+ auth_creds = credentials.authentication_parameters
8
+ model = credentials.model.constantize
9
+ @all_user_objects ||= model.all
10
+ log "\tloaded #{@all_user_objects.size} users from the database"
11
+ @all_user_objects.each do |user|
12
+ matching_attributes = matching_credentials(auth_creds, user)
13
+ possible_matches << [user, matching_attributes] if matching_attributes > 0
14
+ end
15
+
16
+ match = possible_matches.sort {|a,b| a[1] <=> b[1] }.last
17
+ if (match)
18
+ user = match.first
19
+ auth_creds.each {|k,v| user.stratagem.mock_attributes[k] = v }
20
+ user
21
+ else
22
+ nil
23
+ end
24
+ end
25
+
26
+ def matching_credentials(authentication_parameters, user)
27
+ count = 0
28
+ authentication_parameters.each do |attribute, value|
29
+ begin
30
+ count += 1 if (value == user.send(attribute.to_sym))
31
+ rescue
32
+ end
33
+ end
34
+ count
35
+ end
36
+
37
+ end
38
+ end
@@ -1,4 +1,6 @@
1
1
  require 'uri'
2
+ require 'zlib'
3
+ require 'base64'
2
4
 
3
5
  module Stratagem
4
6
  class Client
@@ -7,18 +9,29 @@ module Stratagem
7
9
  end
8
10
 
9
11
  def send(snapshot)
12
+ Stratagem.logger.phase('exporting')
10
13
  Stratagem.logger.debug "Sending report to server"
14
+
11
15
  url = URI.parse("#{@authentication.base_url}/snapshots")
12
16
  req = Stratagem.ssl? ? Net::HTTPS::Post.new(url.path) : Net::HTTP::Post.new(url.path)
17
+ model = snapshot.model.export.to_json
18
+ puts "Size: #{model.size}"
19
+ compressed = Zlib::Deflate.deflate(model, Zlib::BEST_COMPRESSION)
20
+ base64 = Base64.encode64(compressed)
21
+ puts "Compressed size: #{compressed.size}"
22
+ puts "Compressed size base 64: #{base64.size}"
13
23
 
14
24
  req.set_form_data({
15
25
  'auth_token' => @authentication.credentials[:token],
16
26
  'project_id' => @authentication.credentials[:project],
17
27
  'timestamp' => snapshot.timestamp.to_i,
18
- 'model' => snapshot.model.export.to_json
28
+ 'model' => model
19
29
  }, ';')
20
30
  client = Stratagem.ssl? ? Net::HTTPS.new(url.host, url.port) : Net::HTTP.new(url.host, url.port)
21
- res = client.start {|http| http.request(req) }
31
+ res = client.start {|http|
32
+ http.read_timeout= 360
33
+ http.request(req)
34
+ }
22
35
  puts "response:"
23
36
  case res
24
37
  when Net::HTTPSuccess, Net::HTTPRedirection
@@ -27,8 +40,6 @@ module Stratagem
27
40
  else
28
41
  res.error!
29
42
  end
30
-
31
- Stratagem.logger.phase('complete')
32
43
  end
33
44
  end
34
45
  end
@@ -0,0 +1,19 @@
1
+ module Stratagem
2
+ module Configuration
3
+ class AuthAuth
4
+ attr_accessor :model, :authorized_to_view_others_data, :authorized_to_modify_others_data, :authorized_to_upload_files, :authentication_parameters
5
+
6
+ def initialize
7
+ self.authorized_to_view_others_data = true
8
+ self.authorized_to_modify_others_data = false
9
+ self.authorized_to_upload_files = true
10
+ self.model = 'User'
11
+ end
12
+
13
+ def credentials(authentication_parameters)
14
+ self.authentication_parameters = authentication_parameters
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ module Stratagem
2
+ module Configuration
3
+ class Core
4
+ attr_accessor :use_transactional_crawling, :use_automatic_mocking
5
+ attr_reader :credentials
6
+
7
+ def initialize
8
+ @use_automatic_mocking = false
9
+ @use_transactional_crawling = true
10
+ @credentials = []
11
+ end
12
+
13
+ def authenticate_with(&block)
14
+ user = AuthAuth.new
15
+ block.call(user)
16
+ @credentials << user
17
+ end
18
+ end
19
+ end
20
+ end
@@ -7,20 +7,25 @@ module Stratagem::Crawler
7
7
  module Authentication
8
8
  include Stratagem::Crawler::TraceUtils
9
9
 
10
- def users
10
+ def user_model
11
+ model = nil
11
12
  page = find_login_form
12
- users = []
13
13
  if (page)
14
14
  form = page.login_form
15
15
  attr_names = form.inputs.map {|input| input.guess_attribute.to_sym }
16
16
  model = guess_login_model(attr_names)
17
- if (model)
18
- users = aquifer.instances_of(model.klass)
19
- else
20
- log "ERROR: Unable to determine user model"
21
- end
17
+ end
18
+ log "Authenticating with model #{model.klass.name}"
19
+ model
20
+ end
21
+
22
+ def users
23
+ users = []
24
+ model = user_model()
25
+ if (model)
26
+ users = aquifer.instances_of(model.klass).select {|user| user.stratagem.mock_attributes.size > 0 }
22
27
  else
23
- log "ERROR: Could not find login form"
28
+ log "ERROR: Unable to determine authentication model and / or form"
24
29
  end
25
30
  users
26
31
  end
@@ -54,8 +59,7 @@ module Stratagem::Crawler
54
59
  authentication.success = authentication.response_page.login_form.nil?
55
60
  end
56
61
  rescue
57
- puts $!.message
58
- puts $!.backtrace
62
+ Stratagem.logger.error($!)
59
63
  end
60
64
 
61
65
  puts "authenticated? #{authentication.success}"
@@ -80,12 +84,13 @@ module Stratagem::Crawler
80
84
 
81
85
  # if the first page has one or more redirects to it then use it; otherwise select with page with the fewest inputs
82
86
  if (possibilities.first.inbound_edges(:redirect).size > 0)
83
- return possibilities.first
87
+ authentication.login_page = possibilities.first
88
+ return authentication.login_page
84
89
  else
85
90
  page = possibilities.sort {|a,b| a.login_form.inputs.size <=> b.login_form.inputs.size }.first
86
91
  if (page)
87
92
  authentication.login_page = page
88
- return page
93
+ return authentication.login_page
89
94
  end
90
95
  end
91
96
  else
@@ -0,0 +1,40 @@
1
+ module Stratagem::Crawler::Authentication
2
+ class AuthenticationData
3
+ attr_accessor :success, :login_page, :form, :response_page, :ssl, :authenticated_with
4
+ end
5
+
6
+ class Base
7
+ include Stratagem::Crawler::TraceUtils
8
+
9
+ def user_models
10
+ model = nil
11
+ page = find_login_form
12
+ if (page)
13
+ form = page.login_form
14
+ attr_names = form.inputs.map {|input| input.guess_attribute.to_sym }
15
+ model = guess_login_model(attr_names)
16
+ end
17
+ log "Authenticating with model #{model.klass.name}"
18
+ [model]
19
+ end
20
+
21
+ def guess_login_model(attr_names)
22
+ selections = application_model.models.select {|model|
23
+ puts "#{model.klass.name} - #{model.model_attributes.keys.inspect}"
24
+ intersect = (model.model_attributes.keys & attr_names)
25
+ intersect.size > 0
26
+ }.sort {|a,b|
27
+ a_intersect = (a.model_attributes.keys & attr_names)
28
+ b_intersect = (b.model_attributes.keys & attr_names)
29
+ b_intersect.size <=> a_intersect.size
30
+ }
31
+
32
+ explicit_model = application_model.models.find {|model| model.klass.name == 'User' }
33
+ selections.unshift explicit_model if explicit_model
34
+
35
+ puts "selecting model #{selections.first.klass.name} for authentication" if (selections.size > 0)
36
+ selections.first
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,140 @@
1
+ module Stratagem::Crawler::Authentication
2
+ class AuthenticationData
3
+ attr_accessor :success, :login_page, :form, :response_page, :ssl, :authenticated_with
4
+ end
5
+
6
+ class Base
7
+ include Stratagem::Crawler::TraceUtils
8
+
9
+ def users
10
+ users = []
11
+ user_models.each do |user_model|
12
+ users += aquifer.instances_of(user_model.klass).select {|user| user.stratagem.mock_attributes.size > 0 }
13
+ end
14
+ users
15
+ end
16
+
17
+ def reset_authentication
18
+ @authentication_data = nil
19
+ end
20
+
21
+ def authentication
22
+ unless @authentication_data
23
+ @authentication_data = AuthenticationData.new()
24
+ site_model.authentication = @authentication_data
25
+ end
26
+ @authentication_data
27
+ end
28
+
29
+ def authenticate(user, recursion_count=0)
30
+ reset_authentication
31
+
32
+ login(user)
33
+ route = application_model.routes.recognize(request.path, :post)
34
+
35
+ redirected_to = nil
36
+ page = site_model.add(route, controller, request, response) {|redirect_url| redirected_to = redirect_url }
37
+ authentication.response_page = page
38
+
39
+ begin
40
+ if (request.url == (redirected_to || '')) || (![200,302].include?(response.code.to_i))
41
+ authentication.success = false
42
+ else
43
+ authentication.success = authentication.response_page.login_form.nil?
44
+ end
45
+ rescue
46
+ Stratagem.logger.error($!)
47
+ end
48
+
49
+ puts "authenticated? #{authentication.success}"
50
+ if (response && authentication.success)
51
+ authentication.ssl = request.ssl?
52
+ authentication.authenticated_with = user
53
+ yield
54
+ logout
55
+ else
56
+ puts response.body
57
+ false
58
+ end
59
+ end
60
+
61
+ def find_login_form
62
+ puts "finding login form"
63
+ if authentication.login_page.nil?
64
+ puts "locating login page"
65
+ puts "testing #{site_models.first.pages.size} pages"
66
+ possibilities = site_models.first.pages.select {|page| page.login_form != nil }
67
+ possibilities.sort! {|a,b| b.inbound_edges(:redirect).size <=> a.inbound_edges(:redirect).size }
68
+
69
+ # if the first page has one or more redirects to it then use it; otherwise select with page with the fewest inputs
70
+ if (possibilities.first.inbound_edges(:redirect).size > 0)
71
+ authentication.login_page = possibilities.first
72
+ return authentication.login_page
73
+ else
74
+ page = possibilities.sort {|a,b| a.login_form.inputs.size <=> b.login_form.inputs.size }.first
75
+ if (page)
76
+ authentication.login_page = page
77
+ return authentication.login_page
78
+ end
79
+ end
80
+ else
81
+ return authentication.login_page
82
+ end
83
+ nil
84
+ end
85
+
86
+ def logout
87
+ reset!
88
+ end
89
+
90
+ def login(user)
91
+ populate_login_form(user).submit {|action,params|
92
+ post(action, params)
93
+ }
94
+ end
95
+
96
+ def populate_login_form(user)
97
+ # set up the form
98
+ page = find_login_form
99
+ page.reload {|url| get url; [request,response] }
100
+ form = page.login_form
101
+
102
+ # map the input values
103
+ form.inputs.each do |input|
104
+ # try the expected
105
+ attribute_name = input.guess_attribute.to_sym
106
+ attribute_value = user.stratagem.read_mock_attribute(attribute_name) || input.value
107
+
108
+ # try again
109
+ if (attribute_value.nil? || attribute_value == '')
110
+ attribute_name = input.guess_alternate_attribute.to_sym
111
+ attribute_value = user.stratagem.read_mock_attribute(attribute_name) || input.value
112
+ end
113
+
114
+ # if it still failed is it a confirmation value? if so, try the original
115
+ if (attribute_value.nil? || attribute_value == '')
116
+ if (attribute_name.to_s =~ /confirm/)
117
+ possible_match = attribute_name.to_s.split('_').select {|a| a !~ /confirm/ }.join('_')
118
+ if user.stratagem.mock_attributes.keys.include?(possible_match)
119
+ attribute_value = user.stratagem.read_mock_attribute(possible_match) || input.value
120
+ end
121
+ end
122
+ end
123
+
124
+
125
+ if (input.kind_of? Stratagem::Crawler::Toggle)
126
+ input.check
127
+ elsif (user.stratagem.mock_attributes.keys.include?(attribute_name))
128
+ input.value = user.stratagem.read_mock_attribute(attribute_name) unless input.hidden?
129
+ elsif (attribute_name.to_s == 'authenticity_token')
130
+ puts input.value
131
+ else
132
+ puts user.stratagem.mock_attributes.inspect
133
+ puts "ERROR: Cannot find attribute #{attribute_name} in model #{user.class.name}"
134
+ end
135
+ end
136
+ form
137
+ end
138
+
139
+ end
140
+ end
@@ -0,0 +1,27 @@
1
+ module Stratagem::Crawler::Authentication
2
+ class Configured < Base
3
+ include Stratagem::AutoMock::UserLoader
4
+
5
+ def user_models
6
+ credentials.map {|user_credentials|
7
+ application_model.models.find {|model| model.klass.name == user_credentials.model }
8
+ }.compact
9
+ end
10
+
11
+ def users
12
+ log "Loading users for #{credentials.size} sets of credentials"
13
+ credentials.each do |user_credentials|
14
+ user = load_user_from_configuration(user_credentials)
15
+ if (user)
16
+ log "Loaded credentials for user #{user.id}: #{user.stratagem.mock_attributes.inspect}"
17
+ user.stratagem.related_objects.each do |object|
18
+ (objects_by_class[object.class] ||= []) << object
19
+ end
20
+ end
21
+ end
22
+
23
+ log "Loaded #{objects_by_class.keys.size} object types: #{objects_by_class.keys.inspect}"
24
+
25
+ end
26
+ end
27
+ end
@@ -6,12 +6,13 @@ module Stratagem::Crawler
6
6
  route_infos, params = build_url(route_container, resolved_params)
7
7
  route_info = route_infos.first
8
8
  unknown_params = params.keys
9
+ puts "resolving parameter types - #{unknown_params.inspect}"
9
10
 
10
11
  if (unknown_params.size > 0)
11
12
  resolve_with_convention(unknown_params, resolved_params)
12
13
  resolve_with_instrumentation(route_container, resolved_params)
13
14
  resolve_id_with_convention(route_container, unknown_params, resolved_params)
14
- log "\tresolved parameter types - #{resolved_params.inspect}"
15
+ log "\tresolved parameter types - #{resolved_params.keys.inspect}, unknown - #{unknown_params.inspect}"
15
16
  end
16
17
 
17
18
  if (resolved_params.size > 0)
@@ -43,7 +44,7 @@ module Stratagem::Crawler
43
44
  # TODO inspect is a hack, refactor
44
45
  if (invocation.args.include?(value.first)) || (invocation.args.inspect.include?('"'+value.first.to_s+'"'))
45
46
  # found match
46
- puts "\t\tresolved #{key} to #{invocation.model_class}"
47
+ puts "\t\tresolved #{key} to #{invocation.model_class} using instrumentation"
47
48
  unknown_params.delete(key)
48
49
  resolved_params[key] = invocation.model_class
49
50
  progress += 1
@@ -58,15 +59,18 @@ module Stratagem::Crawler
58
59
  end
59
60
 
60
61
  def resolve_with_convention(unknown_params, resolved_params)
61
- unknown_params.each do |param|
62
- if (param =~ /_id$/)
62
+ unknown_params.clone.each do |param|
63
+ if (param.to_s =~ /_id$/)
63
64
  begin
64
- model = param.gsub(/^:/,'').gsub(/_id$/, '').camelize.constantize
65
+ puts "\t\tattempting to resolve #{param.sub(/^:/,'').sub(/_id$/, '').camelize}"
66
+ model = param.sub(/^:/,'').sub(/_id$/, '').camelize.constantize
65
67
  #success
66
68
  resolved_params[param] = model
67
69
  unknown_params.delete(param)
68
- puts "\t\tresolved #{param} to #{model} using convention"
70
+ puts "\t\tresolved #{param} to #{model.name} using convention"
69
71
  rescue NameError
72
+ puts "NAMEERROR: #{$!.message}"
73
+ rescue
70
74
  puts "ERROR: #{$!.message}"
71
75
  end
72
76
  end
@@ -77,12 +81,12 @@ module Stratagem::Crawler
77
81
  def resolve_id_with_convention(route_container, unknown_params, resolved_params)
78
82
  if (unknown_params.include?('id'))
79
83
  route_container.path.split('/').inject(nil) {|prev,part|
80
- if (prev && part == ':id')
84
+ if (prev && part =~ /^\:id/)
81
85
  begin
82
86
  resolved_params['id'] = prev.singularize.camelize.constantize
83
87
  unknown_params.delete('id')
84
88
  rescue
85
- puts $!.message
89
+ Stratagem.logger.error($!)
86
90
  end
87
91
  end
88
92
  part