Skip to content

bug(websearch): Brave fetch follows redirects and can forward X-Subscription-Token cross-host #5409

Closed
davdittrich/gsd-core
#59

Description

@davdittrich

Pre-submission checklist

  • I have searched existing issues and this bug has not already been reported

GSD Version

1.16.0 (next @ 671aa6f)

Runtime

Claude Code

Operating System

Linux (Arch)

Node.js Version

v26.11.1

Shell

/bin/bash

Installation Method

Updated from a previous version

What happened?

The Brave branch of websearch (gsd-core/bin/lib/commands.cjs, ~line 2982) calls fetch('https://api.search.brave.com/res/v1/web/search?…', { headers: { 'X-Subscription-Token': apiKey }, … }) with no redirect option. The default is redirect: 'follow', and Node's fetch does not strip custom headers such as X-Subscription-Token on a cross-host redirect. If api.search.brave.com answers 3xx to another host, the API key is sent to that host.

What did you expect?

The API key is only ever sent to api.search.brave.com. A 3xx is treated as a failure and nothing is re-sent.

Steps to reproduce

Node v26.11.1 shows the header forwarding; the loopback script uses two local servers, not Brave itself:

  1. Start server T on a port that logs req.headers['x-subscription-token'].
  2. Start server R that answers 307 Location: http://localhost:<T>/x.
  3. await fetch('http://127.0.0.1:<R>/', { headers: { 'X-Subscription-Token': 'SECRET' } }).
  4. T logs SECRET.

Error output / logs

T got token: SECRET

How often does this happen?

Every time a redirect to another host occurs (the fixed Brave endpoint does not redirect today, so this is a hardening gap, not an observed leak)

Impact

Low — Hardening: requires the Brave endpoint (or a path in front of it) to redirect cross-host.

Workaround (if any)

None needed today.

Additional context

Found while reviewing the fix for #5288 (reviewer-lane HTTP seam following redirects). Same class of defect, different call site, independent change. Suggested fix: pass redirect: 'manual' and treat any 3xx as a failed attempt.

Activity

  1. trek-e commented on Oct 14, 2026

    @trek-e
    Collaborator

    This was generated by AI during triage.

    Diagnosis

    Reproduced: yes (mechanism) — on Node v26.11.1 a custom header survives a cross-host 307 under default redirect: 'follow'; with redirect: 'manual' the 307 is returned and nothing is re-sent. Not an observed leak: the Brave endpoint does not redirect today, so this is hardening.
    Root cause: src/commands.cts:3278-3288 (cmdWebsearch) sends X-Subscription-Token with the default redirect policy
    Introduced by: long-standing
    Blast radius: low — one caller, tests in tests/commands.test.cjs
    Coupled (must not miss): the retry loop treats 429/5xx as retryable; a 3xx must be a non-retryable failure, not a retry. Sibling of the reviewer-lane HTTP fix for #5288.
    Regression test: a server answering 3xx to another host yields available: false with an error, issues no second request, and the token never reaches the second host

    Evidence note: the graph tools (Memtrace) were unavailable for this sweep; the located symbol and line were read directly from next @ 72730d8 and the symptom reproduced against that tree.

    Agent Brief

    Category: bug
    Summary: the Brave search API key must never be sent to any host other than the Brave API host
    Current behavior: a redirect from the search endpoint is followed and the subscription token header is forwarded to the redirect target
    Desired behavior: any redirect response is treated as a failed attempt; nothing is re-sent
    Key interfaces: the websearch command's Brave request and its failure output
    Acceptance criteria: a 3xx response produces the standard unavailable/error result without a follow-up request; 200, 429/5xx retry behavior and the no-key skip are unchanged
    Out of scope: other HTTP call sites, changing the retry policy

  2. added
    bugSomething isn't working
    and removed
    needs-triageNew issue awaiting maintainer review
    on Oct 14, 2026
  3. added 3 commits that reference this issue on Oct 15, 2026
    00be349
    4336d73
    e260015
  4. added a commit that references this issue on Oct 18, 2026
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

    bugSomething isn't workingconfirmed-bugVerified reproducible bug

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions