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 +4 -4
- data/README.md +1 -1
- data/lib/active_storage/hot_cell/server/analyzers/image/vips.rb +1 -1
- data/lib/active_storage/hot_cell/server/magick_operation.rb +2 -2
- data/lib/active_storage/hot_cell/server/previewers/pdf/mutool.rb +2 -1
- data/lib/active_storage/hot_cell/server/previewers/pdf/poppler.rb +1 -1
- data/lib/active_storage/hot_cell/server/tool_operation.rb +12 -3
- data/lib/active_storage/hot_cell/server/transformers/image/vips.rb +2 -1
- data/lib/active_storage/hot_cell/server/version.rb +1 -1
- data/lib/active_storage/hot_cell/server/vips_operation.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bbf72b384091a64a1a34102dd994dc55ee7631717da7b122d7467b513a6b17e8
|
|
4
|
+
data.tar.gz: bbadd4032329150ba23c9f0e92705ab0eb9785eabe7e7021a156ebd3bdd5fbdf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7c493037474fa7778d2e1dd9e51ead288a3c4c48ef8f7bac0066f1cd9fbcff0b2fba1da4bd44aab8b775cd0e5fa68a6a4307c2f04c2f13abe98fd2b2ea692014
|
|
7
|
+
data.tar.gz: b961c23e3ec4645153a5048ec1c50a53f595c96d99c1cf5b845a24b77feb7d3e85465c7b8104835580aa263dda2f5909561b5bd2c8720f419362ee399039e6a0
|
data/README.md
CHANGED
|
@@ -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
|
|
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
|
|
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`
|
|
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
|
-
|
|
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
|
-
|
|
32
|
-
|
|
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
|
|
@@ -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
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
103
|
-
changelog_uri: https://github.com/basecamp/hotcell/blob/v1.
|
|
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'
|