brakeman 3.0.5 → 3.1.0

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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +19 -0
  3. data/README.md +3 -13
  4. data/lib/brakeman.rb +3 -0
  5. data/lib/brakeman/checks/base_check.rb +19 -47
  6. data/lib/brakeman/checks/check_basic_auth.rb +3 -3
  7. data/lib/brakeman/checks/check_cross_site_scripting.rb +26 -12
  8. data/lib/brakeman/checks/check_default_routes.rb +1 -1
  9. data/lib/brakeman/checks/check_detailed_exceptions.rb +2 -2
  10. data/lib/brakeman/checks/check_evaluation.rb +3 -0
  11. data/lib/brakeman/checks/check_execute.rb +3 -3
  12. data/lib/brakeman/checks/check_file_disclosure.rb +2 -2
  13. data/lib/brakeman/checks/check_forgery_setting.rb +9 -12
  14. data/lib/brakeman/checks/check_header_dos.rb +1 -1
  15. data/lib/brakeman/checks/check_i18n_xss.rb +2 -2
  16. data/lib/brakeman/checks/check_jruby_xml.rb +1 -1
  17. data/lib/brakeman/checks/check_json_encoding.rb +1 -1
  18. data/lib/brakeman/checks/check_json_parsing.rb +3 -3
  19. data/lib/brakeman/checks/check_link_to.rb +1 -1
  20. data/lib/brakeman/checks/check_link_to_href.rb +9 -2
  21. data/lib/brakeman/checks/check_mass_assignment.rb +5 -2
  22. data/lib/brakeman/checks/check_model_attr_accessible.rb +4 -4
  23. data/lib/brakeman/checks/check_model_attributes.rb +7 -7
  24. data/lib/brakeman/checks/check_model_serialize.rb +6 -6
  25. data/lib/brakeman/checks/check_nested_attributes.rb +2 -2
  26. data/lib/brakeman/checks/check_number_to_currency.rb +2 -2
  27. data/lib/brakeman/checks/check_quote_table_name.rb +1 -1
  28. data/lib/brakeman/checks/check_redirect.rb +2 -10
  29. data/lib/brakeman/checks/check_render.rb +1 -1
  30. data/lib/brakeman/checks/check_render_dos.rb +1 -1
  31. data/lib/brakeman/checks/check_safe_buffer_manipulation.rb +1 -1
  32. data/lib/brakeman/checks/check_sanitize_methods.rb +1 -1
  33. data/lib/brakeman/checks/check_select_tag.rb +1 -1
  34. data/lib/brakeman/checks/check_select_vulnerability.rb +2 -2
  35. data/lib/brakeman/checks/check_session_settings.rb +1 -2
  36. data/lib/brakeman/checks/check_simple_format.rb +2 -2
  37. data/lib/brakeman/checks/check_single_quotes.rb +3 -3
  38. data/lib/brakeman/checks/check_skip_before_filter.rb +5 -7
  39. data/lib/brakeman/checks/check_sql.rb +10 -14
  40. data/lib/brakeman/checks/check_sql_cves.rb +4 -4
  41. data/lib/brakeman/checks/check_ssl_verify.rb +27 -9
  42. data/lib/brakeman/checks/check_strip_tags.rb +5 -5
  43. data/lib/brakeman/checks/check_symbol_dos_cve.rb +1 -1
  44. data/lib/brakeman/checks/check_translate_bug.rb +3 -4
  45. data/lib/brakeman/checks/check_unscoped_find.rb +1 -1
  46. data/lib/brakeman/checks/check_validation_regex.rb +2 -2
  47. data/lib/brakeman/checks/check_xml_dos.rb +1 -1
  48. data/lib/brakeman/checks/check_yaml_parsing.rb +1 -1
  49. data/lib/brakeman/file_parser.rb +1 -0
  50. data/lib/brakeman/parsers/template_parser.rb +6 -5
  51. data/lib/brakeman/processor.rb +7 -7
  52. data/lib/brakeman/processors/alias_processor.rb +30 -12
  53. data/lib/brakeman/processors/base_processor.rb +4 -8
  54. data/lib/brakeman/processors/controller_alias_processor.rb +33 -132
  55. data/lib/brakeman/processors/controller_processor.rb +29 -53
  56. data/lib/brakeman/processors/erb_template_processor.rb +4 -6
  57. data/lib/brakeman/processors/erubis_template_processor.rb +8 -11
  58. data/lib/brakeman/processors/gem_processor.rb +19 -35
  59. data/lib/brakeman/processors/haml_template_processor.rb +10 -12
  60. data/lib/brakeman/processors/lib/find_all_calls.rb +3 -5
  61. data/lib/brakeman/processors/lib/find_call.rb +2 -2
  62. data/lib/brakeman/processors/lib/find_return_value.rb +1 -1
  63. data/lib/brakeman/processors/lib/rails2_config_processor.rb +7 -8
  64. data/lib/brakeman/processors/lib/rails3_config_processor.rb +6 -7
  65. data/lib/brakeman/processors/lib/render_helper.rb +15 -14
  66. data/lib/brakeman/processors/lib/render_path.rb +11 -5
  67. data/lib/brakeman/processors/library_processor.rb +13 -35
  68. data/lib/brakeman/processors/model_processor.rb +22 -64
  69. data/lib/brakeman/processors/output_processor.rb +1 -37
  70. data/lib/brakeman/processors/slim_template_processor.rb +6 -8
  71. data/lib/brakeman/processors/template_alias_processor.rb +9 -9
  72. data/lib/brakeman/processors/template_processor.rb +5 -9
  73. data/lib/brakeman/report/report_base.rb +7 -7
  74. data/lib/brakeman/report/report_html.rb +5 -7
  75. data/lib/brakeman/report/report_markdown.rb +4 -6
  76. data/lib/brakeman/report/report_table.rb +4 -6
  77. data/lib/brakeman/rescanner.rb +29 -31
  78. data/lib/brakeman/scanner.rb +17 -8
  79. data/lib/brakeman/tracker.rb +24 -34
  80. data/lib/brakeman/tracker/collection.rb +77 -0
  81. data/lib/brakeman/tracker/config.rb +93 -0
  82. data/lib/brakeman/tracker/controller.rb +161 -0
  83. data/lib/brakeman/tracker/library.rb +17 -0
  84. data/lib/brakeman/tracker/model.rb +90 -0
  85. data/lib/brakeman/tracker/template.rb +33 -0
  86. data/lib/brakeman/util.rb +17 -9
  87. data/lib/brakeman/version.rb +1 -1
  88. data/lib/brakeman/warning.rb +8 -9
  89. data/lib/ruby_parser/bm_sexp.rb +16 -16
  90. data/lib/ruby_parser/bm_sexp_processor.rb +1 -120
  91. metadata +42 -31
  92. checksums.yaml.gz.sig +0 -1
  93. data.tar.gz.sig +0 -0
  94. metadata.gz.sig +0 -0
@@ -0,0 +1,77 @@
1
+ require 'brakeman/util'
2
+
3
+ module Brakeman
4
+ class Collection
5
+ include Brakeman::Util
6
+
7
+ attr_reader :collection, :files, :includes, :name, :options, :parent, :src, :tracker
8
+
9
+ def initialize name, parent, file_name, src, tracker
10
+ @name = name
11
+ @parent = parent
12
+ @file_name = file_name
13
+ @files = [ file_name ]
14
+ @src = { file_name => src }
15
+ @includes = []
16
+ @methods = { :public => {}, :private => {}, :protected => {} }
17
+ @options = {}
18
+ @tracker = tracker
19
+ end
20
+
21
+ def ancestor? parent, seen={}
22
+ seen[self.name] = true
23
+
24
+ if self.parent == parent or seen[self.parent]
25
+ true
26
+ elsif parent_model = collection[self.parent]
27
+ parent_model.ancestor? parent, seen
28
+ else
29
+ false
30
+ end
31
+ end
32
+
33
+ def add_file file_name, src
34
+ @files << file_name unless @files.include? file_name
35
+ @src[file_name] = src
36
+ end
37
+
38
+ def add_include class_name
39
+ @includes << class_name
40
+ end
41
+
42
+ def add_option name, exp
43
+ @options[name] ||= []
44
+ @options[name] << exp
45
+ end
46
+
47
+ def add_method visibility, name, src, file_name
48
+ @methods[visibility][name] = { :src => src, :file => file_name }
49
+ end
50
+
51
+ def each_method
52
+ @methods.each do |vis, meths|
53
+ meths.each do |name, info|
54
+ yield name, info
55
+ end
56
+ end
57
+ end
58
+
59
+ def get_method name
60
+ each_method do |n, info|
61
+ if n == name
62
+ return info
63
+ end
64
+ end
65
+
66
+ nil
67
+ end
68
+
69
+ def file
70
+ @files.first
71
+ end
72
+
73
+ def methods_public
74
+ @methods[:public]
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,93 @@
1
+ require 'brakeman/util'
2
+
3
+ module Brakeman
4
+ class Config
5
+ include Util
6
+
7
+ attr_reader :rails, :tracker
8
+ attr_accessor :rails_version
9
+ attr_writer :erubis, :escape_html
10
+
11
+ def initialize tracker
12
+ @tracker = tracker
13
+ @rails = {}
14
+ @gems = {}
15
+ @settings = {}
16
+ end
17
+
18
+ def allow_forgery_protection?
19
+ @rails[:action_controller] and
20
+ @rails[:action_controller][:allow_forgery_protection] == Sexp.new(:false)
21
+ end
22
+
23
+ def erubis?
24
+ @erubis
25
+ end
26
+
27
+ def escape_html?
28
+ @escape_html
29
+ end
30
+
31
+ def escape_html_entities_in_json?
32
+ #TODO add version-specific information here
33
+ @rails[:active_support] and
34
+ true? @rails[:active_support][:escape_html_entities_in_json]
35
+ end
36
+
37
+ def whitelist_attributes?
38
+ @rails[:active_record] and
39
+ @rails[:active_record][:whitelist_attributes] == Sexp.new(:true)
40
+ end
41
+
42
+ def gem_version name
43
+ @gems[name] and @gems[name][:version]
44
+ end
45
+
46
+ def add_gem name, version, file, line
47
+ name = name.to_sym
48
+ @gems[name] = {
49
+ :version => version,
50
+ :file => file,
51
+ :line => line
52
+ }
53
+ end
54
+
55
+ def has_gem? name
56
+ !!@gems[name]
57
+ end
58
+
59
+ def get_gem name
60
+ @gems[name]
61
+ end
62
+
63
+ def set_rails_version
64
+ # Ignore ~>, etc. when using values from Gemfile
65
+ version = gem_version(:rails) || gem_version(:railties)
66
+ if version and version.match(/(\d+\.\d+\.\d+.*)/)
67
+ @rails_version = $1
68
+
69
+ if tracker.options[:rails3].nil? and tracker.options[:rails4].nil?
70
+ if @rails_version.start_with? "3"
71
+ tracker.options[:rails3] = true
72
+ Brakeman.notify "[Notice] Detected Rails 3 application"
73
+ elsif @rails_version.start_with? "4"
74
+ tracker.options[:rails3] = true
75
+ tracker.options[:rails4] = true
76
+ Brakeman.notify "[Notice] Detected Rails 4 application"
77
+ end
78
+ end
79
+ end
80
+
81
+ if get_gem :rails_xss
82
+ @escape_html = true
83
+ Brakeman.notify "[Notice] Escaping HTML by default"
84
+ end
85
+ end
86
+
87
+ def session_settings
88
+ @rails[:action_controller] &&
89
+ @rails[:action_controller][:session]
90
+ end
91
+
92
+ end
93
+ end
@@ -0,0 +1,161 @@
1
+ require 'brakeman/tracker/collection'
2
+
3
+ module Brakeman
4
+ module ControllerMethods
5
+ attr_accessor :layout
6
+
7
+ def initialize_controller
8
+ @options[:before_filters] = []
9
+ @options[:skip_filters] = []
10
+ @layout = nil
11
+ @skip_filter_cache = nil
12
+ @before_filter_cache = nil
13
+ end
14
+
15
+ def protect_from_forgery?
16
+ @options[:protect_from_forgery]
17
+ end
18
+
19
+ def add_before_filter exp
20
+ @options[:before_filters] << exp
21
+ end
22
+
23
+ def prepend_before_filter exp
24
+ @options[:before_filters].unshift exp
25
+ end
26
+
27
+ def before_filters
28
+ @options[:before_filters]
29
+ end
30
+
31
+ def skip_filter exp
32
+ @options[:skip_filters] << exp
33
+ end
34
+
35
+ def skip_filters
36
+ @options[:skip_filters]
37
+ end
38
+
39
+ def before_filter_list processor, method
40
+ controller = self
41
+ filters = []
42
+
43
+ while controller
44
+ filters = controller.get_before_filters(processor, method) + filters
45
+
46
+ controller = tracker.controllers[controller.parent] ||
47
+ tracker.libs[controller.parent]
48
+ end
49
+
50
+ remove_skipped_filters processor, filters, method
51
+ end
52
+
53
+ def get_skipped_filters processor, method
54
+ filters = []
55
+
56
+ if @skip_filter_cache.nil?
57
+ @skip_filter_cache = skip_filters.map do |filter|
58
+ before_filter_to_hash(processor, filter.args)
59
+ end
60
+ end
61
+
62
+ @skip_filter_cache.each do |f|
63
+ if f[:all] or
64
+ (f[:only] == method) or
65
+ (f[:only].is_a? Array and f[:only].include? method) or
66
+ (f[:except].is_a? Symbol and f[:except] != method) or
67
+ (f[:except].is_a? Array and not f[:except].include? method)
68
+
69
+ filters.concat f[:methods]
70
+ else
71
+ end
72
+ end
73
+
74
+ filters
75
+ end
76
+
77
+ def remove_skipped_filters processor, filters, method
78
+ controller = self
79
+
80
+ while controller
81
+ filters = filters - controller.get_skipped_filters(processor, method)
82
+
83
+ controller = tracker.controllers[controller.parent] ||
84
+ tracker.libs[controller.parent]
85
+ end
86
+
87
+ filters
88
+ end
89
+
90
+ def get_before_filters processor, method
91
+ filters = []
92
+
93
+ if @before_filter_cache.nil?
94
+ @before_filter_cache = []
95
+
96
+ before_filters.each do |filter|
97
+ @before_filter_cache << before_filter_to_hash(processor, filter.args)
98
+ end
99
+ end
100
+
101
+ @before_filter_cache.each do |f|
102
+ if f[:all] or
103
+ (f[:only] == method) or
104
+ (f[:only].is_a? Array and f[:only].include? method) or
105
+ (f[:except].is_a? Symbol and f[:except] != method) or
106
+ (f[:except].is_a? Array and not f[:except].include? method)
107
+
108
+ filters.concat f[:methods]
109
+ end
110
+ end
111
+
112
+
113
+ filters
114
+ end
115
+
116
+ def before_filter_to_hash processor, args
117
+ filter = {}
118
+
119
+ #Process args for the uncommon but possible situation
120
+ #in which some variables are used in the filter.
121
+ args.each do |a|
122
+ if sexp? a
123
+ a = processor.process_default a
124
+ end
125
+ end
126
+
127
+ filter[:methods] = [args[0][1]]
128
+
129
+ args[1..-1].each do |a|
130
+ filter[:methods] << a[1] if a.node_type == :lit
131
+ end
132
+
133
+ if args[-1].node_type == :hash
134
+ option = args[-1][1][1]
135
+ value = args[-1][2]
136
+ case value.node_type
137
+ when :array
138
+ filter[option] = value[1..-1].map {|v| v[1] }
139
+ when :lit, :str
140
+ filter[option] = value[1]
141
+ else
142
+ Brakeman.debug "[Notice] Unknown before_filter value: #{option} => #{value}"
143
+ end
144
+ else
145
+ filter[:all] = true
146
+ end
147
+
148
+ filter
149
+ end
150
+ end
151
+
152
+ class Controller < Brakeman::Collection
153
+ include ControllerMethods
154
+
155
+ def initialize name, parent, file_name, src, tracker
156
+ super
157
+ initialize_controller
158
+ @collection = tracker.controllers
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,17 @@
1
+ require 'brakeman/tracker/collection'
2
+ require 'brakeman/tracker/controller'
3
+ require 'brakeman/tracker/model'
4
+
5
+ module Brakeman
6
+ class Library < Brakeman::Collection
7
+ include ControllerMethods
8
+ include ModelMethods
9
+
10
+ def initialize name, parent, file_name, src, tracker
11
+ super
12
+ initialize_controller
13
+ initialize_model
14
+ @collection = tracker.libs
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,90 @@
1
+ require 'brakeman/tracker/collection'
2
+
3
+ module Brakeman
4
+ module ModelMethods
5
+ attr_reader :associations, :attr_accessible, :role_accessible
6
+
7
+ def initialize_model
8
+ @associations = {}
9
+ @role_accessible = []
10
+ @attr_accessible = nil
11
+ end
12
+
13
+ def association? method_name
14
+ @associations.each do |name, args|
15
+ args.each do |arg|
16
+ if symbol? arg and arg.value == method_name
17
+ return true
18
+ end
19
+ end
20
+ end
21
+
22
+ false
23
+ end
24
+
25
+ def unprotected_model?
26
+ @attr_accessible.nil? and !parent_classes_protected? and ancestor?(:"ActiveRecord::Base")
27
+ end
28
+
29
+ # go up the chain of parent classes to see if any have attr_accessible
30
+ def parent_classes_protected? seen={}
31
+ seen[self.name] = true
32
+
33
+ if @attr_accessible or self.includes.include? :"ActiveModel::ForbiddenAttributesProtection"
34
+ true
35
+ elsif parent = tracker.models[self.parent] and !seen[self.parent]
36
+ parent.parent_classes_protected? seen
37
+ else
38
+ false
39
+ end
40
+ end
41
+
42
+ def set_attr_accessible exp = nil
43
+ if exp
44
+ args = []
45
+
46
+ exp.each_arg do |e|
47
+ if node_type? e, :lit
48
+ args << e.value
49
+ elsif hash? e
50
+ @role_accessible.concat args
51
+ end
52
+ end
53
+
54
+ @attr_accessible ||= []
55
+ @attr_accessible.concat args
56
+ else
57
+ @attr_accessible ||= []
58
+ end
59
+ end
60
+
61
+ def set_attr_protected exp
62
+ add_option :attr_protected, exp
63
+ end
64
+
65
+ def attr_protected
66
+ @options[:attr_protected]
67
+ end
68
+ end
69
+
70
+ class Model < Brakeman::Collection
71
+ include ModelMethods
72
+
73
+ ASSOCIATIONS = Set[:belongs_to, :has_one, :has_many, :has_and_belongs_to_many]
74
+
75
+ def initialize name, parent, file_name, src, tracker
76
+ super
77
+ initialize_model
78
+ @collection = tracker.models
79
+ end
80
+
81
+ def add_option name, exp
82
+ if ASSOCIATIONS.include? name
83
+ @associations[name] ||= []
84
+ @associations[name].concat exp.args
85
+ else
86
+ super name, exp.arglist.line(exp.line)
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,33 @@
1
+ require 'brakeman/tracker/collection'
2
+
3
+ module Brakeman
4
+ class Template < Brakeman::Collection
5
+ attr_accessor :type
6
+ attr_reader :render_path
7
+ attr_writer :src
8
+
9
+ def initialize name, called_from, file_name, tracker
10
+ super name, nil, file_name, nil, tracker
11
+ @render_path = called_from
12
+ @outputs = []
13
+ end
14
+
15
+ def add_output exp
16
+ @outputs << exp
17
+ end
18
+
19
+ def each_output
20
+ @outputs.each do |o|
21
+ yield o
22
+ end
23
+ end
24
+
25
+ def rendered_from_controller?
26
+ if @render_path
27
+ @render_path.rendered_from_controller?
28
+ else
29
+ false
30
+ end
31
+ end
32
+ end
33
+ end