imprimatur 0.4.0-aarch64-linux-gnu → 0.5.0-aarch64-linux-gnu

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: 820b696bf9efd260711282d1ad700b843939f469797577f578db69490d90795c
4
- data.tar.gz: a53d88596e77e76cfa13e967ff9a095f7f996d0d21ffb3aa2b1b6bf6bcfcba9a
3
+ metadata.gz: 5a6d6451a7a370921f43b37e27a8edb7d886a4923981afd28a256b424b85234e
4
+ data.tar.gz: e74c978a45ce43cc0856b186b71f38d3204aea8cb71c4f0ed5d961bd4f0dc874
5
5
  SHA512:
6
- metadata.gz: 0a0bec11ea0fe229a56b729d3c40f8ef8f397d4940780a0460d2772ef5cec8529f7ec97dfcd99759b7b5fd30fa6a026a62d97115a94c113857af06d563616800
7
- data.tar.gz: a659697da26c4e7878a17f6d2442bc53403dbb926f91d930e026a9468f00437a4403b14325f00b3d9e6d63580074d8e83e1f4b22ef3a163b4797d80aa2ea9513
6
+ metadata.gz: bf5c3cc806be6379e109c335d65e4b1f84662cbdcb569db4ae0e9fd0e028d4185cf0f9f0b4cd7cc37ac00cc8a313878aaddaee276898b5835e4365f25fe0dfb4
7
+ data.tar.gz: 0ac1716d1208f48001683efafd3bba638ddc134ff1d67c545a7ad123f99213ee9ff83a96eb7603ca3f3f64ba1cdb8a14507128f530adf21c3e6a5ab989ebca95
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.0](https://github.com/seuros/imprimatur/compare/v0.4.0...v0.5.0) (2026-10-18)
4
+
5
+
6
+ ### Features
7
+
8
+ * a binary String is always a document's bytes, a path must name a regular file, and reads stop at max_bytes ([3484a64](https://github.com/seuros/imprimatur/commit/3484a64b791beb2e9f90cd485441fae255e223f0))
9
+
3
10
  ## [0.4.0](https://github.com/seuros/imprimatur/compare/v0.3.0...v0.4.0) (2026-10-18)
4
11
 
5
12
 
data/README.md CHANGED
@@ -318,6 +318,13 @@ fibers keep running, and `Timeout` or `Thread#raise` cancels it.
318
318
  `Imprimatur::PasswordRequired` and `Imprimatur::UnsupportedDocument` (legacy
319
319
  `.doc`, `.xlsb`, presentations) are the other errors to expect.
320
320
 
321
+ A binary String is always the document's bytes, never a path: an upload
322
+ holding `/etc/passwd` or `/dev/zero` is refused as `UnsupportedDocument`,
323
+ not read from the server. A path (a `Pathname`, or a String that is not
324
+ binary and has no document signature) must name a regular file; devices,
325
+ pipes and directories are refused. An IO or a path is read no further than
326
+ `max_bytes:`, so an endless stream or a huge file stops there.
327
+
321
328
  ### Structure and images
322
329
 
323
330
  Markdown is one rendering of what a read finds. `Imprimatur.content` gives
@@ -12,22 +12,29 @@ module Imprimatur
12
12
  # Documents in: Markdown, their structure, their images. See
13
13
  # {Imprimatur.markdown}, {Imprimatur.content} and {Imprimatur.images}.
14
14
  module Reader
15
- # File signatures a document's bytes start with; a String that starts
16
- # with neither is taken for a path.
15
+ # File signatures a document's bytes start with; a String that is not
16
+ # binary and starts with neither is taken for a path.
17
17
  SIGNATURES = ["%PDF-".b, "PK\x03\x04".b, "PK\x05\x06".b, "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1".b].freeze
18
18
  private_constant :SIGNATURES
19
19
 
20
+ # The largest document read in unless told, as the native core's own limit.
21
+ MAX_BYTES = 512 * 1024 * 1024
22
+ private_constant :MAX_BYTES
23
+
20
24
  # @see Imprimatur.markdown
21
- def self.markdown(document, **) = Native.markdown(read(document), *limits(**))
25
+ def self.markdown(document, max_bytes: nil, **)
26
+ Native.markdown(read(document, max_bytes), *limits(max_bytes:, **))
27
+ end
22
28
 
23
29
  # @see Imprimatur.content
24
- def self.content(document, styles: false, **)
25
- JSON.parse(Native.content(read(document), *limits(**), styles ? true : false), symbolize_names: true)
30
+ def self.content(document, styles: false, max_bytes: nil, **)
31
+ bytes = read(document, max_bytes)
32
+ JSON.parse(Native.content(bytes, *limits(max_bytes:, **), styles ? true : false), symbolize_names: true)
26
33
  end
27
34
 
28
35
  # @see Imprimatur.images
29
- def self.images(document, **)
30
- Native.images(read(document), *limits(**)).map do |data, description|
36
+ def self.images(document, max_bytes: nil, **)
37
+ Native.images(read(document, max_bytes), *limits(max_bytes:, **)).map do |data, description|
31
38
  found = JSON.parse(description, symbolize_names: true)
32
39
  Image.new(data:, **Image.members.drop(1).to_h { [it, found[it]] })
33
40
  end
@@ -38,22 +45,52 @@ module Imprimatur
38
45
  [max_bytes, max_inflated, timeout && Float(timeout)]
39
46
  end
40
47
 
48
+ # A path or an IO is read no further than +max_bytes+ (one byte past it
49
+ # tells it is passed): an endless stream or a huge file stops there.
50
+ #
41
51
  # @api private
42
- def self.read(document)
52
+ def self.read(document, max_bytes = nil)
53
+ limit = max_bytes.is_a?(Integer) && max_bytes >= 0 ? max_bytes : MAX_BYTES
43
54
  case document
44
- when Pathname then File.binread(document)
45
- when String then bytes?(document) ? document : File.binread(document)
55
+ when Pathname then file(document, limit)
56
+ when String then bytes?(document) ? document : file(document, limit)
46
57
  else
47
58
  raise TypeError, "expected a path, bytes or an IO, not #{document.class}" unless document.respond_to?(:read)
48
59
 
49
- document.read.to_s.b
60
+ within(document.read(limit + 1).to_s.b, limit)
50
61
  end
51
62
  end
52
63
 
64
+ # A binary String (what +File.binread+, a binary IO or a download give)
65
+ # is bytes, whatever it holds: uploaded bytes never name a file to read.
66
+ #
53
67
  # @api private
54
68
  def self.bytes?(string)
69
+ return true if string.encoding == Encoding::BINARY
70
+
55
71
  head = string.byteslice(0, 1024).b
56
72
  SIGNATURES.any? { head.start_with?(it) } || head.include?("%PDF-") || head.include?("\0")
57
73
  end
74
+
75
+ # A regular file's bytes. Opened without blocking and checked through
76
+ # the open file, so a pipe cannot hang the read, a device such as
77
+ # +/dev/zero+ is refused, and the file cannot be swapped in between.
78
+ #
79
+ # @api private
80
+ def self.file(path, limit)
81
+ File.open(path, File::RDONLY | File::NONBLOCK | File::BINARY) do |io|
82
+ stat = io.stat
83
+ raise UnsupportedDocument, "cannot read this document: #{path} is not a regular file" unless stat.file?
84
+
85
+ within(stat.size > limit ? '' : io.read(limit + 1).to_s, limit, stat.size)
86
+ end
87
+ end
88
+
89
+ # @api private
90
+ def self.within(bytes, limit, size = bytes.bytesize)
91
+ raise LimitExceeded, 'the document exceeds the input size limit' if [size, bytes.bytesize].max > limit
92
+
93
+ bytes
94
+ end
58
95
  end
59
96
  end
@@ -123,7 +123,7 @@ module Imprimatur
123
123
  # @raise [UnsupportedDocument] a PDF, a Word document or another format
124
124
  # @raise [Imprimatur::Error] a damaged workbook, or past a limit
125
125
  def self.open(document, styles: false, **limits)
126
- new(Native.sheets(Reader.read(document), *Reader.limits(**limits), styles ? true : false))
126
+ new(Native.sheets(Reader.read(document, limits[:max_bytes]), *Reader.limits(**limits), styles ? true : false))
127
127
  end
128
128
 
129
129
  # @return [Array<Sheet>] in the workbook's order, hidden ones included
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Imprimatur
4
- VERSION = '0.4.0'
4
+ VERSION = '0.5.0'
5
5
  end
data/lib/imprimatur.rb CHANGED
@@ -66,7 +66,8 @@ module Imprimatur
66
66
  # Imprimatur.markdown(File.open('sales.xlsx'))
67
67
  #
68
68
  # @param document [String, Pathname, #read] a path, the document's bytes
69
- # (told apart by their signature), or an IO
69
+ # (a binary String always is; another String only with a document's
70
+ # signature), or an IO. A path must name a regular file.
70
71
  # @param timeout [Numeric, nil] seconds the read may take (60), +nil+ for none
71
72
  # @param max_bytes [Integer, nil] the largest document accepted (512 MiB)
72
73
  # @param max_inflated [Integer, nil] decompressed bytes allowed in all (1 GiB)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: imprimatur
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: aarch64-linux-gnu
6
6
  authors:
7
7
  - Abdelkader Boudih