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>
This commit is contained in:
@@ -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 =>
|
||||
`<button type="button" class="folder-item w-full text-left px-4 py-3 hover:bg-indigo-50 border-b border-gray-100 flex items-center gap-3 text-sm focus:outline-none focus:bg-indigo-50" data-path="${f.path}" aria-label="Open folder ${f.name}">` +
|
||||
`<button type="button" class="folder-item w-full text-left px-4 py-3 hover:bg-indigo-50 border-b border-gray-100 flex items-center gap-3 text-sm focus:outline-none focus:bg-indigo-50" data-path="${escapeHtml(f.path)}" aria-label="Open folder ${escapeHtml(f.name)}">` +
|
||||
`<svg class="h-5 w-5 text-yellow-400 flex-shrink-0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path fill-rule="evenodd" d="M2 6a2 2 0 012-2h4l2 2h4a2 2 0 012 2v1H8a3 3 0 00-3 3v1.5a1.5 1.5 0 01-3 0V6z" clip-rule="evenodd" /><path d="M6 12a2 2 0 012-2h8a2 2 0 012 2v2a2 2 0 01-2 2H2h2a2 2 0 002-2v-2z" /></svg>` +
|
||||
`<span class="font-medium text-gray-700">${f.name}</span>` +
|
||||
`<span class="font-medium text-gray-700">${escapeHtml(f.name)}</span>` +
|
||||
`</button>`
|
||||
).join('');
|
||||
|
||||
@@ -363,7 +369,7 @@ DROPBOX_FOLDER=${folderPath || '/Documents/Uploads'}`;
|
||||
updateBreadcrumb(path);
|
||||
})
|
||||
.catch(err => {
|
||||
folderList.innerHTML = `<div class="p-4 text-center text-red-500 text-sm">Failed to load folders: ${err.message}</div>`;
|
||||
folderList.innerHTML = `<div class="p-4 text-center text-red-500 text-sm">Failed to load folders: ${escapeHtml(err.message)}</div>`;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -374,7 +380,7 @@ DROPBOX_FOLDER=${folderPath || '/Documents/Uploads'}`;
|
||||
for (const part of parts) {
|
||||
accumulated += '/' + part;
|
||||
html += `<span class="mx-1 text-gray-400">/</span>`;
|
||||
html += `<button type="button" class="folder-nav-btn text-indigo-600 hover:text-indigo-800 font-medium" data-path="${accumulated}" aria-label="Navigate to ${part}">${part}</button>`;
|
||||
html += `<button type="button" class="folder-nav-btn text-indigo-600 hover:text-indigo-800 font-medium" data-path="${escapeHtml(accumulated)}" aria-label="Navigate to ${escapeHtml(part)}">${escapeHtml(part)}</button>`;
|
||||
}
|
||||
breadcrumb.innerHTML = html;
|
||||
breadcrumb.querySelectorAll('.folder-nav-btn').forEach(btn => {
|
||||
|
||||
Reference in New Issue
Block a user