Module: Brakeman

Defined in:
lib/brakeman.rb,
lib/brakeman/version.rb,
lib/brakeman/processor.rb

Defined Under Namespace

Modules: Options, ProcessorHelper, RenderHelper, RouteHelper, Util Classes: AliasProcessor, BaseCheck, BaseProcessor, CallIndex, CheckBasicAuth, CheckContentTag, CheckCrossSiteScripting, CheckDefaultRoutes, CheckDigestDoS, CheckEscapeFunction, CheckEvaluation, CheckExecute, CheckFileAccess, CheckFilterSkipping, CheckForgerySetting, CheckLinkTo, CheckLinkToHref, CheckMailTo, CheckMassAssignment, CheckModelAttributes, CheckNestedAttributes, CheckQuoteTableName, CheckRedirect, CheckRender, CheckResponseSplitting, CheckSQL, CheckSafeBufferManipulation, CheckSelectTag, CheckSelectVulnerability, CheckSend, CheckSendFile, CheckSessionSettings, CheckSingleQuotes, CheckSkipBeforeFilter, CheckStripTags, CheckTranslateBug, CheckValidationRegex, CheckWithoutProtection, Checks, ConfigAliasProcessor, ConfigProcessor, ControllerAliasProcessor, ControllerProcessor, Differ, ErbTemplateProcessor, ErubisTemplateProcessor, FindAllCalls, FindCall, GemProcessor, HamlTemplateProcessor, LibraryProcessor, ModelProcessor, OutputProcessor, Processor, Rails2ConfigProcessor, Rails2RoutesProcessor, Rails2XSSPluginErubis, Rails3ConfigProcessor, Rails3Erubis, Rails3RoutesProcessor, Report, RescanReport, Rescanner, RouteAliasProcessor, RoutesProcessor, Scanner, ScannerErubis, SexpProcessor, TemplateAliasProcessor, TemplateProcessor, Tracker, Warning

Constant Summary collapse

Warnings_Found_Exit_Code =

This exit code is used when warnings are found and the –exit-on-warn option is set

3
Version =
"1.8.3"
RAILS_CONFIG =

Replace block variable in

Rails::Initializer.run |config|

with this value so we can keep track of it.

Sexp.new(:call, nil, :config, Sexp.new(:arglist))

Class Method Summary collapse

Class Method Details

.compare(options) ⇒ Object

Compare JSON ouptut from a previous scan and return the diff of the two scans

Raises:

  • (ArgumentError)


318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/brakeman.rb', line 318

def self.compare options
  require 'multi_json'
  require 'brakeman/differ'
  raise ArgumentError.new("Comparison file doesn't exist") unless File.exists? options[:previous_results_json]

  begin
    previous_results = MultiJson.load(File.read(options[:previous_results_json]), :symbolize_keys => true)[:warnings]
  rescue MultiJson::DecodeError
    self.notify "Error parsing comparison file: #{options[:previous_results_json]}"
    exit!
  end

  tracker = run(options)

  new_results = MultiJson.load(tracker.report.to_json, :symbolize_keys => true)[:warnings]

  Brakeman::Differ.new(new_results, previous_results).diff
end

.debug(message) ⇒ Object



313
314
315
# File 'lib/brakeman.rb', line 313

def self.debug message
  $stderr.puts message if @debug
end

.dump_config(options) ⇒ Object

Output configuration to YAML



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/brakeman.rb', line 215

def self.dump_config options
  if options[:create_config].is_a? String
    file = options[:create_config]
  else
    file = nil
  end

  options.delete :create_config

  options.each do |k,v|
    if v.is_a? Set
      options[k] = v.to_a
    end
  end

  if file
    File.open file, "w" do |f|
      YAML.dump options, f
    end
    puts "Output configuration to #{file}"
  else
    puts YAML.dump(options)
  end
  exit
end

.get_defaultsObject

Default set of options



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/brakeman.rb', line 111

def self.get_defaults
  { :skip_checks => Set.new, 
    :check_arguments => true, 
    :safe_methods => Set.new,
    :min_confidence => 2,
    :combine_locations => true,
    :collapse_mass_assignment => true,
    :highlight_user_input => true,
    :ignore_redirect_to_model => true,
    :ignore_model_output => false,
    :message_limit => 100,
    :parallel_checks => true,
    :relative_path => false,
    :quiet => true,
    :report_progress => true,
    :html_style => "#{File.expand_path(File.dirname(__FILE__))}/brakeman/format/style.css" 
  }
end

.get_output_formats(options) ⇒ Object

Determine output formats based on options or options



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/brakeman.rb', line 132

def self.get_output_formats options
  #Set output format
  if options[:output_format] && options[:output_files] && options[:output_files].size > 1
    raise ArgumentError, "Cannot specify output format if multiple output files specified"
  end
  if options[:output_format]
    [
      case options[:output_format]
      when :html, :to_html
        :to_html
      when :csv, :to_csv
        :to_csv
      when :pdf, :to_pdf
        :to_pdf
      when :tabs, :to_tabs
        :to_tabs
      when :json, :to_json
        :to_json
      else
        :to_s
      end
    ]
  else
    return [:to_s] unless options[:output_files]
    options[:output_files].map do |output_file|
      case output_file
      when /\.html$/i
        :to_html
      when /\.csv$/i
        :to_csv
      when /\.pdf$/i
        :to_pdf
      when /\.tabs$/i
        :to_tabs
      when /\.json$/i
        :to_json
      else
        :to_s
      end
    end
  end
end

.install_rake_taskObject

Installs Rake task for running Brakeman, which basically means copying ‘lib/brakeman/brakeman.rake` to `lib/tasks/brakeman.rake` in the current Rails application.



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/brakeman.rb', line 188

def self.install_rake_task
  if not File.exists? "Rakefile"
    abort "No Rakefile detected"
  elsif File.exists? "lib/tasks/brakeman.rake"
    abort "Task already exists"
  end

  require 'fileutils'

  if not File.exists? "lib/tasks"
    notify "Creating lib/tasks"
    FileUtils.mkdir_p "lib/tasks"
  end

  path = File.expand_path(File.dirname(__FILE__))

  FileUtils.cp "#{path}/brakeman/brakeman.rake", "lib/tasks/brakeman.rake"

  if File.exists? "lib/tasks/brakeman.rake"
    notify "Task created in lib/tasks/brakeman.rake"
    notify "Usage: rake brakeman:run[output_file]"
  else
    notify "Could not create task"
  end
end

.list_checksObject

Output list of checks (for ‘-k` option)



176
177
178
179
180
181
182
183
# File 'lib/brakeman.rb', line 176

def self.list_checks
  require 'brakeman/scanner'
  $stderr.puts "Available Checks:"
  $stderr.puts "-" * 30
  $stderr.puts Checks.checks.map { |c|
    c.to_s.match(/^Brakeman::(.*)$/)[1].ljust(27) << c.description
  }.sort.join "\n"
end

.load_options(config_file) ⇒ Object

Load options from YAML file



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/brakeman.rb', line 84

def self.load_options config_file
  config_file ||= ""

  #Load configuration file
  [File.expand_path(config_file),
    File.expand_path("./config.yaml"),
    File.expand_path("~/.brakeman/config.yaml"),
    File.expand_path("/etc/brakeman/config.yaml"),
    "#{File.expand_path(File.dirname(__FILE__))}/../lib/config.yaml"].each do |f|

    if File.exist? f and not File.directory? f
      notify "[Notice] Using configuration in #{f}"
      options = YAML.load_file f
      options.each do |k,v|
        if v.is_a? Array
          options[k] = Set.new v
        end
      end

      return options
    end
    end

  return {}
end

.notify(message) ⇒ Object



309
310
311
# File 'lib/brakeman.rb', line 309

def self.notify message
  $stderr.puts message unless @quiet
end

.rescan(tracker, files, options = {}) ⇒ Object

Rescan a subset of files in a Rails application.

A full scan must have been run already to use this method. The returned Tracker object from Brakeman.run is used as a starting point for the rescan.

Options may be given as a hash with the same values as Brakeman.run. Note that these options will be merged into the Tracker.

This method returns a RescanReport object with information about the scan. However, the Tracker object will also be modified as the scan is run.



298
299
300
301
302
303
304
305
306
307
# File 'lib/brakeman.rb', line 298

def self.rescan tracker, files, options = {}
  require 'brakeman/rescanner'

  tracker.options.merge! options

  @quiet = !!tracker.options[:quiet]
  @debug = !!tracker.options[:debug]

  Rescanner.new(tracker.options, tracker.processor, files).recheck
end

.run(options) ⇒ Object

Run Brakeman scan. Returns Tracker object.

Options:

* :app_path - path to root of Rails app (required)
* :assume_all_routes - assume all methods are routes (default: false)
* :check_arguments - check arguments of methods (default: true)
* :collapse_mass_assignment - report unprotected models in single warning (default: true)
* :combine_locations - combine warning locations (default: true)
* :config_file - configuration file
* :escape_html - escape HTML by default (automatic)
* :exit_on_warn - return false if warnings found, true otherwise. Not recommended for library use (default: false)
* :highlight_user_input - highlight user input in reported warnings (default: true)
* :html_style - path to CSS file
* :ignore_model_output - consider models safe (default: false)
* :message_limit - limit length of messages
* :min_confidence - minimum confidence (0-2, 0 is highest)
* :output_files - files for output
* :output_formats - formats for output (:to_s, :to_tabs, :to_csv, :to_html)
* :parallel_checks - run checks in parallel (default: true)
* :print_report - if no output file specified, print to stdout (default: false)
* :quiet - suppress most messages (default: true)
* :rails3 - force Rails 3 mode (automatic)
* :report_routes - show found routes on controllers (default: false)
* :run_checks - array of checks to run (run all if not specified)
* :safe_methods - array of methods to consider safe
* :skip_libs - do not process lib/ directory (default: false)
* :skip_checks - checks not to run (run all if not specified)
* :relative_path - show relative path of each file(default: false)
* :summary_only - only output summary section of report 
                  (does not apply to tabs format)

Alternatively, just supply a path as a string.



47
48
49
50
51
52
53
54
55
56
57
# File 'lib/brakeman.rb', line 47

def self.run options
  options = set_options options

  @quiet = !!options[:quiet]
  @debug = !!options[:debug]

  if @quiet
    options[:report_progress] = false
  end
  scan options
end

.scan(options) ⇒ Object

Run a scan. Generally called from Brakeman.run instead of directly.



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/brakeman.rb', line 242

def self.scan options
  #Load scanner
  notify "Loading scanner..."

  begin
    require 'brakeman/scanner'
  rescue LoadError
    abort "Cannot find lib/ directory."
  end

  #Start scanning
  scanner = Scanner.new options

  notify "[Notice] Using Ruby #{RUBY_VERSION}. Please make sure this matches the one used to run your Rails application."

  notify "Processing application in #{options[:app_path]}"
  tracker = scanner.process

  if options[:parallel_checks]
    notify "Running checks in parallel..."
  else
    notify "Runnning checks..."
  end
  tracker.run_checks

  if options[:output_files]
    notify "Generating report..."

    options[:output_files].each_with_index do |output_file, idx|
      File.open output_file, "w" do |f|
        f.write tracker.report.send(options[:output_formats][idx])
      end
      notify "Report saved in '#{output_file}'"
    end
  elsif options[:print_report]
    notify "Generating report..."

    options[:output_formats].each do |output_format|
      puts tracker.report.send(output_format)
    end
  end

  tracker
end

.set_options(options) ⇒ Object

Sets up options for run, checks given application path



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/brakeman.rb', line 60

def self.set_options options
  if options.is_a? String
    options = { :app_path => options }
  end

  options[:app_path] = File.expand_path(options[:app_path])

  options = load_options(options[:config_file]).merge! options
  options = get_defaults.merge! options
  options[:output_formats] = get_output_formats options

  app_path = options[:app_path]

  abort("Please supply the path to a Rails application.") unless app_path and File.exist? app_path + "/app"

  if File.exist? app_path + "/script/rails"
    options[:rails3] = true
    notify "[Notice] Detected Rails 3 application" unless options[:quiet]
  end

  options
end