提交 f3afc9d5 编写于 作者: J Justin Collins

Merge branch 'master' into update_ruby_parser

Gemfile.lock
...@@ -145,32 +145,8 @@ Brakeman options can stored and read from YAML files. To simplify the process of ...@@ -145,32 +145,8 @@ Brakeman options can stored and read from YAML files. To simplify the process of
Options passed in on the commandline have priority over configuration files. Options passed in on the commandline have priority over configuration files.
The default config locations are `./config.yaml`, `~/.brakeman/`, and `/etc/brakeman/config.yaml` The default config locations are `./config/brakeman.yml`, `~/.brakeman/config.yml`, and `/etc/brakeman/config.yml`
The `-c` option can be used to specify a configuration file to use. The `-c` option can be used to specify a configuration file to use.
# License # License see MIT-LICENSE
The MIT License
Copyright (c) 2012, Twitter, Inc.
Copyright (c) 2010-2012, YELLOWPAGES.COM, LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
task :default do
sh "cd test && ruby test.rb"
end
...@@ -9,6 +9,7 @@ Gem::Specification.new do |s| ...@@ -9,6 +9,7 @@ Gem::Specification.new do |s|
s.homepage = "http://brakemanscanner.org" s.homepage = "http://brakemanscanner.org"
s.files = ["bin/brakeman", "CHANGES", "WARNING_TYPES", "FEATURES", "README.md"] + Dir["lib/**/*"] s.files = ["bin/brakeman", "CHANGES", "WARNING_TYPES", "FEATURES", "README.md"] + Dir["lib/**/*"]
s.executables = ["brakeman"] s.executables = ["brakeman"]
s.license = "MIT"
s.add_dependency "activesupport" s.add_dependency "activesupport"
s.add_dependency "i18n" s.add_dependency "i18n"
s.add_dependency "ruby_parser", "~>3.0.4" s.add_dependency "ruby_parser", "~>3.0.4"
......
...@@ -80,31 +80,38 @@ module Brakeman ...@@ -80,31 +80,38 @@ module Brakeman
options options
end end
#Load options from YAML file DEPRECATED_CONFIG_FILES = [
def self.load_options config_file File.expand_path("./config.yaml"),
config_file ||= "" File.expand_path("~/.brakeman/config.yaml"),
File.expand_path("/etc/brakeman/config.yaml"),
"#{File.expand_path(File.dirname(__FILE__))}/../lib/config.yaml"
]
CONFIG_FILES = [
File.expand_path("./config/brakeman.yml"),
File.expand_path("~/.brakeman/config.yml"),
File.expand_path("/etc/brakeman/config.yml"),
]
#Load options from YAML file
def self.load_options custom_location
#Load configuration file #Load configuration file
[File.expand_path(config_file), if config = config_file(custom_location)
File.expand_path("./config.yaml"), notify "[Notice] Using configuration in #{config}"
File.expand_path("~/.brakeman/config.yaml"), options = YAML.load_file config
File.expand_path("/etc/brakeman/config.yaml"), options.each { |k, v| options[k] = Set.new v if v.is_a? Array }
"#{File.expand_path(File.dirname(__FILE__))}/../lib/config.yaml"].each do |f| options
else
if File.exist? f and not File.directory? f {}
notify "[Notice] Using configuration in #{f}" end
options = YAML.load_file f end
options.each do |k,v|
if v.is_a? Array
options[k] = Set.new v
end
end
return options
end
end
return {} def self.config_file(custom_location=nil)
DEPRECATED_CONFIG_FILES.each do |f|
notify "#{f} is deprecated, please use one of #{CONFIG_FILES.join(", ")}" if File.file?(f)
end
supported_locations = [File.expand_path(custom_location || "")] + DEPRECATED_CONFIG_FILES + CONFIG_FILES
supported_locations.detect{|f| File.file?(f) }
end end
#Default set of options #Default set of options
......
...@@ -7,8 +7,6 @@ class Brakeman::CallIndex ...@@ -7,8 +7,6 @@ class Brakeman::CallIndex
def initialize calls def initialize calls
@calls_by_method = Hash.new { |h,k| h[k] = [] } @calls_by_method = Hash.new { |h,k| h[k] = [] }
@calls_by_target = Hash.new { |h,k| h[k] = [] } @calls_by_target = Hash.new { |h,k| h[k] = [] }
@methods = Set.new
@targets = Set.new
index_calls calls index_calls calls
end end
...@@ -25,7 +23,7 @@ class Brakeman::CallIndex ...@@ -25,7 +23,7 @@ class Brakeman::CallIndex
target = options[:target] || options[:targets] target = options[:target] || options[:targets]
method = options[:method] || options[:methods] method = options[:method] || options[:methods]
nested = options[:nested] nested = options[:nested]
if options[:chained] if options[:chained]
return find_chain options return find_chain options
#Find by narrowest category #Find by narrowest category
...@@ -72,16 +70,12 @@ class Brakeman::CallIndex ...@@ -72,16 +70,12 @@ class Brakeman::CallIndex
calls.delete_if do |call| calls.delete_if do |call|
from_template call, template_name from_template call, template_name
end end
@methods.delete name.to_s if calls.empty?
end end
@calls_by_target.each do |name, calls| @calls_by_target.each do |name, calls|
calls.delete_if do |call| calls.delete_if do |call|
from_template call, template_name from_template call, template_name
end end
@targets.delete name.to_s if calls.empty?
end end
end end
...@@ -90,25 +84,22 @@ class Brakeman::CallIndex ...@@ -90,25 +84,22 @@ class Brakeman::CallIndex
calls.delete_if do |call| calls.delete_if do |call|
call[:location][0] == :class and classes.include? call[:location][1] call[:location][0] == :class and classes.include? call[:location][1]
end end
@methods.delete name.to_s if calls.empty?
end end
@calls_by_target.each do |name, calls| @calls_by_target.each do |name, calls|
calls.delete_if do |call| calls.delete_if do |call|
call[:location][0] == :class and classes.include? call[:location][1] call[:location][0] == :class and classes.include? call[:location][1]
end end
@targets.delete name.to_s if calls.empty?
end end
end end
def index_calls calls def index_calls calls
calls.each do |call| calls.each do |call|
@methods << call[:method].to_s
@targets << call[:target].to_s if call[:target].is_a? Symbol
@calls_by_method[call[:method]] << call @calls_by_method[call[:method]] << call
@calls_by_target[call[:target]] << call
unless call[:target].is_a? Sexp
@calls_by_target[call[:target]] << call
end
end end
end end
...@@ -128,18 +119,6 @@ class Brakeman::CallIndex ...@@ -128,18 +119,6 @@ class Brakeman::CallIndex
def calls_by_target target def calls_by_target target
if target.is_a? Array if target.is_a? Array
calls_by_targets target calls_by_targets target
elsif target.is_a? Regexp
targets = @targets.select do |t|
t.match target
end
if targets.empty?
[]
elsif targets.length > 1
calls_by_targets targets
else
@calls_by_target[targets.first]
end
else else
@calls_by_target[target] @calls_by_target[target]
end end
...@@ -158,18 +137,6 @@ class Brakeman::CallIndex ...@@ -158,18 +137,6 @@ class Brakeman::CallIndex
def calls_by_method method def calls_by_method method
if method.is_a? Array if method.is_a? Array
calls_by_methods method calls_by_methods method
elsif method.is_a? Regexp
methods = @methods.select do |m|
m.match method
end
if methods.empty?
[]
elsif methods.length > 1
calls_by_methods methods
else
@calls_by_method[methods.first.to_sym]
end
else else
@calls_by_method[method.to_sym] @calls_by_method[method.to_sym]
end end
......
require 'brakeman/checks/base_check' require 'brakeman/checks/base_check'
#Check for vulnerability in translate() helper that allows cross-site scripting #Check for vulnerability in translate() helper that allows cross-site scripting
#http://groups.google.com/group/rubyonrails-security/browse_thread/thread/2b61d70fb73c7cc5
class Brakeman::CheckTranslateBug < Brakeman::BaseCheck class Brakeman::CheckTranslateBug < Brakeman::BaseCheck
Brakeman::Checks.add self Brakeman::Checks.add self
...@@ -12,32 +11,34 @@ class Brakeman::CheckTranslateBug < Brakeman::BaseCheck ...@@ -12,32 +11,34 @@ class Brakeman::CheckTranslateBug < Brakeman::BaseCheck
version_between?('3.0.0', '3.0.10') or version_between?('3.0.0', '3.0.10') or
version_between?('3.1.0', '3.1.1') version_between?('3.1.0', '3.1.1')
if uses_translate? confidence = if uses_translate?
confidence = CONFIDENCE[:high] CONFIDENCE[:high]
else else
confidence = CONFIDENCE[:med] CONFIDENCE[:med]
end end
version = tracker.config[:rails_version] version = tracker.config[:rails_version]
description = "have a vulnerability in the translate helper with keys ending in _html"
if version =~ /^3\.1/ message = if version =~ /^3\.1/
message = "Versions before 3.1.2 have a vulnerability in the translate helper." "Versions before 3.1.2 #{description}."
elsif version =~ /^3\.0/ elsif version =~ /^3\.0/
message = "Versions before 3.0.11 have a vulnerability in translate helper." "Versions before 3.0.11 #{description}."
else else
message = "Rails 2.3.x using the rails_xss plugin have a vulnerability in translate helper." "Rails 2.3.x using the rails_xss plugin #{description}}."
end end
warn :warning_type => "Cross Site Scripting", warn :warning_type => "Cross Site Scripting",
:message => message, :message => message,
:confidence => confidence, :confidence => confidence,
:file => gemfile_or_environment :file => gemfile_or_environment,
:link_path => "http://groups.google.com/group/rubyonrails-security/browse_thread/thread/2b61d70fb73c7cc5"
end end
end end
def uses_translate? def uses_translate?
Brakeman.debug "Finding calls to translate() or t()" Brakeman.debug "Finding calls to translate() or t()"
not tracker.find_call(:target => nil, :methods => [:t, :translate]).empty? tracker.find_call(:target => nil, :methods => [:t, :translate]).any?
end end
end end
...@@ -475,7 +475,17 @@ class Brakeman::Report ...@@ -475,7 +475,17 @@ class Brakeman::Report
#Generate header for text output #Generate header for text output
def text_header def text_header
"\n+BRAKEMAN REPORT+\n\nApplication path: #{File.expand_path tracker.options[:app_path]}\nRails version: #{rails_version}\nGenerated at #{Time.now}\nChecks run: #{checks.checks_run.sort.join(", ")}\n" <<-HEADER
+BRAKEMAN REPORT+
Application path: #{File.expand_path tracker.options[:app_path]}
Rails version: #{rails_version}
Brakeman version: #{Brakeman::Version}
Started at #{tracker.start_time}
Duration: #{tracker.duration} seconds
Checks run: #{checks.checks_run.sort.join(", ")}
HEADER
end end
#Generate header for CSV output #Generate header for CSV output
...@@ -670,7 +680,10 @@ class Brakeman::Report ...@@ -670,7 +680,10 @@ class Brakeman::Report
:app_path => File.expand_path(tracker.options[:app_path]), :app_path => File.expand_path(tracker.options[:app_path]),
:rails_version => rails_version, :rails_version => rails_version,
:security_warnings => all_warnings.length, :security_warnings => all_warnings.length,
:timestamp => Time.now.to_s, :start_time => tracker.start_time.to_s,
:end_time => tracker.end_time.to_s,
:timestamp => tracker.end_time.to_s,
:duration => tracker.duration,
:checks_performed => checks.checks_run.sort, :checks_performed => checks.checks_run.sort,
:number_of_controllers =>tracker.controllers.length, :number_of_controllers =>tracker.controllers.length,
# ignore the "fake" model # ignore the "fake" model
......
...@@ -26,13 +26,18 @@ ...@@ -26,13 +26,18 @@
<tr> <tr>
<th>Application Path</th> <th>Application Path</th>
<th>Rails Version</th> <th>Rails Version</th>
<th>Report Generation Time</th> <th>Brakeman Version</th>
<th>Report Time</th>
<th>Checks Performed</th> <th>Checks Performed</th>
</tr> </tr>
<tr> <tr>
<td><%= File.expand_path tracker.options[:app_path] %></td> <td><%= File.expand_path tracker.options[:app_path] %></td>
<td><%= rails_version %></td> <td><%= rails_version %></td>
<td><%= Time.now %></td> <td><%= Brakeman::Version %>
<td>
<%= tracker.start_time %><br><br>
<%= tracker.duration %> seconds
</td>
<td><%= checks.checks_run.sort.join(", ") %></td> <td><%= checks.checks_run.sort.join(", ") %></td>
</tr> </tr>
</table> </table>
......
...@@ -9,7 +9,8 @@ require 'brakeman/processors/lib/find_all_calls' ...@@ -9,7 +9,8 @@ require 'brakeman/processors/lib/find_all_calls'
class Brakeman::Tracker class Brakeman::Tracker
attr_accessor :controllers, :templates, :models, :errors, attr_accessor :controllers, :templates, :models, :errors,
:checks, :initializers, :config, :routes, :processor, :libs, :checks, :initializers, :config, :routes, :processor, :libs,
:template_cache, :options, :filter_cache :template_cache, :options, :filter_cache, :start_time, :end_time,
:duration
#Place holder when there should be a model, but it is not #Place holder when there should be a model, but it is not
#clear what model it will be. #clear what model it will be.
...@@ -44,6 +45,9 @@ class Brakeman::Tracker ...@@ -44,6 +45,9 @@ class Brakeman::Tracker
@template_cache = Set.new @template_cache = Set.new
@filter_cache = {} @filter_cache = {}
@call_index = nil @call_index = nil
@start_time = Time.now
@end_time = nil
@duration = nil
end end
#Add an error to the list. If no backtrace is given, #Add an error to the list. If no backtrace is given,
...@@ -64,6 +68,10 @@ class Brakeman::Tracker ...@@ -64,6 +68,10 @@ class Brakeman::Tracker
#in Tracker#checks. #in Tracker#checks.
def run_checks def run_checks
@checks = Brakeman::Checks.run_checks(self) @checks = Brakeman::Checks.run_checks(self)
@end_time = Time.now
@duration = @end_time - @start_time
@checks
end end
#Iterate over all methods in controllers and models. #Iterate over all methods in controllers and models.
......
...@@ -76,14 +76,14 @@ class Brakeman::Warning ...@@ -76,14 +76,14 @@ class Brakeman::Warning
#Return String of the code output from the OutputProcessor and #Return String of the code output from the OutputProcessor and
#stripped of newlines and tabs. #stripped of newlines and tabs.
def format_code def format_code strip = true
Brakeman::OutputProcessor.new.format(self.code).gsub(/(\t|\r|\n)+/, " ") format_ruby self.code, strip
end end
#Return String of the user input formatted and #Return String of the user input formatted and
#stripped of newlines and tabs. #stripped of newlines and tabs.
def format_user_input def format_user_input strip = true
Brakeman::OutputProcessor.new.format(self.user_input).gsub(/(\t|\r|\n)+/, " ") format_ruby self.user_input, strip
end end
#Return formatted warning message #Return formatted warning message
...@@ -171,9 +171,9 @@ class Brakeman::Warning ...@@ -171,9 +171,9 @@ class Brakeman::Warning
:file => self.file, :file => self.file,
:line => self.line, :line => self.line,
:link => self.link, :link => self.link,
:code => (@code && self.format_code), :code => (@code && self.format_code(false)),
:location => location, :location => location,
:user_input => (@user_input && self.format_user_input), :user_input => (@user_input && self.format_user_input(false)),
:confidence => TEXT_CONFIDENCE[self.confidence] :confidence => TEXT_CONFIDENCE[self.confidence]
} }
end end
...@@ -181,4 +181,12 @@ class Brakeman::Warning ...@@ -181,4 +181,12 @@ class Brakeman::Warning
def to_json def to_json
MultiJson.dump self.to_hash MultiJson.dump self.to_hash
end end
private
def format_ruby code, strip
formatted = Brakeman::OutputProcessor.new.format(code)
formatted.gsub!(/(\t|\r|\n)+/, " ") if strip
formatted
end
end end
## Testing ## Testing
Run `ruby test.rb`. Run `rake` or if you want to avoid bundler `cd test && ruby test.rb`.
This runs Brakeman against full apps in the `apps` directory and checks the results against what is expected. This runs Brakeman against full apps in the `apps` directory and checks the results against what is expected.
## Test Generation ## Test Generation
Run `ruby to_test.rb apps/some_app > tests/test_some_app.rb` to generate a test suite with tests for each warning reported. Run `cd test && ruby to_test.rb apps/some_app > tests/test_some_app.rb` to generate a test suite with tests for each warning reported.
...@@ -9,6 +9,10 @@ class JSONCompareTests < Test::Unit::TestCase ...@@ -9,6 +9,10 @@ class JSONCompareTests < Test::Unit::TestCase
@report = MultiJson.load File.read(@json_path) @report = MultiJson.load File.read(@json_path)
end end
def teardown
File.delete @json_path if File.exist? @json_path
end
def update_json def update_json
File.open @json_path, "w" do |f| File.open @json_path, "w" do |f|
f.puts @report.to_json f.puts @report.to_json
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册