diff --git a/package.json b/package.json index a1405edc96..ec2cc7cf42 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "@storybook/icons": "^1.2.9", "@swc/jest": "^0.2.29", "@tabler/icons-react": "^2.44.0", + "@types/dompurify": "^3.0.5", "@types/facepaint": "^1.2.5", "@types/lodash.camelcase": "^4.3.7", "@types/lodash.merge": "^4.6.7", @@ -80,6 +81,7 @@ "debounce": "^2.0.0", "deep-equal": "^2.2.2", "docusaurus-node-polyfills": "^1.0.0", + "dompurify": "^3.0.11", "dotenv-cli": "^7.2.1", "drizzle-orm": "^0.29.3", "esbuild-plugin-svgr": "^2.1.0", diff --git a/packages/twenty-server/src/engine/core-modules/file/file-upload/services/file-upload.service.ts b/packages/twenty-server/src/engine/core-modules/file/file-upload/services/file-upload.service.ts index f4fd71850a..7405aeb561 100644 --- a/packages/twenty-server/src/engine/core-modules/file/file-upload/services/file-upload.service.ts +++ b/packages/twenty-server/src/engine/core-modules/file/file-upload/services/file-upload.service.ts @@ -2,6 +2,8 @@ import { Injectable } from '@nestjs/common'; import sharp from 'sharp'; import { v4 as uuidV4 } from 'uuid'; +import { JSDOM } from 'jsdom'; +import DOMPurify from 'dompurify'; import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface'; @@ -32,6 +34,25 @@ export class FileUploadService { }); } + private _sanitizeFile({ + file, + ext, + mimeType, + }: { + file: Buffer | Uint8Array | string; + ext: string; + mimeType: string | undefined; + }): Buffer | Uint8Array | string { + if (ext === 'svg' || mimeType === 'image/svg+xml') { + const window = new JSDOM('').window; + const purify = DOMPurify(window); + + return purify.sanitize(file.toString()); + } + + return file; + } + async uploadFile({ file, filename, @@ -48,7 +69,7 @@ export class FileUploadService { const name = `${id}${ext ? `.${ext}` : ''}`; await this._uploadFile({ - file, + file: this._sanitizeFile({ file, ext, mimeType }), filename: name, mimeType, fileFolder, diff --git a/yarn.lock b/yarn.lock index 19f3f64fe2..03fa2cec31 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16068,6 +16068,15 @@ __metadata: languageName: node linkType: hard +"@types/dompurify@npm:^3.0.5": + version: 3.0.5 + resolution: "@types/dompurify@npm:3.0.5" + dependencies: + "@types/trusted-types": "npm:*" + checksum: a34dcc4498ca250815ccf9aecbe82df96ba5db247d0440cf266a876757d47c52519c240db3475e794d7deb0d6b1af23328e02879be368ad0e26b20c0f0865dba + languageName: node + linkType: hard + "@types/ejs@npm:^3.1.1": version: 3.1.5 resolution: "@types/ejs@npm:3.1.5" @@ -17258,6 +17267,13 @@ __metadata: languageName: node linkType: hard +"@types/trusted-types@npm:*": + version: 2.0.7 + resolution: "@types/trusted-types@npm:2.0.7" + checksum: 4c4855f10de7c6c135e0d32ce462419d8abbbc33713b31d294596c0cc34ae1fa6112a2f9da729c8f7a20707782b0d69da3b1f8df6645b0366d08825ca1522e0c + languageName: node + linkType: hard + "@types/type-is@npm:^1.6.3": version: 1.6.6 resolution: "@types/type-is@npm:1.6.6" @@ -24726,6 +24742,13 @@ __metadata: languageName: node linkType: hard +"dompurify@npm:^3.0.11": + version: 3.0.11 + resolution: "dompurify@npm:3.0.11" + checksum: 38740deed057da8076e85026853635312a6720a21430218a85875e5f43e453c78637d93aa08c744460b0e91c652b06c736efb481c408a8f9c81894d8f76e0de1 + languageName: node + linkType: hard + "domutils@npm:^1.5.1": version: 1.7.0 resolution: "domutils@npm:1.7.0" @@ -45948,6 +45971,7 @@ __metadata: "@types/better-sqlite3": "npm:^7.6.8" "@types/bytes": "npm:^3.1.1" "@types/deep-equal": "npm:^1.0.1" + "@types/dompurify": "npm:^3.0.5" "@types/express": "npm:^4.17.13" "@types/facepaint": "npm:^1.2.5" "@types/graphql-fields": "npm:^1.3.6" @@ -46010,6 +46034,7 @@ __metadata: debounce: "npm:^2.0.0" deep-equal: "npm:^2.2.2" docusaurus-node-polyfills: "npm:^1.0.0" + dompurify: "npm:^3.0.11" dotenv-cli: "npm:^7.2.1" drizzle-kit: "npm:^0.20.14" drizzle-orm: "npm:^0.29.3"