From 0f3d1eff1dde572d7490d8ace1ddb6a2628d369f Mon Sep 17 00:00:00 2001 From: Sag Date: Thu, 25 Jun 2026 14:40:09 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20theme=20zip=20extraction?= =?UTF-8?q?=20permissions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit no issue GScan validates and installs theme zips from its extracted temp directory. Some customer archives preserve read-only directory modes, which can block validation or later cleanup. Use the new @tryghost/zip owner-permission normalization option so uploaded themes are extracted with writable owner permissions while keeping the source archive unchanged. --- lib/read-zip.js | 4 +++- package.json | 2 +- test/read-zip.test.js | 9 +++++++-- yarn.lock | 15 ++++++++++----- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/lib/read-zip.js b/lib/read-zip.js index a181575c..67ea9d10 100644 --- a/lib/read-zip.js +++ b/lib/read-zip.js @@ -32,7 +32,9 @@ const resolveBaseDir = async (zipPath) => { const readZip = (zip, options = {}) => { const tempUuid = randomUUID(); const tempPath = os.tmpdir() + '/' + tempUuid; - const extractOptions = {}; + const extractOptions = { + ensureOwnerPermissions: true + }; if (options.limits) { extractOptions.limits = options.limits; diff --git a/package.json b/package.json index e79ee35d..147d01a5 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "@tryghost/nql": "0.13.1", "@tryghost/pretty-cli": "3.3.0", "@tryghost/server": "3.1.0", - "@tryghost/zip": "3.4.0", + "@tryghost/zip": "3.5.0", "chalk": "5.6.2", "express": "5.2.1", "express-handlebars": "8.0.1", diff --git a/test/read-zip.test.js b/test/read-zip.test.js index 211fa68b..51af148f 100644 --- a/test/read-zip.test.js +++ b/test/read-zip.test.js @@ -140,7 +140,10 @@ describe('Zip file handler can read zip files', function () { const zip = await mocked.readZip({path: '/tmp/theme.zip', name: 'theme.zip'}, {limits}); tempDirs.push(zip.origPath); - expect(extract).toHaveBeenCalledWith('/tmp/theme.zip', expect.any(String), {limits}); + expect(extract).toHaveBeenCalledWith('/tmp/theme.zip', expect.any(String), { + ensureOwnerPermissions: true, + limits + }); } finally { mocked.restore(); } @@ -162,7 +165,9 @@ describe('Zip file handler can read zip files', function () { errorDetails: extractError.message }); - expect(extract).toHaveBeenCalledWith('/tmp/theme.zip', expect.any(String), {}); + expect(extract).toHaveBeenCalledWith('/tmp/theme.zip', expect.any(String), { + ensureOwnerPermissions: true + }); } finally { mocked.restore(); } diff --git a/yarn.lock b/yarn.lock index 9d5a0795..d85b3cf5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -543,6 +543,11 @@ resolved "https://registry.yarnpkg.com/@tryghost/errors/-/errors-3.3.0.tgz#aa869dcd3a4118d038bd23a749ab3c2e18a383d1" integrity sha512-kDdCTuZeS38IWp0LwmmFX+ZtvT7j9c6GjJ+JYDStjSXr/63JB76BZIs5mL/5rTs1AcSe5AaaUIwSZlxrxtbu/A== +"@tryghost/errors@3.3.1": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@tryghost/errors/-/errors-3.3.1.tgz#da2d34c53685ce60180e4b911b23d194b458c765" + integrity sha512-/TbbCWtmNbc6JY3lR7g0wL+RxBndbFCv1O4BrQ/HxrZCi3DFXUs3KUh2jJeOoWTtisqqdRkR1Z76NluKle3fQg== + "@tryghost/http-stream@2.3.0": version "2.3.0" resolved "https://registry.yarnpkg.com/@tryghost/http-stream/-/http-stream-2.3.0.tgz#731f8227bad3b4e3055ef9c22ac7f484b96d6541" @@ -684,12 +689,12 @@ "@tryghost/root-utils" "2.3.0" semver "7.8.4" -"@tryghost/zip@3.4.0": - version "3.4.0" - resolved "https://registry.yarnpkg.com/@tryghost/zip/-/zip-3.4.0.tgz#4dd96591364470a3b06d86082022a27d14655184" - integrity sha512-H4DnUK1gtSyGAtZwDlvsUF1D0qK8LpIa88ZY2mknopKenrYb52IfY6mHciTM7pR3/TGDcYI14LEMqJhbZlgMng== +"@tryghost/zip@3.5.0": + version "3.5.0" + resolved "https://registry.yarnpkg.com/@tryghost/zip/-/zip-3.5.0.tgz#e65c35b4fe6ec4b326ede5267cab6bc18e39990a" + integrity sha512-igHHPyBasmo+MWM+l8qtWWX/cdHlAQps5HbCfESi4zGPlkTzuScHfmFAnuXsCx+MZKP5LozgnQHOVIf6jkaU8A== dependencies: - "@tryghost/errors" "3.3.0" + "@tryghost/errors" "3.3.1" archiver "8.0.0" extract-zip "2.0.1"