Skip to content

fix(sandbox): use Buffer.byteLength for env var value size limit#105017

Merged
steipete merged 2 commits into
openclaw:mainfrom
tzy-17:fix/sanitize-env-vars-byte-length
Jul 16, 2026
Merged

fix(sandbox): use Buffer.byteLength for env var value size limit#105017
steipete merged 2 commits into
openclaw:mainfrom
tzy-17:fix/sanitize-env-vars-byte-length

Conversation

@tzy-17

@tzy-17 tzy-17 commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

validateEnvVarValue in sanitize-env-vars.ts checked value.length (UTF-16 code units) against a 32 768-byte limit. Multi-byte CJK values like "值".repeat(11000) have .length === 11000 but occupy 33 000 UTF-8 bytes, silently bypassing the OS limit. The env var would be forwarded to the sandbox without a warning, potentially causing the container/SSH session to fail at launch.

Fix

Replace value.length with Buffer.byteLength(value, "utf8") to correctly measure UTF-8 byte size. Extract the limit into a named constant MAX_ENV_VAR_VALUE_BYTES = 32768.

Evidence

Before fix (main branch): CJK bypass

CJK value: .length = 11000, Buffer.byteLength = 33000
value.length > 32768? false → would NOT trigger warning (BUG)
Buffer.byteLength > 32768? true → SHOULD trigger warning

The 33 000-byte CJK value passes the .length check and is forwarded without a warning.

After fix (this PR): Correct byte-length check

$ npx tsx -e 'import { validateEnvVarValue } from "./src/agents/sandbox/sanitize-env-vars.ts"; ...'

validateEnvVarValue(CJK 11000 chars): Value exceeds maximum length
validateEnvVarValue(ASCII 32768 chars): Value looks like base64-encoded credential data
validateEnvVarValue(ASCII 32769 chars): Value exceeds maximum length
  • CJK 11 000 chars (33 000 bytes): now correctly warned as exceeding the limit
  • ASCII 32 768 chars (32 768 bytes): at the boundary, no length warning (base64 heuristic still applies)
  • ASCII 32 769 chars (32 769 bytes): correctly warned as exceeding the limit

Vitest output (9/9 tests pass)

$ npx vitest run src/agents/sandbox/sanitize-env-vars.test.ts --reporter=verbose

 ✓ sanitizeEnvVars > keeps normal env vars and blocks obvious credentials
 ✓ sanitizeEnvVars > blocks credentials even when suffix pattern matches
 ✓ sanitizeEnvVars > adds warnings for suspicious values
 ✓ sanitizeEnvVars > supports strict mode with explicit allowlist
 ✓ sanitizeEnvVars > skips undefined values when sanitizing process-style env maps
 ✓ sanitizeEnvVars > allows explicit configured sandbox env names that look like credentials
 ✓ sanitizeEnvVars > still blocks invalid explicit configured sandbox env values
 ✓ sanitizeEnvVars > warns on multi-byte values whose UTF-8 byte length exceeds the limit
 ✓ sanitizeEnvVars > allows ASCII values at the byte limit boundary

 Test Files  1 passed (1)
      Tests  9 passed (9)

Test plan

  • New test: CJK 11 000 chars (33 000 bytes) triggers "Value exceeds maximum length"
  • New test: ASCII at exact 32 768-byte boundary passes length check
  • New test: ASCII at 32 769 bytes triggers "Value exceeds maximum length"
  • Existing 7 sanitizeEnvVars tests still pass

@tzy-17
tzy-17 requested a review from a team as a code owner July 12, 2026 05:35
@openclaw-barnacle openclaw-barnacle Bot added triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence. agents Agent runtime and tooling size: XS labels Jul 12, 2026
@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 Jul 12, 2026
@clawsweeper

clawsweeper Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 15, 2026, 11:12 PM ET / July 16, 2026, 03:12 UTC.

Summary
The PR changes sandbox environment-value validation to measure UTF-8 bytes and adds multibyte, exact-boundary, and over-boundary regression coverage.

PR surface: Source +2, Tests +23. Total +25 across 2 files.

Reproducibility: yes. On current main, validateEnvVarValue("值".repeat(11000)) deterministically follows the UTF-16 value.length check and misses a 33,000-byte UTF-8 value, although a live current-main sandbox launch was not supplied.

Review metrics: none identified.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🐚 platinum hermit
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

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

Rank-up moves:

  • [P2] Rebase onto current main and rerun the required dependency, lint, production-type, and aggregate CI-gate checks.

Risk before merge

  • [P1] The branch is behind current main and the dependency, lint, production-type, and aggregate CI-gate checks are failing on the current head, so it needs a rebase and successful required-check refresh before merge even though the focused diff has no evident defect.

Maintainer options:

  1. Decide the mitigation before merge
    Keep the shared UTF-8 byte measurement and boundary tests, then rebase onto current main and require green dependency, lint, production-type, and aggregate CI checks before merge.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • No automated code repair is indicated; a maintainer should refresh the behind branch and required checks, then complete normal merge review.

Security
Cleared: The focused sandbox validation change strengthens size detection without adding dependencies, permissions, secret access, downloaded code, or new execution paths.

Review details

Best possible solution:

Keep the shared UTF-8 byte measurement and boundary tests, then rebase onto current main and require green dependency, lint, production-type, and aggregate CI checks before merge.

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

Yes. On current main, validateEnvVarValue("值".repeat(11000)) deterministically follows the UTF-16 value.length check and misses a 33,000-byte UTF-8 value, although a live current-main sandbox launch was not supplied.

Is this the best way to solve the issue?

Yes. Correcting byte measurement in the shared validator is the narrowest maintainable solution and avoids separate Docker and SSH implementations.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 957cc81175a3.

Label changes

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The updated PR body includes after-fix terminal output from the exported validator showing the changed multibyte and boundary behavior; any future environment output should remain redacted of keys, tokens, endpoints, and other private values.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The updated PR body includes after-fix terminal output from the exported validator showing the changed multibyte and boundary behavior; any future environment output should remain redacted of keys, tokens, endpoints, and other private values.
  • remove rating: 🦪 silver shellfish: Current PR rating is rating: 🐚 platinum hermit, 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 narrow sandbox validation bug that can suppress an oversized multibyte environment-value warning, with limited blast radius.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The updated PR body includes after-fix terminal output from the exported validator showing the changed multibyte and boundary behavior; any future environment output should remain redacted of keys, tokens, endpoints, and other private values.
  • proof: sufficient: Contributor real behavior proof is sufficient. The updated PR body includes after-fix terminal output from the exported validator showing the changed multibyte and boundary behavior; any future environment output should remain redacted of keys, tokens, endpoints, and other private values.
Evidence reviewed

PR surface:

Source +2, Tests +23. Total +25 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 3 1 +2
Tests 1 23 0 +23
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 26 1 +25

What I checked:

  • Current-main behavior: At the supplied current-main SHA, validateEnvVarValue still compares value.length with 32768, so a value containing 11,000 three-byte CJK characters does not receive the intended oversized-value warning. citeturn4view2. (src/agents/sandbox/sanitize-env-vars.ts:59, 957cc81175a3)
  • Shared Docker path: Docker sandbox environment construction calls the shared sanitizer for inherited and explicitly configured values, so fixing the shared validator covers both paths without duplicating policy. citeturn4view3. (src/agents/sandbox/docker.ts:135, 957cc81175a3)
  • Shared SSH path: The SSH sandbox path also obtains its environment through sanitizeEnvVars, confirming that the validator is the correct common ownership boundary. citeturn4view4. (src/agents/sandbox/ssh.ts:58, 957cc81175a3)
  • Proposed implementation: The PR head replaces the code-unit comparison with Buffer.byteLength(value, "utf8") and names the 32,768-byte threshold. (src/agents/sandbox/sanitize-env-vars.ts:56, e2ac8d56b83e)
  • Regression coverage: The PR adds a 33,000-byte CJK case plus exact 32,768-byte and 32,769-byte ASCII boundary assertions. (src/agents/sandbox/sanitize-env-vars.test.ts:108, e2ac8d56b83e)
  • Real behavior proof: The updated PR body contains after-fix terminal output from the exported validator showing the CJK value warning and both ASCII boundary outcomes; this directly addresses the previous ClawSweeper proof request. (e2ac8d56b83e)

Likely related people:

  • steipete: Introduced the current sandbox environment sanitizer and its original tests in the commit from which the present implementation descends. citeturn8view0turn11view0. (role: introduced behavior; confidence: high; commits: 5487c9adebb0; files: src/agents/sandbox/sanitize-env-vars.ts, src/agents/sandbox/sanitize-env-vars.test.ts)
  • thewilloftheshadow: Recently expanded the same sanitizer and its callers to handle configured sandbox environment names, making them relevant to follow-up review of this shared boundary. citeturn8view0. (role: recent area contributor; confidence: medium; commits: 8c9f58431802; files: src/agents/sandbox/sanitize-env-vars.ts, src/agents/sandbox/sanitize-env-vars.test.ts, src/agents/sandbox/docker.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.
Review history (2 earlier review cycles)
  • reviewed 2026-07-12T05:46:07.839Z sha 18d6760 :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-12T06:01:56.545Z sha 18d6760 :: needs real behavior proof before merge. :: none

@openclaw-barnacle openclaw-barnacle Bot removed the triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence. label Jul 12, 2026
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. labels Jul 12, 2026
@tzy-17
tzy-17 force-pushed the fix/sanitize-env-vars-byte-length branch from 18d6760 to e2ac8d5 Compare July 16, 2026 02:37
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed 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. labels Jul 16, 2026
@tzy-17

tzy-17 commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Command router queued. I will update this comment with the next step.

@steipete steipete self-assigned this Jul 16, 2026
tzy-17 and others added 2 commits July 16, 2026 21:41
validateEnvVarValue checked value.length (UTF-16 code units) against
the 32768-byte limit, so multi-byte CJK values like "值".repeat(11000)
passed the check despite exceeding 33 KB in UTF-8. Switch to
Buffer.byteLength(value, "utf8") so the limit matches the actual byte
count the OS and child processes see.
@steipete
steipete force-pushed the fix/sanitize-env-vars-byte-length branch from e2ac8d5 to 77e4a8e Compare July 16, 2026 20:43
@steipete

Copy link
Copy Markdown
Contributor

Maintainer proof for head 77e4a8e54df451dff02be2e120632a4915f1ca0b:

Ready to land.

@steipete
steipete merged commit 84fb48c into openclaw:main Jul 16, 2026
119 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 17, 2026
…nclaw#105017)

* fix(sandbox): use Buffer.byteLength for env var value size limit

validateEnvVarValue checked value.length (UTF-16 code units) against
the 32768-byte limit, so multi-byte CJK values like "值".repeat(11000)
passed the check despite exceeding 33 KB in UTF-8. Switch to
Buffer.byteLength(value, "utf8") so the limit matches the actual byte
count the OS and child processes see.

* test(sandbox): simplify env byte-limit coverage

Co-authored-by: 唐梓夷0668001293 <[email protected]>

---------

Co-authored-by: Peter Steinberger <[email protected]>
RomneyDa added a commit that referenced this pull request Jul 22, 2026
* fix: gate diagnostics command to owners

(cherry picked from commit 170bf72)

* fix(agent): replace self-wait with deferred release in retained-lock abort cleanup (#96100)

* fix(agent): wait for retained session write before releasing held lock on abort

* fix(agent): replace self-wait with deferred release in retained-lock abort cleanup

* fix(test): reject fallback acquire with SessionWriteLockTimeoutError in active-scope cleanup test

* fix(agent): trim retained-lock comments

Signed-off-by: sallyom <[email protected]>

---------

Signed-off-by: sallyom <[email protected]>
Co-authored-by: sallyom <[email protected]>
(cherry picked from commit 0a042f6)

* fix(gateway): resume channel after pending task recovery

(cherry picked from commit 6039da3)

* fix(gateway): resume channel after pending task recovery

(cherry picked from commit ecd29fe)

* fix(outbound): ignore empty delivery receipts (#79811)

(cherry picked from commit 9a735be)

* fix(agents): guard delivery-evidence attachment recursion against cycles (#97041)

* fix(agents): guard delivery-evidence attachment recursion against cycles

* fix(agents): guard delivery-evidence attachment recursion against cycles

* fix(agents): guard delivery-evidence attachment recursion against cycles

---------

Co-authored-by: Pick-cat <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>
(cherry picked from commit 4985671)

* fix(opencode-go): re-arm idle timer on block-boundary events to prevent false stalled-stream abort (#97128)

* fix(opencode-go): re-arm idle timer on block-boundary events to prevent false stalled-stream abort

When the opencode-go model finalizes a tool call and deliberates before
the next one, the provider emits real block-boundary SSE events
(text_end, thinking_end, toolcall_start, toolcall_end) that prove the
socket is alive, but the watchdog's isProviderProgressEvent only
returned true for token deltas (text_delta, thinking_delta,
toolcall_delta). This caused the idle timer to fire and falsely abort a
live stream, replacing a completed answer with a stalled error and
dropping the provider's real done event.

Fix: include block-boundary events in isProviderProgressEvent so the
idle timer is re-armed on any forward-progress provider event.
text_start and thinking_start are intentionally excluded because they
are synthetic preamble events that should not shorten the first-event
window.

Closes #96518

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* test(opencode-go): satisfy lint in stream regression

* test(opencode-go): satisfy lint in stream regression

* test(opencode-go): satisfy lint in stream regression

---------

Co-authored-by: Claude Opus 4.8 <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>
(cherry picked from commit 552ec2b)

* fix(model-fallback): don't rethrow provider-side AbortErrors as user cancellations (#90908)

* fix(model-fallback): don't rethrow provider-side AbortErrors as user cancellations

When the LLM API closes the connection mid-stream, the fetch layer
surfaces AbortError("This operation was aborted") with no external
abort signal triggered. The old guard `shouldRethrowAbort()` returned
false for these errors (because isTimeoutError matched the message),
so they fell through to the fallback loop but were never retried —
the error propagated up and produced SILENT_REPLY_TOKEN in group
sessions, permanently silencing the topic.

Replace the guard with a direct check: only rethrow AbortError when
the external abort signal is actually set (user/gateway cancellation).
Provider-side AbortErrors without an external signal now fall through
to the next fallback candidate, giving the system a chance to recover.

* fix(cron): forward abort signal into runWithModelFallback

Thread the cron executor's abort signal into the shared
runWithModelFallback call so that cron timeouts and cancellations
stop the fallback chain instead of retrying with the next candidate.

Previously, the run callback checked params.abortSignal?.aborted and
threw, but runWithModelFallback itself had no signal — so the new
guard in model-fallback.ts could not distinguish a caller abort from
a provider-side AbortError and would retry silently.

Also adds a focused regression test verifying the signal is forwarded.

---------

Co-authored-by: Shengting Xie <[email protected]>
Co-authored-by: yayu <[email protected]>
(cherry picked from commit 98ed83f)

* fix(browser): block node routes when sandbox host control is disabled (#97958)

(cherry picked from commit 2cf765f)

* fix(exec): bind Windows allowlist execution path (#98260)

* fix(exec): bind windows allowlist execution path

* fix(exec): add windows shadow execution proof

* fix(exec): preserve wildcard allowlist behavior

* fix(exec): correct blocked plan test fixture

(cherry picked from commit 3811001)

* fix(mcp): suppress unhandled error on stderr pipe in stdio transport (#99803)

* fix(mcp): suppress unhandled error on stderr pipe in stdio transport

When child.stderr is piped to stderrStream without an error
handler, a stream-level error (EPIPE, I/O failure) crashes the
process. Add a noop error handler before the pipe, consistent
with the error handlers already present on stdin and stdout.

Co-Authored-By: Claude <[email protected]>

* test(mcp): add regression test for stderr pipe error suppression

Co-Authored-By: Claude <[email protected]>

* fix(mcp): report stderr stream errors

* fix(mcp): report stderr stream errors

---------

Co-authored-by: Claude <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>
(cherry picked from commit 1b84316)

* Harden macOS SQLite WAL checkpoints (#99067)

(cherry picked from commit f7f1be2)

* fix(secrets): suppress unhandled stdout/stderr stream errors in exec resolver (#100521)

* fix(secrets): suppress unhandled stdout/stderr stream errors in exec resolver

* proof(secrets): add real behavior proof script for exec resolver stream error catch

* proof(secrets): replace wrapper with real exec resolver stream error proof

* style: apply oxfmt to changed files

(cherry picked from commit c9a0783)

* fix(agents): retry transient filesystem races when reading workspace bootstrap files (#100910)

* fix(agents): retry transient filesystem races when reading workspace bootstrap files

* fix(agents): retry transient boundary resolution

---------

Co-authored-by: Vincent Koc <[email protected]>
(cherry picked from commit f36d170)

* fix(gateway): finish plugin HTTP responses after post-header failures (#102125)

* fix(gateway): finish plugin HTTP responses after post-header failures

* test(gateway): satisfy plugin HTTP regression lint

* fix(gateway): skip ending destroyed plugin responses

---------

Co-authored-by: Peter Steinberger <[email protected]>
(cherry picked from commit 240d350)

* fix(gateway): validate exact custom browser origins (#38290)

Co-authored-by: Peter Steinberger <[email protected]>
(cherry picked from commit fa0349a)

* fix: block unspecified trusted DNS targets (#103075)

(cherry picked from commit c70f3d0)

* fix(channels): make nack callbacks idempotent (#104919)

* fix(channels): make nack callbacks idempotent

* fix(channels): coalesce overlapping nack callbacks

---------

Co-authored-by: Peter Steinberger <[email protected]>
(cherry picked from commit 02d307e)

* fix(channels): prevent base URL credentials in status output (#107754)

* fix(channels): redact credentials in account URLs

* fix(channels): sanitize final status summaries

(cherry picked from commit 210340f)

* fix(channels): prevent lifecycle listener buildup (#109108)

(cherry picked from commit 0e1fad7)

* fix(sandbox): use Buffer.byteLength for env var value size limit (#105017)

* fix(sandbox): use Buffer.byteLength for env var value size limit

validateEnvVarValue checked value.length (UTF-16 code units) against
the 32768-byte limit, so multi-byte CJK values like "值".repeat(11000)
passed the check despite exceeding 33 KB in UTF-8. Switch to
Buffer.byteLength(value, "utf8") so the limit matches the actual byte
count the OS and child processes see.

* test(sandbox): simplify env byte-limit coverage

Co-authored-by: 唐梓夷0668001293 <[email protected]>

---------

Co-authored-by: Peter Steinberger <[email protected]>
(cherry picked from commit 84fb48c)

* fix(gateway): guard process.kill ESRCH race in signalVerifiedGatewayPidSync (#109590)

* fix(gateway): guard process.kill ESRCH race in signalVerifiedGatewayPidSync

A verified gateway process can exit between the argv validation check and
the process.kill call, causing an unhandled ESRCH error. Wrap the kill in
try-catch and silently swallow ESRCH (process already gone = signal
already delivered).

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* docs(gateway): explain ESRCH signal race

Co-authored-by: 丁宇婷0668001435 <[email protected]>

---------

Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
(cherry picked from commit 853b1a8)

* fix(litellm): guard loopback hostname auto-allow with isIP to prevent DNS SSRF bypass (#110693)

* fix(litellm): guard loopback hostname auto-allow with isIP to prevent DNS bypass

The isAutoAllowedLitellmHostname helper auto-enables private-network access
for loopback-style hosts. Before this fix, lowered.startsWith("127.")
matched DNS hostnames like 127.evil.com, letting remote endpoints bypass
the explicit allowPrivateNetwork opt-in — a SSRF risk.

Add isIP(host)===4 guard so only literal IPv4 loopback addresses qualify.
Same canonical pattern as extensions/slack/src/monitor/relay-source.ts:271
and the codex loopback fix.

Co-Authored-By: Claude <[email protected]>

* test(litellm): cover loopback endpoint policy

---------

Co-authored-by: Claude <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
(cherry picked from commit 3d03b60)

* fix(discord): sustained gateway bursts stop growing memory (#110954)

* fix(discord): sustained gateway bursts stop growing memory

* fix(discord): contain gateway queue overflow

* fix(discord): drop oldest saturated gateway sends

Co-authored-by: 张贵萍0668001030 <[email protected]>

* fix(discord): surface gateway overflow warnings

Co-authored-by: 张贵萍0668001030 <[email protected]>

---------

Co-authored-by: Peter Steinberger <[email protected]>
(cherry picked from commit 69aeba9)

* fix(gateway): bound busy channel health by real run age (#103793)

* fix(gateway): bound busy channel health by real run age

The channel health policy treats a channel as healthy-busy even while
disconnected, bounded only by a 25 minute stale ceiling measured from
lastRunActivityAt. The run-state heartbeat refreshes lastRunActivityAt
every 60 seconds for as long as any run is active, so a run that hangs
forever (for example a send blocking on a dead socket after the
transport already reported connected:false) keeps that timestamp fresh
and the stuck ceiling is never reached. The account is then reported
healthy forever by the health monitor, readiness probe, and health CLI,
and no restart ever fires.

createRunStateMachine now tracks each in-flight run's start time keyed by
an opaque run handle and publishes the oldest still-active run's start as
activeRunStartedAt. The health policy busy override keys its ceiling off
the real run age, so a run stuck longer than the threshold reports stuck
and the monitor can restart it. Because the reported start is the oldest
active run and advances to the next-oldest as runs complete, a channel
churning through many short overlapping runs (activeRuns above 1 across
concurrent queue keys) stays healthy; only a genuinely hung run breaches
the ceiling. Short and active runs stay healthy and the existing
lastRunActivityAt fallback is preserved for snapshots without a start
time.

* fix(channels): retain run-state callback compatibility

Keep the released zero-argument onRunEnd callback source-compatible while allowing internal queue callers to pass a run handle for exact concurrent-run accounting. The compatibility path closes the oldest active run, preserving existing lifecycle behavior for consumers that do not use handles.

* fix(channels): keep anonymous runs out of age tracking

The zero-argument lifecycle callbacks cannot identify which concurrent run completed, so they must not update the identity-sensitive run start used by channel health. Keep their busy count separately and reserve exact start tracking for the shared queue's handle-aware lifecycle path.

* fix(channels): keep tracked runs internal

Keep the public run-state lifecycle callbacks unchanged. The channel queue now owns opaque run identity and augments its status updates with the oldest active queue run, so implementation details do not expand the SDK surface.

* fix(channels): type queue run start status

Keep activeRunStartedAt in the internal status patch type so the queue can publish its private tracked-run age through the existing status sink.

* fix(channels): wrap isActive to satisfy unbound-method lint

* fix(gateway): gate busy run-age ceiling on disconnected transport

(cherry picked from commit 18b79d9)

* fix(deps): update fast-uri past advisory

(cherry picked from commit 1be9db0)

* fix(release): adapt maintenance-line hardening

Backport/adapt 18ec9ce, dea1fe1, 7f32b6c, 1da345e, 931ac3e, 89780d5, and c0d99ed for the 2026.6 extended-stable maintenance line.

* fix(deps): bump protobufjs to 7.6.5

Backport-adapted from a230f74.

* test(gateway): cover bounded macOS process probe

* chore(release): prepare 2026.6.34

* test(dotenv): share path override environment assertions

* fix(release): resolve 2026.6.34 CI blockers

---------

Signed-off-by: sallyom <[email protected]>
Co-authored-by: joshavant <[email protected]>
Co-authored-by: Peter Lee <[email protected]>
Co-authored-by: sallyom <[email protected]>
Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
Co-authored-by: Liu Wenyu <[email protected]>
Co-authored-by: pick-cat <[email protected]>
Co-authored-by: Pick-cat <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>
Co-authored-by: weiqinl <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>
Co-authored-by: shengting <[email protected]>
Co-authored-by: Shengting Xie <[email protected]>
Co-authored-by: yayu <[email protected]>
Co-authored-by: Agustin Rivera <[email protected]>
Co-authored-by: cxbAsDev <[email protected]>
Co-authored-by: ooiuuii <[email protected]>
Co-authored-by: Masato Hoshino <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>
Co-authored-by: mushuiyu886 <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: Bruno Wowk (Volky) <[email protected]>
Co-authored-by: Pavan Kumar Gondhi <[email protected]>
Co-authored-by: Glucksberg <[email protected]>
Co-authored-by: xingzhou <[email protected]>
Co-authored-by: tzy-17 <[email protected]>
Co-authored-by: krissding <[email protected]>
Co-authored-by: lsr911 <[email protected]>
Co-authored-by: Yuval Dinodia <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: XS 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