GitHub Security Advisory: GHSA-rfgh-63mg-8pwm
Package: pyload-ng (PIP)
Severity: MODERATE (CVSS 5.4)
CWE: CWE-285: Improper Authorization, CWE-863: Incorrect Authorization
Vulnerable versions: <= 0.5.0b3
Patched in: unpatched

Summary: pyload-ng has a WebUI JSON permission mismatch that lets ADD/DELETE users invoke MODIFY-only actions

Details:
### Summary
Several WebUI JSON endpoints enforce weaker permissions than the core API methods they invoke. This allows authenticated low-privileged users to execute `MODIFY` operations that should be denied by pyLoad's own permission model.

Confirmed mismatches:
- `ADD` user can reorder packages/files (`order_package`, `order_file`) via `/json/package_order` and `/json/link_order`
- `DELETE` user can abort downloads (`stop_downloads`) via `/json/abort_link`

### Details
pyLoad defines granular permissions in core API:
- `order_package` requires `Perms.MODIFY` (`src/pyload/core/api/__init__.py:1125`)
- `order_file` requires `Perms.MODIFY` (`src/pyload/core/api/__init__.py:1137`)
- `stop_downloads` requires `Perms.MODIFY` (`src/pyload/core/api/__init__.py:1046`)

But WebUI JSON routes use weaker checks:
- `/json/package_order` uses `@login_required("ADD")` then calls `api.order_package(...)` (`src/pyload/webui/app/blueprints/json_blueprint.py:109-117`)
- `/json/link_order` uses `@login_required("ADD")` then calls `api.order_file(...)` (`src/pyload/webui/app/blueprints/json_blueprint.py:137-145`)
- `/json/abort_link` uses `@login_required("DELETE")` then calls `api.stop_downloads(...)` (`src/pyload/webui/app/blueprints/json_blueprint.py:123-131`)

Why this is likely unintended (not just convenience):
- The same JSON blueprint correctly protects other edit actions with `MODIFY`:
  - `/json/move_package` -> `@login_required("MODIFY")` (`json_blueprint.py:188-196`)
  - `/json/edit_package` -> `@login_required("MODIFY")` (`json_blueprint.py:202-217`)
- The project UI exposes granular per-user permission assignment (`settings.html:184-190`), implying these boundaries are intended security controls.

### PoC
Environment:
- Repository version: `0.5.0b3` (`VERSION` file)
- Commit tested: `ddc53b3d7`

PoC A (ADD-only user invokes MODIFY-only reorder):
```python
import os
import sys
from types import SimpleNamespace

sys.path.insert(0, os.path.abspath('src'))

from flask import Flask
from pyload.core.api import Api, Perms, Role
from pyload.webui.app.blueprints import json_blueprint

class FakeApi:
    def __init__(self):
        self.calls = []

    def user_exists(self, username):
        return username == 'attacker'

    def order_package(self, pack_id, pos):
        self.calls.append(('order_package', int(pack_id), int(pos)))

    def order_file(self, file_id, pos):
        self.calls.append(('order_file', int(file_id), int(pos)))

api = Api(SimpleNamespace(_=lambda x: x))
ctx = {'role': Role.USER, 'permission': Perms.ADD}
print('API auth (ADD-only) order_package:', api.is_authorized('order_package', ctx))
print('API auth (ADD-only) order_file:', api.is_authorized('order_file', ctx))

app = Flask(__name__)
app.secret_key = 'k'
app.config['TESTING'] = True
app.config['WTF_CSRF_ENABLED'] = False
f = FakeApi()
app.config['PYLOAD_API'] = f
app.register_blueprint(json_blueprint.bp)

with app.test_client() as c:
    with c.session_transaction() as s:
        s