fuzzr 0.9.9
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of fuzzr might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.rdoc +28 -0
- data/bin/fuzz +7 -0
- data/fuzzers/check_ace_error.excludes +5 -0
- data/fuzzers/check_ace_error.rb +37 -0
- data/fuzzers/check_catch_ex_as_const.rb +41 -0
- data/fuzzers/check_cout_cerr.excludes +5 -0
- data/fuzzers/check_cout_cerr.rb +37 -0
- data/fuzzers/check_executablebit.rb +37 -0
- data/fuzzers/check_exit_keyword.excludes +2 -0
- data/fuzzers/check_exit_keyword.rb +37 -0
- data/fuzzers/check_fileheader.excludes +5 -0
- data/fuzzers/check_fileheader.rb +92 -0
- data/fuzzers/check_filename.excludes +8 -0
- data/fuzzers/check_filename.rb +33 -0
- data/fuzzers/check_id_tag.rb +34 -0
- data/fuzzers/check_new_delete.excludes +6 -0
- data/fuzzers/check_new_delete.rb +37 -0
- data/fuzzers/check_printf_keyword.excludes +2 -0
- data/fuzzers/check_printf_keyword.rb +37 -0
- data/fuzzers/check_taox11_namespace.excludes +5 -0
- data/fuzzers/check_taox11_namespace.rb +38 -0
- data/lib/fuzz/console.rb +52 -0
- data/lib/fuzz/fuzz.rb +399 -0
- data/lib/fuzz/fuzzers/check_whitespace.rb +62 -0
- data/lib/fuzz/fzzr.rb +258 -0
- data/lib/fuzz/log.rb +97 -0
- data/lib/fuzz/options.rb +208 -0
- data/lib/fuzz/screen.rb +85 -0
- data/lib/fuzz/system.rb +57 -0
- data/lib/fuzz/version.rb +18 -0
- metadata +81 -0
data/lib/fuzz/fuzz.rb
ADDED
@@ -0,0 +1,399 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# -------------------------------------------------------------------
|
3
|
+
# fuzz.rb - TAOX11 fuzz checker
|
4
|
+
#
|
5
|
+
# Author: Martin Corino
|
6
|
+
#
|
7
|
+
# Copyright (c) Remedy IT Expertise BV
|
8
|
+
# -------------------------------------------------------------------
|
9
|
+
|
10
|
+
require 'optparse'
|
11
|
+
require 'tempfile'
|
12
|
+
require 'fileutils'
|
13
|
+
require 'yaml'
|
14
|
+
require 'fuzz/log'
|
15
|
+
require 'fuzz/system'
|
16
|
+
require 'fuzz/options'
|
17
|
+
require 'fuzz/fzzr'
|
18
|
+
require 'fuzz/version'
|
19
|
+
|
20
|
+
module Fuzz
|
21
|
+
|
22
|
+
def self.root_path
|
23
|
+
f = File.expand_path(__FILE__)
|
24
|
+
f = File.expand_path(File.readlink(f)) if File.symlink?(f)
|
25
|
+
File.dirname(f)
|
26
|
+
end
|
27
|
+
|
28
|
+
FUZZ_ROOT = self.root_path
|
29
|
+
|
30
|
+
class << self
|
31
|
+
|
32
|
+
include LogMethods
|
33
|
+
include Sys::SysMethods
|
34
|
+
|
35
|
+
def reporter
|
36
|
+
@reporter ||= Fuzz::Reporter.new
|
37
|
+
end
|
38
|
+
|
39
|
+
def set_reporter(rep)
|
40
|
+
@reporter = rep
|
41
|
+
end
|
42
|
+
alias :reporter= :set_reporter
|
43
|
+
|
44
|
+
def options
|
45
|
+
Fuzz::OPTIONS
|
46
|
+
end
|
47
|
+
|
48
|
+
def reset
|
49
|
+
options.reset
|
50
|
+
end
|
51
|
+
|
52
|
+
def load_config
|
53
|
+
options.load_config
|
54
|
+
end
|
55
|
+
|
56
|
+
def includes
|
57
|
+
unless @include_re
|
58
|
+
@include_re = []
|
59
|
+
# @include_re << "\\.(#{Fuzz::FileObject.extensions.join('|')})$"
|
60
|
+
# @include_re << "#{Fuzz::FileObject.filenames.join('|')}$"
|
61
|
+
@include_re << "\\.(#{options.config[:exts].join('|')})$"
|
62
|
+
@include_re << "#{options.config[:filenames].join('|')}$"
|
63
|
+
end
|
64
|
+
@include_re
|
65
|
+
end
|
66
|
+
|
67
|
+
#
|
68
|
+
# fuzzer registration
|
69
|
+
#
|
70
|
+
|
71
|
+
def fuzzers
|
72
|
+
@fuzzers ||= {}
|
73
|
+
end
|
74
|
+
|
75
|
+
def register_fzzr(fzzr)
|
76
|
+
raise "Duplicate fuzzer registration: #{fzzr.fuzz_id}" if fuzzers.has_key?(fzzr.fuzz_id)
|
77
|
+
fuzzers[fzzr.fuzz_id] = fzzr
|
78
|
+
fzzr.setup(options.optparser) if options.optparser && fzzr.respond_to?(:setup) && fzzr_included?(fzzr)
|
79
|
+
end
|
80
|
+
|
81
|
+
def get_fzzr(id)
|
82
|
+
fuzzers[id]
|
83
|
+
end
|
84
|
+
|
85
|
+
def fzzr_excluded?(fzzr)
|
86
|
+
options.config[:fzzr_excludes].include?(fzzr.fuzz_id.to_sym)
|
87
|
+
end
|
88
|
+
|
89
|
+
def fzzr_included?(fzzr)
|
90
|
+
!fzzr_excluded?(fzzr)
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
# load fuzzers
|
95
|
+
#
|
96
|
+
def load_fuzzers
|
97
|
+
# standard fuzzers included in Gem
|
98
|
+
unless loaded_fzzr_paths.include?(_p = File.join(FUZZ_ROOT, 'fuzzers'))
|
99
|
+
Dir.glob(File.join(_p, '*.rb')).each do |fnm|
|
100
|
+
require fnm
|
101
|
+
end
|
102
|
+
loaded_fzzr_paths << _p
|
103
|
+
end
|
104
|
+
# configured fuzzers
|
105
|
+
options.config[:fzzr_paths].each do |fzzrpath|
|
106
|
+
unless loaded_fzzr_paths.include?(_p = File.expand_path(fzzrpath))
|
107
|
+
Dir.glob(File.join(_p, '*.rb')).each do |fnm|
|
108
|
+
require fnm
|
109
|
+
end
|
110
|
+
loaded_fzzr_paths << _p
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def loaded_fzzr_paths
|
118
|
+
@loaded_fuzzr_paths ||= []
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
## Backwards compatibility
|
124
|
+
def self.log_verbose(msg)
|
125
|
+
log_info(msg) if verbose?
|
126
|
+
end
|
127
|
+
|
128
|
+
#
|
129
|
+
# Option methods
|
130
|
+
#
|
131
|
+
|
132
|
+
def self.verbosity
|
133
|
+
options.verbose
|
134
|
+
end
|
135
|
+
|
136
|
+
def self.apply_fix?
|
137
|
+
options.apply_fix || false
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.follow_symlink?
|
141
|
+
options.config[:follow_symlink] || false
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.excludes
|
145
|
+
options.config[:excludes] || []
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.excluded?(object)
|
149
|
+
excludes.any? { |excl| (object.fullpath =~ /#{excl}/) }
|
150
|
+
end
|
151
|
+
|
152
|
+
#
|
153
|
+
# parse commandline arguments
|
154
|
+
#
|
155
|
+
def self.init_optparser
|
156
|
+
script_name = File.basename($0)
|
157
|
+
if not script_name =~ /fuzz/
|
158
|
+
script_name = "ruby "+$0
|
159
|
+
end
|
160
|
+
|
161
|
+
options.optparser = opts = OptionParser.new
|
162
|
+
opts.banner = "Usage: #{script_name} [options] [glob [glob]]\n\n"
|
163
|
+
opts.separator "\n--- [General options] ---\n\n"
|
164
|
+
opts.on('-t', '--filetype', '=EXT', String,
|
165
|
+
'Defines an alternative filetype to search and scan. Can be specified multiple times.',
|
166
|
+
"Default: #{Fuzz::FileObject.extensions.join('|')}") { |v|
|
167
|
+
(options.user_config[:exts] ||= []) << v
|
168
|
+
}
|
169
|
+
opts.on('-f', '--file', '=NAME', String,
|
170
|
+
'Defines an alternative filename to search and scan. Can be specified multiple times.',
|
171
|
+
"Default: #{Fuzz::FileObject.filenames.join('|')}") { |v|
|
172
|
+
(options.user_config[:filenames] ||= []) << v
|
173
|
+
}
|
174
|
+
opts.on('-a', '--add-files',
|
175
|
+
'Add custom filenames and/or filetype extensions to default list instead of replacing defaults.',
|
176
|
+
'Default: false') { |v|
|
177
|
+
options.user_config[:add_files] = true
|
178
|
+
}
|
179
|
+
opts.on('-S', '--no-symlinks',
|
180
|
+
'Do not follow symlinks.',
|
181
|
+
'Default: follow symlinks') { |v|
|
182
|
+
options.user_config[:follow_symlink] = false
|
183
|
+
}
|
184
|
+
opts.on('-P', '--fzzr-path', '=PATH',
|
185
|
+
'Adds search path for Fuzzers.',
|
186
|
+
"Default: loaded from ~/#{FUZZRC} and/or ./#{FUZZRC}") { |v|
|
187
|
+
(options.user_config[:fzzr_paths] ||= []) << v.to_s
|
188
|
+
}
|
189
|
+
opts.on('-B', '--blacklist', '=FZZRID',
|
190
|
+
'Adds Fuzzer ID to list of fuzzers to exclude from Fuzz check.',
|
191
|
+
'Default: none') { |v|
|
192
|
+
(options.user_config[:fzzr_excludes] ||= []) << v.to_sym
|
193
|
+
}
|
194
|
+
opts.on('-X', '--exclude', '=MASK',
|
195
|
+
'Adds path mask (regular expression) to list to exclude from Fuzz check.',
|
196
|
+
'Default: none') { |v|
|
197
|
+
(options.user_config[:excludes] ||= []) << v
|
198
|
+
}
|
199
|
+
opts.on('-c', '--config', '=FUZZRC',
|
200
|
+
'Load config from FUZZRC file.',
|
201
|
+
"Default: ~/#{FUZZRC} and/or ./#{FUZZRC}") { |v|
|
202
|
+
options.add_config(v)
|
203
|
+
}
|
204
|
+
opts.on('--write-config', '=[FUZZRC]',
|
205
|
+
'Write config to file and exit.',
|
206
|
+
"Default: ./#{FUZZRC}") { |v|
|
207
|
+
options.user_config.save(String === v ? v : FUZZRC)
|
208
|
+
exit
|
209
|
+
}
|
210
|
+
opts.on('--show-config',
|
211
|
+
'Display config settings and exit.') { |v|
|
212
|
+
options.load_config
|
213
|
+
puts YAML.dump(options.config.__send__ :table)
|
214
|
+
exit
|
215
|
+
}
|
216
|
+
|
217
|
+
opts.separator ''
|
218
|
+
opts.on('-o', '--output', '=FILE', String,
|
219
|
+
'Specifies filename to write Fuzz messages to.',
|
220
|
+
'Default: stderr') { |v|
|
221
|
+
options.output = v
|
222
|
+
}
|
223
|
+
opts.on('-p', '--apply-fix',
|
224
|
+
'Apply fixes (if any) for Fuzz errors.',
|
225
|
+
'Default: false') { |v|
|
226
|
+
options.apply_fix = true
|
227
|
+
}
|
228
|
+
opts.on('-n', '--no-recurse',
|
229
|
+
'Prevents directory recursion in file selection.',
|
230
|
+
'Default: recurse') { |v|
|
231
|
+
options.recurse = false
|
232
|
+
}
|
233
|
+
opts.on('-v', '--verbose',
|
234
|
+
'Run with increased verbosity level. Repeat to increase more.',
|
235
|
+
'Default: 1') { |v| options.verbose += 1 }
|
236
|
+
|
237
|
+
opts.separator ''
|
238
|
+
opts.on('-L', '--list',
|
239
|
+
'List available Fuzzers and exit.') {
|
240
|
+
options.load_config
|
241
|
+
load_fuzzers
|
242
|
+
puts "TAOX11 fuzz checker #{FUZZ_VERSION_MAJOR}.#{FUZZ_VERSION_MINOR}.#{FUZZ_VERSION_RELEASE}"
|
243
|
+
puts FUZZ_COPYRIGHT
|
244
|
+
puts('%-30s %s' % %w{Fuzzer Description})
|
245
|
+
puts(('-' * 30)+' '+('-' * 48))
|
246
|
+
fuzzers.values.each { |fzzr| puts('%-30s %s' % [fzzr.fuzz_id, fzzr.description]) }
|
247
|
+
puts
|
248
|
+
exit
|
249
|
+
}
|
250
|
+
|
251
|
+
opts.separator ""
|
252
|
+
opts.on('-V', '--version',
|
253
|
+
'Show version information and exit.') {
|
254
|
+
puts "TAOX11 fuzz checker #{FUZZ_VERSION_MAJOR}.#{FUZZ_VERSION_MINOR}.#{FUZZ_VERSION_RELEASE}"
|
255
|
+
puts FUZZ_COPYRIGHT
|
256
|
+
exit
|
257
|
+
}
|
258
|
+
opts.on('-h', '--help',
|
259
|
+
'Show this help message.') {
|
260
|
+
options.load_config
|
261
|
+
load_fuzzers
|
262
|
+
puts opts;
|
263
|
+
puts;
|
264
|
+
exit
|
265
|
+
}
|
266
|
+
|
267
|
+
opts.separator "\n--- [Fuzzer options] ---\n\n"
|
268
|
+
end
|
269
|
+
|
270
|
+
def self.parse_args(argv)
|
271
|
+
options.optparser.parse!(argv)
|
272
|
+
end
|
273
|
+
|
274
|
+
def self.select_fzzrs(object)
|
275
|
+
fuzzers.values.collect { |fzzr| (fzzr_included?(fzzr) && fzzr.applies_to?(object)) ? fzzr : nil }.compact
|
276
|
+
end
|
277
|
+
|
278
|
+
def self.update_file_object(fo)
|
279
|
+
if File.writable?(fo.fullpath)
|
280
|
+
log_verbose(%Q{Updating #{fo}...})
|
281
|
+
ftmp = Tempfile.new(fo.name)
|
282
|
+
log_verbose(%Q{+ Writing temp file #{ftmp.path}...})
|
283
|
+
fo.lines.each { |ln| ftmp.print ln }
|
284
|
+
ftmp.close(false) # close but do NOT unlink
|
285
|
+
log_verbose(%Q{+ Replacing #{fo} with #{ftmp.path}})
|
286
|
+
# create temporary backup
|
287
|
+
ftmp2 = Tempfile.new(fo.name)
|
288
|
+
ftmp2_name = ftmp2.path.dup
|
289
|
+
ftmp2.close(true)
|
290
|
+
mv(fo.fullpath, ftmp2_name)
|
291
|
+
# replace original
|
292
|
+
begin
|
293
|
+
mv(ftmp.path, fo.fullpath)
|
294
|
+
# preserve file mode
|
295
|
+
chmod(File.lstat(ftmp2_name).mode, fo.fullpath)
|
296
|
+
rescue
|
297
|
+
log_error(%Q{FAILED updating #{fo}: #{$!}})
|
298
|
+
# restore backup
|
299
|
+
mv(ftmp2_name, fo.fullpath)
|
300
|
+
raise
|
301
|
+
end
|
302
|
+
# remove backup
|
303
|
+
File.unlink(ftmp2_name)
|
304
|
+
log_verbose(%Q{Finished updating #{fo}.})
|
305
|
+
return true
|
306
|
+
else
|
307
|
+
log_error(%Q{NO_ACCESS - cannot update #{fo}})
|
308
|
+
return false
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
def self.handle_object(object)
|
313
|
+
log_verbose(%Q{Handling #{object}})
|
314
|
+
fzzrs = select_fzzrs(object)
|
315
|
+
no_fixes_allowed = false
|
316
|
+
rc = fzzrs.inject(true) do |result, fzzr|
|
317
|
+
log_verbose(%Q{+ Running fuzzer #{fzzr.fuzz_id}})
|
318
|
+
begin
|
319
|
+
if fzzr.run(object, options.apply_fix)
|
320
|
+
result
|
321
|
+
else
|
322
|
+
log_verbose(%Q{+ Error from fuzzer #{fzzr.fuzz_id}})
|
323
|
+
false
|
324
|
+
end
|
325
|
+
rescue
|
326
|
+
log_error(%Q{EXCEPTION CAUGHT running fuzzer #{fzzr.fuzz_id} on #{object} - #{$!}\n#{$!.backtrace.join("\n")}})
|
327
|
+
no_fixes_allowed = true
|
328
|
+
break ## immediately stop handling this object, rc will remain false
|
329
|
+
end
|
330
|
+
end
|
331
|
+
unless no_fixes_allowed
|
332
|
+
if Fuzz.apply_fix? && object.changed?
|
333
|
+
rc = update_file_object(object) && rc
|
334
|
+
end
|
335
|
+
end
|
336
|
+
rc ? true : false
|
337
|
+
end
|
338
|
+
|
339
|
+
def self.iterate_paths(paths)
|
340
|
+
paths.inject(true) do |result, path|
|
341
|
+
if File.readable?(path) && (!File.symlink?(path) || follow_symlink?)
|
342
|
+
if File.directory?(path)
|
343
|
+
rc = handle_object(dirobj = Fuzz::DirObject.new(path))
|
344
|
+
log_verbose(%Q{Iterating #{path}})
|
345
|
+
if options.recurse && !excluded?(dirobj)
|
346
|
+
rc = iterate_paths(Dir.glob(File.join(path, '*'))) && rc
|
347
|
+
end
|
348
|
+
rc
|
349
|
+
elsif File.file?(path)
|
350
|
+
handle_object(Fuzz::FileObject.new(path))
|
351
|
+
else
|
352
|
+
true
|
353
|
+
end
|
354
|
+
else
|
355
|
+
log_warning(File.readable?(path) ? %Q{Cannot read #{path}} : %Q{Cannot follow symlink #{path}})
|
356
|
+
false
|
357
|
+
end && result
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
def self.run_fzzrs(argv)
|
362
|
+
options.config[:exts].concat(Fuzz::FileObject.extensions) if options.config[:exts].empty? || options.config[:add_files]
|
363
|
+
options.config[:filenames].concat(Fuzz::FileObject.filenames) if options.config[:filenames].empty? || options.config[:add_files]
|
364
|
+
|
365
|
+
options.config[:exts].uniq!
|
366
|
+
options.config[:filenames].uniq!
|
367
|
+
|
368
|
+
f_close_output = false
|
369
|
+
if String === options.output
|
370
|
+
options.output = File.open(options.output, 'w')
|
371
|
+
f_close_output = true
|
372
|
+
end
|
373
|
+
begin
|
374
|
+
# determin files/paths to test
|
375
|
+
paths = argv.collect { |a| Dir.glob(a) }.flatten.uniq
|
376
|
+
paths = Dir.glob('*') if paths.empty?
|
377
|
+
# scan all determined objects
|
378
|
+
return iterate_paths(paths)
|
379
|
+
ensure
|
380
|
+
options.output.close if f_close_output
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
def self.run
|
385
|
+
init_optparser
|
386
|
+
|
387
|
+
# parse arguments
|
388
|
+
parse_args(ARGV)
|
389
|
+
|
390
|
+
# load config (if any)
|
391
|
+
options.load_config
|
392
|
+
|
393
|
+
# load fuzzers
|
394
|
+
load_fuzzers
|
395
|
+
|
396
|
+
run_fzzrs(ARGV)
|
397
|
+
end
|
398
|
+
|
399
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# -------------------------------------------------------------------
|
3
|
+
# check_whitespace.rb - TAOX11 whitespace checker
|
4
|
+
#
|
5
|
+
# Author: Martin Corino
|
6
|
+
#
|
7
|
+
# Copyright (c) Remedy IT Expertise BV
|
8
|
+
# -------------------------------------------------------------------
|
9
|
+
|
10
|
+
module Fuzzers
|
11
|
+
class WhitespaceChecker
|
12
|
+
include Fuzz::Fzzr
|
13
|
+
def initialize
|
14
|
+
@fuzz_id = :check_whitespace
|
15
|
+
@description = 'checks for trailing whitespace, incorrect line endings and tabs'
|
16
|
+
end
|
17
|
+
|
18
|
+
def setup(optparser)
|
19
|
+
optparser.on('--wsc:tab-spacing=NUM', Integer,
|
20
|
+
'Fuzzers::WhitespaceChecker - defines tab spacing to use for TAB replacement when --apply-fix is enabled.',
|
21
|
+
"Default: #{tab_spacing}") {|v| self.options[:tabspacing] = v }
|
22
|
+
end
|
23
|
+
|
24
|
+
def applies_to?(object)
|
25
|
+
Fuzz::FileObject === object && !is_excluded?(object)
|
26
|
+
end
|
27
|
+
|
28
|
+
def run(object, apply_fix)
|
29
|
+
_tws = []
|
30
|
+
_tabs = []
|
31
|
+
object.iterate(fuzz_id) do |lnptr|
|
32
|
+
if lnptr.text =~ /(\s\n|[\ \t\f\r\x0B])\Z/
|
33
|
+
if apply_fix
|
34
|
+
Fuzz.log_verbose(%Q{#{object.path}:#{lnptr.line_nr} - stripping trailing whitespace})
|
35
|
+
lnptr.text.rstrip!
|
36
|
+
lnptr.text << "\n" if $1.end_with?("\n")
|
37
|
+
else
|
38
|
+
_tws << lnptr.line_nr
|
39
|
+
end
|
40
|
+
end
|
41
|
+
if lnptr.text =~ /\t/
|
42
|
+
if apply_fix
|
43
|
+
Fuzz.log_warning(%Q{#{object.path}:#{lnptr.line_nr} - replacing tabs})
|
44
|
+
lnptr.text.gsub!(/\t/, ' ' * tab_spacing)
|
45
|
+
else
|
46
|
+
_tabs << lnptr.line_nr
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
Fuzz.log_error(%Q{#{object.path}:[#{_tws.join(',')}] trailing whitespace or incorrect line ending detected}) unless _tws.empty?
|
51
|
+
Fuzz.log_error(%Q{#{object.path}:[#{_tabs.join(',')}] tab(s) detected}) unless _tabs.empty?
|
52
|
+
return (_tws.empty? && _tabs.empty?)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def tab_spacing
|
57
|
+
self.options[:tabspacing] || 2
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
Fuzz.register_fzzr(WhitespaceChecker.new)
|
62
|
+
end
|
data/lib/fuzz/fzzr.rb
ADDED
@@ -0,0 +1,258 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# -------------------------------------------------------------------
|
3
|
+
# fuzzr.rb - TAOX11 Fuzzer bases
|
4
|
+
#
|
5
|
+
# Author: Martin Corino
|
6
|
+
#
|
7
|
+
# Copyright (c) Remedy IT Expertise BV
|
8
|
+
# -------------------------------------------------------------------
|
9
|
+
|
10
|
+
module Fuzz
|
11
|
+
##
|
12
|
+
# Fuzzers are objects having the following readonly attributes:
|
13
|
+
# #fuzz_id : id of fuzzer (Symbol)
|
14
|
+
# #description: (String)
|
15
|
+
# #errormsg : (String)
|
16
|
+
#
|
17
|
+
# and having the following methods:
|
18
|
+
# #applies_to?(object) : checks if the test applies to the object passed
|
19
|
+
# (Fuzz::DirObject or Fuzz::FileObject)
|
20
|
+
# #run(object,apply_fix): runs the fzzr test on the object passed
|
21
|
+
# (Fuzz::DirObject or Fuzz::FileObject)
|
22
|
+
# when apply_fix == true Fuzzer is directed to
|
23
|
+
# attempt to fix any problems found (future)
|
24
|
+
#
|
25
|
+
# Fuzz::Fzzr is provided as a convenience Mixin for fuzzers
|
26
|
+
#
|
27
|
+
# Fuzzers can inspect the options passed to fuzz.rb by referencing Fuzz::OPTIONS
|
28
|
+
##
|
29
|
+
|
30
|
+
module Fzzr
|
31
|
+
attr_reader :fuzz_id, :description, :errormsg
|
32
|
+
|
33
|
+
def applies_to?(object)
|
34
|
+
!is_excluded?(object)
|
35
|
+
end
|
36
|
+
|
37
|
+
def setup(optparser)
|
38
|
+
end
|
39
|
+
|
40
|
+
def run(object, apply_fix)
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
def is_excluded?(object)
|
45
|
+
# force excludes to be parsed
|
46
|
+
_excludes
|
47
|
+
# now examine
|
48
|
+
((!_is_included?(object)) || _excludes.any? { |excl| (object.fullpath =~ /#{excl}/) })
|
49
|
+
end
|
50
|
+
|
51
|
+
def options
|
52
|
+
Fuzz::OPTIONS[:config][:fzzr_opts][self.fuzz_id] ||= {}
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def _is_included?(object)
|
58
|
+
Fuzz::DirObject === object || _includes.empty? || _includes.any? { |incl| (object.fullpath =~ /#{incl}/) }
|
59
|
+
end
|
60
|
+
|
61
|
+
def _includes
|
62
|
+
@_includes ||= Fuzz.includes.dup
|
63
|
+
end
|
64
|
+
|
65
|
+
def _excludes
|
66
|
+
unless @_excludes
|
67
|
+
@_excludes = []
|
68
|
+
@_excludes.concat(Fuzz.excludes)
|
69
|
+
Fuzz::OPTIONS[:config][:fzzr_paths].each do |fzzrpath|
|
70
|
+
fzzr_excl_file = File.join(fzzrpath, "#{self.fuzz_id}.excludes")
|
71
|
+
if File.readable?(fzzr_excl_file)
|
72
|
+
lns = IO.readlines(fzzr_excl_file).collect { |l| l.strip }
|
73
|
+
@_excludes.concat(lns.select {|l| !(l.empty? || l[0] == '!') })
|
74
|
+
_includes.concat(lns.select {|l| !(l.empty? || l[0] != '!') }.collect {|l| l[1,l.size].strip })
|
75
|
+
else
|
76
|
+
false
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
@_excludes
|
81
|
+
end
|
82
|
+
end # Fzzr
|
83
|
+
|
84
|
+
class DirObject
|
85
|
+
attr_reader :path, :fullpath, :name, :ext
|
86
|
+
def initialize(path)
|
87
|
+
@path = path
|
88
|
+
@fullpath = File.expand_path(path)
|
89
|
+
@name = File.basename(path)
|
90
|
+
@ext = File.extname(path).sub(/^\./,'')
|
91
|
+
end
|
92
|
+
|
93
|
+
def changed?
|
94
|
+
false
|
95
|
+
end
|
96
|
+
|
97
|
+
def iterate(fzzr_id, &block)
|
98
|
+
# nothing to iterate over
|
99
|
+
true
|
100
|
+
end
|
101
|
+
|
102
|
+
def to_s
|
103
|
+
"Dir:#{path}"
|
104
|
+
end
|
105
|
+
end # DirObject
|
106
|
+
|
107
|
+
class FileObject
|
108
|
+
EXTS = [
|
109
|
+
'h', 'hxx', 'hpp', 'c', 'cc', 'cxx', 'cpp', 'H', 'C', 'inl', 'asm',
|
110
|
+
'rb', 'erb', 'pl', 'pm', 'py',
|
111
|
+
'idl', 'pidl',
|
112
|
+
'mwc', 'mpc', 'mpb', 'mpt', 'mpd',
|
113
|
+
'cdp', 'xml', 'conf', 'html',
|
114
|
+
'asc', 'adoc'
|
115
|
+
]
|
116
|
+
FILES = [
|
117
|
+
'ChangeLog', 'README'
|
118
|
+
]
|
119
|
+
|
120
|
+
def self.extensions
|
121
|
+
EXTS
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.filenames
|
125
|
+
FILES
|
126
|
+
end
|
127
|
+
|
128
|
+
class LinePointer
|
129
|
+
attr_reader :err_lines
|
130
|
+
|
131
|
+
FZZR_ENABLE_RE = /X11_FUZZ\: enable ([^\s]+)/
|
132
|
+
FZZR_DISABLE_RE = /X11_FUZZ\: disable ([^\s]+)/
|
133
|
+
|
134
|
+
def initialize(lines, fzzr_id)
|
135
|
+
@lines = lines
|
136
|
+
@fzzr_id = fzzr_id.to_s
|
137
|
+
@err_lines = []
|
138
|
+
reset
|
139
|
+
end
|
140
|
+
def fzzr_disabled?
|
141
|
+
@fzzr_disabled
|
142
|
+
end
|
143
|
+
def line_nr
|
144
|
+
@line_nr+1
|
145
|
+
end
|
146
|
+
def text_at(offs)
|
147
|
+
ln = @line_nr+offs
|
148
|
+
if ln>=0 && ln<@lines.size
|
149
|
+
return @lines[ln]
|
150
|
+
end
|
151
|
+
nil
|
152
|
+
end
|
153
|
+
def set_text_at(offs, txt)
|
154
|
+
ln = @line_nr+offs
|
155
|
+
if ln>=0 && ln<@lines.size
|
156
|
+
return (@lines[ln] = txt)
|
157
|
+
end
|
158
|
+
nil
|
159
|
+
end
|
160
|
+
def text
|
161
|
+
text_at(0)
|
162
|
+
end
|
163
|
+
def text=(txt)
|
164
|
+
set_text_at(0, txt)
|
165
|
+
end
|
166
|
+
def move(offs)
|
167
|
+
if offs < 0
|
168
|
+
_backward(-offs) unless bof?
|
169
|
+
else
|
170
|
+
_forward(offs) unless eof?
|
171
|
+
end
|
172
|
+
self.line_nr
|
173
|
+
end
|
174
|
+
def reset
|
175
|
+
@line_nr = 0
|
176
|
+
@fzzr_disabled = false
|
177
|
+
_check_fzzr_escape
|
178
|
+
end
|
179
|
+
def to_eof
|
180
|
+
_forward(@lines.size - @line_nr)
|
181
|
+
end
|
182
|
+
def eof?
|
183
|
+
@line_nr >= @lines.size
|
184
|
+
end
|
185
|
+
def bof?
|
186
|
+
@line_nr <= 0
|
187
|
+
end
|
188
|
+
def mark_error(ln = nil)
|
189
|
+
@err_lines << (ln || (@line_nr+1))
|
190
|
+
end
|
191
|
+
|
192
|
+
private
|
193
|
+
|
194
|
+
def _forward(distance)
|
195
|
+
distance.times do
|
196
|
+
@line_nr += 1
|
197
|
+
break if eof?
|
198
|
+
_check_fzzr_escape
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def _backward(distance)
|
203
|
+
distance.times do
|
204
|
+
break if bof?
|
205
|
+
@line_nr -= 1
|
206
|
+
_check_fzzr_escape(false)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def _check_fzzr_escape(forward = true)
|
211
|
+
begin
|
212
|
+
if FZZR_ENABLE_RE =~ @lines[@line_nr]
|
213
|
+
@fzzr_disabled = !forward if $1 == @fzzr_id
|
214
|
+
elsif FZZR_DISABLE_RE =~ @lines[@line_nr]
|
215
|
+
@fzzr_disabled = forward if $1 == @fzzr_id
|
216
|
+
end
|
217
|
+
rescue
|
218
|
+
Fuzz.log_error(%Q{ERROR: Exception while checking fzzr escapes in line #{@line_nr+1} - #{$!}\n#{@lines[@line_nr]}})
|
219
|
+
raise
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end # LinePointer
|
223
|
+
|
224
|
+
attr_reader :path, :fullpath, :name, :ext, :lines
|
225
|
+
|
226
|
+
def initialize(path)
|
227
|
+
@path = path
|
228
|
+
@fullpath = File.expand_path(path)
|
229
|
+
@name = File.basename(path)
|
230
|
+
@ext = File.extname(path).sub(/^\./,'')
|
231
|
+
@lines = nil
|
232
|
+
@pointer = nil
|
233
|
+
@changed = false
|
234
|
+
end
|
235
|
+
|
236
|
+
def changed?
|
237
|
+
@changed
|
238
|
+
end
|
239
|
+
|
240
|
+
def iterate(fzzr_id, &block)
|
241
|
+
@lines ||= IO.readlines(fullpath)
|
242
|
+
lines_copy = @lines.collect {|l| l.dup }
|
243
|
+
pointer = LinePointer.new(@lines, fzzr_id)
|
244
|
+
begin
|
245
|
+
block.call(pointer) unless pointer.fzzr_disabled?
|
246
|
+
pointer.move(1)
|
247
|
+
end while !pointer.eof?
|
248
|
+
Fuzz.log_error(%Q{#{self.path}[#{pointer.err_lines.join(',')}] #{Fuzz.get_fzzr(fzzr_id).errormsg}}) unless pointer.err_lines.empty?
|
249
|
+
@changed |= (@lines != lines_copy)
|
250
|
+
lines_copy = nil
|
251
|
+
return pointer.err_lines.empty?
|
252
|
+
end
|
253
|
+
|
254
|
+
def to_s
|
255
|
+
"File:#{fullpath}"
|
256
|
+
end
|
257
|
+
end # FileObject
|
258
|
+
end # Fuzz
|