Skip to content

fix(microsoft-foundry): bound connection test error reads#97812

Merged
vincentkoc merged 1 commit into
openclaw:mainfrom
Pick-cat:fix/microsoft-foundry-bound-connection-test-reads
Jun 29, 2026
Merged

fix(microsoft-foundry): bound connection test error reads#97812
vincentkoc merged 1 commit into
openclaw:mainfrom
Pick-cat:fix/microsoft-foundry-bound-connection-test-reads

Conversation

@Pick-cat

@Pick-cat Pick-cat commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

testFoundryConnection reads the connection-test error body with an unbounded await res.text() on both failure branches (400 and any other non-OK status) during Microsoft Foundry onboarding. The endpoint URL is operator-supplied; a host that returns a large body with no Content-Length (wrong endpoint, error page, broken proxy, or hostile host) makes onboarding buffer the entire payload into memory just to display a 200-char snippet — a memory-pressure vector on the setup path.

Affected surface: extensions/microsoft-foundry/onboard.ts — the 400 and !res.ok branches of testFoundryConnection.

Why This Change Was Made

Both branches only ever display body.slice(0, 200) in the prompter note, but they read the whole body first. This routes both reads through the existing shared bounded reader readResponseTextLimited (from openclaw/plugin-sdk/provider-http) with an 8 KiB cap (FOUNDRY_CONNECTION_TEST_ERROR_BODY_LIMIT_BYTES) — far above the 200-char display need, so the note is unchanged while the read can no longer grow without bound. The reader cancels the upstream stream once the cap is hit. The existing .catch(() => "") is preserved, so the note degrades exactly as before on a read failure.

No new abstraction — reuses the existing plugin-SDK helper. Plugin-local change, two call sites with the identical pattern.

User Impact

Operators running the Foundry connection test still see the same diagnostic note (status + first 200 chars of the error body), but a misbehaving or hostile endpoint can no longer drive onboarding to buffer an arbitrarily large error body into memory.

Evidence

1. Real-behavior proof with a negative control (real HTTP, outside Vitest mocks). The exact swapped-in reader — readResponseTextLimited, imported from the same SDK barrel the fix uses (openclaw/plugin-sdk/provider-http) — is driven against a real node:http server that streams a 25 MiB error body in chunks with no Content-Length, for both changed branches (400 and non-OK 503), measuring bytes actually flushed on the wire before the socket closes. The negative control runs the pre-fix await res.text() against the same server. All endpoints are 127.0.0.1; no secrets.

$ tsx foundry-proof.mts
Hostile connection-test error body = 25.00 MiB, streamed with NO Content-Length

[WITH FIX] 400 Bad Request branch (status 400) -> readResponseTextLimited(res, 8 KiB)
PASS: bounded read <= 8 KiB — read 8192 bytes
PASS: note snippet intact (.slice(0,200) still works) — first 200 chars present: "foundry connection test "...
PASS: upstream stopped early (cap load-bearing) — server flushed only 0.22 MiB before socket closed

[WITH FIX] non-OK branch (status 503) -> readResponseTextLimited(res, 8 KiB)
PASS: bounded read <= 8 KiB — read 8192 bytes
PASS: note snippet intact (.slice(0,200) still works) — first 200 chars present: "foundry connection test "...
PASS: upstream stopped early (cap load-bearing) — server flushed only 0.22 MiB before socket closed

[NEGATIVE CONTROL] old `await res.text()` on same server
PASS: old path buffers entire body — res.text() returned 25.03 MiB (server sent 25.03 MiB)

ALL PROOF ASSERTIONS PASSED

Without the fix the read returns the full 25.03 MiB body; with the fix each branch reads exactly 8192 bytes, the 200-char note slice is still intact, and the server flushes only 0.22 MiB before the client cancels the stream and the socket closes — proving the cap is load-bearing, not incidental. The residual ~0.22 MiB is in-flight TCP/socket buffering, not buffered by the reader.

2. Committed regression test (extensions/microsoft-foundry/onboard.connection.test.ts) drives the real testFoundryConnection through the non-OK branch with a streamed oversized error body, asserts res.text() is never called, asserts the stream is canceled, and asserts the prompter note still renders the bounded snippet.

$ node scripts/run-vitest.mjs extensions/microsoft-foundry/onboard.connection.test.ts
 Test Files  1 passed (1)
      Tests  1 passed (1)

3. Lint on the changed file:

$ node scripts/run-oxlint.mjs extensions/microsoft-foundry/onboard.ts   # exit 0

What was NOT tested: the full testFoundryConnection wrapper was not driven end-to-end against a live Azure endpoint, because it first acquires a token via the az CLI (getAccessTokenResultexecAz) and its fetchWithSsrFGuard call uses the default (private-network-denying) SSRF policy, so localhost cannot be substituted live. The committed Vitest test exercises that wrapper with the token/fetch boundary stubbed; the bounded read itself — the only line this PR changes — is proven live over a real socket above, including the negative control. I did not drive the process to an actual OOM.

AI-assisted: implementation, tests, and the proof harness were drafted with agent assistance; all commands above were run locally.

@clawsweeper

clawsweeper Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 29, 2026, 11:10 AM ET / 15:10 UTC.

Summary
The branch replaces Microsoft Foundry connection-test failure res.text() reads with an 8 KiB readResponseTextLimited cap and adds a focused regression test.

PR surface: Source +9, Tests +73. Total +82 across 2 files.

Reproducibility: yes. Current main source shows res.text() on both failed probe response branches, and the PR body's real-HTTP negative control demonstrates that the old path buffers the full streamed error body.

Review metrics: 1 noteworthy metric.

  • Error-body readers: 2 replaced with 8 KiB limited reads. Both changed branches handle externally controlled HTTP response bodies during Microsoft Foundry onboarding.

Merge readiness
Overall: 🦞 diamond lobster
Proof: 🦞 diamond lobster
Patch quality: 🦞 diamond lobster
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Next step before merge

  • [P2] No repair lane is needed; the patch is review-clean and the remaining action is maintainer approval or merge handling.

Security
Cleared: The diff narrows externally controlled HTTP error-body reads and does not change dependencies, workflows, package metadata, permissions, or secret handling.

Review details

Best possible solution:

Merge this bounded-reader fix after ordinary maintainer approval, preserving the existing 200-character diagnostic while capping the body read.

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

Yes. Current main source shows res.text() on both failed probe response branches, and the PR body's real-HTTP negative control demonstrates that the old path buffers the full streamed error body.

Is this the best way to solve the issue?

Yes. Reusing the existing readResponseTextLimited helper at the Microsoft Foundry onboarding boundary is narrower and more maintainable than adding a provider-local reader or changing the shared SSRF fetch path.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 825d9a66623a.

Label changes

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The updated PR body includes copied real-HTTP terminal output for both changed branches plus a negative control showing the old unbounded read behavior.
  • add rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The updated PR body includes copied real-HTTP terminal output for both changed branches plus a negative control showing the old unbounded read behavior.
  • remove rating: 🧂 unranked krab: Current PR rating is rating: 🦞 diamond lobster, so this older rating label is no longer current.
  • remove status: 📣 needs proof: Current PR status label is status: 👀 ready for maintainer look.

Label justifications:

  • P2: This is a normal-priority provider onboarding availability hardening fix with limited blast radius.
  • rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The updated PR body includes copied real-HTTP terminal output for both changed branches plus a negative control showing the old unbounded read behavior.
  • proof: sufficient: Contributor real behavior proof is sufficient. The updated PR body includes copied real-HTTP terminal output for both changed branches plus a negative control showing the old unbounded read behavior.
Evidence reviewed

PR surface:

Source +9, Tests +73. Total +82 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 11 2 +9
Tests 1 73 0 +73
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 84 2 +82

What I checked:

Likely related people:

  • haxudev: GitHub PR metadata for the merged Microsoft Foundry provider PR shows this person authored the feature that added the onboarding and connection-test surface. (role: introduced feature; confidence: high; commits: a16dd967da51; files: extensions/microsoft-foundry/onboard.ts, extensions/microsoft-foundry/auth.ts, extensions/microsoft-foundry/index.test.ts)
  • BradGroux: GitHub PR metadata shows this person merged the Microsoft Foundry provider PR that introduced the connection-test behavior. (role: merger; confidence: medium; commits: a16dd967da51; files: extensions/microsoft-foundry/onboard.ts, extensions/microsoft-foundry/auth.ts)
  • joshavant: Current line blame for the affected function and the shared bounded helper points at the recent repository import/current-main carry-forward commit, though the commit was broad rather than Foundry-specific. (role: recent current-main carrier; confidence: medium; commits: 7c47904bb42d; files: extensions/microsoft-foundry/onboard.ts, src/agents/provider-http-errors.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@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. labels Jun 29, 2026
@Pick-cat

Copy link
Copy Markdown
Contributor Author

Addressed the P1 "mock-only proof" blocker: updated the PR body with real-HTTP behavior proof outside Vitest mocks. The exact swapped-in reader readResponseTextLimited (same openclaw/plugin-sdk/provider-http barrel the fix imports) is driven against a node:http server streaming a 25 MiB error body with no Content-Length, for both changed branches (400 and non-OK 503), plus a negative control.

With the fix each branch reads exactly 8192 bytes, the 200-char note slice stays intact, and the server flushes only ~0.22 MiB before the stream is canceled; the pre-fix res.text() buffers the full 25.03 MiB on the same server. All endpoints are 127.0.0.1, no secrets. I also noted why the az/SSRF-gated wrapper can't be driven live and that the committed Vitest test covers it. Commands + output are in Evidence.

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed 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. labels Jun 29, 2026
@vincentkoc
vincentkoc merged commit eb5fb2a into openclaw:main Jun 29, 2026
149 of 161 checks passed
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 30, 2026
QiuYuang pushed a commit to QiuYuang/openclaw that referenced this pull request Jul 1, 2026
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 1, 2026
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 3, 2026
Rorqualx pushed a commit to Rorqualx/cortex that referenced this pull request Jul 5, 2026
Rorqualx pushed a commit to Rorqualx/cortex that referenced this pull request Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants