Skip to content

Bridge HTTP API is fully open when api.secret is unset, and the secret travels in query strings #364

Description

@EvolveAegis

Summary

The bridge HTTP server (src/api/http-server.ts) gates all of /api/* behind a single local secret — but only when one is configured:

  • http-server.ts:139const host = secret ? '0.0.0.0' : '127.0.0.1';
  • http-server.ts:311 — the entire auth block is inside if (secret && !isPublicHealth && !url.startsWith('/api/files/')).

So with api.secret unset (the default in the example config), every API — /api/talk, /api/tasks, bot CRUD, /api/skills/*/publish, peers — is open on loopback with no credential at all. The routes behind it include the chat/talk surface and the skill publish path (the skill-hub issue), so any local process (or a browser page reaching localhost) can talk to agents and publish skills.

When a secret IS set, two secondary issues:

  • the server binds 0.0.0.0 and every consumer shares one secret (no per-bot credentials);
  • the secret is accepted as a query parameter (http-server.ts:316-318 token=), so it ends up in access logs, browser history, and any URL logging.

/api/files/ is exempt from auth entirely (:311), serving files by opaque URL.

Details

  • http-server.ts:138-139 — host selection: loopback only when secret is absent.
  • http-server.ts:311-340 — auth applies only if (secret && ...); without secret, all routes pass through.
  • http-server.ts:316-321token= query-param auth path (URL logging exposure).
  • Routes behind it: /api/talk, /api/tasks, bots CRUD, /api/skills/:name/publish (skill-routes.ts:46-88), peers.

How to reproduce

# run the bridge with api.secret unset (default config):
curl -X GET http://127.0.0.1:<port>/api/bots          # -> 200, no credentials
curl -X POST http://127.0.0.1:<port>/api/talk \
  -H 'Content-Type: application/json' -d '{"message": "run this command: ..."}'
# -> 200, agent executes

Impact

With the default (no secret) config, any local process can drive the full bridge API including agent talk and skill publish. With a secret, it's a single shared secret on the LAN, transmitted in query strings.

Suggested change

  • Require a secret (or reject startup without one), and keep loopback binding when unset.
  • Move the token out of the query string (header-only).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions