fix(backend-shared): replace unmaintained decompress with @xhmikosr/d… - #11529
Open
allanmaclean wants to merge 1 commit into
Open
fix(backend-shared): replace unmaintained decompress with @xhmikosr/d…#11529allanmaclean wants to merge 1 commit into
allanmaclean wants to merge 1 commit into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Check List
Issue Reference this PR resolves
#11264
Description of Changes Made
@cubejs-backend/sharedand@cubejs-backend/templatesboth depended ondecompress@^4.2.1,which is unmaintained and carries an unpatched path-traversal advisory
(GHSA-mp2f-45pm-3cg9
/ CVE-2026-53486) — archive extraction can create files and links outside the target directory.
This swaps both onto the maintained fork
@xhmikosr/decompress@^11.1.4, where it is patched.The fork is ESM-only (
"type": "module", Node>=20), which is why downstream consumers can'tjust override the dependency themselves. Rather than migrating any package to ESM, the ESM
boundary is confined to a single function.
Approach
@cubejs-backend/sharedgains one exported helper,extractArchive(archivePath, cwd):The subtlety: under the repo's default
"module": "commonjs", TypeScript downlevels thatimport()toPromise.resolve().then(() => require(...)), which defeats the purpose. Sopackages/cubejs-backend-shared/tsconfig.jsonswitches tomodule/moduleResolution: "Node16".The package still emits CommonJS — only the
import()is left intact:No package migrates to ESM, and no
new Function('return import(...)')shim is needed.@cubejs-backend/templates(which already depends on@cubejs-backend/shared) just calls thehelper, so it drops both
decompressanddecompress-targzwith no tsconfig change of its own.@xhmikosr/decompressbundles@xhmikosr/decompress-targzin its default plugin set, so theexplicit
decompress-targzplugin is no longer required.Both packages already declare
"node": ">=20.0.0", matching the fork's engine requirement.Changes
cubejs-backend-shared/package.jsondecompress+@types/decompress→@xhmikosr/decompresscubejs-backend-shared/tsconfig.jsonmodule/moduleResolution: Node16cubejs-backend-shared/src/http-utils.tsextractArchivehelper;downloadAndExtractFileuses itcubejs-backend-shared/src/xhmikosr-decompress.d.tscubejs-backend-shared/tsconfig.jest.jsoncubejs-backend-shared/jest.config.jscubejs-backend-shared/test/http-utils.test.tscubejs-templates/package.jsondecompress+decompress-targzcubejs-templates/src/PackageFetcher.tsextractArchiveyarn.lockWhy the package-local
tsconfig.jest.json@xhmikosr/decompressships no type declarations, so the package needs an ambientdeclare module. ts-jest was pointed at the roottsconfig.jest.json, whoseincludedoesn't coverthis package's
src, so the declaration wasn't part of the ts-jest program and 9 existing suitesfailed to compile. Extending the package tsconfig keeps
includeintact. It pinsmoduleback toCommonJS because Jest runs sources through its own CommonJS runtime — the Node16 emit only matters
for the published
distbuild.Testing
Two tests in
packages/cubejs-backend-shared/test/http-utils.test.ts:.tar.gzfixture through the compiled CommonJS output,asserting nested paths and file contents survive.
distcontains a nativeawait import('@xhmikosr/decompress')and not a downleveled
require().The runtime test runs the extraction in a child
nodeprocess rather than in-process, becauseJest's CommonJS runtime intercepts dynamic
import()and routes it through its own loader, whichcannot load an ESM-only package (this holds even with
--experimental-vm-modules, since theimporting module is CommonJS). Running it as a plain
nodechild process is what makes theCommonJS → ESM hop observable. Migrating this package's Jest setup to native ESM would be a much
larger change to a 394-test suite, and isn't needed for the fix.
The emit guard is not redundant: Node.js
>=22.12canrequire()ESM, so if someone drops theNode16setting the runtime test would still pass on CI's Node 24 while the package broke on thesupported Node 20 floor. Verified by reverting the tsconfig — the runtime test passed and only the
guard failed.
Verified locally:
yarn tscacross the whole monorepo — clean (many packages consumeshared, so theNode16switch was the main risk; no downstream breakage).
yarn unitincubejs-backend-shared— 16 suites, 394 tests passing.yarn linton both changed packages, plusnpmPkgJsonLint— clean.meaningful one: it has no
require(esm), so it proves theimport()genuinely survived.Notes for reviewers
decompress@4.2.1remains inyarn.lockand isn't removable from this PR. Itcomes from
@cubejs-backend/native→@cubejs-infra/post-installer→@cubejs-backend/shared@0.33.20,i.e. an old published build of this very package pinned by
post-installer. Clearing it needsa
@cubejs-infra/post-installerrelease bumping that pin. Cube's own source tree no longerreferences
decompress.templates: extraction previously restricted itself to thedecompress-targzplugin; it now uses the fork's default plugin set (tar, tar.bz2, tar.gz, zip).The input is always a
master.tar.gzfrom GitHub, so this is not expected to matter.sharedhad no
pluginsoption before, so its accepted formats are unchanged.extractArchiveis newly exported from@cubejs-backend/shared. This is additive public API,chosen so the CommonJS → ESM boundary exists in exactly one place rather than being duplicated in
every package that needs to extract an archive.
AI disclosure: Opus 5 used in the creation of this PR.