🛡️ GHSA-9v4j-7g44-qcqw — algernon
Description
Algernon: Auto-refresh SSE event server binds to all interfaces with Access-Control-Allow-Origin: * and no authentication
Summary
When auto-refresh is enabled, Algernon spins up an SSE handler that streams a data: line for every filesystem event under the watched directory. The handler performs no authentication of any kind — no shared token, no cookie check against the permissions2 userstate, no IP allow-list, no path-prefix permission. Any client that can complete a TCP connection to the listener address receives the stream.
This advisory covers the authentication gap in isolation. The cross-origin browser-reach (advisory #2b) and the network-reach (advisory #2c) amplify the impact, but each is independently fixable; this finding addresses the case where a same-origin or LAN-local client connects directly to the SSE port and reads the stream without proving anything about its identity.
Details
Root cause — the SSE handler does not consult permissions2 or any other auth
```go
// vendor/github.com/xyproto/recwatch/eventserver.go:100-144 (1.17.6)
func GenFileChangeEvents(events TimeEventMap, mut *sync.Mutex, maxAge time.Duration, allowed string) http.HandlerFunc {
return func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "text/event-stream;charset=utf-8")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
w.Header().Set("Access-Control-Allow-Origin", allowed)
// ... loop emits one SSE record per filename touched ...
}
}
```
Note the handler signature: func(w http.ResponseWriter, _ *http.Request). The request is discarded — no Cookie, Authorization, query-string, or remote-IP check is performed before the stream begins.
In 1.17.6 the listener was placed on its own http.ServeMux ([recwatch/eventserver.go:200-215](../vendor/github.com/xyproto/recwatch/eventserver.go)), wholly outside the perm.Rejected middleware chain that gates Algernon's main HTTP listener. Even an operator who had configured admin/user path prefixes via perm.AddAdminPath, set a cookieSecret, and forced authentication on every URL of the main server had no way to gate this listener — it was unreachable from the mux argument the perm middleware uses.
Why authentication matters for this listener
The stream contents are not public data. They reveal:
- Which files the developer is actively editing, with sub-second timing precision.
- The existence of files inside the watched root (including files the operator may have meant to keep private —
.env.local,secrets.lua, in-progress draft files). - By inference, the directory layout of the project.
A client that can connect to the listener obtains a low-rate continuous information disclosure for the lifetime of the connection. The handler is an infinite for {} loop — there is no natural session boundary or expiry.
Source-level evidence
```text
$ rg -n 'GenFileChangeEvents|EventServer\(' vendor/github.com/xyproto/recwatch/
vendor/github.com/xyproto/recwatch/eventserver.go:101:func GenFileChangeEvents(events TimeEventMap, mut *sync.Mutex, maxAge time.Duration, allowed string) http.HandlerFunc {
vendor/github.com/xyproto/recwatch/eventserver.go:177:func EventServer(path, allowed, eventAddr, eventPath string, refreshDuration time.Duration) {
$ rg -n 'Cookie|Authorization|Token|state\.User' vendor/github.com/xyproto/recwatch/eventserver.go
# zero matches — no authentication primitive is referenced anywhere in the file
```
PoC (against 1.17.6)
```bash
# 1. Operator runs algernon with auto-refresh on a project directory:
algernon -a /path/to/project # spins up :5553 on Linux/macOS, localhost:5553 on Windows
# 2. Any client that can reach the listener connects without credentials:
curl -sN http://<server>:5553/sse
# => id: 0
# data: /path/to/project/secret-notes.md
#
# id: 1
# data: /path/to/project/.env.local
```
No Cookie, no Authorization, no X-Token, no preflight, no challenge. The connection succeeds and the stream is delivered for as long as the client keeps the socket open.
Impact
- Confidentiality: medium. Continuous information disclosure of filenames and edit timing to anyone who can connect.
- Integrity: none.
- Availability: low. Each connection consumes a goroutine indefinitely; many simultaneous connections can exhaust descriptors.
Suggestions to fix
Primary fix — require a shared secret on the SSE endpoint. The auto-refresh feature already injects a script into served HTML ([engine/sse.go:118-165](../engine/sse.go)); that script knows the SSE URL. Add a per-startup token, embed it in the injected JS, and require it on the SSE request:
```go
// engine/sse.go -- in InsertAutoRefresh
tmplData.SessionToken = ac.sseToken // generated once at startup, e.g. crypto/rand 32 bytes
// JS:
// var source = new EventSource('...?token={{.SessionToken}}');
// recwatch han
How this vulnerability can be exploited
This issue can be reached over the network, attack complexity is low, an attacker needs no privileges on the target. No user interaction is required. The scope is unchanged, so the impact stays within the vulnerable component. Rated impact: confidentiality low, integrity none, availability none.
Weakness class
GHSA-9v4j-7g44-qcqw is classified as CWE-1188: Insecure Default Initialization of Resource. Default settings are insecure, so an installation is exposed until someone changes them.
Affected software
GHSA-9v4j-7g44-qcqw is recorded against 1 package.
- github.com/xyproto/algernon
Timeline and source
Published on 19 May 2026 and last revised on 25 June 2026. No public exploit is currently recorded for this entry. Record sourced from OSV.
References
Details
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
Affected Packages
| Software | From version | Fixed in |
|---|---|---|
| github.com/xyproto/algernon | — | — |
References
Similar Threats
- High CVE-2026-52792
- Unknown GO-2026-5300
- High CVE-2026-48126
- Medium CVE-2026-46430
- Medium CVE-2026-46431
Free Vulnerability Check
Is your site affected by GHSA-9v4j-7g44-qcqw?
BotEraser helps you identify potentially vulnerable plugins and themes by checking your installation against GHSA-9v4j-7g44-qcqw and other known CVE records.
Scan My Site Free →No credit card required · Results in minutes
ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.