diff --git a/cypress/e2e/attachments.spec.js b/cypress/e2e/attachments.spec.js index e19c5bc7b92..fdb794a0ac9 100644 --- a/cypress/e2e/attachments.spec.js +++ b/cypress/e2e/attachments.spec.js @@ -279,6 +279,49 @@ describe('Test all attachment insertion methods', () => { cy.closeFile() }) + it('Upload a local image file with RTLO character in name (RTLO is stripped)', () => { + const filename = randHash() + '.md' + cy.uploadFile('empty.md', 'text/markdown', filename) + cy.visit('/apps/files') + cy.openFile(filename) + + const requestAlias = 'uploadRTLORequest' + cy.intercept({ method: 'POST', url: '**/text/attachment/upload?**' }).as(requestAlias) + + clickOnAttachmentAction(ACTION_UPLOAD_LOCAL_FILE).then(() => { + cy.getEditor() + .find('input[type="file"][data-text-el="attachment-file-input"]') + .selectFile( + [ + { + contents: 'cypress/fixtures/github.png', + fileName: 'git\u202e.png', + }, + ], + { force: true }, + ) + + return cy + .wait('@' + requestAlias).then((req) => { + const fileName = req.response.body.name // server echoes back name with RTLO + const documentId = req.response.body.documentId + + // insertAttachment strips RTLO from the name before building the src URL and the + // alt text. The src URL no longer matches the on-disk filename (which still has + // the RTLO), so the image 404s. The alt text shows the clean name, exposing the + // real file extension instead of the visually spoofed one. + const strippedName = fileName.replaceAll('\u202e', '') + const encodedName = fixedEncodeURIComponent(strippedName) + + cy.getEditor() + .find('[data-component="image-view"]') + .should('have.length', 1) + .should('have.attr', 'data-src', `.attachments.${documentId}/${encodedName}`) + }) + }) + cy.closeFile() + }) + it('test if attachment files are in the attachment folder', () => { cy.visit('/apps/files') diff --git a/src/components/Editor/MediaHandler.vue b/src/components/Editor/MediaHandler.vue index 9b42cb66194..c1382188906 100644 --- a/src/components/Editor/MediaHandler.vue +++ b/src/components/Editor/MediaHandler.vue @@ -167,15 +167,16 @@ export default { }) }, insertAttachment(name, fileId, mimeType, position = null, dirname = '') { + const sanitizedName = name.replaceAll('\u202E', '') // inspired by the fixedEncodeURIComponent function suggested in // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent const src = dirname + '/' - + encodeURIComponent(name).replace(/[!'()*]/g, (c) => { + + encodeURIComponent(sanitizedName).replace(/[!'()*]/g, (c) => { return '%' + c.charCodeAt(0).toString(16).toUpperCase() }) // simply get rid of brackets to make sure link text is valid // as it does not need to be unique and matching the real file name - const alt = name.replaceAll(/[[\]]/g, '') + const alt = sanitizedName.replaceAll(/[[\]]/g, '') const chain = position ? this.$editor.chain().focus(position)