ronin-support 0.3.0 → 0.4.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/ChangeLog.md +77 -7
  2. data/README.md +19 -3
  3. data/gemspec.yml +2 -2
  4. data/lib/ronin/extensions/regexp.rb +50 -2
  5. data/lib/ronin/extensions/string.rb +1 -0
  6. data/lib/ronin/formatting.rb +1 -0
  7. data/lib/ronin/formatting/extensions.rb +1 -0
  8. data/lib/ronin/formatting/extensions/binary/string.rb +56 -5
  9. data/lib/ronin/formatting/extensions/html/string.rb +6 -7
  10. data/lib/ronin/formatting/extensions/sql/string.rb +34 -0
  11. data/lib/ronin/formatting/extensions/text/string.rb +0 -180
  12. data/lib/ronin/fuzzing.rb +21 -0
  13. data/lib/ronin/fuzzing/extensions.rb +20 -0
  14. data/lib/ronin/fuzzing/extensions/string.rb +380 -0
  15. data/lib/ronin/fuzzing/fuzzing.rb +191 -0
  16. data/lib/ronin/network/esmtp.rb +94 -1
  17. data/lib/ronin/network/extensions/esmtp/net.rb +2 -82
  18. data/lib/ronin/network/extensions/http/net.rb +1 -736
  19. data/lib/ronin/network/extensions/imap/net.rb +1 -103
  20. data/lib/ronin/network/extensions/pop3/net.rb +1 -71
  21. data/lib/ronin/network/extensions/smtp/net.rb +2 -157
  22. data/lib/ronin/network/extensions/ssl/net.rb +1 -132
  23. data/lib/ronin/network/extensions/tcp/net.rb +2 -296
  24. data/lib/ronin/network/extensions/telnet/net.rb +1 -135
  25. data/lib/ronin/network/extensions/udp/net.rb +2 -214
  26. data/lib/ronin/network/http/http.rb +750 -5
  27. data/lib/ronin/network/imap.rb +105 -2
  28. data/lib/ronin/network/mixins.rb +1 -1
  29. data/lib/ronin/network/mixins/esmtp.rb +49 -52
  30. data/lib/ronin/network/mixins/http.rb +49 -53
  31. data/lib/ronin/network/mixins/imap.rb +47 -44
  32. data/lib/ronin/network/mixins/mixin.rb +58 -0
  33. data/lib/ronin/network/mixins/pop3.rb +44 -38
  34. data/lib/ronin/network/mixins/smtp.rb +49 -51
  35. data/lib/ronin/network/mixins/tcp.rb +56 -69
  36. data/lib/ronin/network/mixins/telnet.rb +57 -50
  37. data/lib/ronin/network/mixins/udp.rb +48 -52
  38. data/lib/ronin/network/network.rb +1 -0
  39. data/lib/ronin/network/pop3.rb +72 -2
  40. data/lib/ronin/network/smtp/email.rb +1 -0
  41. data/lib/ronin/network/smtp/smtp.rb +159 -3
  42. data/lib/ronin/network/ssl.rb +131 -2
  43. data/lib/ronin/network/tcp.rb +306 -1
  44. data/lib/ronin/network/telnet.rb +136 -2
  45. data/lib/ronin/network/udp.rb +229 -1
  46. data/lib/ronin/support.rb +2 -3
  47. data/lib/ronin/support/support.rb +38 -0
  48. data/lib/ronin/support/version.rb +1 -1
  49. data/lib/ronin/templates/erb.rb +2 -1
  50. data/lib/ronin/ui/output/helpers.rb +35 -1
  51. data/lib/ronin/ui/shell.rb +12 -2
  52. data/lib/ronin/wordlist.rb +157 -0
  53. data/spec/extensions/regexp_spec.rb +38 -0
  54. data/spec/formatting/html/string_spec.rb +1 -1
  55. data/spec/formatting/sql/string_spec.rb +23 -3
  56. data/spec/formatting/text/string_spec.rb +0 -110
  57. data/spec/fuzzing/string_spec.rb +158 -0
  58. data/spec/wordlist_spec.rb +65 -0
  59. metadata +35 -27
data/ChangeLog.md CHANGED
@@ -1,3 +1,73 @@
1
+ ### 0.4.0 / 2012-01-01
2
+
3
+ * Require uri-query_params ~> 0.6.
4
+ * Require parameters ~> 0.4.
5
+ * Added {Regexp::DELIM}.
6
+ * Added {Regexp::IDENTIFIER}.
7
+ * Added {Regexp::OCTET}.
8
+ * Added {Regexp::FILE_EXT}.
9
+ * Added {Regexp::FILE_NAME}.
10
+ * Added {Regexp::FILE}.
11
+ * Added {Regexp::DIRECTORY}.
12
+ * Added {Regexp::LOCAL_UNIX_PATH}.
13
+ * Added {Regexp::ABSOLUTE_UNIX_PATH}.
14
+ * Added {Regexp::UNIX_PATH}.
15
+ * Added {Regexp::LOCAL_WINDOWS_PATH}.
16
+ * Added {Regexp::ABSOLUTE_WINDOWS_PATH}.
17
+ * Added {Regexp::WINDOWS_PATH}.
18
+ * Added {Regexp::LOCAL_PATH}.
19
+ * Added {Regexp::ABSOLUTE_PATH}.
20
+ * Added {Regexp::PATH}.
21
+ * Added {String#repeating}.
22
+ * Added {String#sql_inject}.
23
+ * Added {String#mutate}.
24
+ * Added {Ronin::Fuzzing}.
25
+ * Added {Ronin::Fuzzing.[]}.
26
+ * Added {Ronin::Fuzzing.bad_strings}.
27
+ * Added {Ronin::Fuzzing.format_strings}.
28
+ * Added {Ronin::Fuzzing.bad_paths}.
29
+ * Added {Ronin::Fuzzing.bit_fields}.
30
+ * Added {Ronin::Fuzzing.signed_bit_fields}.
31
+ * Added {Ronin::Fuzzing.uint8}.
32
+ * Added {Ronin::Fuzzing.uint16}.
33
+ * Added {Ronin::Fuzzing.uint32}.
34
+ * Added {Ronin::Fuzzing.uint64}.
35
+ * Added {Ronin::Fuzzing.int8}.
36
+ * Added {Ronin::Fuzzing.int16}.
37
+ * Added {Ronin::Fuzzing.int32}.
38
+ * Added {Ronin::Fuzzing.int64}.
39
+ * Added {Ronin::Fuzzing.sint8}.
40
+ * Added {Ronin::Fuzzing.sint16}.
41
+ * Added {Ronin::Fuzzing.sint32}.
42
+ * Added {Ronin::Fuzzing.sint64}.
43
+ * Added {Ronin::Wordlist}.
44
+ * Added {Ronin::Network::Mixins::Mixin}.
45
+ * Added {Ronin::UI::Output::Helpers#print_exception}.
46
+ * Made {Regexp::HOST_NAME} case-insensitive.
47
+ * Refactored {Regexp::IPv4} to not match invalid IPv4 addresses.
48
+ * Require `ronin/formatting/html` in `ronin/formatting`.
49
+ * Allow {String#base64_encode} and {String#base64_decode} to accept a formatting
50
+ argument.
51
+ * `:normal`
52
+ * `:strict`
53
+ * `:url` / `:urlsafe`
54
+ * Fixed a bug in {String#js_unescape}, where `%uXX` chars were not being
55
+ unescaped (thanks isis!).
56
+ * Have {String#fuzz} only accept `Regexp` and `String` objects.
57
+ * Moved {String#fuzz} and {String.generate} into `ronin/fuzzing`.
58
+ * Moved `Net.*` methods into the {Ronin::Network} modules.
59
+ * Fixed bugs in {Ronin::Network::UDP#udp_connect} and
60
+ {Ronin::Network::UDP#udp_server}.
61
+ * Fixed a bug in {Ronin::Network::Mixins::HTTP#http_session}, where
62
+ normalized options were not being yielded.
63
+ * Allow {Ronin::Templates::Erb} to use `<%- -%>` syntax.
64
+ * Alias `<<` to `write` in {Ronin::UI::Output::Helpers}.
65
+ * Fixed bugs in {Ronin::UI::Shell}.
66
+ * Warning messages are printed by {Ronin::UI::Output::Helpers}, unless output
67
+ is silenced.
68
+ * {Ronin::UI::Output::Helpers} and {Ronin::Network} modules are included into
69
+ {Ronin::Support}.
70
+
1
71
  ### 0.3.0 / 2011-10-16
2
72
 
3
73
  * Require combinatorics ~> 0.4.
@@ -52,13 +122,13 @@
52
122
  * Added {String#js_escape}.
53
123
  * Added {String#js_unescape}.
54
124
  * Added {String#format_js}.
55
- * Added {Net.smtp_send_message}.
56
- * Added {Net.http_status}.
57
- * Added {Net.http_get_headers}.
58
- * Added {Net.http_post_headers}.
125
+ * Added `Net.smtp_send_message`.
126
+ * Added `Net.http_status`.
127
+ * Added `Net.http_get_headers`.
128
+ * Added `Net.http_post_headers`.
59
129
  * Added YARD `@api` tags to define the public, semi-public and private APIs.
60
130
  * Renamed `Kernel#attempt` to {Kernel#try}.
61
- * Allow `:method` to be used with {Net.http_ok?}.
131
+ * Allow `:method` to be used with `Net.http_ok?`.
62
132
  * Fixed a bug in {Ronin::Network::HTTP.expand_url} where `:host` and `:port`
63
133
  options were being overridden.
64
134
  * Improved the performance of {Integer#bytes}.
@@ -68,8 +138,8 @@
68
138
  for unicode characters.
69
139
  * Deprecated {String#common_postfix}, in favor of {String#common_suffix}.
70
140
  {String#common_postfix} will be removed in ronin-support 1.0.0.
71
- * {Net.http_get_body} no longer accepts a block.
72
- * {Net.http_post_body} no longer accepts a block.
141
+ * `Net.http_get_body` no longer accepts a block.
142
+ * `Net.http_post_body` no longer accepts a block.
73
143
 
74
144
  ### 0.1.0 / 2011-03-20
75
145
 
data/README.md CHANGED
@@ -26,7 +26,7 @@ or payloads over many common Source-Code-Management (SCM) systems.
26
26
  * HTML
27
27
  * JavaScript
28
28
  * SQL
29
- * Generating random text.
29
+ * Fuzzing
30
30
  * Networking:
31
31
  * TCP
32
32
  * UDP
@@ -40,6 +40,13 @@ or payloads over many common Source-Code-Management (SCM) systems.
40
40
  * CIDR / globbed ranges.
41
41
  * (Un-)Hexdumping data.
42
42
  * Handling exceptions.
43
+ * Provides Modules/Classes for:
44
+ * Paths
45
+ * Wordlists
46
+ * Erb Templates
47
+ * UI:
48
+ * Terminal Output
49
+ * Custom Shells
43
50
 
44
51
  ## Examples
45
52
 
@@ -56,16 +63,25 @@ please see [Everyday Ronin](http://ronin-ruby.github.com/guides/everyday_ronin.h
56
63
  * [combinatorics](http://github.com/postmodern/combinatorics#readme)
57
64
  ~> 0.4
58
65
  * [uri-query_params](http://github.com/postmodern/uri-query_params#readme)
59
- ~> 0.5, >= 0.5.2
66
+ ~> 0.6
60
67
  * [data_paths](http://github.com/postmodern/data_paths#readme)
61
68
  ~> 0.3
62
69
  * [parameters](http://github.com/postmodern/parameters#readme)
63
- ~> 0.2, >= 0.2.3
70
+ ~> 0.4
64
71
 
65
72
  ## Install
66
73
 
74
+ ### Stable
75
+
67
76
  $ gem install ronin-support
68
77
 
78
+ ### Edge
79
+
80
+ $ git clone git://github.com/ronin-ruby/ronin-support.git
81
+ $ cd ronin-support/
82
+ $ bundle install
83
+ $ rake console
84
+
69
85
  ## License
70
86
 
71
87
  Copyright (c) 2006-2011 Hal Brodigan (postmodern.mod3 at gmail.com)
data/gemspec.yml CHANGED
@@ -16,9 +16,9 @@ dependencies:
16
16
  chars: ~> 0.2
17
17
  hexdump: ~> 0.1
18
18
  combinatorics: ~> 0.4
19
- uri-query_params: ~> 0.5, >= 0.5.2
19
+ uri-query_params: ~> 0.6
20
20
  data_paths: ~> 0.3
21
- parameters: ~> 0.2, >= 0.2.3
21
+ parameters: ~> 0.4
22
22
 
23
23
  development_dependencies:
24
24
  bundler: ~> 1.0.10
@@ -21,11 +21,14 @@ require 'ronin/extensions/resolv'
21
21
 
22
22
  class Regexp
23
23
 
24
+ # Regular expression for finding a decimal octet (0 - 255)
25
+ OCTET = /25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9]/
26
+
24
27
  # Regular expression for finding MAC addresses in text
25
28
  MAC = /[0-9a-fA-F]{2}(?::[0-9a-fA-F]{2}){5}/
26
29
 
27
30
  # A regular expression for matching IPv4 Addresses.
28
- IPv4 = /[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}/
31
+ IPv4 = /#{OCTET}(?:\.#{OCTET}){3}/
29
32
 
30
33
  # A regular expression for matching IPv6 Addresses.
31
34
  IPv6 = /:(:[0-9a-f]{1,4}){1,7}|([0-9a-f]{1,4}::?){1,7}[0-9a-f]{1,4}(:#{IPv4})?/
@@ -34,7 +37,7 @@ class Regexp
34
37
  IP = /#{IPv4}|#{IPv6}/
35
38
 
36
39
  # Regular expression used to find host-names in text
37
- HOST_NAME = /(?:[a-zA-Z0-9]+(?:[_-][a-zA-Z0-9]+)*\.)+(?:#{union(Resolv::TLDS)})/
40
+ HOST_NAME = /(?:[a-zA-Z0-9]+(?:[_-][a-zA-Z0-9]+)*\.)+(?:#{union(Resolv::TLDS)})/i
38
41
 
39
42
  # Regular expression to match a word in the username of an email address
40
43
  USER_NAME = /[A-Za-z](?:[A-Za-z0-9]+[\._-])*[A-Za-z0-9]+/
@@ -42,4 +45,49 @@ class Regexp
42
45
  # Regular expression to find email addresses in text
43
46
  EMAIL_ADDR = /#{USER_NAME}(?:\.#{USER_NAME})*\@#{HOST_NAME}/
44
47
 
48
+ # Regular expression to find deliminators in text
49
+ DELIM = /[;&\n\r]/
50
+
51
+ # Regular expression to find identifier in text
52
+ IDENTIFIER = /[_a-zA-Z][a-zA-Z0-9_-]*/
53
+
54
+ # Regular expression to find File extensions in text
55
+ FILE_EXT = /(?:\.[A-Za-z0-9_-]+)+/
56
+
57
+ # Regular expression to find File names in text
58
+ FILE_NAME = /(?:[^\/\\\. ]|\\[\/\\ ])+/
59
+
60
+ # Regular expression to find Files in text
61
+ FILE = /#{FILE_NAME}(?:#{FILE_EXT})?/
62
+
63
+ # Regular expression to find Directory names in text
64
+ DIRECTORY = /(?:\.\.|\.|#{FILE})/
65
+
66
+ # Regular expression to find local UNIX Paths in text
67
+ LOCAL_UNIX_PATH = /(?:#{DIRECTORY}\/)+#{DIRECTORY}\/?/
68
+
69
+ # Regular expression to find absolute UNIX Paths in text
70
+ ABSOLUTE_UNIX_PATH = /(?:\/#{FILE})+\/?/
71
+
72
+ # Regular expression to find UNIX Paths in text
73
+ UNIX_PATH = /#{ABSOLUTE_UNIX_PATH}|#{LOCAL_UNIX_PATH}/
74
+
75
+ # Regular expression to find local Windows Paths in text
76
+ LOCAL_WINDOWS_PATH = /(?:#{DIRECTORY}\\)+#{DIRECTORY}\\?/
77
+
78
+ # Regular expression to find absolute Windows Paths in text
79
+ ABSOLUTE_WINDOWS_PATH = /[A-Za-z]:(?:\\#{DIRECTORY})+\\?/
80
+
81
+ # Regular expression to find Windows Paths in text
82
+ WINDOWS_PATH = /#{ABSOLUTE_WINDOWS_PATH}|#{LOCAL_WINDOWS_PATH}/
83
+
84
+ # Regular expression to find local Paths in text
85
+ LOCAL_PATH = /#{LOCAL_UNIX_PATH}|#{LOCAL_WINDOWS_PATH}/
86
+
87
+ # Regular expression to find absolute Paths in text
88
+ ABSOLUTE_PATH = /#{ABSOLUTE_UNIX_PATH}|#{ABSOLUTE_WINDOWS_PATH}/
89
+
90
+ # Regular expression to find Paths in text
91
+ PATH = /#{UNIX_PATH}|#{WINDOWS_PATH}/
92
+
45
93
  end
@@ -194,6 +194,7 @@ class String
194
194
  end
195
195
 
196
196
  if RUBY_VERSION < '1.9.'
197
+ # Special ASCII bytes and their escaped character forms
197
198
  ESCAPE_BYTES = Hash.new do |escape,byte|
198
199
  escape[byte] = if (byte >= 0x20 && byte <= 0x7e)
199
200
  byte.chr
@@ -22,3 +22,4 @@ require 'ronin/formatting/digest'
22
22
  require 'ronin/formatting/binary'
23
23
  require 'ronin/formatting/text'
24
24
  require 'ronin/formatting/http'
25
+ require 'ronin/formatting/html'
@@ -21,3 +21,4 @@ require 'ronin/formatting/extensions/binary'
21
21
  require 'ronin/formatting/extensions/text'
22
22
  require 'ronin/formatting/extensions/digest'
23
23
  require 'ronin/formatting/extensions/http'
24
+ require 'ronin/formatting/extensions/html'
@@ -232,6 +232,13 @@ class String
232
232
  #
233
233
  # Base64 encodes a string.
234
234
  #
235
+ # @param [Symbol, nil] mode
236
+ # The base64 mode to use. May be either:
237
+ #
238
+ # * `:normal`
239
+ # * `:strict`
240
+ # * `:url` / `:urlsafe`
241
+ #
235
242
  # @return [String]
236
243
  # The base64 encoded form of the string.
237
244
  #
@@ -241,24 +248,68 @@ class String
241
248
  #
242
249
  # @api public
243
250
  #
244
- def base64_encode
245
- Base64.encode64(self)
251
+ def base64_encode(mode=nil)
252
+ case mode
253
+ when :strict
254
+ if RUBY_VERSION < '1.9'
255
+ # backported from Ruby 1.9.2
256
+ [self].pack("m")
257
+ else
258
+ Base64.strict_encode64(self)
259
+ end
260
+ when :url, :urlsafe
261
+ if RUBY_VERSION < '1.9'
262
+ # backported from Ruby 1.9.2
263
+ [self].pack("m").tr("+/", "-_")
264
+ else
265
+ Base64.urlsafe_encode64(self)
266
+ end
267
+ else
268
+ Base64.encode64(self)
269
+ end
246
270
  end
247
271
 
248
272
  #
249
273
  # Base64 decodes a string.
250
274
  #
275
+ # @param [Symbol, nil] mode
276
+ # The base64 mode to use. May be either:
277
+ #
278
+ # * `nil`
279
+ # * `:strict`
280
+ # * `:url` / `:urlsafe`
281
+ #
251
282
  # @return [String]
252
283
  # The base64 decoded form of the string.
253
284
  #
285
+ # @note
286
+ # `mode` argument is only available on Ruby >= 1.9.
287
+ #
254
288
  # @example
255
- # "aGVsbG8=\n"
289
+ # "aGVsbG8=\n".base64_decode
256
290
  # # => "hello"
257
291
  #
258
292
  # @api public
259
293
  #
260
- def base64_decode
261
- Base64.decode64(self)
294
+ def base64_decode(mode=nil)
295
+ case mode
296
+ when :strict
297
+ if RUBY_VERSION < '1.9'
298
+ # backported from Ruby 1.9.2
299
+ unpack("m0").first
300
+ else
301
+ Base64.strict_decode64(self)
302
+ end
303
+ when :url, :urlsafe
304
+ if RUBY_VERSION < '1.9'
305
+ # backported from Ruby 1.9.2
306
+ tr("-_", "+/").unpack("m0").first
307
+ else
308
+ Base64.urlsafe_decode64(self)
309
+ end
310
+ else
311
+ Base64.decode64(self)
312
+ end
262
313
  end
263
314
 
264
315
  #
@@ -152,16 +152,15 @@ class String
152
152
  def js_unescape
153
153
  unescaped = ''
154
154
 
155
- scan(/([\\%]u[0-9a-fA-F]{4}|[\\%][0-9a-fA-F]{2}|\\[btnfr"\\]|.)/).each do |match|
155
+ scan(/([\\%]u[0-9a-fA-F]{1,4}|[\\%][0-9a-fA-F]{1,2}|\\[btnfr"\\]|.)/).each do |match|
156
156
  c = match[0]
157
157
 
158
- unescaped << case c.length
159
- when 6
160
- c[2,4].to_i(16)
161
- when 3
162
- c[1,2].to_i(16)
163
- when 2
158
+ unescaped << if JS_BACKSLASHED_CHARS.has_key?(c)
164
159
  JS_BACKSLASHED_CHARS[c]
160
+ elsif (c.start_with?("\\u") || c.start_with?("%u"))
161
+ c[2..-1].to_i(16)
162
+ elsif (c.start_with?("\\") || c.start_with?("%"))
163
+ c[1..-1].to_i(16)
165
164
  else
166
165
  c
167
166
  end
@@ -95,4 +95,38 @@ class String
95
95
  end
96
96
  end
97
97
 
98
+ #
99
+ # Prepares the String for injection into a SQL expression.
100
+ #
101
+ # @return [String]
102
+ # The SQL injection ready String.
103
+ #
104
+ # @example
105
+ # "'1' OR '1'='1'".sql_inject
106
+ # # => "1' OR '1'='1"
107
+ #
108
+ # @example
109
+ # "'1' OR 1=1".sql_inject
110
+ # # => "1' OR 1=1 OR '"
111
+ #
112
+ # @example
113
+ # "'1' OR 1=1".sql_inject(:terminate => true)
114
+ # # => "1' OR 1=1 --"
115
+ #
116
+ # @api public
117
+ #
118
+ # @since 0.4.0
119
+ #
120
+ def sql_inject
121
+ if (start_with?("'") || start_with?('"') || start_with?('`'))
122
+ if self[0,1] == self[-1,1]
123
+ self[1..-2]
124
+ else
125
+ "#{self[1..-1]}--"
126
+ end
127
+ else
128
+ self
129
+ end
130
+ end
131
+
98
132
  end
@@ -17,190 +17,10 @@
17
17
  # along with Ronin Support. If not, see <http://www.gnu.org/licenses/>.
18
18
  #
19
19
 
20
- require 'combinatorics/list_comprehension'
21
- require 'combinatorics/generator'
22
- require 'chars'
23
20
  require 'set'
24
21
 
25
22
  class String
26
23
 
27
- #
28
- # Generate permutations of Strings from a format template.
29
- #
30
- # @param [Array(<String, Symbol, Enumerable>, <Integer, Enumerable>)] template
31
- # The template which defines the string or character sets which will
32
- # make up parts of the String.
33
- #
34
- # @yield [string]
35
- # The given block will be passed each unique String.
36
- #
37
- # @yieldparam [String] string
38
- # A newly generated String.
39
- #
40
- # @return [Enumerator]
41
- # If no block is given, an Enumerator will be returned.
42
- #
43
- # @raise [ArgumentError]
44
- # A given character set name was unknown.
45
- #
46
- # @raise [TypeError]
47
- # A given string set was not a String, Symbol or Enumerable.
48
- # A given string set length was not an Integer or Enumerable.
49
- #
50
- # @example Generate Strings with ranges of repeating sub-strings.
51
- #
52
- # @example Generate Strings with three alpha chars and one numeric chars.
53
- # String.generate([:alpha, 3], :numeric) do |password|
54
- # puts password
55
- # end
56
- #
57
- # @example Generate Strings with two to four alpha chars.
58
- # String.generate([:alpha, 2..4]) do |password|
59
- # puts password
60
- # end
61
- #
62
- # @example Generate Strings using alpha and punctuation chars.
63
- # String.generate([Chars.alpha + Chars.punctuation, 4]) do |password|
64
- # puts password
65
- # end
66
- #
67
- # @example Generate Strings from a custom char set.
68
- # String.generate([['a', 'b', 'c'], 3], [['1', '2', '3'], 3]) do |password|
69
- # puts password
70
- # end
71
- #
72
- # @example Generate Strings containing known Strings.
73
- # String.generate("rock", [:numeric, 4]) do |password|
74
- # puts password
75
- # end
76
- #
77
- # @example Generate Strings with ranges of repeating sub-strings.
78
- # String.generate(['/AA', (1..100).step(5)]) do |path|
79
- # puts path
80
- # end
81
- #
82
- # @since 0.3.0
83
- #
84
- # @api public
85
- #
86
- def self.generate(*template)
87
- return enum_for(:generate,*template) unless block_given?
88
-
89
- sets = []
90
-
91
- template.each do |pattern|
92
- set, length = pattern
93
- set = case set
94
- when String
95
- [set].each
96
- when Symbol
97
- name = set.to_s.upcase
98
-
99
- unless Chars.const_defined?(name)
100
- raise(ArgumentError,"unknown charset #{set.inspect}")
101
- end
102
-
103
- Chars.const_get(name).each_char
104
- when Enumerable
105
- Chars::CharSet.new(set).each_char
106
- else
107
- raise(TypeError,"set must be a String, Symbol or Enumerable")
108
- end
109
-
110
- case length
111
- when Integer
112
- length.times { sets << set.dup }
113
- when Enumerable
114
- sets << Combinatorics::Generator.new do |g|
115
- length.each do |sublength|
116
- superset = Array.new(sublength) { set.dup }
117
-
118
- superset.comprehension { |strings| g.yield strings.join }
119
- end
120
- end
121
- when nil
122
- sets << set
123
- else
124
- raise(TypeError,"length must be an Integer or Enumerable")
125
- end
126
- end
127
-
128
- sets.comprehension { |strings| yield strings.join }
129
- return nil
130
- end
131
-
132
- #
133
- # Incrementally fuzzes the String.
134
- #
135
- # @param [Hash{Regexp,String,Integer,Enumerable => #each}] mutations
136
- # Patterns and their substitutions.
137
- #
138
- # @yield [fuzz]
139
- # The given block will be passed every fuzzed String.
140
- #
141
- # @yieldparam [String] fuzz
142
- # A fuzzed String.
143
- #
144
- # @return [Enumerator]
145
- # If no block is given, an Enumerator will be returned.
146
- #
147
- # @example Replace every `e`, `i`, `o`, `u` with `(`, 100 `A`s and a `\0`:
148
- # "the quick brown fox".fuzz(/[eiou]/ => ['(', ('A' * 100), "\0"]) do |str|
149
- # p str
150
- # end
151
- #
152
- # @example {String.generate} with {String.fuzz}:
153
- # "GET /".fuzz('/' => String.generate(['A', 1..100])) do |str|
154
- # p str
155
- # end
156
- #
157
- # @since 0.3.0
158
- #
159
- # @api public
160
- #
161
- def fuzz(mutations={})
162
- return enum_for(:fuzz,mutations) unless block_given?
163
-
164
- mutations.each do |pattern,substitution|
165
- pattern = case pattern
166
- when Regexp
167
- pattern
168
- when String
169
- Regexp.new(Regexp.escape(pattern))
170
- when Integer
171
- Regexp.new(pattern.chr)
172
- when Enumerable
173
- Regexp.union(pattern.map { |s| Regexp.escape(s.to_s) })
174
- else
175
- raise(TypeError,"cannot convert #{pattern.inspect} to a Regexp")
176
- end
177
-
178
- scanner = StringScanner.new(self)
179
- indices = []
180
-
181
- while scanner.scan_until(pattern)
182
- indices << [scanner.pos - scanner.matched_size, scanner.matched_size]
183
- end
184
-
185
- indices.each do |index,length|
186
- substitution.each do |substitute|
187
- substitute = case substitute
188
- when Proc
189
- substitute.call(self[index,length])
190
- when Integer
191
- substitute.chr
192
- else
193
- substitute.to_s
194
- end
195
-
196
- fuzzed = dup
197
- fuzzed[index,length] = substitute
198
- yield fuzzed
199
- end
200
- end
201
- end
202
- end
203
-
204
24
  #
205
25
  # Creates a new String by formatting each byte.
206
26
  #