Threats affecting pkg:golang/github.com/xyproto/algernon
8 threats
Most recently published first
| Severity | CVSS | Signals | Threat | Source | Published | Score and star |
|---|---|---|---|---|---|---|
| High | 8.7 | EPSS 0.6% | CVE-2026-52792github.com/xyproto/algernon · Algernon vulnerable to server-side script source disclosure on Windows via NTFS filename High8.7EPSS 0.6%GCVE DatabaseCVE-2026-52792 ### Summary Algernon selects its file handler from `filepath.Ext()` (engine/handlers.go:134), which does not treat the NTFS-equivalent names `x.lua::$DATA`, `x.lua.`, or `x.lua ` as `.lua`. On Windows, an unauthenticated client appends one of these suffixes to any server-side script on a public path and receives its raw source instead of executed output, leaking embedded secrets such as database credentials and the `SetCookieSecret` value. Linux and macOS hosts are unaffected. ### Preconditions - Algernon runs on a Windows host (NTFS filesystem). - The instance serves at least one server-side script (`.lua`, `.tl`, `.po2`, `.amber`, `.frm`). - The script sits on a public path, or no auth backend is configured (`--nodb`, `--simple`, or default no-DB). - HTTP/HTTPS reachability to the server. ### Details ```go // engine/handlers.go:133 lowercaseFilename := strings.ToLower(filename) ext := filepath.Ext(lowercaseFilename) // "index.lua::$data" -> ".lua::$data", not ".lua" [offending] ... if ac.dispatchRenderer(w, req, filename, ext) { // ext unrecognised, returns false return } switch ext { case ".lua", ".tl": // execute the script -- never reached for the equivalent forms // ... RunLua ... default: // control reaches the raw-file branch below } ``` ```go // engine/handlers.go:452 f, err := os.Open(filename) // NTFS resolves "index.lua::$DATA" to index.lua's data stream ... // engine/handlers.go:479 if dataBlock, err := ac.ReadAndLogErrors(w, filename, ext); err == nil { dataBlock.ToClient(w, req, filename, ac.ClientCanGzip(req), gzipThreshold) // raw source to client } ``` The request path reaches `FilePage` through `URL2filename` (utils/files.go:24), which rejects only `..`; a `:`, a trailing `.`, and a trailing space all pass through into `filename`. `filepath.Ext` does an exact suffix match, so `.lua::$data`, `.`, and `.lua ` are not equal to `.lua` or `.tl`. The renderer registry and the execute case are both skipped and control falls to the `default` branch. The default branch opens `filename` with `os.Open` and streams the bytes verbatim. On Windows, NTFS canonicalises the alternate-data-stream suffix `::$DATA`, a trailing dot, and a trailing space back to the underlying file, so the bytes returned are the real script source. The missing check: Algernon never rejects or canonicalises Windows-equivalent filenames before choosing a handler. ### Proof of concept **Setup** 1. Build Algernon from source on a Windows host: ```powershell git clone https://github.com/xyproto/algernon cd algernon git checkout v1.17.8 go build -o algernon.exe . ``` 2. Create a web root with a script that embeds secrets, exactly as a real handler would: ```powershell New-Item -ItemType Directory webroot | Out-Null Set-Content webroot\index.lua @' -- db = POSTGRES("postgres://app:S3cr3t@db/prod") SetCookieSecret("hardcoded-session-key") print("<h1>hello</h1>") '@ ``` 3. Serve the directory over plain HTTP with no auth backend (run in its own window): ```powershell .\algernon.exe --httponly --noninteractive --nodb --addr ':8088' --dir .\webroot ``` **Exploit** 1. Request the script normally. It executes, and the source is not disclosed: ```powershell curl.exe -s http://127.0.0.1:8088/index.lua ``` Expected: `<h1>hello</h1>`. The DSN and cookie secret are absent from the response. 2. Request the same script through its NTFS `::$DATA` stream. Algernon returns the raw source: ```powershell curl.exe -s --path-as-is 'http://127.0.0.1:8088/index.lua::$DATA' ``` Expected: HTTP 200, `Content-Type: application/octet-stream`, body is the verbatim Lua source including `SetCookieSecret("hardcoded-session-key")` and the Postgres DSN. 3. The trailing-dot and trailing-space forms leak the same source: ```powershell curl.exe -s --path-as-is 'http://127.0.0.1:8088/index.lua.' curl.exe -s --path-as-is 'http://127.0.0.1:8088/index.lua%20' ``` Expected: identical raw-source response for both. ### Impact - **Confidentiality:** Reads the verbatim source of any public-path server-side script, exposing hardcoded DB credentials, API keys, and `SetCookieSecret(...)` values. - **Authentication:** A disclosed `SetCookieSecret` value lets an unauthenticated attacker forge session cookies and log in as any user. ### Suggestions to fix > _This has not been tested - it is illustrative only._ Reject request paths whose final segment uses a Windows-equivalent form (alternate data stream, trailing dot, or trailing space) before extension dispatch. ```diff func (ac *Config) FilePage(w http.ResponseWriter, req *http.Request, filename, luaDataFilename string) { + // Reject Windows filename-equivalent forms that alias a different file + // than filepath.Ext sees (e.g. "x.lua::$DATA", "x.lua.", "x.lua "). + if base := filepath.Base(filename); strings.ContainsRune(base, ':') || + strings.HasSuffix(base, ".") || strings.HasSuffix(base, | GCVE Database | ||
| High | 8.2 | EPSS 0.3% | CVE-2026-48126xyproto algernon · Path Traversal High8.2EPSS 0.3%CVE Database V5CVE-2026-48126 Algernon is a small self-contained pure-Go web server. Prior to 1.17.8, when algernon is started with --domain (or --letsencrypt, which silently turns on --domain at engine/flags.go:372), the request handler resolves the served directory by joining the configured --dir with the value of the client-supplied Host header. The join is performed by filepath.Join with no validation, so a Host: .. header walks one level above the document root. Subsequent file resolution then exposes everything in that parent directory — arbitrary file read, full directory listing, and, if any .lua file is present, server-side Lua execution. This vulnerability is fixed in 1.17.8. | CVE Database V5 | ||
| Medium | 4.3 | EPSS 0.2% | Medium4.3EPSS 0.2%CVE Database V5CVE-2026-46431 Algernon is a small self-contained pure-Go web server. Prior to 1.17.7, the SSE event server's Access-Control-Allow-Origin response header was hardcoded to the wildcard * regardless of the caller's Origin. Because EventSource does not preflight and does not send cookies, the wildcard is sufficient to let any third-party page the developer visits open a cross-origin EventSource to the SSE port and read the live filename stream from JavaScript. This vulnerability is fixed in 1.17.7. | CVE Database V5 | ||
| Medium | 4.3 | EPSS 0.2% | CVE-2026-46430xyproto algernon · Resource Exposure Medium4.3EPSS 0.2%CVE Database V5CVE-2026-46430 Algernon is a small self-contained pure-Go web server. Prior to 1.17.7, the SSE event server bound to 0.0.0.0:5553 on Linux/macOS by default because the platform-dependent host default in engine/flags.go:39-46 set host = "" for non-Windows, and utils.JoinHostPort("", ":5553") resolves to ":5553". This vulnerability is fixed in 1.17.7. | CVE Database V5 | ||
| High | 7.5 | EPSS 0.3% | High7.5EPSS 0.3%CVE Database V5CVE-2026-45728 Algernon is a small self-contained pure-Go web server. Prior to 1.17.7, when Algernon is invoked with a single file path instead of a directory, singleFileMode is set to true and debugMode is forcibly enabled.debugMode activates the PrettyError renderer, which on any Lua or template error response dumps the absolute path of the file that errored, complete byte contents of that file, and exception or parser error text. This response is served with HTTP 200 OK to whoever sent the request that triggered the error. Any client able to reach the server and able to provoke a runtime error in the served script obtains the full server-side source of that script and of any sibling Lua data file consulted during the request. This vulnerability is fixed in 1.17.7. | CVE Database V5 | ||
| Critical | 9.0 | EPSS 0.4% | CVE-2026-45721xyproto algernon · Improper Input Validation Critical9.0EPSS 0.4%CVE Database V5CVE-2026-45721 Algernon is a small self-contained pure-Go web server. Prior to 1.17.7, when Algernon is asked for any URL path that resolves to a directory without an index file, DirPage walks upward through parent directories — past the configured server root — looking for a file named handler.lua to execute as the request handler. The loop terminates only after 100 ancestor steps or when filepath.Dir returns ., so on any absolute server-root path the search reaches the filesystem root (/ on Unix, drive letter on Windows). The first handler.lua it finds is loaded into the Lua interpreter with the full Algernon API exposed — including run3(), httpclient, os.execute, io.popen, PQ, MSSQL, raw filesystem access, and the userstate database. Any process that can write handler.lua anywhere in a parent directory of the server root obtains pre-authenticated remote code execution on the next HTTP request. This is reachable without authentication — the lookup happens before the permission check returns a hit (the perm system only gates URL prefixes, not the handler-resolution step), and any URL pointing at a directory without an index triggers the walk. On a fresh stock Algernon install the request GET / is enough. This vulnerability is fixed in 1.17.7. | CVE Database V5 | ||
| High | 8.7 | EPSS 0.3% | CVE-2026-43982xyproto algernon · Path Traversal High8.7EPSS 0.3%CVE Database V5CVE-2026-43982 Algernon is a small self-contained pure-Go web server. Prior to 1.17.6, uploadedFileSaveIn() in lua/upload/upload.go uses filepath.Join() with the caller-supplied directory but performs no boundary check after joining. A directory of ../../../tmp resolves cleanly to /tmp, outside the web root. This vulnerability is fixed in 1.17.6. | CVE Database V5 | ||
| Medium | 6.1 | EPSS 0.4% | CVE-2025-65754algernon · Cross-site Scripting Medium6.1EPSS 0.4%CVE Database V5CVE-2025-65754 Cross Site Scripting vulnerability in Algernon v1.17.4 allows attackers to execute arbitrary code via injecting a crafted payload into a filename. | CVE Database V5 |
Page 1 of 1