flaw_detector 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: