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