ronin-support 0.3.0 → 0.4.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  #