From 85334dda6cd3e75eedf05c6071af2223b12e0cb2 Mon Sep 17 00:00:00 2001 From: emrazik <81395258+emrazik@users.noreply.github.com> Date: Sun, 21 Jun 2026 14:23:06 -0700 Subject: [PATCH] Fix persistent XSS in data export via review messages Review messages were stored and re-emitted verbatim by the JSON export endpoint and then rendered into a new window via document.write(), allowing stored XSS payloads to execute in any user's browser on export. - routes/dataExport.ts: sanitize review.message with sanitizeSecure() before including it in the export payload - data-export.component.ts: replace document.write() with a safe Blob download so the JSON is saved as a file instead of rendered as HTML Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/app/data-export/data-export.component.ts | 10 +++++++++- routes/dataExport.ts | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/data-export/data-export.component.ts b/frontend/src/app/data-export/data-export.component.ts index fd230688268..a0ab0cb30d6 100644 --- a/frontend/src/app/data-export/data-export.component.ts +++ b/frontend/src/app/data-export/data-export.component.ts @@ -68,7 +68,15 @@ export class DataExportComponent implements OnInit { this.error = null this.confirmation = data.confirmation this.userData = data.userData - window.open('', '_blank', 'width=500')?.document.write(this.userData) + const blob = new Blob([this.userData], { type: 'application/json' }) + const url = URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = 'data-export.json' + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + URL.revokeObjectURL(url) this.lastSuccessfulTry = new Date() localStorage.setItem('lstdtxprt', JSON.stringify(this.lastSuccessfulTry)) this.ngOnInit() diff --git a/routes/dataExport.ts b/routes/dataExport.ts index ee3b4afcdf9..37dc7edad25 100644 --- a/routes/dataExport.ts +++ b/routes/dataExport.ts @@ -93,7 +93,7 @@ export function dataExport () { likesCount: number likedBy: string }) => ({ - message: review.message, + message: security.sanitizeSecure(review.message), author: review.author, productId: review.product, likesCount: review.likesCount,