brakeman 1.8.2 → 1.8.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +16 -0
- data/bin/brakeman +2 -1
- data/lib/brakeman.rb +5 -4
- data/lib/brakeman/call_index.rb +2 -2
- data/lib/brakeman/checks/base_check.rb +20 -19
- data/lib/brakeman/checks/check_cross_site_scripting.rb +1 -0
- data/lib/brakeman/checks/check_file_access.rb +3 -1
- data/lib/brakeman/checks/check_redirect.rb +75 -27
- data/lib/brakeman/processors/gem_processor.rb +1 -1
- data/lib/brakeman/processors/haml_template_processor.rb +2 -1
- data/lib/brakeman/processors/lib/rails2_route_processor.rb +5 -2
- data/lib/brakeman/processors/model_processor.rb +11 -2
- data/lib/brakeman/processors/template_alias_processor.rb +6 -0
- data/lib/brakeman/report.rb +13 -5
- data/lib/brakeman/rescanner.rb +2 -2
- data/lib/brakeman/version.rb +1 -1
- data/lib/brakeman/warning.rb +3 -3
- metadata +10 -9
data/CHANGES
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
# 1.8.3
|
2
|
+
|
3
|
+
* Use `multi_json` gem for better harmony
|
4
|
+
* Performance improvement for call indexing
|
5
|
+
* Fix issue with processing HAML files
|
6
|
+
* Handle pre-release versions when processing `Gemfile.lock`
|
7
|
+
* Only check first argument of `redirect_to`
|
8
|
+
* Fix false positives from `Model.arel_table` accesses
|
9
|
+
* Fix false positives on redirects to models decorated with Draper gem
|
10
|
+
* Fix false positive on redirect to model association
|
11
|
+
* Fix false positive on `YAML.load`
|
12
|
+
* Fix false positive XSS on any `to_i` output
|
13
|
+
* Fix error on Rails 2 name routes with no args
|
14
|
+
* Fix error in rescan of mixins with symbols in method name
|
15
|
+
* Do not rescan non-Ruby files in config/
|
16
|
+
|
1
17
|
# 1.8.2
|
2
18
|
|
3
19
|
* Fixed rescanning problems caused by 1.8.0 changes
|
data/bin/brakeman
CHANGED
@@ -54,7 +54,8 @@ end
|
|
54
54
|
|
55
55
|
if options[:previous_results_json]
|
56
56
|
vulns = Brakeman.compare options.merge(:quiet => options[:quiet])
|
57
|
-
puts
|
57
|
+
puts MultiJson.dump(vulns, :pretty => true)
|
58
|
+
|
58
59
|
if options[:exit_on_warn] and (vulns[:new].count + vulns[:fixed].count > 0)
|
59
60
|
exit Brakeman::Warnings_Found_Exit_Code
|
60
61
|
end
|
data/lib/brakeman.rb
CHANGED
@@ -316,19 +316,20 @@ module Brakeman
|
|
316
316
|
|
317
317
|
# Compare JSON ouptut from a previous scan and return the diff of the two scans
|
318
318
|
def self.compare options
|
319
|
-
require '
|
319
|
+
require 'multi_json'
|
320
320
|
require 'brakeman/differ'
|
321
321
|
raise ArgumentError.new("Comparison file doesn't exist") unless File.exists? options[:previous_results_json]
|
322
322
|
|
323
323
|
begin
|
324
|
-
previous_results =
|
325
|
-
rescue
|
324
|
+
previous_results = MultiJson.load(File.read(options[:previous_results_json]), :symbolize_keys => true)[:warnings]
|
325
|
+
rescue MultiJson::DecodeError
|
326
326
|
self.notify "Error parsing comparison file: #{options[:previous_results_json]}"
|
327
327
|
exit!
|
328
328
|
end
|
329
329
|
|
330
330
|
tracker = run(options)
|
331
|
-
|
331
|
+
|
332
|
+
new_results = MultiJson.load(tracker.report.to_json, :symbolize_keys => true)[:warnings]
|
332
333
|
|
333
334
|
Brakeman::Differ.new(new_results, previous_results).diff
|
334
335
|
end
|
data/lib/brakeman/call_index.rb
CHANGED
@@ -106,7 +106,7 @@ class Brakeman::CallIndex
|
|
106
106
|
def index_calls calls
|
107
107
|
calls.each do |call|
|
108
108
|
@methods << call[:method].to_s
|
109
|
-
@targets << call[:target].to_s
|
109
|
+
@targets << call[:target].to_s if call[:target].is_a? Symbol
|
110
110
|
@calls_by_method[call[:method]] << call
|
111
111
|
@calls_by_target[call[:target]] << call
|
112
112
|
end
|
@@ -138,7 +138,7 @@ class Brakeman::CallIndex
|
|
138
138
|
elsif targets.length > 1
|
139
139
|
calls_by_targets targets
|
140
140
|
else
|
141
|
-
calls_by_target[targets.first]
|
141
|
+
@calls_by_target[targets.first]
|
142
142
|
end
|
143
143
|
else
|
144
144
|
@calls_by_target[target]
|
@@ -23,6 +23,7 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
|
|
23
23
|
@current_set = nil
|
24
24
|
@current_template = @current_module = @current_class = @current_method = nil
|
25
25
|
@mass_assign_disabled = nil
|
26
|
+
@safe_input_attributes = Set[:to_i, :to_f, :arel_table]
|
26
27
|
end
|
27
28
|
|
28
29
|
#Add result to result list, which is used to check for duplicates
|
@@ -63,14 +64,16 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
|
|
63
64
|
|
64
65
|
target = exp.target
|
65
66
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
67
|
+
unless @safe_input_attributes.include? exp.method
|
68
|
+
if params? target
|
69
|
+
@has_user_input = Match.new(:params, exp)
|
70
|
+
elsif cookies? target
|
71
|
+
@has_user_input = Match.new(:cookies, exp)
|
72
|
+
elsif request_env? target
|
73
|
+
@has_user_input = Match.new(:request, exp)
|
74
|
+
elsif sexp? target and model_name? target[1]
|
75
|
+
@has_user_input = Match.new(:model, exp)
|
76
|
+
end
|
74
77
|
end
|
75
78
|
|
76
79
|
exp
|
@@ -255,19 +258,15 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
|
|
255
258
|
def has_immediate_user_input? exp
|
256
259
|
if exp.nil?
|
257
260
|
false
|
258
|
-
elsif
|
259
|
-
|
260
|
-
elsif cookies? exp
|
261
|
-
return Match.new(:cookies, exp)
|
262
|
-
elsif call? exp
|
263
|
-
if params? exp.target
|
261
|
+
elsif call? exp and not @safe_input_attributes.include? exp.method
|
262
|
+
if params? exp
|
264
263
|
return Match.new(:params, exp)
|
265
|
-
elsif cookies? exp
|
264
|
+
elsif cookies? exp
|
266
265
|
return Match.new(:cookies, exp)
|
267
|
-
elsif request_env? exp
|
266
|
+
elsif request_env? exp
|
268
267
|
return Match.new(:request, exp)
|
269
268
|
else
|
270
|
-
|
269
|
+
has_immediate_user_input? exp.target
|
271
270
|
end
|
272
271
|
elsif sexp? exp
|
273
272
|
case exp.node_type
|
@@ -318,9 +317,11 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
|
|
318
317
|
target = exp.target
|
319
318
|
method = exp.method
|
320
319
|
|
321
|
-
if
|
320
|
+
if @safe_input_attributes.include? method
|
321
|
+
false
|
322
|
+
elsif call? target and not method.to_s[-1,1] == "?"
|
322
323
|
has_immediate_model? target, out
|
323
|
-
elsif model_name? target
|
324
|
+
elsif model_name? target
|
324
325
|
exp
|
325
326
|
else
|
326
327
|
false
|
@@ -236,6 +236,7 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
|
|
236
236
|
((target == URI or target == CGI) and method == :escape) or
|
237
237
|
(target == XML_HELPER and method == :escape_xml) or
|
238
238
|
(target == FORM_BUILDER and @ignore_methods.include? method) or
|
239
|
+
(target and @safe_input_attributes.include? method) or
|
239
240
|
(method.to_s[-1,1] == "?")
|
240
241
|
|
241
242
|
#exp[0] = :ignore #should not be necessary
|
@@ -9,7 +9,9 @@ class Brakeman::CheckFileAccess < Brakeman::BaseCheck
|
|
9
9
|
|
10
10
|
def run_check
|
11
11
|
Brakeman.debug "Finding possible file access"
|
12
|
-
methods = tracker.find_call :targets => [:Dir, :File, :IO, :Kernel, :"Net::FTP", :"Net::HTTP", :PStore, :Pathname, :Shell
|
12
|
+
methods = tracker.find_call :targets => [:Dir, :File, :IO, :Kernel, :"Net::FTP", :"Net::HTTP", :PStore, :Pathname, :Shell], :methods => [:[], :chdir, :chroot, :delete, :entries, :foreach, :glob, :install, :lchmod, :lchown, :link, :load, :load_file, :makedirs, :move, :new, :open, :read, :readlines, :rename, :rmdir, :safe_unlink, :symlink, :syscopy, :sysopen, :truncate, :unlink]
|
13
|
+
|
14
|
+
methods.concat tracker.find_call :target => :YAML, :methods => [:load_file, :parse_file]
|
13
15
|
|
14
16
|
Brakeman.debug "Finding calls to load()"
|
15
17
|
methods.concat tracker.find_call :target => false, :method => :load
|
@@ -53,44 +53,40 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck
|
|
53
53
|
#is being output directly. This is necessary because of tracker.options[:check_arguments]
|
54
54
|
#which can be used to enable/disable reporting output of method calls which use
|
55
55
|
#user input as arguments.
|
56
|
-
def include_user_input? call
|
56
|
+
def include_user_input? call, immediate = :immediate
|
57
57
|
Brakeman.debug "Checking if call includes user input"
|
58
58
|
|
59
|
-
|
60
|
-
first_arg = call.first_arg
|
59
|
+
arg = call.first_arg
|
61
60
|
|
62
61
|
# if the first argument is an array, rails assumes you are building a
|
63
62
|
# polymorphic route, which will never jump off-host
|
64
|
-
return false if array?
|
65
|
-
|
66
|
-
if tracker.options[:ignore_redirect_to_model] and call? first_arg and
|
67
|
-
(@model_find_calls.include? first_arg.method or first_arg.method.to_s.match(/^find_by_/)) and
|
68
|
-
model_name? first_arg.target
|
63
|
+
return false if array? arg
|
69
64
|
|
70
|
-
|
65
|
+
if tracker.options[:ignore_redirect_to_model]
|
66
|
+
if model_instance?(arg) or decorated_model?(arg)
|
67
|
+
return false
|
68
|
+
end
|
71
69
|
end
|
72
70
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
return false
|
86
|
-
end
|
87
|
-
elsif request_value? arg
|
88
|
-
return Match.new(:immediate, arg)
|
71
|
+
if res = has_immediate_model?(arg)
|
72
|
+
return Match.new(immediate, res)
|
73
|
+
elsif call? arg
|
74
|
+
if request_value? arg
|
75
|
+
return Match.new(immediate, arg)
|
76
|
+
elsif request_value? arg[1]
|
77
|
+
return Match.new(immediate, arg[1])
|
78
|
+
elsif arg[2] == :url_for and include_user_input? arg
|
79
|
+
return Match.new(immediate, arg)
|
80
|
+
#Ignore helpers like some_model_url?
|
81
|
+
elsif arg[2].to_s =~ /_(url|path)\z/
|
82
|
+
return false
|
89
83
|
end
|
84
|
+
elsif request_value? arg
|
85
|
+
return Match.new(immediate, arg)
|
90
86
|
end
|
91
87
|
|
92
|
-
if tracker.options[:check_arguments]
|
93
|
-
|
88
|
+
if tracker.options[:check_arguments] and call? arg
|
89
|
+
include_user_input? arg, false #I'm doubting if this is really necessary...
|
94
90
|
else
|
95
91
|
false
|
96
92
|
end
|
@@ -125,4 +121,56 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck
|
|
125
121
|
|
126
122
|
true
|
127
123
|
end
|
124
|
+
|
125
|
+
#Returns true if exp is (probably) a model instance
|
126
|
+
def model_instance? exp
|
127
|
+
if node_type? exp, :or
|
128
|
+
model_instance? exp.lhs or model_instance? exp.rhs
|
129
|
+
elsif call? exp
|
130
|
+
if model_name? exp.target and
|
131
|
+
(@model_find_calls.include? exp.method or exp.method.to_s.match(/^find_by_/))
|
132
|
+
true
|
133
|
+
else
|
134
|
+
association?(exp.target, exp.method)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
#Returns true if exp is (probably) a decorated model instance
|
140
|
+
#using the Draper gem
|
141
|
+
def decorated_model? exp
|
142
|
+
if node_type? exp, :or
|
143
|
+
decorated_model? exp.lhs or decorated_model? exp.rhs
|
144
|
+
else
|
145
|
+
tracker.config[:gems] and
|
146
|
+
tracker.config[:gems][:draper] and
|
147
|
+
call? exp and
|
148
|
+
node_type?(exp.target, :const) and
|
149
|
+
exp.target.value.to_s.match(/Decorator$/) and
|
150
|
+
exp.method == :decorate
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
#Check if method is actually an association in a Model
|
155
|
+
def association? model_name, meth
|
156
|
+
if call? model_name
|
157
|
+
return association? model_name.target, meth
|
158
|
+
elsif model_name? model_name
|
159
|
+
model = tracker.models[class_name(model_name)]
|
160
|
+
else
|
161
|
+
return false
|
162
|
+
end
|
163
|
+
|
164
|
+
return false unless model
|
165
|
+
|
166
|
+
model[:associations].each do |name, args|
|
167
|
+
args.each do |arg|
|
168
|
+
if symbol? arg and arg.value == meth
|
169
|
+
return true
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
false
|
175
|
+
end
|
128
176
|
end
|
@@ -74,7 +74,8 @@ class Brakeman::HamlTemplateProcessor < Brakeman::TemplateProcessor
|
|
74
74
|
res
|
75
75
|
|
76
76
|
#_hamlout.buffer <<
|
77
|
-
#This seems to be used rarely, but directly appends args to output buffer
|
77
|
+
#This seems to be used rarely, but directly appends args to output buffer.
|
78
|
+
#Has something to do with values of blocks?
|
78
79
|
elsif sexp? target and method == :<< and is_buffer_target? target
|
79
80
|
@inside_concat = true
|
80
81
|
out = exp.arglist[1] = process(exp.arglist[1])
|
@@ -112,7 +112,8 @@ class Brakeman::Rails2RoutesProcessor < Brakeman::BaseProcessor
|
|
112
112
|
hash_iterate(exp) do |option, value|
|
113
113
|
case option[1]
|
114
114
|
when :controller, :requirements, :singular, :path_prefix, :as,
|
115
|
-
:path_names, :shallow, :name_prefix, :member_path, :nested_member_path
|
115
|
+
:path_names, :shallow, :name_prefix, :member_path, :nested_member_path,
|
116
|
+
:belongs_to, :conditions, :active_scaffold
|
116
117
|
#should be able to skip
|
117
118
|
when :collection, :member, :new
|
118
119
|
process_collection value
|
@@ -129,7 +130,7 @@ class Brakeman::Rails2RoutesProcessor < Brakeman::BaseProcessor
|
|
129
130
|
when :except
|
130
131
|
process_option_except value
|
131
132
|
else
|
132
|
-
Brakeman.notify "[Notice] Unhandled resource option: #{option}"
|
133
|
+
Brakeman.notify "[Notice] Unhandled resource option, please report: #{option}"
|
133
134
|
end
|
134
135
|
end
|
135
136
|
end
|
@@ -178,6 +179,8 @@ class Brakeman::Rails2RoutesProcessor < Brakeman::BaseProcessor
|
|
178
179
|
#Process
|
179
180
|
# map.connect '/something', :controller => 'blah', :action => 'whatever'
|
180
181
|
def process_connect exp
|
182
|
+
return if exp.empty?
|
183
|
+
|
181
184
|
controller = check_for_controller_name exp
|
182
185
|
self.current_controller = controller if controller
|
183
186
|
|
@@ -2,6 +2,9 @@ require 'brakeman/processors/base_processor'
|
|
2
2
|
|
3
3
|
#Processes models. Puts results in tracker.models
|
4
4
|
class Brakeman::ModelProcessor < Brakeman::BaseProcessor
|
5
|
+
|
6
|
+
ASSOCIATIONS = Set[:belongs_to, :has_one, :has_many, :has_and_belongs_to_many]
|
7
|
+
|
5
8
|
def initialize tracker
|
6
9
|
super
|
7
10
|
@model = nil
|
@@ -38,6 +41,7 @@ class Brakeman::ModelProcessor < Brakeman::BaseProcessor
|
|
38
41
|
:private => {},
|
39
42
|
:protected => {},
|
40
43
|
:options => {},
|
44
|
+
:associations => {},
|
41
45
|
:file => @file_name }
|
42
46
|
@tracker.models[@model[:name]] = @model
|
43
47
|
res = process exp.body
|
@@ -83,8 +87,13 @@ class Brakeman::ModelProcessor < Brakeman::BaseProcessor
|
|
83
87
|
@model[:attr_accessible].concat args
|
84
88
|
else
|
85
89
|
if @model
|
86
|
-
|
87
|
-
|
90
|
+
if ASSOCIATIONS.include? method
|
91
|
+
@model[:associations][method] ||= []
|
92
|
+
@model[:associations][method].concat exp.args
|
93
|
+
else
|
94
|
+
@model[:options][method] ||= []
|
95
|
+
@model[:options][method] << exp.arglist
|
96
|
+
end
|
88
97
|
end
|
89
98
|
end
|
90
99
|
end
|
@@ -96,11 +96,17 @@ class Brakeman::TemplateAliasProcessor < Brakeman::AliasProcessor
|
|
96
96
|
false
|
97
97
|
end
|
98
98
|
|
99
|
+
#Ignore `<<` calls on template variables which are used by the templating
|
100
|
+
#library (HAML, ERB, etc.)
|
99
101
|
def find_push_target exp
|
100
102
|
if sexp? exp
|
101
103
|
if exp.node_type == :lvar and (exp.value == :_buf or exp.value == :_erbout)
|
102
104
|
return nil
|
103
105
|
elsif exp.node_type == :ivar and exp.value == :@output_buffer
|
106
|
+
return nil
|
107
|
+
elsif exp.node_type == :call and call? exp.target and
|
108
|
+
exp.target.method == :_hamlout and exp.method == :buffer
|
109
|
+
|
104
110
|
return nil
|
105
111
|
end
|
106
112
|
end
|
data/lib/brakeman/report.rb
CHANGED
@@ -6,6 +6,7 @@ require 'brakeman/util'
|
|
6
6
|
require 'terminal-table'
|
7
7
|
require 'highline/system_extensions'
|
8
8
|
require "csv"
|
9
|
+
require 'multi_json'
|
9
10
|
require 'brakeman/version'
|
10
11
|
|
11
12
|
if CSV.const_defined? :Reader
|
@@ -17,6 +18,15 @@ else
|
|
17
18
|
# CSV is now FasterCSV in ruby 1.9
|
18
19
|
end
|
19
20
|
|
21
|
+
#This is so OkJson will work with symbol values
|
22
|
+
if MultiJson.default_adapter == :ok_json
|
23
|
+
class Symbol
|
24
|
+
def to_json
|
25
|
+
self.to_s.inspect
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
20
30
|
#Generates a report based on the Tracker and the results of
|
21
31
|
#Tracker#run_checks. Be sure to +run_checks+ before generating
|
22
32
|
#a report.
|
@@ -647,8 +657,6 @@ class Brakeman::Report
|
|
647
657
|
end
|
648
658
|
|
649
659
|
def to_json
|
650
|
-
require 'json'
|
651
|
-
|
652
660
|
errors = tracker.errors.map{|e| { :error => e[:error], :location => e[:backtrace][0] }}
|
653
661
|
app_path = tracker.options[:app_path]
|
654
662
|
|
@@ -662,7 +670,7 @@ class Brakeman::Report
|
|
662
670
|
:app_path => File.expand_path(tracker.options[:app_path]),
|
663
671
|
:rails_version => rails_version,
|
664
672
|
:security_warnings => all_warnings.length,
|
665
|
-
:timestamp => Time.now,
|
673
|
+
:timestamp => Time.now.to_s,
|
666
674
|
:checks_performed => checks.checks_run.sort,
|
667
675
|
:number_of_controllers =>tracker.controllers.length,
|
668
676
|
# ignore the "fake" model
|
@@ -672,11 +680,11 @@ class Brakeman::Report
|
|
672
680
|
:brakeman_version => Brakeman::Version
|
673
681
|
}
|
674
682
|
|
675
|
-
|
683
|
+
MultiJson.dump({
|
676
684
|
:scan_info => scan_info,
|
677
685
|
:warnings => warnings,
|
678
686
|
:errors => errors
|
679
|
-
})
|
687
|
+
}, :pretty => true)
|
680
688
|
end
|
681
689
|
|
682
690
|
def all_warnings
|
data/lib/brakeman/rescanner.rb
CHANGED
@@ -304,7 +304,7 @@ class Brakeman::Rescanner < Brakeman::Scanner
|
|
304
304
|
:initializer
|
305
305
|
when /config\/routes\.rb/
|
306
306
|
:routes
|
307
|
-
when /\/config/
|
307
|
+
when /\/config\/.+\.rb/
|
308
308
|
:config
|
309
309
|
when /Gemfile/
|
310
310
|
:gemfile
|
@@ -322,7 +322,7 @@ class Brakeman::Rescanner < Brakeman::Scanner
|
|
322
322
|
end
|
323
323
|
end
|
324
324
|
|
325
|
-
method_matcher = /##{method_names.join('|')}$/
|
325
|
+
method_matcher = /##{method_names.map {|n| Regexp.escape(n.to_s)}.join('|')}$/
|
326
326
|
|
327
327
|
#Rescan controllers that mixed in library
|
328
328
|
tracker.controllers.each do |name, controller|
|
data/lib/brakeman/version.rb
CHANGED
data/lib/brakeman/warning.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
|
1
3
|
#The Warning class stores information about warnings
|
2
4
|
class Brakeman::Warning
|
3
5
|
attr_reader :called_from, :check, :class, :confidence, :controller,
|
@@ -177,8 +179,6 @@ class Brakeman::Warning
|
|
177
179
|
end
|
178
180
|
|
179
181
|
def to_json
|
180
|
-
|
181
|
-
|
182
|
-
JSON.dump self.to_hash
|
182
|
+
MultiJson.dump self.to_hash
|
183
183
|
end
|
184
184
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: brakeman
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 49
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 8
|
9
|
-
-
|
10
|
-
version: 1.8.
|
9
|
+
- 3
|
10
|
+
version: 1.8.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Justin Collins
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-11-13 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: activesupport
|
@@ -167,17 +167,18 @@ dependencies:
|
|
167
167
|
type: :runtime
|
168
168
|
version_requirements: *id010
|
169
169
|
- !ruby/object:Gem::Dependency
|
170
|
-
name:
|
170
|
+
name: multi_json
|
171
171
|
prerelease: false
|
172
172
|
requirement: &id011 !ruby/object:Gem::Requirement
|
173
173
|
none: false
|
174
174
|
requirements:
|
175
|
-
- -
|
175
|
+
- - ~>
|
176
176
|
- !ruby/object:Gem::Version
|
177
|
-
hash:
|
177
|
+
hash: 9
|
178
178
|
segments:
|
179
|
-
-
|
180
|
-
|
179
|
+
- 1
|
180
|
+
- 3
|
181
|
+
version: "1.3"
|
181
182
|
type: :runtime
|
182
183
|
version_requirements: *id011
|
183
184
|
description: Brakeman detects security vulnerabilities in Ruby on Rails applications via static analysis.
|