wordlist 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '09442b4fa26d163890845f9ba05bb3620d081ede1aeea4f4681e3292023a329e'
4
- data.tar.gz: d69e26ee74fef00403afdbd2acddbc617398132d75074fe6cf8546704540ce83
3
+ metadata.gz: 84d2d2f168e655e19497f02a9cf7841e553315ec52e64ea46e9a9f01e943db9d
4
+ data.tar.gz: 709fedece526b979a744579a1868e6612b24da8ea0e85a2c9a496e2a219f58a7
5
5
  SHA512:
6
- metadata.gz: f96fd76cbccdeab025ec9bf7c46946f7d684f91e5e01e7a3c68820b9bcbb1c80ec9e50a8d39fea61cbd5e757a0bfffca8e8e2ab1b435b91327d365efeac0f971
7
- data.tar.gz: a99ea8d51f2e0ffd6b8a5a444870c28bee24698fdce3b44474c1c66acc000ebdba009493b267e68e68d592bd668189e5b77dff971f7e03f08ef7921032071a1c
6
+ metadata.gz: fd6bf3d64ddef5eae7bd03e03b53676b03b06165233b0bc8812c81cce5ad04f208e969f1d5a69f6330641d4997a3bdfaa43d00103721b9704fdf4f0d98331261
7
+ data.tar.gz: 44c4075fdd792d75586be3d6b82ab93da2e2bb6ad07c7139479996bb46751aa483937097a53da1a968996ca3e5bb4341fc05767bb999f3c23ed4f7e46081bd7d
@@ -4,25 +4,33 @@ on: [ push, pull_request ]
4
4
 
5
5
  jobs:
6
6
  tests:
7
- runs-on: ubuntu-latest
7
+ runs-on: ${{ matrix.os }}
8
8
  strategy:
9
9
  fail-fast: false
10
10
  matrix:
11
+ os:
12
+ - ubuntu-latest
13
+ - macos-latest
11
14
  ruby:
12
- - 2.6
13
- - 2.7
14
15
  - 3.0
15
16
  - 3.1
17
+ - 3.2
16
18
  - jruby
17
19
  - truffleruby
18
- name: Ruby ${{ matrix.ruby }}
20
+ name: OS ${{ matrix.os }} / Ruby ${{ matrix.ruby }}
19
21
  steps:
20
22
  - uses: actions/checkout@v2
21
23
  - name: Set up Ruby
22
24
  uses: ruby/setup-ruby@v1
23
25
  with:
24
26
  ruby-version: ${{ matrix.ruby }}
25
- - name: Install dependencies
27
+ - if: matrix.os == 'ubuntu-latest'
28
+ name: Install external dependencies
29
+ run: sudo apt-get install -y xz-utils unzip p7zip
30
+ - if: matrix.os == 'macos-latest'
31
+ name: Install external dependencies
32
+ run: brew install xz unzip p7zip
33
+ - name: Install Ruby dependencies
26
34
  run: bundle install --jobs 4 --retry 3
27
35
  - name: Run tests
28
36
  run: bundle exec rake test
data/ChangeLog.md CHANGED
@@ -1,3 +1,20 @@
1
+ ### 1.1.0 / 2023-09-12
2
+
3
+ * Added support for reading zip (`.zip`) and 7zip (`.7z`) compressed wordlist
4
+ files.
5
+ * Added support for building zip or 7zip compressed wordlist files.
6
+
7
+ #### CLI
8
+
9
+ * `-f,--format` now accepts `zip` and `7zip` format values.
10
+
11
+ ### 1.0.3 / 2023-08-04
12
+
13
+ * Fix reading of compressed wordlists on macOS.
14
+ * macOS's version of `zcat`, `bzcat`, and `xzcat` do not accept a file path
15
+ argument, but instead require the compressed input be piped or redirected
16
+ into them (ex: `zcat < path/to/file.gz`).
17
+
1
18
  ### 1.0.2 / 2023-07-18
2
19
 
3
20
  #### CLI
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009-2021 Hal Brodigan
1
+ Copyright (c) 2009-2023 Hal Brodigan
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -15,10 +15,10 @@ building wordlists, efficiently.
15
15
 
16
16
  ## Features
17
17
 
18
- * Supports reading `.txt` wordlists, and `.txt.gz`, `.txt.bz2`, and `.txt.xz`
18
+ * Supports reading `.txt` wordlists, and `.gz`, `.bz2`, `.xz`, `.zip`, and `.7z`
19
19
  compressed wordlists.
20
20
  * Supports building wordlists from arbitrary text. Also supports `.gz`, `.bz2,`
21
- and `.xz` compression.
21
+ `.xz`, `.zip`, and `.7z` compression.
22
22
  * Provides an advanced lexer for parsing text into words.
23
23
  * Can parse/skip digits, special characters, whole numbers, acronyms.
24
24
  * Can normalize case, apostrophes, and acronyms.
@@ -223,7 +223,7 @@ builder.parse_file(path)
223
223
 
224
224
  ## Requirements
225
225
 
226
- * [ruby] >= 2.0.0
226
+ * [ruby] >= 3.0.0
227
227
 
228
228
  [ruby]: https://www.ruby-lang.org/
229
229
 
@@ -247,6 +247,59 @@ gem 'wordlist', '~> 1.0'
247
247
 
248
248
  ### Synopsis
249
249
 
250
+ ```
251
+ usage: wordlist { [options] WORDLIST ... | --build WORDLIST [FILE ...] }
252
+
253
+ Wordlist Reading Options:
254
+ -f {txt|gzip|bz2|xz|zip|7zip}, Sets the desired wordlist format
255
+ --format
256
+ --exec COMMAND Runs the command with each word from the wordlist.
257
+ The string "{}" will be replaced with each word.
258
+
259
+ Wordlist Operations:
260
+ -U, --union WORDLIST Unions the wordlist with the other WORDLIST
261
+ -I, --intersect WORDLIST Intersects the wordlist with the other WORDLIST
262
+ -S, --subtract WORDLIST Subtracts the words from the WORDLIST
263
+ -p, --product WORDLIST Combines every word with the other words from WORDLIST
264
+ -P, --power NUM Combines every word with the other words from WORDLIST
265
+ -u, --unique Filters out duplicate words
266
+
267
+ Wordlist Modifiers:
268
+ -C, --capitalize Capitalize each word
269
+ --uppercase, --upcase Converts each word to UPPERCASE
270
+ --lowercase, --downcase Converts each word to lowercase
271
+ -t, --tr CHARS:REPLACE Translates the characters of each word
272
+ -s, --sub PATTERN:SUB Replaces PATTERN with SUB in each word
273
+ -g, --gsub PATTERN:SUB Replaces all PATTERNs with SUB in each word
274
+ -m, --mutate PATTERN:SUB Performs every possible substitution on each word
275
+ -M, --mutate-case Switches the case of each letter in each word
276
+
277
+ Wordlist Building Options:
278
+ -b, --build WORDLIST Builds a wordlist
279
+ -a, --[no-]append Appends to the new wordlist instead of overwriting it
280
+ -L, --lang LANG The language to expect
281
+ --stop-words WORDS... Ignores the stop words
282
+ --ignore-words WORDS... Ignore the words
283
+ --[no-]digits Allow digits in the middle of words
284
+ --special-chars CHARS Allows the given special characters inside of words
285
+ --[no-]numbers Parses whole numbers in addition to words
286
+ --[no-]acronyms Parses acronyms in addition to words
287
+ --[no-]normalize-case Converts all words to lowercase
288
+ --[no-]normalize-apostrophes Removes "'s" from words
289
+ --[no-]normalize-acronyms Removes the dots from acronyms
290
+
291
+ General Options:
292
+ -V, --version Print the version
293
+ -h, --help Print the help output
294
+
295
+ Examples:
296
+ wordlist rockyou.txt.gz
297
+ wordlist passwords_short.txt passwords_long.txt
298
+ wordlist sport_teams.txt -p beers.txt -p digits.txt
299
+ cat *.txt | wordlist --build custom.txt
300
+
301
+ ```
302
+
250
303
  Reading a wordlist:
251
304
 
252
305
  ```shell
@@ -332,6 +385,6 @@ Wordlist::File#mutate_case (N=1000) 24.178521 0.000000 24.178521 ( 24.2
332
385
 
333
386
  ## License
334
387
 
335
- Copyright (c) 2009-2021 Hal Brodigan
388
+ Copyright (c) 2009-2023 Hal Brodigan
336
389
 
337
390
  See {file:LICENSE.txt} for details.
data/gemspec.yml CHANGED
@@ -8,7 +8,7 @@ description:
8
8
  license: MIT
9
9
  authors: Postmodern
10
10
  email: postmodern.mod3@gmail.com
11
- homepage: https://github.com/postmodern/wordlist.rb
11
+ homepage: https://github.com/postmodern/wordlist.rb#readme
12
12
  has_yard: true
13
13
 
14
14
  metadata:
@@ -17,7 +17,7 @@ metadata:
17
17
  bug_tracker_uri: https://github.com/postmodern/wordlist.rb/issues
18
18
  changelog_uri: https://github.com/postmodern/wordlist.rb/blob/master/ChangeLog.md
19
19
 
20
- required_ruby_version: ">= 2.0.0"
20
+ required_ruby_version: ">= 3.0.0"
21
21
 
22
22
  development_dependencies:
23
23
  bundler: ~> 2.0
@@ -22,7 +22,7 @@ module Wordlist
22
22
 
23
23
  # The format of the wordlist file.
24
24
  #
25
- # @return [:txt, :gzip, :bzip2, :xz]
25
+ # @return [:txt, :gzip, :bzip2, :xz, :zip, :7zip]
26
26
  attr_reader :format
27
27
 
28
28
  # The word lexer.
@@ -41,7 +41,7 @@ module Wordlist
41
41
  # @param [String] path
42
42
  # The path of the wordlist file.
43
43
  #
44
- # @param [:txt, :gz, :bzip2, :xz, nil] format
44
+ # @param [:txt, :gz, :bzip2, :xz, :zip, :7zip, nil] format
45
45
  # The format of the wordlist. If not given the format will be inferred
46
46
  # from the file extension.
47
47
  #
data/lib/wordlist/cli.rb CHANGED
@@ -26,7 +26,9 @@ module Wordlist
26
26
  'txt' => :txt,
27
27
  'gzip' => :gzip,
28
28
  'bzip2'=> :bzip2,
29
- 'xz' => :xz
29
+ 'xz' => :xz,
30
+ 'zip' => :zip,
31
+ '7zip' => :"7zip"
30
32
  }
31
33
 
32
34
  # The command's option parser.
@@ -74,7 +76,7 @@ module Wordlist
74
76
  #
75
77
  # @param [:read, :build] mode
76
78
  #
77
- # @param [:txt, :gzip, :bzip2, :xz, nil] format
79
+ # @param [:txt, :gzip, :bzip2, :xz, :zip, :7zip, nil] format
78
80
  #
79
81
  # @param [String, nil] command
80
82
  #
@@ -277,7 +279,7 @@ module Wordlist
277
279
  opts.separator ""
278
280
  opts.separator "Wordlist Reading Options:"
279
281
 
280
- opts.on('-f','--format {txt|gzip|bz2|xz}', FORMATS, 'Saves the output to FILE') do |format|
282
+ opts.on('-f','--format {txt|gzip|bz2|xz|zip|7zip}', FORMATS, 'Saves the output to FILE') do |format|
281
283
  @format = format
282
284
  end
283
285
 
@@ -10,34 +10,31 @@ module Wordlist
10
10
  # @since 1.0.0
11
11
  #
12
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
13
  #
21
14
  # Returns the command to read the compressed wordlist.
22
15
  #
23
16
  # @param [String] path
24
17
  # The path to the file.
25
18
  #
26
- # @param [:gzip, :bzip2, :xz] format
19
+ # @param [:gzip, :bzip2, :xz, :zip, :7zip] format
27
20
  # The compression format of the file.
28
21
  #
29
22
  # @return [String]
30
23
  # The shellescaped command string.
31
24
  #
32
25
  # @raise [UnknownFormat]
33
- # The given format was not `:gzip`, `:bzip2`, or `:xz`.
26
+ # The given format was not `:gzip`, `:bzip2`, `:xz`, `:zip`, `:7zip`.
34
27
  #
35
28
  def self.command(path, format: )
36
- command = COMMANDS.fetch(format) do
37
- raise(UnknownFormat,"unsupported format: #{format.inspect}")
29
+ case format
30
+ when :gzip then "zcat < #{Shellwords.shellescape(path)}"
31
+ when :bzip2 then "bzcat < #{Shellwords.shellescape(path)}"
32
+ when :xz then "xzcat < #{Shellwords.shellescape(path)}"
33
+ when :zip then "unzip -p #{Shellwords.shellescape(path)}"
34
+ when :"7zip" then "7za e -so #{Shellwords.shellescape(path)}"
35
+ else
36
+ raise(UnknownFormat,"unsupported input format: #{format.inspect}")
38
37
  end
39
-
40
- Shellwords.shelljoin([command, path])
41
38
  end
42
39
 
43
40
  #
@@ -53,10 +50,10 @@ module Wordlist
53
50
  # The uncompressed IO stream.
54
51
  #
55
52
  # @raise [ArgumentError]
56
- # The given format was not `:gzip`, `:bzip2`, or `:xz`.
53
+ # The given format was not `:gzip`, `:bzip2`, `:xz`, `:zip`, or `:7zip`.
57
54
  #
58
55
  # @raise [CommandNotFound]
59
- # The `zcat,` `bzcat`, or `xzcat` command could not be found.
56
+ # The `zcat,` `bzcat`, `xzcat`, or `unzip` command could not be found.
60
57
  #
61
58
  def self.open(path,**kwargs,&block)
62
59
  command = self.command(path,**kwargs)
@@ -10,20 +10,13 @@ module Wordlist
10
10
  # @since 1.0.0
11
11
  #
12
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
13
  #
21
14
  # Returns the command to write to the compressed wordlist.
22
15
  #
23
16
  # @param [String] path
24
17
  # The path to the file.
25
18
  #
26
- # @param [:gzip, :bzip2, :xz] format
19
+ # @param [:gzip, :bzip2, :xz, :zip] format
27
20
  # The compression format of the file.
28
21
  #
29
22
  # @param [Boolean] append
@@ -33,19 +26,36 @@ module Wordlist
33
26
  # @return [String]
34
27
  # The shellescaped command string.
35
28
  #
36
- # @raise [UnknownFormat]
37
- # The given format was not `:gzip`, `:bzip2`, or `:xz`.
29
+ # @raise [UnknownFormat, AppendNotSupported]
30
+ # * {UnknownFormat} - the given format was not `:gzip`, `:bzip2`, `:xz`,
31
+ # or `:zip`.
32
+ # * {AppendNotSupported} - the `zip` archive format does not support
33
+ # appending to existing files within existing archives.
38
34
  #
39
35
  def self.command(path, format: , append: false)
40
- command = COMMANDS.fetch(format) do
41
- raise(UnknownFormat,"unsupported format: #{format.inspect}")
42
- end
36
+ case format
37
+ when :gzip, :bzip2, :xz
38
+ command = format.to_s
39
+ redirect = if append then '>>'
40
+ else '>'
41
+ end
43
42
 
44
- redirect = if append then '>>'
45
- else '>'
46
- end
43
+ "#{command} #{redirect} #{Shellwords.shellescape(path)}"
44
+ when :zip
45
+ if append
46
+ raise(AppendNotSupported,"zip format does not support appending to files within pre-existing archives: #{path.inspect}")
47
+ end
47
48
 
48
- return "#{Shellwords.shellescape(command)} #{redirect} #{Shellwords.shellescape(path)}"
49
+ "zip -q #{Shellwords.shellescape(path)} -"
50
+ when :"7zip"
51
+ if append
52
+ raise(AppendNotSupported,"7zip format does not support appending to files within pre-existing archives: #{path.inspect}")
53
+ end
54
+
55
+ "7za a -si #{Shellwords.shellescape(path)} >/dev/null"
56
+ else
57
+ raise(UnknownFormat,"unsupported output format: #{format.inspect}")
58
+ end
49
59
  end
50
60
 
51
61
  #
@@ -61,10 +71,11 @@ module Wordlist
61
71
  # The uncompressed IO stream.
62
72
  #
63
73
  # @raise [ArgumentError]
64
- # The given format was not `:gzip`, `:bzip2`, or `:xz`.
74
+ # The given format was not `:gzip`, `:bzip2`, `:xz`, `:zip`.
65
75
  #
66
76
  # @raise [CommandNotFound]
67
- # The `gzip`, `bzip2,` or `xz` command was not found on the system.
77
+ # The `gzip`, `bzip2,` `xz`, or `zip` command was not found on the
78
+ # system.
68
79
  #
69
80
  def self.open(path,**kwargs)
70
81
  command = self.command(path,**kwargs)
@@ -17,6 +17,12 @@ module Wordlist
17
17
  class UnknownFormat < WordlistError
18
18
  end
19
19
 
20
+ #
21
+ # @since 1.1.0
22
+ #
23
+ class AppendNotSupported < WordlistError
24
+ end
25
+
20
26
  #
21
27
  # @since 1.0.0
22
28
  #
data/lib/wordlist/file.rb CHANGED
@@ -24,7 +24,7 @@ module Wordlist
24
24
 
25
25
  # The format of the wordlist file.
26
26
  #
27
- # @return [:txt, :gzip, :bzip2, :xz]
27
+ # @return [:txt, :gzip, :bzip2, :xz, :zip, :7zip]
28
28
  attr_reader :format
29
29
 
30
30
  #
@@ -33,7 +33,7 @@ module Wordlist
33
33
  # @param [String] path
34
34
  # The path to the `.txt` file wordlist read from.
35
35
  #
36
- # @param [:txt, :gz, :bzip2, :xz, nil] format
36
+ # @param [:txt, :gz, :bzip2, :xz, :zip, :7zip, nil] format
37
37
  # The format of the wordlist. If not given the format will be inferred
38
38
  # from the file extension.
39
39
  #
@@ -13,7 +13,9 @@ module Wordlist
13
13
  '.txt' => :txt,
14
14
  '.gz' => :gzip,
15
15
  '.bz2' => :bzip2,
16
- '.xz' => :xz
16
+ '.xz' => :xz,
17
+ '.zip' => :zip,
18
+ '.7z' => :"7zip"
17
19
  }
18
20
 
19
21
  # Valid formats.
@@ -25,7 +27,7 @@ module Wordlist
25
27
  # @param [String] path
26
28
  # The path to the file.
27
29
  #
28
- # @return [:txt, :gzip, :bzip2, :xz]
30
+ # @return [:txt, :gzip, :bzip2, :xz, :zip, :7zip]
29
31
  #
30
32
  # @raise [UnknownFormat]
31
33
  # The format could not be inferred from the file path.
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Wordlist
4
4
  # wordlist version
5
- VERSION = '1.0.2'
5
+ VERSION = '1.1.0'
6
6
  end
data/lib/wordlist.rb CHANGED
@@ -31,7 +31,7 @@ module Wordlist
31
31
  # @param [Hash{Symbol => Object}] kwargs
32
32
  # Additional keyword arguments for {Wordlist::File#initialize}.
33
33
  #
34
- # @option kwargs [:txt, :bzip, :bzip2, :xz] :format
34
+ # @option kwargs [:txt, :bzip, :bzip2, :xz, :zip, :7zip] :format
35
35
  # Specifies the format of the wordlist. If no format is given, the format
36
36
  # will be inferred from the path's file extension.
37
37
  #
data/spec/builder_spec.rb CHANGED
@@ -46,6 +46,22 @@ describe Wordlist::Builder do
46
46
  end
47
47
  end
48
48
 
49
+ context "when the path ends in '.zip'" do
50
+ let(:path) { ::File.join(fixtures_dir,'new_wordlist.zip') }
51
+
52
+ it "must default #format to :zip" do
53
+ expect(subject.format).to eq(:zip)
54
+ end
55
+ end
56
+
57
+ context "when the path ends in '.7z'" do
58
+ let(:path) { ::File.join(fixtures_dir,'new_wordlist.7z') }
59
+
60
+ it "must default #format to :7zip" do
61
+ expect(subject.format).to eq(:"7zip")
62
+ end
63
+ end
64
+
49
65
  context "when format: :txt is given" do
50
66
  subject { described_class.new(path, format: :txt) }
51
67
 
@@ -78,6 +94,22 @@ describe Wordlist::Builder do
78
94
  end
79
95
  end
80
96
 
97
+ context "when format: :zip is given" do
98
+ subject { described_class.new(path, format: :zip) }
99
+
100
+ it "must set #format to :zip" do
101
+ expect(subject.format).to eq(:zip)
102
+ end
103
+ end
104
+
105
+ context "when format: :7zip is given" do
106
+ subject { described_class.new(path, format: :"7zip") }
107
+
108
+ it "must set #format to :7zip" do
109
+ expect(subject.format).to eq(:"7zip")
110
+ end
111
+ end
112
+
81
113
  it "must default #append? to false" do
82
114
  expect(subject.append?).to be(false)
83
115
  end
@@ -8,15 +8,15 @@ describe Wordlist::Compression::Reader do
8
8
  context "when given format: :gzip" do
9
9
  subject { described_class.command(path, format: :gzip) }
10
10
 
11
- it "must return 'zcat path/to/file'" do
12
- expect(subject).to eq("zcat #{path}")
11
+ it "must return 'zcat < path/to/file'" do
12
+ expect(subject).to eq("zcat < #{path}")
13
13
  end
14
14
 
15
15
  context "and the file contains special characters" do
16
16
  let(:path) { 'path/to/the file' }
17
17
 
18
18
  it "must shellescape them" do
19
- expect(subject).to eq("zcat #{Shellwords.shellescape(path)}")
19
+ expect(subject).to eq("zcat < #{Shellwords.shellescape(path)}")
20
20
  end
21
21
  end
22
22
  end
@@ -24,15 +24,15 @@ describe Wordlist::Compression::Reader do
24
24
  context "when given format: :bzip2" do
25
25
  subject { described_class.command(path, format: :bzip2) }
26
26
 
27
- it "must return 'bzcat path/to/file'" do
28
- expect(subject).to eq("bzcat #{path}")
27
+ it "must return 'bzcat < path/to/file'" do
28
+ expect(subject).to eq("bzcat < #{path}")
29
29
  end
30
30
 
31
31
  context "and the file contains special characters" do
32
32
  let(:path) { 'path/to/the file' }
33
33
 
34
34
  it "must shellescape them" do
35
- expect(subject).to eq("bzcat #{Shellwords.shellescape(path)}")
35
+ expect(subject).to eq("bzcat < #{Shellwords.shellescape(path)}")
36
36
  end
37
37
  end
38
38
  end
@@ -40,26 +40,48 @@ describe Wordlist::Compression::Reader do
40
40
  context "when given format: :xz" do
41
41
  subject { described_class.command(path, format: :xz) }
42
42
 
43
- it "must return 'xzcat path/to/file'" do
44
- expect(subject).to eq("xzcat #{path}")
43
+ it "must return 'xzcat < path/to/file'" do
44
+ expect(subject).to eq("xzcat < #{path}")
45
45
  end
46
46
 
47
47
  context "and the file contains special characters" do
48
48
  let(:path) { 'path/to/the file' }
49
49
 
50
50
  it "must shellescape them" do
51
- expect(subject).to eq("xzcat #{Shellwords.shellescape(path)}")
51
+ expect(subject).to eq("xzcat < #{Shellwords.shellescape(path)}")
52
52
  end
53
53
  end
54
54
  end
55
55
 
56
+ context "when given format: :zip" do
57
+ let(:file_name) { 'file_name' }
58
+ let(:path) { "path/to/#{file_name}.zip" }
59
+
60
+ subject { described_class.command(path, format: :zip) }
61
+
62
+ it "must return 'unzip -p path/to/file_name.zip'" do
63
+ expect(subject).to eq("unzip -p #{path}")
64
+ end
65
+ end
66
+
67
+ context "when given format: :7zip" do
68
+ let(:file_name) { 'file_name' }
69
+ let(:path) { "path/to/#{file_name}.7z" }
70
+
71
+ subject { described_class.command(path, format: :"7zip") }
72
+
73
+ it "must return 'unzip -p path/to/file_name.zip'" do
74
+ expect(subject).to eq("7za e -so #{path}")
75
+ end
76
+ end
77
+
56
78
  context "when given an unknown format: value" do
57
79
  let(:format) { :foo }
58
80
 
59
81
  it do
60
82
  expect {
61
83
  subject.command(path, format: format)
62
- }.to raise_error(Wordlist::UnknownFormat,"unsupported format: #{format.inspect}")
84
+ }.to raise_error(Wordlist::UnknownFormat,"unsupported input format: #{format.inspect}")
63
85
  end
64
86
  end
65
87
  end
@@ -69,7 +91,7 @@ describe Wordlist::Compression::Reader do
69
91
 
70
92
  context "when given format: :gzip" do
71
93
  let(:path) { ::File.join(fixtures_dir,'wordlist.txt.gz') }
72
- let(:expected_contents) { `zcat #{Shellwords.shellescape(path)}` }
94
+ let(:expected_contents) { `zcat < #{Shellwords.shellescape(path)}` }
73
95
 
74
96
  subject { described_class.open(path, format: :gzip) }
75
97
 
@@ -86,7 +108,7 @@ describe Wordlist::Compression::Reader do
86
108
 
87
109
  context "when given format: :bzip2" do
88
110
  let(:path) { ::File.join(fixtures_dir,'wordlist.txt.bz2') }
89
- let(:expected_contents) { `bzcat #{Shellwords.shellescape(path)}` }
111
+ let(:expected_contents) { `bzcat < #{Shellwords.shellescape(path)}` }
90
112
 
91
113
  subject { described_class.open(path, format: :bzip2) }
92
114
 
@@ -103,7 +125,7 @@ describe Wordlist::Compression::Reader do
103
125
 
104
126
  context "when given format: :xz" do
105
127
  let(:path) { ::File.join(fixtures_dir,'wordlist.txt.xz') }
106
- let(:expected_contents) { `xzcat #{Shellwords.shellescape(path)}` }
128
+ let(:expected_contents) { `xzcat < #{Shellwords.shellescape(path)}` }
107
129
 
108
130
  subject { described_class.open(path, format: :xz) }
109
131
 
@@ -120,7 +142,7 @@ describe Wordlist::Compression::Reader do
120
142
 
121
143
  context "when the command is not installed" do
122
144
  let(:format) { :gzip }
123
- let(:command) { Shellwords.shelljoin(['zcat', path]) }
145
+ let(:command) { "zcat < #{Shellwords.shellescape(path)}" }
124
146
  let(:path) { 'path/to/wordlist.gz' }
125
147
 
126
148
  before do
@@ -79,13 +79,61 @@ describe Wordlist::Compression::Writer do
79
79
  end
80
80
  end
81
81
 
82
+ context "when given format: :zip" do
83
+ subject { described_class.command(path, format: :zip) }
84
+
85
+ it "must return 'zip -q path/to/file -'" do
86
+ expect(subject).to eq("zip -q #{path} -")
87
+ end
88
+
89
+ context "and given append: true" do
90
+ it do
91
+ expect {
92
+ described_class.command(path, format: :zip, append: true)
93
+ }.to raise_error(Wordlist::AppendNotSupported,"zip format does not support appending to files within pre-existing archives: #{path.inspect}")
94
+ end
95
+ end
96
+
97
+ context "and the file contains special characters" do
98
+ let(:path) { 'path/to/the file' }
99
+
100
+ it "must shellescape them" do
101
+ expect(subject).to eq("zip -q #{Shellwords.shellescape(path)} -")
102
+ end
103
+ end
104
+ end
105
+
106
+ context "when given format: :7zip" do
107
+ subject { described_class.command(path, format: :"7zip") }
108
+
109
+ it "must return '7za a -si path/to/file'" do
110
+ expect(subject).to eq("7za a -si #{path} >/dev/null")
111
+ end
112
+
113
+ context "and given append: true" do
114
+ it do
115
+ expect {
116
+ described_class.command(path, format: :"7zip", append: true)
117
+ }.to raise_error(Wordlist::AppendNotSupported,"7zip format does not support appending to files within pre-existing archives: #{path.inspect}")
118
+ end
119
+ end
120
+
121
+ context "and the file contains special characters" do
122
+ let(:path) { 'path/to/the file' }
123
+
124
+ it "must shellescape them" do
125
+ expect(subject).to eq("7za a -si #{Shellwords.shellescape(path)} >/dev/null")
126
+ end
127
+ end
128
+ end
129
+
82
130
  context "when given an unknown format: value" do
83
131
  let(:format) { :foo }
84
132
 
85
133
  it do
86
134
  expect {
87
135
  subject.command(path, format: format)
88
- }.to raise_error(Wordlist::UnknownFormat,"unsupported format: #{format.inspect}")
136
+ }.to raise_error(Wordlist::UnknownFormat,"unsupported output format: #{format.inspect}")
89
137
  end
90
138
  end
91
139
  end
@@ -107,10 +155,11 @@ describe Wordlist::Compression::Writer do
107
155
  context "when written to" do
108
156
  before do
109
157
  subject.puts words
158
+ subject.flush
110
159
  subject.close
111
160
  end
112
161
 
113
- let(:written_contents) { `zcat #{Shellwords.shellescape(path)}` }
162
+ let(:written_contents) { `zcat < #{Shellwords.shellescape(path)}` }
114
163
  let(:written_words) { written_contents.lines.map(&:chomp) }
115
164
 
116
165
  it "must writing gzip compressed data to the file" do
@@ -118,7 +167,10 @@ describe Wordlist::Compression::Writer do
118
167
  end
119
168
  end
120
169
 
121
- after { ::FileUtils.rm_f(path) }
170
+ after do
171
+ subject.close
172
+ ::FileUtils.rm_f(path)
173
+ end
122
174
  end
123
175
 
124
176
  context "when given format: :bzip2" do
@@ -133,10 +185,11 @@ describe Wordlist::Compression::Writer do
133
185
  context "when written to" do
134
186
  before do
135
187
  subject.puts words
188
+ subject.flush
136
189
  subject.close
137
190
  end
138
191
 
139
- let(:written_contents) { `bzcat #{Shellwords.shellescape(path)}` }
192
+ let(:written_contents) { `bzcat < #{Shellwords.shellescape(path)}` }
140
193
  let(:written_words) { written_contents.lines.map(&:chomp) }
141
194
 
142
195
  it "must writing bzip2 compressed data to the file" do
@@ -144,7 +197,10 @@ describe Wordlist::Compression::Writer do
144
197
  end
145
198
  end
146
199
 
147
- after { ::FileUtils.rm_f(path) }
200
+ after do
201
+ subject.close
202
+ ::FileUtils.rm_f(path)
203
+ end
148
204
  end
149
205
 
150
206
  context "when given format: :xz" do
@@ -159,10 +215,11 @@ describe Wordlist::Compression::Writer do
159
215
  context "when written to" do
160
216
  before do
161
217
  subject.puts words
218
+ subject.flush
162
219
  subject.close
163
220
  end
164
221
 
165
- let(:written_contents) { `xzcat #{Shellwords.shellescape(path)}` }
222
+ let(:written_contents) { `xzcat < #{Shellwords.shellescape(path)}` }
166
223
  let(:written_words) { written_contents.lines.map(&:chomp) }
167
224
 
168
225
  it "must writing xz compressed data to the file" do
@@ -170,7 +227,70 @@ describe Wordlist::Compression::Writer do
170
227
  end
171
228
  end
172
229
 
173
- after { ::FileUtils.rm_f(path) }
230
+ after do
231
+ subject.close
232
+ ::FileUtils.rm_f(path)
233
+ end
234
+ end
235
+
236
+ context "when given format: :zip" do
237
+ let(:path) { ::File.join(fixtures_dir,'new_wordlist.txt.zip') }
238
+
239
+ subject { described_class.open(path, format: :zip) }
240
+
241
+ it "must return an IO object" do
242
+ expect(subject).to be_kind_of(IO)
243
+ end
244
+
245
+ context "when written to" do
246
+ before do
247
+ subject.puts words
248
+ subject.flush
249
+ subject.close
250
+ end
251
+
252
+ let(:written_contents) { `unzip -p #{Shellwords.shellescape(path)}` }
253
+ let(:written_words) { written_contents.lines.map(&:chomp) }
254
+
255
+ it "must writing xz compressed data to the file" do
256
+ expect(written_words).to eq(words)
257
+ end
258
+ end
259
+
260
+ after do
261
+ subject.close
262
+ ::FileUtils.rm_f(path)
263
+ end
264
+ end
265
+
266
+ context "when given format: :7zip" do
267
+ let(:path) { ::File.join(fixtures_dir,'new_wordlist.txt.7z') }
268
+
269
+ subject { described_class.open(path, format: :"7zip") }
270
+
271
+ it "must return an IO object" do
272
+ expect(subject).to be_kind_of(IO)
273
+ end
274
+
275
+ context "when written to" do
276
+ before do
277
+ subject.puts words
278
+ subject.flush
279
+ subject.close
280
+ end
281
+
282
+ let(:written_contents) { `7za e -so #{Shellwords.shellescape(path)}` }
283
+ let(:written_words) { written_contents.lines.map(&:chomp) }
284
+
285
+ it "must writing xz compressed data to the file" do
286
+ expect(written_words).to eq(words)
287
+ end
288
+ end
289
+
290
+ after do
291
+ subject.close
292
+ ::FileUtils.rm_f(path)
293
+ end
174
294
  end
175
295
 
176
296
  context "when the command is not installed" do
data/spec/file_spec.rb CHANGED
@@ -75,6 +75,22 @@ describe Wordlist::File do
75
75
  expect(subject.format).to eq(:xz)
76
76
  end
77
77
  end
78
+
79
+ context "and the path ends in .zip" do
80
+ let(:path) { ::File.join(fixtures_dir,'wordlist.txt.zip') }
81
+
82
+ it "must set #format to :zip" do
83
+ expect(subject.format).to eq(:zip)
84
+ end
85
+ end
86
+
87
+ context "and the path ends in .7z" do
88
+ let(:path) { ::File.join(fixtures_dir,'wordlist.txt.7z') }
89
+
90
+ it "must set #format to :7zip" do
91
+ expect(subject.format).to eq(:"7zip")
92
+ end
93
+ end
78
94
  end
79
95
 
80
96
  context "when format: is given" do
@@ -132,7 +148,7 @@ describe Wordlist::File do
132
148
 
133
149
  context "and the wordlist format is gzip" do
134
150
  let(:path) { ::File.join(fixtures_dir,'wordlist.txt.gz') }
135
- let(:expected_contents) { `zcat #{Shellwords.shellescape(path)}` }
151
+ let(:expected_contents) { `zcat < #{Shellwords.shellescape(path)}` }
136
152
 
137
153
  it "must read the uncompressed gzip data" do
138
154
  expect { |b|
@@ -143,7 +159,7 @@ describe Wordlist::File do
143
159
 
144
160
  context "and the wordlist format is bzip2" do
145
161
  let(:path) { ::File.join(fixtures_dir,'wordlist.txt.bz2') }
146
- let(:expected_contents) { `bzcat #{Shellwords.shellescape(path)}` }
162
+ let(:expected_contents) { `bzcat < #{Shellwords.shellescape(path)}` }
147
163
 
148
164
  it "must read the uncompressed gzip data" do
149
165
  expect { |b|
@@ -154,7 +170,7 @@ describe Wordlist::File do
154
170
 
155
171
  context "and the wordlist format is xz" do
156
172
  let(:path) { ::File.join(fixtures_dir,'wordlist.txt.xz') }
157
- let(:expected_contents) { `xzcat #{Shellwords.shellescape(path)}` }
173
+ let(:expected_contents) { `xzcat < #{Shellwords.shellescape(path)}` }
158
174
 
159
175
  it "must read the uncompressed gzip data" do
160
176
  expect { |b|
@@ -162,6 +178,28 @@ describe Wordlist::File do
162
178
  }.to yield_successive_args(*expected_lines)
163
179
  end
164
180
  end
181
+
182
+ context "and the wordlist format is zip" do
183
+ let(:path) { ::File.join(fixtures_dir,'wordlist.txt.zip') }
184
+ let(:expected_contents) { `unzip -p #{Shellwords.shellescape(path)}` }
185
+
186
+ it "must read the uncompressed zip data" do
187
+ expect { |b|
188
+ subject.each_line(&b)
189
+ }.to yield_successive_args(*expected_lines)
190
+ end
191
+ end
192
+
193
+ context "and the wordlist format is 7zip" do
194
+ let(:path) { ::File.join(fixtures_dir,'wordlist.txt.7z') }
195
+ let(:expected_contents) { `7za e -so #{Shellwords.shellescape(path)}` }
196
+
197
+ it "must read the uncompressed 7zip data" do
198
+ expect { |b|
199
+ subject.each_line(&b)
200
+ }.to yield_successive_args(*expected_lines)
201
+ end
202
+ end
165
203
  end
166
204
 
167
205
  context "when not given a block" do
@@ -172,7 +210,7 @@ describe Wordlist::File do
172
210
 
173
211
  context "and the wordlist format is gzip" do
174
212
  let(:path) { ::File.join(fixtures_dir,'wordlist.txt.gz') }
175
- let(:expected_contents) { `zcat #{Shellwords.shellescape(path)}` }
213
+ let(:expected_contents) { `zcat < #{Shellwords.shellescape(path)}` }
176
214
 
177
215
  it "must return an Enumerator of the uncompressed gzip data" do
178
216
  expect(subject.each_line).to be_kind_of(Enumerator)
@@ -182,7 +220,7 @@ describe Wordlist::File do
182
220
 
183
221
  context "and the wordlist format is bzip2" do
184
222
  let(:path) { ::File.join(fixtures_dir,'wordlist.txt.bz2') }
185
- let(:expected_contents) { `bzcat #{Shellwords.shellescape(path)}` }
223
+ let(:expected_contents) { `bzcat < #{Shellwords.shellescape(path)}` }
186
224
 
187
225
  it "must return an Enumerator of the compressed gzip data" do
188
226
  expect(subject.each_line).to be_kind_of(Enumerator)
@@ -192,13 +230,33 @@ describe Wordlist::File do
192
230
 
193
231
  context "and the wordlist format is xz" do
194
232
  let(:path) { ::File.join(fixtures_dir,'wordlist.txt.xz') }
195
- let(:expected_contents) { `xzcat #{Shellwords.shellescape(path)}` }
233
+ let(:expected_contents) { `xzcat < #{Shellwords.shellescape(path)}` }
196
234
 
197
235
  it "must return an Enumerator of the compressed gzip data" do
198
236
  expect(subject.each_line).to be_kind_of(Enumerator)
199
237
  expect(subject.each_line.to_a).to eq(expected_lines)
200
238
  end
201
239
  end
240
+
241
+ context "and the wordlist format is zip" do
242
+ let(:path) { ::File.join(fixtures_dir,'wordlist.txt.zip') }
243
+ let(:expected_contents) { `unzip -p #{Shellwords.shellescape(path)}` }
244
+
245
+ it "must return an Enumerator of the compressed zip data" do
246
+ expect(subject.each_line).to be_kind_of(Enumerator)
247
+ expect(subject.each_line.to_a).to eq(expected_lines)
248
+ end
249
+ end
250
+
251
+ context "and the wordlist format is 7zip" do
252
+ let(:path) { ::File.join(fixtures_dir,'wordlist.txt.7z') }
253
+ let(:expected_contents) { `7za e -so #{Shellwords.shellescape(path)}` }
254
+
255
+ it "must return an Enumerator of the compressed 7zip data" do
256
+ expect(subject.each_line).to be_kind_of(Enumerator)
257
+ expect(subject.each_line.to_a).to eq(expected_lines)
258
+ end
259
+ end
202
260
  end
203
261
  end
204
262
 
@@ -211,7 +269,7 @@ describe Wordlist::File do
211
269
 
212
270
  context "and the wordlist format is gzip" do
213
271
  let(:path) { ::File.join(fixtures_dir,'wordlist.txt.gz') }
214
- let(:expected_contents) { `zcat #{Shellwords.shellescape(path)}` }
272
+ let(:expected_contents) { `zcat < #{Shellwords.shellescape(path)}` }
215
273
 
216
274
  it "must read the uncompressed gzip data" do
217
275
  expect { |b|
@@ -222,7 +280,7 @@ describe Wordlist::File do
222
280
 
223
281
  context "and the wordlist format is bzip2" do
224
282
  let(:path) { ::File.join(fixtures_dir,'wordlist.txt.bz2') }
225
- let(:expected_contents) { `bzcat #{Shellwords.shellescape(path)}` }
283
+ let(:expected_contents) { `bzcat < #{Shellwords.shellescape(path)}` }
226
284
 
227
285
  it "must read the uncompressed gzip data" do
228
286
  expect { |b|
@@ -233,7 +291,7 @@ describe Wordlist::File do
233
291
 
234
292
  context "and the wordlist format is xz" do
235
293
  let(:path) { ::File.join(fixtures_dir,'wordlist.txt.xz') }
236
- let(:expected_contents) { `xzcat #{Shellwords.shellescape(path)}` }
294
+ let(:expected_contents) { `xzcat < #{Shellwords.shellescape(path)}` }
237
295
 
238
296
  it "must read the uncompressed gzip data" do
239
297
  expect { |b|
@@ -242,6 +300,28 @@ describe Wordlist::File do
242
300
  end
243
301
  end
244
302
 
303
+ context "and the wordlist format is zip" do
304
+ let(:path) { ::File.join(fixtures_dir,'wordlist.txt.zip') }
305
+ let(:expected_contents) { `unzip -p #{Shellwords.shellescape(path)}` }
306
+
307
+ it "must read the uncompressed zip data" do
308
+ expect { |b|
309
+ subject.each_line(&b)
310
+ }.to yield_successive_args(*expected_lines)
311
+ end
312
+ end
313
+
314
+ context "and the wordlist format is 7z" do
315
+ let(:path) { ::File.join(fixtures_dir,'wordlist.txt.7z') }
316
+ let(:expected_contents) { `7za e -so #{Shellwords.shellescape(path)}` }
317
+
318
+ it "must read the uncompressed 7zip data" do
319
+ expect { |b|
320
+ subject.each_line(&b)
321
+ }.to yield_successive_args(*expected_lines)
322
+ end
323
+ end
324
+
245
325
  context "when the wordlist contains empty lines" do
246
326
  let(:expected_words) do
247
327
  super().reject { |w| w.empty? }
Binary file
Binary file
data/spec/format_spec.rb CHANGED
@@ -27,6 +27,18 @@ describe Wordlist::Format do
27
27
  end
28
28
  end
29
29
 
30
+ context "when given a path ending in '.zip'" do
31
+ it "must return :zip" do
32
+ expect(subject.infer("path/to/file.zip")).to eq(:zip)
33
+ end
34
+ end
35
+
36
+ context "when given a path ending in '.7z'" do
37
+ it "must return :7zip" do
38
+ expect(subject.infer("path/to/file.7z")).to eq(:"7zip")
39
+ end
40
+ end
41
+
30
42
  context "when given a path ending in another file extension" do
31
43
  let(:path) { "path/to/file.foo" }
32
44
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wordlist
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Postmodern
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-19 00:00:00.000000000 Z
11
+ date: 2023-09-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -135,9 +135,11 @@ files:
135
135
  - spec/compression/writer_spec.rb
136
136
  - spec/file_spec.rb
137
137
  - spec/fixtures/wordlist.txt
138
+ - spec/fixtures/wordlist.txt.7z
138
139
  - spec/fixtures/wordlist.txt.bz2
139
140
  - spec/fixtures/wordlist.txt.gz
140
141
  - spec/fixtures/wordlist.txt.xz
142
+ - spec/fixtures/wordlist.txt.zip
141
143
  - spec/fixtures/wordlist_with_ambiguous_format
142
144
  - spec/fixtures/wordlist_with_comments.txt
143
145
  - spec/fixtures/wordlist_with_empty_lines.txt
@@ -175,7 +177,7 @@ files:
175
177
  - spec/wordlist_spec.rb
176
178
  - spec/words_spec.rb
177
179
  - wordlist.gemspec
178
- homepage: https://github.com/postmodern/wordlist.rb
180
+ homepage: https://github.com/postmodern/wordlist.rb#readme
179
181
  licenses:
180
182
  - MIT
181
183
  metadata:
@@ -191,7 +193,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
191
193
  requirements:
192
194
  - - ">="
193
195
  - !ruby/object:Gem::Version
194
- version: 2.0.0
196
+ version: 3.0.0
195
197
  required_rubygems_version: !ruby/object:Gem::Requirement
196
198
  requirements:
197
199
  - - ">="