From 0ded7e9b7524c390d35034465b4a46a88f2cc5d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 27 Aug 2026 20:50:28 +0200 Subject: [PATCH] Enable SVG decode via librsvg, read-only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Appwrite preview endpoint substitutes SVG uploads with a placeholder icon because Imagick cannot open them: the image ships no SVG decode delegate (Alpine's imagemagick package without the imagemagick-svg subpackage) and the hardened policy denies the SVG coders and module. Install imagemagick-svg, which decodes through librsvg — the memory-safe Rust renderer — and grant the SVG/SVGZ coders read-only rights so previews can rasterize, crop, rotate and convert SVG uploads. The reasons SVG was blocked stay enforced: HTTP/HTTPS delegates remain denied (no external hrefs), the @* path deny still blocks local file reads, MSVG — ImageMagick's internal, riskier SVG renderer — stays denied so it cannot be reached even if librsvg goes missing, and SVG encode is denied by the read-only grant. The policy install step now decodes a probe SVG so the build fails loudly if the delegate or policy regresses, and structure tests assert the registered formats, the read-only policy pairing, and a real decode through the PHP extension. Co-Authored-By: Claude Fable 5 --- CHANGES.md | 1 + Dockerfile | 6 +++++- policy.xml | 15 +++++++++------ tests.yaml | 6 ++++++ 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 201debf..0862ef1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,7 @@ ### Add +* SVG decode support for ImageMagick. `imagemagick-svg` (the librsvg-backed coder) is now installed, and the hardened policy grants the `SVG`/`SVGZ` coders read-only rights so Imagick can rasterize SVG uploads for preview transformations. The guards that made SVG worth blocking stay in force: HTTP/HTTPS delegates and `@*` path reads remain denied (no external hrefs, no local file leaks), `MSVG` — ImageMagick's internal, riskier SVG renderer — stays blocked so it cannot be reached even if librsvg goes missing, and SVG encode is still denied. The policy install step now decodes a probe SVG so the build fails loudly if SVG support regresses, and structure tests assert the delegate, the read-only policy pairing, and a real decode. * Weekly dependency automation (`.github/workflows/dependencies.yml`). A scheduled job resolves the newest upstream release for every pinned Dockerfile source, rewrites the pins, opens a pull request, waits for the exact CI runs for that head, approves and merges it, then tags, builds, and publishes the release. A `recover` step resumes a run that died between merge and publish, so a half-finished release is completed rather than duplicated. * PHP automation domain under `.github/scripts` — `Dependency` (catalog, resolvers, Dockerfile pin rewriting, reporting), `Automation` (release orchestration, version selection, merge and target validation, recovery), `Command`, and `Parity`. Entry points are `bin/dependencies.php`, `bin/orchestrator.php`, and `bin/parity.php`. * Composer tooling for the automation: `lint` (Pint), `check` (PHPStan), `test` (PHPUnit), `parity` (asserts every source class has covering tests), and `verify` to run all four. CI runs `composer verify` before touching any dependency. diff --git a/Dockerfile b/Dockerfile index 18b0702..3764da2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -267,6 +267,7 @@ RUN apk update && \ icu-libs \ imagemagick \ imagemagick-heic \ + imagemagick-svg \ libavif \ libgomp \ libheif \ @@ -294,7 +295,10 @@ RUN set -eux; \ POLICY_DIR="$(identify -list configure | awk '/^CONFIGURE_PATH/ {print $2}' | cut -d: -f1)"; \ cp /tmp/policy.xml "${POLICY_DIR%/}/policy.xml"; \ rm /tmp/policy.xml; \ - identify -list policy | grep -q '50KP' + identify -list policy | grep -q '50KP'; \ + printf '' > /tmp/probe.svg; \ + identify /tmp/probe.svg; \ + rm /tmp/probe.svg WORKDIR /usr/src/code diff --git a/policy.xml b/policy.xml index ea431ba..10c930f 100644 --- a/policy.xml +++ b/policy.xml @@ -23,7 +23,7 @@ allocated, and disk caps the pixel-cache spill. memory/map/area merely change when the cache moves to disk. All values are tunable. - Preview inputs are limited to JPEG, PNG, HEIC, WEBP and GIF (output adds + Preview inputs are limited to JPEG, PNG, HEIC, WEBP, GIF and SVG (output adds AVIF), so the delegate coders disabled below are never used and only add risk. Validate changes with https://imagemagick-secevaluator.doyensec.com/. --> @@ -60,13 +60,16 @@ - + - - + + diff --git a/tests.yaml b/tests.yaml index 228e1f3..39d3436 100644 --- a/tests.yaml +++ b/tests.yaml @@ -102,6 +102,7 @@ commandTests: args: ["-i"] expectedOutput: - "ImageMagick supported formats .*WEBP.*" + - "ImageMagick supported formats .*SVG.*" - name: 'ImageMagick hardened policy loaded' command: "identify" args: ["-list", "policy"] @@ -110,6 +111,11 @@ commandTests: - "name: height" - "name: disk" - "50KP" + - "rights: Read\\s+pattern: \\{SVG,SVGZ\\}" + - name: 'ImageMagick SVG decode' + command: "php" + args: ["-r", "$i = new Imagick(); $i->readImageBlob(''); print($i->getImageWidth());"] + expectedOutput: ["8"] - name: 'PHP intl' command: "php" args: ["-r", 'print(\Normalizer::FORM_D);']