excavate 1.0.1 → 1.0.2

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: d579e821dbcdc3891ec86ccc385b815735aec447f7aaccf6980430002b77e009
4
- data.tar.gz: 409103ecdac9190c965a2e9414dc507b02e9a0654dcc7c1eeab01eec018ce916
3
+ metadata.gz: ed5cec17e4c8c985ae34d044fa170ccbecdc3bd783a0cd0916454a8c567149d7
4
+ data.tar.gz: 750dcde7f88158d2e107763a5fc733c4a72ac1030b53e91c6fd7e2697b167686
5
5
  SHA512:
6
- metadata.gz: 2431a0910c6d163b790dddcd5745129017848aca78093b935156ef8ef0ff4d0c4cf8ecbc8c6fb1b8eafcc29078556236632b9e0e9884c5c5794b24073829bbe0
7
- data.tar.gz: 4450fa82f329350881d0ea3f25f2396ec40c3c34af5523753d59f7a3540a3ea5dde9bcdb77bd8873f6ab5064f4d8beeb1b81bb05db41368a16a598a970669de8
6
+ metadata.gz: b62877409f75d68041b7b973ee364356e6e47b09bd714ebd4430f0df627643213bd8ff2dc3ca40d4b8d141c2fda3ff6b9b686f4720fc8997424b169effff169e
7
+ data.tar.gz: e3b0dee5b195329ecbc6cc85ffc5fc64c7b4c736481ac3c964b0353828c59e0176b71ad3c70ba53b7503a01419f5e6f205e28fd3531fe3584449ad8b39a66082
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2026-02-25 10:37:11 UTC using RuboCop version 1.84.2.
3
+ # on 2026-03-17 11:57:02 UTC using RuboCop version 1.85.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -37,16 +37,10 @@ Lint/DuplicateBranch:
37
37
  Exclude:
38
38
  - 'lib/excavate/utils.rb'
39
39
 
40
- # Offense count: 1
41
- # This cop supports safe autocorrection (--autocorrect).
42
- Lint/ScriptPermission:
43
- Exclude:
44
- - 'scripts/create_multi_nested_fixture.rb'
45
-
46
- # Offense count: 5
40
+ # Offense count: 4
47
41
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
48
42
  Metrics/MethodLength:
49
- Max: 15
43
+ Max: 12
50
44
 
51
45
  # Offense count: 46
52
46
  # Configuration parameters: Prefixes, AllowedPatterns.
@@ -71,6 +65,16 @@ RSpec/MultipleExpectations:
71
65
  RSpec/NestedGroups:
72
66
  Max: 4
73
67
 
68
+ # Offense count: 2
69
+ # This cop supports safe autocorrection (--autocorrect).
70
+ # Configuration parameters: AllowOnlyRestArgument, UseAnonymousForwarding, RedundantRestArgumentNames, RedundantKeywordRestArgumentNames, RedundantBlockArgumentNames.
71
+ # RedundantRestArgumentNames: args, arguments
72
+ # RedundantKeywordRestArgumentNames: kwargs, options, opts
73
+ # RedundantBlockArgumentNames: blk, block, proc
74
+ Style/ArgumentsForwarding:
75
+ Exclude:
76
+ - 'lib/excavate/archive.rb'
77
+
74
78
  # Offense count: 2
75
79
  # This cop supports unsafe autocorrection (--autocorrect-all).
76
80
  Style/IdenticalConditionalBranches:
@@ -18,7 +18,7 @@ module Excavate
18
18
  @archive = archive
19
19
  end
20
20
 
21
- def files(recursive_packages: false, files: [], filter: nil, &block)
21
+ def files(recursive_packages: false, files: [], filter: nil, &)
22
22
  # Auto-enable recursive_packages when extracting specific files
23
23
  recursive_packages = true if files.any?
24
24
 
@@ -26,7 +26,7 @@ module Excavate
26
26
  extract(target, recursive_packages: recursive_packages,
27
27
  files: files, filter: filter)
28
28
 
29
- all_files_in(target).map(&block)
29
+ all_files_in(target).map(&)
30
30
  ensure
31
31
  windows_safe_rm_rf(target)
32
32
  end
@@ -218,7 +218,11 @@ module Excavate
218
218
  replace_archive_with_contents(archive, target)
219
219
  rescue StandardError
220
220
  FileUtils.rm_rf(target)
221
- raise unless normalized_extension(archive) == "exe"
221
+ # During recursive extraction of nested archives, silently skip
222
+ # any that fail (e.g. .msi files that aren't real OLE, .cab files
223
+ # with incompatible format, .exe files with unsupported compression).
224
+ # Only re-raise for file types we don't recognize as archives.
225
+ raise unless TYPES.key?(normalized_extension(archive))
222
226
  end
223
227
 
224
228
  def replace_archive_with_contents(archive, target)
@@ -22,6 +22,8 @@ module Excavate
22
22
 
23
23
  Omnizip::Formats::Ole.open(@archive) do |ole|
24
24
  children(ole).each do |entry|
25
+ next if ole.directory?(entry)
26
+
25
27
  path = File.join(target, prepare_filename(entry))
26
28
  FileUtils.mkdir_p(File.dirname(path))
27
29
  content = ole.read(entry)
@@ -28,20 +28,37 @@ module Excavate
28
28
  end
29
29
 
30
30
  def extract_tar_xz(target)
31
- # Decompress XZ to get gzip data
32
- gzip_data = Omnizip::Formats::Xz.decompress(@archive)
33
- # Decompress gzip to get tar data
34
- tar_data = Zlib::GzipReader.new(StringIO.new(gzip_data)).read
31
+ data = Omnizip::Formats::Xz.decompress(@archive)
32
+ data = strip_compression(data)
33
+ validate_tar!(data)
35
34
 
36
35
  # Write tar file and extract
37
36
  temp_tar = File.join(target, ".temp_#{Time.now.to_i}_#{rand(1000)}.tar")
38
- File.binwrite(temp_tar, tar_data)
37
+ File.binwrite(temp_tar, data)
39
38
 
40
39
  TarExtractor.new(temp_tar).extract(target)
41
40
  ensure
42
41
  File.delete(temp_tar) if temp_tar && File.exist?(temp_tar)
43
42
  end
44
43
 
44
+ def strip_compression(data)
45
+ if FileMagic.detect_bytes(data) == :gzip
46
+ return Zlib::GzipReader.new(StringIO.new(data)).read
47
+ end
48
+
49
+ data
50
+ end
51
+
52
+ def validate_tar!(data)
53
+ inner_type = FileMagic.detect_bytes(data)
54
+ return if inner_type == :tar
55
+
56
+ inner_type ||= "unknown format"
57
+
58
+ raise UnknownArchiveError,
59
+ "Expected tar inside #{@archive}, got #{inner_type}"
60
+ end
61
+
45
62
  def extract_pure_xz(target)
46
63
  # Decompress XZ
47
64
  data = Omnizip::Formats::Xz.decompress(@archive)
@@ -1,29 +1,32 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Excavate
2
4
  class FileMagic
5
+ # [offset, magic_bytes, type]
6
+ SIGNATURES = [
7
+ [0, "MSCF\x00\x00\x00\x00".b, :cab],
8
+ [0, "\xFD7zXZ\x00".b, :xz],
9
+ [0, "\x1F\x8B".b, :gzip],
10
+ [257, "ustar".b, :tar],
11
+ ].freeze
12
+
13
+ MAX_READ = SIGNATURES.map { |o, m, _| o + m.bytesize }.max
14
+
3
15
  def self.detect(path)
4
- new(path).detect
16
+ beginning = File.read(path, MAX_READ, mode: "rb")
17
+ detect_bytes(beginning)
5
18
  end
6
19
 
7
- def initialize(path)
8
- @path = path
9
- end
20
+ def self.detect_bytes(data)
21
+ return nil if data.nil? || data.empty?
22
+
23
+ SIGNATURES.each do |offset, magic, type|
24
+ next if data.bytesize < offset + magic.bytesize
10
25
 
11
- def detect
12
- beginning = File.read(@path, 8, mode: "rb")
13
- case beginning
14
- when "MSCF\x00\x00\x00\x00".force_encoding("BINARY")
15
- :cab
16
- else
17
- case beginning.byteslice(0, 6)
18
- when "\xFD7zXZ\x00".force_encoding("BINARY")
19
- :xz
20
- else
21
- case beginning.byteslice(0, 2)
22
- when "\x1F\x8B".force_encoding("BINARY")
23
- :gzip
24
- end
25
- end
26
+ return type if data.byteslice(offset, magic.bytesize) == magic
26
27
  end
28
+
29
+ nil
27
30
  end
28
31
  end
29
32
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Excavate
4
- VERSION = "1.0.1"
4
+ VERSION = "1.0.2"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: excavate
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-02-25 00:00:00.000000000 Z
11
+ date: 2026-03-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cabriolet