Severity by source
AV:A/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
Primary rating from GitHub Advisory · only source for this CVE.
CVSS VectorGitHub Advisory
CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
Lifecycle Timeline
2DescriptionGitHub Advisory
Summary
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" - a Go http.Server.Addr of ":5553" listens on every interface. On Windows the same code chose "localhost", binding loopback only.
The result was a platform split where the OS Algernon's dev workflow is most often used on (Linux/macOS) got the network-exposed default, and only Windows users got the loopback-safe one. A LAN peer with no developer interaction could connect to <dev-laptop-ip>:5553 and read the file-change stream.
This advisory covers the bind-address default in isolation. The fix is independent of authentication (#2a) and CORS (#2b) - switching the default to loopback can be done without touching either.
Details
Root cause - platform-dependent host default in handleFlags
// engine/flags.go:39-46 (1.17.6)
host := ""
if runtime.GOOS == "windows" {
host = "localhost"
// Default Bolt database file
ac.defaultBoltFilename = filepath.Join(serverTempDir, "algernon.db")
// Default log file
ac.defaultLogFile = filepath.Join(serverTempDir, "algernon.log")
}// engine/config.go:388-391 (1.17.6, finalConfiguration)
if ac.eventAddr == "" {
ac.eventAddr = utils.JoinHostPort(host, ac.defaultEventColonPort)
}Result tabulated:
| Platform | host | eventAddr after JoinHostPort | Effective bind |
|---|---|---|---|
| Linux | "" | ":5553" | 0.0.0.0:5553 (all interfaces) |
| macOS | "" | ":5553" | 0.0.0.0:5553 (all interfaces) |
| Windows | "localhost" | "localhost:5553" | 127.0.0.1:5553 (loopback) |
The same host value also governs the main web server bind, so the platform split affects both ports. The web-server bind on Linux/macOS is a separate (defensible) design decision - a server intended to be reachable; the SSE port is *not* such a service and inherited the same default by accident.
Why this is an independent finding
The fix is a single line: change the default host value, or change the eventAddr default specifically, to "localhost" regardless of platform. No change to authentication or CORS is required to close the network-reach half of the original bundled advisory. A LAN peer can no longer connect - the listener is unreachable from another host - even if the SSE handler still has no authentication and still returns Allow-Origin: *.
PoC (against 1.17.6 on Linux/macOS)
# Operator's laptop on a hotel/cafe/office WiFi:
algernon -a /path/to/project
# => SSE listener bound to 0.0.0.0:5553
# Any peer on the same subnet:
$ curl -sN http://<dev-laptop-ip>:5553/sse
id: 0
data: /path/to/project/secret-notes.md
id: 1
data: /path/to/project/.env.localNo interaction from the developer is required. The peer needs network reach and nothing else.
Impact
- Confidentiality: medium. LAN-bounded continuous information disclosure of filenames and edit timing.
- Integrity: none.
- Availability: none directly.
The CVSS vector uses AV:A (adjacent network) to model the LAN-only reach. The vector for a misconfigured deployment behind a NAT-less or routed network would shift to AV:N and rise to 5.3.
Suggestions to fix
Primary fix - pick localhost as the SSE default on every platform.
// engine/flags.go -- platform-independent default for the event listener
// (keep the existing platform split for the WEB server if desired, but
// not for the event server)
host := "localhost"Or, more surgically:
// engine/config.go -- finalConfiguration
if ac.eventAddr == "" {
ac.eventAddr = utils.JoinHostPort("localhost", ac.defaultEventColonPort)
}An operator who genuinely wants LAN-reachable SSE can pass --eventserver 0.0.0.0:5553 explicitly and accept the consequences.
Stronger fix - eliminate the second listener entirely. Mount the SSE handler on the main mux at /sse. The bind address is then whatever the main server uses; there is no second listener and therefore no second bind-address default to get wrong.
Live verification
Audit-host bind check (Windows 10):
$ netstat -an | findstr 5553
TCP 127.0.0.1:5553 0.0.0.0:0 LISTENINGConfirms the Windows default is localhost. The Linux/macOS bind to 0.0.0.0:5553 is documented in the code path above; it was not exercised on the audit machine because the audit host was Windows. A maintainer reproducing on a Linux host would see 0.0.0.0:5553 LISTENING from ss -tlnp.
AnalysisAI
Algernon's auto-refresh SSE event server unintentionally exposes developer file-change streams to unauthenticated LAN peers on Linux and macOS due to a platform-dependent bind address default that was never intended to reach adjacent hosts. On non-Windows platforms, the SSE listener resolves to 0.0.0.0:5553 (all interfaces), while Windows correctly binds to 127.0.0.1:5553 - a silent asymmetry introduced in engine/flags.go that leaves developers on the most common Algernon platforms exposed whenever they work on shared networks. A publicly available proof-of-concept demonstrates that any host on the same subnet can enumerate project filenames and edit timing with a single unauthenticated curl command, with no developer interaction required; no public exploit identified at time of analysis rises to confirmed active exploitation (not in CISA KEV).
Technical ContextAI
Algernon is a Go-based web server (pkg:go/github.com_xyproto_algernon) that provides an auto-refresh feature via a Server-Sent Events (SSE) endpoint, typically used during local development to push file-change notifications to the browser. The root cause is classified under CWE-668 (Exposure of Resource to Wrong Sphere): in engine/flags.go lines 39-46, the variable host is initialized to an empty string on non-Windows platforms and to 'localhost' on Windows. This value flows into engine/config.go (finalConfiguration) where utils.JoinHostPort('', ':5553') produces the string ':5553'. In Go's net/http package, an http.Server.Addr of ':5553' instructs the runtime to listen on every available network interface (0.0.0.0:5553), not just loopback. The SSE endpoint at /sse then streams file-change events - including full project-relative file paths - to any TCP client that connects. The same host variable governs the main web server bind address, but exposing the main server to a LAN is arguably intentional for a web server; exposing the SSE event port is not, making this an unintended resource exposure rather than a deliberate design choice.
RemediationAI
Upgrade Algernon to version 1.17.7, which is confirmed as the patched release per the GitHub advisory GHSA-gj84-924c-48fx. The fix changes the SSE event server bind address default to 'localhost' on all platforms, either by updating the host default in engine/flags.go or by hardcoding 'localhost' in the eventAddr assignment in engine/config.go, so that the SSE listener is never reachable from outside the local machine without explicit operator configuration. Full advisory details are at https://github.com/xyproto/algernon/security/advisories/GHSA-gj84-924c-48fx. If an immediate upgrade is not feasible, a compensating control is to explicitly pass the --eventserver flag with a loopback address (e.g., --eventserver 127.0.0.1:5553) on every Algernon invocation; this overrides the default and restricts the SSE listener to loopback without touching authentication or CORS. A host-based firewall rule blocking inbound connections to TCP port 5553 from non-loopback sources is an alternative control with no application-level side effects, but requires firewall management discipline and does not fix the root cause. Developers who genuinely require LAN-accessible SSE can pass --eventserver 0.0.0.0:5553 explicitly after upgrading, accepting the network exposure as a deliberate choice.
Same weakness CWE-668 – Exposure of Resource to Wrong Sphere
View allSame technique Information Disclosure
View allShare
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-31869
GHSA-gj84-924c-48fx