stratagem 0.1.7

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 (101) hide show
  1. data/Manifest +99 -0
  2. data/Rakefile +17 -0
  3. data/bin/stratagem +10 -0
  4. data/init.rb +2 -0
  5. data/lib/bootstrap.rb +31 -0
  6. data/lib/stratagem/authentication.rb +64 -0
  7. data/lib/stratagem/auto_mock/aquifer.rb +86 -0
  8. data/lib/stratagem/auto_mock/factory.rb +213 -0
  9. data/lib/stratagem/auto_mock/value_generator.rb +174 -0
  10. data/lib/stratagem/auto_mock.rb +6 -0
  11. data/lib/stratagem/blocker.rb +16 -0
  12. data/lib/stratagem/client.rb +32 -0
  13. data/lib/stratagem/command.rb +13 -0
  14. data/lib/stratagem/commands/analyze.rb +22 -0
  15. data/lib/stratagem/commands/base.rb +11 -0
  16. data/lib/stratagem/commands/devel_crawl.rb +27 -0
  17. data/lib/stratagem/commands/devel_mock.rb +10 -0
  18. data/lib/stratagem/commands.rb +7 -0
  19. data/lib/stratagem/crawler/authentication.rb +109 -0
  20. data/lib/stratagem/crawler/form.rb +101 -0
  21. data/lib/stratagem/crawler/html_utils.rb +92 -0
  22. data/lib/stratagem/crawler/session.rb +296 -0
  23. data/lib/stratagem/crawler/site_model.rb +138 -0
  24. data/lib/stratagem/crawler/trace_utils.rb +10 -0
  25. data/lib/stratagem/crawler.rb +9 -0
  26. data/lib/stratagem/extensions/class.rb +9 -0
  27. data/lib/stratagem/extensions/hash.rb +16 -0
  28. data/lib/stratagem/extensions/module.rb +11 -0
  29. data/lib/stratagem/extensions/object.rb +15 -0
  30. data/lib/stratagem/extensions/red_parse.rb +86 -0
  31. data/lib/stratagem/extensions/string.rb +20 -0
  32. data/lib/stratagem/extensions.rb +6 -0
  33. data/lib/stratagem/framework_extensions/controllers/action_controller.rb +10 -0
  34. data/lib/stratagem/framework_extensions/controllers/action_mailer.rb +12 -0
  35. data/lib/stratagem/framework_extensions/controllers.rb +5 -0
  36. data/lib/stratagem/framework_extensions/models/adapters/active_model/detect.rb +7 -0
  37. data/lib/stratagem/framework_extensions/models/adapters/active_model/extensions.rb +35 -0
  38. data/lib/stratagem/framework_extensions/models/adapters/active_model/metadata.rb +103 -0
  39. data/lib/stratagem/framework_extensions/models/adapters/active_model/tracing.rb +50 -0
  40. data/lib/stratagem/framework_extensions/models/adapters/authlogic/detect.rb +11 -0
  41. data/lib/stratagem/framework_extensions/models/adapters/authlogic/extensions.rb +10 -0
  42. data/lib/stratagem/framework_extensions/models/adapters/authlogic/metadata.rb +30 -0
  43. data/lib/stratagem/framework_extensions/models/adapters/authlogic/tracing.rb +4 -0
  44. data/lib/stratagem/framework_extensions/models/adapters/common/authentication_metadata.rb +21 -0
  45. data/lib/stratagem/framework_extensions/models/adapters/restful_authentication/detect.rb +13 -0
  46. data/lib/stratagem/framework_extensions/models/adapters/restful_authentication/extensions.rb +19 -0
  47. data/lib/stratagem/framework_extensions/models/adapters/restful_authentication/metadata.rb +30 -0
  48. data/lib/stratagem/framework_extensions/models/adapters/restful_authentication/tracing.rb +4 -0
  49. data/lib/stratagem/framework_extensions/models/annotations.rb +79 -0
  50. data/lib/stratagem/framework_extensions/models/detect.rb +7 -0
  51. data/lib/stratagem/framework_extensions/models/metadata.rb +85 -0
  52. data/lib/stratagem/framework_extensions/models/mocking.rb +23 -0
  53. data/lib/stratagem/framework_extensions/models/tracing.rb +71 -0
  54. data/lib/stratagem/framework_extensions/models.rb +21 -0
  55. data/lib/stratagem/framework_extensions/rails.rb +8 -0
  56. data/lib/stratagem/framework_extensions.rb +6 -0
  57. data/lib/stratagem/interface/browser.rb +37 -0
  58. data/lib/stratagem/interface/public/images/backgrounds/content.png +0 -0
  59. data/lib/stratagem/interface/public/images/backgrounds/shadow.png +0 -0
  60. data/lib/stratagem/interface/public/javascripts/jquery-1.4.2.min.js +154 -0
  61. data/lib/stratagem/interface/public/javascripts/stratagem.js +27 -0
  62. data/lib/stratagem/interface/public/javascripts/stratagem_debug.js +53 -0
  63. data/lib/stratagem/interface/public/stylesheets/960.css +1 -0
  64. data/lib/stratagem/interface/public/stylesheets/reset.css +10 -0
  65. data/lib/stratagem/interface/public/stylesheets/stratagem.css +20 -0
  66. data/lib/stratagem/interface/public/stylesheets/stratagem_debug.css +20 -0
  67. data/lib/stratagem/interface/views/debug.haml +43 -0
  68. data/lib/stratagem/interface/views/index.haml +35 -0
  69. data/lib/stratagem/labs/auto_mock.rb +7 -0
  70. data/lib/stratagem/labs/crawler.rb +0 -0
  71. data/lib/stratagem/logger.rb +46 -0
  72. data/lib/stratagem/model/application.rb +157 -0
  73. data/lib/stratagem/model/components/base.rb +55 -0
  74. data/lib/stratagem/model/components/controller.rb +118 -0
  75. data/lib/stratagem/model/components/model.rb +170 -0
  76. data/lib/stratagem/model/components/reference.rb +30 -0
  77. data/lib/stratagem/model/components/route.rb +53 -0
  78. data/lib/stratagem/model/components/static_file.rb +18 -0
  79. data/lib/stratagem/model/components/view.rb +186 -0
  80. data/lib/stratagem/model/parse_util.rb +61 -0
  81. data/lib/stratagem/model.rb +12 -0
  82. data/lib/stratagem/model_builder.rb +146 -0
  83. data/lib/stratagem/recipes/deploy.rb +30 -0
  84. data/lib/stratagem/scan/checks/capistrano/secure_deploy.rb +43 -0
  85. data/lib/stratagem/scan/checks/email_address.rb +15 -0
  86. data/lib/stratagem/scan/checks/error_pages.rb +25 -0
  87. data/lib/stratagem/scan/checks/filter_parameter_logging.rb +6 -0
  88. data/lib/stratagem/scan/checks/mongo_mapper/base.rb +19 -0
  89. data/lib/stratagem/scan/checks/mongo_mapper/foreign_keys_exposed.rb +32 -0
  90. data/lib/stratagem/scan/checks/routes.rb +16 -0
  91. data/lib/stratagem/scan/checks/ssl/secure_login_page.rb +19 -0
  92. data/lib/stratagem/scan/checks/ssl/secure_login_submit.rb +18 -0
  93. data/lib/stratagem/scan/result.rb +45 -0
  94. data/lib/stratagem/scan.rb +19 -0
  95. data/lib/stratagem/scanner.rb +32 -0
  96. data/lib/stratagem/site_crawler.rb +47 -0
  97. data/lib/stratagem/snapshot.rb +33 -0
  98. data/lib/stratagem.rb +77 -0
  99. data/lib/tasks/_old_stratagem.rake +99 -0
  100. data/stratagem.gemspec +56 -0
  101. metadata +380 -0
@@ -0,0 +1,186 @@
1
+ module Stratagem::Model::Component
2
+ class View < Base
3
+ include Stratagem::Model::ParseUtil
4
+
5
+ RAILS_FORM_FIELDS = ['check_box', 'file_field', 'hidden_field', 'password_field', 'radio_button', 'text_area', 'text_field']
6
+
7
+ attr_reader :extension, :render_path # as seen by the controllers
8
+
9
+ def initialize(render_path)
10
+ render_path =~ /\.(.*)/
11
+ @extension = $1
12
+ @path = render_path
13
+ @render_path = render_path.gsub(/\..*/, '')
14
+ end
15
+
16
+ def read
17
+ File.open(full_path) {|file| file.readlines().join }
18
+ end
19
+
20
+ def partial?
21
+ File.basename(render_path) =~ /^_/
22
+ end
23
+
24
+ def full_path
25
+ File.join(RAILS_ROOT, 'app', 'views', render_path+'.'+extension)
26
+ end
27
+
28
+ def directory
29
+ File.dirname(full_path)
30
+ end
31
+
32
+ def export
33
+ begin
34
+ {
35
+ :type => :view,
36
+ :path => @path,
37
+ :render_path => @render_path,
38
+ :forms => forms.map {|form| form.export }
39
+ }
40
+ rescue
41
+ logger.fatal($!)
42
+ nil
43
+ end
44
+ end
45
+
46
+ def rendered_by
47
+
48
+ end
49
+
50
+ def forms
51
+ # extract ruby from the html
52
+ ruby = ruby_blocks(approximate_html).join("\n")
53
+
54
+ # dump ruby into a parse tree
55
+ begin
56
+ parse_tree = RedParse.new(ruby).parse
57
+ # find the form nodes and send to a specialized function
58
+ forms = []
59
+ walk_tree(parse_tree) do |node|
60
+ if (node.kind_of?(RedParse::CallNode) && (node.name =~ /form_/) && (node.block))
61
+ forms << walk_form(node)
62
+ end
63
+ end
64
+ forms
65
+ rescue
66
+ puts "ERROR: Unable to parse ruby. - #{$!.message}"
67
+ puts ruby
68
+ []
69
+ end
70
+ end
71
+
72
+ def approximate_html
73
+ html = read().gsub(RUBY_REGEX) do |match|
74
+ handle_render(match)
75
+ end
76
+ html
77
+ end
78
+
79
+ def handle_render(ruby)
80
+ # remove the surrounding ruby indicators
81
+ result = ruby
82
+ ruby = ruby.stratagem_strip_erb
83
+ if (ruby =~ /^render/)
84
+ # load into parse tree
85
+
86
+ parse_tree = RedParse.new(ruby).parse
87
+
88
+ parse_tree.walk {|parent,i,subi,node|
89
+ case node
90
+ when RedParse::CallNode #... do something with method calls
91
+ params = node.params.first # render always takes a hash
92
+ render_path = nil
93
+ passed_vars = {}
94
+ if (params.kind_of?(RedParse::StringNode))
95
+ render_path = params.first.to_s
96
+ else
97
+ render_path = params.get(:partial).first.split('/')
98
+ object = params.get(:object).name if params.get(:object)
99
+
100
+ locals = params.get(:locals)
101
+ render_path.last.gsub!(/^/, '_')
102
+ render_path = render_path.join('/')
103
+ end
104
+
105
+ full_path = nil
106
+ if (render_path =~ /\//)
107
+ full_path = File.join(RAILS_ROOT, 'app', 'views', render_path+"."+extension)
108
+ else
109
+ full_path = File.join(directory, render_path+"."+extension)
110
+ end
111
+
112
+ begin
113
+ result = File.read(full_path)
114
+ rescue
115
+ puts "ERROR: #{full_path} not found"
116
+ end
117
+ end
118
+ }
119
+ end
120
+ result
121
+ end
122
+
123
+ private
124
+
125
+ def walk_tree(tree)
126
+ tree.walk {|parent,i,subi,node|
127
+ yield node
128
+ case node
129
+ when RedParse::SequenceNode
130
+ node.each {|child|
131
+ walk_tree(child) { yield }
132
+ }
133
+ end
134
+ }
135
+ end
136
+
137
+ def walk_form(form_node)
138
+ form = Form.new(nil)
139
+
140
+ form_node.block.each {|node|
141
+ case node
142
+ when RedParse::CallNode
143
+ RAILS_FORM_FIELDS.each {|field_name|
144
+ if (node.name.include?(field_name) && node.params)
145
+ param = node.params.first
146
+ value = param.methods_include?(:val) ? param.val : param.to_s
147
+ form.add_field(value, node.name)
148
+ break
149
+ end
150
+ }
151
+ end
152
+ }
153
+ form
154
+ end
155
+ end
156
+
157
+ class Form
158
+ attr_reader :fields
159
+
160
+ def initialize(model)
161
+ @fields = []
162
+ end
163
+
164
+ def add_field(name, type)
165
+ @fields << FormField.new(name, type)
166
+ end
167
+
168
+ def export
169
+ {:model => @model, :fields => @fields.map {|f| f.export } }
170
+ end
171
+ end
172
+
173
+ class FormField
174
+ attr_reader :name, :field_type
175
+
176
+ def initialize(name, field_type)
177
+ @name = name
178
+ @field_type = field_type
179
+ end
180
+
181
+ def export
182
+ {:name => @name, :field_type => @field_type }
183
+ end
184
+ end
185
+ end
186
+
@@ -0,0 +1,61 @@
1
+ module Stratagem::Model
2
+ module ParseUtil
3
+ RUBY_REGEX = /\<\%(.*?\%)\>/m
4
+ RUBY_OUTPUT_REGEX = /\<\%\=(.*?\%)\>/m
5
+
6
+ def self.find_classes(parse_tree)
7
+ class_names = qualified_class_name(parse_tree)
8
+ class_names.map {|name|
9
+ clazz = Kernel
10
+ begin
11
+ name.split('::').each {|part| clazz = clazz.const_get(part) }
12
+ clazz
13
+ rescue
14
+ $!
15
+ end
16
+ }
17
+ end
18
+
19
+ # assumes a single class in the tree
20
+ # will return the qualified class name (modules + class)
21
+ def self.qualified_class_name(parse_tree)
22
+ qualified_names = []
23
+ path = []
24
+ parse_tree.walk {|parent,i,subi,node|
25
+ path.pop while (path.include?(parent) && (path.last != parent))
26
+ path << parent unless path.last == parent
27
+ qualified_names << (path.clone << node) if node.kind_of?(RedParse::ClassNode)
28
+ true
29
+ }
30
+
31
+ qualified_names.map! {|qualified_name|
32
+ qualified_name.select {|node| node.kind_of?(RedParse::ModuleNode) || node.kind_of?(RedParse::ClassNode)}.map {|node|
33
+ begin
34
+ node.name.ident
35
+ rescue
36
+ node.name
37
+ end
38
+ }.join('::')
39
+ }
40
+ qualified_names
41
+ end
42
+
43
+ def ruby_output_blocks(view_erb)
44
+ view_erb.scan(RUBY_OUTPUT_REGEX).flatten.map {|line|
45
+ line.strip.gsub(/^\=\s*/, '').gsub(/\%$/, '').gsub(/\-$/, '')
46
+ }
47
+ end
48
+
49
+ def ruby_blocks(view_erb)
50
+ view_erb.scan(RUBY_REGEX).flatten.map {|line|
51
+ line.strip.gsub(/^\=\s*/, '').gsub(/\%$/, '').gsub(/\-$/, '')
52
+ }
53
+ end
54
+
55
+ def gsub_ruby_blocks(view_erb)
56
+ view_erb.gsub(RUBY_REGEX) {|ruby|
57
+ yield ruby
58
+ }
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,12 @@
1
+ module Stratagem::Model
2
+ end
3
+
4
+ require 'stratagem/model/application'
5
+ require 'stratagem/model/parse_util'
6
+ require 'stratagem/model/components/base'
7
+ require 'stratagem/model/components/reference'
8
+ require 'stratagem/model/components/model'
9
+ require 'stratagem/model/components/view'
10
+ require 'stratagem/model/components/controller'
11
+ require 'stratagem/model/components/route'
12
+ require 'stratagem/model/components/static_file'
@@ -0,0 +1,146 @@
1
+ module Stratagem
2
+ class ModelBuilder
3
+ attr_reader :parsed_models, :parsed_controllers, :aquifer
4
+
5
+ def initialize
6
+ @model = Stratagem::Model::Application.instance
7
+ @aquifer = Stratagem::AutoMock::Aquifer.init(@model)
8
+ end
9
+
10
+ def run
11
+ load_plugins
12
+ load_public
13
+ load_template_paths
14
+ load_routes
15
+ load_models
16
+
17
+ print_errors
18
+
19
+ @aquifer.fill
20
+ @model
21
+ end
22
+
23
+ def log(msg)
24
+ Stratagem.logger.debug msg
25
+ end
26
+
27
+ def print_errors
28
+ @model.routes.invalid.each {|route|
29
+ puts "route: #{route.route.to_s} is invalid"
30
+ }
31
+
32
+ @model.controllers.missing.each {|controller, routes|
33
+ puts "controller: #{controller}, with #{routes.size} routes is missing"
34
+ }
35
+ end
36
+
37
+ def load_plugins()
38
+ loader = Rails::Plugin::Loader.new(Rails::Initializer.new(Rails::Configuration.new))
39
+ loader.load_plugins
40
+ @model.plugins << loader.initializer.loaded_plugins
41
+ end
42
+
43
+ def load_models()
44
+ # load files into classes
45
+ log "loading models"
46
+ root = File.join(RAILS_ROOT, 'app','models')
47
+ load_files(File.join(root)).map {|model|
48
+ models = Stratagem::Model::Component::Model.load_all(File.join(root, model))
49
+ models.each do |c|
50
+ log "\t#{c.klass.name} loaded from #{model}"
51
+
52
+ references = []
53
+ @model.controllers.each do |controller|
54
+ references += controller.modifies(c)
55
+ end
56
+ log "\t\t#{references.size} references from controllers"
57
+ c.model_referenced_by = references
58
+ end
59
+ @model.models << models
60
+ }
61
+ log ""
62
+ end
63
+
64
+ def load_public
65
+ log "loading static files"
66
+ Dir[File.join(RAILS_ROOT, 'public', '**', '*.html')].each {|static|
67
+ static.gsub!(RAILS_ROOT, '').gsub!(/^\/public\//, '')
68
+ @model.static_files << Stratagem::Model::Component::StaticFile.new(static)
69
+ log "\t#{static}"
70
+ }
71
+ log ""
72
+ end
73
+
74
+ def load_template_paths
75
+ log "loading templates"
76
+ root = File.join(RAILS_ROOT, 'app','views')
77
+ load_files(root).map {|template|
78
+ @model.views << Stratagem::Model::Component::View.new(template)
79
+ log "\t#{template}"
80
+ }
81
+ log ""
82
+ end
83
+
84
+ def load_routes
85
+ log 'loading routes'
86
+ root = File.join(RAILS_ROOT, 'app','controllers')
87
+ ActionController::Routing::Routes.routes.each {|route|
88
+ route_container = Stratagem::Model::Component::Route.new(route)
89
+ @model.routes << route_container
90
+ begin
91
+ filename = File.join(root, "#{route.parameter_shell[:controller]}_controller.rb")
92
+
93
+ controllers = @model.controllers.select {|c| c.path == filename }
94
+ unless controllers.size > 0
95
+ controllers = Stratagem::Model::Component::Controller.load_all(filename)
96
+ puts "loading controllers from #{filename} -> controllers #{controllers.map {|c| c.klass.name }.inspect}"
97
+ @model.controllers << controllers
98
+ end
99
+
100
+ controller_name = route.parameter_shell[:controller].gsub('/','::').split('::').map {|part| part.camelcase }.join('::')
101
+ controller_name << 'Controller'
102
+ controller_class = controllers.find {|controller| controller.klass.name == controller_name }
103
+ controller_object = controller_class ? controller_class.klass.new : nil
104
+ controller_action = route.parameter_shell[:action].to_sym
105
+
106
+ if (controller_object) && (controller_object.methods_include?(controller_action))
107
+ controllers.each do |controller|
108
+ controller.add_routable_action(controller_action, route.conditions[:method] || :get)
109
+ end
110
+ else
111
+ # if the controller does not contain the indicated method
112
+ # then check for a template that may be rendered anyway
113
+ log "\tinvalid route #{route.to_s}"
114
+ unless @model.views.find {|v| v.render_path == "#{route.parameter_shell[:controller]}/#{controller_action}" }
115
+ # route is invalid
116
+ @model.routes.invalid << Stratagem::Model::Component::Route.new(route)
117
+ log "\tinvalid route #{route.to_s}"
118
+ end
119
+ end
120
+
121
+ rescue Errno::ENOENT
122
+ @model.routes.invalid << Stratagem::Model::Component::Route.new(route)
123
+ rescue MissingSourceFile
124
+ @model.routes.invalid << Stratagem::Model::Component::Route.new(route)
125
+ end
126
+ }
127
+ log ""
128
+ end
129
+
130
+ def load_files(base_dir,path=[],file_list=[])
131
+ dir = File.join(base_dir, *path)
132
+ files = Dir.new(dir).entries
133
+ files.each do |file|
134
+ next if file =~ /^\./
135
+
136
+ if (File.directory?(File.join(dir, file)))
137
+ load_files(base_dir, path + [file], file_list)
138
+ else
139
+ file_path = File.join(path, file)
140
+ file_list << file_path
141
+ end
142
+ end
143
+ file_list
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,30 @@
1
+ Capistrano::Configuration.instance(:must_exist).load do
2
+ namespace :stratagem do
3
+
4
+ desc "Analyzes your server environments for correct configuration and gem versions."
5
+ task :default do
6
+ servers = find_servers_for_task(current_task)
7
+ servers.each do |server|
8
+ puts "listing gems on #{server.host}"
9
+ run "gem list", :hosts => server do |ch, stream, data|
10
+ if stream == :err
11
+ logger.fatal "ERROR listing gems #{data}"
12
+ else # stream == :out
13
+ gems = {}
14
+ data.split("\n").each do |line|
15
+ line =~ /(.*)\s\((.*)\)/
16
+ name = $1
17
+ versions = $2.split(', ')
18
+ gems[name] = versions
19
+ end
20
+ end
21
+ end
22
+ end
23
+ # variables.each do |k,v| p k; end
24
+ # run "echo 'hi'"
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+
@@ -0,0 +1,43 @@
1
+ require 'uri'
2
+
3
+ # Stratagem::Scan::Checks::SecureDeploy
4
+ module Stratagem::Scan::Checks::Capistrano
5
+ class SecureDeploy < Stratagem::Scan::Checks::Base
6
+ SECURE_PROTOCOLS = ['https']
7
+
8
+ def run
9
+ begin
10
+ gem 'capistrano'
11
+ require 'capistrano/configuration'
12
+
13
+ begin
14
+ config = Capistrano::Configuration.new
15
+ config.load "config/deploy"
16
+
17
+ vars = config.variables
18
+ repository_url = vars[:repository]
19
+ if (repository_url)
20
+ uri = URI::parse(repository_url)
21
+ unless (SECURE_PROTOCOLS.include?(uri.scheme.downcase))
22
+ result(
23
+ :concern_type => :best_practice,
24
+ :unique => 'repository_url',
25
+ :component => nil,
26
+ :payload => repository_url)
27
+ end
28
+ else
29
+ puts "Unable to locate Capistrano repository in deploy script"
30
+ end
31
+ rescue ArgumentError
32
+ puts "Capistrano deploy script could not be loaded. - #{$!.message}"
33
+ rescue LoadError
34
+ puts "Capistrano deploy script not found. - #{$!.message}"
35
+ puts $!.class.name
36
+ end
37
+ rescue Gem::LoadError
38
+ puts "ERROR: Unable to load Capistrano"
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,15 @@
1
+ # Stratagem::Scan::Checks::EmailAddress
2
+
3
+ module Stratagem::Scan::Checks
4
+ class EmailAddress < Base
5
+ include ViewBase
6
+
7
+ Scanner = Regexp.compile(/\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}\b/)
8
+
9
+ def scan(view)
10
+ view.scan(Scanner).uniq.each do |email|
11
+ result(:concern_type => :warning, :unique => email, :payload => email)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ # Stratagem::Scan::Checks::ErrorPages
2
+
3
+ module Stratagem::Scan::Checks
4
+ class ErrorPages < Base
5
+ include ViewBase
6
+
7
+ Strings = {
8
+ 404 => ['The page you were looking for doesn\'t exist.', 'You may have mistyped the address or the page may have moved.'],
9
+ 500 => ['We\'re sorry, but something went wrong.', 'We\'ve been notified about this issue and we\'ll take a look at it shortly.']
10
+ }
11
+
12
+ def scan(view)
13
+ Strings.each {|type, set|
14
+ matched = true
15
+ set.each {|s|
16
+ unless view.include?(s)
17
+ matched = false
18
+ break
19
+ end
20
+ }
21
+ result(:concern_type => :best_practice, :unique => type, :payload => type) if (matched)
22
+ }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,6 @@
1
+ # note -
2
+ # should render views
3
+ # look at form paths
4
+ # anything that is "password"
5
+ # should be filtered
6
+ # also password, password_confirmation
@@ -0,0 +1,19 @@
1
+ module Stratagem::Scan::Checks::MongoMapper
2
+ class Base < Stratagem::Scan::Checks::Base
3
+ alias_method :parent_result, :result
4
+
5
+ def run
6
+ if (self.class.method_defined?(:scan))
7
+ application_model.models.each {|model|
8
+ log "scanning model #{model.klass.name}"
9
+ scan(model)
10
+ }
11
+ end
12
+ end
13
+
14
+ def result(hash)
15
+ hash[:specialization] = :mongo_mapper
16
+ parent_result(hash)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,32 @@
1
+ # Stratagem::Scan::Checks::MassAssignment
2
+
3
+ module Stratagem::Scan::Checks::MongoMapper
4
+ class ForeignKeysExposed < Base
5
+
6
+ def description
7
+ "analyzes application to find models vulnerable to mass assignment"
8
+ end
9
+
10
+ def scan(model)
11
+ return unless model.methods_include?(:stratagem)
12
+
13
+ # look up the controllers that reference it
14
+ instance = model.klass.new
15
+ assignable_keys = model.model_assignable_attributes & instance.stratagem.foreign_keys
16
+ if (assignable_keys.size > 0)
17
+ references = application_model.controllers.map {|controller| controller.modifies(model) }.flatten.compact
18
+ concern_type = references.size > 0 ? :error : :best_practice
19
+ solution_payload = assignable_keys
20
+ result(
21
+ :concern_type => concern_type,
22
+ :unique => model.klass.name,
23
+ :payload => model.klass.name,
24
+ :component => model,
25
+ :confirmed => false,
26
+ :solution_payload => solution_payload
27
+ )
28
+ end
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,16 @@
1
+ # Stratagem::Scan::Checks::EmailAddress
2
+
3
+ module Stratagem::Scan::Checks
4
+ class Routes < Base
5
+ def run
6
+ application_model.routes.invalid.each {|route|
7
+ payload = {
8
+ :path => route.route.segments.inject("") { |str,s| str << s.to_s },
9
+ :method => route.route.conditions[:method],
10
+ :requirements => route.route.requirements
11
+ }
12
+ result :concern_type => :best_practice, :unique => payload.inspect, :payload => payload
13
+ }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ # Stratagem::Scan::Checks::EmailAddress
2
+
3
+ module Stratagem::Scan::Checks::Ssl
4
+ class SecureLoginPage < Stratagem::Scan::Checks::Base
5
+ def run
6
+ auth = application_model.crawler.authentication
7
+ if (auth.success && !auth.login_page.response.request.ssl?)
8
+
9
+ route = application_model.routes.recognize(auth.login_page)
10
+ payload = {
11
+ :path => auth.login_page.response.request.path,
12
+ :method => auth.login_page.response.request.method,
13
+ :action => route.action
14
+ }
15
+ result :concern_type => :error, :unique => :secure_login_page, :component => route.controller, :payload => payload
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ # Stratagem::Scan::Checks::EmailAddress
2
+
3
+ module Stratagem::Scan::Checks::Ssl
4
+ class SecureLoginSubmit < Stratagem::Scan::Checks::Base
5
+ def run
6
+ auth = application_model.crawler.authentication
7
+ if (auth.success && !auth.ssl)
8
+ route = application_model.routes.recognize(auth.response_page)
9
+ payload = {
10
+ :path => auth.response_page.response.request.path,
11
+ :method => auth.response_page.response.request.method,
12
+ :action => route.action
13
+ }
14
+ result :concern_type => :error, :unique => :secure_login_submit, :component => route.controller, :payload => payload
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,45 @@
1
+ # Stratagem::Scan::Result
2
+
3
+ module Stratagem::Scan
4
+ # Each security check emits 1 or more result objects based on its findings
5
+ # Payload is an arbitrary piece of data that the check produces. It must be able to be encoded to JSON
6
+ # Unique is a value that identifies the check result within the namespace of the check
7
+ class Result
8
+ attr_accessor :unique, :check, :component, :payload, :line_number, :code, :passed, :concern_type, :confirmed, :solution_payload, :specialization
9
+
10
+ # passed = true / false
11
+ def initialize(args)
12
+ args.each {|key,value| self.send("#{key}=", value) }
13
+ end
14
+
15
+ def export
16
+ h = {
17
+ :guid => guid,
18
+ :check_name => check_name,
19
+ :specialization => specialization,
20
+ :component => component_name,
21
+ :payload => payload,
22
+ :line_number => line_number,
23
+ :code => code,
24
+ :concern_type => concern_type,
25
+ :confirmed => confirmed || false,
26
+ :solution_payload => solution_payload
27
+ }
28
+ h[:path] = component.path.gsub(RAILS_ROOT+'/', '') if component
29
+ h
30
+ end
31
+
32
+ def component_name
33
+ component ? component.name : nil
34
+ end
35
+
36
+ def check_name
37
+ check ? check.name : nil
38
+ end
39
+
40
+ def guid
41
+ "#{check_name.underscore}:#{(component_name || '').underscore}:#{unique.to_s.underscore}"
42
+ end
43
+ end
44
+ end
45
+