From 8e83836de98bc910c1b5b2e753b9643ed8b43bf2 Mon Sep 17 00:00:00 2001 From: Daniel Lockyer Date: Tue, 26 Sep 2023 12:51:11 +0200 Subject: [PATCH] Fixed listing all themes when a theme path is invalid - in the event one of the theme paths is invalid (a symlink that goes to a non-existent path), Ghost currently bails out of listing all themes because `fs.stat` throws an error - in that case, we should just ignore the theme and continue - this helps us prepare new symlinks whilst their source is not valid - this commit wraps the code with try-catch to protect against that - also adds a test --- ghost/package-json/lib/package-json.js | 11 +++++++--- ghost/package-json/test/read.test.js | 28 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/ghost/package-json/lib/package-json.js b/ghost/package-json/lib/package-json.js index 95b13c2f19..5eb35f299e 100644 --- a/ghost/package-json/lib/package-json.js +++ b/ghost/package-json/lib/package-json.js @@ -132,8 +132,13 @@ async function readPackages(packagePath) { } if (file.isSymbolicLink()) { - const packageFileOrig = await fs.stat(join(packagePath, file.name)); - return packageFileOrig.isDirectory(); + try { + const packageFileOrig = await fs.stat(join(packagePath, file.name)); + return packageFileOrig.isDirectory(); + } catch (err) { + // if there's an error reading the symlink, we should just return false + return false; + } } // Check the remaining items to ensure they are a directory @@ -144,7 +149,7 @@ async function readPackages(packagePath) { const absolutePath = join(packagePath, packageFile.name); return processPackage(absolutePath, packageFile.name); }))); - + return _.keyBy(packages, 'name'); } diff --git a/ghost/package-json/test/read.test.js b/ghost/package-json/test/read.test.js index 7d125ae310..f587ef83b5 100644 --- a/ghost/package-json/test/read.test.js +++ b/ghost/package-json/test/read.test.js @@ -137,6 +137,34 @@ describe('package-json read', function () { .catch(done) .finally(packagePath.removeCallback); }); + + it('should read directory and ignore invalid symlinks', async function () { + const packagePath = tmp.dirSync({unsafeCleanup: true}); + const pkgJson = JSON.stringify({ + name: 'test' + }); + + // create example theme + fs.mkdirSync(join(packagePath.name, 'testtheme')); + fs.writeFileSync(join(packagePath.name, 'testtheme', 'package.json'), pkgJson); + fs.writeFileSync(join(packagePath.name, 'testtheme', 'index.hbs'), ''); + + // Create a symlink that has a missing source dir + fs.symlinkSync(join(packagePath.name, 'missing-dir'), join(packagePath.name, 'source')); + + try { + const pkgs = await packageJSON.readPackages(packagePath.name); + pkgs.should.eql({ + testtheme: { + name: 'testtheme', + path: join(packagePath.name, 'testtheme'), + 'package.json': null + } + }); + } finally { + await packagePath.removeCallback(); + } + }); }); describe('readPackage', function () {