Skip to content

design: Altimate Base HTTP registration route isn't bound to real consent (no live exploitation path today) #1283

Description

@sahrizvi

Summary

POST /altimate/base/register (added in #1266 to let non-TUI hosts like the VS Code extension trigger Altimate Base) accepts a hash that any caller can compute locally without ever fetching or displaying the actual disclosure text — so any local process that can reach altimate serve's HTTP port can register Altimate Base, mint a persistent per-install tracking identifier, and opt the install into request/response logging, without the consent dialog ever being shown.

Recalibration (see discussion below): this was initially filed as a security P0 ("any attacker can bypass consent"). On review, that framing overstated it — reaching this endpoint already requires local code execution, and an attacker at that privilege level can automate the TUI dialog just as easily as forming an HTTP POST, and has far more valuable things to do with that access than silently register a free model tier. There is no meaningful capability gained by a malicious local actor that they didn't already have.

The real, narrower issue: the consent gate is a UX affordance, not a security boundary (its own code comment in host.ts says so), and #1266 exposes a route that lets any co-resident, non-malicious process — a future Altimate integration, a CI job, some other local tool — flip a privacy-relevant switch invisibly, with no way for the user to see or decline it. That matters for the feature's informed-consent posture even though nothing malicious is implied. As of today this is a latent design gap, not an active exploit — no shipping client calls this route yet (confirmed: zero references to it in sdks/vscode).

Root cause (verified by tracing the code)

  • packages/opencode/src/server/server.tsPOST /altimate/base/register calls FreeTierHost.registerWithAcceptedDisclosure(acceptedDisclosureSha256).
  • packages/opencode/src/altimate/free/host.ts:79-88 — this checks the supplied hash against FreeTierConsent.disclosureHash().
  • packages/core/src/altimate-base-disclosure.ts:15-16disclosureHash() is a SHA-256 of a public, hardcoded string. Any caller can compute this locally without ever calling GET /altimate/base/disclosure, let alone rendering it to a person.

There is no session/UI-provenance check, no interactive-confirmation flag, nothing binding the POST to a human having actually read anything.

Proposed fix (scoped, <30 min, no design work)

Bind POST /altimate/base/register to a prior GET /altimate/base/disclosure call from the same caller via a short-lived, single-use nonce that must be echoed back on the register call. This doesn't prove a human read the screen, but it closes the "compute the public hash and skip the GET entirely" gap for any non-malicious co-resident caller, which is the actual risk here.

Provenance

Found during the v0.11.0 stable release's Chaos Gremlin (privacy/compliance) persona review. Deliberately shipped in v0.11.0 as an explicit, user-reviewed risk acceptance (recalibrated to a design-gap/P1, not a blocking security P0) — recorded here so the fix isn't lost.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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