wordlist 1.0.2 → 1.1.0

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.
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
  - - ">="