From b3a238744d1618b7f891b7c43f243dd9099ecf49 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 13:46:08 +0000 Subject: [PATCH] fix(security): escape HTML in folder browser to prevent XSS from folder names Added escapeHtml() utility function to both Dropbox and OneDrive callback pages. Folder names, paths, and error messages inserted into innerHTML via template literals are now escaped to prevent potential cross-site scripting from malicious folder names. Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> --- frontend/templates/dropbox_callback.html | 14 ++++++++++---- frontend/templates/onedrive_callback.html | 14 ++++++++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/frontend/templates/dropbox_callback.html b/frontend/templates/dropbox_callback.html index e59c6e40..5b07f0b8 100644 --- a/frontend/templates/dropbox_callback.html +++ b/frontend/templates/dropbox_callback.html @@ -318,6 +318,12 @@ DROPBOX_FOLDER=${folderPath || '/Documents/Uploads'}`; } // ── Folder browser ──────────────────────────────────────────────── + function escapeHtml(str) { + const div = document.createElement('div'); + div.appendChild(document.createTextNode(str)); + return div.innerHTML; + } + function initFolderBrowser(accessToken, integrationId) { const folderSelector = document.getElementById('folder-selector'); if (!folderSelector || !integrationId) return; @@ -344,9 +350,9 @@ DROPBOX_FOLDER=${folderPath || '/Documents/Uploads'}`; .then(data => { if (data.folders && data.folders.length > 0) { folderList.innerHTML = data.folders.map(f => - `` ).join(''); @@ -363,7 +369,7 @@ DROPBOX_FOLDER=${folderPath || '/Documents/Uploads'}`; updateBreadcrumb(path); }) .catch(err => { - folderList.innerHTML = `
Failed to load folders: ${err.message}
`; + folderList.innerHTML = `
Failed to load folders: ${escapeHtml(err.message)}
`; }); } @@ -374,7 +380,7 @@ DROPBOX_FOLDER=${folderPath || '/Documents/Uploads'}`; for (const part of parts) { accumulated += '/' + part; html += `/`; - html += ``; + html += ``; } breadcrumb.innerHTML = html; breadcrumb.querySelectorAll('.folder-nav-btn').forEach(btn => { diff --git a/frontend/templates/onedrive_callback.html b/frontend/templates/onedrive_callback.html index 306f0ec4..72b98cd7 100644 --- a/frontend/templates/onedrive_callback.html +++ b/frontend/templates/onedrive_callback.html @@ -330,6 +330,12 @@ ONEDRIVE_FOLDER_PATH=${folderPath || 'Documents/Uploads'}`; } // ── Folder browser ──────────────────────────────────────────────── + function escapeHtml(str) { + const div = document.createElement('div'); + div.appendChild(document.createTextNode(str)); + return div.innerHTML; + } + function initFolderBrowser(accessToken, integrationId) { const folderSelector = document.getElementById('folder-selector'); if (!folderSelector || !integrationId || !accessToken) return; @@ -354,9 +360,9 @@ ONEDRIVE_FOLDER_PATH=${folderPath || 'Documents/Uploads'}`; .then(data => { if (data.folders && data.folders.length > 0) { folderList.innerHTML = data.folders.map(f => - `` ).join(''); @@ -373,7 +379,7 @@ ONEDRIVE_FOLDER_PATH=${folderPath || 'Documents/Uploads'}`; updateBreadcrumb(path); }) .catch(err => { - folderList.innerHTML = `
Failed to load folders: ${err.message}
`; + folderList.innerHTML = `
Failed to load folders: ${escapeHtml(err.message)}
`; }); } @@ -384,7 +390,7 @@ ONEDRIVE_FOLDER_PATH=${folderPath || 'Documents/Uploads'}`; for (const part of parts) { accumulated += '/' + part; html += `/`; - html += ``; + html += ``; } breadcrumb.innerHTML = html; breadcrumb.querySelectorAll('.folder-nav-btn').forEach(btn => {