Skip to content

v1.4.0

Latest

Choose a tag to compare

@github-actions github-actions released this 28 Oct 19:53
· 37 commits to main since this release
d1f2ff8

Upgrading from 1.3.0

No format or env var was removed, and no database migration runs. Every deployment should update:

  • From 1.1.0 to 1.3.0, GIFs and palette PNGs came back in one colour, and portrait photos on their side.
  • A request's upload and result are now removed however the request ends.
  • The image moves past dnspython CVE-2026-114605 (high) and Mako CVE-2026-102991, which scanners report for 1.3.0. Neither is reachable in FileMorph.

See the Fixed and Security entries in the changelog.

Five DOMPurify advisories in the Swagger UI bundle on /docs stay accepted, as in 1.3.0, because Swagger UI does not use the options they need. They are reviewed again on 2026-11-02 (see docs/security-overview.md).

Check before pulling:

  • Pinned to 1.3 or 1.3.0 (or their -office variants):
    • There is no 1.3.1, so these image tags stay on 1.3.0 without these fixes.
    • Move to 1.4 (or 1.4.0).
    • If you can't move to 1.4, SECURITY.md describes how to request a backport onto 1.3.
  • New:
    • Inserting pages into a PDF: POST /api/v1/pdf/insert and the page /pdf/insert.
    • A Word file goes in through LibreOffice, which only the -office image ships. Without it, or with FILEMORPH_OFFICE_ENGINE=mammoth, the API answers word_not_converted.
    • A page for Video → GIF: /convert/video-to-gif.
    • /tools lists every tool page.
  • Audio and video:
    • One time limit, MEDIA_SUBPROCESS_TIMEOUT_SECONDS, now covers the whole request, with a batch's files sharing it. It is 85 seconds by default; it used to be 600 seconds for each ffmpeg run.
    • An .env copied from the .env.example of 1.2.0 to 1.3.0 sets 600, which now covers the whole request. Delete the line to get the 85 seconds. Keep a higher value only where no CDN or proxy in front of FileMorph stops waiting sooner. The nginx and Caddy examples in docs/self-hosting.md wait 300 seconds.
    • A file that runs out of time gets 400 (invalid_input) instead of 500.
    • Two new refusals, neither with a setting, both 400 (invalid_input; video → GIF: video_too_complex, as before):
      • a video larger than 4K;
      • a stream in a codec outside INPUT_DECODERS (app/converters/_ffmpeg.py).
    • On Linux, ffmpeg now stops writing at the output cap. The answer stays 413 (output_cap_exceeded); its message no longer gives the output's size.
    • Video outputs no longer copy the source's tags and chapters. Audio keeps only the music tags.
  • Temporary files: the sweep for folders that a crashed worker left behind now runs every 10 minutes instead of every hour (TEMP_SWEEP_INTERVAL_MINUTES).
    • An .env copied from an older .env.example sets 60: change it to 10 or delete the line.
    • The sweep removes any temp folder older than TEMP_SWEEP_MAX_AGE_MINUTES, even one a request is still using. If MEDIA_SUBPROCESS_TIMEOUT_SECONDS is above 600 (ten minutes), set the max age above it.
  • Now 400 instead of 500:
    • Images between one and two times the size limit get decompression_bomb.
    • PDF → TXT of a PDF whose page content is JBIG2 data, on a host with jbig2dec.
  • Docker image and debugging:
    • In the image, the app's code belongs to root and is read-only for appuser; only /app/data stays writable. A custom Dockerfile that runs chown -R appuser:appuser /app can limit that to /app/data.
    • On Linux the app process is non-dumpable. Attaching a debugger or profiler (gdb, py-spy) needs root; in a container, root with SYS_PTRACE.
  • Translations: the app reads the .po files directly. A .mo placed in locale/ is ignored; edit the .po instead.
  • Installing from requirements.txt:
    • Floors that move up: FastAPI 0.162.0, SQLAlchemy 2.1.6, alembic 1.20.1, pypdf 6.21.2, ReportLab 5.1.0 and certifi 2026.10.23.

    • Newly listed, with these floors: Starlette 1.10.0, Mako 1.4.3 and dnspython 2.9.0.

    • FastAPI 0.155–0.162 changed three things:

      • it refuses unknown keyword arguments to Form() and Query();
      • it applies a parameter model's extra setting to the fields declared next to it;
      • it no longer reads an omitted bool form field as False.

      FileMorph's routes rely on none of these, but a fork's own routes might.

The full list of changes is in CHANGELOG.md.


What's Changed

  • fix(media): video and audio outputs drop the source's metadata — like images by @MrChengLen in #264
  • ci(deps): audit every lockfile against PyPI and OSV — uv and SBOM locks too, Mako 1.4.3 by @MrChengLen in #267
  • feat(media): ffmpeg runs with resource limits — time per request, memory, decoders by @MrChengLen in #271
  • feat(security): app process non-dumpable — gs without app secrets, code read-only by @MrChengLen in #272
  • refactor(pages): one catalogue of public pages — sitemap, llms.txt and related links built from it by @MrChengLen in #277
  • deps: dnspython 2.9.0 — CVE-2026-114605 in DNS name decompression by @MrChengLen in #278
  • fix(images): palette images keep their colours — strip_metadata now keeps the palette by @MrChengLen in #274
  • ci(deps): Bump the actions-all group across 1 directory with 2 updates by @dependabot[bot] in #269
  • test(media): #271 follow-ups — parent-death test waits on a pidfd; docs name ffmpeg and ffprobe by @MrChengLen in #276
  • chore(i18n): translations load from the .po files — no .mo in the repository by @MrChengLen in #275
  • fix(images): portrait photos stay upright — the EXIF turn is applied before the strip by @MrChengLen in #279
  • feat(pdf): insert pages into a PDF — a PDF, Word file or image at a chosen page by @MrChengLen in #280
  • fix(pdf): JBIG2 page content gets a 400 in PDF → TXT — also with jbig2dec installed by @MrChengLen in #281
  • feat(pages): /tools lists every tool page — quick actions, /tools and the footer read the page catalogue by @MrChengLen in #282
  • feat(pages): one layout for the tool pages — a breadcrumb instead of "← Back", one related block by @MrChengLen in #283
  • feat(pages): /convert/video-to-gif — a page for Video → GIF, findable on /tools, in the footer and from /compress by @MrChengLen in #284
  • deps: python-all batch — FastAPI 0.162, Starlette 1.10, SQLAlchemy 2.1.6 and five smaller updates by @MrChengLen in #285
  • feat(pages): /pdf/insert — insert a PDF, Word file or image at a chosen page by @MrChengLen in #286
  • fix(files): temp dirs go however a response ends — sweep every 10 minutes by @MrChengLen in #287
  • docs(changelog): release 1.4.0 — fold changelog.d, bump the version by @MrChengLen in #288

Full Changelog: v1.3.0...v1.4.0