From 629e968878ded8dbdcd3c14f2e6c2a45fd09070e Mon Sep 17 00:00:00 2001 From: Henry Heino <46334387+personalizedrefrigerator@users.noreply.github.com> Date: Mon, 10 Jun 2024 23:41:23 -0700 Subject: [PATCH] Windows: Fixes #10525: Prevent notes with titles that differ only in case from being overwritten when exporting (#10541) --- packages/lib/fs-driver-base.ts | 10 +++++++++- packages/lib/fsDriver.test.ts | 18 ++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/packages/lib/fs-driver-base.ts b/packages/lib/fs-driver-base.ts index f776bbb06..9a2122205 100644 --- a/packages/lib/fs-driver-base.ts +++ b/packages/lib/fs-driver-base.ts @@ -152,13 +152,21 @@ export default class FsDriverBase { } let counter = 1; + // On Windows, ./FiLe.md and ./file.md are equivalent file paths. + // As such, to avoid overwriting reserved names, comparisons need to be + // case-insensitive. + reservedNames = reservedNames.map(name => name.toLowerCase()); + const isReserved = (testName: string) => { + return reservedNames.includes(testName.toLowerCase()); + }; + const nameNoExt = filename(name, true); let extension = fileExtension(name); if (extension) extension = `.${extension}`; let nameToTry = nameNoExt + extension; while (true) { // Check if the filename does not exist in the filesystem and is not reserved - const exists = await this.exists(nameToTry) || reservedNames.includes(nameToTry); + const exists = await this.exists(nameToTry) || isReserved(nameToTry); if (!exists) return nameToTry; if (!markdownSafe) { nameToTry = `${nameNoExt} (${counter})${extension}`; diff --git a/packages/lib/fsDriver.test.ts b/packages/lib/fsDriver.test.ts index 4a6e2d2bd..5984679ed 100644 --- a/packages/lib/fsDriver.test.ts +++ b/packages/lib/fsDriver.test.ts @@ -1,6 +1,7 @@ +import { join } from 'path'; import FsDriverNode from './fs-driver-node'; import shim from './shim'; -import { expectThrow } from './testing/test-utils'; +import { expectThrow, supportDir } from './testing/test-utils'; const windowsPartitionLetter = __filename[0]; @@ -15,7 +16,6 @@ function platformPath(path: string) { } describe('fsDriver', () => { - it('should resolveRelativePathWithinDir', async () => { const fsDriver = new FsDriverNode(); expect(fsDriver.resolveRelativePathWithinDir('/test/temp', './my/file.txt').toLowerCase()).toBe(platformPath('/test/temp/my/file.txt')); @@ -28,4 +28,18 @@ describe('fsDriver', () => { await expectThrow(() => fsDriver.resolveRelativePathWithinDir('/test/temp', '/var/local/no.txt')); }); + it('should compare reserved names in a case-insensitive way in findUniqueFilename', async () => { + // Compare with filenames in the reserved list should be case insensitive + expect( + await shim.fsDriver().findUniqueFilename( + join(supportDir, 'this-file-does-not-exist.txt'), + [join(supportDir, 'THIS-file-does-not-exist.txt'), join(supportDir, 'THIS-file-DOES-not-exist (1).txt')], + ), + ).toBe(join(supportDir, 'this-file-does-not-exist (2).txt')); + + // Should still not match reserved names that aren't equivalent. + expect( + await shim.fsDriver().findUniqueFilename(join(supportDir, 'this-file-does-not-exist.txt'), [join(supportDir, 'some-other-file.txt')]), + ).toBe(join(supportDir, 'this-file-does-not-exist.txt')); + }); });