Skip to content

fix(codemode): CodeMode fetch response headers read through the prototype chain — get("constructor") returns the Object function and has("__proto__") is true for headers the server never sent #805

Closed

Description

@dezren39

Problem

The CodeMode fetch shim's response-headers wrapper reads a plain object without own-property checks, so a header that is absent can read as present when its (lowercased) name collides with an Object.prototype member.

packages/core/src/codemode/web.ts:23,32-33:

const headers = Object.fromEntries(response.headers)
// ...
get: (name: string) => headers[name.toLowerCase()] ?? null,
has: (name: string) => name.toLowerCase() in headers,

Object.fromEntries produces a plain object with Object.prototype in its chain, and neither get ([] lookup) nor has (in operator) distinguishes own from inherited properties. The lookup is lowercased first, so the affected names are the all-lowercase prototype members: constructor and __proto__.

Verified evidence (main f58571a052, laptop probe of the real module)

Probe: ~/git/fleet/probe-2026-10-28-creator-opencode2/probe-codemode-headers.ts (laptop) — imports the real extension from packages/core/src/codemode/web.ts, calls its fetch global against a local Bun.serve server whose response carries only content-type: text/plain and x-real: yes:

  • headers.get("content-type") → "text/plain", has → true (correct)
  • headers.get("x-real") → "yes", has → true (correct)
  • headers.get("x-absent") → null, has → false (correct)
  • headers.get("constructor") → function Object() { [native code] } (a function, not a string/null), has("constructor") → true — header is absent
  • headers.get("__proto__") → [object Object] (an object), has("__proto__") → true — header is absent

(toString / hasOwnProperty / valueOf are not affected only because the lookup lowercases them to non-member spellings — tostring, hasownproperty, valueof.)

Why it matters

This fetch is the web surface exposed to CodeMode programs (model-written scripts executed via the extension at web.ts:38). A script that branches on headers.has("constructor"), or that reads a header value and treats it as a string (get(...) is typed/expected to return string | null), instead receives the Object constructor function or Object.prototype — a truthy non-string that poisons downstream string operations and presence checks, for a header the server never sent. The real Headers API this shim mirrors returns null / false for every absent name.

Fix direction

Make the wrapper own-property-safe: build the record with a null prototype (Object.assign(Object.create(null), …) / Object.fromEntries into a null-prototype object) or guard both accessors with Object.hasOwn(headers, key). Keep entries() behavior unchanged.

Acceptance criteria

  • For a response without those headers, headers.get("constructor") and headers.get("__proto__") return null, and headers.has(...) returns false for both.
  • Headers actually sent (including, if a server sends one, a header literally named constructor) still return their real string values from get and true from has.
  • entries() output is unchanged.
  • A test exercises the CodeMode web fetch wrapper (or its header record) against a response lacking prototype-named headers and pins the two names above.

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    task/small-fixQueue packet kind: bounded fix with reproduction and targeted tests
    source/creatorFiled by the new-issue-creator job
    on Oct 29, 2026
  2. developing-today commented on Oct 29, 2026

    @developing-today
  3. added
    priority/lowNice to have; do when convenient
    queue/queuedHas a queued task in the Supabase agent queue
    app/codemodePackage: codemode (pkgs/ or apps/ in this repo)
    effort/xsTrivial: expected under 30 minutes of work
    on Oct 29, 2026
  4. dezren39 commented on Oct 29, 2026

    @dezren39
    Author

    🤖 Agent note · filed

    • Agent: new-issue-creator-opencode
    • Session / ID: new-issue-creator-opencode run 2026-10-29T01:48Z
    • Found via: filed this run
    • Task: Fix the CodeMode fetch headers wrapper (packages/core/src/codemode/web.ts:23,32-33) reading through the prototype chain: get("constructor") returns the Object function and has("proto") is true for headers the server never sent.
    • Expectation: effort/xs — done when the header record/accessors are own-property-safe (null-prototype record or Object.hasOwn guards), real headers (including a literal "constructor" header) still resolve, entries() unchanged, with a regression test.
    • Links: probe ~/git/fleet/probe-2026-10-28-creator-opencode2/probe-codemode-headers.ts (laptop, real extension fetch against a local Bun.serve response, main f58571a); body issue-opencode-codemode-headers-prototype-body.md
  5. added
    queue/claimedA queue worker has claimed the task for this item
    work/assignedAn agent holds this item via the queue (claimed task)
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Oct 29, 2026
  6. 5 remaining items

  7. dezren39 commented on Oct 29, 2026

    @dezren39
    Author

    CI re-check by queue worker (lane B): PR #808 is MERGEABLE, but its checks are still QUEUED / IN_PROGRESS ~35 minutes after opening (fleet runner backlog — the same congestion affecting PRs #806/#810). No check has failed on this change; the only completed non-neutral results are CANCELLED pr-standards runs whose replacements are queued.

    Task 2969 re-parked 30m. Resume: if CI is green (or fails only on pre-existing unrelated tests), merge PR #808 per the standing directive and complete the task.

  8. dezren39 commented on Oct 29, 2026

    @dezren39
    Author

    🤖 Agent note · state: parked (PR #808, awaiting CI rerun)

  9. added
    queue/claimedA queue worker has claimed the task for this item
    and removed
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Oct 29, 2026
  10. added a commit that references this issue on Oct 29, 2026
  11. dezren39 commented on Oct 29, 2026

    @dezren39
    Author

    Merged via PR #808 (merge commit 9a8652bcaeddc6015f1d3e34521f71cf59490308); this issue auto-closed by the merge. CI at head d5cd8372: typecheck, build (linux+windows), e2e (linux+windows), nix-eval, and pr-standards green. Unit (linux+windows) failed only on the pre-existing #565 tui permission pair (fixed on main by PR #789; this branch was 27 commits behind main) plus an unrelated Windows debug-paths 30s timeout; the diff is codemode-only (packages/core/src/codemode/web.ts + new web.test.ts) and was verified sound before merging under the standing merge directive. Queue task 2969 completed.


    agent-note:v1 agent=repo-queue-worker-shift-opencode lane=A task=2969 state=complete pr=#808

  12. removed
    queue/claimedA queue worker has claimed the task for this item
    work/assignedAn agent holds this item via the queue (claimed task)
    on Oct 29, 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

    app/codemodePackage: codemode (pkgs/ or apps/ in this repo)effort/xsTrivial: expected under 30 minutes of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/lowNice to have; do when convenientsource/creatorFiled by the new-issue-creator jobstatus:donetask/small-fixQueue packet kind: bounded fix with reproduction and targeted tests

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions