diff --git a/e2e/src/api/specs/shared-link.e2e-spec.ts b/e2e/src/api/specs/shared-link.e2e-spec.ts index 3918429e4e..154f190f53 100644 --- a/e2e/src/api/specs/shared-link.e2e-spec.ts +++ b/e2e/src/api/specs/shared-link.e2e-spec.ts @@ -95,7 +95,7 @@ describe('/shared-links', () => { expect(resp.status).toBe(200); expect(resp.header['content-type']).toContain('text/html'); expect(resp.text).toContain( - ``, + ``, ); }); @@ -103,14 +103,14 @@ describe('/shared-links', () => { const resp = await request(shareUrl).get(`/${linkWithAlbum.key}`); expect(resp.status).toBe(200); expect(resp.header['content-type']).toContain('text/html'); - expect(resp.text).toContain(``); + expect(resp.text).toContain(``); }); it('should have correct asset count in meta tag for shared asset', async () => { const resp = await request(shareUrl).get(`/${linkWithAssets.key}`); expect(resp.status).toBe(200); expect(resp.header['content-type']).toContain('text/html'); - expect(resp.text).toContain(``); + expect(resp.text).toContain(``); }); it('should have fqdn og:image meta tag for shared asset', async () => { diff --git a/server/package-lock.json b/server/package-lock.json index 8a2c0c9f25..80de8b37ff 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -59,6 +59,7 @@ "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1", "sanitize-filename": "^1.6.3", + "sanitize-html": "^2.14.0", "semver": "^7.6.2", "sharp": "^0.33.0", "sirv": "^3.0.0", @@ -91,6 +92,7 @@ "@types/picomatch": "^3.0.0", "@types/pngjs": "^6.0.5", "@types/react": "^19.0.0", + "@types/sanitize-html": "^2.13.0", "@types/semver": "^7.5.8", "@types/supertest": "^6.0.0", "@types/ua-parser-js": "^0.7.36", @@ -6042,6 +6044,16 @@ "@types/node": "*" } }, + "node_modules/@types/sanitize-html": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.13.0.tgz", + "integrity": "sha512-X31WxbvW9TjIhZZNyNBZ/p5ax4ti7qsNDBDEnH4zAgmEh35YnFD1UiS6z9Cd34kKm0LslFW0KPmTQzu/oGtsqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "htmlparser2": "^8.0.0" + } + }, "node_modules/@types/semver": { "version": "7.5.8", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", @@ -8931,7 +8943,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -10747,6 +10758,15 @@ "node": ">=0.12.0" } }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", @@ -12394,6 +12414,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-srcset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", + "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==", + "license": "MIT" + }, "node_modules/parse5": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", @@ -14072,6 +14098,20 @@ "truncate-utf8-bytes": "^1.0.0" } }, + "node_modules/sanitize-html": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.14.0.tgz", + "integrity": "sha512-CafX+IUPxZshXqqRaG9ZClSlfPVjSxI0td7n07hk8QO2oO+9JDnlcL8iM8TWeOXOIBFgIOx6zioTzM53AOMn3g==", + "license": "MIT", + "dependencies": { + "deepmerge": "^4.2.2", + "escape-string-regexp": "^4.0.0", + "htmlparser2": "^8.0.0", + "is-plain-object": "^5.0.0", + "parse-srcset": "^1.0.2", + "postcss": "^8.3.11" + } + }, "node_modules/scheduler": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", diff --git a/server/package.json b/server/package.json index 06c23f3a1e..2d3356bb2c 100644 --- a/server/package.json +++ b/server/package.json @@ -85,6 +85,7 @@ "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1", "sanitize-filename": "^1.6.3", + "sanitize-html": "^2.14.0", "semver": "^7.6.2", "sharp": "^0.33.0", "sirv": "^3.0.0", @@ -117,6 +118,7 @@ "@types/picomatch": "^3.0.0", "@types/pngjs": "^6.0.5", "@types/react": "^19.0.0", + "@types/sanitize-html": "^2.13.0", "@types/semver": "^7.5.8", "@types/supertest": "^6.0.0", "@types/ua-parser-js": "^0.7.36", diff --git a/server/src/services/api.service.ts b/server/src/services/api.service.ts index a18f863f99..71fb36b4f2 100644 --- a/server/src/services/api.service.ts +++ b/server/src/services/api.service.ts @@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common'; import { Cron, CronExpression, Interval } from '@nestjs/schedule'; import { NextFunction, Request, Response } from 'express'; import { readFileSync } from 'node:fs'; +import sanitizeHtml from 'sanitize-html'; import { ONE_HOUR } from 'src/constants'; import { ConfigRepository } from 'src/repositories/config.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; @@ -12,21 +13,25 @@ import { VersionService } from 'src/services/version.service'; import { OpenGraphTags } from 'src/utils/misc'; const render = (index: string, meta: OpenGraphTags) => { + const [title, description, imageUrl] = [meta.title, meta.description, meta.imageUrl].map((item) => + item ? sanitizeHtml(item, { allowedTags: [] }) : '', + ); + const tags = ` - + - - - ${meta.imageUrl ? `` : ''} + + + ${imageUrl ? `` : ''} - - + + - ${meta.imageUrl ? `` : ''}`; + ${imageUrl ? `` : ''}`; return index.replace('', tags); };