brakeman 0.0.2

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 (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