activestorage-hotcell-server 1.0.0 → 1.2.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: bf17ea2179bb687a22b71617e04962d371dec1f92a53eec85e04f1a03df6ae01
4
- data.tar.gz: 52ee3dedc13811e9e71882c1a26bfe342a3f28013c9005842f1969e1cce1d3fe
3
+ metadata.gz: bbf72b384091a64a1a34102dd994dc55ee7631717da7b122d7467b513a6b17e8
4
+ data.tar.gz: bbadd4032329150ba23c9f0e92705ab0eb9785eabe7e7021a156ebd3bdd5fbdf
5
5
  SHA512:
6
- metadata.gz: d7375a3807281a2f753fcaf71300f7aabbc6fd3e5312964232e75547565667264bada483eb7f0c67bc7a0b4d2093f39851b401ba144860098174da91b3b60b37
7
- data.tar.gz: 1d2fe8b510a6eaffc40a19a21f5fee3bd20077a399a83c436f6616ce8476270541a8fe8347743bf736f77b80f29961e18e206f4bf96a49c8ac63524db70681e1
6
+ metadata.gz: 7c493037474fa7778d2e1dd9e51ead288a3c4c48ef8f7bac0066f1cd9fbcff0b2fba1da4bd44aab8b775cd0e5fa68a6a4307c2f04c2f13abe98fd2b2ea692014
7
+ data.tar.gz: b961c23e3ec4645153a5048ec1c50a53f595c96d99c1cf5b845a24b77feb7d3e85465c7b8104835580aa263dda2f5909561b5bd2c8720f419362ee399039e6a0
data/README.md CHANGED
@@ -1,3 +1,3 @@
1
1
  # activestorage-hotcell-server
2
2
 
3
- Part of [HotCell](https://github.com/basecamp/hotcell). See the repository README.
3
+ Part of [Hot Cell](https://github.com/basecamp/hotcell). See the repository README.
@@ -18,7 +18,7 @@ module ActiveStorage
18
18
  # deliberately does not copy: it rescues every Vips::Error and returns an empty hash, which is then
19
19
  # merged with `analyzed: true`. An undecodable image is recorded as successfully analyzed, forever,
20
20
  # and nothing re-enqueues AnalyzeJob. Here an undecodable input raises Vips::Error, the cell answers
21
- # `unreadable`, and the client decides — because only the client knows whether that verdict is safe
21
+ # `unreadable`, and the client decides — because only the client knows whether that failure is safe
22
22
  # to write down.
23
23
  class Vips < VipsOperation
24
24
  operation "active_storage.analyzers.image.vips"
@@ -60,7 +60,7 @@ module ActiveStorage
60
60
 
61
61
  # MiniMagick::Error is how mini_magick reports a `magick` that exited non-zero — the common shape of an
62
62
  # input it cannot decode. MiniMagick::Invalid is an input `identify` rejects outright.
63
- # ImageProcessing::Error is the pipeline's own verdict on the input against the requested
63
+ # ImageProcessing::Error is the pipeline's own rejection of the input against the requested
64
64
  # transform — a multi-layer source into a single-layer destination, for example. All three are
65
65
  # the input's fault rather than the operation's; unclassified, each was a transient `failed`
66
66
  # that never marked the blob, so the same file spent a full conversion on every request.
@@ -69,7 +69,7 @@ module ActiveStorage
69
69
  # **Accepted risk.** A tool's output is not bounded on this path. `Operation#run_tool` caps what it
70
70
  # reads at 64KB and drops the rest as it arrives, because an input that makes a tool print gigabytes
71
71
  # of diagnostics costs this worker gigabytes of address space, takes RLIMIT_DATA with it, and arrives
72
- # as a `memory` verdict — which is permanent, for a document whose only crime was being noisy.
72
+ # as a `memory` failure — which is permanent, for a document whose only crime was being noisy.
73
73
  # mini_magick reads both streams to EOF in a thread apiece and has no setting that bounds either;
74
74
  # `graphicsmagick`, `cli_prefix`, `cli_env`, `restricted_env`, `timeout`, `logger`, `tmpdir`,
75
75
  # `errors` and `warnings` are the whole list. The premise is that patching the library is worse than
@@ -22,7 +22,8 @@ module ActiveStorage
22
22
  # separate change.
23
23
  def render(source, destination, page:, resolution:)
24
24
  run! "mutool", "draw", "-F", "png", "-r", resolution.to_s, "-o", destination.path,
25
- source.fd_path, page.to_s, pass: [ source.to_io ]
25
+ source.fd_path, page.to_s, pass: [ source.to_io ],
26
+ causes: { /cannot authenticate password/ => ProtectedDocument }
26
27
  end
27
28
 
28
29
  def tool
@@ -22,7 +22,7 @@ module ActiveStorage
22
22
  def render(source, destination, page:, resolution:)
23
23
  run! "pdftoppm", "-png", "-singlefile", "-cropbox", "-r", resolution.to_s,
24
24
  "-f", page.to_s, "-l", page.to_s, source.fd_path, destination.path,
25
- pass: [ source.to_io ]
25
+ pass: [ source.to_io ], causes: { /Incorrect password/ => ProtectedDocument }
26
26
 
27
27
  destination.adopt destination.path(extension: "png")
28
28
  end
@@ -20,16 +20,25 @@ module ActiveStorage
20
20
  # UnreadableInput is always in the rescue list, and this descends from it.
21
21
  class UnreadableDocument < ::HotCell::UnreadableInput; end
22
22
 
23
+ # The document demands credentials the cell does not have.
24
+ class ProtectedDocument < UnreadableDocument
25
+ def self.cause
26
+ ::HotCell::Codes::PROTECTED
27
+ end
28
+ end
29
+
23
30
  private
24
31
  # Runs the tool with unsetenv_others and a written environment, and turns a non-zero exit into
25
32
  # `unreadable`. The tool's own stderr is the only useful diagnostic, and it is attacker-influenced,
26
33
  # so it is capped and scrubbed on its way onto the wire like any other error message.
27
- def run!(*command, pass: [])
34
+ # `causes` maps stderr patterns to the UnreadableDocument subclass to raise instead.
35
+ def run!(*command, pass: [], causes: {})
28
36
  result = run_tool(*command, pass: pass)
29
37
  return result if result.ok?
30
38
 
31
- raise UnreadableDocument, "#{command.first} exited #{result.status.exitstatus}: " \
32
- "#{result.err.to_s.strip[0, 200]}"
39
+ error = causes.detect { |pattern, _| result.err.to_s.match?(pattern) }&.last || UnreadableDocument
40
+ raise error, "#{command.first} exited #{result.status.exitstatus}: " \
41
+ "#{result.err.to_s.strip[0, 200]}"
33
42
  end
34
43
 
35
44
  # An empty output means the tool said it succeeded and produced nothing, which the client would
@@ -10,7 +10,8 @@ module ActiveStorage
10
10
  module Image
11
11
  # What `ActiveStorage::Transformers::ImageProcessingTransformer` does, moved out of the application,
12
12
  # and deliberately nothing more: the transformations and the format reach ImageProcessing exactly as
13
- # Rails hands them over.
13
+ # Rails hands them over, except that the client sends a web image's format as its content type's
14
+ # canonical extension, so `jfif` arrives as `jpg` (#84).
14
15
  #
15
16
  # There is no allowlist here, and that mirrors Rails: the vips path validates exactly one thing —
16
17
  # `combine_options` is refused — and `ActiveStorage.supported_image_processing_methods` is enforced by
@@ -3,7 +3,7 @@
3
3
  module ActiveStorage
4
4
  module HotCell
5
5
  module Server
6
- VERSION = "1.0.0"
6
+ VERSION = "1.2.0"
7
7
  end
8
8
  end
9
9
  end
@@ -22,7 +22,7 @@ module ActiveStorage
22
22
 
23
23
  # Vips::Error is how libvips reports a file it cannot decode, which is common rather than exceptional: it
24
24
  # covers truncated uploads, formats this build was not compiled with, and formats deliberately refused by
25
- # block_untrusted. ImageProcessing::Error is the pipeline's own verdict on the input against the
25
+ # block_untrusted. ImageProcessing::Error is the pipeline's own rejection of the input against the
26
26
  # requested transform, classified the same way. All of those are the input's fault and none is
27
27
  # the operation's.
28
28
  unreadable Vips::Error, ImageProcessing::Error
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activestorage-hotcell-server
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Dalessio
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 1.0.0
18
+ version: 1.2.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - '='
24
24
  - !ruby/object:Gem::Version
25
- version: 1.0.0
25
+ version: 1.2.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: image_processing
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -66,7 +66,7 @@ dependencies:
66
66
  - !ruby/object:Gem::Version
67
67
  version: 2.2.1
68
68
  description: |
69
- The HotCell operations behind activestorage-hotcell-client: image transformation with libvips or
69
+ The Hot Cell operations behind activestorage-hotcell-client: image transformation with libvips or
70
70
  ImageMagick, image and media analysis, PDF previews with mutool or poppler, and video previews with
71
71
  ffmpeg. Install it in the cell, with the tools the operations you load require.
72
72
  email:
@@ -99,8 +99,8 @@ licenses:
99
99
  - MIT
100
100
  metadata:
101
101
  homepage_uri: https://github.com/basecamp/hotcell
102
- source_code_uri: https://github.com/basecamp/hotcell/tree/v1.0.0/activestorage-hotcell-server
103
- changelog_uri: https://github.com/basecamp/hotcell/blob/v1.0.0/CHANGELOG.md
102
+ source_code_uri: https://github.com/basecamp/hotcell/tree/v1.2.0/activestorage-hotcell-server
103
+ changelog_uri: https://github.com/basecamp/hotcell/blob/v1.2.0/CHANGELOG.md
104
104
  documentation_uri: https://basecamp.github.io/hotcell/
105
105
  bug_tracker_uri: https://github.com/basecamp/hotcell/issues
106
106
  rubygems_mfa_required: 'true'