Skip to content

fix(gateway): block internal HTTP session overrides#55789

Closed
RichardCao wants to merge 1 commit into
openclaw:mainfrom
RichardCao:fix/gateway-http-block-internal-session-override
Closed

fix(gateway): block internal HTTP session overrides#55789
RichardCao wants to merge 1 commit into
openclaw:mainfrom
RichardCao:fix/gateway-http-block-internal-session-override

Conversation

@RichardCao

Copy link
Copy Markdown
Contributor

Summary

  • reject explicit x-openclaw-session-key overrides that target internal namespaces like subagent:, acp:, or cron:
  • return a 400 invalid_request_error from the OpenAI and OpenResponses HTTP endpoints instead of passing those keys into owner-trusted ingress
  • add request-context and endpoint coverage for the blocked overrides while preserving normal custom session keys

Testing

  • pnpm exec vitest run src/gateway/http-utils.request-context.test.ts src/gateway/openai-http.test.ts src/gateway/openresponses-http.test.ts

@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime size: S labels Mar 27, 2026
@greptile-apps

greptile-apps Bot commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a security guard that blocks x-openclaw-session-key header overrides targeting internal session namespaces (subagent:, acp:, cron:) in the OpenAI-compatible and OpenResponses HTTP endpoints, returning a 400 invalid_request_error instead of forwarding the key to the agent layer.\n\nKey changes:\n- http-utils.ts: Adds resolveReservedSessionKeyPrefix / normalizeReservedSessionKeyCandidate helpers that correctly handle both bare keys (subagent:worker) and agent:<id>: scoped keys (agent:main:subagent:worker) in a case-insensitive, whitespace-tolerant way; consistent with existing semantics in isSubagentSessionKey, isCronSessionKey, and isAcpSessionKey.\n- openai-http.ts / openresponses-http.ts: Both now wrap resolveGatewayRequestContext in a try/catch that serialises the thrown error as a 400 response, preventing the request from reaching the agent layer.\n- Tests added for each endpoint and the utility function; the only minor gap is that only the subagent: prefix is exercised — acp: and cron: have no dedicated test cases.\n- Note: tools-invoke-http.ts accepts a body-supplied sessionKey via a separate code path without the same reserved-prefix guard — this appears to be a pre-existing gap outside the stated scope of this PR and may warrant a follow-up.

Confidence Score: 5/5

Safe to merge; the guard logic is correct and consistent with existing session-key semantics, and both HTTP endpoints are protected.

The only open finding is a P2 test-coverage gap (missing acp: and cron: cases alongside the existing subagent: test), which does not affect correctness of the shipped guard. No P0 or P1 issues found.

src/gateway/http-utils.request-context.test.ts — missing acp: and cron: prefix test cases.

Important Files Changed

Filename Overview
src/gateway/http-utils.ts Adds resolveReservedSessionKeyPrefix / normalizeReservedSessionKeyCandidate helpers; throws from resolveSessionKey when a reserved prefix is matched.
src/gateway/openai-http.ts Wraps resolveGatewayRequestContext in try/catch; returns 400 invalid_request_error when the session key guard throws.
src/gateway/openresponses-http.ts Same try/catch guard as openai-http.ts applied to the OpenResponses handler.
src/gateway/http-utils.request-context.test.ts Adds a test for the subagent: prefix rejection path; acp: and cron: prefixes have no corresponding test cases.
src/gateway/openai-http.test.ts Adds an e2e block verifying 400 is returned for an internal subagent: session key and no agent command is dispatched.
src/gateway/openresponses-http.test.ts Adds equivalent e2e coverage for the OpenResponses endpoint.

Comments Outside Diff (1)

  1. src/gateway/http-utils.request-context.test.ts, line 46-57 (link)

    P2 Only subagent: prefix tested, acp: and cron: cases uncovered

    The new test exercises only the subagent: reserved prefix. RESERVED_SESSION_KEY_PREFIXES also includes acp: and cron:, but there are no cases that verify those are blocked. A future refactor that drops or misspells one of those entries would break silently.

    Consider adding additional it blocks (or parametrizing the existing one) to cover at least acp: and cron::

    // acp: prefix
    { "x-openclaw-session-key": "agent:main:acp:session" } → throws "...internal session namespace acp:"
    // cron: prefix
    { "x-openclaw-session-key": "agent:main:cron:daily" }  → throws "...internal session namespace cron:"
    // also test bare (non-agent-prefixed) variants e.g. "cron:daily"
    
    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: src/gateway/http-utils.request-context.test.ts
    Line: 46-57
    
    Comment:
    **Only `subagent:` prefix tested, `acp:` and `cron:` cases uncovered**
    
    The new test exercises only the `subagent:` reserved prefix. `RESERVED_SESSION_KEY_PREFIXES` also includes `acp:` and `cron:`, but there are no cases that verify those are blocked. A future refactor that drops or misspells one of those entries would break silently.
    
    Consider adding additional `it` blocks (or parametrizing the existing one) to cover at least `acp:` and `cron:`:
    
    ```
    // acp: prefix
    { "x-openclaw-session-key": "agent:main:acp:session" } → throws "...internal session namespace acp:"
    // cron: prefix
    { "x-openclaw-session-key": "agent:main:cron:daily" }  → throws "...internal session namespace cron:"
    // also test bare (non-agent-prefixed) variants e.g. "cron:daily"
    ```
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/gateway/http-utils.request-context.test.ts
Line: 46-57

Comment:
**Only `subagent:` prefix tested, `acp:` and `cron:` cases uncovered**

The new test exercises only the `subagent:` reserved prefix. `RESERVED_SESSION_KEY_PREFIXES` also includes `acp:` and `cron:`, but there are no cases that verify those are blocked. A future refactor that drops or misspells one of those entries would break silently.

Consider adding additional `it` blocks (or parametrizing the existing one) to cover at least `acp:` and `cron:`:

```
// acp: prefix
{ "x-openclaw-session-key": "agent:main:acp:session" } → throws "...internal session namespace acp:"
// cron: prefix
{ "x-openclaw-session-key": "agent:main:cron:daily" }  → throws "...internal session namespace cron:"
// also test bare (non-agent-prefixed) variants e.g. "cron:daily"
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(gateway): block internal HTTP sessio..." | Re-trigger Greptile

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 14fd898273

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +450 to +453
sendJson(res, 400, {
error: {
message: err instanceof Error ? err.message : "Invalid session context.",
type: "invalid_request_error",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid exposing internal exceptions as client input errors

This catch now maps all resolveGatewayRequestContext failures to a 400 and returns err.message verbatim, but that context path calls resolveAgentIdForRequest() which reads config and can throw internal errors (for example loadConfig() throws detailed Invalid config at <path>... messages in src/config/io.ts). In that scenario, API callers receive server internals (filesystem path + validation details) as invalid_request_error, and a server-side failure is misclassified as a client error. Please only convert the reserved-session override failure to 400 and keep unexpected exceptions as generic 500s.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 825e21b. The reserved-prefix rejection now throws a dedicated GatewaySessionKeyOverrideError, and the OpenAI/OpenResponses handlers only translate that specific error to a 400 invalid_request_error. Unexpected context/config failures now fall through instead of leaking internal messages as client input errors.

@clawsweeper

clawsweeper Bot commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

Thanks for the context here. I swept through the related work, and this is now duplicate or superseded.

Close: the same gateway session-override fix was merged through the replacement PR #93443, and current main has the guard, endpoint handling, tests, and docs that this source branch was trying to add.

Root-cause cluster
Relationship: superseded
Canonical: #93443
Summary: The source PR is superseded by the merged replacement PR that implemented the same central gateway HTTP session-override guard on current main.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Canonical path: Keep the merged replacement implementation on current main and close this source PR with contributor credit preserved by the replacement PR.

So I’m closing this here because the remaining work is already tracked in the canonical issue.

Review details

Best possible solution:

Keep the merged replacement implementation on current main and close this source PR with contributor credit preserved by the replacement PR.

Do we have a high-confidence way to reproduce the issue?

No. Current main no longer reproduces the unsafe path because reserved internal session-key overrides are rejected before OpenAI/OpenResponses dispatch.

Is this the best way to solve the issue?

Yes. The merged replacement is the best current solution because it keeps the guard at the shared request-context boundary, preserves ordinary custom session keys, maps the expected error narrowly, and documents the API exception.

Security review:

Security review cleared: The source diff and merged replacement tighten an existing gateway HTTP session-routing boundary and do not add dependency, workflow, lockfile, secret, download, publish, or code-execution surface changes.

AGENTS.md: found and applied where relevant.

What I checked:

  • Current main blocks reserved session overrides: Current main rejects explicit x-openclaw-session-key values that resolve to reserved subagent:, cron:, or acp: namespaces before returning the explicit session key. (src/gateway/http-utils.ts:202, de1d329e3167)
  • Current main maps the guard to HTTP 400: OpenAI-compatible request handling catches the session-key override error and returns invalid_request_error without dispatching the request. (src/gateway/openai-http.ts:991, de1d329e3167)
  • Docs now describe the reserved namespace exception: The public OpenAI-compatible HTTP docs now say reserved internal session namespaces are rejected with 400 invalid_request_error. Public docs: docs/gateway/openai-http-api.md. (docs/gateway/openai-http-api.md:102, de1d329e3167)
  • Replacement PR is merged: fix(gateway): block internal HTTP session overrides #93443 was merged on 2026-06-16T01:30:28Z with merge commit 8694fe7 and explicitly says it repairs and finalizes this source PR while preserving credit. (8694fe7e81f2)
  • Source branch is no longer the landing path: Live PR metadata shows this source PR is still open but CONFLICTING, while the merged replacement includes an additional docs update and the current-main-compatible error handling. (868a0c4eb513)
  • Fix provenance: The fix/proof commit is 8694fe7, committed at 2026-06-16T01:30:27Z, with the message fix(gateway): block internal HTTP session overrides. (8694fe7e81f2)

Likely related people:

  • RichardCao: Authored this source branch and is credited as co-author on the merged replacement commit that carries the same behavior forward. (role: source contributor and merged-fix co-author; confidence: high; commits: 868a0c4eb513, 8694fe7e81f2; files: src/gateway/http-utils.ts, src/gateway/openai-http.ts, src/gateway/openresponses-http.ts)
  • vincentkoc: Posted land-ready verification on the replacement PR immediately before merge, including the current-main guard, tests, and compatibility sanity checks. (role: reviewer and merger of replacement path; confidence: high; commits: 8694fe7e81f2; files: src/gateway/http-utils.ts, src/gateway/openai-http.ts, src/gateway/openresponses-http.ts)
  • steipete: Git history around resolveGatewayRequestContext, gateway plumbing, and session-key classifiers shows adjacent ownership of the affected gateway/session surfaces. (role: adjacent gateway and session-routing contributor; confidence: medium; commits: 7066d5e192a6, de3b68740aa9, dbfa316d19d5; files: src/gateway/http-utils.ts, src/routing/session-key.ts, src/sessions/session-key-utils.ts)

Codex review notes: model internal, reasoning high; reviewed against de1d329e3167; fix evidence: commit 8694fe7e81f2, main fix timestamp 2026-06-16T01:30:27Z.

@RichardCao
RichardCao force-pushed the fix/gateway-http-block-internal-session-override branch from 825e21b to e58b389 Compare April 30, 2026 05:41
@RichardCao

Copy link
Copy Markdown
Contributor Author

Refreshed this PR onto latest upstream/main (99950c7f1272dff6e2c34c2be45dfc5f89e62a60) and force-pushed the updated branch.

New head: e58b3893aee1fcece10a02f8ff24bde298b45ec5

What changed:

  • Rebuilt the original minimal fix on top of current main.
  • Rejects explicit x-openclaw-session-key overrides targeting internal namespaces over the OpenAI-compatible HTTP surfaces.
  • Covers subagent:, acp:, and cron: namespaces, including agent-scoped forms via parseAgentSessionKey.
  • Keeps error handling narrow: only GatewaySessionKeyOverrideError is mapped to HTTP 400; unexpected errors still surface through the normal server error path.

Review:

  • Fresh gpt-5.5 xhigh review found no important issues requiring changes before push.
  • Prior review feedback was considered:
    • Added acp: and cron: coverage alongside subagent: in http-utils.request-context.test.ts.
    • Kept the dedicated GatewaySessionKeyOverrideError so only invalid session override input maps to 400; unrelated context/config failures still surface through the normal server error path.
    • The noted tools-invoke-http.ts body-supplied sessionKey path was intentionally not folded into this PR to keep scope limited to OpenAI-compatible/OpenResponses header overrides. That separate HTTP ingress can be tracked as follow-up if maintainers want a broader all-HTTP policy.

Verification:

  • Passed: ./node_modules/.bin/vitest run --config test/vitest/vitest.gateway-core.config.ts src/gateway/http-utils.request-context.test.ts
  • Result: 1 file passed, 14 tests passed.
  • Blocked locally: src/gateway/openai-http.test.ts / src/gateway/openresponses-http.test.ts failed during import because this local node_modules is missing declared dependencies (typebox, global-agent). No endpoint assertions ran in those suites.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels May 19, 2026
@openclaw-barnacle openclaw-barnacle Bot added the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label May 19, 2026
@clawsweeper

clawsweeper Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg

🎁 Pass real behavior proof to wake the egg and unlock a hatchable treat.

Where did the egg go?
  • The egg game starts only after the PR passes the real-behavior proof check.
  • Before that, no creature or rarity is rolled. The treat waits for real proof.
  • This is still just collectible flavor: proof affects review readiness, not creature quality.

@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. labels May 24, 2026
@RichardCao
RichardCao force-pushed the fix/gateway-http-block-internal-session-override branch from 9eaa1fa to 868a0c4 Compare June 1, 2026 12:24
@clawsweeper clawsweeper Bot added rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 1, 2026
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. labels Jun 14, 2026
@openclaw-clownfish

Copy link
Copy Markdown
Contributor

Clownfish 🐠 reef update

Thanks for the work on this. Clownfish could not push to this branch with the permissions available, so it opened a narrow replacement PR to keep the fix swimming forward without losing the contributor trail. not your fault, just GitHub branch-permission tides.

Replacement PR: #93443
Source PR: #55789
This source PR remains open; the replacement is just the writable fix lane.
The replacement PR keeps the contributor trail visible for review and changelog credit.

fish notes: model gpt-5.5, reasoning medium; reviewed against b18a288.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jun 16, 2026
@clawsweeper

clawsweeper Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper applied the proposed close for this PR.

  • Action: closed this PR.
  • Close reason: duplicate or superseded.
  • Evidence: durable ClawSweeper review.
  • Coverage proof: PR B clearly implements and merged the same core behavior PR A was trying to land, with current-main-compatible handling and verification. Any leftover PR A details are incidental or reviewable on the merged replacement, so PR A does not need independent review. Covering PR: fix(gateway): block internal HTTP session overrides #93443.

@clawsweeper clawsweeper Bot closed this Jun 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gateway Gateway runtime P2 Normal backlog priority with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: S status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant