Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 5 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ RUN apk update && \
icu-libs \
imagemagick \
imagemagick-heic \
imagemagick-svg \
libavif \
libgomp \
libheif \
Expand Down Expand Up @@ -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 '<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8"/>' > /tmp/probe.svg; \
identify /tmp/probe.svg; \
rm /tmp/probe.svg

WORKDIR /usr/src/code

Expand Down
15 changes: 9 additions & 6 deletions policy.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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/.
-->
Expand Down Expand Up @@ -60,13 +60,16 @@
<policy domain="delegate" rights="none" pattern="HTTPS"/>
<policy domain="delegate" rights="none" pattern="HTTP"/>
<policy domain="coder" rights="none" pattern="{PS,PS2,PS3,EPS,EPI,EPT,EPSF,EPSI,PDF,XPS}"/>
<policy domain="coder" rights="none" pattern="{MSL,MVG,SVG,SVGZ,MSVG,TEXT,LABEL,CAPTION}"/>
<policy domain="coder" rights="none" pattern="{MSL,MVG,MSVG,TEXT,LABEL,CAPTION}"/>
<policy domain="coder" rights="none" pattern="{URL,FTP,EPHEMERAL,MPEG,MPG}"/>

<!-- SVG is an SSRF/XXE vector (its renderer resolves external hrefs and
entities) and is never a preview input. The module deny also closes the
delegate route (e.g. rsvg), which a plain coder deny does not. -->
<policy domain="module" rights="none" pattern="{SVG,MSVG,SVGZ}"/>
<!-- SVG previews decode via librsvg (imagemagick-svg), read-only - nothing
encodes SVG. SVG is historically an SSRF/XXE vector, so the guards
around it stay load-bearing: the HTTP/HTTPS delegate denies block
external hrefs, the @* path deny below blocks local file reads, and
MSVG stays denied above so ImageMagick's internal SVG renderer (the
riskier fallback) cannot be reached even if librsvg goes missing. -->
<policy domain="coder" rights="read" pattern="{SVG,SVGZ}"/>

<!-- Block indirect reads (e.g. @file, caption:@file) that can leak local files. -->
<policy domain="path" rights="none" pattern="@*"/>
Expand Down
6 changes: 6 additions & 0 deletions tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand All @@ -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('<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"8\" height=\"8\"/>'); print($i->getImageWidth());"]
expectedOutput: ["8"]
Comment thread
Meldiron marked this conversation as resolved.
- name: 'PHP intl'
command: "php"
args: ["-r", 'print(\Normalizer::FORM_D);']
Expand Down
Loading