From db7bd821589f2f31f118529447a98f5dc11e9bc9 Mon Sep 17 00:00:00 2001 From: Olzhas Askar Date: Mon, 22 Sep 2025 16:00:52 +0200 Subject: [PATCH] Merge pull request #28596 from overleaf/oa-possessive-s [web] Unescaping possessive s and ampersand for Email subjects GitOrigin-RevId: 8ba0fd509c8b6bd2a3eec13adf10f13b0bdb6aca --- .../app/src/Features/Email/EmailBuilder.js | 4 +-- .../test/unit/src/Email/EmailBuilderTests.js | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/services/web/app/src/Features/Email/EmailBuilder.js b/services/web/app/src/Features/Email/EmailBuilder.js index 4741838b15..f1d52220fb 100644 --- a/services/web/app/src/Features/Email/EmailBuilder.js +++ b/services/web/app/src/Features/Email/EmailBuilder.js @@ -280,9 +280,7 @@ templates.projectInvite = ctaTemplate({ const safeEmail = SpamSafe.isSafeEmail(opts.owner.email) if (safeName && safeEmail) { - return `"${_.escape(opts.project.name)}" — shared by ${_.escape( - opts.owner.email - )}` + return `"${opts.project.name}" — shared by ${_.escape(opts.owner.email)}` } if (safeName) { return `${settings.appName} project shared with you — "${_.escape( diff --git a/services/web/test/unit/src/Email/EmailBuilderTests.js b/services/web/test/unit/src/Email/EmailBuilderTests.js index 8cc83f0228..11380012ad 100644 --- a/services/web/test/unit/src/Email/EmailBuilderTests.js +++ b/services/web/test/unit/src/Email/EmailBuilderTests.js @@ -62,6 +62,31 @@ describe('EmailBuilder', function () { }) }) + describe('when dealing with escaping', function () { + it("should not show possessive 's as '", function () { + this.opts.project.name = "Aktöbe's project" + this.email = this.EmailBuilder.buildEmail('projectInvite', this.opts) + expect(this.email.subject).to.not.contain(''') + expect(this.email.subject).to.contain(this.opts.project.name) + }) + + it('should not show an ampersand as &', function () { + this.opts.project.name = 'Aktöbe & Almaty project' + this.email = this.EmailBuilder.buildEmail('projectInvite', this.opts) + expect(this.email.subject).to.not.contain('&') + expect(this.email.subject).to.contain(this.opts.project.name) + }) + + it('should prevent dangerous characters as project names', function () { + const characters = ['""', '<>', '//'] + for (const pair of characters) { + this.opts.project.name = `${pair} project` + this.email = this.EmailBuilder.buildEmail('projectInvite', this.opts) + expect(this.email.subject).to.not.contain(pair) + } + }) + }) + describe('when someone is up to no good', function () { it('should not contain the project name at all if unsafe', function () { this.opts.project.name = ""