GitHub Security Advisory: GHSA-66hx-chf7-3332
Package: pyload-ng (PIP)
Severity: HIGH (CVSS 8.8)
CWE: CWE-613: Insufficient Session Expiration
Vulnerable versions: <= 0.5.0b3.dev97
Patched in: unpatched

Summary: pyLoad has Stale Session Privilege After Role/Permission Change (Privilege Revocation Bypass)

Details:
### Summary
pyLoad caches `role` and `permission` in the session at login and continues to authorize requests using these cached values, even after an admin changes the user's role/permissions in the database.

As a result, an already logged-in user can keep old (revoked) privileges until logout/session expiry, enabling continued privileged actions.

This is a core authorization/session-consistency issue and is not resolved by toggling an optional security feature.

### Details
The WebUI auth flow stores authorization state in session:

- `src/pyload/webui/app/helpers.py:187-200`
  - `set_session(...)` writes:
    - `"role": user_info["role"]`
    - `"perms": user_info["permission"]`

Authorization checks later trust cached session values:

- `src/pyload/webui/app/helpers.py:134-151`
  - `parse_permissions(...)` reads `session.get("role")` / `session.get("perms")`
- `src/pyload/webui/app/helpers.py:225-230`
  - `is_authenticated(...)` only verifies `authenticated` and `api.user_exists(user)` (existence), not fresh role/permission
- `src/pyload/webui/app/helpers.py:267-275`
  - `login_required(...)` uses `parse_permissions(s)` for allow/deny decisions
- `src/pyload/webui/app/helpers.py:356-365`
  - API session auth path also trusts `s["role"]` and `s["perms"]`

Role/permission updates are written to DB but active sessions are not invalidated/refreshed:

- `src/pyload/webui/app/blueprints/json_blueprint.py:389-434`
  - `update_users(...)` calls `api.set_user_permission(...)` and returns
- `src/pyload/core/api/__init__.py:1643-1645`
  - `set_user_permission(...)` updates DB role/permission only

Default exposure window is long:

- `src/pyload/core/config/default.cfg:47`
  - `session_lifetime = 44640` minutes (~31 days)

Therefore, privilege revocation is not enforced immediately for active sessions.

Note on duplicates:
- This appears distinct from CVE-2023-0227 (session validity after **user deletion**) because this report is about stale authorization after **role/permission changes** while the user still exists.

### PoC

```python
#!/usr/bin/env python3
"""
Repro: stale session privilege after role/permission changes.

This PoC is source-based and leaves no persistent state.
It validates that:
1) Role/permission are cached into session at login.
2) Authorization checks read role/permission from session, not fresh DB values.
3) User updates write DB permission/role without invalidating active sessions.
4) Default session lifetime is long, increasing stale-privilege exposure window.
"""

from __future__ import annotations

import pathlib
import re
from typing import Iterable


ROOT = pathlib.Path(__file__).resolve().parent / "pyload" / "src" / "pyload"


def read(rel: str) -> str:
    return (ROOT / rel).read_text(encoding="utf-8")


def has_any(text: str, patterns: Iterable[str]) -> bool:
    return all(re.search(p, text, re.MULTILINE) for p in patterns)


def main() -> None:
    helpers = read("webui/app/helpers.py")
    json_blueprint = read("we