From 31a66d9f48f4377be1e58e16653a40299f245cf0 Mon Sep 17 00:00:00 2001 From: Johnny Willemsen Date: Thu, 27 Feb 2020 11:33:03 +0100 Subject: [PATCH] Initial source release Co-authored-by: Johnny Willemsen Co-authored-by: Martin Corino --- .github/workflows/main.yml | 27 ++ .gitignore | 4 + LICENSE | 21 ++ README.rdoc | 28 ++ Rakefile | 13 + bin/fuzz | 11 + fuzzers/check_ace_error.excludes | 5 + fuzzers/check_ace_error.rb | 37 +++ fuzzers/check_catch_ex_as_const.rb | 41 +++ fuzzers/check_cout_cerr.excludes | 5 + fuzzers/check_cout_cerr.rb | 37 +++ fuzzers/check_executablebit.rb | 37 +++ fuzzers/check_exit_keyword.excludes | 2 + fuzzers/check_exit_keyword.rb | 37 +++ fuzzers/check_fileheader.excludes | 5 + fuzzers/check_fileheader.rb | 92 ++++++ fuzzers/check_filename.excludes | 8 + fuzzers/check_filename.rb | 33 ++ fuzzers/check_id_tag.excludes | 3 + fuzzers/check_id_tag.rb | 34 ++ fuzzers/check_new_delete.excludes | 6 + fuzzers/check_new_delete.rb | 37 +++ fuzzers/check_printf_keyword.excludes | 2 + fuzzers/check_printf_keyword.rb | 37 +++ fuzzers/check_taox11_namespace.excludes | 5 + fuzzers/check_taox11_namespace.rb | 38 +++ lib/fuzz.rb | 14 + lib/fuzz/console.rb | 52 +++ lib/fuzz/fuzz.rb | 402 ++++++++++++++++++++++++ lib/fuzz/fuzzers/check_whitespace.rb | 62 ++++ lib/fuzz/fzzr.rb | 258 +++++++++++++++ lib/fuzz/log.rb | 97 ++++++ lib/fuzz/options.rb | 208 ++++++++++++ lib/fuzz/screen.rb | 85 +++++ lib/fuzz/system.rb | 57 ++++ rakelib/gem.rake | 71 +++++ rakelib/help.rake | 34 ++ test/test_fuzz.rb | 8 + 38 files changed, 1953 insertions(+) create mode 100644 .github/workflows/main.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.rdoc create mode 100644 Rakefile create mode 100755 bin/fuzz create mode 100644 fuzzers/check_ace_error.excludes create mode 100644 fuzzers/check_ace_error.rb create mode 100644 fuzzers/check_catch_ex_as_const.rb create mode 100644 fuzzers/check_cout_cerr.excludes create mode 100644 fuzzers/check_cout_cerr.rb create mode 100644 fuzzers/check_executablebit.rb create mode 100644 fuzzers/check_exit_keyword.excludes create mode 100644 fuzzers/check_exit_keyword.rb create mode 100644 fuzzers/check_fileheader.excludes create mode 100644 fuzzers/check_fileheader.rb create mode 100644 fuzzers/check_filename.excludes create mode 100644 fuzzers/check_filename.rb create mode 100644 fuzzers/check_id_tag.excludes create mode 100644 fuzzers/check_id_tag.rb create mode 100644 fuzzers/check_new_delete.excludes create mode 100644 fuzzers/check_new_delete.rb create mode 100644 fuzzers/check_printf_keyword.excludes create mode 100644 fuzzers/check_printf_keyword.rb create mode 100644 fuzzers/check_taox11_namespace.excludes create mode 100644 fuzzers/check_taox11_namespace.rb create mode 100644 lib/fuzz.rb create mode 100644 lib/fuzz/console.rb create mode 100644 lib/fuzz/fuzz.rb create mode 100644 lib/fuzz/fuzzers/check_whitespace.rb create mode 100644 lib/fuzz/fzzr.rb create mode 100644 lib/fuzz/log.rb create mode 100644 lib/fuzz/options.rb create mode 100644 lib/fuzz/screen.rb create mode 100644 lib/fuzz/system.rb create mode 100644 rakelib/gem.rake create mode 100644 rakelib/help.rake create mode 100644 test/test_fuzz.rb diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..2d095e8 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,27 @@ +name: Ruby Gem Release + +on: + release: + types: [published] + +jobs: + build: + name: Publish + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@master + - name: Set up Ruby 2.6 + uses: actions/setup-ruby@v1 + with: + ruby-version: 2.6.x + - name: Publish to RubyGems + run: | + mkdir -p $HOME/.gem + touch $HOME/.gem/credentials + chmod 0600 $HOME/.gem/credentials + printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials + rake gem + gem push pkg/*.gem + env: + GEM_HOST_API_KEY: ${{secrets.RUBYGEMS_API_KEY}} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..80205ae --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.* +*~ +pkg* + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..663a0ec --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Remedy IT Expertise BV + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.rdoc b/README.rdoc new file mode 100644 index 0000000..5644d9a --- /dev/null +++ b/README.rdoc @@ -0,0 +1,28 @@ +{Gem Version}[https://badge.fury.io/rb/fuzzr] +{CodeFactor}[https://www.codefactor.io/repository/github/remedyit/fuzzr] + += fuzz + +== DESCRIPTION: + +{fuzzr}[https://github.com/RemedyIT/fuzzr] is an open source fuzz check tool + +== Bugs + +If you find a bug, please report it as {fuzzr issue}[https://github.com/RemedyIT/fuzzr/issues]. + +== Warranty + +This software is provided "as is" and without any express or implied warranties, including, without limitation, the implied warranties of merchantibility and fitness for a particular purpose. + +== Installing fuzzr + +fuzzr is distributed as a Ruby Gem. You can download and install fuzzr as a Ruby Gem from the common {Rubygems.org}[https://www.rubygems.org/gems/fuzzr] repository by executing the following command: + + $ gem install fuzzr + +The RIDL Gem is a Ruby-only Gem without any dependencies. + +== Releasing new RIDL Ruby Gem + +A new fuzzr ruby gem release can be made by incrementing the fuzzr version in link:lib/fuzz.rb and create a new release on {github}[https://github.com/RemedyIT/fuzzr/releases] matching the new version (for example v2.7.0). The github {Ruby Gem Release}[https://github.com/RemedyIT/ridl/actions?query=workflow%3A%22Ruby+Gem+Release%22] action will automatically create a new gem and push it to {Rubygems.org}[https://www.rubygems.org/gems/fuzzr]. diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..6cf9287 --- /dev/null +++ b/Rakefile @@ -0,0 +1,13 @@ +#-------------------------------------------------------------------- +# Rakefile - main build file for fuzzr +# +# Author: Martin Corino +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the R2CORBA LICENSE which is +# included with this program. +# +# Copyright (c) Remedy IT Expertise BV +#-------------------------------------------------------------------- + +task :default => 'help' diff --git a/bin/fuzz b/bin/fuzz new file mode 100755 index 0000000..2c6dca8 --- /dev/null +++ b/bin/fuzz @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby +# encoding: utf-8 +#--------------------------------- +#--------------------------------- +if File.directory?(File.join(File.dirname(File.symlink?(__FILE__) ? File.expand_path(File.readlink(__FILE__)) : __FILE__), '..', 'lib','fuzz')) + $: << File.expand_path((File.join(File.dirname(File.symlink?(__FILE__) ? File.expand_path(File.readlink(__FILE__)) : __FILE__), '..', 'lib'))) +end + +require 'fuzz.rb' + +exit(Fuzz.run ? 0 : 1) diff --git a/fuzzers/check_ace_error.excludes b/fuzzers/check_ace_error.excludes new file mode 100644 index 0000000..f96a186 --- /dev/null +++ b/fuzzers/check_ace_error.excludes @@ -0,0 +1,5 @@ +taox11\/tao\/x11\/.* +taox11\/lichk\/.* +taox11\/util\/md5\.cpp +.*(C|S|P|SP)\.(h|inl|cpp) +test\/.*\/tao\/.* diff --git a/fuzzers/check_ace_error.rb b/fuzzers/check_ace_error.rb new file mode 100644 index 0000000..0be7db4 --- /dev/null +++ b/fuzzers/check_ace_error.rb @@ -0,0 +1,37 @@ +# encoding: utf-8 +# ------------------------------------------------------------------- +# check_ace_error.rb - TAOX11 ACE_ERROR checker +# +# Author: Martin Corino +# +# Copyright (c) Remedy IT Expertise BV +# ------------------------------------------------------------------- + +module Fuzzers + class TAOX11AceErrorChecker + include Fuzz::Fzzr + def initialize + @fuzz_id = :check_ace_error + @description = 'checks against the use of the old ACE logging macros in test code' + @errormsg = 'detected use of ACE_ERROR, ACE_DEBUG, and/or ACE_ERROR_RETURN' + end + + OBJECT_EXTS = ['h', 'hxx', 'hpp', 'c', 'cc', 'cxx', 'cpp', 'H', 'C', 'asm'] + + def applies_to?(object) + Fuzz::FileObject === object && + OBJECT_EXTS.include?(object.ext) && + !is_excluded?(object) + end + + def run(object, apply_fix) + object.iterate(fuzz_id) do |lnptr| + if lnptr.text =~ /(^|\s+)(ACE_ERROR|ACE_ERROR_RETURN|ACE_DEBUG)(\s+|$)/ + lnptr.mark_error + end + end + end + end + + Fuzz.register_fzzr(TAOX11AceErrorChecker.new) +end diff --git a/fuzzers/check_catch_ex_as_const.rb b/fuzzers/check_catch_ex_as_const.rb new file mode 100644 index 0000000..97cbdbb --- /dev/null +++ b/fuzzers/check_catch_ex_as_const.rb @@ -0,0 +1,41 @@ +# encoding: utf-8 +# ------------------------------------------------------------------- +# check_catch_ex_as_const.rb - Checks whether an exception is caught as const +# +# Author: Marcel Smit +# +# Copyright (c) Remedy IT Expertise BV +# ------------------------------------------------------------------- + +module Fuzzers + class ExAsConstChecker + include Fuzz::Fzzr + def initialize + @fuzz_id = :check_catch_ex_as_const + @description = 'checks whether an exception is caught as const' + @errormsg = 'exceptions should be caught as const' + end + + OBJECT_EXTS = ['h', 'hxx', 'hpp', 'c', 'cc', 'cxx', 'cpp', 'H', 'C'] + + def applies_to?(object) + Fuzz::FileObject === object && + OBJECT_EXTS.include?(object.ext) && + !is_excluded?(object) + end + + def run(object, apply_fix) + object.iterate(fuzz_id) do |lnptr| + if lnptr.text =~ /^\s*(catch\(|catch \()/ + unless lnptr.text =~ /([...])/ + if not lnptr.text =~ /const/ + lnptr.mark_error + end + end + end + end + end + end + + Fuzz.register_fzzr(ExAsConstChecker.new) +end diff --git a/fuzzers/check_cout_cerr.excludes b/fuzzers/check_cout_cerr.excludes new file mode 100644 index 0000000..b202341 --- /dev/null +++ b/fuzzers/check_cout_cerr.excludes @@ -0,0 +1,5 @@ +taox11\/util\/.* +taox11\/orbsvcs\/tests\/.* +public\/.* +test\/.*\/tao\/.* +.*(C|S|P|SP)\.(h|inl|cpp) diff --git a/fuzzers/check_cout_cerr.rb b/fuzzers/check_cout_cerr.rb new file mode 100644 index 0000000..ae336c6 --- /dev/null +++ b/fuzzers/check_cout_cerr.rb @@ -0,0 +1,37 @@ +# encoding: utf-8 +# ------------------------------------------------------------------- +# check_cout_cerr.rb - TAOX11 cout/cerr in core checker +# +# Author: Marcel Smit +# +# Copyright (c) Remedy IT Expertise BV +# ------------------------------------------------------------------- + +module Fuzzers + class TAOX11CoutCerrChecker + include Fuzz::Fzzr + def initialize + @fuzz_id = :check_cout_cerr + @description = 'checks against the use of cout and cerr in the CORE code (to be expanded to all code)' + @errormsg = 'detected use of cout and/or cerr' + end + + OBJECT_EXTS = ['h', 'hxx', 'hpp', 'c', 'cc', 'cxx', 'cpp', 'H', 'C'] + + def applies_to?(object) + Fuzz::FileObject === object && + OBJECT_EXTS.include?(object.ext) && + !is_excluded?(object) + end + + def run(object, apply_fix) + object.iterate(fuzz_id) do |lnptr| + if lnptr.text =~ /(^|\s+)(cout|cerr|std::cout|std::cerr|std::wcout|std::wcerr|wcout|wcerr)(\s+|$)/ + lnptr.mark_error + end + end + end + end + + Fuzz.register_fzzr(TAOX11CoutCerrChecker.new) +end diff --git a/fuzzers/check_executablebit.rb b/fuzzers/check_executablebit.rb new file mode 100644 index 0000000..bed12dc --- /dev/null +++ b/fuzzers/check_executablebit.rb @@ -0,0 +1,37 @@ +# encoding: utf-8 +# ------------------------------------------------------------------- +# check_executablebit.rb - executable bit checker +# +# Author: Johnny Willemsen +# +# Copyright (c) Remedy IT Expertise BV +# ------------------------------------------------------------------- + +module Fuzzers + class ExecutablebitChecker + include Fuzz::Fzzr + def initialize + @fuzz_id = :check_executablebit + @description = 'checks for executable bit set' + end + + OBJECT_EXTS = ['pl', 'sh', 'bat'] + + def applies_to?(object) + Fuzz::FileObject === object && + OBJECT_EXTS.include?(object.ext) && + !is_excluded?(object) + end + + def run(object, apply_fix) + if !File::executable?(object.path) + Fuzz.log_error(%Q{#{object.path} - lacks executable bit}) + false + end + true + end + + end + + Fuzz.register_fzzr(ExecutablebitChecker.new) +end diff --git a/fuzzers/check_exit_keyword.excludes b/fuzzers/check_exit_keyword.excludes new file mode 100644 index 0000000..6b2b957 --- /dev/null +++ b/fuzzers/check_exit_keyword.excludes @@ -0,0 +1,2 @@ +.*(C|S|P|SP)\.(h|inl|cpp) +test\/.*\/tao\/.* diff --git a/fuzzers/check_exit_keyword.rb b/fuzzers/check_exit_keyword.rb new file mode 100644 index 0000000..9563574 --- /dev/null +++ b/fuzzers/check_exit_keyword.rb @@ -0,0 +1,37 @@ +# encoding: utf-8 +# ------------------------------------------------------------------- +# check_exit_keyword.rb - TAOX11 exit checker +# +# Author: Marcel Smit +# +# Copyright (c) Remedy IT Expertise BV +# ------------------------------------------------------------------- + +module Fuzzers + class TAOX11ExitChecker + include Fuzz::Fzzr + def initialize + @fuzz_id = :check_exit_keyword + @description = 'checks against the use of the exit keyword in test code' + @errormsg = 'detected use of exit' + end + + OBJECT_EXTS = ['h', 'hxx', 'hpp', 'c', 'cc', 'cxx', 'cpp', 'H', 'C'] + + def applies_to?(object) + Fuzz::FileObject === object && + OBJECT_EXTS.include?(object.ext) && + !is_excluded?(object) + end + + def run(object, apply_fix) + object.iterate(fuzz_id) do |lnptr| + if lnptr.text =~ /(^|\s+)(exit)(\s+|$)/ + lnptr.mark_error + end + end + end + end + + Fuzz.register_fzzr(TAOX11ExitChecker.new) +end diff --git a/fuzzers/check_fileheader.excludes b/fuzzers/check_fileheader.excludes new file mode 100644 index 0000000..d4dd1e7 --- /dev/null +++ b/fuzzers/check_fileheader.excludes @@ -0,0 +1,5 @@ +.*(C|S|P)\.(h|inl|cpp) +test\/.*\/tao\/.* +ridl\/.* +.*\.erb +.*\.cmd \ No newline at end of file diff --git a/fuzzers/check_fileheader.rb b/fuzzers/check_fileheader.rb new file mode 100644 index 0000000..0e5979a --- /dev/null +++ b/fuzzers/check_fileheader.rb @@ -0,0 +1,92 @@ +# encoding: utf-8 +# ------------------------------------------------------------------- +# check_fileheader.rb - TAOX11 file header checker +# +# Author: Marcel Smit +# +# Copyright (c) Remedy IT Expertise BV +# ------------------------------------------------------------------- + +module Fuzzers + class TAOX11FileHeaderChecker + include Fuzz::Fzzr + def initialize + @fuzz_id = :check_fileheader + @description = 'checks whether a file contains a correct file header' + @errormsg = 'incorrect or no fileheader detected. Fileheaders should start on the first or second line, should have a size of 6 lines and the file name should be on the second line of the header.' + end + + def applies_to?(object) + Fuzz::FileObject === object && + !is_excluded?(object) + end + + def run(object, apply_fix) + header_start = nil + script = false + object.iterate(fuzz_id) do |lnptr| + case lnptr.line_nr + when 1 + if lnptr.text =~ /^(\/[\*]|[#])/ + if not lnptr.text =~ /^(#!)/ + header_start = lnptr.line_nr + end + end + if lnptr.text =~ /^[#]/ + script = true + end + when 2 + unless header_start + if not lnptr.text =~ /^(\/[\*]|[#])/ + lnptr.mark_error 1 + else + header_start = lnptr.line_nr + if lnptr.text =~ /^[#]/ + script = true + end + end + else + if lnptr.text =~ /[\*]\// + lnptr.mark_error + end + if not lnptr.text.match(object.name) + lnptr.mark_error + end + end + when 3 + if header_start && header_start == 2 + # header on second line so file name should + # be on the third + if not lnptr.text.match(object.name) + lnptr.mark_error + end + end + else + if header_start + # for */ + if lnptr.text =~ /[\*]\// + hdr_lns = 1 + lnptr.line_nr - header_start + if hdr_lns < 6 + lnptr.mark_error + lnptr.to_eof # stop + end + else + unless !script + # for scripts + if not lnptr.text =~ /[#]/ + hdr_lns = 1 + lnptr.line_nr - header_start + if hdr_lns < 6 + lnptr.mark_error + lnptr.to_eof # stop + end + end + end + end + end # header_start + end + end + end #def run + end + + Fuzz.register_fzzr(TAOX11FileHeaderChecker.new) +end diff --git a/fuzzers/check_filename.excludes b/fuzzers/check_filename.excludes new file mode 100644 index 0000000..661c392 --- /dev/null +++ b/fuzzers/check_filename.excludes @@ -0,0 +1,8 @@ +ChangeLog +\/MPC$ +MPC\/modules\/GNU.*pm$ +tao\/x11\/Policy.* +tao\/x11\/.*A\.cpp +GNUmakefile +public\/.*(C|S|P|SP)\.(h|inl|cpp) +ridl\/.* diff --git a/fuzzers/check_filename.rb b/fuzzers/check_filename.rb new file mode 100644 index 0000000..ec763d3 --- /dev/null +++ b/fuzzers/check_filename.rb @@ -0,0 +1,33 @@ +# encoding: utf-8 +# ------------------------------------------------------------------- +# check_filename.rb - TAOX11 filename fuzzer +# +# Author: Martin Corino +# +# Copyright (c) Remedy IT Expertise BV +# ------------------------------------------------------------------- + +module Fuzzers + class FilenameChecker + include Fuzz::Fzzr + def initialize + @fuzz_id = :check_filename + @description = 'checks against the use of uppercase in file/directory names' + end + + def applies_to?(object) + !is_excluded?(object) + end + + def run(object, apply_fix) + if object.name =~ /[A-Z]/ + Fuzz.log_error(%Q{name for #{object.path} contains uppercase}) + false + else + true + end + end + end + + Fuzz.register_fzzr(FilenameChecker.new) +end diff --git a/fuzzers/check_id_tag.excludes b/fuzzers/check_id_tag.excludes new file mode 100644 index 0000000..bb34d27 --- /dev/null +++ b/fuzzers/check_id_tag.excludes @@ -0,0 +1,3 @@ +.*(C|S|P|SP)\.(h|inl|cpp) +test\/.*\/tao\/.* +ridl\/.* diff --git a/fuzzers/check_id_tag.rb b/fuzzers/check_id_tag.rb new file mode 100644 index 0000000..4754752 --- /dev/null +++ b/fuzzers/check_id_tag.rb @@ -0,0 +1,34 @@ +# encoding: utf-8 +# ------------------------------------------------------------------- +# check_id_tag.rb - TAOX11 $Id$ tag checker +# +# Author: Marcel Smit +# +# Copyright (c) Remedy IT Expertise BV +# ------------------------------------------------------------------- + +module Fuzzers + class TAOX11IdTagChecker + include Fuzz::Fzzr + def initialize + @fuzz_id = :check_id_tag + @description = 'checks against the use of the $Id$ tag' + @errormsg = 'detected use of $Id:$' + end + + def applies_to?(object) + Fuzz::FileObject === object && + !is_excluded?(object) + end + + def run(object, apply_fix) + object.iterate(fuzz_id) do |lnptr| + if lnptr.text =~ /^\s*([\*]|\/\/|#)\s*([\$]Id)/i + lnptr.mark_error + end + end + end + end + + Fuzz.register_fzzr(TAOX11IdTagChecker.new) +end diff --git a/fuzzers/check_new_delete.excludes b/fuzzers/check_new_delete.excludes new file mode 100644 index 0000000..931c944 --- /dev/null +++ b/fuzzers/check_new_delete.excludes @@ -0,0 +1,6 @@ +taox11\/tao\/x11\/.* +taox11\/orbsvcs\/orbsvcs\/.* +taox11\/lichk\/.* +taox11\/util\/md5\.cpp +.*(C|S|P|SP)\.(h|inl|cpp) +test\/.*\/tao\/.* diff --git a/fuzzers/check_new_delete.rb b/fuzzers/check_new_delete.rb new file mode 100644 index 0000000..32196dc --- /dev/null +++ b/fuzzers/check_new_delete.rb @@ -0,0 +1,37 @@ +# encoding: utf-8 +# ------------------------------------------------------------------- +# check_new_delete.rb - TAOX11 new delete checker +# +# Author: Martin Corino +# +# Copyright (c) Remedy IT Expertise BV +# ------------------------------------------------------------------- + +module Fuzzers + class TAOX11NewDeleteChecker + include Fuzz::Fzzr + def initialize + @fuzz_id = :check_new_delete + @description = 'checks against the use of new and/or delete in test code' + @errormsg = 'detected use of new and/or delete' + end + + OBJECT_EXTS = ['h', 'hxx', 'hpp', 'c', 'cc', 'cxx', 'cpp', 'H', 'C'] + + def applies_to?(object) + Fuzz::FileObject === object && + OBJECT_EXTS.include?(object.ext) && + !is_excluded?(object) + end + + def run(object, apply_fix) + object.iterate(fuzz_id) do |lnptr| + if lnptr.text =~ /(^|\s+)(new|delete)(\s+|$)/ + lnptr.mark_error + end + end + end + end + + Fuzz.register_fzzr(TAOX11NewDeleteChecker.new) +end diff --git a/fuzzers/check_printf_keyword.excludes b/fuzzers/check_printf_keyword.excludes new file mode 100644 index 0000000..6b2b957 --- /dev/null +++ b/fuzzers/check_printf_keyword.excludes @@ -0,0 +1,2 @@ +.*(C|S|P|SP)\.(h|inl|cpp) +test\/.*\/tao\/.* diff --git a/fuzzers/check_printf_keyword.rb b/fuzzers/check_printf_keyword.rb new file mode 100644 index 0000000..55cdc26 --- /dev/null +++ b/fuzzers/check_printf_keyword.rb @@ -0,0 +1,37 @@ +# encoding: utf-8 +# ------------------------------------------------------------------- +# check_printf_keyword.rb - TAOX11 printf checker +# +# Author: Johnny Willemsen +# +# Copyright (c) Remedy IT Expertise BV +# ------------------------------------------------------------------- + +module Fuzzers + class TAOX11PrintfChecker + include Fuzz::Fzzr + def initialize + @fuzz_id = :check_printf_keyword + @description = 'checks against the use of the printf keyword in test code' + @errormsg = 'detected use of printf' + end + + OBJECT_EXTS = ['h', 'hxx', 'hpp', 'c', 'cc', 'cxx', 'cpp', 'H', 'C'] + + def applies_to?(object) + Fuzz::FileObject === object && + OBJECT_EXTS.include?(object.ext) && + !is_excluded?(object) + end + + def run(object, apply_fix) + object.iterate(fuzz_id) do |lnptr| + if lnptr.text =~ /(^|\s+|::)(printf)(\s+|$)/ + lnptr.mark_error + end + end + end + end + + Fuzz.register_fzzr(TAOX11PrintfChecker.new) +end diff --git a/fuzzers/check_taox11_namespace.excludes b/fuzzers/check_taox11_namespace.excludes new file mode 100644 index 0000000..8731861 --- /dev/null +++ b/fuzzers/check_taox11_namespace.excludes @@ -0,0 +1,5 @@ +tao\/x11\/Policy.* +taox11\/tao\/x11\/.* +taox11\/tao\/.* +taox11\/orbsvcs\/orbsvcs\/.* +public\/.*(C|S|P|SP)\.(h|inl|cpp) diff --git a/fuzzers/check_taox11_namespace.rb b/fuzzers/check_taox11_namespace.rb new file mode 100644 index 0000000..d86eaca --- /dev/null +++ b/fuzzers/check_taox11_namespace.rb @@ -0,0 +1,38 @@ +# encoding: utf-8 +# ------------------------------------------------------------------- +# check_taox11_namespace.rb - TAOX11 namespace checker +# +# Author: Martin Corino +# +# Copyright (c) Remedy IT Expertise BV +# ------------------------------------------------------------------- + +module Fuzzers + class TAOX11NamespaceChecker + include Fuzz::Fzzr + def initialize + @fuzz_id = :check_taox11_namespace + @description = 'checks against the use of the TAOX11_NAMESPACE macro in user/test code' + @errormsg = 'detected use TAOX11_xxx namespace macro' + end + + OBJECT_EXTS = ['h', 'hxx', 'hpp', 'c', 'cc', 'cxx', 'cpp', 'H', 'C'] + + def applies_to?(object) + Fuzz::FileObject === object && + OBJECT_EXTS.include?(object.ext) && + !is_excluded?(object) + end + + def run(object, apply_fix) + object.iterate(fuzz_id) do |lnptr| + if lnptr.text =~ /(TAOX11_NAMESPACE|TAOX11_CORBA|TAOX11_PORTABLE_SERVER)::/ || + lnptr.text =~ /namespace\s+(TAOX11_NAMESPACE|TAOX11_CORBA|TAOX11_PORTABLE_SERVER)/ + lnptr.mark_error + end + end + end + end + + Fuzz.register_fzzr(TAOX11NamespaceChecker.new) +end diff --git a/lib/fuzz.rb b/lib/fuzz.rb new file mode 100644 index 0000000..b222215 --- /dev/null +++ b/lib/fuzz.rb @@ -0,0 +1,14 @@ +# encoding: utf-8 +# ------------------------------------------------------------------- +# fuzz.rb - TAOX11 fuzz checker +# +# Author: Martin Corino +# +# Copyright (c) Remedy IT Expertise BV +# ------------------------------------------------------------------- + +module Fuzz + VERSION = '0.9.6' unless defined? VERSION +end + +require 'fuzz/fuzz' diff --git a/lib/fuzz/console.rb b/lib/fuzz/console.rb new file mode 100644 index 0000000..037ca11 --- /dev/null +++ b/lib/fuzz/console.rb @@ -0,0 +1,52 @@ +#-------------------------------------------------------------------- +# @file console.rb +# @author Martin Corino +# +# @brief Fuzz console wrapper +# +# @copyright Copyright (c) Remedy IT Expertise BV +#-------------------------------------------------------------------- +require 'fuzz/screen' + +module Fuzz + + module Console + + class << self + def screen + @screen ||= Screen.new(Fuzz.options[:output] || $stdout, $stdin) + end + include Screen::ColorizeMethods + end + + def self.print(*args) + screen.print(*args) + end + + def self.println(*args) + screen.println(*args) + end + + def self.error_print(*args) + screen.error_print(*args) + end + + def self.error_println(*args) + screen.error_println(*args) + end + + def self.colorizer_include + Screen::ColorizeMethods + end + + def self.display_break + println("\n") + end + + def self.display_hline(len = nil) + println("#{'-' * (len || [80, (screen.output_cols / 5 * 4)].min)}\n") + end + + end # Console + +end # Fuzz diff --git a/lib/fuzz/fuzz.rb b/lib/fuzz/fuzz.rb new file mode 100644 index 0000000..7f7aa9c --- /dev/null +++ b/lib/fuzz/fuzz.rb @@ -0,0 +1,402 @@ +# encoding: utf-8 +# ------------------------------------------------------------------- +# fuzz.rb - TAOX11 fuzz checker +# +# Author: Martin Corino +# +# Copyright (c) Remedy IT Expertise BV +# ------------------------------------------------------------------- + +require 'optparse' +require 'tempfile' +require 'fileutils' +require 'yaml' +require 'fuzz/log' +require 'fuzz/system' +require 'fuzz/options' +require 'fuzz/fzzr' + +module Fuzz + VERSION_MAJOR, + VERSION_MINOR, + VERSION_RELEASE = VERSION.split('.') + COPYRIGHT = "Copyright (c) 2012-#{Time.now.year} Remedy IT Expertise BV, The Netherlands".freeze + + def self.root_path + f = File.expand_path(__FILE__) + f = File.expand_path(File.readlink(f)) if File.symlink?(f) + File.dirname(f) + end + + FUZZ_ROOT = self.root_path + + class << self + + include LogMethods + include Sys::SysMethods + + def reporter + @reporter ||= Fuzz::Reporter.new + end + + def set_reporter(rep) + @reporter = rep + end + alias :reporter= :set_reporter + + def options + Fuzz::OPTIONS + end + + def reset + options.reset + end + + def load_config + options.load_config + end + + def includes + unless @include_re + @include_re = [] + # @include_re << "\\.(#{Fuzz::FileObject.extensions.join('|')})$" + # @include_re << "#{Fuzz::FileObject.filenames.join('|')}$" + @include_re << "\\.(#{options.config[:exts].join('|')})$" + @include_re << "#{options.config[:filenames].join('|')}$" + end + @include_re + end + + # + # fuzzer registration + # + + def fuzzers + @fuzzers ||= {} + end + + def register_fzzr(fzzr) + raise RuntimeError, "Duplicate fuzzer registration: #{fzzr.fuzz_id}" if fuzzers.has_key?(fzzr.fuzz_id) + fuzzers[fzzr.fuzz_id] = fzzr + fzzr.setup(options.optparser) if options.optparser && fzzr.respond_to?(:setup) && fzzr_included?(fzzr) + end + + def get_fzzr(id) + fuzzers[id] + end + + def fzzr_excluded?(fzzr) + options.config[:fzzr_excludes].include?(fzzr.fuzz_id.to_sym) + end + + def fzzr_included?(fzzr) + !fzzr_excluded?(fzzr) + end + + # + # load fuzzers + # + def load_fuzzers + # standard fuzzers included in Gem + unless loaded_fzzr_paths.include?(_p = File.join(FUZZ_ROOT, 'fuzzers')) + Dir.glob(File.join(_p, '*.rb')).each do |fnm| + require fnm + end + loaded_fzzr_paths << _p + end + # configured fuzzers + options.config[:fzzr_paths].each do |fzzrpath| + unless loaded_fzzr_paths.include?(_p = File.expand_path(fzzrpath)) + Dir.glob(File.join(_p, '*.rb')).each do |fnm| + require fnm + end + loaded_fzzr_paths << _p + end + end + end + + private + + def loaded_fzzr_paths + @loaded_fuzzr_paths ||= [] + end + + end + + ## Backwards compatibility + def self.log_verbose(msg) + log_info(msg) if verbose? + end + + # + # Option methods + # + + def self.verbosity + options.verbose + end + + def self.apply_fix? + options.apply_fix || false + end + + def self.follow_symlink? + options.config[:follow_symlink] || false + end + + def self.excludes + options.config[:excludes] || [] + end + + def self.excluded?(object) + excludes.any? { |excl| (object.fullpath =~ /#{excl}/) } + end + + # + # parse commandline arguments + # + def self.init_optparser + script_name = File.basename($0) + if not script_name =~ /fuzz/ + script_name = "ruby "+$0 + end + + options.optparser = opts = OptionParser.new + opts.banner = "Usage: #{script_name} [options] [glob [glob]]\n\n" + opts.separator "\n--- [General options] ---\n\n" + opts.on('-t', '--filetype', '=EXT', String, + 'Defines an alternative filetype to search and scan. Can be specified multiple times.', + "Default: #{Fuzz::FileObject.extensions.join('|')}") { |v| + (options.user_config[:exts] ||= []) << v + } + opts.on('-f', '--file', '=NAME', String, + 'Defines an alternative filename to search and scan. Can be specified multiple times.', + "Default: #{Fuzz::FileObject.filenames.join('|')}") { |v| + (options.user_config[:filenames] ||= []) << v + } + opts.on('-a', '--add-files', + 'Add custom filenames and/or filetype extensions to default list instead of replacing defaults.', + 'Default: false') { |v| + options.user_config[:add_files] = true + } + opts.on('-S', '--no-symlinks', + 'Do not follow symlinks.', + 'Default: follow symlinks') { |v| + options.user_config[:follow_symlink] = false + } + opts.on('-P', '--fzzr-path', '=PATH', + 'Adds search path for Fuzzers.', + "Default: loaded from ~/#{FUZZRC} and/or ./#{FUZZRC}") { |v| + (options.user_config[:fzzr_paths] ||= []) << v.to_s + } + opts.on('-B', '--blacklist', '=FZZRID', + 'Adds Fuzzer ID to list of fuzzers to exclude from Fuzz check.', + 'Default: none') { |v| + (options.user_config[:fzzr_excludes] ||= []) << v.to_sym + } + opts.on('-X', '--exclude', '=MASK', + 'Adds path mask (regular expression) to list to exclude from Fuzz check.', + 'Default: none') { |v| + (options.user_config[:excludes] ||= []) << v + } + opts.on('-c', '--config', '=FUZZRC', + 'Load config from FUZZRC file.', + "Default: ~/#{FUZZRC} and/or ./#{FUZZRC}") { |v| + options.add_config(v) + } + opts.on('--write-config', '=[FUZZRC]', + 'Write config to file and exit.', + "Default: ./#{FUZZRC}") { |v| + options.user_config.save(String === v ? v : FUZZRC) + exit + } + opts.on('--show-config', + 'Display config settings and exit.') { |v| + options.load_config + puts YAML.dump(options.config.__send__ :table) + exit + } + + opts.separator '' + opts.on('-o', '--output', '=FILE', String, + 'Specifies filename to write Fuzz messages to.', + 'Default: stderr') { |v| + options.output = v + } + opts.on('-p', '--apply-fix', + 'Apply fixes (if any) for Fuzz errors.', + 'Default: false') { |v| + options.apply_fix = true + } + opts.on('-n', '--no-recurse', + 'Prevents directory recursion in file selection.', + 'Default: recurse') { |v| + options.recurse = false + } + opts.on('-v', '--verbose', + 'Run with increased verbosity level. Repeat to increase more.', + 'Default: 1') { |v| options.verbose += 1 } + + opts.separator '' + opts.on('-L', '--list', + 'List available Fuzzers and exit.') { + options.load_config + load_fuzzers + puts "TAOX11 fuzz checker #{VERSION_MAJOR}.#{VERSION_MINOR}.#{VERSION_RELEASE}" + puts COPYRIGHT + puts('%-30s %s' % %w{Fuzzer Description}) + puts(('-' * 30)+' '+('-' * 48)) + fuzzers.values.each { |fzzr| puts('%-30s %s' % [fzzr.fuzz_id, fzzr.description]) } + puts + exit + } + + opts.separator "" + opts.on('-V', '--version', + 'Show version information and exit.') { + puts "TAOX11 fuzz checker #{VERSION_MAJOR}.#{VERSION_MINOR}.#{VERSION_RELEASE}" + puts COPYRIGHT + exit + } + opts.on('-h', '--help', + 'Show this help message.') { + options.load_config + load_fuzzers + puts opts; + puts; + exit + } + + opts.separator "\n--- [Fuzzer options] ---\n\n" + end + + def self.parse_args(argv) + options.optparser.parse!(argv) + end + + def self.select_fzzrs(object) + fuzzers.values.collect { |fzzr| (fzzr_included?(fzzr) && fzzr.applies_to?(object)) ? fzzr : nil }.compact + end + + def self.update_file_object(fo) + if File.writable?(fo.fullpath) + log_verbose(%Q{Updating #{fo}...}) + ftmp = Tempfile.new(fo.name) + log_verbose(%Q{+ Writing temp file #{ftmp.path}...}) + fo.lines.each { |ln| ftmp.print ln } + ftmp.close(false) # close but do NOT unlink + log_verbose(%Q{+ Replacing #{fo} with #{ftmp.path}}) + # create temporary backup + ftmp2 = Tempfile.new(fo.name) + ftmp2_name = ftmp2.path.dup + ftmp2.close(true) + mv(fo.fullpath, ftmp2_name) + # replace original + begin + mv(ftmp.path, fo.fullpath) + # preserve file mode + chmod(File.lstat(ftmp2_name).mode, fo.fullpath) + rescue + log_error(%Q{FAILED updating #{fo}: #{$!}}) + # restore backup + mv(ftmp2_name, fo.fullpath) + raise + end + # remove backup + File.unlink(ftmp2_name) + log_verbose(%Q{Finished updating #{fo}.}) + return true + else + log_error(%Q{NO_ACCESS - cannot update #{fo}}) + return false + end + end + + def self.handle_object(object) + log_verbose(%Q{Handling #{object}}) + fzzrs = select_fzzrs(object) + no_fixes_allowed = false + rc = fzzrs.inject(true) do |result, fzzr| + log_verbose(%Q{+ Running fuzzer #{fzzr.fuzz_id}}) + begin + if fzzr.run(object, options.apply_fix) + result + else + log_verbose(%Q{+ Error from fuzzer #{fzzr.fuzz_id}}) + false + end + rescue + log_error(%Q{EXCEPTION CAUGHT running fuzzer #{fzzr.fuzz_id} on #{object} - #{$!}\n#{$!.backtrace.join("\n")}}) + no_fixes_allowed = true + break ## immediately stop handling this object, rc will remain false + end + end + unless no_fixes_allowed + if Fuzz.apply_fix? && object.changed? + rc = update_file_object(object) && rc + end + end + rc ? true : false + end + + def self.iterate_paths(paths) + paths.inject(true) do |result, path| + if File.readable?(path) && (!File.symlink?(path) || follow_symlink?) + if File.directory?(path) + rc = handle_object(dirobj = Fuzz::DirObject.new(path)) + log_verbose(%Q{Iterating #{path}}) + if options.recurse && !excluded?(dirobj) + rc = iterate_paths(Dir.glob(File.join(path, '*'))) && rc + end + rc + elsif File.file?(path) + handle_object(Fuzz::FileObject.new(path)) + else + true + end + else + log_warning(File.readable?(path) ? %Q{Cannot read #{path}} : %Q{Cannot follow symlink #{}path}) + false + end && result + end + end + + def self.run_fzzrs(argv) + options.config[:exts].concat(Fuzz::FileObject.extensions) if options.config[:exts].empty? || options.config[:add_files] + options.config[:filenames].concat(Fuzz::FileObject.filenames) if options.config[:filenames].empty? || options.config[:add_files] + + options.config[:exts].uniq! + options.config[:filenames].uniq! + + f_close_output = false + if String === options.output + options.output = File.open(options.output, 'w') + f_close_output = true + end + begin + # determin files/paths to test + paths = argv.collect { |a| Dir.glob(a) }.flatten.uniq + paths = Dir.glob('*') if paths.empty? + # scan all determined objects + return iterate_paths(paths) + ensure + options.output.close if f_close_output + end + end + + def self.run + init_optparser + + # parse arguments + parse_args(ARGV) + + # load config (if any) + options.load_config + + # load fuzzers + load_fuzzers + + run_fzzrs(ARGV) + end + +end diff --git a/lib/fuzz/fuzzers/check_whitespace.rb b/lib/fuzz/fuzzers/check_whitespace.rb new file mode 100644 index 0000000..7808238 --- /dev/null +++ b/lib/fuzz/fuzzers/check_whitespace.rb @@ -0,0 +1,62 @@ +# encoding: utf-8 +# ------------------------------------------------------------------- +# check_whitespace.rb - TAOX11 whitespace checker +# +# Author: Martin Corino +# +# Copyright (c) Remedy IT Expertise BV +# ------------------------------------------------------------------- + +module Fuzzers + class WhitespaceChecker + include Fuzz::Fzzr + def initialize + @fuzz_id = :check_whitespace + @description = 'checks for trailing whitespace, incorrect line endings and tabs' + end + + def setup(optparser) + optparser.on('--wsc:tab-spacing=NUM', Integer, + 'Fuzzers::WhitespaceChecker - defines tab spacing to use for TAB replacement when --apply-fix is enabled.', + "Default: #{tab_spacing}") {|v| self.options[:tabspacing] = v } + end + + def applies_to?(object) + Fuzz::FileObject === object && !is_excluded?(object) + end + + def run(object, apply_fix) + _tws = [] + _tabs = [] + object.iterate(fuzz_id) do |lnptr| + if lnptr.text =~ /(\s\n|[\ \t\f\r\x0B])\Z/ + if apply_fix + Fuzz.log_verbose(%Q{#{object.path}:#{lnptr.line_nr} - stripping trailing whitespace}) + lnptr.text.rstrip! + lnptr.text << "\n" if $1.end_with?("\n") + else + _tws << lnptr.line_nr + end + end + if lnptr.text =~ /\t/ + if apply_fix + Fuzz.log_warning(%Q{#{object.path}:#{lnptr.line_nr} - replacing tabs}) + lnptr.text.gsub!(/\t/, ' ' * tab_spacing) + else + _tabs << lnptr.line_nr + end + end + end + Fuzz.log_error(%Q{#{object.path}:[#{_tws.join(',')}] trailing whitespace or incorrect line ending detected}) unless _tws.empty? + Fuzz.log_error(%Q{#{object.path}:[#{_tabs.join(',')}] tab(s) detected}) unless _tabs.empty? + return (_tws.empty? && _tabs.empty?) + end + + private + def tab_spacing + self.options[:tabspacing] || 2 + end + end + + Fuzz.register_fzzr(WhitespaceChecker.new) +end diff --git a/lib/fuzz/fzzr.rb b/lib/fuzz/fzzr.rb new file mode 100644 index 0000000..b03505c --- /dev/null +++ b/lib/fuzz/fzzr.rb @@ -0,0 +1,258 @@ +# encoding: utf-8 +# ------------------------------------------------------------------- +# fuzzr.rb - TAOX11 Fuzzer bases +# +# Author: Martin Corino +# +# Copyright (c) Remedy IT Expertise BV +# ------------------------------------------------------------------- + +module Fuzz + ## + # Fuzzers are objects having the following readonly attributes: + # #fuzz_id : id of fuzzer (Symbol) + # #description: (String) + # #errormsg : (String) + # + # and having the following methods: + # #applies_to?(object) : checks if the test applies to the object passed + # (Fuzz::DirObject or Fuzz::FileObject) + # #run(object,apply_fix): runs the fzzr test on the object passed + # (Fuzz::DirObject or Fuzz::FileObject) + # when apply_fix == true Fuzzer is directed to + # attempt to fix any problems found (future) + # + # Fuzz::Fzzr is provided as a convenience Mixin for fuzzers + # + # Fuzzers can inspect the options passed to fuzz.rb by referencing Fuzz::OPTIONS + ## + + module Fzzr + attr_reader :fuzz_id, :description, :errormsg + + def applies_to?(object) + !is_excluded?(object) + end + + def setup(optparser) + end + + def run(object, apply_fix) + true + end + + def is_excluded?(object) + # force excludes to be parsed + _excludes + # now examine + ((!_is_included?(object)) || _excludes.any? { |excl| (object.fullpath =~ /#{excl}/) }) + end + + def options + Fuzz::OPTIONS[:config][:fzzr_opts][self.fuzz_id] ||= {} + end + + private + + def _is_included?(object) + Fuzz::DirObject === object || _includes.empty? || _includes.any? { |incl| (object.fullpath =~ /#{incl}/) } + end + + def _includes + @_includes ||= Fuzz.includes.dup + end + + def _excludes + unless @_excludes + @_excludes = [] + @_excludes.concat(Fuzz.excludes) + Fuzz::OPTIONS[:config][:fzzr_paths].each do |fzzrpath| + fzzr_excl_file = File.join(fzzrpath, "#{self.fuzz_id}.excludes") + if File.readable?(fzzr_excl_file) + lns = IO.readlines(fzzr_excl_file).collect { |l| l.strip } + @_excludes.concat(lns.select {|l| !(l.empty? || l[0] == '!') }) + _includes.concat(lns.select {|l| !(l.empty? || l[0] != '!') }.collect {|l| l[1,l.size].strip }) + else + false + end + end + end + @_excludes + end + end # Fzzr + + class DirObject + attr_reader :path, :fullpath, :name, :ext + def initialize(path) + @path = path + @fullpath = File.expand_path(path) + @name = File.basename(path) + @ext = File.extname(path).sub(/^\./,'') + end + + def changed? + false + end + + def iterate(fzzr_id, &block) + # nothing to iterate over + true + end + + def to_s + "Dir:#{path}" + end + end # DirObject + + class FileObject + EXTS = [ + 'h', 'hxx', 'hpp', 'c', 'cc', 'cxx', 'cpp', 'H', 'C', 'inl', 'asm', + 'rb', 'erb', 'pl', 'pm', 'py', + 'idl', 'pidl', + 'mwc', 'mpc', 'mpb', 'mpt', 'mpd', + 'cdp', 'xml', 'conf', 'html', + 'asc', 'adoc' + ] + FILES = [ + 'ChangeLog', 'README' + ] + + def self.extensions + EXTS + end + + def self.filenames + FILES + end + + class LinePointer + attr_reader :err_lines + + FZZR_ENABLE_RE = /X11_FUZZ\: enable ([^\s]+)/ + FZZR_DISABLE_RE = /X11_FUZZ\: disable ([^\s]+)/ + + def initialize(lines, fzzr_id) + @lines = lines + @fzzr_id = fzzr_id.to_s + @err_lines = [] + reset + end + def fzzr_disabled? + @fzzr_disabled + end + def line_nr + @line_nr+1 + end + def text_at(offs) + ln = @line_nr+offs + if ln>=0 && ln<@lines.size + return @lines[ln] + end + nil + end + def set_text_at(offs, txt) + ln = @line_nr+offs + if ln>=0 && ln<@lines.size + return (@lines[ln] = txt) + end + nil + end + def text + text_at(0) + end + def text=(txt) + set_text_at(0, txt) + end + def move(offs) + if offs < 0 + _backward(-offs) unless bof? + else + _forward(offs) unless eof? + end + self.line_nr + end + def reset + @line_nr = 0 + @fzzr_disabled = false + _check_fzzr_escape + end + def to_eof + _forward(@lines.size - @line_nr) + end + def eof? + @line_nr >= @lines.size + end + def bof? + @line_nr <= 0 + end + def mark_error(ln = nil) + @err_lines << (ln || (@line_nr+1)) + end + + private + + def _forward(distance) + distance.times do + @line_nr += 1 + break if eof? + _check_fzzr_escape + end + end + + def _backward(distance) + distance.times do + break if bof? + @line_nr -= 1 + _check_fzzr_escape(false) + end + end + + def _check_fzzr_escape(forward = true) + begin + if FZZR_ENABLE_RE =~ @lines[@line_nr] + @fzzr_disabled = !forward if $1 == @fzzr_id + elsif FZZR_DISABLE_RE =~ @lines[@line_nr] + @fzzr_disabled = forward if $1 == @fzzr_id + end + rescue + Fuzz.log_error(%Q{ERROR: Exception while checking fzzr escapes in line #{@line_nr+1} - #{$!}\n#{@lines[@line_nr]}}) + raise + end + end + end # LinePointer + + attr_reader :path, :fullpath, :name, :ext, :lines + + def initialize(path) + @path = path + @fullpath = File.expand_path(path) + @name = File.basename(path) + @ext = File.extname(path).sub(/^\./,'') + @lines = nil + @pointer = nil + @changed = false + end + + def changed? + @changed + end + + def iterate(fzzr_id, &block) + @lines ||= IO.readlines(fullpath) + lines_copy = @lines.collect {|l| l.dup } + pointer = LinePointer.new(@lines, fzzr_id) + begin + block.call(pointer) unless pointer.fzzr_disabled? + pointer.move(1) + end while !pointer.eof? + Fuzz.log_error(%Q{#{self.path}[#{pointer.err_lines.join(',')}] #{Fuzz.get_fzzr(fzzr_id).errormsg}}) unless pointer.err_lines.empty? + @changed |= (@lines != lines_copy) + lines_copy = nil + return pointer.err_lines.empty? + end + + def to_s + "File:#{fullpath}" + end + end # FileObject +end # Fuzz diff --git a/lib/fuzz/log.rb b/lib/fuzz/log.rb new file mode 100644 index 0000000..39f90f3 --- /dev/null +++ b/lib/fuzz/log.rb @@ -0,0 +1,97 @@ +#-------------------------------------------------------------------- +# @file log.rb +# @author Martin Corino +# +# @brief Fuzz logging support +# +# @copyright Copyright (c) Remedy IT Expertise BV +#-------------------------------------------------------------------- +require 'fuzz/console' + +module Fuzz + + # + # Default Reporting/Logging + # + class Reporter + def initialize(output = Fuzz::Console) + @output = output + klass = class << self; self; end + klass.__send__(:include, @output.colorizer_include) + end + + attr_reader :output + + def log_error(msg) + output.error_println 'Fuzz - ', red(bold 'ERROR'), ' : ', msg + end + + def log_warning(msg) + output.error_println 'Fuzz - ', yellow(bold 'WARNING'), ' : ', msg + end + + def log_info(msg) + output.println 'Fuzz - ', msg + end + + def show_error(msg) + log_error(msg) + end + + def show_warning(msg) + log_error(msg) + end + + def show_msg(msg) + log(msg) + end + end + + module LogMethods + def log_fatal(msg, rc=1) + Fuzz.reporter.log_error(msg) + exit rc + end + + def log_error(msg) + Fuzz.reporter.log_error(msg) + end + + def log_warning(msg) + Fuzz.reporter.log_warning(msg) + end + + def log_info(msg) + Fuzz.reporter.log_info(msg) + end + + def log(lvl, msg) + Fuzz.reporter.log_info(msg) if lvl <= verbosity + end + + def show_error(msg) + Fuzz.reporter.show_error(msg) + end + + def show_warning(msg) + Fuzz.reporter.show_warning(msg) + end + + def show_msg(msg) + Fuzz.reporter.show_msg(msg) + end + + def verbosity + Fuzz.verbosity + end + + def verbose? + verbosity > 1 + end + + def silent? + verbosity < 1 + end + end + +end # Fuzz diff --git a/lib/fuzz/options.rb b/lib/fuzz/options.rb new file mode 100644 index 0000000..feaefd8 --- /dev/null +++ b/lib/fuzz/options.rb @@ -0,0 +1,208 @@ +#-------------------------------------------------------------------- +# @file options.rb +# @author Martin Corino +# +# @brief Options module for fuzz +# +# @copyright Copyright (c) Remedy IT Expertise BV +#-------------------------------------------------------------------- + +require 'ostruct' +require 'yaml' +require 'fuzz/log' + +module Fuzz + + FUZZRC = '.fuzzrc' + FUZZRC_GLOBAL = File.expand_path(File.join(ENV['HOME'] || ENV['HOMEPATH'] || '~', FUZZRC)) + + OPTIONS = OpenStruct.new + + class << OPTIONS + + include Fuzz::LogMethods + + def options + self + end + + class Config < OpenStruct + + include Fuzz::LogMethods + + def initialize(hash=nil) + super + @table = _merge(_defaults, @table) + end + + def options + Fuzz.options + end + + def merge(from) + _merge(@table, from) + self + end + + def load(rcpath) + log(3, "Loading #{FUZZRC} from #{rcpath}") + _cfg = YAML.load(IO.read(rcpath)) + log(4, "Read from #{rcpath}: [#{_cfg}]") + # handle automatic env var expansion in fzzr_paths + _cfg[:fzzr_paths] = (_cfg[:fzzr_paths] || []).collect do |p| + log(5, "Examining fzzr_path [#{p}]") + # for paths coming from rc files environment vars are immediately expanded and + p.gsub!(/\$([^\s\/]+)/) { |m| ENV[$1] } + log(6, "Expanded fzzr_path [#{p}]") + # resulting relative paths converted to absolute paths + if File.directory?(p) # relative to working dir? + p = File.expand_path(p) + else # relative to rc location? + _fp = File.expand_path(File.join(File.dirname(rcpath), p)) + log(4, "Ignoring invalid fuzzer search path #{p} configured in #{rcpath}") unless File.directory?(_fp) + p = _fp + end + log(4, "Adding fuzzer search path: #{p}") + p + end + merge(_cfg) + end + + def save(rcpath) + File.open(rcpath, 'w') {|f| f << YAML.dump(@table) } + end + + protected + + def _defaults + { + :brix_paths => [] + } + end + + def _merge(to, from) + from.each_pair do |(k,v)| + k = k.to_sym + if to.has_key?(k) + case to[k] + when Array + to[k].concat v + when Hash + to[k].merge!(v) + when OpenStruct + _merge(to[k].__send__(:table), v) + else + to[k] = v + end + else + to[k] = v + end + end + to + end + + end + + protected + + def _defaults + { + :verbose => (ENV['FUZZ_VERBOSE'] || 1).to_i, + :recurse => true, + :apply_fix => false, + :config => Config.new({ + :follow_symlink => true, + :exts => [], + :filenames => [], + :excludes => [], + :add_files => false, + :fzzr_paths => [], + :fzzr_opts => {}, + :fzzr_excludes => [] + }) + } + end + + def _rc_paths + @rc_paths ||= [] + end + def _loaded_rc_paths + @loaded_rc_paths ||= [] + end + + def _add_rcpath(path) + if _loaded_rc_paths.include?(File.expand_path(path)) + log(3, "ignoring already loaded rc : #{path}") + else + log(3, "adding rc path : #{path}") + _rc_paths << path + end + _rc_paths + end + + public + + def reset + @table.clear + @table.merge!(_defaults) + _rc_paths.clear + _rc_paths << FUZZRC_GLOBAL + _loaded_rc_paths.clear + (ENV['FUZZRC'] || '').split(/:|;/).each do |p| + _add_rcpath(p) + end + end + + def load_config + # first collect config from known (standard and configured) locations + _rc_paths.collect {|path| File.expand_path(path) }.each do |rcp| + log(3, "Testing rc path #{rcp}") + if File.readable?(rcp) && !_loaded_rc_paths.include?(rcp) + _cfg = Config.new.load(rcp) + self[:config].merge(_cfg) + _loaded_rc_paths << rcp + else + log(3, "Ignoring #{File.readable?(rcp) ? 'already loaded' : 'inaccessible'} rc path #{rcp}") + end + end + # now scan working path for any rc files unless specified otherwise + unless self[:no_rc_scan] + _cwd = File.expand_path(Dir.getwd) + log(3, "scanning working path #{_cwd} for rc files") + # first collect any rc files found + _rcpaths = [] + begin + _rcp = File.join(_cwd, FUZZRC) + if File.readable?(_rcp) && !_loaded_rc_paths.include?(_rcp) + _rcpaths << _rcp + else + log(3, "Ignoring #{File.readable?(_rcp) ? 'already loaded' : 'inaccessible'} rc path #{_rcp}") + end + break if /\A(.:(\\|\/)|\.|\/)\Z/ =~ _cwd + _cwd = File.dirname(_cwd) + end while true + # now load them in reverse order + _rcpaths.reverse.each do |_rcp| + _cfg = Config.new.load(_rcp) + self[:config].merge(_cfg) + _loaded_rc_paths << _rcp + end + end + # lastly merge config specified by user on commandline + self[:config].merge(user_config) + end + + def add_config(rcpath) + log_fatal("inaccessible rc path specified : #{rcpath}") unless File.readable?(rcpath) + _add_rcpath(rcpath) + end + + def user_config + @user_config ||= Config.new + end + + end # OPTIONS class + + OPTIONS.reset # initialize + +end # Fuzz diff --git a/lib/fuzz/screen.rb b/lib/fuzz/screen.rb new file mode 100644 index 0000000..1c092e8 --- /dev/null +++ b/lib/fuzz/screen.rb @@ -0,0 +1,85 @@ +#-------------------------------------------------------------------- +# @file screen.rb +# @author Martin Corino +# +# @brief Fuzz screen wrapper +# +# @copyright Copyright (c) Remedy IT Expertise BV +#-------------------------------------------------------------------- +require 'fuzz/system' + +module Fuzz + + class Screen + + class Color + def initialize(code) + @code = code + end + attr_reader :code + def to_s + '' + end + alias :to_str :to_s + end + + COLORS = { + black: [Color.new("\e[30m"), Color.new("\e[m")], + red: [Color.new("\e[31m"), Color.new("\e[m")], + green: [Color.new("\e[32m"), Color.new("\e[m")], + yellow: [Color.new("\e[33m"), Color.new("\e[m")], + blue: [Color.new("\e[34m"), Color.new("\e[m")], + magenta: [Color.new("\e[34m"), Color.new("\e[m")], + bold: [Color.new("\e[1m"), Color.new("\e[m")], + reverse: [Color.new("\e[7m"), Color.new("\e[m")], + underline:[Color.new("\e[4m"), Color.new("\e[m")] + } + + module ColorizeMethods + def self.included(mod) + Screen::COLORS.keys.each do |color| + mod.module_eval <<-EOT, __FILE__, __LINE__+1 + def #{color}(s) + [Fuzz::Screen::COLORS[:#{color}].first, s, Fuzz::Screen::COLORS[:#{color}].last] + end + EOT + end + end + end + + def initialize(output = STDOUT, input = STDIN, errout = STDERR) + @output = output + @input = input + @errout = errout + @colorize = output.tty? && Fuzz::Sys.has_ansi? + end + + attr_reader :input, :output, :errout + + def colorize? + @colorize + end + + def output_cols + 80 + end + + def print(*args) + output.print args.flatten.collect {|a| (colorize? && Color === a) ? a.code : a }.join + end + + def println(*args) + output.puts args.flatten.collect {|a| (colorize? && Color === a) ? a.code : a }.join + end + + def error_print(*args) + errout.print args.flatten.collect {|a| (colorize? && Color === a) ? a.code : a }.join + end + + def error_println(*args) + errout.puts args.flatten.collect {|a| (colorize? && Color === a) ? a.code : a }.join + end + + end # Screen + +end # Fuzz diff --git a/lib/fuzz/system.rb b/lib/fuzz/system.rb new file mode 100644 index 0000000..04afdde --- /dev/null +++ b/lib/fuzz/system.rb @@ -0,0 +1,57 @@ +#-------------------------------------------------------------------- +# @file system.rb +# @author Martin Corino +# +# @brief System support for Fuzz +# +# @copyright Copyright (c) Remedy IT Expertise BV +#-------------------------------------------------------------------- +require 'fileutils' + +module Fuzz + + module Sys + + module SysMethods + + def mswin? + /mingw/ =~ RUBY_PLATFORM ? true : false + end + + def has_ansi? + # only ANSI escape code support on Windows + # if ANSICON (https://github.com/adoxa/ansicon) installed + (!mswin?) || ENV['ANSICON'] + end + + def in_dir(dir, &block) + STDERR.puts "cd #{dir}" if Fuzz.verbose? + rc = if Fuzz.dryrun? + yield if block_given? + else + Dir.chdir(dir, &block) + end + STDERR.puts "cd -" if Fuzz.verbose? + rc + end + def mv(src, tgt) + FileUtils.move(src, tgt, :verbose => Fuzz.verbose?) + end + + def cp(src, tgt) + FileUtils.copy(src, tgt, :verbose => Fuzz.verbose?) + end + + def chmod(mode, path) + FileUtils.chmod(mode, path, :verbose => Fuzz.verbose?) + end + + end # SysMethods + + class << self + include Sys::SysMethods + end + + end # Sys + +end # Fuzz diff --git a/rakelib/gem.rake b/rakelib/gem.rake new file mode 100644 index 0000000..6efba94 --- /dev/null +++ b/rakelib/gem.rake @@ -0,0 +1,71 @@ +#-------------------------------------------------------------------- +# gem.rake - build file +# +# Author: Martin Corino +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the fuzzr LICENSE which is +# included with this program. +# +# Copyright (c) Remedy IT Expertise BV +#-------------------------------------------------------------------- +require 'rubygems' +begin + require 'rubygems/builder' +rescue LoadError + require 'rubygems/package' +end + +require './lib/fuzz' + +module Fuzz + + def self.pkg_root + File.dirname(File.expand_path(File.dirname(__FILE__))) + end + + def self.define_spec(name, version, &block) + gemspec = Gem::Specification.new(name,version) + gemspec.required_rubygems_version = Gem::Requirement.new(">= 0") if gemspec.respond_to? :required_rubygems_version= + block.call(gemspec) + gemspec + end + + def self.build_gem(gemspec) + if defined?(Gem::Builder) + gem_file_name = Gem::Builder.new(gemspec).build + else + gem_file_name = Gem::Package.build(gemspec) + end + + pkg_dir = File.join(pkg_root, 'pkg') + FileUtils.mkdir_p(pkg_dir) + + gem_file_name = File.join(pkg_root, gem_file_name) + FileUtils.mv(gem_file_name, pkg_dir) + end +end + +desc 'Build fuzzr gem' +task :gem do + gemspec = Fuzz.define_spec('fuzzr', Fuzz::VERSION) do |gem| + # gem is a Gem::Specification... see https://guides.rubygems.org/specification-reference/ for more options + gem.summary = %Q{fuzzr} + gem.description = %Q{Fuzzer} + gem.email = 'mcorino@remedy.nl' + gem.homepage = "https://github.com/RemedyIT/fuzzr" + gem.authors = ['Martin Corino', 'Johnny Willemsen'] + gem.files = %w{LICENSE README.rdoc}.concat(Dir.glob('{lib,bin,fuzzers}/**/*')) + gem.extensions = [] + gem.extra_rdoc_files = %w{LICENSE README.rdoc} + gem.rdoc_options << '--main' << 'README.rdoc' + gem.executables = [] + gem.license = 'MIT' + gem.metadata = { + "bug_tracker_uri" => "https://github.com/RemedyIT/fuzzr/issues", + "source_code_uri" => "https://github.com/RemedyIT/fuzzr" + } + gem.required_ruby_version = '>= 2.0' + end + Fuzz.build_gem(gemspec) +end diff --git a/rakelib/help.rake b/rakelib/help.rake new file mode 100644 index 0000000..7ab81ac --- /dev/null +++ b/rakelib/help.rake @@ -0,0 +1,34 @@ +#-------------------------------------------------------------------- +# help.rake - build file +# +# Author: Martin Corino +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the fuzzr LICENSE which is +# included with this program. +# +# Copyright (c) Remedy IT Expertise BV +#-------------------------------------------------------------------- + +module Fuzz + HELP = <<__HELP_TXT + +fuzzr Rake based build system +------------------------------- + +commands: + +rake [rake-options] help # Provide help description about fuzzr build system +rake [rake-options] gem # Build fuzzr gem + +__HELP_TXT +end + +namespace :fuzz do + task :help do + puts Fuzz::HELP + end +end + +desc 'Provide help description about fuzzr build system' +task :help => 'fuzz:help' diff --git a/test/test_fuzz.rb b/test/test_fuzz.rb new file mode 100644 index 0000000..0866dcb --- /dev/null +++ b/test/test_fuzz.rb @@ -0,0 +1,8 @@ +require "test/unit" +require "fuzz" + +class TestFuzz < Test::Unit::TestCase + def test_sanity + flunk "write tests or I will kneecap you" + end +end