rite-fuzzr 0.9.10
Sign up to get free protection for your applications and to get access to all the features.
- 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 +80 -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
|