Skip to content

v1.11.0

Choose a tag to compare

@mstgnz mstgnz released this 03 Aug 21:43
· 2 commits to main since this release

1.10.0 tightened upload validation. This release fixes the parts of it that rejected real files, which is the failure operators work around by turning validation off entirely.

Fixed

  • HEIC, MP4, MOV, 3GP and TIFF uploads were rejected under extensions the allowlist accepted. These are ISO base media files, and none of them has a fixed signature at offset 0: what identifies them is the literal ftyp at offset 4, preceded by that box's length. The content check treated the length as part of the signature, pinning 0x18 for HEIC and MP4 and 0x14 for 3GP, so a file passed only when its first box happened to be exactly 24 or 20 bytes long. ImageMagick writes 0x1C for HEIC, ffmpeg writes 0x20 for MP4, QuickTime writes 0x14 with a qt brand, and iPhone HEIC varies by device. All were refused as INVALID_FILE_CONTENT. TIFF had no entry at all and could never pass in either byte order.

    The family is now identified structurally, by ftyp at offset 4, and TIFF is recognised little- and big-endian. Nothing widens what the service accepts: the extension allowlist is what keeps executable types out, and it is unchanged.

  • The MIME type gate rejected valid uploads and stopped nothing. ValidateFile checked the multipart part's Content-Type against an allowlist, but that string is written by the client, so as a gate it never had teeth. application/octet-stream has to be on any such list because mobile clients send it for everything, and that single entry was a documented bypass. Meanwhile it refused a perfectly good PNG whose part carried no Content-Type at all, and a PDF announced as application/x-pdf. The only workaround available was VALIDATE_FILE=false, which switches off the two checks that were doing the work.

    The gate is gone. What rejects a hostile upload is the extension allowlist and the content signature, neither of which the caller controls.

  • An empty config/tokens.json stopped the service from booting. The line that matters is between "no scoped tokens" and "scoped tokens that cannot be read", not between "no file" and "some file". A missing, empty, {} or {"buckets": []} file now boots with zero scoped tokens. Content that cannot be parsed, or an entry that fails validation, still stops boot: there the operator wrote definitions and believes they are live, so starting with fewer credentials than configured would surface as callers getting "invalid token" with nothing to explain it.

    The general TOKEN is unaffected either way, and is accepted on every route whatever this file contains.

  • Placeholder AWS credentials switched the archive on. Copying .env.example left AWS_ACCESS_KEY_ID=your-aws-key in place, which is not empty, so the archive reported itself enabled and then failed against a destination that never existed. The shipped placeholders now count as unconfigured.

Added

  • MINIO_VERSION pins the MinIO server image. Blank still means latest, which is fine for a fresh install and not fine for an existing one: MinIO migrates its on-disk format forward and never backward, so an accidental jump is not something you undo by changing the tag back.

  • The archive destination is verified at boot when archiving is enabled. Credentials that look valid stay looking valid until something uses them; this makes one real call and reports the result. It runs off the startup path deliberately, so boot never waits on AWS and a transient outage during a restart cannot disable archiving for the life of the process.

Changed

  • .env.example is ordered by how much attention each setting needs, from "nothing works until this is right" down to values you can ignore for years.
  • The example host nginx config (cdn.conf) is rewritten: TLS and redirects, the proxy headers the service actually reads, a log_format that records the vhost, a cache zone, and an expires map that respects a no-store from upstream. Note that caching in front of this service means a deleted object keeps being served until its entry expires, and the open source nginx build has no purge.
  • docs/api.md and the OpenAPI spec now describe two upload gates rather than three, and list the formats actually accepted. The spec had been advertising "JPG, PNG, GIF, WebP" while the allowlist held 27 extensions.

Upgrading

No configuration change is required and no endpoint contract changed. Uploads that 1.10.0 refused start succeeding; nothing that it accepted is now refused.

If you set VALIDATE_FILE=false to work around rejected uploads, this is the release to set it back to true.