ronin-fuzzer 0.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,365 @@
1
+ #
2
+ # ronin-fuzzer - A Ruby library for generating, mutating, and fuzzing data.
3
+ #
4
+ # Copyright (c) 2006-2022 Hal Brodigan (postmodern.mod3 at gmail.com)
5
+ #
6
+ # This file is part of ronin-fuzzer.
7
+ #
8
+ # ronin-fuzzer is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU Lesser General Public License as published
10
+ # by the Free Software Foundation, either version 3 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # ronin-fuzzer is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU Lesser General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU Lesser General Public License
19
+ # along with ronin-fuzzer. If not, see <https://www.gnu.org/licenses/>.
20
+ #
21
+
22
+ require 'ronin/fuzzing/fuzzer'
23
+ require 'ronin/fuzzing/mutator'
24
+ require 'ronin/fuzzing/repeater'
25
+ require 'ronin/fuzzing/core_ext'
26
+
27
+ require 'set'
28
+
29
+ module Ronin
30
+ #
31
+ # Contains class-methods which generate malicious data for fuzzing.
32
+ #
33
+ # @see Fuzzing.[]
34
+ #
35
+ module Fuzzing
36
+ # Short String lengths
37
+ SHORT_LENGTHS = Set[1, 100, 500, 1_000, 10_000]
38
+
39
+ # Long String lengths
40
+ LONG_LENGTHS = Set[
41
+ 128, 255, 256, 257, 511, 512, 513, 1023, 1024, 2048, 2049, 4095,
42
+ 4096, 4097, 5_000, 10_000, 20_000, 32762, 32763, 32764, 32765, 32766,
43
+ 32767, 32768, 32769,
44
+ 0xffff-2, 0xffff-1, 0xffff, 0xffff+1, 0xffff+2,
45
+ 99_999, 100_000, 500_000, 1_000_000
46
+ ]
47
+
48
+ # Null bytes in various encodings
49
+ NULL_BYTES = ['%00', '%u0000', "\x00"]
50
+
51
+ # Newline characters
52
+ NEW_LINES = ["\n", "\r", "\n\r"]
53
+
54
+ # Format String flags
55
+ FORMAT_STRINGS = ['%p', '%s', '%n']
56
+
57
+ #
58
+ # Returns a fuzzer method.
59
+ #
60
+ # @param [Symbol] name
61
+ # The name of the fuzzer to return.
62
+ #
63
+ # @return [Enumerator]
64
+ # An Enumerator for the fuzzer method.
65
+ #
66
+ # @raise [NoMethodError]
67
+ # The fuzzing method could not be found.
68
+ #
69
+ # @api semipublic
70
+ #
71
+ def self.[](name)
72
+ if (!respond_to?(name) || Module.respond_to?(name))
73
+ raise(NoMethodError,"no such fuzzing method: #{name}")
74
+ end
75
+
76
+ return enum_for(name)
77
+ end
78
+
79
+ module_function
80
+
81
+ #
82
+ # Various bad-strings.
83
+ #
84
+ # @yield [string]
85
+ # The given block will be passed each bad-string.
86
+ #
87
+ # @yieldparam [String] string
88
+ # A bad-string containing known control characters, deliminators
89
+ # or null-bytes (see {NULL_BYTES}), of varying length
90
+ # (see {SHORT_LENGTHS} and {LONG_LENGTHS}).
91
+ #
92
+ def bad_strings(&block)
93
+ yield ''
94
+
95
+ chars = [
96
+ 'A', 'a', '1', '<', '>', '"', "'", '/', "\\", '?', '=', 'a=', '&',
97
+ '.', ',', '(', ')', ']', '[', '%', '*', '-', '+', '{', '}',
98
+ "\x14", "\xfe", "\xff"
99
+ ]
100
+
101
+ chars.each do |c|
102
+ LONG_LENGTHS.each { |length| yield c * length }
103
+ end
104
+
105
+ yield '!@#$%%^#$%#$@#$%$$@#$%^^**(()'
106
+ yield '%01%02%03%04%0a%0d%0aADSF'
107
+ yield '%01%02%03@%04%0a%0d%0aADSF'
108
+
109
+ NULL_BYTES.each do |c|
110
+ SHORT_LENGTHS.each { |length| yield c * length }
111
+ end
112
+
113
+ yield "%\xfe\xf0%\x00\xff"
114
+ yield "%\xfe\xf0%\x00\xff" * 20
115
+
116
+ SHORT_LENGTHS.each do |length|
117
+ yield "\xde\xad\xbe\xef" * length
118
+ end
119
+
120
+ yield "\n\r" * 100
121
+ yield "<>" * 500
122
+ end
123
+
124
+ #
125
+ # Various format-strings.
126
+ #
127
+ # @yield [fmt_string]
128
+ # The given block will be passed each format-string.
129
+ #
130
+ # @yieldparam [String] fmt_string
131
+ # A format-string containing format operators (see {FORMAT_STRINGS}).
132
+ #
133
+ def format_strings(&block)
134
+ FORMAT_STRINGS.each do |fmt|
135
+ yield fmt
136
+ yield fmt * 100
137
+ yield fmt * 500
138
+ yield "\"#{fmt}\"" * 500
139
+ end
140
+ end
141
+
142
+ #
143
+ # Various bad paths and directory traversals.
144
+ #
145
+ # @yield [path]
146
+ # The given block will be passed each path.
147
+ #
148
+ # @yieldparam [String] path
149
+ # A known bad path.
150
+ #
151
+ def bad_paths(&block)
152
+ padding = 'A' * 5_000
153
+
154
+ yield "/.:/#{padding}\x00\x00"
155
+ yield "/.../#{padding}\x00\x00"
156
+
157
+ yield "\\\\*"
158
+ yield "\\\\?\\"
159
+
160
+ yield "/\\" * 5_000
161
+ yield '/.' * 5_000
162
+
163
+ NULL_BYTES.each do |c|
164
+ if c.start_with?('%')
165
+ yield "#{c}/"
166
+ yield "/#{c}"
167
+ yield "/#{c}/"
168
+ end
169
+ end
170
+ end
171
+
172
+ #
173
+ # The range of bit-fields.
174
+ #
175
+ # @yield [bitfield]
176
+ # The given block will be passed each bit-field.
177
+ #
178
+ # @yieldparam [String] bitfield
179
+ # A bit-field (8bit - 64bit).
180
+ #
181
+ def bit_fields(&block)
182
+ ("\x00".."\xff").each do |c|
183
+ yield c
184
+ yield c << c # x2
185
+ yield c << c # x4
186
+ yield c << c # x8
187
+ end
188
+ end
189
+
190
+ #
191
+ # The range of signed bit-fields.
192
+ #
193
+ # @yield [bitfield]
194
+ # The given block will be passed each bit-field.
195
+ #
196
+ # @yieldparam [String] bitfield
197
+ # A signed bit-field (8bit - 64bit).
198
+ #
199
+ def signed_bit_fields(&block)
200
+ ("\x80".."\xff").each do |c|
201
+ yield c
202
+ yield c << c # x2
203
+ yield c << c # x4
204
+ yield c << c # x8
205
+ end
206
+ end
207
+
208
+ #
209
+ # The range of unsigned 8bit integers.
210
+ #
211
+ # @yield [int]
212
+ # The given block will be passed each integer.
213
+ #
214
+ # @yieldparam [String] int
215
+ # A unsigned 8bit integer.
216
+ #
217
+ def uint8(&block)
218
+ ("\x00".."\xff").each(&block)
219
+ end
220
+
221
+ #
222
+ # The range of unsigned 16bit integers.
223
+ #
224
+ # @yield [int]
225
+ # The given block will be passed each integer.
226
+ #
227
+ # @yieldparam [String] int
228
+ # A unsigned 16bit integer.
229
+ #
230
+ def uint16
231
+ uint8 { |c| yield c * 2 }
232
+ end
233
+
234
+ #
235
+ # The range of unsigned 32bit integers.
236
+ #
237
+ # @yield [int]
238
+ # The given block will be passed each integer.
239
+ #
240
+ # @yieldparam [String] int
241
+ # A unsigned 32bit integer.
242
+ #
243
+ def uint32
244
+ uint8 { |c| yield c * 4 }
245
+ end
246
+
247
+ #
248
+ # The range of unsigned 64bit integers.
249
+ #
250
+ # @yield [int]
251
+ # The given block will be passed each integer.
252
+ #
253
+ # @yieldparam [String] int
254
+ # A unsigned 64bit integer.
255
+ #
256
+ def uint64
257
+ uint8 { |c| yield c * 8 }
258
+ end
259
+
260
+ #
261
+ # The range of signed 8bit integers.
262
+ #
263
+ # @yield [int]
264
+ # The given block will be passed each integer.
265
+ #
266
+ # @yieldparam [String] int
267
+ # A signed 8bit integer.
268
+ #
269
+ def int8(&block)
270
+ ("\x00".."\x70").each(&block)
271
+ end
272
+
273
+ #
274
+ # The range of signed 16bit integers.
275
+ #
276
+ # @yield [int]
277
+ # The given block will be passed each integer.
278
+ #
279
+ # @yieldparam [String] int
280
+ # A signed 16bit integer.
281
+ #
282
+ def int16
283
+ int8 { |c| yield c * 2 }
284
+ end
285
+
286
+ #
287
+ # The range of signed 32bit integers.
288
+ #
289
+ # @yield [int]
290
+ # The given block will be passed each integer.
291
+ #
292
+ # @yieldparam [String] int
293
+ # A signed 32bit integer.
294
+ #
295
+ def int32
296
+ int8 { |c| yield c * 4 }
297
+ end
298
+
299
+ #
300
+ # The range of signed 64bit integers.
301
+ #
302
+ # @yield [int]
303
+ # The given block will be passed each integer.
304
+ #
305
+ # @yieldparam [String] int
306
+ # A signed 64bit integer.
307
+ #
308
+ def int64
309
+ int8 { |c| yield c * 8 }
310
+ end
311
+
312
+ #
313
+ # The range of negative-signed 8bit integers.
314
+ #
315
+ # @yield [int]
316
+ # The given block will be passed each integer.
317
+ #
318
+ # @yieldparam [String] int
319
+ # A negative-signed 8bit integer.
320
+ #
321
+ def sint8(&block)
322
+ ("\x80".."\xff").each(&block)
323
+ end
324
+
325
+ #
326
+ # The range of negative-signed 16bit integers.
327
+ #
328
+ # @yield [int]
329
+ # The given block will be passed each integer.
330
+ #
331
+ # @yieldparam [String] int
332
+ # A negative-signed 16bit integer.
333
+ #
334
+ def sint16
335
+ sint8 { |c| yield c * 2 }
336
+ end
337
+
338
+ #
339
+ # The range of negative-signed 32bit integers.
340
+ #
341
+ # @yield [int]
342
+ # The given block will be passed each integer.
343
+ #
344
+ # @yieldparam [String] int
345
+ # A negative-signed 32bit integer.
346
+ #
347
+ def sint32
348
+ sint8 { |c| yield c * 4 }
349
+ end
350
+
351
+ #
352
+ # The range of negative-signed 64bit integers.
353
+ #
354
+ # @yield [int]
355
+ # The given block will be passed each integer.
356
+ #
357
+ # @yieldparam [String] int
358
+ # A negative-signed 64bit integer.
359
+ #
360
+ def sint64
361
+ sint8 { |c| yield c * 8 }
362
+ end
363
+
364
+ end
365
+ end
@@ -0,0 +1,95 @@
1
+ .\" Generated by kramdown-man 0.1.8
2
+ .\" https://github.com/postmodern/kramdown-man#readme
3
+ .TH ronin-fuzzer-fuzz 1 "2022-01-01" Ronin Fuzzer "User Manuals"
4
+ .LP
5
+ .SH SYNOPSIS
6
+ .LP
7
+ .HP
8
+ \fBronin-fuzzer fuzz\fR \[lB]\fIoptions\fP\[rB] \[lB]\fITEMPLATE\fP\[rB]
9
+ .LP
10
+ .SH DESCRIPTION
11
+ .LP
12
+ .PP
13
+ Fuzzes data read from a \fIFILE\fP or from \fBSTDIN\fR\. The fuzzed data can be written
14
+ to output files, run in commands or sent to TCP\[sl]UDP services\.
15
+ .LP
16
+ .SH OPTIONS
17
+ .LP
18
+ .TP
19
+ \fB-v\fR, \fB--[no-]verbose\fR
20
+ Enable verbose output\.
21
+ .LP
22
+ .TP
23
+ \fB-q\fR, \fB--[no-]quiet\fR
24
+ Disable verbose output\.
25
+ .LP
26
+ .TP
27
+ \fB--[no-]silent\fR
28
+ Silence all output\.
29
+ .LP
30
+ .TP
31
+ \fB-i\fR, \fB--input\fR \fIFILE\fP
32
+ The input text FILE to parse\. Data will be read from \fBSTDIN\fR by default\.
33
+ .LP
34
+ .HP
35
+ \fB-r\fR, \fB--rule\fR \[lB]\fIPATTERN\fP\[or]\fI\[sl]REGEXP\[sl]\fP\[or]STRING\[rB]:\[lB]\fIMETHOD\fP\[or]\fISTRING\fP\fI*N\fP\[lB]\-\fIM\fP\[rB]\[rB]
36
+ The rule to apply to the \fIINPUT\fP\. Fuzzer rules consist of a pattern and
37
+ substitution\. Patterns may be one of the following:
38
+ .LP
39
+ .nf
40
+ * A name of a Ronin Regular Expression (ex: \`unix\[ru]path\`)
41
+ * A custom Regular Expression (ex: \`\[sl]\ed\[pl]\[sl]\`)
42
+ * A plain String (ex: \`example\.com\`)\.
43
+
44
+ Substitutions may be one of the following:
45
+
46
+ * A method from \`Ronin::Fuzzer\` (ex: \`bad\[ru]strings\`)
47
+ * A *STRING*, repeated *N* or *M* times (ex: \`A*100\-200\`)\.
48
+ .fi
49
+ .LP
50
+ .TP
51
+ \fB-o\fR, \fB--output\fR \fIPATH\fP
52
+ The output PATH to write the fuzzer to\.
53
+ .LP
54
+ .TP
55
+ \fB-c\fR, \fB--command\fR \fICOMMAND\fP
56
+ The command to run with the fuzzed data\. All ocurrences of \fB#string#\fR
57
+ will be replaced with the fuzzed data, and ocurrences of \fB#path#\fR will
58
+ be replaced with the path to the fuzzed data\.
59
+ .LP
60
+ .TP
61
+ \fB-t\fR, \fB--tcp\fR \fIHOST\fP:\fIPORT\fP
62
+ The TCP service to send the fuzzed data to\.
63
+ .LP
64
+ .TP
65
+ \fB-u\fR, \fB--udp\fR \fIHOST\fP:\fIPORT\fP
66
+ The UDP service to send the fuzzed data to\.
67
+ .LP
68
+ .TP
69
+ \fB-p\fR, \fB--pause\fR \fISECONDS\fP
70
+ Pause in between mutations\.
71
+ .LP
72
+ .SH EXAMPLES
73
+ .LP
74
+ .TP
75
+ \fBronin-fuzzer fuzz -i http_request.txt -o bad.txt -r unix_path:bad_strings\fR
76
+ Fuzzes a HTTP request, replacing every occurrence of a UNIX path, with
77
+ strings from the \fBbad_strings\fR method\.
78
+ .LP
79
+ .SH LINKS
80
+ .LP
81
+ .PP
82
+ Ronin Regular Expressions
83
+ https:\[sl]\[sl]ronin\-rb\.dev\[sl]docs\[sl]ronin\-support\[sl]Regexp\.html
84
+ .LP
85
+ .TP
86
+ \fBRonin::Fuzzer\fR
87
+ https:\[sl]\[sl]ronin\-rb\.dev\[sl]docs\[sl]ronin\-fuzzer\[sl]Ronin\[sl]Fuzzer\.html
88
+ .LP
89
+ .SH AUTHOR
90
+ .LP
91
+ .PP
92
+ Postmodern
93
+ .MT postmodern\.mod3\[at]gmail\.com
94
+ .ME
95
+ .LP
@@ -0,0 +1,73 @@
1
+ # ronin-fuzzer-fuzz 1 "2022-01-01" Ronin Fuzzer "User Manuals"
2
+
3
+ ## SYNOPSIS
4
+
5
+ `ronin-fuzzer fuzz` [*options*] [*TEMPLATE*]
6
+
7
+ ## DESCRIPTION
8
+
9
+ Fuzzes data read from a *FILE* or from `STDIN`. The fuzzed data can be written
10
+ to output files, run in commands or sent to TCP/UDP services.
11
+
12
+ ## OPTIONS
13
+
14
+ `-v`, `--[no-]verbose`
15
+ Enable verbose output.
16
+
17
+ `-q`, `--[no-]quiet`
18
+ Disable verbose output.
19
+
20
+ `--[no-]silent`
21
+ Silence all output.
22
+
23
+ `-i`, `--input` *FILE*
24
+ The input text FILE to parse. Data will be read from `STDIN` by default.
25
+
26
+ `-r`, `--rule` [*PATTERN*|*/REGEXP/*|STRING]:[*METHOD*|*STRING***N*[-*M*]]
27
+ The rule to apply to the *INPUT*. Fuzzer rules consist of a pattern and
28
+ substitution. Patterns may be one of the following:
29
+
30
+ * A name of a Ronin Regular Expression (ex: `unix_path`)
31
+ * A custom Regular Expression (ex: `/\d+/`)
32
+ * A plain String (ex: `example.com`).
33
+
34
+ Substitutions may be one of the following:
35
+
36
+ * A method from `Ronin::Fuzzer` (ex: `bad_strings`)
37
+ * A *STRING*, repeated *N* or *M* times (ex: `A*100-200`).
38
+
39
+ `-o`, `--output` *PATH*
40
+ The output PATH to write the fuzzer to.
41
+
42
+ `-c`, `--command` *COMMAND*
43
+ The command to run with the fuzzed data. All ocurrences of `#string#`
44
+ will be replaced with the fuzzed data, and ocurrences of `#path#` will
45
+ be replaced with the path to the fuzzed data.
46
+
47
+ `-t`, `--tcp` *HOST*:*PORT*
48
+ The TCP service to send the fuzzed data to.
49
+
50
+ `-u`, `--udp` *HOST*:*PORT*
51
+ The UDP service to send the fuzzed data to.
52
+
53
+ `-p`, `--pause` *SECONDS*
54
+ Pause in between mutations.
55
+
56
+ ## EXAMPLES
57
+
58
+ `ronin-fuzzer fuzz -i http_request.txt -o bad.txt -r unix_path:bad_strings`
59
+ Fuzzes a HTTP request, replacing every occurrence of a UNIX path, with
60
+ strings from the `bad_strings` method.
61
+
62
+ ## LINKS
63
+
64
+ Ronin Regular Expressions
65
+ https://ronin-rb.dev/docs/ronin-support/Regexp.html
66
+
67
+ `Ronin::Fuzzer`
68
+ https://ronin-rb.dev/docs/ronin-fuzzer/Ronin/Fuzzer.html
69
+
70
+ ## AUTHOR
71
+
72
+ Postmodern <postmodern.mod3@gmail.com>
73
+
@@ -0,0 +1,61 @@
1
+ # encoding: utf-8
2
+
3
+ require 'yaml'
4
+
5
+ Gem::Specification.new do |gem|
6
+ gemspec = YAML.load_file('gemspec.yml')
7
+
8
+ gem.name = gemspec.fetch('name')
9
+ gem.version = gemspec.fetch('version') do
10
+ lib_dir = File.join(File.dirname(__FILE__),'lib')
11
+ $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
12
+
13
+ require 'ronin/fuzzer/version'
14
+ Ronin::Fuzzer::VERSION
15
+ end
16
+
17
+ gem.summary = gemspec['summary']
18
+ gem.description = gemspec['description']
19
+ gem.licenses = Array(gemspec['license'])
20
+ gem.authors = Array(gemspec['authors'])
21
+ gem.email = gemspec['email']
22
+ gem.homepage = gemspec['homepage']
23
+ gem.metadata = gemspec['metadata'] if gemspec['metadata']
24
+
25
+ glob = lambda { |patterns| gem.files & Dir[*patterns] }
26
+
27
+ gem.files = `git ls-files`.split($/)
28
+ gem.files = glob[gemspec['files']] if gemspec['files']
29
+ gem.files += Array(gemspec['generated_files'])
30
+
31
+ gem.executables = gemspec.fetch('executables') do
32
+ glob['bin/*'].map { |path| File.basename(path) }
33
+ end
34
+
35
+ gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
36
+ gem.test_files = glob[gemspec['test_files'] || 'spec/{**/}*_spec.rb']
37
+ gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
38
+
39
+ gem.require_paths = Array(gemspec.fetch('require_paths') {
40
+ %w[ext lib].select { |dir| File.directory?(dir) }
41
+ })
42
+
43
+ gem.requirements = gemspec['requirements']
44
+ gem.required_ruby_version = gemspec['required_ruby_version']
45
+ gem.required_rubygems_version = gemspec['required_rubygems_version']
46
+ gem.post_install_message = gemspec['post_install_message']
47
+
48
+ split = lambda { |string| string.split(/,\s*/) }
49
+
50
+ if gemspec['dependencies']
51
+ gemspec['dependencies'].each do |name,versions|
52
+ gem.add_dependency(name,split[versions])
53
+ end
54
+ end
55
+
56
+ if gemspec['development_dependencies']
57
+ gemspec['development_dependencies'].each do |name,versions|
58
+ gem.add_development_dependency(name,split[versions])
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+ require 'ronin/fuzzing/core_ext/string'
3
+
4
+ describe String do
5
+ it "should provide String.generate" do
6
+ expect(described_class).to respond_to(:generate)
7
+ end
8
+
9
+ it "should provide String#repeating" do
10
+ expect(subject).to respond_to(:repeating)
11
+ end
12
+
13
+ it "should provide String#fuzz" do
14
+ expect(subject).to respond_to(:fuzz)
15
+ end
16
+
17
+ it "should provide String#mutate" do
18
+ expect(subject).to respond_to(:mutate)
19
+ end
20
+
21
+ describe "generate" do
22
+ subject { described_class }
23
+
24
+ it "should generate Strings from a template" do
25
+ strings = subject.generate([:numeric, 2]).to_a
26
+
27
+ expect(strings.grep(/^[0-9]{2}$/)).to eq(strings)
28
+ end
29
+ end
30
+
31
+ describe "#repeating" do
32
+ subject { 'A' }
33
+
34
+ context "when n is an Integer" do
35
+ let(:n) { 100 }
36
+
37
+ it "should multiply the String by n" do
38
+ expect(subject.repeating(n)).to eq(subject * n)
39
+ end
40
+ end
41
+
42
+ context "when n is Enumerable" do
43
+ let(:n) { [128, 512, 1024] }
44
+
45
+ it "should repeat the String by each length" do
46
+ strings = subject.repeating(n).to_a
47
+
48
+ expect(strings).to eq(n.map { |length| subject * length })
49
+ end
50
+ end
51
+ end
52
+
53
+ describe "#fuzz" do
54
+ subject { "foo bar" }
55
+
56
+ it "should apply each fuzzing rule individually" do
57
+ strings = subject.fuzz(/o/ => ['O', '0'], /a/ => ['A', '@']).to_a
58
+
59
+ expect(strings).to match_array([
60
+ "fOo bar",
61
+ "f0o bar",
62
+ "foO bar",
63
+ "fo0 bar",
64
+ "foo bAr",
65
+ "foo b@r"
66
+ ])
67
+ end
68
+ end
69
+
70
+ describe "#mutate" do
71
+ subject { "foo bar" }
72
+
73
+ it "should apply every combination of mutation rules" do
74
+ strings = subject.mutate(/o/ => ['0'], /a/ => ['@']).to_a
75
+
76
+ expect(strings).to match_array([
77
+ "f0o bar",
78
+ "fo0 bar",
79
+ "f00 bar",
80
+ "foo b@r",
81
+ "f0o b@r",
82
+ "fo0 b@r",
83
+ "f00 b@r"
84
+ ])
85
+ end
86
+ end
87
+ end