Skip to content

fix(image-node): upgrade to jimp v1 to clear unpatchable file-type advisory - #189

Open
jnbn wants to merge 2 commits into
Vibrant-Colors:mainfrom
jnbn:fix/jimp-v1-file-type-advisory
Open

fix(image-node): upgrade to jimp v1 to clear unpatchable file-type advisory#189
jnbn wants to merge 2 commits into
Vibrant-Colors:mainfrom
jnbn:fix/jimp-v1-file-type-advisory

Conversation

@jnbn

@jnbn jnbn commented Aug 8, 2026

Copy link
Copy Markdown

Problem

@vibrant/image-node depends on @jimp/* 0.22, which pins file-type@^16.5.4. The 16.x line is affected by GHSA-5v7r-6r5c-r473 (infinite loop in the ASF parser on malformed input, >=13.0.0 <21.3.1) and never received a backport, so the 16.x branch has no patched release.

That means every project depending on node-vibrant reports a moderate advisory:

file-type  13.0.0 - 21.3.0
Severity: moderate
file-type affected by infinite loop in ASF parser on malformed input
node_modules/@jimp/core/node_modules/file-type
  @jimp/core  ...
    @jimp/custom

Critically, downstream users cannot resolve this themselves. The obvious fix, an npm override forcing a patched file-type, breaks jimp outright: file-type 21.x is ESM-only and renamed fromBuffer to fileTypeFromBuffer, while jimp 0.22 calls the CJS fromBuffer. The result is a runtime failure on every image:

TypeError: _fileType.default.fromBuffer is not a function
    at getMIMEFromBuffer (node_modules/@jimp/core/dist/utils/image-bitmap.js:37)

So the only version that clears the advisory is the one jimp cannot use, and the fix has to happen here.

Solution

Move the image backend to jimp@^1.6.1, which already depends on file-type@^21.3.3 — past the fixes for both GHSA-5v7r-6r5c-r473 and GHSA-j47w-4g3g-c36v. This also collapses three dependencies (@jimp/custom, @jimp/plugin-resize, @jimp/types) into one.

Three API differences are handled:

  1. @jimp/custom's configure() no longer exists; jimp 1.x ships a preconfigured Jimp with resize included.
  2. Jimp.read no longer accepts a Buffer; buffers go through Jimp.fromBuffer. This also removes the type assertion the old code needed to work around the missing overload.
  3. resize() takes an options object rather than positional arguments.

One behavioural detail worth calling out: ImageBase.scaleDown passes fractional dimensions, which jimp 0.x rounded internally. jimp 1.x rounds identically, but the rounding is now explicit at the call site so the resized bitmap is provably unchanged rather than relying on an implementation detail.

Verification

Output is byte-for-byte identical. I compared palettes produced before and after the change across a corpus of 202 real images (mixed JPEG and PNG), covering all six palette entries:

Swatches compared 1184
Identical hex 1184 (100%)
Population counts identical yes
Load failures 0

Also run against the built artifact rather than just source, to confirm the packaged output behaves the same.

Package checks pass locally:

  • pnpm run test:types:ts56 — clean
  • pnpm run test:eslint — clean
  • pnpm run build — clean
  • pnpm run test:build (publint) — only the pre-existing pkg.repository.url suggestion, unrelated to this change

Note: pnpm run build at the workspace root fails for me before reaching any @vibrant/* package, in vibrant-fixture-sample:build (exit 130). That looks environmental rather than related to this change, so I built and checked @vibrant/image-node and its dependency directly.

Notes

  • No public API change; NodeImage keeps the same shape and behaviour.
  • jimp 1.x still cannot decode webp, so webp support is unchanged (still unsupported) by this PR.
  • There is precedent for this kind of bump in chore: upgrade @jimp to v0.22 for security #158, which moved @jimp/* to 0.22 for the same reason.

@vibrant/image-node depends on @jimp/* 0.22, which pins file-type ^16.5.4.
The 16.x line is affected by GHSA-5v7r-6r5c-r473 (infinite loop in the ASF
parser on malformed input) and never received a patch, so every consumer of
node-vibrant reports a moderate advisory that cannot be resolved from the
outside: overriding file-type to a patched release breaks jimp, because
21.x is ESM-only and renamed fromBuffer to fileTypeFromBuffer.

jimp 1.6.1 already depends on file-type ^21.3.3, which is past the fixes for
both GHSA-5v7r-6r5c-r473 and GHSA-j47w-4g3g-c36v, so moving the image backend
forward resolves it at the source.

Three API differences are handled:

- @jimp/custom's configure() is gone; jimp 1.x ships a preconfigured Jimp.
- Jimp.read no longer takes a Buffer; buffers go through Jimp.fromBuffer.
  This also removes the type assertion the old code needed.
- resize() takes an options object rather than positional arguments.

ImageBase.scaleDown passes fractional dimensions, which jimp 0.x rounded
internally. That rounding is now explicit at the call site so the resized
bitmap is byte-for-byte what it was before.

Output is unchanged. Verified against a corpus of 202 real images (a mix of
JPEG and PNG, 1184 swatches across all six palette entries) comparing the
palettes produced before and after: 1184 of 1184 identical, including
population counts.
@crutchcorn

crutchcorn commented Aug 8, 2026

Copy link
Copy Markdown
Member

Jimp must continue to use the custom adapter:

https://jimp-dev.github.io/jimp/guides/custom-jimp/

To avoid bundle size increases

Importing the `jimp` bundle pulled in every plugin, of which this package
calls only resize, and pushed all of them onto its dependents. Composed with
createJimp instead, which is the v1 equivalent of the configure() call this
package used before, with the five decoders that @jimp/types used to provide.
@jnbn

jnbn commented Aug 9, 2026

Copy link
Copy Markdown
Author

Good catch, thank you. Fixed in bdc8e53.

The adapter is composed with createJimp now, which is the v1 equivalent of the configure() call the package used before:

const Jimp = createJimp({
	formats: [bmp, msBmp, gif, jpeg, png, tiff],
	plugins: [resize.methods],
});

The six formats are what @jimp/types supplied in 0.22, and resize is still the only plugin, so nothing about the dependency footprint grows compared with the version this replaces.

One small thing worth flagging for review: the instance type is now taken from the loaders rather than written as InstanceType<typeof Jimp>, because the two are structurally different. write resolves its extension and mime generics differently on the class than on what read and fromBuffer return, so the direct form does not assign. Deriving it avoids the type assertion the 0.22 code needed.

test:lib, test:types (5.3 through 5.6), test:build and test:eslint all pass on the package, and sherif is clean. I also ran the built package over 65 real jpg and png images and compared hex, rgb and population for all six swatches against palettes generated by 3.1.6: 1170 of 1170 fields identical.

@hylaride

hylaride commented Sep 3, 2026

Copy link
Copy Markdown

@crutchcorn Any possibility of getting this out anytime soon now that the custom adapter is back? Not trying to nag open source volunteers, just curious if it's planned for anytime soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants