wordlist 0.1.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (148) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ruby.yml +27 -0
  3. data/.gitignore +6 -3
  4. data/ChangeLog.md +45 -1
  5. data/Gemfile +13 -0
  6. data/LICENSE.txt +1 -3
  7. data/README.md +266 -61
  8. data/Rakefile +7 -32
  9. data/benchmarks.rb +115 -0
  10. data/bin/wordlist +4 -7
  11. data/data/stop_words/ar.txt +104 -0
  12. data/data/stop_words/bg.txt +259 -0
  13. data/data/stop_words/bn.txt +363 -0
  14. data/data/stop_words/ca.txt +126 -0
  15. data/data/stop_words/cs.txt +138 -0
  16. data/data/stop_words/da.txt +101 -0
  17. data/data/stop_words/de.txt +129 -0
  18. data/data/stop_words/el.txt +79 -0
  19. data/data/stop_words/en.txt +175 -0
  20. data/data/stop_words/es.txt +178 -0
  21. data/data/stop_words/eu.txt +98 -0
  22. data/data/stop_words/fa.txt +332 -0
  23. data/data/stop_words/fi.txt +747 -0
  24. data/data/stop_words/fr.txt +116 -0
  25. data/data/stop_words/ga.txt +109 -0
  26. data/data/stop_words/gl.txt +160 -0
  27. data/data/stop_words/he.txt +499 -0
  28. data/data/stop_words/hi.txt +97 -0
  29. data/data/stop_words/hr.txt +179 -0
  30. data/data/stop_words/hu.txt +35 -0
  31. data/data/stop_words/hy.txt +45 -0
  32. data/data/stop_words/id.txt +357 -0
  33. data/data/stop_words/it.txt +134 -0
  34. data/data/stop_words/ja.txt +44 -0
  35. data/data/stop_words/ko.txt +677 -0
  36. data/data/stop_words/ku.txt +63 -0
  37. data/data/stop_words/lt.txt +507 -0
  38. data/data/stop_words/lv.txt +163 -0
  39. data/data/stop_words/mr.txt +99 -0
  40. data/data/stop_words/nl.txt +48 -0
  41. data/data/stop_words/no.txt +172 -0
  42. data/data/stop_words/pl.txt +138 -0
  43. data/data/stop_words/pt.txt +147 -0
  44. data/data/stop_words/ro.txt +281 -0
  45. data/data/stop_words/ru.txt +421 -0
  46. data/data/stop_words/sk.txt +173 -0
  47. data/data/stop_words/sv.txt +386 -0
  48. data/data/stop_words/th.txt +115 -0
  49. data/data/stop_words/tr.txt +114 -0
  50. data/data/stop_words/uk.txt +28 -0
  51. data/data/stop_words/ur.txt +513 -0
  52. data/data/stop_words/zh.txt +125 -0
  53. data/gemspec.yml +4 -10
  54. data/lib/wordlist/abstract_wordlist.rb +24 -0
  55. data/lib/wordlist/builder.rb +170 -138
  56. data/lib/wordlist/cli.rb +458 -0
  57. data/lib/wordlist/compression/reader.rb +72 -0
  58. data/lib/wordlist/compression/writer.rb +80 -0
  59. data/lib/wordlist/exceptions.rb +31 -0
  60. data/lib/wordlist/file.rb +176 -0
  61. data/lib/wordlist/format.rb +38 -0
  62. data/lib/wordlist/lexer/lang.rb +32 -0
  63. data/lib/wordlist/lexer/stop_words.rb +68 -0
  64. data/lib/wordlist/lexer.rb +218 -0
  65. data/lib/wordlist/list_methods.rb +462 -0
  66. data/lib/wordlist/modifiers/capitalize.rb +45 -0
  67. data/lib/wordlist/modifiers/downcase.rb +45 -0
  68. data/lib/wordlist/modifiers/gsub.rb +51 -0
  69. data/lib/wordlist/modifiers/modifier.rb +44 -0
  70. data/lib/wordlist/modifiers/mutate.rb +133 -0
  71. data/lib/wordlist/modifiers/mutate_case.rb +25 -0
  72. data/lib/wordlist/modifiers/sub.rb +97 -0
  73. data/lib/wordlist/modifiers/tr.rb +71 -0
  74. data/lib/wordlist/modifiers/upcase.rb +45 -0
  75. data/lib/wordlist/modifiers.rb +8 -0
  76. data/lib/wordlist/operators/binary_operator.rb +38 -0
  77. data/lib/wordlist/operators/concat.rb +47 -0
  78. data/lib/wordlist/operators/intersect.rb +55 -0
  79. data/lib/wordlist/operators/operator.rb +29 -0
  80. data/lib/wordlist/operators/power.rb +72 -0
  81. data/lib/wordlist/operators/product.rb +50 -0
  82. data/lib/wordlist/operators/subtract.rb +54 -0
  83. data/lib/wordlist/operators/unary_operator.rb +29 -0
  84. data/lib/wordlist/operators/union.rb +61 -0
  85. data/lib/wordlist/operators/unique.rb +52 -0
  86. data/lib/wordlist/operators.rb +7 -0
  87. data/lib/wordlist/unique_filter.rb +40 -61
  88. data/lib/wordlist/version.rb +1 -1
  89. data/lib/wordlist/words.rb +71 -0
  90. data/lib/wordlist.rb +103 -2
  91. data/spec/abstract_list_spec.rb +18 -0
  92. data/spec/builder_spec.rb +220 -76
  93. data/spec/cli_spec.rb +801 -0
  94. data/spec/compression/reader_spec.rb +137 -0
  95. data/spec/compression/writer_spec.rb +194 -0
  96. data/spec/file_spec.rb +258 -0
  97. data/spec/fixtures/wordlist.txt +15 -0
  98. data/spec/fixtures/wordlist.txt.bz2 +0 -0
  99. data/spec/fixtures/wordlist.txt.gz +0 -0
  100. data/spec/fixtures/wordlist.txt.xz +0 -0
  101. data/spec/fixtures/wordlist_with_ambiguous_format +3 -0
  102. data/spec/fixtures/wordlist_with_comments.txt +19 -0
  103. data/spec/fixtures/wordlist_with_empty_lines.txt +19 -0
  104. data/spec/format_spec.rb +50 -0
  105. data/spec/helpers/text.rb +3 -3
  106. data/spec/helpers/wordlist.rb +2 -2
  107. data/spec/lexer/lang_spec.rb +70 -0
  108. data/spec/lexer/stop_words_spec.rb +77 -0
  109. data/spec/lexer_spec.rb +652 -0
  110. data/spec/list_methods_spec.rb +181 -0
  111. data/spec/modifiers/capitalize_spec.rb +27 -0
  112. data/spec/modifiers/downcase_spec.rb +27 -0
  113. data/spec/modifiers/gsub_spec.rb +59 -0
  114. data/spec/modifiers/modifier_spec.rb +20 -0
  115. data/spec/modifiers/mutate_case_spec.rb +46 -0
  116. data/spec/modifiers/mutate_spec.rb +39 -0
  117. data/spec/modifiers/sub_spec.rb +98 -0
  118. data/spec/modifiers/tr_spec.rb +46 -0
  119. data/spec/modifiers/upcase_spec.rb +27 -0
  120. data/spec/operators/binary_operator_spec.rb +19 -0
  121. data/spec/operators/concat_spec.rb +26 -0
  122. data/spec/operators/intersect_spec.rb +37 -0
  123. data/spec/operators/operator_spec.rb +16 -0
  124. data/spec/operators/power_spec.rb +57 -0
  125. data/spec/operators/product_spec.rb +39 -0
  126. data/spec/operators/subtract_spec.rb +37 -0
  127. data/spec/operators/union_spec.rb +37 -0
  128. data/spec/operators/unique_spec.rb +25 -0
  129. data/spec/spec_helper.rb +2 -1
  130. data/spec/unique_filter_spec.rb +108 -18
  131. data/spec/wordlist_spec.rb +55 -3
  132. data/spec/words_spec.rb +41 -0
  133. metadata +183 -120
  134. data/lib/wordlist/builders/website.rb +0 -216
  135. data/lib/wordlist/builders.rb +0 -1
  136. data/lib/wordlist/flat_file.rb +0 -47
  137. data/lib/wordlist/list.rb +0 -162
  138. data/lib/wordlist/mutator.rb +0 -113
  139. data/lib/wordlist/parsers.rb +0 -74
  140. data/lib/wordlist/runners/list.rb +0 -116
  141. data/lib/wordlist/runners/runner.rb +0 -67
  142. data/lib/wordlist/runners.rb +0 -2
  143. data/scripts/benchmark +0 -59
  144. data/scripts/text/comedy_of_errors.txt +0 -4011
  145. data/spec/flat_file_spec.rb +0 -25
  146. data/spec/list_spec.rb +0 -58
  147. data/spec/mutator_spec.rb +0 -43
  148. data/spec/parsers_spec.rb +0 -118
@@ -0,0 +1,458 @@
1
+ require 'wordlist/file'
2
+ require 'wordlist/builder'
3
+ require 'wordlist/version'
4
+
5
+ require 'optparse'
6
+
7
+ module Wordlist
8
+ #
9
+ # Represents the `wordlist` command's logic.
10
+ #
11
+ # @api private
12
+ #
13
+ # @since 1.0.0
14
+ #
15
+ class CLI
16
+
17
+ # The program name.
18
+ PROGRAM_NAME = "wordlist"
19
+
20
+ # The URL to report bugs to.
21
+ BUG_REPORT_URL = "https://github.com/postmodern/wordlist.rb/issues/new"
22
+
23
+ # Mapping of `--format` option values and `format:` Symbols.
24
+ FORMATS = {
25
+ 'txt' => :txt,
26
+ 'gzip' => :gzip,
27
+ 'bzip2'=> :bzip2,
28
+ 'xz' => :xz
29
+ }
30
+
31
+ # The command's option parser.
32
+ #
33
+ # @return [OptionParser]
34
+ attr_reader :option_parser
35
+
36
+ # Command mode (building or reading).
37
+ #
38
+ # @return [:build, :read]
39
+ attr_reader :mode
40
+
41
+ # The explicit wordlist format to use.
42
+ #
43
+ # @return [:txt, :gzip, :bzip2, :xz, nil]
44
+ attr_reader :format
45
+
46
+ # The path to the output wordlist file.
47
+ #
48
+ # @return [String, nil]
49
+ attr_reader :output
50
+
51
+ # The command to run with each word from the wordlist.
52
+ #
53
+ # @return [String, nil]
54
+ attr_reader :command
55
+
56
+ # Wordlist operators to apply.
57
+ #
58
+ # @return [Array<(Symbol, ...)>]
59
+ attr_reader :operators
60
+
61
+ # Wordlist modifiers to apply.
62
+ #
63
+ # @return [Array<(Symbol, ...)>]
64
+ attr_reader :modifiers
65
+
66
+ # Additional options for {Builder#initialize}.
67
+ #
68
+ # @return [Hash{Symbol => Object}]
69
+ attr_reader :builder_options
70
+
71
+ #
72
+ # Initializes the command.
73
+ #
74
+ # @param [:read, :build] mode
75
+ #
76
+ # @param [:txt, :gzip, :bzip2, :xz, nil] format
77
+ #
78
+ # @param [String, nil] command
79
+ #
80
+ def initialize(mode: :read, format: nil, command: nil)
81
+ @option_parser = option_parser
82
+
83
+ @mode = mode
84
+ @format = format
85
+ @command = command
86
+ @output = nil
87
+
88
+ @operators = []
89
+ @modifiers = []
90
+
91
+ @builder_options = {}
92
+ end
93
+
94
+ #
95
+ # Adds an operator to be applied to the wordlist(s) later.
96
+ #
97
+ # @param [Symbol] name
98
+ # The operator method name.
99
+ #
100
+ # @param [Array<Object>] args
101
+ # Additional arguments for the operator.
102
+ #
103
+ def add_operator(name,*args)
104
+ @operators << [name, args]
105
+ end
106
+
107
+ #
108
+ # Adds a modifier to be applied to the wordlist(s) later.
109
+ #
110
+ # @param [Symbol] name
111
+ # The modifier method name.
112
+ #
113
+ # @param [Array<Object>] args
114
+ # Additional arguments for the modifier.
115
+ #
116
+ def add_modifier(name,*args)
117
+ @modifiers << [name, args]
118
+ end
119
+
120
+ #
121
+ # Opens a wordlist file.
122
+ #
123
+ # @param [String] path
124
+ # The path to the wordlist file.
125
+ #
126
+ # @return [Wordlist::File]
127
+ # The opened wordlist.
128
+ #
129
+ def open_wordlist(path)
130
+ if @format
131
+ Wordlist::File.open(path, format: @format)
132
+ else
133
+ Wordlist::File.open(path)
134
+ end
135
+ rescue WordlistNotFound, UnknownFormat => error
136
+ print_error(error.message)
137
+ exit -1
138
+ end
139
+
140
+ #
141
+ # Initializes and runs the command.
142
+ #
143
+ # @param [Array<String>] argv
144
+ # Command-line arguments.
145
+ #
146
+ # @return [Integer]
147
+ # The exit status of the command.
148
+ #
149
+ def self.run(argv=ARGV)
150
+ new().run(argv)
151
+ rescue Interrupt
152
+ # https://tldp.org/LDP/abs/html/exitcodes.html
153
+ return 130
154
+ rescue Errno::EPIPE
155
+ # STDOUT pipe broken
156
+ return 0
157
+ end
158
+
159
+ #
160
+ # Runs the command.
161
+ #
162
+ # @param [Array<String>] argv
163
+ # Command-line arguments.
164
+ #
165
+ # @return [Integer]
166
+ # The return status code.
167
+ #
168
+ def run(argv=ARGV)
169
+ argv = begin
170
+ @option_parser.parse(argv)
171
+ rescue OptionParser::ParseError => error
172
+ print_error(error.message)
173
+ return -1
174
+ end
175
+
176
+ case @mode
177
+ when :build then build_mode(argv)
178
+ else read_mode(argv)
179
+ end
180
+ rescue => error
181
+ print_backtrace(error)
182
+ return -1
183
+ end
184
+
185
+ #
186
+ # Wordlist building mode.
187
+ #
188
+ # @param [Array<String>] argv
189
+ # Additional command-line arguments.
190
+ #
191
+ def build_mode(argv)
192
+ builder = begin
193
+ if @format
194
+ Builder.open(@output, format: @format, **@builder_options)
195
+ else
196
+ Builder.open(@output, **@builder_options)
197
+ end
198
+ rescue UnknownFormat, CommandNotFound => error
199
+ print_error(error.message)
200
+ return -1
201
+ end
202
+
203
+ begin
204
+ if argv.empty?
205
+ $stdin.each_line do |line|
206
+ builder.parse(line)
207
+ end
208
+ else
209
+ argv.each do |file|
210
+ builder.parse_file(file)
211
+ end
212
+ end
213
+ ensure
214
+ builder.close
215
+ end
216
+
217
+ return 0
218
+ end
219
+
220
+ #
221
+ # Wordlist reading mode.
222
+ #
223
+ # @param [Array<String>] argv
224
+ # Additional command-line arguments.
225
+ #
226
+ def read_mode(argv)
227
+ unless argv.length >= 1
228
+ print_error "too few arguments given, requires at least one WORDLIST argument"
229
+ print_error "usage: #{PROGRAM_NAME} [options] WORDLIST ..."
230
+ return -1
231
+ end
232
+
233
+ # open the first wodlist
234
+ wordlist = open_wordlist(argv.first)
235
+
236
+ # append the additional wordlists
237
+ argv[1..].each { |arg| wordlist += (open_wordlist(arg)) }
238
+
239
+ # apply operators first
240
+ @operators.each do |(operator,args)|
241
+ wordlist.send(operator,*args)
242
+ end
243
+
244
+ # then apply modifiers
245
+ @modifiers.each do |(method,args)|
246
+ wordlist = wordlist.send(method,*args)
247
+ end
248
+
249
+ begin
250
+ if @command
251
+ wordlist.each do |word|
252
+ system(@command.gsub('{}',word))
253
+ end
254
+ else
255
+ wordlist.each do |word|
256
+ puts word
257
+ end
258
+ end
259
+ rescue CommandNotFound => error
260
+ print_error(error.message)
261
+ return -1
262
+ end
263
+
264
+ return 0
265
+ end
266
+
267
+ #
268
+ # The option parser.
269
+ #
270
+ # @return [OptionParser]
271
+ #
272
+ def option_parser
273
+ OptionParser.new do |opts|
274
+ opts.banner = "usage: #{PROGRAM_NAME} { [options] WORDLIST ... | --build WORDLIST [FILE ...] }"
275
+
276
+ opts.separator ""
277
+ opts.separator "Wordlist Reading Options:"
278
+
279
+ opts.on('-f','--format {txt|gzip|bz2|xz}', FORMATS, 'Saves the output to FILE') do |format|
280
+ @format = format
281
+ end
282
+
283
+ opts.on('--exec COMMAND','Runs the command with each word from the wordlist.', 'The string "{}" will be replaced with each word.') do |command|
284
+ @command = command
285
+ end
286
+
287
+ opts.separator ""
288
+ opts.separator "Wordlist Operations:"
289
+
290
+ opts.on('-U','--union WORDLIST','Unions the wordlist with the other WORDLIST') do |wordlist|
291
+ add_operator(:|, open_wordlist(wordlist))
292
+ end
293
+
294
+ opts.on('-I','--intersect WORDLIST','Intersects the wordlist with the other WORDLIST') do |wordlist|
295
+ add_operator(:&, open_wordlist(wordlist))
296
+ end
297
+
298
+ opts.on('-S','--subtract WORDLIST','Subtracts the words from the WORDLIST') do |wordlist|
299
+ add_operator(:-, open_wordlist(wordlist))
300
+ end
301
+
302
+ opts.on('-p','--product WORDLIST', 'Combines every word with the other words from WORDLIST') do |wordlist|
303
+ add_operator(:*, open_wordlist(wordlist))
304
+ end
305
+
306
+ opts.on('-P','--power NUM', Integer, 'Combines every word with the other words from WORDLIST') do |power|
307
+ add_operator(:**, power)
308
+ end
309
+
310
+ opts.on('-u','--unique','Filters out duplicate words') do
311
+ add_operator(:uniq)
312
+ end
313
+
314
+ opts.separator ""
315
+ opts.separator "Wordlist Modifiers:"
316
+
317
+ opts.on('-C','--capitalize','Capitalize each word') do
318
+ add_modifier(:capitalize)
319
+ end
320
+
321
+ opts.on('--uppercase', '--upcase','Converts each word to UPPERCASE') do
322
+ add_modifier(:upcase)
323
+ end
324
+
325
+ opts.on('--lowercase', '--downcase','Converts each word to lowercase') do
326
+ add_modifier(:downcase)
327
+ end
328
+
329
+ opts.on('-t','--tr CHARS:REPLACE','Translates the characters of each word') do |string|
330
+ chars, replace = string.split(':',2)
331
+
332
+ add_modifier(:tr, chars, replace)
333
+ end
334
+
335
+ opts.on('-s','--sub PATTERN:SUB','Replaces PATTERN with SUB in each word') do |string|
336
+ pattern, replace = string.split(':',2)
337
+
338
+ add_modifier(:sub, pattern, replace)
339
+ end
340
+
341
+ opts.on('-g','--gsub PATTERN:SUB','Replaces all PATTERNs with SUB in each word') do |string|
342
+ pattern, replace = string.split(':',2)
343
+
344
+ add_modifier(:gsub, pattern, replace)
345
+ end
346
+
347
+ opts.on('-m','--mutate PATTERN:SUB','Performs every possible substitution on each word') do |string|
348
+ pattern, replace = string.split(':',2)
349
+
350
+ add_modifier(:mutate, pattern, replace)
351
+ end
352
+
353
+ opts.on('-M','--mutate-case','Switches the case of each letter in each word') do
354
+ add_modifier(:mutate_case)
355
+ end
356
+
357
+ opts.separator ""
358
+ opts.separator "Wordlist Building Options:"
359
+
360
+ opts.on('-b','--build WORDLIST','Builds a wordlist') do |wordlist|
361
+ @mode = :build
362
+ @output = wordlist
363
+ end
364
+
365
+ opts.on('-a', '--[no-]append', TrueClass, 'Appends to the new wordlist instead of overwriting it') do |bool|
366
+ @builder_options[:append] = bool
367
+ end
368
+
369
+ opts.on('-L','--lang LANG','The language to expect') do |lang|
370
+ @builder_options[:lang] = lang
371
+ end
372
+
373
+ opts.on('--stop-words WORDS...','Ignores the stop words') do |words|
374
+ @builder_options[:stop_words] = words.split
375
+ end
376
+
377
+ opts.on('--ignore-words WORDS...','Ignore the words') do |words|
378
+ @builder_options[:ignore_words] = words.split
379
+ end
380
+
381
+ opts.on('--[no-]digits', TrueClass, 'Allow digits in the middle of words') do |bool|
382
+ @builder_options[:digits] = bool
383
+ end
384
+
385
+ opts.on('--special-chars CHARS','Allows the given special characters inside of words') do |string|
386
+ @builder_options[:special_chars] = string.chars
387
+ end
388
+
389
+ opts.on('--[no-]numbers', TrueClass, 'Parses whole numbers in addition to words') do |bool|
390
+ @builder_options[:numbers] = bool
391
+ end
392
+
393
+ opts.on('--[no-]acronyms', TrueClass, 'Parses acronyms in addition to words') do |bool|
394
+ @builder_options[:acronyms] = bool
395
+ end
396
+
397
+ opts.on('--[no-]normalize-case', TrueClass, 'Converts all words to lowercase') do |bool|
398
+ @builder_options[:normalize_case] = bool
399
+ end
400
+
401
+ opts.on('--[no-]normalize-apostrophes', TrueClass, 'Removes "\'s" from words') do |bool|
402
+ @builder_options[:normalize_apostrophes] = bool
403
+ end
404
+
405
+ opts.on('--[no-]normalize-acronyms', TrueClass, 'Removes the dots from acronyms') do |bool|
406
+ @builder_options[:normalize_acronyms] = bool
407
+ end
408
+
409
+ opts.separator ""
410
+ opts.separator "General Options:"
411
+
412
+ opts.on('-V','--version','Print the version') do
413
+ puts "#{PROGRAM_NAME} #{VERSION}"
414
+ exit
415
+ end
416
+
417
+ opts.on('-h','--help','Print the help output') do
418
+ puts opts
419
+ exit
420
+ end
421
+
422
+ opts.separator ""
423
+ opts.separator "Examples:"
424
+ opts.separator " #{PROGRAM_NAME} rockyou.txt.gz"
425
+ opts.separator " #{PROGRAM_NAME} passwords_short.txt passwords_long.txt"
426
+ opts.separator " #{PROGRAM_NAME} sport_teams.txt -p beers.txt -p digits.txt"
427
+ opts.separator " cat *.txt | #{PROGRAM_NAME} --build custom.txt"
428
+ opts.separator ""
429
+ end
430
+ end
431
+
432
+ #
433
+ # Prints an error message to stderr.
434
+ #
435
+ # @param [String] error
436
+ # The error message.
437
+ #
438
+ def print_error(error)
439
+ $stderr.puts "#{PROGRAM_NAME}: #{error}"
440
+ end
441
+
442
+ #
443
+ # Prints a backtrace to stderr.
444
+ #
445
+ # @param [Exception] exception
446
+ # The exception.
447
+ #
448
+ def print_backtrace(exception)
449
+ $stderr.puts "Oops! Looks like you've found a bug!"
450
+ $stderr.puts "Please report the following text to: #{BUG_REPORT_URL}"
451
+ $stderr.puts
452
+ $stderr.puts "```"
453
+ $stderr.puts "#{exception.full_message}"
454
+ $stderr.puts "```"
455
+ end
456
+
457
+ end
458
+ end
@@ -0,0 +1,72 @@
1
+ require 'wordlist/exceptions'
2
+
3
+ require 'shellwords'
4
+
5
+ module Wordlist
6
+ module Compression
7
+ #
8
+ # Handles reading compressed files.
9
+ #
10
+ # @since 1.0.0
11
+ #
12
+ module Reader
13
+ # Mapping of compression formats to the commands to read them.
14
+ COMMANDS = {
15
+ gzip: 'zcat',
16
+ bzip2: 'bzcat',
17
+ xz: 'xzcat'
18
+ }
19
+
20
+ #
21
+ # Returns the command to read the compressed wordlist.
22
+ #
23
+ # @param [String] path
24
+ # The path to the file.
25
+ #
26
+ # @param [:gzip, :bzip2, :xz] format
27
+ # The compression format of the file.
28
+ #
29
+ # @return [String]
30
+ # The shellescaped command string.
31
+ #
32
+ # @raise [UnknownFormat]
33
+ # The given format was not `:gzip`, `:bzip2`, or `:xz`.
34
+ #
35
+ def self.command(path, format: )
36
+ command = COMMANDS.fetch(format) do
37
+ raise(UnknownFormat,"unsupported format: #{format.inspect}")
38
+ end
39
+
40
+ Shellwords.shelljoin([command, path])
41
+ end
42
+
43
+ #
44
+ # Opens the compressed wordlist for reading.
45
+ #
46
+ # @param [String] path
47
+ # The path to the file.
48
+ #
49
+ # @param [Hash{Symbol => Object}] kwargs
50
+ # Additional keyword arguments for {command}.
51
+ #
52
+ # @return [IO]
53
+ # The uncompressed IO stream.
54
+ #
55
+ # @raise [ArgumentError]
56
+ # The given format was not `:gzip`, `:bzip2`, or `:xz`.
57
+ #
58
+ # @raise [CommandNotFound]
59
+ # The `zcat,` `bzcat`, or `xzcat` command could not be found.
60
+ #
61
+ def self.open(path,**kwargs,&block)
62
+ command = self.command(path,**kwargs)
63
+
64
+ begin
65
+ IO.popen(command,&block)
66
+ rescue Errno::ENOENT
67
+ raise(CommandNotFound,"#{command.inspect} command not found")
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,80 @@
1
+ require 'wordlist/exceptions'
2
+
3
+ require 'shellwords'
4
+
5
+ module Wordlist
6
+ module Compression
7
+ #
8
+ # Handles writing compressed files.
9
+ #
10
+ # @since 1.0.0
11
+ #
12
+ module Writer
13
+ # Mapping of compression formats to the commands to write to them.
14
+ COMMANDS = {
15
+ gzip: 'gzip',
16
+ bzip2: 'bzip2',
17
+ xz: 'xz'
18
+ }
19
+
20
+ #
21
+ # Returns the command to write to the compressed wordlist.
22
+ #
23
+ # @param [String] path
24
+ # The path to the file.
25
+ #
26
+ # @param [:gzip, :bzip2, :xz] format
27
+ # The compression format of the file.
28
+ #
29
+ # @param [Boolean] append
30
+ # Indicates that new words should be appended to the file instead of
31
+ # overwriting the file.
32
+ #
33
+ # @return [String]
34
+ # The shellescaped command string.
35
+ #
36
+ # @raise [UnknownFormat]
37
+ # The given format was not `:gzip`, `:bzip2`, or `:xz`.
38
+ #
39
+ def self.command(path, format: , append: false)
40
+ command = COMMANDS.fetch(format) do
41
+ raise(UnknownFormat,"unsupported format: #{format.inspect}")
42
+ end
43
+
44
+ redirect = if append then '>>'
45
+ else '>'
46
+ end
47
+
48
+ return "#{Shellwords.shellescape(command)} #{redirect} #{Shellwords.shellescape(path)}"
49
+ end
50
+
51
+ #
52
+ # Opens the compressed wordlist for reading.
53
+ #
54
+ # @param [String] path
55
+ # The path to the file.
56
+ #
57
+ # @param [Hash{Symbol => Object}] kwargs
58
+ # Additional keyword arguments for {command}.
59
+ #
60
+ # @return [IO]
61
+ # The uncompressed IO stream.
62
+ #
63
+ # @raise [ArgumentError]
64
+ # The given format was not `:gzip`, `:bzip2`, or `:xz`.
65
+ #
66
+ # @raise [CommandNotFound]
67
+ # The `gzip`, `bzip2,` or `xz` command was not found on the system.
68
+ #
69
+ def self.open(path,**kwargs)
70
+ command = self.command(path,**kwargs)
71
+
72
+ begin
73
+ IO.popen(command,'w')
74
+ rescue Errno::ENOENT
75
+ raise(CommandNotFound,"#{command.inspect} command not found")
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,31 @@
1
+ module Wordlist
2
+ #
3
+ # @since 1.0.0
4
+ #
5
+ class WordlistError < RuntimeError
6
+ end
7
+
8
+ #
9
+ # @since 1.0.0
10
+ #
11
+ class WordlistNotFound < WordlistError
12
+ end
13
+
14
+ #
15
+ # @since 1.0.0
16
+ #
17
+ class UnknownFormat < WordlistError
18
+ end
19
+
20
+ #
21
+ # @since 1.0.0
22
+ #
23
+ class CommandNotFound < WordlistError
24
+ end
25
+
26
+ #
27
+ # @since 1.0.0
28
+ #
29
+ class UnsupportedLanguage < WordlistError
30
+ end
31
+ end