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.
- checksums.yaml +7 -0
- data/CHANGES +872 -0
- data/FEATURES +16 -0
- data/README.md +169 -0
- data/WARNING_TYPES +95 -0
- data/bin/brakeman +89 -0
- data/lib/brakeman.rb +495 -0
- data/lib/brakeman/app_tree.rb +161 -0
- data/lib/brakeman/brakeman.rake +17 -0
- data/lib/brakeman/call_index.rb +219 -0
- data/lib/brakeman/checks.rb +191 -0
- data/lib/brakeman/checks/base_check.rb +518 -0
- data/lib/brakeman/checks/check_basic_auth.rb +88 -0
- data/lib/brakeman/checks/check_basic_auth_timing_attack.rb +33 -0
- data/lib/brakeman/checks/check_content_tag.rb +160 -0
- data/lib/brakeman/checks/check_create_with.rb +75 -0
- data/lib/brakeman/checks/check_cross_site_scripting.rb +385 -0
- data/lib/brakeman/checks/check_default_routes.rb +86 -0
- data/lib/brakeman/checks/check_deserialize.rb +57 -0
- data/lib/brakeman/checks/check_detailed_exceptions.rb +55 -0
- data/lib/brakeman/checks/check_digest_dos.rb +38 -0
- data/lib/brakeman/checks/check_dynamic_finders.rb +49 -0
- data/lib/brakeman/checks/check_escape_function.rb +21 -0
- data/lib/brakeman/checks/check_evaluation.rb +36 -0
- data/lib/brakeman/checks/check_execute.rb +167 -0
- data/lib/brakeman/checks/check_file_access.rb +63 -0
- data/lib/brakeman/checks/check_file_disclosure.rb +35 -0
- data/lib/brakeman/checks/check_filter_skipping.rb +31 -0
- data/lib/brakeman/checks/check_forgery_setting.rb +74 -0
- data/lib/brakeman/checks/check_header_dos.rb +31 -0
- data/lib/brakeman/checks/check_i18n_xss.rb +48 -0
- data/lib/brakeman/checks/check_jruby_xml.rb +38 -0
- data/lib/brakeman/checks/check_json_encoding.rb +47 -0
- data/lib/brakeman/checks/check_json_parsing.rb +107 -0
- data/lib/brakeman/checks/check_link_to.rb +132 -0
- data/lib/brakeman/checks/check_link_to_href.rb +115 -0
- data/lib/brakeman/checks/check_mail_to.rb +49 -0
- data/lib/brakeman/checks/check_mass_assignment.rb +198 -0
- data/lib/brakeman/checks/check_mime_type_dos.rb +39 -0
- data/lib/brakeman/checks/check_model_attr_accessible.rb +55 -0
- data/lib/brakeman/checks/check_model_attributes.rb +119 -0
- data/lib/brakeman/checks/check_model_serialize.rb +67 -0
- data/lib/brakeman/checks/check_nested_attributes.rb +38 -0
- data/lib/brakeman/checks/check_nested_attributes_bypass.rb +58 -0
- data/lib/brakeman/checks/check_number_to_currency.rb +74 -0
- data/lib/brakeman/checks/check_quote_table_name.rb +40 -0
- data/lib/brakeman/checks/check_redirect.rb +215 -0
- data/lib/brakeman/checks/check_regex_dos.rb +69 -0
- data/lib/brakeman/checks/check_render.rb +92 -0
- data/lib/brakeman/checks/check_render_dos.rb +37 -0
- data/lib/brakeman/checks/check_render_inline.rb +54 -0
- data/lib/brakeman/checks/check_response_splitting.rb +21 -0
- data/lib/brakeman/checks/check_route_dos.rb +42 -0
- data/lib/brakeman/checks/check_safe_buffer_manipulation.rb +31 -0
- data/lib/brakeman/checks/check_sanitize_methods.rb +79 -0
- data/lib/brakeman/checks/check_secrets.rb +40 -0
- data/lib/brakeman/checks/check_select_tag.rb +60 -0
- data/lib/brakeman/checks/check_select_vulnerability.rb +60 -0
- data/lib/brakeman/checks/check_send.rb +48 -0
- data/lib/brakeman/checks/check_send_file.rb +19 -0
- data/lib/brakeman/checks/check_session_manipulation.rb +36 -0
- data/lib/brakeman/checks/check_session_settings.rb +170 -0
- data/lib/brakeman/checks/check_simple_format.rb +59 -0
- data/lib/brakeman/checks/check_single_quotes.rb +101 -0
- data/lib/brakeman/checks/check_skip_before_filter.rb +60 -0
- data/lib/brakeman/checks/check_sql.rb +660 -0
- data/lib/brakeman/checks/check_sql_cves.rb +101 -0
- data/lib/brakeman/checks/check_ssl_verify.rb +49 -0
- data/lib/brakeman/checks/check_strip_tags.rb +89 -0
- data/lib/brakeman/checks/check_symbol_dos.rb +64 -0
- data/lib/brakeman/checks/check_symbol_dos_cve.rb +30 -0
- data/lib/brakeman/checks/check_translate_bug.rb +45 -0
- data/lib/brakeman/checks/check_unsafe_reflection.rb +51 -0
- data/lib/brakeman/checks/check_unscoped_find.rb +41 -0
- data/lib/brakeman/checks/check_validation_regex.rb +116 -0
- data/lib/brakeman/checks/check_weak_hash.rb +151 -0
- data/lib/brakeman/checks/check_without_protection.rb +80 -0
- data/lib/brakeman/checks/check_xml_dos.rb +51 -0
- data/lib/brakeman/checks/check_yaml_parsing.rb +121 -0
- data/lib/brakeman/differ.rb +66 -0
- data/lib/brakeman/file_parser.rb +50 -0
- data/lib/brakeman/format/style.css +133 -0
- data/lib/brakeman/options.rb +301 -0
- data/lib/brakeman/parsers/rails2_erubis.rb +6 -0
- data/lib/brakeman/parsers/rails2_xss_plugin_erubis.rb +48 -0
- data/lib/brakeman/parsers/rails3_erubis.rb +74 -0
- data/lib/brakeman/parsers/template_parser.rb +89 -0
- data/lib/brakeman/processor.rb +102 -0
- data/lib/brakeman/processors/alias_processor.rb +1013 -0
- data/lib/brakeman/processors/base_processor.rb +277 -0
- data/lib/brakeman/processors/config_processor.rb +14 -0
- data/lib/brakeman/processors/controller_alias_processor.rb +273 -0
- data/lib/brakeman/processors/controller_processor.rb +326 -0
- data/lib/brakeman/processors/erb_template_processor.rb +80 -0
- data/lib/brakeman/processors/erubis_template_processor.rb +104 -0
- data/lib/brakeman/processors/gem_processor.rb +57 -0
- data/lib/brakeman/processors/haml_template_processor.rb +190 -0
- data/lib/brakeman/processors/lib/basic_processor.rb +37 -0
- data/lib/brakeman/processors/lib/find_all_calls.rb +223 -0
- data/lib/brakeman/processors/lib/find_call.rb +183 -0
- data/lib/brakeman/processors/lib/find_return_value.rb +134 -0
- data/lib/brakeman/processors/lib/processor_helper.rb +75 -0
- data/lib/brakeman/processors/lib/rails2_config_processor.rb +145 -0
- data/lib/brakeman/processors/lib/rails2_route_processor.rb +313 -0
- data/lib/brakeman/processors/lib/rails3_config_processor.rb +132 -0
- data/lib/brakeman/processors/lib/rails3_route_processor.rb +308 -0
- data/lib/brakeman/processors/lib/render_helper.rb +181 -0
- data/lib/brakeman/processors/lib/render_path.rb +107 -0
- data/lib/brakeman/processors/lib/route_helper.rb +68 -0
- data/lib/brakeman/processors/lib/safe_call_helper.rb +16 -0
- data/lib/brakeman/processors/library_processor.rb +119 -0
- data/lib/brakeman/processors/model_processor.rb +191 -0
- data/lib/brakeman/processors/output_processor.rb +171 -0
- data/lib/brakeman/processors/route_processor.rb +17 -0
- data/lib/brakeman/processors/slim_template_processor.rb +107 -0
- data/lib/brakeman/processors/template_alias_processor.rb +116 -0
- data/lib/brakeman/processors/template_processor.rb +74 -0
- data/lib/brakeman/report.rb +78 -0
- data/lib/brakeman/report/config/remediation.yml +71 -0
- data/lib/brakeman/report/ignore/config.rb +135 -0
- data/lib/brakeman/report/ignore/interactive.rb +311 -0
- data/lib/brakeman/report/renderer.rb +24 -0
- data/lib/brakeman/report/report_base.rb +286 -0
- data/lib/brakeman/report/report_codeclimate.rb +70 -0
- data/lib/brakeman/report/report_csv.rb +55 -0
- data/lib/brakeman/report/report_hash.rb +23 -0
- data/lib/brakeman/report/report_html.rb +216 -0
- data/lib/brakeman/report/report_json.rb +42 -0
- data/lib/brakeman/report/report_markdown.rb +156 -0
- data/lib/brakeman/report/report_table.rb +107 -0
- data/lib/brakeman/report/report_tabs.rb +17 -0
- data/lib/brakeman/report/templates/controller_overview.html.erb +22 -0
- data/lib/brakeman/report/templates/controller_warnings.html.erb +21 -0
- data/lib/brakeman/report/templates/error_overview.html.erb +29 -0
- data/lib/brakeman/report/templates/header.html.erb +58 -0
- data/lib/brakeman/report/templates/ignored_warnings.html.erb +25 -0
- data/lib/brakeman/report/templates/model_warnings.html.erb +21 -0
- data/lib/brakeman/report/templates/overview.html.erb +38 -0
- data/lib/brakeman/report/templates/security_warnings.html.erb +23 -0
- data/lib/brakeman/report/templates/template_overview.html.erb +21 -0
- data/lib/brakeman/report/templates/view_warnings.html.erb +34 -0
- data/lib/brakeman/report/templates/warning_overview.html.erb +17 -0
- data/lib/brakeman/rescanner.rb +483 -0
- data/lib/brakeman/scanner.rb +317 -0
- data/lib/brakeman/tracker.rb +347 -0
- data/lib/brakeman/tracker/collection.rb +93 -0
- data/lib/brakeman/tracker/config.rb +101 -0
- data/lib/brakeman/tracker/constants.rb +101 -0
- data/lib/brakeman/tracker/controller.rb +161 -0
- data/lib/brakeman/tracker/library.rb +17 -0
- data/lib/brakeman/tracker/model.rb +90 -0
- data/lib/brakeman/tracker/template.rb +33 -0
- data/lib/brakeman/util.rb +481 -0
- data/lib/brakeman/version.rb +3 -0
- data/lib/brakeman/warning.rb +255 -0
- data/lib/brakeman/warning_codes.rb +111 -0
- data/lib/ruby_parser/bm_sexp.rb +610 -0
- data/lib/ruby_parser/bm_sexp_processor.rb +116 -0
- 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
|