🐛 Fixed LinkReplacer bug causing broken links on published post/page (#16514)

refs TryGhost/Team#2840

- moves the `entities.decode()` step to the `LinkReplacer` class so that
it's applied to all links, not just the ones that are replaced in the
email service
- adds a test case to `LinkReplacer` to ensure that the
`entities.decode()` step is applied to all links correctly, decoding any
URLs with HTML entities in them

---------

Co-authored-by: Chris Raible <chris@ghost.org>
This commit is contained in:
Rishabh Garg 2023-03-28 15:59:15 +05:30 committed by Rishabh
parent 234853aebf
commit 0fb7abae87
6 changed files with 16 additions and 11 deletions

View File

@ -8,7 +8,6 @@ const {textColorForBackgroundColor, darkenToContrastThreshold} = require('@trygh
const {DateTime} = require('luxon');
const htmlToPlaintext = require('@tryghost/html-to-plaintext');
const tpl = require('@tryghost/tpl');
const entities = require('entities');
const messages = {
subscriptionStatus: {
@ -293,8 +292,6 @@ class EmailRenderer {
// Link tracking
if (options.clickTrackingEnabled) {
html = await this.#linkReplacer.replace(html, async (url) => {
// Decode any escaped entities in the url
url = new URL(entities.decode(url.toString()));
// We ignore all links that contain %%{uuid}%%
// because otherwise we would add tracking to links that need to be replaced first
if (url.toString().indexOf('%%{uuid}%%') !== -1) {

View File

@ -35,7 +35,6 @@
"@tryghost/validator": "^0.2.0",
"bson-objectid": "2.0.4",
"cheerio": "0.22.0",
"entities": "4.4.0",
"handlebars": "4.7.7",
"juice": "8.1.0",
"moment-timezone": "0.5.23"

View File

@ -7,6 +7,7 @@ class LinkReplacer {
*/
async replace(html, replaceLink) {
const cheerio = require('cheerio');
const entities = require('entities');
try {
const $ = cheerio.load(html, {
xml: {
@ -22,7 +23,7 @@ class LinkReplacer {
if (href) {
let url;
try {
url = new URL(href);
url = new URL(entities.decode(href));
} catch (e) {
// Ignore invalid URLs
}

View File

@ -24,6 +24,7 @@
"sinon": "15.0.2"
},
"dependencies": {
"cheerio": "0.22.0"
"cheerio": "0.22.0",
"entities": "^4.4.0"
}
}

View File

@ -36,6 +36,13 @@ describe('LinkReplacementService', function () {
assert.equal(replaced, html);
});
it('Does escape HTML characters within a link\'s href', async function () {
const html = '<a href="https://www.google.com/maps/d/u/0/viewer?mid&#x3D;1kQUV2O5QQOigaxJUorLUC9LBP4Ibppg&amp;ll&#x3D;37.87151888616819%2C-122.27759691003418&amp;z&#x3D;13">link</a>';
const expected = '<a href="https://www.google.com/maps/d/u/0/viewer?mid=1kQUV2O5QQOigaxJUorLUC9LBP4Ibppg&ll=37.87151888616819%2C-122.27759691003418&z=13">link</a>';
const replaced = await linkReplacer.replace(html, url => new URL(url));
assert.equal(replaced, expected);
});
it('Can replace to string', async function () {
const html = '<a href="http://localhost:2368/dir/path">link</a>';
const expected = '<a href="#valid-string">link</a>';

View File

@ -13838,11 +13838,6 @@ ensure-posix-path@^1.0.0, ensure-posix-path@^1.0.1, ensure-posix-path@^1.0.2, en
resolved "https://registry.yarnpkg.com/ensure-posix-path/-/ensure-posix-path-1.1.1.tgz#3c62bdb19fa4681544289edb2b382adc029179ce"
integrity sha512-VWU0/zXzVbeJNXvME/5EmLuEj2TauvoaTz6aFYK1Z92JCBlDlZ3Gu0tuGR42kpW1754ywTs+QB0g5TP0oj9Zaw==
entities@4.4.0, entities@^4.2.0, entities@^4.3.0, entities@^4.4.0, entities@~4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174"
integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==
entities@^1.1.1, entities@~1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
@ -13853,6 +13848,11 @@ entities@^2.0.0:
resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
entities@^4.2.0, entities@^4.3.0, entities@^4.4.0, entities@~4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174"
integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==
entities@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"