safe_image 0.1.0 → 0.3.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/CHANGELOG.md +351 -0
- data/README.md +637 -288
- data/SECURITY.md +27 -7
- data/lib/safe_image/discourse_compat.rb +256 -98
- data/lib/safe_image/fonts/DEJAVU-LICENSE +187 -0
- data/lib/safe_image/fonts/DejaVuSans.ttf +0 -0
- data/lib/safe_image/ico.rb +286 -0
- data/lib/safe_image/image_magick_backend.rb +39 -3
- data/lib/safe_image/imagemagick_policy/policy.xml +8 -1
- data/lib/safe_image/jpegli_backend.rb +3 -1
- data/lib/safe_image/native.rb +380 -1
- data/lib/safe_image/optimizer.rb +79 -4
- data/lib/safe_image/processor.rb +24 -70
- data/lib/safe_image/remote.rb +188 -10
- data/lib/safe_image/runner.rb +10 -2
- data/lib/safe_image/sandbox.rb +82 -29
- data/lib/safe_image/svg_css.rb +314 -0
- data/lib/safe_image/svg_metadata.rb +183 -27
- data/lib/safe_image/svg_sanitizer.rb +524 -43
- data/lib/safe_image/version.rb +1 -1
- data/lib/safe_image/vips_backend.rb +57 -0
- data/lib/safe_image/vips_glue.rb +361 -0
- data/lib/safe_image/zygote.rb +619 -0
- data/lib/safe_image.rb +154 -35
- metadata +47 -15
- data/ext/safe_image_native/extconf.rb +0 -8
- data/ext/safe_image_native/safe_image_native.c +0 -392
data/lib/safe_image.rb
CHANGED
|
@@ -4,27 +4,45 @@ require_relative "safe_image/version"
|
|
|
4
4
|
|
|
5
5
|
module SafeImage
|
|
6
6
|
class Error < StandardError; end
|
|
7
|
+
|
|
8
|
+
# Raised when any operation is attempted before SafeImage.configure!.
|
|
9
|
+
class NotConfiguredError < Error; end
|
|
10
|
+
|
|
7
11
|
class UnsupportedFormatError < Error; end
|
|
12
|
+
|
|
13
|
+
# Raised when libvips cannot be loaded at runtime. configure!(backend: :vips)
|
|
14
|
+
# surfaces this at boot; operations never fall back to ImageMagick.
|
|
15
|
+
class VipsUnavailableError < UnsupportedFormatError; end
|
|
8
16
|
class UnsafePathError < Error; end
|
|
9
17
|
class InvalidImageError < Error; end
|
|
10
18
|
class LimitError < Error; end
|
|
11
19
|
|
|
12
|
-
# Default decompression-bomb ceiling
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
20
|
+
# Default decompression-bomb ceiling when configure! is not given an explicit
|
|
21
|
+
# max_pixels. Mirrored in the native binding (SAFE_IMAGE_DEFAULT_MAX_PIXELS)
|
|
22
|
+
# and aligned with the 128MP area limit on the ImageMagick path. Per-call
|
|
23
|
+
# max_pixels: overrides the configured value.
|
|
16
24
|
DEFAULT_MAX_PIXELS = 128 * 1024 * 1024
|
|
25
|
+
|
|
26
|
+
BACKENDS = %i[vips imagemagick].freeze
|
|
27
|
+
|
|
28
|
+
# Process-wide configuration. configure! builds a frozen instance and swaps
|
|
29
|
+
# it in with a single assignment, so readers never observe a half-applied
|
|
30
|
+
# config.
|
|
31
|
+
Config = Data.define(:backend, :landlock, :max_pixels)
|
|
17
32
|
end
|
|
18
33
|
|
|
19
34
|
require_relative "safe_image/native"
|
|
20
35
|
require_relative "safe_image/result"
|
|
21
36
|
require_relative "safe_image/runner"
|
|
22
37
|
require_relative "safe_image/sandbox"
|
|
38
|
+
require_relative "safe_image/zygote"
|
|
23
39
|
require_relative "safe_image/path_safety"
|
|
24
40
|
require_relative "safe_image/optimizer"
|
|
25
41
|
require_relative "safe_image/svg_metadata"
|
|
42
|
+
require_relative "safe_image/svg_css"
|
|
26
43
|
require_relative "safe_image/svg_sanitizer"
|
|
27
44
|
require_relative "safe_image/remote"
|
|
45
|
+
require_relative "safe_image/ico"
|
|
28
46
|
require_relative "safe_image/image_magick_backend"
|
|
29
47
|
require_relative "safe_image/jpegli_backend"
|
|
30
48
|
require_relative "safe_image/vips_backend"
|
|
@@ -34,46 +52,83 @@ require_relative "safe_image/discourse_compat"
|
|
|
34
52
|
module SafeImage
|
|
35
53
|
module_function
|
|
36
54
|
|
|
37
|
-
@
|
|
55
|
+
@config = nil
|
|
56
|
+
|
|
57
|
+
# Decides, in one place, everything that varies by host: which backend
|
|
58
|
+
# decodes untrusted bytes, whether operations run inside the Landlock
|
|
59
|
+
# sandbox, and the default decompression-bomb ceiling. Must be called before
|
|
60
|
+
# any operation; calling it again replaces the configuration.
|
|
61
|
+
#
|
|
62
|
+
# Validation is eager so a misconfigured host fails at boot rather than on
|
|
63
|
+
# the first request.
|
|
64
|
+
def configure!(backend:, landlock:, max_pixels: DEFAULT_MAX_PIXELS)
|
|
65
|
+
backend = backend.to_sym
|
|
66
|
+
unless BACKENDS.include?(backend)
|
|
67
|
+
raise ArgumentError, "unknown backend: #{backend.inspect} (expected :vips or :imagemagick)"
|
|
68
|
+
end
|
|
69
|
+
unless [true, false].include?(landlock)
|
|
70
|
+
raise ArgumentError, "landlock must be true or false, got: #{landlock.inspect}"
|
|
71
|
+
end
|
|
72
|
+
max_pixels = Integer(max_pixels)
|
|
73
|
+
raise ArgumentError, "max_pixels must be positive" if max_pixels <= 0
|
|
74
|
+
|
|
75
|
+
case backend
|
|
76
|
+
when :vips
|
|
77
|
+
begin
|
|
78
|
+
VipsGlue.init!
|
|
79
|
+
rescue VipsUnavailableError => e
|
|
80
|
+
raise Error, "backend: :vips requested but libvips is unavailable: #{e.message}"
|
|
81
|
+
end
|
|
82
|
+
when :imagemagick
|
|
83
|
+
unless Runner.available?("magick") || Runner.available?("convert")
|
|
84
|
+
raise Error, "backend: :imagemagick requested but no magick/convert executable was found"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
if landlock && !Sandbox.available?
|
|
88
|
+
raise Error, "landlock: true requested but the Landlock sandbox is unavailable on this host"
|
|
89
|
+
end
|
|
38
90
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
end
|
|
91
|
+
# The zygote bakes the backend and max_pixels in at boot; a reconfigure
|
|
92
|
+
# must not serve from a stale one.
|
|
93
|
+
Zygote.shutdown!
|
|
43
94
|
|
|
44
|
-
|
|
45
|
-
@sandbox_enabled = false
|
|
95
|
+
@config = Config.new(backend: backend, landlock: landlock, max_pixels: max_pixels)
|
|
46
96
|
end
|
|
47
97
|
|
|
48
|
-
def
|
|
49
|
-
@
|
|
98
|
+
def config
|
|
99
|
+
@config || raise(NotConfiguredError, "call SafeImage.configure!(backend: :vips | :imagemagick, landlock: true | false) before using SafeImage")
|
|
50
100
|
end
|
|
51
101
|
|
|
52
|
-
def
|
|
53
|
-
previous = @sandbox_enabled
|
|
54
|
-
@sandbox_enabled = false
|
|
55
|
-
yield
|
|
56
|
-
ensure
|
|
57
|
-
@sandbox_enabled = previous
|
|
58
|
-
end
|
|
102
|
+
def configured? = !@config.nil?
|
|
59
103
|
|
|
60
104
|
def sandbox_available? = Sandbox.available?
|
|
61
105
|
|
|
62
|
-
|
|
63
|
-
|
|
106
|
+
# Internal: whether operations must route through the sandbox worker. False
|
|
107
|
+
# before configure! (so configure!'s own availability probes can run
|
|
108
|
+
# commands) and inside worker children (so sandboxed operations never nest).
|
|
109
|
+
def sandbox?
|
|
110
|
+
!!@config&.landlock && ENV["SAFE_IMAGE_SANDBOX_CHILD"] != "1"
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Internal: per-call max_pixels overrides the configured default.
|
|
114
|
+
def resolved_max_pixels(max_pixels)
|
|
115
|
+
max_pixels.nil? ? config.max_pixels : max_pixels
|
|
64
116
|
end
|
|
65
117
|
|
|
66
118
|
def maybe_sandbox(operation, args: [], kwargs: {})
|
|
67
|
-
|
|
119
|
+
config
|
|
120
|
+
return yield unless sandbox?
|
|
68
121
|
|
|
69
|
-
|
|
122
|
+
Sandbox.public_call!(operation, args: args, kwargs: kwargs)
|
|
70
123
|
end
|
|
71
124
|
|
|
72
125
|
def probe(path, max_pixels: nil)
|
|
73
126
|
maybe_sandbox(:probe, args: [path], kwargs: { max_pixels: max_pixels }) do
|
|
74
127
|
path = PathSafety.local_path(path)
|
|
128
|
+
max_pixels = resolved_max_pixels(max_pixels)
|
|
75
129
|
|
|
76
|
-
|
|
130
|
+
case File.extname(path).downcase
|
|
131
|
+
when ".svg"
|
|
77
132
|
info = SvgMetadata.probe(path, max_pixels: max_pixels)
|
|
78
133
|
Result.new(
|
|
79
134
|
input: File.expand_path(path),
|
|
@@ -87,10 +142,26 @@ module SafeImage
|
|
|
87
142
|
duration_ms: info.fetch(:duration_ms),
|
|
88
143
|
optimizer: nil
|
|
89
144
|
)
|
|
145
|
+
when ".ico"
|
|
146
|
+
# Pure-Ruby directory parse; reports the largest entry's dimensions.
|
|
147
|
+
info = Ico.probe(path, max_pixels: max_pixels)
|
|
148
|
+
Result.new(
|
|
149
|
+
input: File.expand_path(path),
|
|
150
|
+
output: nil,
|
|
151
|
+
input_format: "ico",
|
|
152
|
+
output_format: nil,
|
|
153
|
+
width: info.fetch(:width),
|
|
154
|
+
height: info.fetch(:height),
|
|
155
|
+
filesize: File.size(path),
|
|
156
|
+
backend: "ico-metadata",
|
|
157
|
+
duration_ms: info.fetch(:duration_ms),
|
|
158
|
+
optimizer: nil
|
|
159
|
+
)
|
|
90
160
|
else
|
|
91
|
-
|
|
161
|
+
case config.backend
|
|
162
|
+
when :vips
|
|
92
163
|
Processor.new(max_pixels: max_pixels).probe(path)
|
|
93
|
-
|
|
164
|
+
when :imagemagick
|
|
94
165
|
info = ImageMagickBackend.probe(path, max_pixels: max_pixels)
|
|
95
166
|
Result.new(
|
|
96
167
|
input: File.expand_path(path),
|
|
@@ -144,24 +215,60 @@ module SafeImage
|
|
|
144
215
|
|
|
145
216
|
def orientation(path, max_pixels: nil)
|
|
146
217
|
maybe_sandbox(:orientation, args: [path], kwargs: { max_pixels: max_pixels }) do
|
|
147
|
-
|
|
218
|
+
case File.extname(PathSafety.local_path(path)).downcase
|
|
219
|
+
when ".svg", ".ico"
|
|
220
|
+
# No EXIF orientation in either format; upright by definition.
|
|
148
221
|
1
|
|
149
222
|
else
|
|
150
|
-
|
|
151
|
-
|
|
223
|
+
max_pixels = resolved_max_pixels(max_pixels)
|
|
224
|
+
case config.backend
|
|
225
|
+
when :vips
|
|
226
|
+
# Header-only native read.
|
|
227
|
+
VipsBackend.orientation(path, max_pixels: max_pixels)
|
|
228
|
+
when :imagemagick
|
|
229
|
+
# Probe first: rejects undecodable files and enforces the pixel cap.
|
|
230
|
+
ImageMagickBackend.probe(path, max_pixels: max_pixels)
|
|
231
|
+
ImageMagickBackend.orientation(path)
|
|
232
|
+
end
|
|
152
233
|
end
|
|
153
234
|
end
|
|
154
235
|
end
|
|
155
236
|
|
|
237
|
+
def dominant_color(path, max_pixels: nil)
|
|
238
|
+
maybe_sandbox(:dominant_color, args: [path], kwargs: { max_pixels: max_pixels }) do
|
|
239
|
+
max_pixels = resolved_max_pixels(max_pixels)
|
|
240
|
+
case config.backend
|
|
241
|
+
when :vips
|
|
242
|
+
if File.extname(PathSafety.local_path(path)).downcase == ".ico"
|
|
243
|
+
# Pure-Ruby ICO decode; vips only averages the decoded pixels.
|
|
244
|
+
Ico.dominant_color(path, max_pixels: max_pixels)
|
|
245
|
+
else
|
|
246
|
+
VipsBackend.dominant_color(path, max_pixels: max_pixels)
|
|
247
|
+
end
|
|
248
|
+
when :imagemagick
|
|
249
|
+
imagemagick_dominant_color(path, max_pixels: max_pixels)
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def imagemagick_dominant_color(path, max_pixels:)
|
|
255
|
+
# Probe first: rejects undecodable files and enforces the pixel cap
|
|
256
|
+
# before ImageMagick fully decodes the image to average it.
|
|
257
|
+
probe(path, max_pixels: max_pixels)
|
|
258
|
+
ImageMagickBackend.dominant_color(path)
|
|
259
|
+
end
|
|
260
|
+
|
|
156
261
|
def fastimage_type(format)
|
|
157
262
|
format.to_s == "jpg" ? :jpeg : format.to_s.to_sym
|
|
158
263
|
end
|
|
159
264
|
|
|
160
265
|
def remote_info(url, **kwargs)
|
|
266
|
+
config
|
|
161
267
|
Remote.info(url, **kwargs)
|
|
162
268
|
end
|
|
163
269
|
|
|
164
270
|
def remote_size(url, **kwargs)
|
|
271
|
+
config
|
|
165
272
|
Remote.size(url, **kwargs)
|
|
166
273
|
end
|
|
167
274
|
|
|
@@ -170,18 +277,26 @@ module SafeImage
|
|
|
170
277
|
end
|
|
171
278
|
|
|
172
279
|
def remote_type(url, **kwargs)
|
|
280
|
+
config
|
|
173
281
|
Remote.type(url, **kwargs)
|
|
174
282
|
end
|
|
175
283
|
|
|
176
284
|
def remote_animated?(url, **kwargs)
|
|
285
|
+
config
|
|
177
286
|
Remote.animated?(url, **kwargs)
|
|
178
287
|
end
|
|
179
288
|
|
|
289
|
+
def remote_dominant_color(url, **kwargs)
|
|
290
|
+
config
|
|
291
|
+
Remote.dominant_color(url, **kwargs)
|
|
292
|
+
end
|
|
293
|
+
|
|
180
294
|
def fetch_remote(url, **kwargs, &block)
|
|
295
|
+
config
|
|
181
296
|
Remote.fetch(url, **kwargs, &block)
|
|
182
297
|
end
|
|
183
298
|
|
|
184
|
-
def thumbnail(input:, output:, width:, height:, format: nil, quality: 85, max_pixels: nil,
|
|
299
|
+
def thumbnail(input:, output:, width:, height:, format: nil, quality: 85, max_pixels: nil, optimize: false, optimize_mode: :lossless, chroma_subsampling: :auto)
|
|
185
300
|
maybe_sandbox(
|
|
186
301
|
:thumbnail,
|
|
187
302
|
kwargs: {
|
|
@@ -192,15 +307,12 @@ module SafeImage
|
|
|
192
307
|
format: format,
|
|
193
308
|
quality: quality,
|
|
194
309
|
max_pixels: max_pixels,
|
|
195
|
-
backend: backend,
|
|
196
310
|
optimize: optimize,
|
|
197
311
|
optimize_mode: optimize_mode,
|
|
198
|
-
execution: :inline,
|
|
199
|
-
encoder: encoder,
|
|
200
312
|
chroma_subsampling: chroma_subsampling
|
|
201
313
|
}
|
|
202
314
|
) do
|
|
203
|
-
Processor.new(max_pixels: max_pixels,
|
|
315
|
+
Processor.new(max_pixels: resolved_max_pixels(max_pixels), chroma_subsampling: chroma_subsampling).thumbnail(
|
|
204
316
|
input: input,
|
|
205
317
|
output: output,
|
|
206
318
|
width: width,
|
|
@@ -252,6 +364,7 @@ module SafeImage
|
|
|
252
364
|
end
|
|
253
365
|
|
|
254
366
|
def animated?(*args, **kwargs)
|
|
367
|
+
config
|
|
255
368
|
path = args.first
|
|
256
369
|
return false if path && File.extname(PathSafety.local_path(path)).downcase == ".svg"
|
|
257
370
|
|
|
@@ -267,6 +380,12 @@ module SafeImage
|
|
|
267
380
|
end
|
|
268
381
|
|
|
269
382
|
def sanitize_svg!(*args, **kwargs)
|
|
383
|
+
# Validate the required id_namespace in the parent (after the configured
|
|
384
|
+
# check) so omitting/malformed values raise ArgumentError consistently —
|
|
385
|
+
# otherwise, under the sandbox, the worker raises and it surfaces as a
|
|
386
|
+
# sandbox CommandError instead of the documented ArgumentError.
|
|
387
|
+
config
|
|
388
|
+
SvgSanitizer.resolve_namespace(kwargs.fetch(:id_namespace, SvgSanitizer::NAMESPACE_REQUIRED))
|
|
270
389
|
maybe_sandbox(:sanitize_svg!, args: args, kwargs: kwargs) { SvgSanitizer.sanitize!(*args, **kwargs) }
|
|
271
390
|
end
|
|
272
391
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: safe_image
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sam Saffron
|
|
@@ -10,6 +10,34 @@ bindir: bin
|
|
|
10
10
|
cert_chain: []
|
|
11
11
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: fiddle
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: nokogiri
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '1.16'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '1.16'
|
|
13
41
|
- !ruby/object:Gem::Dependency
|
|
14
42
|
name: rexml
|
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -17,7 +45,7 @@ dependencies:
|
|
|
17
45
|
- - "~>"
|
|
18
46
|
- !ruby/object:Gem::Version
|
|
19
47
|
version: '3.4'
|
|
20
|
-
type: :
|
|
48
|
+
type: :development
|
|
21
49
|
prerelease: false
|
|
22
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
51
|
requirements:
|
|
@@ -53,51 +81,52 @@ dependencies:
|
|
|
53
81
|
- !ruby/object:Gem::Version
|
|
54
82
|
version: '13.0'
|
|
55
83
|
- !ruby/object:Gem::Dependency
|
|
56
|
-
name:
|
|
84
|
+
name: rubocop-discourse
|
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
|
58
86
|
requirements:
|
|
59
87
|
- - "~>"
|
|
60
88
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '
|
|
89
|
+
version: '3.18'
|
|
62
90
|
type: :development
|
|
63
91
|
prerelease: false
|
|
64
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
93
|
requirements:
|
|
66
94
|
- - "~>"
|
|
67
95
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
96
|
+
version: '3.18'
|
|
69
97
|
- !ruby/object:Gem::Dependency
|
|
70
|
-
name:
|
|
98
|
+
name: landlock
|
|
71
99
|
requirement: !ruby/object:Gem::Requirement
|
|
72
100
|
requirements:
|
|
73
|
-
- - "
|
|
101
|
+
- - ">="
|
|
74
102
|
- !ruby/object:Gem::Version
|
|
75
|
-
version: '
|
|
103
|
+
version: '0'
|
|
76
104
|
type: :development
|
|
77
105
|
prerelease: false
|
|
78
106
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
107
|
requirements:
|
|
80
|
-
- - "
|
|
108
|
+
- - ">="
|
|
81
109
|
- !ruby/object:Gem::Version
|
|
82
|
-
version: '
|
|
110
|
+
version: '0'
|
|
83
111
|
description: 'Safe Image is a small Ruby image-processing boundary for untrusted uploads:
|
|
84
112
|
direct libvips thumbnails/probing, hardened ImageMagick compatibility operations,
|
|
85
113
|
optimisation, SVG sanitising, and optional atomic Landlock sandbox execution.'
|
|
86
114
|
email:
|
|
87
115
|
- sam@discourse.org
|
|
88
116
|
executables: []
|
|
89
|
-
extensions:
|
|
90
|
-
- ext/safe_image_native/extconf.rb
|
|
117
|
+
extensions: []
|
|
91
118
|
extra_rdoc_files: []
|
|
92
119
|
files:
|
|
120
|
+
- CHANGELOG.md
|
|
93
121
|
- LICENSE
|
|
94
122
|
- README.md
|
|
95
123
|
- SECURITY.md
|
|
96
|
-
- ext/safe_image_native/extconf.rb
|
|
97
|
-
- ext/safe_image_native/safe_image_native.c
|
|
98
124
|
- lib/safe_image.rb
|
|
99
125
|
- lib/safe_image/RT_sRGB.icm
|
|
100
126
|
- lib/safe_image/discourse_compat.rb
|
|
127
|
+
- lib/safe_image/fonts/DEJAVU-LICENSE
|
|
128
|
+
- lib/safe_image/fonts/DejaVuSans.ttf
|
|
129
|
+
- lib/safe_image/ico.rb
|
|
101
130
|
- lib/safe_image/image_magick_backend.rb
|
|
102
131
|
- lib/safe_image/imagemagick_policy/policy.xml
|
|
103
132
|
- lib/safe_image/jpegli_backend.rb
|
|
@@ -109,10 +138,13 @@ files:
|
|
|
109
138
|
- lib/safe_image/result.rb
|
|
110
139
|
- lib/safe_image/runner.rb
|
|
111
140
|
- lib/safe_image/sandbox.rb
|
|
141
|
+
- lib/safe_image/svg_css.rb
|
|
112
142
|
- lib/safe_image/svg_metadata.rb
|
|
113
143
|
- lib/safe_image/svg_sanitizer.rb
|
|
114
144
|
- lib/safe_image/version.rb
|
|
115
145
|
- lib/safe_image/vips_backend.rb
|
|
146
|
+
- lib/safe_image/vips_glue.rb
|
|
147
|
+
- lib/safe_image/zygote.rb
|
|
116
148
|
homepage: https://github.com/sam-saffron-jarvis/safe-image
|
|
117
149
|
licenses:
|
|
118
150
|
- MIT
|
|
@@ -134,7 +166,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
134
166
|
- !ruby/object:Gem::Version
|
|
135
167
|
version: '0'
|
|
136
168
|
requirements: []
|
|
137
|
-
rubygems_version:
|
|
169
|
+
rubygems_version: 3.6.9
|
|
138
170
|
specification_version: 4
|
|
139
171
|
summary: Hardened image processing boundary for untrusted uploads
|
|
140
172
|
test_files: []
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "mkmf"
|
|
4
|
-
|
|
5
|
-
pkg_config("vips") or abort "libvips development files are required (pkg-config vips failed)"
|
|
6
|
-
have_header("vips/vips.h") or abort "missing vips/vips.h"
|
|
7
|
-
have_library("vips") or abort "missing libvips"
|
|
8
|
-
create_makefile("safe_image_native")
|