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,16 @@
1
+ Can detect:
2
+ -Possibly unescaped model attributes or parameters in views (Cross Site Scripting)
3
+ -Bad string interpolation in calls to Model.find, Model.last, Model.first, etc., as well as chained calls (SQL Injection)
4
+ -String interpolation in find_by_sql (SQL Injection)
5
+ -String interpolation or params in calls to system, exec, and syscall and `` (Command Injection)
6
+ -Unrestricted mass assignments
7
+ -Global restriction of mass assignment
8
+ -Missing call to protect_from_forgery in ApplicationController (CSRF protection)
9
+ -Default routes, per-controller and globally
10
+ -Redirects based on params (probably too broad currently)
11
+ -Validation regexes not using \A and \z
12
+ -Calls to render with dynamic paths
13
+
14
+ General capabilities:
15
+ -Search for method calls based on target class and/or method name
16
+ -Determine 'output' of templates using ERB, Erubis, or HAML. Can handle automatic escaping
@@ -0,0 +1,169 @@
1
+ [![Brakeman Logo](http://brakemanscanner.org/images/logo_medium.png)](http://brakemanscanner.org/)
2
+ [![Brakeman Pro Logo](https://brakemanpro.com/images/bmp_square_white.png)](https://brakemanpro.com)
3
+
4
+ [![Build Status](https://travis-ci.org/presidentbeef/brakeman.svg?branch=master)](https://travis-ci.org/presidentbeef/brakeman)
5
+ [![Code Climate](https://codeclimate.com/github/presidentbeef/brakeman/badges/gpa.svg)](https://codeclimate.com/github/presidentbeef/brakeman)
6
+ [![Test Coverage](https://codeclimate.com/github/presidentbeef/brakeman/badges/coverage.svg)](https://codeclimate.com/github/presidentbeef/brakeman/coverage)
7
+ [![Gitter](https://badges.gitter.im/presidentbeef/brakeman.svg)](https://gitter.im/presidentbeef/brakeman)
8
+
9
+ # Brakeman
10
+
11
+ Brakeman is an open source static analysis tool which checks Ruby on Rails applications for security vulnerabilities.
12
+
13
+ Check out [Brakeman Pro](https://brakemanpro.com/) if you are looking for a commercially-supported version with a GUI and advanced features.
14
+
15
+ # Installation
16
+
17
+ Using RubyGems:
18
+
19
+ gem install brakeman
20
+
21
+ Using Bundler:
22
+
23
+ group :development do
24
+ gem 'brakeman', :require => false
25
+ end
26
+
27
+ # Usage
28
+
29
+ From a Rails application's root directory:
30
+
31
+ brakeman
32
+
33
+ Outside of Rails root:
34
+
35
+ brakeman /path/to/rails/application
36
+
37
+ # Compatibility
38
+
39
+ Brakeman works with Rails 2.x, 3.x, and 4.x.
40
+
41
+ # Basic Options
42
+
43
+ For a full list of options, use `brakeman --help` or see the [OPTIONS.md](OPTIONS.md) file.
44
+
45
+ To specify an output file for the results:
46
+
47
+ brakeman -o output_file
48
+
49
+ The output format is determined by the file extension or by using the `-f` option. Current options are: `text`, `html`, `tabs`, `json`, `markdown`, `csv`, and `codeclimate`.
50
+
51
+ Multiple output files can be specified:
52
+
53
+ brakeman -o output.html -o output.json
54
+
55
+ To suppress informational warnings and just output the report:
56
+
57
+ brakeman -q
58
+
59
+ Note all Brakeman output except reports are sent to stderr, making it simple to redirect stdout to a file and just get the report.
60
+
61
+ To see all kinds of debugging information:
62
+
63
+ brakeman -d
64
+
65
+ Specific checks can be skipped, if desired. The name needs to be the correct case. For example, to skip looking for default routes (`DefaultRoutes`):
66
+
67
+ brakeman -x DefaultRoutes
68
+
69
+ Multiple checks should be separated by a comma:
70
+
71
+ brakeman -x DefaultRoutes,Redirect
72
+
73
+ To do the opposite and only run a certain set of tests:
74
+
75
+ brakeman -t SQL,ValidationRegex
76
+
77
+ If Brakeman is running a bit slow, try
78
+
79
+ brakeman --faster
80
+
81
+ This will disable some features, but will probably be much faster (currently it is the same as `--skip-libs --no-branching`). *WARNING*: This may cause Brakeman to miss some vulnerabilities.
82
+
83
+ By default, Brakeman will return 0 as an exit code unless something went very wrong. To return an error code when warnings were found:
84
+
85
+ brakeman -z
86
+
87
+ To skip certain files or directories that Brakeman may have trouble parsing, use:
88
+
89
+ brakeman --skip-files file1,/path1/,path2/
90
+
91
+ To compare results of a scan with a previous scan, use the JSON output option and then:
92
+
93
+ brakeman --compare old_report.json
94
+
95
+ This will output JSON with two lists: one of fixed warnings and one of new warnings.
96
+
97
+ Brakeman will ignore warnings if configured to do so. By default, it looks for a configuration file in `config/brakeman.ignore`.
98
+ To create and manage this file, use:
99
+
100
+ brakeman -I
101
+
102
+ # Warning information
103
+
104
+ See [WARNING\_TYPES](WARNING_TYPES) for more information on the warnings reported by this tool.
105
+
106
+ # Warning context
107
+
108
+ The HTML output format provides an excerpt from the original application source where a warning was triggered. Due to the processing done while looking for vulnerabilities, the source may not resemble the reported warning and reported line numbers may be slightly off. However, the context still provides a quick look into the code which raised the warning.
109
+
110
+ # Confidence levels
111
+
112
+ Brakeman assigns a confidence level to each warning. This provides a rough estimate of how certain the tool is that a given warning is actually a problem. Naturally, these ratings should not be taken as absolute truth.
113
+
114
+ There are three levels of confidence:
115
+
116
+ + High - Either this is a simple warning (boolean value) or user input is very likely being used in unsafe ways.
117
+ + Medium - This generally indicates an unsafe use of a variable, but the variable may or may not be user input.
118
+ + Weak - Typically means user input was indirectly used in a potentially unsafe manner.
119
+
120
+ To only get warnings above a given confidence level:
121
+
122
+ brakeman -w3
123
+
124
+ The `-w` switch takes a number from 1 to 3, with 1 being low (all warnings) and 3 being high (only highest confidence warnings).
125
+
126
+ # Configuration files
127
+
128
+ Brakeman options can stored and read from YAML files. To simplify the process of writing a configuration file, the `-C` option will output the currently set options.
129
+
130
+ Options passed in on the commandline have priority over configuration files.
131
+
132
+ The default config locations are `./config/brakeman.yml`, `~/.brakeman/config.yml`, and `/etc/brakeman/config.yml`
133
+
134
+ The `-c` option can be used to specify a configuration file to use.
135
+
136
+ # Continuous Integration
137
+
138
+ There is a [plugin available](http://brakemanscanner.org/docs/jenkins/) for Jenkins/Hudson.
139
+
140
+ For even more continuous testing, try the [Guard plugin](https://github.com/guard/guard-brakeman).
141
+
142
+ # Building
143
+
144
+ git clone git://github.com/presidentbeef/brakeman.git
145
+ cd brakeman
146
+ gem build brakeman.gemspec
147
+ gem install brakeman*.gem
148
+
149
+ # Who is Using Brakeman?
150
+
151
+ * [Code Climate](https://codeclimate.com/)
152
+ * [GitHub](https://github.com/)
153
+ * [Groupon](http://www.groupon.com/)
154
+ * [New Relic](http://newrelic.com)
155
+ * [Twitter](https://twitter.com/)
156
+
157
+ [..and more!](http://brakemanscanner.org/brakeman_users)
158
+
159
+ # Homepage/News
160
+
161
+ Website: http://brakemanscanner.org/
162
+
163
+ Twitter: https://twitter.com/brakeman
164
+
165
+ Chat: https://gitter.im/presidentbeef/brakeman
166
+
167
+ # License
168
+
169
+ see [MIT-LICENSE](MIT-LICENSE)
@@ -0,0 +1,95 @@
1
+ This file describes the various warning types reported by this tool.
2
+
3
+ # Attribute Restriction
4
+
5
+ This warning comes up if a model does not limit what attributes can be set through mass assignment.
6
+
7
+ In particular, this check looks for `attr_accessible` inside model definitions. If it is not found, this warning will be issued.
8
+
9
+ Note that disabling mass assignment globally will suppress these warnings.
10
+
11
+ # Authentication
12
+
13
+ # Basic Auth
14
+
15
+ # Command Injection
16
+
17
+ Request parameters or string interpolation has been detected in a `system` call. This can lead to someone executing arbitrary commands. Use the safe form of `system` instead, which will pass in arguments safely.
18
+
19
+ See http://guides.rubyonrails.org/security.html#command-line-injection for details.
20
+
21
+ # Cross Site Scripting
22
+
23
+ Cross site scripting warnings are raised when a parameter or model attribute is output through a view without being escaped.
24
+
25
+ See http://guides.rubyonrails.org/security.html#cross-site-scripting-xss for details.
26
+
27
+ # Cross-Site Request Forgery
28
+
29
+ No call to `protect_from_forgery` was found in `ApplicationController`. This method prevents CSRF.
30
+
31
+ See http://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf for details.
32
+
33
+ # Dangerous Eval
34
+
35
+ # Dangerous Send
36
+
37
+ # Default Routes
38
+
39
+ The general default routes warning means there is a call to `map.connect ":controller/:action/:id"` in config/routes.rb. This allows any public method on any controller to be called as an action.
40
+
41
+ If this warning is reported for a particular controller, it means there is a route to that controller containing `:action`.
42
+
43
+ Default routes can be dangerous if methods are made public which are not intended to be used as URLs or actions.
44
+
45
+ # Denial of Service
46
+
47
+ # Dynamic Render Path
48
+
49
+ When a call to `render` uses a dynamically generated path, template name, file name, or action, there is the possibility that a user can access templates that should be restricted. The issue may be worse if those templates execute code or modify the database.
50
+
51
+ This warning is shown whenever the path to be rendered is not a static string or symbol.
52
+
53
+ # File Access
54
+
55
+ # Format Validation
56
+
57
+ Calls to `validates_format_of ..., :with => //` which do not use `\A` and `\z` as anchors will cause this warning. Using `^` and `$` is not sufficient, as `$` will only match up to a new line. This allows an attacker to put whatever malicious input they would like after a new line character.
58
+
59
+ See http://guides.rubyonrails.org/security.html#regular-expressions for details.
60
+
61
+ # Information Disclosure
62
+
63
+ # Mail Link
64
+
65
+ # Mass Assignment
66
+
67
+ Mass assignment is a method for initializing models. If the attributes which are set is not restricted, someone may set the attributes to any value they wish.
68
+
69
+ Mass assignment can be disabled globally.
70
+
71
+ Please see http://railspikes.com/2008/9/22/is-your-rails-application-safe-from-mass-assignment for more details.
72
+
73
+ # Nested Attributes
74
+
75
+ # Redirect
76
+
77
+ Redirects which rely on user-supplied values can be used to "spoof" websites or hide malicious links in otherwise harmless-looking URLs. They can also allow access to restricted areas of a site if the destination is not validated.
78
+
79
+ This warning is shown when request parameters are used inside a call to `redirect_to`.
80
+
81
+ See http://www.owasp.org/index.php/Top_10_2010-A10 for more information.
82
+
83
+ # Remote Code Execution
84
+
85
+ # Response Splitting
86
+
87
+ # Session Setting
88
+
89
+ # SQL Injection
90
+
91
+ String interpolation or concatenation has been detected in an SQL query. Use parameterized queries instead.
92
+
93
+ See http://guides.rubyonrails.org/security.html#sql-injection for details.
94
+
95
+ # SSL Verification Bypass
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env ruby
2
+ #Adjust path in case called directly and not through gem
3
+ $:.unshift "#{File.expand_path(File.dirname(__FILE__))}/../lib"
4
+
5
+ require 'brakeman'
6
+ require 'brakeman/options'
7
+ require 'brakeman/version'
8
+
9
+ #Parse options
10
+ begin
11
+ options, parser = Brakeman::Options.parse! ARGV
12
+ rescue OptionParser::ParseError => e
13
+ $stderr.puts e.message.capitalize
14
+ $stderr.puts "Please see `brakeman --help` for valid options"
15
+ exit(-1)
16
+ end
17
+
18
+ #Exit early for these options
19
+ if options[:list_checks] or options[:list_optional_checks]
20
+ Brakeman.list_checks options
21
+ exit
22
+ elsif options[:create_config]
23
+ Brakeman.dump_config options
24
+ exit
25
+ elsif options[:show_help]
26
+ puts parser
27
+ exit
28
+ elsif options[:show_version]
29
+ puts "brakeman #{Brakeman::Version}"
30
+ exit
31
+ elsif options[:install_rake_task]
32
+ Brakeman.install_rake_task
33
+ exit
34
+ end
35
+
36
+ #Set application path according to the commandline arguments
37
+ unless options[:app_path]
38
+ if ARGV[-1].nil?
39
+ options[:app_path] = "."
40
+ else
41
+ options[:app_path] = ARGV[-1]
42
+ end
43
+ end
44
+
45
+ trap("INT") do
46
+ $stderr.puts "\nInterrupted - exiting."
47
+
48
+ if options[:debug]
49
+ $stderr.puts caller
50
+ end
51
+
52
+ exit!
53
+ end
54
+
55
+ if options[:quiet].nil?
56
+ options[:quiet] = :command_line
57
+ end
58
+
59
+ begin
60
+ if options[:previous_results_json]
61
+ require 'json'
62
+ vulns = Brakeman.compare options.merge(:quiet => options[:quiet])
63
+
64
+ if options[:comparison_output_file]
65
+ File.open options[:comparison_output_file], "w" do |f|
66
+ f.puts JSON.pretty_generate(vulns)
67
+ end
68
+
69
+ Brakeman.notify "Comparison saved in '#{options[:comparison_output_file]}'"
70
+ else
71
+ puts JSON.pretty_generate(vulns)
72
+ end
73
+
74
+ if options[:exit_on_warn] && vulns[:new].count > 0
75
+ exit Brakeman::Warnings_Found_Exit_Code
76
+ end
77
+ else
78
+ #Run scan and output a report
79
+ tracker = Brakeman.run options.merge(:print_report => true, :quiet => options[:quiet])
80
+
81
+ #Return error code if --exit-on-warn is used and warnings were found
82
+ if tracker.options[:exit_on_warn] and not tracker.filtered_warnings.empty?
83
+ exit Brakeman::Warnings_Found_Exit_Code
84
+ end
85
+ end
86
+ rescue Brakeman::NoApplication => e
87
+ $stderr.puts e.message
88
+ exit Brakeman::No_App_Found_Exit_Code
89
+ end
@@ -0,0 +1,495 @@
1
+ require 'set'
2
+
3
+ module Brakeman
4
+
5
+ #This exit code is used when warnings are found and the --exit-on-warn
6
+ #option is set
7
+ Warnings_Found_Exit_Code = 3
8
+
9
+ #Exit code returned when no Rails application is detected
10
+ No_App_Found_Exit_Code = 4
11
+
12
+ @debug = false
13
+ @quiet = false
14
+ @loaded_dependencies = []
15
+ @vendored_paths = false
16
+
17
+ #Run Brakeman scan. Returns Tracker object.
18
+ #
19
+ #Options:
20
+ #
21
+ # * :app_path - path to root of Rails app (required)
22
+ # * :additional_checks_path - array of additional directories containing additional out-of-tree checks to run
23
+ # * :additional_libs_path - array of additional application relative lib directories (ex. app/mailers) to process
24
+ # * :assume_all_routes - assume all methods are routes (default: true)
25
+ # * :check_arguments - check arguments of methods (default: true)
26
+ # * :collapse_mass_assignment - report unprotected models in single warning (default: false)
27
+ # * :combine_locations - combine warning locations (default: true)
28
+ # * :config_file - configuration file
29
+ # * :escape_html - escape HTML by default (automatic)
30
+ # * :exit_on_warn - return false if warnings found, true otherwise. Not recommended for library use (default: false)
31
+ # * :github_repo - github repo to use for file links (user/repo[/path][@ref])
32
+ # * :highlight_user_input - highlight user input in reported warnings (default: true)
33
+ # * :html_style - path to CSS file
34
+ # * :ignore_model_output - consider models safe (default: false)
35
+ # * :index_libs - add libraries to call index (default: true)
36
+ # * :interprocedural - limited interprocedural processing of method calls (default: false)
37
+ # * :message_limit - limit length of messages
38
+ # * :min_confidence - minimum confidence (0-2, 0 is highest)
39
+ # * :output_files - files for output
40
+ # * :output_formats - formats for output (:to_s, :to_tabs, :to_csv, :to_html)
41
+ # * :parallel_checks - run checks in parallel (default: true)
42
+ # * :print_report - if no output file specified, print to stdout (default: false)
43
+ # * :quiet - suppress most messages (default: true)
44
+ # * :rails3 - force Rails 3 mode (automatic)
45
+ # * :report_routes - show found routes on controllers (default: false)
46
+ # * :run_checks - array of checks to run (run all if not specified)
47
+ # * :safe_methods - array of methods to consider safe
48
+ # * :skip_libs - do not process lib/ directory (default: false)
49
+ # * :skip_checks - checks not to run (run all if not specified)
50
+ # * :absolute_paths - show absolute path of each file (default: false)
51
+ # * :summary_only - only output summary section of report
52
+ # (does not apply to tabs format)
53
+ #
54
+ #Alternatively, just supply a path as a string.
55
+ def self.run options
56
+ options = set_options options
57
+
58
+ @quiet = !!options[:quiet]
59
+ @debug = !!options[:debug]
60
+
61
+ if @quiet
62
+ options[:report_progress] = false
63
+ end
64
+ scan options
65
+ end
66
+
67
+ #Sets up options for run, checks given application path
68
+ def self.set_options options
69
+ if options.is_a? String
70
+ options = { :app_path => options }
71
+ end
72
+
73
+ if options[:quiet] == :command_line
74
+ command_line = true
75
+ options.delete :quiet
76
+ end
77
+
78
+ options = default_options.merge(load_options(options)).merge(options)
79
+
80
+ if options[:quiet].nil? and not command_line
81
+ options[:quiet] = true
82
+ end
83
+
84
+ options[:output_formats] = get_output_formats options
85
+ options[:github_url] = get_github_url options
86
+
87
+ options
88
+ end
89
+
90
+ #Load options from YAML file
91
+ def self.load_options line_options
92
+ custom_location = line_options[:config_file]
93
+ quiet = line_options[:quiet]
94
+ app_path = line_options[:app_path]
95
+
96
+ #Load configuration file
97
+ if config = config_file(custom_location, app_path)
98
+ require 'date' # https://github.com/dtao/safe_yaml/issues/80
99
+ self.load_brakeman_dependency 'safe_yaml/load'
100
+ options = SafeYAML.load_file config, :deserialize_symbols => true
101
+
102
+ if options
103
+ options.each { |k, v| options[k] = Set.new v if v.is_a? Array }
104
+
105
+ # After parsing the yaml config file for options, convert any string keys into symbols.
106
+ options.keys.select {|k| k.is_a? String}.map {|k| k.to_sym }.each {|k| options[k] = options[k.to_s]; options.delete(k.to_s) }
107
+
108
+ # notify if options[:quiet] and quiet is nil||false
109
+ notify "[Notice] Using configuration in #{config}" unless (options[:quiet] || quiet)
110
+ options
111
+ else
112
+ notify "[Notice] Empty configuration file: #{config}" unless quiet
113
+ {}
114
+ end
115
+ else
116
+ {}
117
+ end
118
+ end
119
+
120
+ CONFIG_FILES = [
121
+ File.expand_path("~/.brakeman/config.yml"),
122
+ File.expand_path("/etc/brakeman/config.yml")
123
+ ]
124
+
125
+ def self.config_file custom_location, app_path
126
+ app_config = File.expand_path(File.join(app_path, "config", "brakeman.yml"))
127
+ supported_locations = [File.expand_path(custom_location || ""), app_config] + CONFIG_FILES
128
+ supported_locations.detect {|f| File.file?(f) }
129
+ end
130
+
131
+ #Default set of options
132
+ def self.default_options
133
+ { :assume_all_routes => true,
134
+ :skip_checks => Set.new,
135
+ :check_arguments => true,
136
+ :safe_methods => Set.new,
137
+ :min_confidence => 2,
138
+ :combine_locations => true,
139
+ :collapse_mass_assignment => false,
140
+ :highlight_user_input => true,
141
+ :ignore_redirect_to_model => true,
142
+ :ignore_model_output => false,
143
+ :index_libs => true,
144
+ :message_limit => 100,
145
+ :parallel_checks => true,
146
+ :relative_path => false,
147
+ :report_progress => true,
148
+ :html_style => "#{File.expand_path(File.dirname(__FILE__))}/brakeman/format/style.css"
149
+ }
150
+ end
151
+
152
+ #Determine output formats based on options[:output_formats]
153
+ #or options[:output_files]
154
+ def self.get_output_formats options
155
+ #Set output format
156
+ if options[:output_format] && options[:output_files] && options[:output_files].size > 1
157
+ raise ArgumentError, "Cannot specify output format if multiple output files specified"
158
+ end
159
+ if options[:output_format]
160
+ get_formats_from_output_format options[:output_format]
161
+ elsif options[:output_files]
162
+ get_formats_from_output_files options[:output_files]
163
+ else
164
+ begin
165
+ self.load_brakeman_dependency 'terminal-table', :allow_fail
166
+ return [:to_s]
167
+ rescue LoadError
168
+ return [:to_json]
169
+ end
170
+ end
171
+ end
172
+
173
+ def self.get_formats_from_output_format output_format
174
+ case output_format
175
+ when :html, :to_html
176
+ [:to_html]
177
+ when :csv, :to_csv
178
+ [:to_csv]
179
+ when :pdf, :to_pdf
180
+ [:to_pdf]
181
+ when :tabs, :to_tabs
182
+ [:to_tabs]
183
+ when :json, :to_json
184
+ [:to_json]
185
+ when :markdown, :to_markdown
186
+ [:to_markdown]
187
+ when :cc, :to_cc, :codeclimate, :to_codeclimate
188
+ [:to_codeclimate]
189
+ else
190
+ [:to_s]
191
+ end
192
+ end
193
+ private_class_method :get_formats_from_output_format
194
+
195
+ def self.get_formats_from_output_files output_files
196
+ output_files.map do |output_file|
197
+ case output_file
198
+ when /\.html$/i
199
+ :to_html
200
+ when /\.csv$/i
201
+ :to_csv
202
+ when /\.pdf$/i
203
+ :to_pdf
204
+ when /\.tabs$/i
205
+ :to_tabs
206
+ when /\.json$/i
207
+ :to_json
208
+ when /\.md$/i
209
+ :to_markdown
210
+ when /(\.cc|\.codeclimate)$/i
211
+ :to_codeclimate
212
+ else
213
+ :to_s
214
+ end
215
+ end
216
+ end
217
+ private_class_method :get_formats_from_output_files
218
+
219
+ def self.get_github_url options
220
+ if github_repo = options[:github_repo]
221
+ full_repo, ref = github_repo.split '@', 2
222
+ name, repo, path = full_repo.split '/', 3
223
+ unless name && repo && !(name.empty? || repo.empty?)
224
+ raise ArgumentError, "Invalid GitHub repository format"
225
+ end
226
+ path.chomp '/' if path
227
+ ref ||= 'master'
228
+ ['https://github.com', name, repo, 'blob', ref, path].compact.join '/'
229
+ else
230
+ nil
231
+ end
232
+ end
233
+ private_class_method :get_github_url
234
+
235
+ #Output list of checks (for `-k` option)
236
+ def self.list_checks options
237
+ require 'brakeman/scanner'
238
+
239
+ add_external_checks options
240
+
241
+ if options[:list_optional_checks]
242
+ $stderr.puts "Optional Checks:"
243
+ checks = Checks.optional_checks
244
+ else
245
+ $stderr.puts "Available Checks:"
246
+ checks = Checks.checks
247
+ end
248
+
249
+ format_length = 30
250
+
251
+ $stderr.puts "-" * format_length
252
+ checks.each do |check|
253
+ $stderr.printf("%-#{format_length}s%s\n", check.name, check.description)
254
+ end
255
+ end
256
+
257
+ #Installs Rake task for running Brakeman,
258
+ #which basically means copying `lib/brakeman/brakeman.rake` to
259
+ #`lib/tasks/brakeman.rake` in the current Rails application.
260
+ def self.install_rake_task install_path = nil
261
+ if install_path
262
+ rake_path = File.join(install_path, "Rakefile")
263
+ task_path = File.join(install_path, "lib", "tasks", "brakeman.rake")
264
+ else
265
+ rake_path = "Rakefile"
266
+ task_path = File.join("lib", "tasks", "brakeman.rake")
267
+ end
268
+
269
+ if not File.exist? rake_path
270
+ raise RakeInstallError, "No Rakefile detected"
271
+ elsif File.exist? task_path
272
+ raise RakeInstallError, "Task already exists"
273
+ end
274
+
275
+ require 'fileutils'
276
+
277
+ if not File.exist? "lib/tasks"
278
+ notify "Creating lib/tasks"
279
+ FileUtils.mkdir_p "lib/tasks"
280
+ end
281
+
282
+ path = File.expand_path(File.dirname(__FILE__))
283
+
284
+ FileUtils.cp "#{path}/brakeman/brakeman.rake", task_path
285
+
286
+ if File.exist? task_path
287
+ notify "Task created in #{task_path}"
288
+ notify "Usage: rake brakeman:run[output_file]"
289
+ else
290
+ raise RakeInstallError, "Could not create task"
291
+ end
292
+ end
293
+
294
+ #Output configuration to YAML
295
+ def self.dump_config options
296
+ require 'yaml'
297
+ if options[:create_config].is_a? String
298
+ file = options[:create_config]
299
+ else
300
+ file = nil
301
+ end
302
+
303
+ options.delete :create_config
304
+
305
+ options.each do |k,v|
306
+ if v.is_a? Set
307
+ options[k] = v.to_a
308
+ end
309
+ end
310
+
311
+ if file
312
+ File.open file, "w" do |f|
313
+ YAML.dump options, f
314
+ end
315
+ notify "Output configuration to #{file}"
316
+ else
317
+ notify YAML.dump(options)
318
+ end
319
+ end
320
+
321
+ #Run a scan. Generally called from Brakeman.run instead of directly.
322
+ def self.scan options
323
+ #Load scanner
324
+ notify "Loading scanner..."
325
+
326
+ begin
327
+ require 'brakeman/scanner'
328
+ rescue LoadError
329
+ raise NoBrakemanError, "Cannot find lib/ directory."
330
+ end
331
+
332
+ add_external_checks options
333
+
334
+ #Start scanning
335
+ scanner = Scanner.new options
336
+ tracker = scanner.tracker
337
+
338
+ notify "Processing application in #{tracker.app_path}"
339
+ scanner.process
340
+
341
+ if options[:parallel_checks]
342
+ notify "Running checks in parallel..."
343
+ else
344
+ notify "Runnning checks..."
345
+ end
346
+
347
+ tracker.run_checks
348
+
349
+ self.filter_warnings tracker, options
350
+
351
+ if options[:output_files]
352
+ notify "Generating report..."
353
+
354
+ write_report_to_files tracker, options[:output_files]
355
+ elsif options[:print_report]
356
+ notify "Generating report..."
357
+
358
+ write_report_to_formats tracker, options[:output_formats]
359
+ end
360
+
361
+ tracker
362
+ end
363
+
364
+ def self.write_report_to_files tracker, output_files
365
+ output_files.each_with_index do |output_file, idx|
366
+ File.open output_file, "w" do |f|
367
+ f.write tracker.report.format(tracker.options[:output_formats][idx])
368
+ end
369
+ notify "Report saved in '#{output_file}'"
370
+ end
371
+ end
372
+ private_class_method :write_report_to_files
373
+
374
+ def self.write_report_to_formats tracker, output_formats
375
+ output_formats.each do |output_format|
376
+ puts tracker.report.format(output_format)
377
+ end
378
+ end
379
+ private_class_method :write_report_to_formats
380
+
381
+ #Rescan a subset of files in a Rails application.
382
+ #
383
+ #A full scan must have been run already to use this method.
384
+ #The returned Tracker object from Brakeman.run is used as a starting point
385
+ #for the rescan.
386
+ #
387
+ #Options may be given as a hash with the same values as Brakeman.run.
388
+ #Note that these options will be merged into the Tracker.
389
+ #
390
+ #This method returns a RescanReport object with information about the scan.
391
+ #However, the Tracker object will also be modified as the scan is run.
392
+ def self.rescan tracker, files, options = {}
393
+ require 'brakeman/rescanner'
394
+
395
+ tracker.options.merge! options
396
+
397
+ @quiet = !!tracker.options[:quiet]
398
+ @debug = !!tracker.options[:debug]
399
+
400
+ Rescanner.new(tracker.options, tracker.processor, files).recheck
401
+ end
402
+
403
+ def self.notify message
404
+ $stderr.puts message unless @quiet
405
+ end
406
+
407
+ def self.debug message
408
+ $stderr.puts message if @debug
409
+ end
410
+
411
+ # Compare JSON ouptut from a previous scan and return the diff of the two scans
412
+ def self.compare options
413
+ require 'json'
414
+ require 'brakeman/differ'
415
+ raise ArgumentError.new("Comparison file doesn't exist") unless File.exist? options[:previous_results_json]
416
+
417
+ begin
418
+ previous_results = JSON.parse(File.read(options[:previous_results_json]), :symbolize_names => true)[:warnings]
419
+ rescue JSON::ParserError
420
+ self.notify "Error parsing comparison file: #{options[:previous_results_json]}"
421
+ exit!
422
+ end
423
+
424
+ tracker = run(options)
425
+
426
+ new_results = JSON.parse(tracker.report.to_json, :symbolize_names => true)[:warnings]
427
+
428
+ Brakeman::Differ.new(new_results, previous_results).diff
429
+ end
430
+
431
+ def self.load_brakeman_dependency name, allow_fail = false
432
+ return if @loaded_dependencies.include? name
433
+
434
+ unless @vendored_paths
435
+ path_load = "#{File.expand_path(File.dirname(__FILE__))}/../bundle/load.rb"
436
+
437
+ if File.exist? path_load
438
+ require path_load
439
+ end
440
+
441
+ @vendored_paths = true
442
+ end
443
+
444
+ begin
445
+ require name
446
+ rescue LoadError => e
447
+ if allow_fail
448
+ raise e
449
+ else
450
+ $stderr.puts e.message
451
+ $stderr.puts "Please install the appropriate dependency: #{name}."
452
+ exit!(-1)
453
+ end
454
+ end
455
+ end
456
+
457
+ def self.filter_warnings tracker, options
458
+ require 'brakeman/report/ignore/config'
459
+
460
+ app_tree = Brakeman::AppTree.from_options(options)
461
+
462
+ if options[:ignore_file]
463
+ file = options[:ignore_file]
464
+ elsif app_tree.exists? "config/brakeman.ignore"
465
+ file = app_tree.expand_path("config/brakeman.ignore")
466
+ elsif not options[:interactive_ignore]
467
+ return
468
+ end
469
+
470
+ notify "Filtering warnings..."
471
+
472
+ if options[:interactive_ignore]
473
+ require 'brakeman/report/ignore/interactive'
474
+ config = InteractiveIgnorer.new(file, tracker.warnings).start
475
+ else
476
+ notify "[Notice] Using '#{file}' to filter warnings"
477
+ config = IgnoreConfig.new(file, tracker.warnings)
478
+ config.read_from_file
479
+ config.filter_ignored
480
+ end
481
+
482
+ tracker.ignored_filter = config
483
+ end
484
+
485
+ def self.add_external_checks options
486
+ options[:additional_checks_path].each do |path|
487
+ Brakeman::Checks.initialize_checks path
488
+ end if options[:additional_checks_path]
489
+ end
490
+
491
+ class DependencyError < RuntimeError; end
492
+ class RakeInstallError < RuntimeError; end
493
+ class NoBrakemanError < RuntimeError; end
494
+ class NoApplication < RuntimeError; end
495
+ end