brakeman-lib 3.3.1

Sign up to get free protection for your applications and to get access to all the features.
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