# # ronin-fuzzer - A Ruby library for generating, mutating, and fuzzing data. # # Copyright (c) 2006-2023 Hal Brodigan (postmodern.mod3 at gmail.com) # # This file is part of ronin-fuzzer. # # ronin-fuzzer is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # ronin-fuzzer is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with ronin-fuzzer. If not, see . # require 'ronin/fuzzing' require 'ronin/support/text/patterns' require 'strscan' module Ronin module Fuzzing # # Fuzzing class that incrementally fuzzes a String, given substitution # rules. # # @api semipublic # class Fuzzer PATTERNS = Support::Text::Patterns # Patterns and their substitutions attr_reader :rules # # Initializes a new Fuzzer. # # @param [Hash{Regexp,String,Symbol => Enumerable,Symbol}] rules # Patterns and their substitutions. # def initialize(rules) @rules = {} rules.each do |pattern,substitution| pattern = case pattern when Regexp then pattern when String then Regexp.new(Regexp.escape(pattern)) when Symbol then PATTERNS.const_get(pattern.upcase) else raise(TypeError,"cannot convert #{pattern.inspect} to a Regexp") end substitution = case substitution when Enumerable then substitution when Symbol then Fuzzing[substitution] else raise(TypeError,"substitutions must be Enumerable or a Symbol") end @rules[pattern] = substitution end end # # Incrementally fuzzes the String. # # @yield [fuzz] # The given block will be passed every fuzzed String. # # @yieldparam [String] fuzz # A fuzzed String. # # @return [Enumerator] # If no block is given, an Enumerator will be returned. # def each(string) return enum_for(__method__,string) unless block_given? @rules.each do |pattern,substitution| scanner = StringScanner.new(string) indices = [] while scanner.scan_until(pattern) indices << [scanner.pos - scanner.matched_size, scanner.matched_size] end indices.each do |index,length| substitution.each do |substitute| substitute = case substitute when Proc then substitute[string[index,length]] when Integer then substitute.chr else substitute.to_s end fuzzed = string.dup fuzzed[index,length] = substitute yield fuzzed end end end end end end end