safe_image 0.1.0 → 0.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: cb95e7819270c76a02c8ca67321d234f945cb572098e80055e43b44fac0c84ce
4
- data.tar.gz: 3886ea56e99336f15cc1afd124ac57e200052120b2f61dd98bb17240fe1d3d04
3
+ metadata.gz: e078245fa2e4fb0707b0d477ad0c4b41f31423905ed0dc84f964cd648a5a032f
4
+ data.tar.gz: 9155876c2761ac9c6afb2b4da4a97af8b6b7e035ffee553c72dacda6166c1048
5
5
  SHA512:
6
- metadata.gz: e5da964df60b257eb3d72df410379c9ae52b66375d8b75f165a0ad49953099e7f41c894f896d3d8c2371fff4337d53d096cb78657dfba2c7f455f14c1d9bb873
7
- data.tar.gz: 201b2f6499be3cc1bb10141c55688564022dca6f133760ecaccf6bb0502b536eb6e1ba2d1bc3824c9909d61474ac9ba5db35f812a85326b7c8cf387d4d947719
6
+ metadata.gz: 2f98d64d3b9665a156c27cbcef898cd148e6b559c73c03ba757b1147d2e47f1733d5a6e0ebc7d5c038cb989f874d67747175cd1d094cdf9677f98550cfe635be
7
+ data.tar.gz: ae1008885bf83da844145369cd929edd12c0af238fd0d975e2c078ef95b4d7cc301f1fed2da5cdd815d520591938acc144a2bd79d4bd2573336a2c9eb1078e5b
data/CHANGELOG.md ADDED
@@ -0,0 +1,158 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.2.0] - 2026-06-10
9
+
10
+ The host's whole image-processing posture is now decided in one place, once,
11
+ at boot. There is no per-call backend fidelity and no automatic routing
12
+ between backends.
13
+
14
+ ### Added
15
+
16
+ - **`SafeImage.configure!(backend:, landlock:, max_pixels:)` is mandatory**:
17
+ every operation before it raises the new `SafeImage::NotConfiguredError`.
18
+ `backend:` (`:vips` or `:imagemagick`) picks the decoder for all untrusted
19
+ bytes; `landlock:` decides sandboxing; `max_pixels:` sets the default
20
+ decompression-bomb ceiling (128MP, still overridable per call). Validation
21
+ is eager — a missing libvips, ImageMagick binary, or Landlock support fails
22
+ at boot, not on the first request. Calling `configure!` again replaces the
23
+ configuration atomically, so initializer reloads are safe.
24
+ - `SafeImage.config` (frozen current configuration) and
25
+ `SafeImage.configured?`.
26
+
27
+ ### Changed
28
+
29
+ - **Backend selection is strict.** `:auto` is gone; nothing ever falls back
30
+ from libvips to ImageMagick. A format the configured backend cannot decode
31
+ (e.g. ICO transform input on `:vips`, HEIC on a libvips build without
32
+ libheif) raises `UnsupportedFormatError`. Pure-Ruby format handling (SVG
33
+ metadata/sanitising, ICO metadata) still works on either backend.
34
+ - **cjpegli is availability-driven**, like the optimizer tools: installed
35
+ means used for JPEG output on the `:vips` backend, absent means libvips
36
+ encodes. It is no longer a per-call or configuration choice.
37
+ - Sandbox worker processes inherit the parent's backend and pixel-ceiling
38
+ configuration through the request payload (landlock is forced off inside
39
+ the worker, so sandboxed operations never nest).
40
+ - `dominant_color` on the `:imagemagick` backend now decodes ICO through
41
+ ImageMagick; the pure-Ruby ICO decoder serves the `:vips` backend.
42
+
43
+ ### Removed
44
+
45
+ - Per-call `backend:` keyword on `resize`, `crop`, `downsize`, `convert`,
46
+ `thumbnail`, `letter_avatar`, `fix_orientation` and `dominant_color`.
47
+ - Per-call `encoder:` keyword on `convert`, `convert_to_jpeg`, `thumbnail`,
48
+ `resize`, `crop` and `downsize`.
49
+ - `execution:` keyword on `thumbnail` (`:sandbox` / `:sandbox_if_available`)
50
+ — sandboxing is decided by `configure!(landlock:)`.
51
+ - `SafeImage.enable_sandbox!`, `SafeImage.disable_sandbox!`,
52
+ `SafeImage.sandbox_enabled?` and `SafeImage.sandbox_call` —
53
+ `SafeImage.sandbox_available?` remains as the pre-configuration probe.
54
+
55
+ ## [0.1.1] - 2026-06-10
56
+
57
+ ### Added
58
+
59
+ - `SafeImage.dominant_color` and `SafeImage.remote_dominant_color`: alpha-weighted
60
+ average colour as an `RRGGBB` hex string, computed natively through libvips,
61
+ with an ImageMagick histogram backend (`backend: :imagemagick`) matching the
62
+ command Discourse runs.
63
+ - Pure-Ruby ICO support: directory parsing for `probe`/`frame_count`,
64
+ largest-entry favicon extraction for `convert_favicon_to_png` (embedded PNG
65
+ payloads are sanitised by re-encoding; legacy DIB payloads decode 1/4/8/24/32bpp
66
+ with the AND mask), and decompression-bomb caps enforced from the container
67
+ and IHDR headers before any decode.
68
+ - Native GIF decode through libvips' bundled libnsgif loader (first frame,
69
+ matching the ImageMagick `[0]` semantics) and GIF output through cgif.
70
+ - JPEG XL support end to end: native libvips loader/saver, ImageMagick coder
71
+ and policy allowlisting, remote content-type/extension handling, and a
72
+ committed test fixture.
73
+ - Native letter avatars rendered through libvips' Pango text support with the
74
+ glyph blended in one linear transform; the gem now bundles DejaVu Sans (see
75
+ `lib/safe_image/fonts/DEJAVU-LICENSE`) so the default font renders
76
+ identically on every host with no font packages installed.
77
+ - Header-only native metadata reads: `frame_count`/`animated?` from the
78
+ n-pages field and `orientation` from the orientation field — no pixel decode
79
+ and no `identify` subprocess for natively supported formats.
80
+ - `fix_orientation` lossless tier: MCU-aligned JPEGs are transformed with
81
+ `jpegtran` (zero generation loss) when installed, falling back to a libvips
82
+ re-encode with a new `quality:` keyword (default 95).
83
+ - Native `convert` tier: decode through the allowlisted loaders,
84
+ auto-orient, flatten transparency onto white for JPEG targets (matching the
85
+ ImageMagick path), re-encode; default JPEG quality 92 when unspecified.
86
+ - `backend:` keyword (`:auto` / `:vips` / `:imagemagick`) across
87
+ `resize`, `crop`, `downsize`, `convert`, `thumbnail`, `letter_avatar`,
88
+ `fix_orientation` and `dominant_color`. `:auto` prefers the native path and
89
+ uses ImageMagick only for capabilities libvips cannot serve; `:vips` fails
90
+ closed; `:imagemagick` pins the compatibility pipeline.
91
+ - Graceful operation without libvips: the new `SafeImage::VipsUnavailableError`
92
+ (a subclass of `UnsupportedFormatError`) makes `backend: :auto` route through
93
+ ImageMagick on hosts without the library, while explicit `backend: :vips`
94
+ calls fail closed. `SafeImage::VipsGlue.available?` reports the state.
95
+ - `docker/run.sh`: containerised validation against Debian bookworm's packaged
96
+ libvips 8.14 with no toolchain installed.
97
+
98
+ ### Changed
99
+
100
+ - **The compiled C extension is gone.** libvips is now bound at runtime
101
+ through a minimal Fiddle binding (`SafeImage::VipsGlue`) that exposes only
102
+ the operations the gem invokes. Nothing compiles at gem install time; the
103
+ only gem dependencies are `fiddle` and `rexml`. Minimum libvips is 8.13
104
+ (Debian bookworm's 8.14 package is tested); `SAFE_IMAGE_LIBVIPS` overrides
105
+ the library name.
106
+ - All transform defaults are now native-first: `resize`, `crop`, `downsize`,
107
+ `convert` and `thumbnail` default to `backend: :auto` (previously
108
+ ImageMagick for the first three and `:vips` fail-closed for `thumbnail`).
109
+ Pin `backend: :imagemagick` where byte-similar output with previously
110
+ generated thumbnails matters.
111
+ - The default letter avatar font is now `DejaVu-Sans` (bundled), and the
112
+ native renderer centres the glyph's ink box optically; the ImageMagick
113
+ path's baseline placement (which could clip descenders) remains available
114
+ via `backend: :imagemagick`. Regenerate cached avatars when switching.
115
+ - `convert_favicon_to_png` extracts the largest ICO entry rather than the
116
+ last one, and `probe` on ICO reports the largest entry's dimensions from a
117
+ pure-Ruby directory parse.
118
+ - libvips' GLib warnings about rejected input (e.g. "Not a PNG file") are
119
+ suppressed by default — failures already surface as exceptions; set
120
+ `SAFE_IMAGE_VIPS_WARNINGS=1` to restore them for debugging.
121
+
122
+ ### Fixed
123
+
124
+ - `resize`/`crop`/`downsize`/`convert` with `optimize: true` no longer raise
125
+ for output formats the optimizer tools cannot handle (GIF, JPEG XL); the
126
+ optimize pass is skipped instead.
127
+ - In-place `fix_orientation` writes through a sibling tempfile and renames,
128
+ so libvips never streams its input into itself.
129
+ - Garbage EXIF orientation tags clamp to the valid 1–8 range instead of
130
+ leaking raw values.
131
+ - The Landlock sandbox grants read access to Ruby's `libdir`, so workers no
132
+ longer fail to start under `--enable-shared` Rubies installed outside the
133
+ default read roots (e.g. GitHub Actions' hostedtoolcache builds). Sandbox
134
+ failures now include the child's stderr in the error message.
135
+
136
+ ### Security
137
+
138
+ - The bundled ImageMagick `policy.xml` gained write-only `HISTOGRAM`/`INFO`
139
+ coders (for the dominant-colour backend) and the `JXL` coder, plus a
140
+ regression test asserting the policy parses completely — ImageMagick's
141
+ hand-rolled tokenizer silently truncates the file on a stray backtick or
142
+ apostrophe in a comment.
143
+ - The libjxl loader/saver are deliberately re-enabled from libvips'
144
+ untrusted-operation block: JPEG XL is part of the supported input surface,
145
+ and inputs still pass extension routing, pixel caps and (optionally) the
146
+ Landlock sandbox.
147
+ - The Fiddle binding doubles as an operation allowlist, and a leak-loop test
148
+ guards GObject reference handling.
149
+
150
+ ## [0.1.0] - 2026-06-09
151
+
152
+ Initial release: hardened image-processing boundary for untrusted uploads.
153
+ Probing and metadata helpers, thumbnails/resize/crop/downsize/convert through
154
+ a native libvips fast path with an ImageMagick compatibility backend under a
155
+ restrictive bundled policy, JPEG/PNG optimisation (jpegoptim/oxipng/pngquant),
156
+ optional Jpegli encoding, allowlist-based SVG sanitising, SSRF-hardened remote
157
+ fetching with DNS pinning, symlink-safe path handling, 128MP default pixel
158
+ caps, and optional atomic Landlock sandboxing.