flaw_detector 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,526 @@
1
+ require 'spec_helper'
2
+ require 'yaml'
3
+ require 'tempfile'
4
+
5
+ describe FlawDetector do
6
+ def flaws_from_comment(filename, code)
7
+ result = []
8
+ code.split("\n").each_with_index do |line, lineno_zero_basis|
9
+ next unless line =~ /#\*\*([\w_]+)(,([\w,]+))*\*\*/
10
+ lineno = lineno_zero_basis + 1
11
+ params = if $3 then $3.split(",") else [] end
12
+ result << FlawDetector::make_info(:file => filename, :line => lineno, :msgid => $1, :params => params)
13
+ end
14
+ result
15
+ end
16
+
17
+ describe "::NilVarDereference#analyze" do
18
+ let(:nvd){ FlawDetector::Detector::NilFalsePathFlow.new}
19
+
20
+ context "refer a variable which don't have def" do
21
+ before{
22
+ code =<<EOF
23
+ def nil_var_ref(a)
24
+ if a
25
+ i = 1
26
+ else
27
+ # i's type is Nil
28
+ puts(i + 1) # will be detected
29
+ end
30
+ return i
31
+ end
32
+ nil_var_ref(1)
33
+ EOF
34
+ }
35
+ it 'should detect nil variable dereference'
36
+ end
37
+
38
+ context "NP_ALWAYS_FALSE exists" do
39
+ let(:code){
40
+ <<EOF
41
+ def nil_var_ref(a)
42
+ if a
43
+ rl = a + 1
44
+ rl = 1 + a
45
+ elsif
46
+ rl = a + 1 #**NP_ALWAYS_FALSE,a**
47
+ rl = 1 + a #**NP_ALWAYS_FALSE,a**
48
+ end
49
+ return rl
50
+ end
51
+ nil_var_ref(1)
52
+ EOF
53
+ }
54
+ let(:dom){FlawDetector::parse(code)}
55
+ it {expect(nvd.analyze(dom)).to eq(flaws_from_comment("<compiled>", code))}
56
+ end
57
+
58
+ context "Falsecheck of value previously dereferenced" do
59
+ let(:code){
60
+ <<EOF
61
+ a.times do |i|
62
+ some_method1(i+a)
63
+ end
64
+ if a #**RCN_REDUNDANT_FALSECHECK_WOULD_HAVE_BEEN_A_NPE,a**
65
+ some_method2(a)
66
+ end
67
+ EOF
68
+ }
69
+ let(:dom){FlawDetector::parse(code)}
70
+ #it {expect(nvd.analyze(dom)).to eq(flaws_from_comment("<compiled>", code))}
71
+ it "Falsecheck of value previously dereferenced"
72
+ end
73
+
74
+ context "RCN_REDUNDANT_FALSECHECK_OF_FALSE_VALUE exists" do
75
+ let(:code) {
76
+ code =<<EOF
77
+ def nil_var_ref(a)
78
+ if a
79
+ rl = a + 1
80
+ elsif a #**RCN_REDUNDANT_FALSECHECK_OF_FALSE_VALUE,a,2**
81
+ rl = 1
82
+ else
83
+ rl = 1
84
+ end
85
+ end
86
+ nil_var_ref(1)
87
+ EOF
88
+ }
89
+ let(:dom){FlawDetector::parse(code)}
90
+ it {expect(nvd.analyze(dom)).to eq(flaws_from_comment("<compiled>", code))}
91
+ end
92
+
93
+ context "complex error exists" do
94
+ let(:code) {
95
+ code =<<EOF
96
+ def nil_var_ref(a)
97
+ if a
98
+ rl = a + 1
99
+ elsif a #**RCN_REDUNDANT_FALSECHECK_OF_FALSE_VALUE,a,2**
100
+ rl = 1 + a
101
+ else
102
+ rl = a + 1 #**NP_ALWAYS_FALSE,a**
103
+ end
104
+ end
105
+ nil_var_ref(1)
106
+ EOF
107
+ }
108
+ let(:dom){FlawDetector::parse(code)}
109
+ it {expect(nvd.analyze(dom)).to eq(flaws_from_comment("<compiled>", code))}
110
+ end
111
+
112
+ context "complex error exists2" do
113
+ let(:code) {
114
+ code =<<EOF
115
+ def nil_var_ref(a)
116
+ if a.nil?
117
+ rl = a + 1 #**NP_ALWAYS_FALSE,a**
118
+ elsif a.nil? #**RCN_REDUNDANT_FALSECHECK_OF_TRUE_VALUE,a,2**
119
+ rl = a + 1
120
+ else
121
+ rl = 1 + a
122
+ end
123
+ end
124
+ nil_var_ref(1)
125
+ EOF
126
+ }
127
+ let(:dom){FlawDetector::parse(code)}
128
+ it {expect(nvd.analyze(dom)).to eq(flaws_from_comment("<compiled>", code))}
129
+ end
130
+
131
+ describe "bug fixed" do
132
+ context "no error1" do
133
+ let(:code){
134
+ <<EOF
135
+ def find_def_insn_index_of_arg(index, insn, argnum=0)
136
+ if index
137
+ elsif insn
138
+ end
139
+ end
140
+ EOF
141
+ }
142
+ let(:dom){FlawDetector::parse(code)}
143
+ it {expect(nvd.analyze(dom)).to eq(flaws_from_comment("<compiled>", code))}
144
+ end
145
+ context "no RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE" do
146
+ let(:code){
147
+ <<EOF
148
+ namespace = "hogehoge"
149
+ unnamespaced = self.sub(/^::/, '') if namespace
150
+ param_key = (namespace ? _singularize(unnamespaced) : singular).freeze
151
+ EOF
152
+ }
153
+ let(:dom){FlawDetector::parse(code)}
154
+ it {expect(nvd.analyze(dom)).to eq(flaws_from_comment("<compiled>", code))}
155
+ end
156
+ context "no undefined method" do
157
+ let(:code){
158
+ <<EOF
159
+ def _parse_validates_options(options) #:nodoc:
160
+ case options
161
+ when TrueClass
162
+ {}
163
+ when Hash
164
+ options
165
+ when Range, Array
166
+ { :in => options }
167
+ else
168
+ { :with => options }
169
+ end
170
+ end
171
+ EOF
172
+ }
173
+ let(:dom){FlawDetector::parse(code)}
174
+ it {expect(nvd.analyze(dom)).to eq(flaws_from_comment("<compiled>", code))}
175
+ end
176
+
177
+ context "no undefined method" do
178
+ let(:code){
179
+ <<-'EOF'
180
+ def human_attribute_name(attribute, options = {})
181
+ defaults = []
182
+ parts = attribute.to_s.split(".")
183
+ attribute = parts.pop
184
+ namespace = parts.join("/") unless parts.empty?
185
+
186
+ if namespace
187
+ lookup_ancestors.each do |klass|
188
+ defaults << :"#{self.i18n_scope}.attributes.#{klass.model_name.i18n_key}/#{namespace}.#{attribute}"
189
+ end
190
+ else
191
+ lookup_ancestors.each do |klass|
192
+ defaults << :"#{self.i18n_scope}.attributes.#{klass.model_name.i18n_key}.#{attribute}"
193
+ end
194
+ end
195
+
196
+ defaults << options.delete(:default) if options[:default]
197
+ end
198
+ EOF
199
+ }
200
+ let(:dom){FlawDetector::parse(code)}
201
+ it {expect(nvd.analyze(dom)).to eq(flaws_from_comment("<compiled>", code))}
202
+ end
203
+ end
204
+ end
205
+ describe "::CodeModel::CodeDocument" do
206
+ context "one branch code in a method" do
207
+ before {
208
+ code =<<EOF
209
+ def nil_var_ref(a)
210
+ if a
211
+ i = 1
212
+ else
213
+ # i's type is Nil
214
+ puts(i + 1)
215
+ end
216
+ return i
217
+ end
218
+ nil_var_ref(1)
219
+ EOF
220
+ @dom = FlawDetector::parse(code)
221
+ }
222
+
223
+ it 'should have no branch in the top frame' do
224
+ cfg = @dom.root.entry_cfg_node
225
+ branches_cnt = 0
226
+ cfg.each do |name, node|
227
+ if node.next_nodes.count >= 2
228
+ branches_cnt += 1
229
+ end
230
+ end
231
+ expect(branches_cnt).to eq(0)
232
+ end
233
+
234
+ it 'should have one branch in the method frame' do
235
+ cfg = @dom.select_methods("nil_var_ref").first.entry_cfg_node
236
+ branches_cnt = 0
237
+ cfg.each do |name, node|
238
+ if node.next_nodes.count >= 2
239
+ branches_cnt += 1
240
+ end
241
+ end
242
+ expect(branches_cnt).to eq(1)
243
+ end
244
+ end
245
+
246
+ context "two branches code in a block in a method" do
247
+ before {
248
+ code =<<EOF
249
+ def one_branch_in_block
250
+ puts("aaa")
251
+ 1.upto(3) do |num| #a branch
252
+ if num > 2 #a branch
253
+ puts(num)
254
+ end
255
+ end
256
+ puts("bbb")
257
+ end
258
+ EOF
259
+ @dom = FlawDetector::parse(code)
260
+ }
261
+
262
+ it 'should have no branch in the top frame' do
263
+ cfg = @dom.root.entry_cfg_node
264
+ branches_cnt = 0
265
+ cfg.each do |name, node|
266
+ if node.next_nodes.count >= 2
267
+ branches_cnt += 1
268
+ end
269
+ end
270
+ expect(branches_cnt).to eq(0)
271
+ end
272
+
273
+ it 'should have two branches in the method frame' do
274
+ cfg = @dom.select_methods("one_branch_in_block").first.entry_cfg_node
275
+ branches_cnt = 0
276
+ cfg.each do |name, node|
277
+ if node.next_nodes.count >= 2
278
+ branches_cnt += 1
279
+ end
280
+ end
281
+ expect(branches_cnt).to eq(2)
282
+ end
283
+
284
+ it 'each send instruction has obj of [:putspecialobject,1]' do
285
+ block = @dom.root
286
+ block.raw_block_data[:body][:insns].each_with_index do |insn, index|
287
+ next unless insn[0] == :send
288
+ obj_index,argnum = block.find_insn_index_of_send_obj(index, insn)
289
+ obj_insn = block.raw_block_data[:body][:insns][obj_index]
290
+ end
291
+ end
292
+
293
+ it 'each send instruction has obj of [:putnil] or [:putobject,1]' do
294
+ block = @dom.select_methods("one_branch_in_block").first
295
+ block.raw_block_data[:body][:insns].each_with_index do |insn, index|
296
+ next unless insn[0] == :send
297
+ obj_index,argnum = block.find_insn_index_of_send_obj(index, insn)
298
+ obj_insn = block.raw_block_data[:body][:insns][obj_index]
299
+ end
300
+ end
301
+ end
302
+
303
+ context "one branch code in a proc in a method" do
304
+ before {
305
+ code =<<-EOF
306
+ def one_branch_in_block
307
+ pr = proc {|num| p num} #a branch
308
+ 1.upto(3,&pr)
309
+ puts("bbb")
310
+ end
311
+ EOF
312
+ @dom = FlawDetector::parse(code)
313
+ }
314
+
315
+ it 'should have no branch in the top frame' do
316
+ cfg = @dom.root.entry_cfg_node
317
+ branches_cnt = 0
318
+ cfg.each do |name, node|
319
+ if node.next_nodes.count >= 2
320
+ branches_cnt += 1
321
+ end
322
+ end
323
+ expect(branches_cnt).to eq(0)
324
+ end
325
+
326
+ it 'should have one branch in the method frame' do
327
+ cfg = @dom.select_methods("one_branch_in_block").first.entry_cfg_node
328
+ branches_cnt = 0
329
+ cfg.each do |name, node|
330
+ if node.next_nodes.count >= 2
331
+ branches_cnt += 1
332
+ end
333
+ end
334
+ expect(branches_cnt).to eq(1)
335
+ end
336
+
337
+ it 'each send instruction has obj of [:putnil] or [:putobject,1]' do
338
+ block = @dom.select_methods("one_branch_in_block").first
339
+ block.raw_block_data[:body][:insns].each_with_index do |insn, index|
340
+ next unless insn[0] == :send
341
+ obj_index,argnum = block.find_insn_index_of_send_obj(index, insn)
342
+ obj_insn = block.raw_block_data[:body][:insns][obj_index]
343
+ end
344
+ end
345
+
346
+ end
347
+
348
+ context "two branches code in a block in a method of SomeClass" do
349
+ before {
350
+ code =<<EOF
351
+ class SomeClass
352
+ def one_branch_in_block
353
+ puts("aaa")
354
+ 1.upto(3) do |num| #a branch
355
+ if num > 2 #a branch
356
+ puts(num)
357
+ end
358
+ end
359
+ puts("bbb")
360
+ end
361
+ end
362
+ EOF
363
+ @dom = FlawDetector::parse(code)
364
+ }
365
+
366
+ it 'should have no branch in the top frame' do
367
+ cfg = @dom.root.entry_cfg_node
368
+ branches_cnt = 0
369
+ cfg.each do |name, node|
370
+ if node.next_nodes.count >= 2
371
+ branches_cnt += 1
372
+ end
373
+ end
374
+ expect(branches_cnt).to eq(0)
375
+ end
376
+
377
+ it 'each send instruction has obj of [:putspecialobject,1]' do
378
+ block = @dom.select_class("SomeClass")
379
+ block.raw_block_data[:body][:insns].each_with_index do |insn, index|
380
+ next unless insn[0] == :send
381
+ obj_index,argnum = block.find_insn_index_of_send_obj(index, insn)
382
+ obj_insn = block.raw_block_data[:body][:insns][obj_index]
383
+ end
384
+ end
385
+ end
386
+
387
+ context "two branches and a throw for return in a block in a method" do
388
+ before {
389
+ code =<<EOF
390
+ def one_branch_in_block
391
+ puts("aaa")
392
+ 1.upto(3) do |num| #a branch
393
+ if num > 2 #a branch
394
+ puts(num)
395
+ else
396
+ return #throw
397
+ end
398
+ end
399
+ puts("bbb")
400
+ end
401
+ EOF
402
+ @dom = FlawDetector::parse(code)
403
+ }
404
+
405
+ it 'should have two branches in the method frame'
406
+ it 'should have outer in the break\'s basic block'
407
+ end
408
+
409
+ context "two branches and a throw for break code in a block in a method" do
410
+ before {
411
+ code =<<EOF
412
+ def one_branch_in_block
413
+ puts("aaa")
414
+ 1.upto(3) do |num| #a branch
415
+ if num > 2 #a branch
416
+ puts(num)
417
+ else
418
+ break #throw
419
+ end
420
+ end
421
+ puts("bbb")
422
+ end
423
+ EOF
424
+ @dom = FlawDetector::parse(code)
425
+ }
426
+
427
+ it 'should have two branches in the cfg'
428
+ it 'should have outer in the break\'s basic block'
429
+ end
430
+
431
+ end
432
+
433
+ USE_OPS = [:getlocal, :getspecial, :getdynamic, :getinstancevariable, :getclassvariable, :getconstant, :getglobal]
434
+ describe "::InsnBlock" do
435
+ context "one branch code in a method" do
436
+ before {
437
+ code =<<EOF
438
+ def nil_var_ref(a)
439
+ if a # use at :branchif and arg0
440
+ rl = a + 1 #*flaw*
441
+ rl = 1 + a #*flaw*
442
+ elsif a.nil? #use at send and arg0
443
+ rl = a + 1 #*flaw*
444
+ rl = 1 + a #*flaw*
445
+ else
446
+ rl = 1 + a #use at optplus
447
+ rl = a + 1 #use at optplus
448
+ end
449
+ some_method(1,2,a) # use at send and arg3
450
+ a.times do |i| #*flaw*
451
+ puts(i)
452
+ end
453
+ return i
454
+ end
455
+ nil_var_ref(1)
456
+ EOF
457
+ @dom = FlawDetector::parse(code)
458
+ }
459
+ it 'use def' do
460
+ block = @dom.select_methods("nil_var_ref").first
461
+ block.raw_block_data[:body][:insns].each_with_index do |insn, index|
462
+ next unless USE_OPS.include?(insn[0])
463
+ obj_index,argnum = block.find_use_insn_index_of_ret(index, insn)
464
+ obj_insn = block.raw_block_data[:body][:insns][obj_index]
465
+ robj_index,retnum = block.find_def_insn_index_of_arg(obj_index, obj_insn, argnum)
466
+ robj_insn = block.raw_block_data[:body][:insns][robj_index]
467
+ expect(robj_index).to eq(index)
468
+ end
469
+ a = block.domtree
470
+ a.each do |key, val|
471
+ next unless val
472
+ #p "node: #{key}, idom: #{val.name}"
473
+ #p val.dominators.count
474
+ end
475
+ end
476
+
477
+ end
478
+
479
+ context "two branches code in a block in a method of SomeClass" do
480
+ before {
481
+ code =<<EOF
482
+ class SomeClass
483
+ def one_branch_in_block
484
+ puts("aaa")
485
+ 1.upto(3) do |num| #a branch
486
+ if num > 2 #a branch
487
+ puts(num)
488
+ end
489
+ end
490
+ puts("bbb")
491
+ end
492
+ end
493
+ obj = SomeClass.new
494
+ EOF
495
+ @dom = FlawDetector::parse(code)
496
+ }
497
+
498
+ it 'should have no branch in the top frame' do
499
+ block = @dom.root
500
+ block.raw_block_data[:body][:insns].each_with_index do |insn, index|
501
+ next unless USE_OPS.include?(insn[0])
502
+ obj_index,argnum = block.find_use_insn_index_of_ret(index, insn)
503
+ obj_insn = block.raw_block_data[:body][:insns][obj_index]
504
+ robj_index,retnum = block.find_def_insn_index_of_arg(obj_index, obj_insn, argnum)
505
+ robj_insn = block.raw_block_data[:body][:insns][robj_index]
506
+ expect(robj_index).to eq(index)
507
+ end
508
+ end
509
+
510
+ it 'each send instruction has obj of [:putspecialobject,1]' do
511
+ block = @dom.select_class("SomeClass")
512
+ block.raw_block_data[:body][:insns].each_with_index do |insn, index|
513
+ next unless USE_OPS.include?(insn[0])
514
+ obj_index,argnum = block.find_use_insn_index_of_ret(index, insn)
515
+ obj_insn = block.raw_block_data[:body][:insns][obj_index]
516
+ robj_index,retnum = block.find_def_insn_index_of_arg(obj_index, obj_insn, argnum)
517
+ robj_insn = block.raw_block_data[:body][:insns][robj_index]
518
+
519
+ expect(robj_index).to eq(index)
520
+ end
521
+ end
522
+ end
523
+
524
+ end
525
+
526
+ end
@@ -0,0 +1,7 @@
1
+ require 'rspec'
2
+ require 'flaw_detector'
3
+
4
+ RSpec.configure do |config|
5
+ config.color_enabled = true
6
+ config.formatter = 'documentation'
7
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flaw_detector
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Rikiya Ayukawa
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2013-04-28 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ type: :development
25
+ version_requirements: *id001
26
+ description: The tool detects code's flaw, which should be fixed, with static analysis of RubyVM bytecode. Therefore, it works for only ruby 1.9.x. ASAP, I will support ruby 2.0.x .
27
+ email:
28
+ - dbc.ginriki@gmail.com
29
+ executables:
30
+ - flaw_detector
31
+ extensions:
32
+ - ext/insns_ext/extconf.rb
33
+ extra_rdoc_files: []
34
+
35
+ files:
36
+ - .gitignore
37
+ - .travis.yml
38
+ - Gemfile
39
+ - LICENSE
40
+ - README.md
41
+ - Rakefile
42
+ - bin/flaw_detector
43
+ - ext/insns_ext/extconf.rb
44
+ - ext/insns_ext/insn_ext.rb
45
+ - ext/insns_ext/insns.inc
46
+ - ext/insns_ext/insns_ext.c
47
+ - ext/insns_ext/insns_info.inc
48
+ - flaw_detector.gemspec
49
+ - lib/flaw_detector.rb
50
+ - lib/flaw_detector/code_model/cfg_node.rb
51
+ - lib/flaw_detector/code_model/code_document.rb
52
+ - lib/flaw_detector/code_model/insns_frame.rb
53
+ - lib/flaw_detector/controller.rb
54
+ - lib/flaw_detector/detector/abstract_detector.rb
55
+ - lib/flaw_detector/detector/nil_false_path_flow.rb
56
+ - lib/flaw_detector/formatter/csv_formatter.rb
57
+ - lib/flaw_detector/message.rb
58
+ - lib/flaw_detector/version.rb
59
+ - sample/flaw_in_code.rb
60
+ - spec/lib/flaw_detector_spec.rb
61
+ - spec/spec_helper.rb
62
+ homepage: ""
63
+ licenses: []
64
+
65
+ post_install_message:
66
+ rdoc_options: []
67
+
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 1.9.0
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: "0"
82
+ requirements: []
83
+
84
+ rubyforge_project: flaw detector
85
+ rubygems_version: 1.8.12
86
+ signing_key:
87
+ specification_version: 3
88
+ summary: The tool to detect code's flaw with static analysis
89
+ test_files:
90
+ - spec/lib/flaw_detector_spec.rb
91
+ - spec/spec_helper.rb
92
+ has_rdoc: