diff --git a/packages/app-cli/tests/enex_to_md/resource_filename_with_colons.enex b/packages/app-cli/tests/enex_to_md/resource_filename_with_colons.enex new file mode 100644 index 000000000..5ec08b20f --- /dev/null +++ b/packages/app-cli/tests/enex_to_md/resource_filename_with_colons.enex @@ -0,0 +1,35 @@ + + + + + ABOUT + 20200221T010525Z + 20230720T000138Z + recovery + San Luis Obispo + homelessness in SLO + Sunny Acres + + Michael B. Goldstein + web.clip + https://www.sunnyacres.community/about.html + + + + + ]]> + + + +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 + + image/png + 16 + 16 + + 08.06.2014 16:58:55 + en-cache://tokenKey%3D%22AuthToken%3AUser%3A33065190%22+542a52b5-e793-4e26-9839-c13552e5a75a+2fb6ad590ae59702499393cc35850cdf+https://www.evernote.com/shard/s254/res/6da89c4b-e2d2-41a5-afe7-df2c15839e6f + + + + diff --git a/packages/lib/import-enex-md-gen.test.ts b/packages/lib/import-enex-md-gen.test.ts index 228e844b2..c90a3f94c 100644 --- a/packages/lib/import-enex-md-gen.test.ts +++ b/packages/lib/import-enex-md-gen.test.ts @@ -206,4 +206,12 @@ describe('import-enex-md-gen', () => { expect(resource.title).toBe('app_images/resizable/961b875f-24ac-402f-9b76-37e2d4f03a6c/house_500.jpg.png'); }); + it('should sanitize resource filenames with colons', async () => { + await importEnexFile('resource_filename_with_colons.enex'); + const resource: ResourceEntity = (await Resource.all())[0]; + expect(resource.filename).toBe('08.06.2014165855'); + expect(resource.file_extension).toBe('2014165855'); + expect(resource.title).toBe('08.06.2014 16:58:55'); + }); + }); diff --git a/packages/lib/import-enex.ts b/packages/lib/import-enex.ts index bd04eec79..19110c935 100644 --- a/packages/lib/import-enex.ts +++ b/packages/lib/import-enex.ts @@ -9,7 +9,7 @@ import shim from './shim'; import { NoteEntity, ResourceEntity } from './services/database/types'; import { enexXmlToMd } from './import-enex-md-gen'; import { MarkupToHtml } from '@joplin/renderer'; -import { fileExtension, friendlySafeFilename } from './path-utils'; +import { fileExtension, friendlySafeFilename, safeFileExtension } from './path-utils'; const moment = require('moment'); const { wrapError } = require('./errorUtils'); const { enexXmlToHtml } = require('./import-enex-html-gen.js'); @@ -208,7 +208,7 @@ async function saveNoteResources(note: ExtractedNote) { delete (toSave as any).dataFilePath; delete (toSave as any).dataEncoding; delete (toSave as any).hasData; - toSave.file_extension = resource.filename ? fileExtension(resource.filename) : ''; + toSave.file_extension = resource.filename ? safeFileExtension(fileExtension(resource.filename)) : ''; // ENEX resource filenames can contain slashes, which may confuse other // parts of the app, which expect this `filename` field to be safe. diff --git a/packages/lib/path-utils.ts b/packages/lib/path-utils.ts index f83600d8e..f37ec6162 100644 --- a/packages/lib/path-utils.ts +++ b/packages/lib/path-utils.ts @@ -39,6 +39,9 @@ export function isHidden(path: string) { return b[0] === '.'; } +// Note that this function only sanitizes a file extension - it does NOT extract +// the file extension from a filename. So the way you'd normally call this is +// `safeFileExtension(fileExtension(filename))` export function safeFileExtension(e: string, maxLength: number = null) { // In theory the file extension can have any length but in practice Joplin // expects a fixed length, so we limit it to 20 which should cover most cases.