brakeman 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/FEATURES +16 -0
  2. data/README.md +112 -0
  3. data/WARNING_TYPES +69 -0
  4. data/bin/brakeman +266 -0
  5. data/lib/checks.rb +67 -0
  6. data/lib/checks/base_check.rb +338 -0
  7. data/lib/checks/check_cross_site_scripting.rb +216 -0
  8. data/lib/checks/check_default_routes.rb +29 -0
  9. data/lib/checks/check_evaluation.rb +29 -0
  10. data/lib/checks/check_execute.rb +110 -0
  11. data/lib/checks/check_file_access.rb +46 -0
  12. data/lib/checks/check_forgery_setting.rb +25 -0
  13. data/lib/checks/check_mass_assignment.rb +72 -0
  14. data/lib/checks/check_model_attributes.rb +36 -0
  15. data/lib/checks/check_redirect.rb +98 -0
  16. data/lib/checks/check_render.rb +65 -0
  17. data/lib/checks/check_send_file.rb +15 -0
  18. data/lib/checks/check_session_settings.rb +36 -0
  19. data/lib/checks/check_sql.rb +124 -0
  20. data/lib/checks/check_validation_regex.rb +60 -0
  21. data/lib/format/style.css +105 -0
  22. data/lib/processor.rb +83 -0
  23. data/lib/processors/alias_processor.rb +384 -0
  24. data/lib/processors/base_processor.rb +235 -0
  25. data/lib/processors/config_processor.rb +146 -0
  26. data/lib/processors/controller_alias_processor.rb +222 -0
  27. data/lib/processors/controller_processor.rb +175 -0
  28. data/lib/processors/erb_template_processor.rb +84 -0
  29. data/lib/processors/erubis_template_processor.rb +62 -0
  30. data/lib/processors/haml_template_processor.rb +115 -0
  31. data/lib/processors/lib/find_call.rb +176 -0
  32. data/lib/processors/lib/find_model_call.rb +39 -0
  33. data/lib/processors/lib/processor_helper.rb +36 -0
  34. data/lib/processors/lib/render_helper.rb +118 -0
  35. data/lib/processors/library_processor.rb +117 -0
  36. data/lib/processors/model_processor.rb +125 -0
  37. data/lib/processors/output_processor.rb +204 -0
  38. data/lib/processors/params_processor.rb +77 -0
  39. data/lib/processors/route_processor.rb +338 -0
  40. data/lib/processors/template_alias_processor.rb +86 -0
  41. data/lib/processors/template_processor.rb +55 -0
  42. data/lib/report.rb +628 -0
  43. data/lib/scanner.rb +232 -0
  44. data/lib/tracker.rb +144 -0
  45. data/lib/util.rb +141 -0
  46. data/lib/warning.rb +97 -0
  47. metadata +191 -0
@@ -0,0 +1,36 @@
1
+ #Contains a couple shared methods for Processors.
2
+ module ProcessorHelper
3
+
4
+ #Sets the current module.
5
+ def process_module exp
6
+ @current_module = class_name(exp[1]).to_s
7
+ process exp[2]
8
+ @current_module = nil
9
+ exp
10
+ end
11
+
12
+ #Returns a class name as a Symbol.
13
+ def class_name exp
14
+ case exp
15
+ when Sexp
16
+ case exp.node_type
17
+ when :const
18
+ exp[1]
19
+ when :colon2
20
+ "#{class_name(exp[1])}::#{exp[2]}".to_sym
21
+ when :colon3
22
+ "::#{exp[1]}".to_sym
23
+ when :call
24
+ process exp
25
+ else
26
+ raise "Error: Cannot get class name from #{exp}"
27
+ end
28
+ when Symbol
29
+ exp
30
+ when nil
31
+ nil
32
+ else
33
+ raise "Error: Cannot get class name from #{exp}"
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,118 @@
1
+ require 'digest/sha1'
2
+
3
+ #Processes a call to render() in a controller or template
4
+ module RenderHelper
5
+
6
+ #Process s(:render, TYPE, OPTIONS)
7
+ def process_render exp
8
+ process_default exp
9
+ @rendered = true
10
+ case exp[1]
11
+ when :action
12
+ process_action exp[2][1], exp[3]
13
+ when :default
14
+ process_template template_name, exp[3]
15
+ when :partial
16
+ process_partial exp[2], exp[3]
17
+ when :nothing
18
+ end
19
+ exp
20
+ end
21
+
22
+ #Determines file name for partial and then processes it
23
+ def process_partial name, args
24
+ if name == "" or !(string? name or symbol? name)
25
+ return
26
+ end
27
+
28
+ names = name[1].to_s.split("/")
29
+ names[-1] = "_" + names[-1]
30
+ process_template template_name(names.join("/")), args
31
+ end
32
+
33
+ #Processes a given action
34
+ def process_action name, args
35
+ process_template template_name(name), args
36
+ end
37
+
38
+ #Processes a template, adding any instance variables
39
+ #to its environment.
40
+ def process_template name, args, called_from = nil
41
+ #Get scanned source for this template
42
+ name = name.to_s.gsub(/^\//, "")
43
+ template = @tracker.templates[name.to_sym]
44
+ unless template
45
+ warn "[Notice] No such template: #{name}" if OPTIONS[:debug]
46
+ return
47
+ end
48
+
49
+ template_env = only_ivars
50
+
51
+ #Hash the environment and the source of the template to avoid
52
+ #pointlessly processing templates, which can become prohibitively
53
+ #expensive in terms of time and memory.
54
+ digest = Digest::SHA1.new.update(template_env.instance_variable_get(:@env).to_a.sort.to_s << template[:src].to_s).to_s.to_sym
55
+
56
+ if @tracker.template_cache.include? digest
57
+ #Already processed this template with identical environment
58
+ return
59
+ else
60
+ @tracker.template_cache << digest
61
+
62
+ options = get_options args
63
+
64
+ if hash? options[:locals]
65
+ hash_iterate options[:locals] do |key, value|
66
+ template_env[Sexp.new(:call, nil, key[1], Sexp.new(:arglist))] = value
67
+ end
68
+ end
69
+
70
+ if options[:collection]
71
+
72
+ #The collection name is the name of the partial without the leading
73
+ #underscore.
74
+ variable = template[:name].to_s.match(/[^\/_][^\/]+$/)[0].to_sym
75
+
76
+ #Unless the :as => :variable_name option is used
77
+ if options[:as]
78
+ if string? options[:as] or symbol? options[:as]
79
+ variable = options[:as][1].to_sym
80
+ end
81
+ end
82
+
83
+ template_env[Sexp.new(:call, nil, variable, Sexp.new(:arglist))] = Sexp.new(:call, Sexp.new(:const, Tracker::UNKNOWN_MODEL), :new, Sexp.new(:arglist))
84
+ end
85
+
86
+ #Run source through AliasProcessor with instance variables from the
87
+ #current environment.
88
+ #TODO: Add in :locals => { ... } to environment
89
+ src = TemplateAliasProcessor.new(@tracker, template).process_safely(template[:src], template_env)
90
+
91
+ #Run alias-processed src through the template processor to pull out
92
+ #information and outputs.
93
+ #This information will be stored in tracker.templates, but with a name
94
+ #specifying this particular route. The original source should remain
95
+ #pristine (so it can be processed within other environments).
96
+ @tracker.processor.process_template name, src, template[:type], called_from
97
+ end
98
+ end
99
+
100
+ #Override to process name, such as adding the controller name.
101
+ def template_name name
102
+ raise "RenderHelper#template_name should be overridden."
103
+ end
104
+
105
+ #Turn options Sexp into hash
106
+ def get_options args
107
+ options = {}
108
+ return options unless hash? args
109
+
110
+ hash_iterate args do |key, value|
111
+ if symbol? key
112
+ options[key[1]] = value
113
+ end
114
+ end
115
+
116
+ options
117
+ end
118
+ end
@@ -0,0 +1,117 @@
1
+ require 'processors/base_processor'
2
+ require 'processors/alias_processor'
3
+
4
+ #Process generic library and stores it in Tracker.libs
5
+ class LibraryProcessor < BaseProcessor
6
+
7
+ def initialize tracker
8
+ super
9
+ @file_name = nil
10
+ @alias_processor = AliasProcessor.new
11
+ end
12
+
13
+ def process_library src, file_name = nil
14
+ @file_name = file_name
15
+ process src
16
+ end
17
+
18
+ def process_class exp
19
+ name = class_name(exp[1])
20
+
21
+ if @current_class
22
+ outer_class = @current_class
23
+ name = (outer_class[:name].to_s + "::" + name.to_s).to_sym
24
+ end
25
+
26
+ if @current_module
27
+ name = (@current_module[:name].to_s + "::" + name.to_s).to_sym
28
+ end
29
+
30
+ if @tracker.libs[name]
31
+ @current_class = @tracker.libs[name]
32
+ else
33
+ @current_class = { :name => name,
34
+ :parent => class_name(exp[2]),
35
+ :includes => [],
36
+ :public => {},
37
+ :private => {},
38
+ :protected => {},
39
+ :src => exp,
40
+ :file => @file_name }
41
+
42
+ @tracker.libs[name] = @current_class
43
+ end
44
+
45
+ exp[3] = process exp[3]
46
+
47
+ if outer_class
48
+ @current_class = outer_class
49
+ else
50
+ @current_class = nil
51
+ end
52
+
53
+ exp
54
+ end
55
+
56
+ def process_module exp
57
+ name = class_name(exp[1])
58
+
59
+ if @current_module
60
+ outer_class = @current_module
61
+ name = (outer_class[:name].to_s + "::" + name.to_s).to_sym
62
+ end
63
+
64
+ if @current_class
65
+ name = (@current_class[:name].to_s + "::" + name.to_s).to_sym
66
+ end
67
+
68
+ if @tracker.libs[name]
69
+ @current_module = @tracker.libs[name]
70
+ else
71
+ @current_module = { :name => name,
72
+ :includes => [],
73
+ :public => {},
74
+ :private => {},
75
+ :protected => {},
76
+ :src => exp }
77
+
78
+ @tracker.libs[name] = @current_module
79
+ end
80
+
81
+ exp[2] = process exp[2]
82
+
83
+ if outer_class
84
+ @current_module = outer_class
85
+ else
86
+ @current_module = nil
87
+ end
88
+
89
+ exp
90
+ end
91
+
92
+ def process_defn exp
93
+ exp[0] = :methdef
94
+ exp[3] = @alias_processor.process_safely process(exp[3]), SexpProcessor::Environment.new
95
+
96
+ if @current_class
97
+ @current_class[:public][exp[1]] = exp[3]
98
+ elsif @current_module
99
+ @current_module[:public][exp[1]] = exp[3]
100
+ end
101
+
102
+ exp
103
+ end
104
+
105
+ def process_defs exp
106
+ exp[0] = :selfdef
107
+ exp[4] = @alias_processor.process_safely process(exp[4]), SexpProcessor::Environment.new
108
+
109
+ if @current_class
110
+ @current_class[:public][exp[2]] = exp[4]
111
+ elsif @current_module
112
+ @current_module[:public][exp[3]] = exp[4]
113
+ end
114
+
115
+ exp
116
+ end
117
+ end
@@ -0,0 +1,125 @@
1
+ require 'processors/base_processor'
2
+
3
+ #Processes models. Puts results in tracker.models
4
+ class ModelProcessor < BaseProcessor
5
+ def initialize tracker
6
+ super
7
+ @model = nil
8
+ @current_method = nil
9
+ @visibility = :public
10
+ @file_name = nil
11
+ end
12
+
13
+ #Process model source
14
+ def process_model src, file_name = nil
15
+ @file_name = file_name
16
+ process src
17
+ end
18
+
19
+ #s(:class, NAME, PARENT, s(:scope ...))
20
+ def process_class exp
21
+ if @model
22
+ warn "[Notice] Skipping inner class: #{class_name exp[1]}" if OPTIONS[:debug]
23
+ ignore
24
+ else
25
+ @model = { :name => class_name(exp[1]),
26
+ :parent => class_name(exp[2]),
27
+ :includes => [],
28
+ :public => {},
29
+ :private => {},
30
+ :protected => {},
31
+ :options => {},
32
+ :file => @file_name }
33
+ @tracker.models[@model[:name]] = @model
34
+ res = process exp[3]
35
+ @model = nil
36
+ res
37
+ end
38
+ end
39
+
40
+ #Handle calls outside of methods,
41
+ #such as include, attr_accessible, private, etc.
42
+ def process_call exp
43
+ return exp unless @model
44
+ target = exp[1]
45
+ if sexp? target
46
+ target = process target
47
+ end
48
+
49
+ method = exp[2]
50
+ args = exp[3]
51
+
52
+ #Methods called inside class definition
53
+ #like attr_* and other settings
54
+ if @current_method.nil? and target.nil?
55
+ if args.length == 1 #actually, empty
56
+ case method
57
+ when :private, :protected, :public
58
+ @visibility = method
59
+ else
60
+ #??
61
+ end
62
+ else
63
+ case method
64
+ when :include
65
+ @model[:includes] << class_name(args[1]) if @model
66
+ when :attr_accessible
67
+ @model[:attr_accessible] ||= []
68
+ args = args[1..-1].map do |e|
69
+ e[1]
70
+ end
71
+
72
+ @model[:attr_accessible].concat args
73
+ else
74
+ if @model
75
+ @model[:options][method] ||= []
76
+ @model[:options][method] << process(args)
77
+ end
78
+ end
79
+ end
80
+ ignore
81
+ else
82
+ call = Sexp.new :call, target, method, process(args)
83
+ call.line(exp.line)
84
+ call
85
+ end
86
+ end
87
+
88
+ #Add method definition to tracker
89
+ def process_defn exp
90
+ return exp unless @model
91
+ name = exp[1]
92
+
93
+ @current_method = name
94
+ res = Sexp.new :methdef, name, process(exp[2]), process(exp[3][1])
95
+ res.line(exp.line)
96
+ @current_method = nil
97
+ if @model
98
+ list = @model[@visibility]
99
+ list[name] = res
100
+ end
101
+ res
102
+ end
103
+
104
+ #Add method definition to tracker
105
+ def process_defs exp
106
+ return exp unless @model
107
+ name = exp[2]
108
+
109
+ if exp[1].node_type == :self
110
+ target = @model[:name]
111
+ else
112
+ target = class_name exp[1]
113
+ end
114
+
115
+ @current_method = name
116
+ res = Sexp.new :selfdef, target, name, process(exp[3]), process(exp[4][1])
117
+ res.line(exp.line)
118
+ @current_method = nil
119
+ if @model
120
+ @model[@visibility][name] = res unless @model.nil?
121
+ end
122
+ res
123
+ end
124
+
125
+ end
@@ -0,0 +1,204 @@
1
+ require 'rubygems'
2
+ require 'ruby2ruby'
3
+ require 'util'
4
+
5
+ #Produces formatted output strings from Sexps.
6
+ #Recommended usage is
7
+ #
8
+ # OutputProcessor.new.format(Sexp.new(:str, "hello"))
9
+ class OutputProcessor < Ruby2Ruby
10
+ include Util
11
+
12
+ #Copies +exp+ and then formats it.
13
+ def format exp
14
+ process exp.deep_clone
15
+ end
16
+
17
+ alias process_safely format
18
+
19
+ def process exp
20
+ begin
21
+ super exp if sexp? exp and not exp.empty?
22
+ rescue Exception => e
23
+ warn "While formatting #{exp}: #{e}\n#{e.backtrace.join("\n")}" if OPTIONS[:debug]
24
+ end
25
+ end
26
+
27
+ def process_call exp
28
+ if exp[0].is_a? Symbol
29
+ target = exp[0]
30
+
31
+ method = exp[1]
32
+
33
+ args = process exp[2]
34
+
35
+ out = nil
36
+
37
+ if method == :[]
38
+ if target
39
+ out = "#{target}[#{args}]"
40
+ else
41
+ raise Exception.new("Not sure what to do with access and no target: #{exp}")
42
+ end
43
+ else
44
+ if target
45
+ out = "#{target}.#{method}(#{args})"
46
+ else
47
+ out = "#{method}(#{args})"
48
+ end
49
+ end
50
+ exp.clear
51
+ out
52
+ else
53
+ super exp
54
+ end
55
+ end
56
+
57
+ def process_lvar exp
58
+ out = "(local #{exp[0]})"
59
+ exp.clear
60
+ out
61
+ end
62
+
63
+ def process_ignore exp
64
+ exp.clear
65
+ "[ignored]"
66
+ end
67
+
68
+ def process_params exp
69
+ exp.clear
70
+ "params"
71
+ end
72
+
73
+ def process_session exp
74
+ exp.clear
75
+ "session"
76
+ end
77
+
78
+ def process_cookies exp
79
+ exp.clear
80
+ "cookies"
81
+ end
82
+
83
+ def process_string_interp exp
84
+ out = '"'
85
+ exp.each do |e|
86
+ if e.is_a? String
87
+ out << e
88
+ else
89
+ res = process e
90
+ out << res unless res == ""
91
+ end
92
+ end
93
+ out << '"'
94
+ exp.clear
95
+ out
96
+ end
97
+
98
+ def process_string_eval exp
99
+ out = "\#{#{process(exp[0])}}"
100
+ exp.clear
101
+ out
102
+ end
103
+
104
+ def process_dxstr exp
105
+ out = "`"
106
+ out << exp.map! do |e|
107
+ if e.is_a? String
108
+ e
109
+ elsif string? e
110
+ e[1]
111
+ else
112
+ process e
113
+ end
114
+ end.join
115
+ exp.clear
116
+ out << "`"
117
+ end
118
+
119
+ def process_rlist exp
120
+ out = exp.map do |e|
121
+ res = process e
122
+ if res == ""
123
+ nil
124
+ else
125
+ res
126
+ end
127
+ end.compact.join("\n")
128
+ exp.clear
129
+ out
130
+ end
131
+
132
+ def process_call_with_block exp
133
+ call = process exp[0]
134
+ block = process exp[1] if exp[1]
135
+ out = "#{call} do\n #{block}\n end"
136
+ exp.clear
137
+ out
138
+ end
139
+
140
+ def process_output exp
141
+ out = if exp[0].node_type == :str
142
+ ""
143
+ else
144
+ res = process exp[0]
145
+
146
+ if res == ""
147
+ ""
148
+ else
149
+ "[Output] #{res}"
150
+ end
151
+ end
152
+ exp.clear
153
+ out
154
+ end
155
+
156
+ def process_format exp
157
+ out = if exp[0].node_type == :str or exp[0].node_type == :ignore
158
+ ""
159
+ else
160
+ res = process exp[0]
161
+
162
+ if res == ""
163
+ ""
164
+ else
165
+ "[Format] #{res}"
166
+ end
167
+ end
168
+ exp.clear
169
+ out
170
+ end
171
+
172
+ def process_format_escaped exp
173
+ out = if exp[0].node_type == :str or exp[0].node_type == :ignore
174
+ ""
175
+ else
176
+ res = process exp[0]
177
+
178
+ if res == ""
179
+ ""
180
+ else
181
+ "[Escaped] #{res}"
182
+ end
183
+ end
184
+ exp.clear
185
+ out
186
+ end
187
+
188
+ def process_const exp
189
+ if exp[0] == Tracker::UNKNOWN_MODEL
190
+ exp.clear
191
+ "(Unresolved Model)"
192
+ else
193
+ super exp
194
+ end
195
+ end
196
+
197
+ def process_render exp
198
+ exp[1] = process exp[1] if sexp? exp[1]
199
+ exp[2] = process exp[2] if sexp? exp[2]
200
+ out = "render(#{exp[0]} => #{exp[1]}, #{exp[2]})"
201
+ exp.clear
202
+ out
203
+ end
204
+ end