Skip to content

Commit 7651752

Browse files
authored
fix: XSS and duplicate file detection in file upload UI (#2129)
Signed-off-by: tdruez <tdruez@aboutcode.org>
1 parent 4b26245 commit 7651752

1 file changed

Lines changed: 34 additions & 24 deletions

File tree

scancodeio/static/js/add_inputs.js

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ fileInput.onchange = updateFiles;
2828
function updateFiles() {
2929
if (fileInput.files.length > 0) {
3030
const fileName = document.querySelector("#inputs_file_name");
31-
fileName.innerHTML = "";
31+
fileName.replaceChildren();
3232

3333
// Update the selectedFiles array
3434
const newFiles = Array.from(fileInput.files);
@@ -40,22 +40,40 @@ function updateFiles() {
4040
selectedFiles = selectedFiles.concat(filteredNewFiles);
4141

4242
for (let file of selectedFiles) {
43-
const fileNameWithoutSpaces = file.name.replace(/\s/g, '');
44-
fileName.innerHTML += `
45-
<span class="is-flex is-justify-content-space-between is-block" id="file-name-${fileNameWithoutSpaces}">
46-
<span class="is-block">${file.name}</span>
47-
<a href="#" onclick="removeFile('${fileNameWithoutSpaces}')" class="model-button" id="file-delete-btn-${fileNameWithoutSpaces}">
48-
<i class="fa-solid fa-trash-can"></i>
49-
</a>
50-
</span>
51-
`;
52-
document.getElementById("file-delete-btn-"+ fileNameWithoutSpaces).addEventListener("click", function(event){
43+
const fileNameWithoutSpaces = file.name.replace(/\s/g, "");
44+
45+
// Build the wrapper span
46+
const wrapper = document.createElement("span");
47+
wrapper.className = "is-flex is-justify-content-space-between is-block";
48+
wrapper.id = `file-name-${fileNameWithoutSpaces}`;
49+
50+
// File name label - textContent is safe, no HTML injection possible
51+
const label = document.createElement("span");
52+
label.className = "is-block";
53+
label.textContent = file.name;
54+
55+
// Delete button
56+
const deleteLink = document.createElement("a");
57+
deleteLink.href = "#";
58+
deleteLink.className = "model-button";
59+
deleteLink.id = `file-delete-btn-${fileNameWithoutSpaces}`;
60+
deleteLink.addEventListener("click", function(event) {
5361
disableEvent(event);
5462
removeFile(fileNameWithoutSpaces);
55-
if(selectedFiles.length == 0){
56-
fileName.innerHTML ="<i>No files selected</i>"
63+
if (selectedFiles.length == 0) {
64+
const emptyNotice = document.createElement("i");
65+
emptyNotice.textContent = "No files selected";
66+
fileName.replaceChildren(emptyNotice);
5767
}
5868
});
69+
70+
const icon = document.createElement("i");
71+
icon.className = "fa-solid fa-trash-can";
72+
73+
deleteLink.appendChild(icon);
74+
wrapper.appendChild(label);
75+
wrapper.appendChild(deleteLink);
76+
fileName.appendChild(wrapper);
5977
}
6078
}
6179
}
@@ -68,7 +86,7 @@ function disableEvent(event) {
6886

6987
function removeFile(fileName) {
7088
selectedFiles = selectedFiles.filter(file => {
71-
const fileNameWithoutSpaces = file.name.replace(/\s/g, '');
89+
const fileNameWithoutSpaces = file.name.replace(/\s/g, "");
7290
return fileNameWithoutSpaces !== fileName;
7391
});
7492

@@ -87,18 +105,10 @@ function removeFile(fileName) {
87105

88106
function dropHandler(event) {
89107
disableEvent(event);
90-
const droppedFiles = event.dataTransfer.files;
91-
const updatedFilesSet = new Set(Array.from(fileInput.files));
92-
93-
for (let file of droppedFiles) {
94-
updatedFilesSet.add(file);
95-
}
96-
97-
// Convert the Set back to an array if needed
98-
const updatedFiles = Array.from(updatedFilesSet);
99108

109+
// Merge existing files and dropped files, let updateFiles handle dedup
100110
const dataTransfer = new DataTransfer();
101-
for (let file of updatedFiles) {
111+
for (const file of [...fileInput.files, ...event.dataTransfer.files]) {
102112
dataTransfer.items.add(file);
103113
}
104114

0 commit comments

Comments
 (0)