Every CVE Search/ Get API key

CVE-2026-108372

LoopX serve-status permissive CORS policy exposes control-plane state and private Markdown files

7.1 HighFix available

Open and provider-neutral, LoopX is a lightweight state kernel and local-first control plane for loop engineering. From version 0.0.1 until 0.4.5, loopx serve-status in loopx/status_server.py uses StatusRequestHandler._send_json() and do_OPTIONS() to send Access-Control-Allow-Origin: * for unauthenticated responses on predictable loopback ports. A remote attacker can cause an operator who visits attacker-controlled web content to issue cross-origin requests to GET /status.json, GET /review-material?goal_id=...&path=..., GET /, and POST /control-plane/configure-goal/dry-run, exposing control-plane state, goal and run information, local absolute paths, private Markdown files below registered goal roots, and dry-run configuration projections. The affected routes do not provide cross-origin writes or arbitrary-file access. This issue is fixed in version 0.4.5.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
None
User interaction
Passive

What an attacker can affect

Confidentiality
High
Integrity
None
Availability
None

Exploit likelihood

0.30%Up 0.1 points since Oct 17, 2026
90%50%10%1%

What each source reports

CVE List Score used 7.1

LoopX serve-status permissive CORS policy exposes control-plane state and private Markdown files

Open and provider-neutral, LoopX is a lightweight state kernel and local-first control plane for loop engineering. From version 0.0.1 until 0.4.5, loopx serve-status in loopx/status_server.py uses StatusRequestHandler._send_json() and do_OPTIONS() to send Access-Control-Allow-Origin: * for unauthenticated responses on predictable loopback ports. A remote attacker can cause an operator who visits attacker-controlled web content to issue cross-origin requests to GET /status.json, GET /review-material?goal_id=...&path=..., GET /, and POST /control-plane/configure-goal/dry-run, exposing control-plane state, goal and run information, local absolute paths, private Markdown files below registered goal roots, and dry-run configuration projections. The affected routes do not provide cross-origin writes or arbitrary-file access. This issue is fixed in version 0.4.5.

Record
CVE-2026-108372
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-346, CWE-942
Updated
2026-11-02
EUVDEUVD-2026-103186 7.1

Open and provider-neutral, LoopX is a lightweight state kernel and local-first control plane for loop engineering. From version 0.0.1 until 0.4.5, loopx serve-status in loopx/status_server.py uses StatusRequestHandler._send_json() and do_OPTIONS() to send Access-Control-Allow-Origin: * for unauthenticated responses on predictable loopback ports. A remote attacker can cause an operator who visits attacker-controlled web content to issue cross-origin requests to GET /status.json, GET /review-material?goal_id=...&path=..., GET /, and POST /control-plane/configure-goal/dry-run, exposing control-plane state, goal and run information, local absolute paths, private Markdown files below registered goal roots, and dry-run configuration projections. The affected routes do not provide cross-origin writes or arbitrary-file access. This issue is fixed in version 0.4.5.

Record
EUVD-2026-103186
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-vx2m-gpq4-8j5q 7.1

LoopX serve-status permissive CORS policy exposes control-plane state and private Markdown files

Summary

loopx serve-status sends Access-Control-Allow-Origin: * on every response, including its unauthenticated GET routes. Because the endpoint is unauthenticated and bound to a fixed, well-known loopback port, any website the operator visits can read LoopX control-plane state and the contents of Markdown files inside every registered goal repo, using a plain fetch() with no user interaction.

The write endpoints are not affected — they are correctly Origin-checked. This report is strictly about the read paths.

Impact

While loopx serve-status is running, a page on any origin can run:

// no interaction, no preflight, no credentials needed
const s = await (await fetch('http://127.0.0.1:8766/status.json')).json();
for (const g of s.goals ?? []) {
  const m = await (await fetch(
    `http://127.0.0.1:8766/review-material?goal_id=${g.id}&path=design-notes.md`
  )).json();
  exfiltrate(m.content);
}

and read back:

  • GET /status.json — the whole status projection: goal ids, run history, and local absolute paths (registry, runtime_root). With --global-registry this covers every project registered on the machine, not just one.
  • GET /review-material?goal_id=…&path=… — the content of any .md/.markdown file under a goal's repo root, state dir, or runtime dir. In practice that is the operator's private design notes, handoff docs, and evidence write-ups.
  • GET / — endpoint discovery, including which write APIs are enabled.
  • POST /control-plane/configure-goal/dry-run — this route has no Origin check at all, and do_OPTIONS returns Access-Control-Allow-Headers: Content-Type, so a cross-origin JSON POST passes preflight. The response leaks the before/after control-plane configuration for a goal. (It does not write — I verified written=false.)

docs/public-private-boundary.md classifies "local absolute paths", "task ids", "raw logs and metrics" and "active goal state that reveals current user context" as material that must stay private. All of it is currently readable by any origin.

Two things make this more than theoretical:

  1. Fixed default ports. 8765 (project) and 8766 (global) are hardcoded defaults, so an attacker page can simply try both — no discovery step needed.
  2. It is documented as a persistent service. docs/guides/getting-started.md documents loopx serve-status, and scripts/macos-dashboard-launchagent.sh installs a LaunchAgent that keeps serve-status --global-registry --port 8766 running after every login. For anyone following that guide, this is an always-on, machine-wide read surface.

I have graded this High on the basis of zero-interaction exfiltration of private local material from a persistent service. It is read-only and scope-limited, so if you read the exposure as lower I would not argue the point — you know your users' setups better than I do.

Location

loopx/status_server.py:136-146 — StatusRequestHandler._send_json():

def _send_json(self, payload: dict[str, Any], *, status: int = 200) -> None:
    ...
    self.send_header("Access-Control-Allow-Origin", "*")   # <-- line 142

_send_json is the single response path for all routes, so the wildcard is applied to /status.json, /review-material, /healthz, / and the dry-run POSTs alike. do_OPTIONS (:148-153) repeats it.

Confirmed present at main HEAD (b0c8e315, blob c9e53d1a) at the time of writing.

What is NOT affected (I checked, so you don't have to)

I tried to refute this before reporting. These controls all hold:

  • Writes are properly defended. With --enable-reward-write-api and --enable-control-plane-write-api both on, POST /reward/append and POST /control-plane/configure-goal/apply return 403 "only accepts loopback browser origins" for a foreign Origin. is_loopback_origin() does its job. There is no …
Record
GHSA-vx2m-gpq4-8j5q
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-346, CWE-942
Updated
2026-11-02
NVD 7.1

Open and provider-neutral, LoopX is a lightweight state kernel and local-first control plane for loop engineering. From version 0.0.1 until 0.4.5, loopx serve-status in loopx/status_server.py uses StatusRequestHandler._send_json() and do_OPTIONS() to send Access-Control-Allow-Origin: * for unauthenticated responses on predictable loopback ports. A remote attacker can cause an operator who visits attacker-controlled web content to issue cross-origin requests to GET /status.json, GET /review-material?goal_id=...&path=..., GET /, and POST /control-plane/configure-goal/dry-run, exposing control-plane state, goal and run information, local absolute paths, private Markdown files below registered goal roots, and dry-run configuration projections. The affected routes do not provide cross-origin writes or arbitrary-file access. This issue is fixed in version 0.4.5.

Record
CVE-2026-108372
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Weakness
CWE-346, CWE-942
Updated
2026-11-02
OSV 7.1

LoopX serve-status permissive CORS policy exposes control-plane state and private Markdown files

Open and provider-neutral, LoopX is a lightweight state kernel and local-first control plane for loop engineering. From version 0.0.1 until 0.4.5, loopx serve-status in loopx/status_server.py uses StatusRequestHandler._send_json() and do_OPTIONS() to send Access-Control-Allow-Origin: * for unauthenticated responses on predictable loopback ports. A remote attacker can cause an operator who visits attacker-controlled web content to issue cross-origin requests to GET /status.json, GET /review-material?goal_id=...&path=..., GET /, and POST /control-plane/configure-goal/dry-run, exposing control-plane state, goal and run information, local absolute paths, private Markdown files below registered goal roots, and dry-run configuration projections. The affected routes do not provide cross-origin writes or arbitrary-file access. This issue is fixed in version 0.4.5.

Record
CVE-2026-108372
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-346, CWE-942
Updated
2026-11-02
OSVGHSA-vx2m-gpq4-8j5q 7.1

LoopX serve-status permissive CORS policy exposes control-plane state and private Markdown files

Summary

loopx serve-status sends Access-Control-Allow-Origin: * on every response, including its unauthenticated GET routes. Because the endpoint is unauthenticated and bound to a fixed, well-known loopback port, any website the operator visits can read LoopX control-plane state and the contents of Markdown files inside every registered goal repo, using a plain fetch() with no user interaction.

The write endpoints are not affected — they are correctly Origin-checked. This report is strictly about the read paths.

Impact

While loopx serve-status is running, a page on any origin can run:

// no interaction, no preflight, no credentials needed
const s = await (await fetch('http://127.0.0.1:8766/status.json')).json();
for (const g of s.goals ?? []) {
  const m = await (await fetch(
    `http://127.0.0.1:8766/review-material?goal_id=${g.id}&path=design-notes.md`
  )).json();
  exfiltrate(m.content);
}

and read back:

  • GET /status.json — the whole status projection: goal ids, run history, and local absolute paths (registry, runtime_root). With --global-registry this covers every project registered on the machine, not just one.
  • GET /review-material?goal_id=…&path=… — the content of any .md/.markdown file under a goal's repo root, state dir, or runtime dir. In practice that is the operator's private design notes, handoff docs, and evidence write-ups.
  • GET / — endpoint discovery, including which write APIs are enabled.
  • POST /control-plane/configure-goal/dry-run — this route has no Origin check at all, and do_OPTIONS returns Access-Control-Allow-Headers: Content-Type, so a cross-origin JSON POST passes preflight. The response leaks the before/after control-plane configuration for a goal. (It does not write — I verified written=false.)

docs/public-private-boundary.md classifies "local absolute paths", "task ids", "raw logs and metrics" and "active goal state that reveals current user context" as material that must stay private. All of it is currently readable by any origin.

Two things make this more than theoretical:

  1. Fixed default ports. 8765 (project) and 8766 (global) are hardcoded defaults, so an attacker page can simply try both — no discovery step needed.
  2. It is documented as a persistent service. docs/guides/getting-started.md documents loopx serve-status, and scripts/macos-dashboard-launchagent.sh installs a LaunchAgent that keeps serve-status --global-registry --port 8766 running after every login. For anyone following that guide, this is an always-on, machine-wide read surface.

I have graded this High on the basis of zero-interaction exfiltration of private local material from a persistent service. It is read-only and scope-limited, so if you read the exposure as lower I would not argue the point — you know your users' setups better than I do.

Location

loopx/status_server.py:136-146 — StatusRequestHandler._send_json():

def _send_json(self, payload: dict[str, Any], *, status: int = 200) -> None:
    ...
    self.send_header("Access-Control-Allow-Origin", "*")   # <-- line 142

_send_json is the single response path for all routes, so the wildcard is applied to /status.json, /review-material, /healthz, / and the dry-run POSTs alike. do_OPTIONS (:148-153) repeats it.

Confirmed present at main HEAD (b0c8e315, blob c9e53d1a) at the time of writing.

What is NOT affected (I checked, so you don't have to)

I tried to refute this before reporting. These controls all hold:

  • Writes are properly defended. With --enable-reward-write-api and --enable-control-plane-write-api both on, POST /reward/append and POST /control-plane/configure-goal/apply return 403 "only accepts loopback browser origins" for a foreign Origin. is_loopback_origin() does its job. There is no …
Record
GHSA-vx2m-gpq4-8j5q
CVSS 4.0
7.1 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-346, CWE-942
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
loopx
PyPI
0.0.1 up to 0.4.5
0.4.5

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-108372 is a high vulnerability (CVSS 7.1) in Huangruiteng Loopx, published October 16, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.30%, the 23rd percentile. Fixed in loopx 0.4.5.

References

This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open