brakeman-lib 3.3.1

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 (159) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +872 -0
  3. data/FEATURES +16 -0
  4. data/README.md +169 -0
  5. data/WARNING_TYPES +95 -0
  6. data/bin/brakeman +89 -0
  7. data/lib/brakeman.rb +495 -0
  8. data/lib/brakeman/app_tree.rb +161 -0
  9. data/lib/brakeman/brakeman.rake +17 -0
  10. data/lib/brakeman/call_index.rb +219 -0
  11. data/lib/brakeman/checks.rb +191 -0
  12. data/lib/brakeman/checks/base_check.rb +518 -0
  13. data/lib/brakeman/checks/check_basic_auth.rb +88 -0
  14. data/lib/brakeman/checks/check_basic_auth_timing_attack.rb +33 -0
  15. data/lib/brakeman/checks/check_content_tag.rb +160 -0
  16. data/lib/brakeman/checks/check_create_with.rb +75 -0
  17. data/lib/brakeman/checks/check_cross_site_scripting.rb +385 -0
  18. data/lib/brakeman/checks/check_default_routes.rb +86 -0
  19. data/lib/brakeman/checks/check_deserialize.rb +57 -0
  20. data/lib/brakeman/checks/check_detailed_exceptions.rb +55 -0
  21. data/lib/brakeman/checks/check_digest_dos.rb +38 -0
  22. data/lib/brakeman/checks/check_dynamic_finders.rb +49 -0
  23. data/lib/brakeman/checks/check_escape_function.rb +21 -0
  24. data/lib/brakeman/checks/check_evaluation.rb +36 -0
  25. data/lib/brakeman/checks/check_execute.rb +167 -0
  26. data/lib/brakeman/checks/check_file_access.rb +63 -0
  27. data/lib/brakeman/checks/check_file_disclosure.rb +35 -0
  28. data/lib/brakeman/checks/check_filter_skipping.rb +31 -0
  29. data/lib/brakeman/checks/check_forgery_setting.rb +74 -0
  30. data/lib/brakeman/checks/check_header_dos.rb +31 -0
  31. data/lib/brakeman/checks/check_i18n_xss.rb +48 -0
  32. data/lib/brakeman/checks/check_jruby_xml.rb +38 -0
  33. data/lib/brakeman/checks/check_json_encoding.rb +47 -0
  34. data/lib/brakeman/checks/check_json_parsing.rb +107 -0
  35. data/lib/brakeman/checks/check_link_to.rb +132 -0
  36. data/lib/brakeman/checks/check_link_to_href.rb +115 -0
  37. data/lib/brakeman/checks/check_mail_to.rb +49 -0
  38. data/lib/brakeman/checks/check_mass_assignment.rb +198 -0
  39. data/lib/brakeman/checks/check_mime_type_dos.rb +39 -0
  40. data/lib/brakeman/checks/check_model_attr_accessible.rb +55 -0
  41. data/lib/brakeman/checks/check_model_attributes.rb +119 -0
  42. data/lib/brakeman/checks/check_model_serialize.rb +67 -0
  43. data/lib/brakeman/checks/check_nested_attributes.rb +38 -0
  44. data/lib/brakeman/checks/check_nested_attributes_bypass.rb +58 -0
  45. data/lib/brakeman/checks/check_number_to_currency.rb +74 -0
  46. data/lib/brakeman/checks/check_quote_table_name.rb +40 -0
  47. data/lib/brakeman/checks/check_redirect.rb +215 -0
  48. data/lib/brakeman/checks/check_regex_dos.rb +69 -0
  49. data/lib/brakeman/checks/check_render.rb +92 -0
  50. data/lib/brakeman/checks/check_render_dos.rb +37 -0
  51. data/lib/brakeman/checks/check_render_inline.rb +54 -0
  52. data/lib/brakeman/checks/check_response_splitting.rb +21 -0
  53. data/lib/brakeman/checks/check_route_dos.rb +42 -0
  54. data/lib/brakeman/checks/check_safe_buffer_manipulation.rb +31 -0
  55. data/lib/brakeman/checks/check_sanitize_methods.rb +79 -0
  56. data/lib/brakeman/checks/check_secrets.rb +40 -0
  57. data/lib/brakeman/checks/check_select_tag.rb +60 -0
  58. data/lib/brakeman/checks/check_select_vulnerability.rb +60 -0
  59. data/lib/brakeman/checks/check_send.rb +48 -0
  60. data/lib/brakeman/checks/check_send_file.rb +19 -0
  61. data/lib/brakeman/checks/check_session_manipulation.rb +36 -0
  62. data/lib/brakeman/checks/check_session_settings.rb +170 -0
  63. data/lib/brakeman/checks/check_simple_format.rb +59 -0
  64. data/lib/brakeman/checks/check_single_quotes.rb +101 -0
  65. data/lib/brakeman/checks/check_skip_before_filter.rb +60 -0
  66. data/lib/brakeman/checks/check_sql.rb +660 -0
  67. data/lib/brakeman/checks/check_sql_cves.rb +101 -0
  68. data/lib/brakeman/checks/check_ssl_verify.rb +49 -0
  69. data/lib/brakeman/checks/check_strip_tags.rb +89 -0
  70. data/lib/brakeman/checks/check_symbol_dos.rb +64 -0
  71. data/lib/brakeman/checks/check_symbol_dos_cve.rb +30 -0
  72. data/lib/brakeman/checks/check_translate_bug.rb +45 -0
  73. data/lib/brakeman/checks/check_unsafe_reflection.rb +51 -0
  74. data/lib/brakeman/checks/check_unscoped_find.rb +41 -0
  75. data/lib/brakeman/checks/check_validation_regex.rb +116 -0
  76. data/lib/brakeman/checks/check_weak_hash.rb +151 -0
  77. data/lib/brakeman/checks/check_without_protection.rb +80 -0
  78. data/lib/brakeman/checks/check_xml_dos.rb +51 -0
  79. data/lib/brakeman/checks/check_yaml_parsing.rb +121 -0
  80. data/lib/brakeman/differ.rb +66 -0
  81. data/lib/brakeman/file_parser.rb +50 -0
  82. data/lib/brakeman/format/style.css +133 -0
  83. data/lib/brakeman/options.rb +301 -0
  84. data/lib/brakeman/parsers/rails2_erubis.rb +6 -0
  85. data/lib/brakeman/parsers/rails2_xss_plugin_erubis.rb +48 -0
  86. data/lib/brakeman/parsers/rails3_erubis.rb +74 -0
  87. data/lib/brakeman/parsers/template_parser.rb +89 -0
  88. data/lib/brakeman/processor.rb +102 -0
  89. data/lib/brakeman/processors/alias_processor.rb +1013 -0
  90. data/lib/brakeman/processors/base_processor.rb +277 -0
  91. data/lib/brakeman/processors/config_processor.rb +14 -0
  92. data/lib/brakeman/processors/controller_alias_processor.rb +273 -0
  93. data/lib/brakeman/processors/controller_processor.rb +326 -0
  94. data/lib/brakeman/processors/erb_template_processor.rb +80 -0
  95. data/lib/brakeman/processors/erubis_template_processor.rb +104 -0
  96. data/lib/brakeman/processors/gem_processor.rb +57 -0
  97. data/lib/brakeman/processors/haml_template_processor.rb +190 -0
  98. data/lib/brakeman/processors/lib/basic_processor.rb +37 -0
  99. data/lib/brakeman/processors/lib/find_all_calls.rb +223 -0
  100. data/lib/brakeman/processors/lib/find_call.rb +183 -0
  101. data/lib/brakeman/processors/lib/find_return_value.rb +134 -0
  102. data/lib/brakeman/processors/lib/processor_helper.rb +75 -0
  103. data/lib/brakeman/processors/lib/rails2_config_processor.rb +145 -0
  104. data/lib/brakeman/processors/lib/rails2_route_processor.rb +313 -0
  105. data/lib/brakeman/processors/lib/rails3_config_processor.rb +132 -0
  106. data/lib/brakeman/processors/lib/rails3_route_processor.rb +308 -0
  107. data/lib/brakeman/processors/lib/render_helper.rb +181 -0
  108. data/lib/brakeman/processors/lib/render_path.rb +107 -0
  109. data/lib/brakeman/processors/lib/route_helper.rb +68 -0
  110. data/lib/brakeman/processors/lib/safe_call_helper.rb +16 -0
  111. data/lib/brakeman/processors/library_processor.rb +119 -0
  112. data/lib/brakeman/processors/model_processor.rb +191 -0
  113. data/lib/brakeman/processors/output_processor.rb +171 -0
  114. data/lib/brakeman/processors/route_processor.rb +17 -0
  115. data/lib/brakeman/processors/slim_template_processor.rb +107 -0
  116. data/lib/brakeman/processors/template_alias_processor.rb +116 -0
  117. data/lib/brakeman/processors/template_processor.rb +74 -0
  118. data/lib/brakeman/report.rb +78 -0
  119. data/lib/brakeman/report/config/remediation.yml +71 -0
  120. data/lib/brakeman/report/ignore/config.rb +135 -0
  121. data/lib/brakeman/report/ignore/interactive.rb +311 -0
  122. data/lib/brakeman/report/renderer.rb +24 -0
  123. data/lib/brakeman/report/report_base.rb +286 -0
  124. data/lib/brakeman/report/report_codeclimate.rb +70 -0
  125. data/lib/brakeman/report/report_csv.rb +55 -0
  126. data/lib/brakeman/report/report_hash.rb +23 -0
  127. data/lib/brakeman/report/report_html.rb +216 -0
  128. data/lib/brakeman/report/report_json.rb +42 -0
  129. data/lib/brakeman/report/report_markdown.rb +156 -0
  130. data/lib/brakeman/report/report_table.rb +107 -0
  131. data/lib/brakeman/report/report_tabs.rb +17 -0
  132. data/lib/brakeman/report/templates/controller_overview.html.erb +22 -0
  133. data/lib/brakeman/report/templates/controller_warnings.html.erb +21 -0
  134. data/lib/brakeman/report/templates/error_overview.html.erb +29 -0
  135. data/lib/brakeman/report/templates/header.html.erb +58 -0
  136. data/lib/brakeman/report/templates/ignored_warnings.html.erb +25 -0
  137. data/lib/brakeman/report/templates/model_warnings.html.erb +21 -0
  138. data/lib/brakeman/report/templates/overview.html.erb +38 -0
  139. data/lib/brakeman/report/templates/security_warnings.html.erb +23 -0
  140. data/lib/brakeman/report/templates/template_overview.html.erb +21 -0
  141. data/lib/brakeman/report/templates/view_warnings.html.erb +34 -0
  142. data/lib/brakeman/report/templates/warning_overview.html.erb +17 -0
  143. data/lib/brakeman/rescanner.rb +483 -0
  144. data/lib/brakeman/scanner.rb +317 -0
  145. data/lib/brakeman/tracker.rb +347 -0
  146. data/lib/brakeman/tracker/collection.rb +93 -0
  147. data/lib/brakeman/tracker/config.rb +101 -0
  148. data/lib/brakeman/tracker/constants.rb +101 -0
  149. data/lib/brakeman/tracker/controller.rb +161 -0
  150. data/lib/brakeman/tracker/library.rb +17 -0
  151. data/lib/brakeman/tracker/model.rb +90 -0
  152. data/lib/brakeman/tracker/template.rb +33 -0
  153. data/lib/brakeman/util.rb +481 -0
  154. data/lib/brakeman/version.rb +3 -0
  155. data/lib/brakeman/warning.rb +255 -0
  156. data/lib/brakeman/warning_codes.rb +111 -0
  157. data/lib/ruby_parser/bm_sexp.rb +610 -0
  158. data/lib/ruby_parser/bm_sexp_processor.rb +116 -0
  159. metadata +362 -0
@@ -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
@@ -0,0 +1,481 @@
1
+ require 'set'
2
+ require 'pathname'
3
+
4
+ #This is a mixin containing utility methods.
5
+ module Brakeman::Util
6
+
7
+ QUERY_PARAMETERS = Sexp.new(:call, Sexp.new(:call, nil, :request), :query_parameters)
8
+
9
+ PATH_PARAMETERS = Sexp.new(:call, Sexp.new(:call, nil, :request), :path_parameters)
10
+
11
+ REQUEST_PARAMETERS = Sexp.new(:call, Sexp.new(:call, nil, :request), :request_parameters)
12
+
13
+ REQUEST_PARAMS = Sexp.new(:call, Sexp.new(:call, nil, :request), :parameters)
14
+
15
+ REQUEST_ENV = Sexp.new(:call, Sexp.new(:call, nil, :request), :env)
16
+
17
+ PARAMETERS = Sexp.new(:call, nil, :params)
18
+
19
+ COOKIES = Sexp.new(:call, nil, :cookies)
20
+
21
+ SESSION = Sexp.new(:call, nil, :session)
22
+
23
+ ALL_PARAMETERS = Set[PARAMETERS, QUERY_PARAMETERS, PATH_PARAMETERS, REQUEST_PARAMETERS, REQUEST_PARAMS]
24
+
25
+ #Convert a string from "something_like_this" to "SomethingLikeThis"
26
+ #
27
+ #Taken from ActiveSupport.
28
+ def camelize lower_case_and_underscored_word
29
+ lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
30
+ end
31
+
32
+ #Convert a string from "Something::LikeThis" to "something/like_this"
33
+ #
34
+ #Taken from ActiveSupport.
35
+ def underscore camel_cased_word
36
+ camel_cased_word.to_s.gsub(/::/, '/').
37
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
38
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
39
+ tr("-", "_").
40
+ downcase
41
+ end
42
+
43
+ # stupid simple, used to delegate to ActiveSupport
44
+ def pluralize word
45
+ word + "s"
46
+ end
47
+
48
+ #Returns a class name as a Symbol.
49
+ #If class name cannot be determined, returns _exp_.
50
+ def class_name exp
51
+ case exp
52
+ when Sexp
53
+ case exp.node_type
54
+ when :const
55
+ exp.value
56
+ when :lvar
57
+ exp.value.to_sym
58
+ when :colon2
59
+ "#{class_name(exp.lhs)}::#{exp.rhs}".to_sym
60
+ when :colon3
61
+ "::#{exp.value}".to_sym
62
+ when :self
63
+ @current_class || @current_module || nil
64
+ else
65
+ exp
66
+ end
67
+ when Symbol
68
+ exp
69
+ when nil
70
+ nil
71
+ else
72
+ exp
73
+ end
74
+ end
75
+
76
+ #Takes an Sexp like
77
+ # (:hash, (:lit, :key), (:str, "value"))
78
+ #and yields the key and value pairs to the given block.
79
+ #
80
+ #For example:
81
+ #
82
+ # h = Sexp.new(:hash, (:lit, :name), (:str, "bob"), (:lit, :name), (:str, "jane"))
83
+ # names = []
84
+ # hash_iterate(h) do |key, value|
85
+ # if symbol? key and key[1] == :name
86
+ # names << value[1]
87
+ # end
88
+ # end
89
+ # names #["bob"]
90
+ def hash_iterate hash
91
+ 1.step(hash.length - 1, 2) do |i|
92
+ yield hash[i], hash[i + 1]
93
+ end
94
+ end
95
+
96
+ #Insert value into Hash Sexp
97
+ def hash_insert hash, key, value
98
+ index = 1
99
+ hash_iterate hash.dup do |k,v|
100
+ if k == key
101
+ hash[index + 1] = value
102
+ return hash
103
+ end
104
+ index += 2
105
+ end
106
+
107
+ hash << key << value
108
+
109
+ hash
110
+ end
111
+
112
+ #Get value from hash using key.
113
+ #
114
+ #If _key_ is a Symbol, it will be converted to a Sexp(:lit, key).
115
+ def hash_access hash, key
116
+ if key.is_a? Symbol
117
+ key = Sexp.new(:lit, key)
118
+ end
119
+
120
+ if index = hash.find_index(key) and index > 0
121
+ return hash[index + 1]
122
+ end
123
+
124
+ nil
125
+ end
126
+
127
+ #These are never modified
128
+ PARAMS_SEXP = Sexp.new(:params)
129
+ SESSION_SEXP = Sexp.new(:session)
130
+ COOKIES_SEXP = Sexp.new(:cookies)
131
+
132
+ #Adds params, session, and cookies to environment
133
+ #so they can be replaced by their respective Sexps.
134
+ def set_env_defaults
135
+ @env[PARAMETERS] = PARAMS_SEXP
136
+ @env[SESSION] = SESSION_SEXP
137
+ @env[COOKIES] = COOKIES_SEXP
138
+ end
139
+
140
+ #Check if _exp_ represents a hash: s(:hash, {...})
141
+ #This also includes pseudo hashes params, session, and cookies.
142
+ def hash? exp
143
+ exp.is_a? Sexp and (exp.node_type == :hash or
144
+ exp.node_type == :params or
145
+ exp.node_type == :session or
146
+ exp.node_type == :cookies)
147
+ end
148
+
149
+ #Check if _exp_ represents an array: s(:array, [...])
150
+ def array? exp
151
+ exp.is_a? Sexp and exp.node_type == :array
152
+ end
153
+
154
+ #Check if _exp_ represents a String: s(:str, "...")
155
+ def string? exp
156
+ exp.is_a? Sexp and exp.node_type == :str
157
+ end
158
+
159
+ def string_interp? exp
160
+ exp.is_a? Sexp and exp.node_type == :dstr
161
+ end
162
+
163
+ #Check if _exp_ represents a Symbol: s(:lit, :...)
164
+ def symbol? exp
165
+ exp.is_a? Sexp and exp.node_type == :lit and exp[1].is_a? Symbol
166
+ end
167
+
168
+ #Check if _exp_ represents a method call: s(:call, ...)
169
+ def call? exp
170
+ exp.is_a? Sexp and
171
+ (exp.node_type == :call or exp.node_type == :safe_call)
172
+ end
173
+
174
+ #Check if _exp_ represents a Regexp: s(:lit, /.../)
175
+ def regexp? exp
176
+ exp.is_a? Sexp and exp.node_type == :lit and exp[1].is_a? Regexp
177
+ end
178
+
179
+ #Check if _exp_ represents an Integer: s(:lit, ...)
180
+ def integer? exp
181
+ exp.is_a? Sexp and exp.node_type == :lit and exp[1].is_a? Integer
182
+ end
183
+
184
+ #Check if _exp_ represents a number: s(:lit, ...)
185
+ def number? exp
186
+ exp.is_a? Sexp and exp.node_type == :lit and exp[1].is_a? Numeric
187
+ end
188
+
189
+ #Check if _exp_ represents a result: s(:result, ...)
190
+ def result? exp
191
+ exp.is_a? Sexp and exp.node_type == :result
192
+ end
193
+
194
+ #Check if _exp_ represents a :true, :lit, or :string node
195
+ def true? exp
196
+ exp.is_a? Sexp and (exp.node_type == :true or
197
+ exp.node_type == :lit or
198
+ exp.node_type == :string)
199
+ end
200
+
201
+ #Check if _exp_ represents a :false or :nil node
202
+ def false? exp
203
+ exp.is_a? Sexp and (exp.node_type == :false or
204
+ exp.node_type == :nil)
205
+ end
206
+
207
+ #Check if _exp_ represents a block of code
208
+ def block? exp
209
+ exp.is_a? Sexp and (exp.node_type == :block or
210
+ exp.node_type == :rlist)
211
+ end
212
+
213
+ #Check if _exp_ is a params hash
214
+ def params? exp
215
+ if exp.is_a? Sexp
216
+ return true if exp.node_type == :params or ALL_PARAMETERS.include? exp
217
+
218
+ if call? exp
219
+ if params? exp[1]
220
+ return true
221
+ elsif exp[2] == :[]
222
+ return params? exp[1]
223
+ end
224
+ end
225
+ end
226
+
227
+ false
228
+ end
229
+
230
+ def cookies? exp
231
+ if exp.is_a? Sexp
232
+ return true if exp.node_type == :cookies or exp == COOKIES
233
+
234
+ if call? exp
235
+ if cookies? exp[1]
236
+ return true
237
+ elsif exp[2] == :[]
238
+ return cookies? exp[1]
239
+ end
240
+ end
241
+ end
242
+
243
+ false
244
+ end
245
+
246
+ def request_env? exp
247
+ call? exp and (exp == REQUEST_ENV or exp[1] == REQUEST_ENV)
248
+ end
249
+
250
+ #Check if exp is params, cookies, or request_env
251
+ def request_value? exp
252
+ params? exp or
253
+ cookies? exp or
254
+ request_env? exp
255
+ end
256
+
257
+ def constant? exp
258
+ node_type? exp, :const, :colon2, :colon3
259
+ end
260
+
261
+ #Check if _exp_ is a Sexp.
262
+ def sexp? exp
263
+ exp.is_a? Sexp
264
+ end
265
+
266
+ #Check if _exp_ is a Sexp and the node type matches one of the given types.
267
+ def node_type? exp, *types
268
+ exp.is_a? Sexp and types.include? exp.node_type
269
+ end
270
+
271
+ #Returns true if the given _exp_ contains a :class node.
272
+ #
273
+ #Useful for checking if a module is just a module or if it is a namespace.
274
+ def contains_class? exp
275
+ todo = [exp]
276
+
277
+ until todo.empty?
278
+ current = todo.shift
279
+
280
+ if node_type? current, :class
281
+ return true
282
+ elsif sexp? current
283
+ todo = current[1..-1].concat todo
284
+ end
285
+ end
286
+
287
+ false
288
+ end
289
+
290
+ def make_call target, method, *args
291
+ call = Sexp.new(:call, target, method)
292
+
293
+ if args.empty? or args.first.empty?
294
+ #nothing to do
295
+ elsif node_type? args.first, :arglist
296
+ call.concat args.first[1..-1]
297
+ elsif args.first.node_type.is_a? Sexp #just a list of args
298
+ call.concat args.first
299
+ else
300
+ call.concat args
301
+ end
302
+
303
+ call
304
+ end
305
+
306
+ def rails_version
307
+ @tracker.config.rails_version
308
+ end
309
+
310
+ #Return file name related to given warning. Uses +warning.file+ if it exists
311
+ def file_for warning, tracker = nil
312
+ if tracker.nil?
313
+ tracker = @tracker || self.tracker
314
+ end
315
+
316
+ if warning.file
317
+ File.expand_path warning.file, tracker.app_path
318
+ elsif warning.template and warning.template.file
319
+ warning.template.file
320
+ else
321
+ case warning.warning_set
322
+ when :controller
323
+ file_by_name warning.controller, :controller, tracker
324
+ when :template
325
+ file_by_name warning.template.name, :template, tracker
326
+ when :model
327
+ file_by_name warning.model, :model, tracker
328
+ when :warning
329
+ file_by_name warning.class, nil, tracker
330
+ else
331
+ nil
332
+ end
333
+ end
334
+ end
335
+
336
+ #Attempt to determine path to context file based on the reported name
337
+ #in the warning.
338
+ #
339
+ #For example,
340
+ #
341
+ # file_by_name FileController #=> "/rails/root/app/controllers/file_controller.rb
342
+ def file_by_name name, type, tracker = nil
343
+ return nil unless name
344
+ string_name = name.to_s
345
+ name = name.to_sym
346
+
347
+ unless type
348
+ if string_name =~ /Controller$/
349
+ type = :controller
350
+ elsif camelize(string_name) == string_name # This is not always true
351
+ type = :model
352
+ else
353
+ type = :template
354
+ end
355
+ end
356
+
357
+ path = tracker.app_path
358
+
359
+ case type
360
+ when :controller
361
+ if tracker.controllers[name]
362
+ path = tracker.controllers[name].file
363
+ else
364
+ path += "/app/controllers/#{underscore(string_name)}.rb"
365
+ end
366
+ when :model
367
+ if tracker.models[name]
368
+ path = tracker.models[name].file
369
+ else
370
+ path += "/app/models/#{underscore(string_name)}.rb"
371
+ end
372
+ when :template
373
+ if tracker.templates[name] and tracker.templates[name].file
374
+ path = tracker.templates[name].file
375
+ elsif string_name.include? " "
376
+ name = string_name.split[0].to_sym
377
+ path = file_for tracker, name, :template
378
+ else
379
+ path = nil
380
+ end
381
+ end
382
+
383
+ path
384
+ end
385
+
386
+ #Return array of lines surrounding the warning location from the original
387
+ #file.
388
+ def context_for app_tree, warning, tracker = nil
389
+ file = file_for warning, tracker
390
+ context = []
391
+ return context unless warning.line and file and @app_tree.path_exists? file
392
+
393
+ current_line = 0
394
+ start_line = warning.line - 5
395
+ end_line = warning.line + 5
396
+
397
+ start_line = 1 if start_line < 0
398
+
399
+ File.open file do |f|
400
+ f.each_line do |line|
401
+ current_line += 1
402
+
403
+ next if line.strip == ""
404
+
405
+ if current_line > end_line
406
+ break
407
+ end
408
+
409
+ if current_line >= start_line
410
+ context << [current_line, line]
411
+ end
412
+ end
413
+ end
414
+
415
+ context
416
+ end
417
+
418
+ def relative_path file
419
+ pname = Pathname.new file
420
+ if file and not file.empty? and pname.absolute?
421
+ pname.relative_path_from(Pathname.new(@tracker.app_path)).to_s
422
+ else
423
+ file
424
+ end
425
+ end
426
+
427
+ #Convert path/filename to view name
428
+ #
429
+ # views/test/something.html.erb -> test/something
430
+ def template_path_to_name path
431
+ names = path.split("/")
432
+ names.last.gsub!(/(\.(html|js)\..*|\.rhtml)$/, '')
433
+ names[(names.index("views") + 1)..-1].join("/").to_sym
434
+ end
435
+
436
+ def github_url file, line=nil
437
+ if repo_url = @tracker.options[:github_url] and file and not file.empty? and file.start_with? '/'
438
+ url = "#{repo_url}/#{relative_path(file)}"
439
+ url << "#L#{line}" if line
440
+ else
441
+ nil
442
+ end
443
+ end
444
+
445
+ def truncate_table str
446
+ @terminal_width ||= if @tracker.options[:table_width]
447
+ @tracker.options[:table_width]
448
+ elsif $stdin && $stdin.tty?
449
+ Brakeman.load_brakeman_dependency 'highline'
450
+ ::HighLine.new.terminal_size[0]
451
+ else
452
+ 80
453
+ end
454
+ lines = str.lines
455
+
456
+ lines.map do |line|
457
+ if line.chomp.length > @terminal_width
458
+ line[0..(@terminal_width - 3)] + ">>\n"
459
+ else
460
+ line
461
+ end
462
+ end.join
463
+ end
464
+
465
+ # rely on Terminal::Table to build the structure, extract the data out in CSV format
466
+ def table_to_csv table
467
+ return "" unless table
468
+
469
+ Brakeman.load_brakeman_dependency 'terminal-table'
470
+ headings = table.headings
471
+ if headings.is_a? Array
472
+ headings = headings.first
473
+ end
474
+
475
+ output = CSV.generate_line(headings.cells.map{|cell| cell.to_s.strip})
476
+ table.rows.each do |row|
477
+ output << CSV.generate_line(row.cells.map{|cell| cell.to_s.strip})
478
+ end
479
+ output
480
+ end
481
+ end