splunk / splunk/token-meter

Loopback HTTP API publishes the action token and does not pin Host

Open
#34 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
94
Forks
16
Avg merge
1d 12h
Merged PRs (30d)
5

Description

Summary

The Token Meter server binds 127.0.0.1:8722 with no authentication. Mutation POSTs are gated by X-Token-Meter-Action, but that token is generated once at process start and then returned in ordinary GET JSON (/state, /session, /updates/status, /menubar).

GET handlers do not check Host or Origin. POST origin checks are skipped when the Origin header is absent:

origin = self.headers.get("Origin") or ""
if origin and (urlparse(origin).hostname or "") not in ("localhost", "127.0.0.1", "::1"):
    ...

Impact

Anyone who can speak to loopback can read session metadata, costs, project names, and the live action token, then call:

  • POST /session/delete (move discovered .jsonl logs to Trash)
  • POST /agent-access/toggle (add/remove the tokenmeter MCP entry in Codex/Claude)
  • POST /capability/toggle and /capability/disable-unused (rewrite Claude/Codex plugin settings)
  • POST /updates/install (fetch + reinstall)

That includes every local UID on a shared machine (curl 127.0.0.1:8722/state). DNS rebinding is also in play because there is no Host allowlist: GET is enough to exfiltrate the token and dashboard JSON. Classic HTML-form CSRF is mostly blocked by the JSON content-type requirement; local processes and rebinding pages are not.

Where

  • token_meter/web/server.py (bind address)
  • token_meter/app.py (_ACTION_TOKEN, software_update_status, do_GET / do_POST)
  • menubar_software_update() also copies the token into /menubar

Suggested fix

  1. Reject any request whose Host is not 127.0.0.1, localhost, or ::1 (with optional port).
  2. Do not put the action token on unauthenticated GET JSON. Prefer a SameSite=Strict cookie, or a one-time bootstrap that is not readable cross-origin.
  3. Require a loopback Origin on browser POSTs; do not treat a missing Origin as trusted.
  4. Add Content-Security-Policy: frame-ancestors 'none' and X-Frame-Options: DENY so the dashboard cannot be clickjacked.

Happy to send a patch against a fork if useful.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading token_meter/web/server.py and the _ACTION_TOKEN, software_update_status, do_GET, do_POST, and menubar_software_update paths in token_meter/app.py. Trace how Host, Origin, GET JSON, and mutation requests are handled. Done means loopback host and browser-origin checks are enforced, GET responses no longer expose the action token, and the stated security headers protect the dashboard.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, backend, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.