VYPR
Moderate severityNVD Advisory· Published Jun 2, 2025· Updated Jun 2, 2025

Gokapi has stored XSS vulnerability in friendly name for API keys

CVE-2025-48495

Description

Gokapi is a self-hosted file sharing server with automatic expiration and encryption support. By renaming the friendly name of an API key, an authenticated user could inject JS into the API key overview, which would also be executed when another user clicks on his API tab. Prior to version 2.0.0, there was no user permission system implemented, therefore all authenticated users were already able to see and modify all resources, even if end-to-end encrypted, as the encryption key had to be the same for all users of versions prior to 2.0.0. If a user is the only authenticated user using Gokapi, they are not affected. This issue has been fixed in v2.0.0. A workaround would be to not open the API page if it is possible that another user might have injected code.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/forceu/gokapiGo
>= 1.0.1, <= 1.9.6
github.com/forceu/gokapiGo
< 0.0.0-20250530185940-65ddbc68fbfd0.0.0-20250530185940-65ddbc68fbfd

Affected products

1

Patches

1
65ddbc68fbfd

Fix CVE-2025-48495 XSS vulnerability for API key name, refactoring and formatting

https://github.com/Forceu/GokapiMarc BullingMay 30, 2025via ghsa
2 files changed · +121 42
  • internal/webserver/web/static/js/admin_ui_api.js+120 33 modified
    @@ -47,10 +47,10 @@ function deleteApiKey(apiKey) {
     
         apiAuthDelete(apiKey)
             .then(data => {
    -        document.getElementById("row-" + apiKey).classList.add("rowDeleting");
    -        setTimeout(() => {
    -            document.getElementById("row-" + apiKey).remove();
    -    }, 290);
    +            document.getElementById("row-" + apiKey).classList.add("rowDeleting");
    +            setTimeout(() => {
    +                document.getElementById("row-" + apiKey).remove();
    +            }, 290);
             })
             .catch(error => {
                 alert("Unable to delete API key: " + error);
    @@ -81,7 +81,7 @@ function addFriendlyNameChange(apiKey) {
         if (cell.classList.contains("isBeingEdited"))
             return;
         cell.classList.add("isBeingEdited");
    -    let currentName = cell.innerHTML;
    +    let currentName = cell.innerText;
         let input = document.createElement("input");
         input.size = 5;
         input.value = currentName;
    @@ -93,9 +93,9 @@ function addFriendlyNameChange(apiKey) {
             allowEdit = false;
             let newName = input.value;
             if (newName == "") {
    -        	newName = "Unnamed key";
    +            newName = "Unnamed key";
             }
    -        cell.innerHTML = newName;
    +        cell.innerText = newName;
     
             cell.classList.remove("isBeingEdited");
     
    @@ -114,7 +114,7 @@ function addFriendlyNameChange(apiKey) {
                 submitEntry();
             }
         });
    -    cell.innerHTML = "";
    +    cell.innerText = "";
         cell.appendChild(input);
         input.focus();
     }
    @@ -126,7 +126,7 @@ function addRowApi(apiKey, publicId) {
     
         let table = document.getElementById("apitable");
         let row = table.insertRow(0);
    -    
    +
         row.id = "row-" + publicId;
         let cellCount = 0;
         let cellFriendlyName = row.insertCell(cellCount++);
    @@ -135,13 +135,13 @@ function addRowApi(apiKey, publicId) {
         let cellPermissions = row.insertCell(cellCount++);
         let cellUserName;
         if (canViewOtherApiKeys) {
    -    	cellUserName= row.insertCell(cellCount++);
    -    	}
    -    let cellButtons= row.insertCell(cellCount++);
    -    
    +        cellUserName = row.insertCell(cellCount++);
    +    }
    +    let cellButtons = row.insertCell(cellCount++);
    +
         if (canViewOtherApiKeys) {
    -    cellUserName.classList.add("newApiKey");
    -    cellUserName.innerText = userName;
    +        cellUserName.classList.add("newApiKey");
    +        cellUserName.innerText = userName;
         }
     
         cellFriendlyName.classList.add("newApiKey");
    @@ -157,28 +157,115 @@ function addRowApi(apiKey, publicId) {
         cellFriendlyName.onclick = function() {
             addFriendlyNameChange(publicId);
         };
    -    cellId.innerHTML = '<div class="font-monospace">'+apiKey+'</div>';
    +    cellId.innerText = apiKey;
    +    cellId.classList.add("font-monospace");
         cellLastUsed.innerText = "Never";
    -    cellButtons.innerHTML = '<button type="button" data-clipboard-text="' + apiKey + '"  onclick="showToast(1000)" title="Copy API Key" class="copyurl btn btn-outline-light btn-sm"><i class="bi bi-copy"></i></button> <button id="delete-' + publicId + '" type="button" class="btn btn-outline-danger btn-sm" onclick="deleteApiKey(\'' + publicId + '\')" title="Delete"><i class="bi bi-trash3"></i></button>';
    -    cellPermissions.innerHTML = `
    -	    	<i id="perm_view_` + publicId + `" class="bi bi-eye perm-granted" title="List Uploads" onclick='changeApiPermission("` + publicId + `","PERM_VIEW", "perm_view_` + publicId + `");'></i>
    -	    	<i id="perm_upload_` + publicId + `" class="bi bi-file-earmark-arrow-up perm-granted" title="Upload" onclick='changeApiPermission("` + publicId + `","PERM_UPLOAD", "perm_upload_` + publicId + `");'></i>
    -	    	<i id="perm_edit_` + publicId + `" class="bi bi-pencil perm-granted" title="Edit Uploads" onclick='changeApiPermission("` + publicId + `","PERM_EDIT", "perm_edit_` + publicId + `");'></i>
    -	    	<i id="perm_delete_` + publicId + `" class="bi bi-trash3 perm-granted" title="Delete Uploads" onclick='changeApiPermission("` + publicId + `","PERM_DELETE", "perm_delete_` + publicId + `");'></i>
    -	    	<i id="perm_replace_` + publicId + `" class="bi bi-recycle perm-notgranted" title="Replace Uploads" onclick='changeApiPermission("` + publicId + `","PERM_REPLACE", "perm_replace_` + publicId + `");'></i>
    -	    	<i id="perm_users_` + publicId + `" class="bi bi-people perm-notgranted" title="Manage Users" onclick='changeApiPermission("` + publicId + `", "PERM_MANAGE_USERS", "perm_users_` + publicId + `");'></i>
    -	    	<i id="perm_logs_` + publicId + `" class="bi bi-card-list perm-notgranted" title="Manage System Logs" onclick='changeApiPermission("` + publicId + `", "PERM_MANAGE_LOGS", "perm_logs_` + publicId + `");'></i>
    -	    	<i id="perm_api_` + publicId + `" class="bi bi-sliders2 perm-notgranted" title="Manage API Keys" onclick='changeApiPermission("` + publicId + `","PERM_API_MOD", "perm_api_` + publicId + `");'></i>`;
    -   
    +
    +
    +    // === Buttons Cell ===
    +    const copyButton = document.createElement('button');
    +    copyButton.type = 'button';
    +    copyButton.dataset.clipboardText = apiKey;
    +    copyButton.title = 'Copy API Key';
    +    copyButton.className = 'copyurl btn btn-outline-light btn-sm';
    +    copyButton.setAttribute('onclick', 'showToast(1000)');
    +
    +    const copyIcon = document.createElement('i');
    +    copyIcon.className = 'bi bi-copy';
    +    copyButton.appendChild(copyIcon);
    +
    +    const deleteButton = document.createElement('button');
    +    deleteButton.type = 'button';
    +    deleteButton.id = `delete-${publicId}`;
    +    deleteButton.title = 'Delete';
    +    deleteButton.className = 'btn btn-outline-danger btn-sm';
    +    deleteButton.setAttribute('onclick', `deleteApiKey('${publicId}')`);
    +
    +    const deleteIcon = document.createElement('i');
    +    deleteIcon.className = 'bi bi-trash3';
    +    deleteButton.appendChild(deleteIcon);
    +
    +    cellButtons.appendChild(copyButton);
    +    cellButtons.appendChild(document.createTextNode(' ')); // space between buttons
    +    cellButtons.appendChild(deleteButton);
    +
    +    // === Permissions Cell ===
    +    const perms = [{
    +            perm: 'PERM_VIEW',
    +            icon: 'bi-eye',
    +            granted: true,
    +            title: 'List Uploads'
    +        },
    +        {
    +            perm: 'PERM_UPLOAD',
    +            icon: 'bi-file-earmark-arrow-up',
    +            granted: true,
    +            title: 'Upload'
    +        },
    +        {
    +            perm: 'PERM_EDIT',
    +            icon: 'bi-pencil',
    +            granted: true,
    +            title: 'Edit Uploads'
    +        },
    +        {
    +            perm: 'PERM_DELETE',
    +            icon: 'bi-trash3',
    +            granted: true,
    +            title: 'Delete Uploads'
    +        },
    +        {
    +            perm: 'PERM_REPLACE',
    +            icon: 'bi-recycle',
    +            granted: false,
    +            title: 'Replace Uploads'
    +        },
    +        {
    +            perm: 'PERM_MANAGE_USERS',
    +            icon: 'bi-people',
    +            granted: false,
    +            title: 'Manage Users'
    +        },
    +        {
    +            perm: 'PERM_MANAGE_LOGS',
    +            icon: 'bi-card-list',
    +            granted: false,
    +            title: 'Manage System Logs'
    +        },
    +        {
    +            perm: 'PERM_API_MOD',
    +            icon: 'bi-sliders2',
    +            granted: false,
    +            title: 'Manage API Keys'
    +        }
    +    ];
    +
    +    perms.forEach(({
    +        perm,
    +        icon,
    +        granted,
    +        title
    +    }) => {
    +        const i = document.createElement('i');
    +        const id = `perm_${perm.toLowerCase().replace('perm_', '')}_${publicId}`;
    +        i.id = id;
    +        i.className = `bi ${icon} ${granted ? 'perm-granted' : 'perm-notgranted'}`;
    +        i.title = title;
    +        i.setAttribute('onclick', `changeApiPermission("${publicId}","${perm}", "${id}");`);
    +        cellPermissions.appendChild(i);
    +        cellPermissions.appendChild(document.createTextNode(' '));
    +    });
    +
    +
         if (!canReplaceFiles) {
    -    	let cell = document.getElementById("perm_replace_"+publicId);
    -    	cell.classList.add("perm-unavailable");
    -    	cell.classList.add("perm-nochange");
    +        let cell = document.getElementById("perm_replace_" + publicId);
    +        cell.classList.add("perm-unavailable");
    +        cell.classList.add("perm-nochange");
         }
         if (!canManageUsers) {
    -    	let cell = document.getElementById("perm_users_"+publicId);
    -    	cell.classList.add("perm-unavailable");
    -    	cell.classList.add("perm-nochange");
    +        let cell = document.getElementById("perm_users_" + publicId);
    +        cell.classList.add("perm-unavailable");
    +        cell.classList.add("perm-nochange");
         }
     
         setTimeout(() => {
    
  • internal/webserver/web/static/js/min/admin.min.9.js+1 9 modified
    @@ -1,12 +1,4 @@
    -async function apiAuthModify(e,t,n){const s="./api/auth/modify",o={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,targetKey:e,permission:t,permissionModifier:n}};try{const e=await fetch(s,o);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiAuthModify:",e),e}}async function apiAuthFriendlyName(e,t){const n="./api/auth/friendlyname",s={method:"PUT",headers:{"Content-Type":"application/json",apikey:systemKey,targetKey:e,friendlyName:t}};try{const e=await fetch(n,s);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiAuthModify:",e),e}}async function apiAuthDelete(e){const t="./api/auth/delete",n={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,targetKey:e}};try{const e=await fetch(t,n);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiAuthDelete:",e),e}}async function apiAuthCreate(){const e="./api/auth/create",t={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,basicPermissions:"true"}};try{const n=await fetch(e,t);if(!n.ok)throw new Error(`Request failed with status: ${n.status}`);const s=await n.json();return s}catch(e){throw console.error("Error in apiAuthCreate:",e),e}}async function apiChunkComplete(e,t,n,s,o,i,a,r,c,l){const d="./api/chunk/complete",u={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,uuid:e,filename:t,filesize:n,realsize:s,contenttype:o,allowedDownloads:i,expiryDays:a,password:r,isE2E:c,nonblocking:l}};try{const e=await fetch(d,u);if(!e.ok){let t;try{const n=await e.json();t=n.ErrorMessage||`Request failed with status: ${e.status}`}catch{const n=await e.text();t=n||`Request failed with status: ${e.status}`}throw new Error(t)}const t=await e.json();return t}catch(e){throw console.error("Error in apiChunkComplete:",e),e}}async function apiFilesReplace(e,t){const n="./api/files/replace",s={method:"PUT",headers:{"Content-Type":"application/json",id:e,apikey:systemKey,idNewContent:t,deleteNewFile:!1}};try{const e=await fetch(n,s);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiFilesReplace:",e),e}}async function apiFilesListById(e){const t="./api/files/list/"+e,n={method:"GET",headers:{"Content-Type":"application/json",apikey:systemKey}};try{const e=await fetch(t,n);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const s=await e.json();return s}catch(e){throw console.error("Error in apiFilesListById:",e),e}}async function apiFilesModify(e,t,n,s,o){const i="./api/files/modify",a={method:"PUT",headers:{"Content-Type":"application/json",id:e,apikey:systemKey,allowedDownloads:t,expiryTimestamp:n,password:s,originalPassword:o}};try{const e=await fetch(i,a);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiFilesModify:",e),e}}async function apiFilesDelete(e,t){const n="./api/files/delete",s={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,id:e,delay:t}};try{const e=await fetch(n,s);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiFilesDelete:",e),e}}async function apiFilesRestore(e){const t="./api/files/restore",n={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,id:e}};try{const e=await fetch(t,n);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const s=await e.json();return s}catch(e){throw console.error("Error in apiFilesRestore:",e),e}}async function apiUserCreate(e){const t="./api/user/create",n={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,username:e}};try{const e=await fetch(t,n);if(!e.ok)throw e.status==409?new Error("duplicate"):new Error(`Request failed with status: ${e.status}`);const s=await e.json();return s}catch(e){throw console.error("Error in apiUserModify:",e),e}}async function apiUserModify(e,t,n){const s="./api/user/modify",o={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,userid:e,userpermission:t,permissionModifier:n}};try{const e=await fetch(s,o);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiUserModify:",e),e}}async function apiUserChangeRank(e,t){const n="./api/user/changeRank",s={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,userid:e,newRank:t}};try{const e=await fetch(n,s);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiUserModify:",e),e}}async function apiUserDelete(e,t){const n="./api/user/delete",s={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,userid:e,deleteFiles:t}};try{const e=await fetch(n,s);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiUserDelete:",e),e}}async function apiUserResetPassword(e,t){const n="./api/user/resetPassword",s={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,userid:e,generateNewPassword:t}};try{const e=await fetch(n,s);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiUserResetPassword:",e),e}}async function apiLogsDelete(e){const t="./api/logs/delete",n={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,timestamp:e}};try{const e=await fetch(t,n);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiLogsDelete:",e),e}}var toastId,dropzoneObject,isE2EEnabled,isUploading,rowCount,calendarInstance,statusItemCount,clipboard=new ClipboardJS(".btn");function showToast(e,t){let n=document.getElementById("toastnotification");typeof t!="undefined"?n.innerText=t:n.innerText=n.dataset.default,n.classList.add("show"),clearTimeout(toastId),toastId=setTimeout(()=>{hideToast()},e)}function hideToast(){document.getElementById("toastnotification").classList.remove("show")}function changeApiPermission(e,t,n){var o,i,s=document.getElementById(n);if(s.classList.contains("perm-processing")||s.classList.contains("perm-nochange"))return;o=s.classList.contains("perm-granted"),s.classList.add("perm-processing"),s.classList.remove("perm-granted"),s.classList.remove("perm-notgranted"),i="GRANT",o&&(i="REVOKE"),apiAuthModify(e,t,i).then(e=>{o?s.classList.add("perm-notgranted"):s.classList.add("perm-granted"),s.classList.remove("perm-processing")}).catch(e=>{o?s.classList.add("perm-granted"):s.classList.add("perm-notgranted"),s.classList.remove("perm-processing"),alert("Unable to set permission: "+e),console.error("Error:",e)})}function deleteApiKey(e){document.getElementById("delete-"+e).disabled=!0,apiAuthDelete(e).then(t=>{document.getElementById("row-"+e).classList.add("rowDeleting"),setTimeout(()=>{document.getElementById("row-"+e).remove()},290)}).catch(e=>{alert("Unable to delete API key: "+e),console.error("Error:",e)})}function newApiKey(){document.getElementById("button-newapi").disabled=!0,apiAuthCreate().then(e=>{addRowApi(e.Id,e.PublicId),document.getElementById("button-newapi").disabled=!1}).catch(e=>{alert("Unable to create API key: "+e),console.error("Error:",e)})}function addFriendlyNameChange(e){let t=document.getElementById("friendlyname-"+e);if(t.classList.contains("isBeingEdited"))return;t.classList.add("isBeingEdited");let i=t.innerHTML,n=document.createElement("input");n.size=5,n.value=i;let s=!0,o=function(){if(!s)return;s=!1;let o=n.value;o==""&&(o="Unnamed key"),t.innerHTML=o,t.classList.remove("isBeingEdited"),apiAuthFriendlyName(e,o).catch(e=>{alert("Unable to save name: "+e),console.error("Error:",e)})};n.onblur=o,n.addEventListener("keyup",function(e){e.keyCode===13&&(e.preventDefault(),o())}),t.innerHTML="",t.appendChild(n),n.focus()}function addRowApi(e,t){let d=document.getElementById("apitable"),n=d.insertRow(0);n.id="row-"+t;let s=0,o=n.insertCell(s++),a=n.insertCell(s++),r=n.insertCell(s++),i=n.insertCell(s++),c;canViewOtherApiKeys&&(c=n.insertCell(s++));let l=n.insertCell(s++);if(canViewOtherApiKeys&&(c.classList.add("newApiKey"),c.innerText=userName),o.classList.add("newApiKey"),a.classList.add("newApiKey"),r.classList.add("newApiKey"),i.classList.add("newApiKey"),i.classList.add("prevent-select"),l.classList.add("newApiKey"),o.innerText="Unnamed key",o.id="friendlyname-"+t,o.onclick=function(){addFriendlyNameChange(t)},a.innerHTML='<div class="font-monospace">'+e+"</div>",r.innerText="Never",l.innerHTML='<button type="button" data-clipboard-text="'+e+'"  onclick="showToast(1000)" title="Copy API Key" class="copyurl btn btn-outline-light btn-sm"><i class="bi bi-copy"></i></button> <button id="delete-'+t+`" type="button" class="btn btn-outline-danger btn-sm" onclick="deleteApiKey('`+t+`')" title="Delete"><i class="bi bi-trash3"></i></button>`,i.innerHTML=`
    -	    	<i id="perm_view_`+t+`" class="bi bi-eye perm-granted" title="List Uploads" onclick='changeApiPermission("`+t+`","PERM_VIEW", "perm_view_`+t+`");'></i>
    -	    	<i id="perm_upload_`+t+`" class="bi bi-file-earmark-arrow-up perm-granted" title="Upload" onclick='changeApiPermission("`+t+`","PERM_UPLOAD", "perm_upload_`+t+`");'></i>
    -	    	<i id="perm_edit_`+t+`" class="bi bi-pencil perm-granted" title="Edit Uploads" onclick='changeApiPermission("`+t+`","PERM_EDIT", "perm_edit_`+t+`");'></i>
    -	    	<i id="perm_delete_`+t+`" class="bi bi-trash3 perm-granted" title="Delete Uploads" onclick='changeApiPermission("`+t+`","PERM_DELETE", "perm_delete_`+t+`");'></i>
    -	    	<i id="perm_replace_`+t+`" class="bi bi-recycle perm-notgranted" title="Replace Uploads" onclick='changeApiPermission("`+t+`","PERM_REPLACE", "perm_replace_`+t+`");'></i>
    -	    	<i id="perm_users_`+t+`" class="bi bi-people perm-notgranted" title="Manage Users" onclick='changeApiPermission("`+t+`", "PERM_MANAGE_USERS", "perm_users_`+t+`");'></i>
    -	    	<i id="perm_logs_`+t+`" class="bi bi-card-list perm-notgranted" title="Manage System Logs" onclick='changeApiPermission("`+t+`", "PERM_MANAGE_LOGS", "perm_logs_`+t+`");'></i>
    -	    	<i id="perm_api_`+t+`" class="bi bi-sliders2 perm-notgranted" title="Manage API Keys" onclick='changeApiPermission("`+t+`","PERM_API_MOD", "perm_api_`+t+`");'></i>`,!canReplaceFiles){let e=document.getElementById("perm_replace_"+t);e.classList.add("perm-unavailable"),e.classList.add("perm-nochange")}if(!canManageUsers){let e=document.getElementById("perm_users_"+t);e.classList.add("perm-unavailable"),e.classList.add("perm-nochange")}setTimeout(()=>{o.classList.remove("newApiKey"),a.classList.remove("newApiKey"),r.classList.remove("newApiKey"),i.classList.remove("newApiKey"),l.classList.remove("newApiKey")},700)}function filterLogs(e){e=="all"?textarea.value=logContent:textarea.value=logContent.split(`
    +async function apiAuthModify(e,t,n){const s="./api/auth/modify",o={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,targetKey:e,permission:t,permissionModifier:n}};try{const e=await fetch(s,o);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiAuthModify:",e),e}}async function apiAuthFriendlyName(e,t){const n="./api/auth/friendlyname",s={method:"PUT",headers:{"Content-Type":"application/json",apikey:systemKey,targetKey:e,friendlyName:t}};try{const e=await fetch(n,s);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiAuthModify:",e),e}}async function apiAuthDelete(e){const t="./api/auth/delete",n={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,targetKey:e}};try{const e=await fetch(t,n);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiAuthDelete:",e),e}}async function apiAuthCreate(){const e="./api/auth/create",t={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,basicPermissions:"true"}};try{const n=await fetch(e,t);if(!n.ok)throw new Error(`Request failed with status: ${n.status}`);const s=await n.json();return s}catch(e){throw console.error("Error in apiAuthCreate:",e),e}}async function apiChunkComplete(e,t,n,s,o,i,a,r,c,l){const d="./api/chunk/complete",u={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,uuid:e,filename:t,filesize:n,realsize:s,contenttype:o,allowedDownloads:i,expiryDays:a,password:r,isE2E:c,nonblocking:l}};try{const e=await fetch(d,u);if(!e.ok){let t;try{const n=await e.json();t=n.ErrorMessage||`Request failed with status: ${e.status}`}catch{const n=await e.text();t=n||`Request failed with status: ${e.status}`}throw new Error(t)}const t=await e.json();return t}catch(e){throw console.error("Error in apiChunkComplete:",e),e}}async function apiFilesReplace(e,t){const n="./api/files/replace",s={method:"PUT",headers:{"Content-Type":"application/json",id:e,apikey:systemKey,idNewContent:t,deleteNewFile:!1}};try{const e=await fetch(n,s);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiFilesReplace:",e),e}}async function apiFilesListById(e){const t="./api/files/list/"+e,n={method:"GET",headers:{"Content-Type":"application/json",apikey:systemKey}};try{const e=await fetch(t,n);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const s=await e.json();return s}catch(e){throw console.error("Error in apiFilesListById:",e),e}}async function apiFilesModify(e,t,n,s,o){const i="./api/files/modify",a={method:"PUT",headers:{"Content-Type":"application/json",id:e,apikey:systemKey,allowedDownloads:t,expiryTimestamp:n,password:s,originalPassword:o}};try{const e=await fetch(i,a);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiFilesModify:",e),e}}async function apiFilesDelete(e,t){const n="./api/files/delete",s={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,id:e,delay:t}};try{const e=await fetch(n,s);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiFilesDelete:",e),e}}async function apiFilesRestore(e){const t="./api/files/restore",n={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,id:e}};try{const e=await fetch(t,n);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const s=await e.json();return s}catch(e){throw console.error("Error in apiFilesRestore:",e),e}}async function apiUserCreate(e){const t="./api/user/create",n={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,username:e}};try{const e=await fetch(t,n);if(!e.ok)throw e.status==409?new Error("duplicate"):new Error(`Request failed with status: ${e.status}`);const s=await e.json();return s}catch(e){throw console.error("Error in apiUserModify:",e),e}}async function apiUserModify(e,t,n){const s="./api/user/modify",o={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,userid:e,userpermission:t,permissionModifier:n}};try{const e=await fetch(s,o);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiUserModify:",e),e}}async function apiUserChangeRank(e,t){const n="./api/user/changeRank",s={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,userid:e,newRank:t}};try{const e=await fetch(n,s);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiUserModify:",e),e}}async function apiUserDelete(e,t){const n="./api/user/delete",s={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,userid:e,deleteFiles:t}};try{const e=await fetch(n,s);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiUserDelete:",e),e}}async function apiUserResetPassword(e,t){const n="./api/user/resetPassword",s={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,userid:e,generateNewPassword:t}};try{const e=await fetch(n,s);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`);const t=await e.json();return t}catch(e){throw console.error("Error in apiUserResetPassword:",e),e}}async function apiLogsDelete(e){const t="./api/logs/delete",n={method:"POST",headers:{"Content-Type":"application/json",apikey:systemKey,timestamp:e}};try{const e=await fetch(t,n);if(!e.ok)throw new Error(`Request failed with status: ${e.status}`)}catch(e){throw console.error("Error in apiLogsDelete:",e),e}}var toastId,dropzoneObject,isE2EEnabled,isUploading,rowCount,calendarInstance,statusItemCount,clipboard=new ClipboardJS(".btn");function showToast(e,t){let n=document.getElementById("toastnotification");typeof t!="undefined"?n.innerText=t:n.innerText=n.dataset.default,n.classList.add("show"),clearTimeout(toastId),toastId=setTimeout(()=>{hideToast()},e)}function hideToast(){document.getElementById("toastnotification").classList.remove("show")}function changeApiPermission(e,t,n){var o,i,s=document.getElementById(n);if(s.classList.contains("perm-processing")||s.classList.contains("perm-nochange"))return;o=s.classList.contains("perm-granted"),s.classList.add("perm-processing"),s.classList.remove("perm-granted"),s.classList.remove("perm-notgranted"),i="GRANT",o&&(i="REVOKE"),apiAuthModify(e,t,i).then(e=>{o?s.classList.add("perm-notgranted"):s.classList.add("perm-granted"),s.classList.remove("perm-processing")}).catch(e=>{o?s.classList.add("perm-granted"):s.classList.add("perm-notgranted"),s.classList.remove("perm-processing"),alert("Unable to set permission: "+e),console.error("Error:",e)})}function deleteApiKey(e){document.getElementById("delete-"+e).disabled=!0,apiAuthDelete(e).then(t=>{document.getElementById("row-"+e).classList.add("rowDeleting"),setTimeout(()=>{document.getElementById("row-"+e).remove()},290)}).catch(e=>{alert("Unable to delete API key: "+e),console.error("Error:",e)})}function newApiKey(){document.getElementById("button-newapi").disabled=!0,apiAuthCreate().then(e=>{addRowApi(e.Id,e.PublicId),document.getElementById("button-newapi").disabled=!1}).catch(e=>{alert("Unable to create API key: "+e),console.error("Error:",e)})}function addFriendlyNameChange(e){let t=document.getElementById("friendlyname-"+e);if(t.classList.contains("isBeingEdited"))return;t.classList.add("isBeingEdited");let i=t.innerText,n=document.createElement("input");n.size=5,n.value=i;let s=!0,o=function(){if(!s)return;s=!1;let o=n.value;o==""&&(o="Unnamed key"),t.innerText=o,t.classList.remove("isBeingEdited"),apiAuthFriendlyName(e,o).catch(e=>{alert("Unable to save name: "+e),console.error("Error:",e)})};n.onblur=o,n.addEventListener("keyup",function(e){e.keyCode===13&&(e.preventDefault(),o())}),t.innerText="",t.appendChild(n),n.focus()}function addRowApi(e,t){let f=document.getElementById("apitable"),s=f.insertRow(0);s.id="row-"+t;let i=0,a=s.insertCell(i++),l=s.insertCell(i++),d=s.insertCell(i++),r=s.insertCell(i++),u;canViewOtherApiKeys&&(u=s.insertCell(i++));let c=s.insertCell(i++);canViewOtherApiKeys&&(u.classList.add("newApiKey"),u.innerText=userName),a.classList.add("newApiKey"),l.classList.add("newApiKey"),d.classList.add("newApiKey"),r.classList.add("newApiKey"),r.classList.add("prevent-select"),c.classList.add("newApiKey"),a.innerText="Unnamed key",a.id="friendlyname-"+t,a.onclick=function(){addFriendlyNameChange(t)},l.innerText=e,l.classList.add("font-monospace"),d.innerText="Never";const n=document.createElement("button");n.type="button",n.dataset.clipboardText=e,n.title="Copy API Key",n.className="copyurl btn btn-outline-light btn-sm",n.setAttribute("onclick","showToast(1000)");const h=document.createElement("i");h.className="bi bi-copy",n.appendChild(h);const o=document.createElement("button");o.type="button",o.id=`delete-${t}`,o.title="Delete",o.className="btn btn-outline-danger btn-sm",o.setAttribute("onclick",`deleteApiKey('${t}')`);const m=document.createElement("i");m.className="bi bi-trash3",o.appendChild(m),c.appendChild(n),c.appendChild(document.createTextNode(" ")),c.appendChild(o);const p=[{perm:"PERM_VIEW",icon:"bi-eye",granted:!0,title:"List Uploads"},{perm:"PERM_UPLOAD",icon:"bi-file-earmark-arrow-up",granted:!0,title:"Upload"},{perm:"PERM_EDIT",icon:"bi-pencil",granted:!0,title:"Edit Uploads"},{perm:"PERM_DELETE",icon:"bi-trash3",granted:!0,title:"Delete Uploads"},{perm:"PERM_REPLACE",icon:"bi-recycle",granted:!1,title:"Replace Uploads"},{perm:"PERM_MANAGE_USERS",icon:"bi-people",granted:!1,title:"Manage Users"},{perm:"PERM_MANAGE_LOGS",icon:"bi-card-list",granted:!1,title:"Manage System Logs"},{perm:"PERM_API_MOD",icon:"bi-sliders2",granted:!1,title:"Manage API Keys"}];if(p.forEach(({perm:e,icon:n,granted:s,title:o})=>{const i=document.createElement("i"),a=`perm_${e.toLowerCase().replace("perm_","")}_${t}`;i.id=a,i.className=`bi ${n} ${s?"perm-granted":"perm-notgranted"}`,i.title=o,i.setAttribute("onclick",`changeApiPermission("${t}","${e}", "${a}");`),r.appendChild(i),r.appendChild(document.createTextNode(" "))}),!canReplaceFiles){let e=document.getElementById("perm_replace_"+t);e.classList.add("perm-unavailable"),e.classList.add("perm-nochange")}if(!canManageUsers){let e=document.getElementById("perm_users_"+t);e.classList.add("perm-unavailable"),e.classList.add("perm-nochange")}setTimeout(()=>{a.classList.remove("newApiKey"),l.classList.remove("newApiKey"),d.classList.remove("newApiKey"),r.classList.remove("newApiKey"),c.classList.remove("newApiKey")},700)}function filterLogs(e){e=="all"?textarea.value=logContent:textarea.value=logContent.split(`
     `).filter(t=>t.includes("["+e+"]")).join(`
     `),textarea.scrollTop=textarea.scrollHeight}function deleteLogs(e){if(e=="none")return;if(!confirm("Do you want to delete the selected logs?")){document.getElementById("deleteLogs").selectedIndex=0;return}let t=Math.floor(Date.now()/1e3);switch(e){case"all":t=0;break;case"2":t=t-2*24*60*60;break;case"7":t=t-7*24*60*60;break;case"14":t=t-14*24*60*60;break;case"30":t=t-30*24*60*60;break}apiLogsDelete(t).then(e=>{location.reload()}).catch(e=>{alert("Unable to delete logs: "+e),console.error("Error:",e)})}isE2EEnabled=!1,isUploading=!1,rowCount=-1;function initDropzone(){Dropzone.options.uploaddropzone={paramName:"file",dictDefaultMessage:"Drop files, paste or click here to upload",createImageThumbnails:!1,chunksUploaded:function(e,t){sendChunkComplete(e,t)},init:function(){dropzoneObject=this,this.on("addedfile",e=>{saveUploadDefaults(),addFileProgress(e)}),this.on("queuecomplete",function(){isUploading=!1}),this.on("sending",function(){isUploading=!0}),this.on("error",function(e,t,n){n&&n.status===413?showError(e,"File too large to upload. If you are using a reverse proxy, make sure that the allowed body size is at least 70MB."):showError(e,"Error: "+t)}),this.on("uploadprogress",function(e,t,n){updateProgressbar(e,t,n)}),isE2EEnabled&&(dropzoneObject.disable(),dropzoneObject.options.dictDefaultMessage="Loading end-to-end encryption...",document.getElementsByClassName("dz-button")[0].innerText="Loading end-to-end encryption...",setE2eUpload())}},document.onpaste=function(e){if(dropzoneObject.disabled)return;var t,n=(e.clipboardData||e.originalEvent.clipboardData).items;for(let e in n)t=n[e],t.kind==="file"&&dropzoneObject.addFile(t.getAsFile()),t.kind==="string"&&t.getAsString(function(e){const t=/<img *.+>/gi;if(t.test(e)===!1){let t=new Blob([e],{type:"text/plain"}),n=new File([t],"Pasted Text.txt",{type:"text/plain",lastModified:new Date(0)});dropzoneObject.addFile(n)}})},window.addEventListener("beforeunload",e=>{isUploading&&(e.returnValue="Upload is still in progress. Do you want to close this page?")})}function updateProgressbar(e,t,n){let o=e.upload.uuid,i=document.getElementById(`us-container-${o}`);if(i==null||i.getAttribute("data-complete")==="true")return;let s=Math.round(t);s<0&&(s=0),s>100&&(s=100);let r=Date.now()-i.getAttribute("data-starttime"),c=n/(r/1e3)/1024/1024;document.getElementById(`us-progressbar-${o}`).style.width=s+"%";let a=Math.round(c*10)/10;Number.isNaN(a)||(document.getElementById(`us-progress-info-${o}`).innerText=s+"% - "+a+"MB/s")}function addFileProgress(e){addFileStatus(e.upload.uuid,e.upload.filename)}function setUploadDefaults(){let s=getLocalStorageWithDefault("defaultDownloads",1),o=getLocalStorageWithDefault("defaultExpiry",14),e=getLocalStorageWithDefault("defaultPassword",""),t=getLocalStorageWithDefault("defaultUnlimitedDownloads",!1)==="true",n=getLocalStorageWithDefault("defaultUnlimitedTime",!1)==="true";document.getElementById("allowedDownloads").value=s,document.getElementById("expiryDays").value=o,document.getElementById("password").value=e,document.getElementById("enableDownloadLimit").checked=!t,document.getElementById("enableTimeLimit").checked=!n,e===""?(document.getElementById("enablePassword").checked=!1,document.getElementById("password").disabled=!0):(document.getElementById("enablePassword").checked=!0,document.getElementById("password").disabled=!1),t&&(document.getElementById("allowedDownloads").disabled=!0),n&&(document.getElementById("expiryDays").disabled=!0)}function saveUploadDefaults(){localStorage.setItem("defaultDownloads",document.getElementById("allowedDownloads").value),localStorage.setItem("defaultExpiry",document.getElementById("expiryDays").value),localStorage.setItem("defaultPassword",document.getElementById("password").value),localStorage.setItem("defaultUnlimitedDownloads",!document.getElementById("enableDownloadLimit").checked),localStorage.setItem("defaultUnlimitedTime",!document.getElementById("enableTimeLimit").checked)}function getLocalStorageWithDefault(e,t){var n=localStorage.getItem(e);return n===null?t:n}function urlencodeFormData(e){let t="";function s(e){return encodeURIComponent(e).replace(/%20/g,"+")}for(var n of e.entries())typeof n[1]=="string"&&(t+=(t?"&":"")+s(n[0])+"="+s(n[1]));return t}function sendChunkComplete(e,t){let c=e.upload.uuid,n=e.name,s=e.size,l=e.size,o=e.type,i=document.getElementById("allowedDownloads").value,a=document.getElementById("expiryDays").value,d=document.getElementById("password").value,r=e.isEndToEndEncrypted===!0,u=!0;document.getElementById("enableDownloadLimit").checked||(i=0),document.getElementById("enableTimeLimit").checked||(a=0),r&&(s=e.sizeEncrypted,n="Encrypted File",o=""),apiChunkComplete(c,n,s,l,o,i,a,d,r,u).then(n=>{t();let s=document.getElementById(`us-progress-info-${e.upload.uuid}`);s!=null&&(s.innerText="In Queue...")}).catch(t=>{console.error("Error:",t),dropzoneUploadError(e,t)})}function dropzoneUploadError(e,t){e.accepted=!1,dropzoneObject._errorProcessing([e],t),showError(e,t)}function dropzoneGetFile(e){for(let t=0;t<dropzoneObject.files.length;t++){const n=dropzoneObject.files[t];if(n.upload.uuid===e)return n}return null}function requestFileInfo(e,t){apiFilesListById(e).then(n=>{addRow(n);let s=dropzoneGetFile(t);if(s==null)return;if(s.isEndToEndEncrypted===!0){try{let o=GokapiE2EAddFile(t,e,s.name);if(o instanceof Error)throw o;let n=GokapiE2EInfoEncrypt();if(n instanceof Error)throw n;storeE2EInfo(n)}catch(e){s.accepted=!1,dropzoneObject._errorProcessing([s],e);return}GokapiE2EDecryptMenu()}removeFileStatus(t)}).catch(e=>{let n=dropzoneGetFile(t);n!=null&&dropzoneUploadError(n,e),console.error("Error:",e)})}function parseProgressStatus(e){let n=document.getElementById(`us-container-${e.chunk_id}`);if(n==null)return;n.setAttribute("data-complete","true");let t;switch(e.upload_status){case 0:t="Processing file...";break;case 1:t="Uploading file...";break;case 2:t="Finalising...",requestFileInfo(e.file_id,e.chunk_id);break;case 3:t="Error";let n=dropzoneGetFile(e.chunk_id);e.error_message==""&&(e.error_message="Server Error"),n!=null&&dropzoneUploadError(n,e.error_message);return;default:t="Unknown status";break}document.getElementById(`us-progress-info-${e.chunk_id}`).innerText=t}function showError(e,t){let n=e.upload.uuid;document.getElementById(`us-progressbar-${n}`).style.width="100%",document.getElementById(`us-progressbar-${n}`).style.backgroundColor="red",document.getElementById(`us-progress-info-${n}`).innerText=t,document.getElementById(`us-progress-info-${n}`).classList.add("uploaderror")}function editFile(){const e=document.getElementById("mb_save");e.disabled=!0;let s=e.getAttribute("data-fileid"),o=document.getElementById("mi_edit_down").value,i=document.getElementById("mi_edit_expiry").value,t=document.getElementById("mi_edit_pw").value,a=t==="(unchanged)";document.getElementById("mc_download").checked||(o=0),document.getElementById("mc_expiry").checked||(i=0),document.getElementById("mc_password").checked||(a=!1,t="");let r=!1,n="";document.getElementById("mc_replace").checked&&(n=document.getElementById("mi_edit_replace").value,r=n!=""),apiFilesModify(s,o,i,t,a).then(t=>{if(!r){location.reload();return}apiFilesReplace(s,n).then(e=>{location.reload()}).catch(t=>{alert("Unable to edit file: "+t),console.error("Error:",t),e.disabled=!1})}).catch(t=>{alert("Unable to edit file: "+t),console.error("Error:",t),e.disabled=!1})}calendarInstance=null;function createCalendar(e){const t=new Date(e*1e3);calendarInstance=flatpickr("#mi_edit_expiry",{enableTime:!0,dateFormat:"U",altInput:!0,altFormat:"Y-m-d H:i",allowInput:!0,time_24hr:!0,defaultDate:t,minDate:"today"})}function handleEditCheckboxChange(e){var t=document.getElementById(e.getAttribute("data-toggle-target")),n=e.getAttribute("data-timestamp");e.checked?(t.classList.remove("disabled"),t.removeAttribute("disabled"),n!=null&&(calendarInstance._input.disabled=!1)):(n!=null&&(calendarInstance._input.disabled=!0),t.classList.add("disabled"),t.setAttribute("disabled",!0))}function showEditModal(e,t,n,s,o,i,a,r,c){let d=$("#modaledit").clone();$("#modaledit").on("hide.bs.modal",function(){$("#modaledit").remove();let e=d.clone();$("body").append(e)}),document.getElementById("m_filenamelabel").innerHTML=e,document.getElementById("mc_expiry").setAttribute("data-timestamp",s),document.getElementById("mb_save").setAttribute("data-fileid",t),createCalendar(s),i?(document.getElementById("mi_edit_down").value="1",document.getElementById("mi_edit_down").disabled=!0,document.getElementById("mc_download").checked=!1):(document.getElementById("mi_edit_down").value=n,document.getElementById("mi_edit_down").disabled=!1,document.getElementById("mc_download").checked=!0),a?(document.getElementById("mi_edit_expiry").value=add14DaysIfBeforeCurrentTime(s),document.getElementById("mi_edit_expiry").disabled=!0,document.getElementById("mc_expiry").checked=!1,calendarInstance._input.disabled=!0):(document.getElementById("mi_edit_expiry").value=s,document.getElementById("mi_edit_expiry").disabled=!1,document.getElementById("mc_expiry").checked=!0,calendarInstance._input.disabled=!1),o?(document.getElementById("mi_edit_pw").value="(unchanged)",document.getElementById("mi_edit_pw").disabled=!1,document.getElementById("mc_password").checked=!0):(document.getElementById("mi_edit_pw").value="",document.getElementById("mi_edit_pw").disabled=!0,document.getElementById("mc_password").checked=!1);let l=document.getElementById("mi_edit_replace");if(c)if(document.getElementById("replaceGroup").style.display="flex",r)document.getElementById("mc_replace").disabled=!0,document.getElementById("mc_replace").title="Replacing content is not available for end-to-end encrypted files",l.add(new Option("Unavailable",0)),l.title="Replacing content is not available for end-to-end encrypted files",l.value="0";else{let e=getAllAvailableFiles();for(let n=0;n<e[0].length;n++){if(e[0][n]==t)continue;l.add(new Option(e[1][n]+" ("+e[0][n]+")",e[0][n]))}}else document.getElementById("replaceGroup").style.display="none";new bootstrap.Modal("#modaledit",{}).show()}function selectTextForPw(e){e.value==="(unchanged)"&&e.setSelectionRange(0,e.value.length)}function add14DaysIfBeforeCurrentTime(e){let t=Date.now(),n=e*1e3;if(n<t){let e=t+14*24*60*60*1e3;return Math.floor(e/1e3)}return e}function getAllAvailableFiles(){let e=[],t=[],n=document.querySelectorAll('[id^="cell-name-"]');for(let s of n)e.push(s.id.replace("cell-name-","")),t.push(s.innerHTML);return[e,t]}function deleteFile(e){document.getElementById("button-delete-"+e).disabled=!0,apiFilesDelete(e,10).then(t=>{changeRowCount(!1,document.getElementById("row-"+e)),showToastFileDeletion(e)}).catch(e=>{alert("Unable to delete file: "+e),console.error("Error:",e)})}function checkBoxChanged(e,t){let n=!e.checked;n?document.getElementById(t).setAttribute("disabled",""):document.getElementById(t).removeAttribute("disabled"),t==="password"&&n&&(document.getElementById("password").value="")}function parseSseData(e){let t;try{t=JSON.parse(e)}catch(e){console.error("Failed to parse event data:",e);return}switch(t.event){case"download":setNewDownloadCount(t.file_id,t.download_count,t.downloads_remaining);return;case"uploadStatus":parseProgressStatus(t);return;default:console.error("Unknown event",t)}}function setNewDownloadCount(e,t,n){let s=document.getElementById("cell-downloads-"+e);if(s!=null&&(s.innerHTML=t,s.classList.add("updatedDownloadCount"),setTimeout(()=>s.classList.remove("updatedDownloadCount"),500)),n!=-1){let t=document.getElementById("cell-downloadsRemaining-"+e);t!=null&&(t.innerHTML=n,t.classList.add("updatedDownloadCount"),setTimeout(()=>t.classList.remove("updatedDownloadCount"),500))}}function registerChangeHandler(){const e=new EventSource("./uploadStatus");e.onmessage=e=>{parseSseData(e.data)},e.onerror=t=>{t.target.readyState!==EventSource.CLOSED&&e.close(),console.log("Reconnecting to SSE..."),setTimeout(registerChangeHandler,5e3)}}statusItemCount=0;function addFileStatus(e,t){const n=document.createElement("div");n.setAttribute("id",`us-container-${e}`),n.classList.add("us-container");const a=document.createElement("div");a.classList.add("filename"),a.textContent=t,n.appendChild(a);const s=document.createElement("div");s.classList.add("upload-progress-container"),s.setAttribute("id",`us-progress-container-${e}`);const r=document.createElement("div");r.classList.add("upload-progress-bar");const o=document.createElement("div");o.setAttribute("id",`us-progressbar-${e}`),o.classList.add("upload-progress-bar-progress"),o.style.width="0%",r.appendChild(o);const i=document.createElement("div");i.setAttribute("id",`us-progress-info-${e}`),i.classList.add("upload-progress-info"),i.textContent="0%",s.appendChild(r),s.appendChild(i),n.appendChild(s),n.setAttribute("data-starttime",Date.now()),n.setAttribute("data-complete","false");const c=document.getElementById("uploadstatus");c.appendChild(n),c.style.visibility="visible",statusItemCount++}function removeFileStatus(e){const t=document.getElementById(`us-container-${e}`);if(t==null)return;t.remove(),statusItemCount--,statusItemCount<1&&(document.getElementById("uploadstatus").style.visibility="hidden")}function addRow(e){let u=document.getElementById("downloadtable"),n=u.insertRow(0);n.id="row-"+e.Id;let o=n.insertCell(0),i=n.insertCell(1),s=n.insertCell(2),a=n.insertCell(3),r=n.insertCell(4),c=n.insertCell(5),l=n.insertCell(6),d="";e.IsPasswordProtected===!0&&(d='  <i  title="Password protected" class="bi bi-key"></i>'),o.innerText=e.Name,o.id="cell-name-"+e.Id,r.id="cell-downloads-"+e.Id,i.innerText=e.Size,e.UnlimitedDownloads?s.innerText="Unlimited":(s.innerText=e.DownloadsRemaining,s.id="cell-downloadsRemaining-"+e.Id),e.UnlimitedTime?a.innerText="Unlimited":a.innerText=e.ExpireAtString,r.innerText=e.DownloadCount,c.innerHTML='<a  target="_blank" style="color: inherit" id="url-href-'+e.Id+'" href="'+e.UrlDownload+'">'+e.Id+"</a>"+d;let t='<button type="button" onclick="showToast(1000)" id="url-button-'+e.Id+'"  data-clipboard-text="'+e.UrlDownload+'" class="copyurl btn btn-outline-light btn-sm"><i class="bi bi-copy"></i> URL</button> ';return e.UrlHotlink===""?t=t+'<button type="button"class="copyurl btn btn-outline-light btn-sm disabled"><i class="bi bi-copy"></i> Hotlink</button> ':t=t+'<button type="button" onclick="showToast(1000)" data-clipboard-text="'+e.UrlHotlink+'" class="copyurl btn btn-outline-light btn-sm"><i class="bi bi-copy"></i> Hotlink</button> ',t=t+'<button type="button" id="qrcode-'+e.Id+`" title="QR Code" class="btn btn-outline-light btn-sm" onclick="showQrCode('`+e.UrlDownload+`');"><i class="bi bi-qr-code"></i></button> `,t=t+`<button type="button" title="Edit" class="btn btn-outline-light btn-sm" onclick="showEditModal('`+e.Name+"','"+e.Id+"', "+e.DownloadsRemaining+", "+e.ExpireAt+", "+e.IsPasswordProtected+", "+e.UnlimitedDownloads+", "+e.UnlimitedTime+", "+e.IsEndToEndEncrypted+', canReplaceOwnFiles);"><i class="bi bi-pencil"></i></button> ',t=t+'<button type="button" id="button-delete-'+e.Id+`" title="Delete" class="btn btn-outline-danger btn-sm" onclick="deleteFile('`+e.Id+`')"><i class="bi bi-trash3"></i></button>`,l.innerHTML=t,o.classList.add("newItem"),i.classList.add("newItem"),s.classList.add("newItem"),a.classList.add("newItem"),r.classList.add("newItem"),c.classList.add("newItem"),l.classList.add("newItem"),i.setAttribute("data-order",e.SizeBytes),changeRowCount(!0,n),e.Id}function changeRowCount(e,t){let n=$("#maintable").DataTable();rowCount==-1&&(rowCount=n.rows().count()),e?(rowCount=rowCount+1,n.row.add(t)):(rowCount=rowCount-1,t.classList.add("rowDeleting"),setTimeout(()=>{n.row(t).remove(),t.remove()},290));let s=document.getElementsByClassName("dataTables_empty")[0];typeof s!="undefined"?s.innerText="Files stored: "+rowCount:document.getElementsByClassName("dataTables_info")[0].innerText="Files stored: "+rowCount}function hideQrCode(){document.getElementById("qroverlay").style.display="none",document.getElementById("qrcode").innerHTML=""}function showQrCode(e){const t=document.getElementById("qroverlay");t.style.display="block",new QRCode(document.getElementById("qrcode"),{text:e,width:200,height:200,colorDark:"#000000",colorLight:"#ffffff",correctLevel:QRCode.CorrectLevel.H}),t.addEventListener("click",hideQrCode)}function showToastFileDeletion(e){let t=document.getElementById("toastnotificationUndo"),n=document.getElementById("cell-name-"+e).innerText,s=document.getElementById("toastFilename"),o=document.getElementById("toastUndoButton");s.innerText=n,o.dataset.fileid=e,hideToast(),t.classList.add("show"),clearTimeout(toastId),toastId=setTimeout(()=>{hideFileToast()},5e3)}function hideFileToast(){document.getElementById("toastnotificationUndo").classList.remove("show")}function handleUndo(e){hideFileToast(),apiFilesRestore(e.dataset.fileid).then(e=>{addRow(e.FileInfo)}).catch(e=>{alert("Unable to restore file: "+e),console.error("Error:",e)})}function changeUserPermission(e,t,n){let s=document.getElementById(n);if(s.classList.contains("perm-processing")||s.classList.contains("perm-nochange"))return;let o=s.classList.contains("perm-granted");s.classList.add("perm-processing"),s.classList.remove("perm-granted"),s.classList.remove("perm-notgranted");let i="GRANT";o&&(i="REVOKE"),t=="PERM_REPLACE_OTHER"&&!o&&(hasNotPermissionReplace=document.getElementById("perm_replace_"+e).classList.contains("perm-notgranted"),hasNotPermissionReplace&&(showToast(2e3,"Also granting permission to replace own files"),changeUserPermission(e,"PERM_REPLACE","perm_replace_"+e))),t=="PERM_REPLACE"&&o&&(hasPermissionReplaceOthers=document.getElementById("perm_replace_other_"+e).classList.contains("perm-granted"),hasPermissionReplaceOthers&&(showToast(2e3,"Also revoking permission to replace files of other users"),changeUserPermission(e,"PERM_REPLACE_OTHER","perm_replace_other_"+e))),apiUserModify(e,t,i).then(e=>{o?s.classList.add("perm-notgranted"):s.classList.add("perm-granted"),s.classList.remove("perm-processing")}).catch(e=>{o?s.classList.add("perm-granted"):s.classList.add("perm-notgranted"),s.classList.remove("perm-processing"),alert("Unable to set permission: "+e),console.error("Error:",e)})}function changeRank(e,t,n){let s=document.getElementById(n);if(s.disabled)return;s.disabled=!0,apiUserChangeRank(e,t).then(e=>{location.reload()}).catch(e=>{s.disabled=!1,alert("Unable to change rank: "+e),console.error("Error:",e)})}function showDeleteModal(e,t){let n=document.getElementById("checkboxDelete");n.checked=!1,document.getElementById("deleteModalBody").innerText=t,$("#deleteModal").modal("show"),document.getElementById("buttonDelete").onclick=function(){apiUserDelete(e,n.checked).then(t=>{$("#deleteModal").modal("hide"),document.getElementById("row-"+e).classList.add("rowDeleting"),setTimeout(()=>{document.getElementById("row-"+e).remove()},290)}).catch(e=>{alert("Unable to delete user: "+e),console.error("Error:",e)})}}function showAddUserModal(){let e=$("#newUserModal").clone();$("#newUserModal").on("hide.bs.modal",function(){$("#newUserModal").remove();let t=e.clone();$("body").append(t)}),$("#newUserModal").modal("show")}function showResetPwModal(e,t){let n=$("#resetPasswordModal").clone();$("#resetPasswordModal").on("hide.bs.modal",function(){$("#resetPasswordModal").remove();let e=n.clone();$("body").append(e)}),document.getElementById("l_userpwreset").innerText=t;let s=document.getElementById("resetPasswordButton");s.onclick=function(){resetPw(e,document.getElementById("generateRandomPassword").checked)},$("#resetPasswordModal").modal("show")}function resetPw(e,t){let n=document.getElementById("resetPasswordButton");document.getElementById("resetPasswordButton").disabled=!0,apiUserResetPassword(e,t).then(e=>{if(!t){$("#resetPasswordModal").modal("hide"),showToast(1e3,"Password change requirement set successfully");return}n.style.display="none",document.getElementById("cancelPasswordButton").style.display="none",document.getElementById("formentryReset").style.display="none",document.getElementById("randomPasswordContainer").style.display="block",document.getElementById("closeModalResetPw").style.display="block",document.getElementById("l_returnedPw").innerText=e.password,document.getElementById("copypwclip").onclick=function(){navigator.clipboard.writeText(e.password),showToast(1e3,"Password copied to clipboard")}}).catch(e=>{alert("Unable to reset user password: "+e),console.error("Error:",e),n.disabled=!1})}function addNewUser(){let e=document.getElementById("mb_addUser");e.disabled=!0;let t=document.getElementById("newUserForm");if(t.checkValidity()){let t=document.getElementById("e_userName");apiUserCreate(t.value.trim()).then(e=>{$("#newUserModal").modal("hide"),addRowUser(e.id,e.name)}).catch(t=>{t.message=="duplicate"?(alert("A user already exists with that name"),e.disabled=!1):(alert("Unable to create user: "+t),console.error("Error:",t),e.disabled=!1)})}else t.classList.add("was-validated"),e.disabled=!1}function addRowUser(e,t){let l=document.getElementById("usertable"),n=l.insertRow(1);n.id="row-"+e;let o=n.insertCell(0),i=n.insertCell(1),a=n.insertCell(2),r=n.insertCell(3),c=n.insertCell(4),s=n.insertCell(5);o.classList.add("newUser"),i.classList.add("newUser"),a.classList.add("newUser"),r.classList.add("newUser"),c.classList.add("newUser"),s.classList.add("newUser"),o.innerText=t,i.innerText="User",a.innerText="Never",r.innerText="0";let d='<button id="pwchange-'+e+`" type="button" class="btn btn-outline-light btn-sm" onclick="showResetPwModal('`+e+"', '"+t+`')" title="Reset Password"><i class="bi bi-key-fill"></i></button>&nbsp; `;s.innerHTML='<button id="changeRank_'+e+'" type="button" onclick="changeRank( '+e+" , 'ADMIN', 'changeRank_"+e+`')" title="Promote User" class="btn btn-outline-light btn-sm"><i class="bi bi-chevron-double-up"></i></button>&nbsp; <button id="delete-`+e+'" type="button" class="btn btn-outline-danger btn-sm"  onclick="showDeleteModal('+e+", '"+t+`')" title="Delete"><i class="bi bi-trash3"></i></button>`,isInternalAuth&&(s.innerHTML=d+s.innerHTML),c.innerHTML=`
         <i id="perm_replace_`+e+`" class="bi bi-recycle perm-notgranted " title="Replace own uploads" onclick='changeUserPermission(`+e+`,"PERM_REPLACE", "perm_replace_`+e+`");'></i>
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.