#
# Copyright (c) 2006-2012 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# This file is part of Ronin Support.
#
# Ronin Support 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 Support 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 Support. If not, see .
#
require 'chars'
require 'combinatorics/generator'
require 'combinatorics/list_comprehension'
module Ronin
module Fuzzing
#
# Fuzzing class that generates Strings based on a template.
#
# @api semipublic
#
# @since 0.5.0
#
class Template
include Enumerable
#
# Initializes a new Fuzzing template.
#
# @param [Array(, )] fields
# The fields which defines the string or character sets which will
# make up parts of the String.
#
# @raise [ArgumentError]
# A given character set name was unknown.
#
# @raise [TypeError]
# A given string set was not a String, Symbol or Enumerable.
# A given string set length was not an Integer or Enumerable.
#
def initialize(fields)
@enumerators = []
fields.each do |(set,length)|
enum = case set
when String
[set].each
when Enumerable
set.each
when Symbol
name = set.to_s.upcase
unless Chars.const_defined?(name)
raise(ArgumentError,"unknown charset #{set.inspect}")
end
Chars.const_get(name).each_char
else
raise(TypeError,"set must be a String, Symbol or Enumerable")
end
case length
when Integer
length.times { @enumerators << enum.dup }
when Array, Range
@enumerators << Combinatorics::Generator.new do |g|
length.each do |sublength|
superset = Array.new(sublength) { enum.dup }
superset.comprehension { |strings| g.yield strings.join }
end
end
when nil
@enumerators << enum
else
raise(TypeError,"length must be an Integer, Range or Array")
end
end
end
#
# @see #initialize
#
def self.[](*fields)
new(fields)
end
#
# Generate permutations of Strings from a format template.
#
# @yield [string]
# The given block will be passed each unique String.
#
# @yieldparam [String] string
# A newly generated String.
#
# @return [Enumerator]
# If no block is given, an Enumerator will be returned.
#
def each
return enum_for(__method__) unless block_given?
@enumerators.comprehension do |fields|
string = ''
fields.each do |field|
string << case field
when Integer
field.chr
else
field.to_s
end
end
yield string
end
return nil
end
end
end
end