* fix(proxy-capture): guard body-less arrayBuffer reads against oversized responses
* fix(proxy-capture): exercise body-less fallback in bounded read tests
New tests use mock clones with body: null plus arrayBuffer spies to
prove the content-length precheck guards the !body path. A real
Response clone exposes body.getReader in Node 24, so the prior test
only exercised the streaming branch and would stay green even if
the precheck were deleted.
* chore: retrigger CI
* fix(proxy-capture): reject non-safe content-length before arrayBuffer (#101268)
ClawSweeper P2: the body-less fallback used Number(content-length), so a
huge digit-only Content-Length value could overflow to Infinity, bypass the
Number.isFinite guard, and still call arrayBuffer() — leaving an OOM path in
the hardening PR.
Add declaredContentLengthExceedsCap, which accepts only plain digit strings,
treats any value longer than Number.MAX_SAFE_INTEGER as oversized, and
compares safe-integer parsed values against the cap. Non-numeric or malformed
values fall through to the post-read length check.
Adds a regression test for a 100-digit Content-Length that would previously
have bypassed the guard.
* fix(proxy-capture): normalize zero-padded Content-Length before digit-count guard
* fix(proxy-capture): fail closed without response streams
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
(cherry picked from commit 7cde0ac8c0)
* fix(qqbot): bound tail log reads to actual bytes returned by fs.readSync
* fix(qqbot): satisfy knip deadcode check for testing export
Add __testing re-export and test-api.ts barrel so knip traces the testing export through a recognized entry point.
* fix(qqbot): restore testing export alongside __testing re-export
Both exports are needed: testing for proof scripts, __testing for knip tracing.
* fix(qqbot): remove unused __testing re-export from log-helpers
test-api.ts already imports testing and re-exports as __testing. The extra re-export in log-helpers.ts was unused by production code.
* fix(qqbot): retry short log tail reads
* test(qqbot): keep short-read seam private
Co-authored-by: RileyJJY <100176083+RileyJJY@users.noreply.github.com>
* test(qqbot): exercise short reads through log export
Co-authored-by: RileyJJY <0668000974@xydigit.com>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: RileyJJY <100176083+RileyJJY@users.noreply.github.com>
(cherry picked from commit 3f89aae98d)
* fix(agents): bound base64 image input before decode in tool-image sanitizer
* fix(agents): lower input-size cap to 10MB for OOM headroom
* fix(agents): align tool-image input-cap comment with 10 MiB ceiling
* fix(agents): typecheck-safe access in tool-image input-cap test
* test(agents): exercise real tool image input cap
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
(cherry picked from commit b85531c62f)
* fix(memory-core): write MEMORY.md atomically during short-term promotion
applyShortTermPromotions rewrote MEMORY.md with a single non-atomic
fs.writeFile, which truncates the file before streaming the new content.
An OS write failure part way through (for example EFBIG on a size-limited
or full volume) left MEMORY.md truncated to the bytes written before the
failure, permanently dropping user long-term memory. The dreaming cron
path invokes this writer automatically, and the recall store is only
updated after the write, so the promotion stays eligible and the next
run reads the already-truncated file.
Route the write through replaceFileAtomic (temp file, fsync, atomic
rename), the same durable-write helper the sibling DREAMS.md writer in
this extension already uses. On failure the temp file is discarded and
the existing MEMORY.md is left untouched; on success the content and the
existing file mode are preserved.
* fix(memory-core): harden atomic promotion durability
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
(cherry picked from commit 0317d7e628)
* fix(sessions): stop leaking file path as prompt content on read failure
When readFileSync fails for a valid file path, resolvePromptInput returns
the raw path string as prompt content instead of undefined. This injects
filesystem paths into the LLM context. The existing console.error warning
still fires; the caller already handles undefined returns correctly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test: cover unreadable prompt paths
* test: use tracked resource loader temp dirs
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <peter@steipete.me>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
(cherry picked from commit c924819292)
* fix(synology-chat): settle user_list overflow without hanging
Cap reads at 1 MiB with Buffer concat. On overflow, finish the promise
before destroy() — bare destroy often skips end/error and hung the test.
* fix(synology-chat): share bounded user-list reader
Co-authored-by: zw-xysk <zhao.wang1@xydigit.com>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
(cherry picked from commit 30c257f6b4)
* fix(moonshot): bound video description JSON response reads
The Moonshot video description endpoint used an unbounded await res.json()
to parse the media understanding response. Route through
readProviderJsonResponse (16 MiB cap) to match the bound already in
place for other media understanding providers (xai, openrouter).
AI-assisted.
Co-authored-by: Cursor <cursoragent@cursor.com>
* test(moonshot): add bounds and malformed-JSON coverage for video description
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit 765d05c2e4)
Replace unbounded response.text() in readResponseBody and
response.json() in the device-code polling loop with
readResponseWithLimit (16 MiB cap).
(cherry picked from commit d5aca1d6d2)
Replace bare `await response.json()` in `installSignalCliFromRelease` with
`readProviderJsonResponse` (16 MiB cap, stream cancel on overflow). The
external GitHub Releases endpoint can include a large `body` changelog field;
the error path was already guarded but the success path was unbounded.
The existing inner catch continues to convert overflow errors into the
graceful `{ ok: false, error: "Failed to parse signal-cli release info." }` path.
Adds a regression test verifying the stream is cancelled before all chunks are
read on an oversized 20 MiB streaming response.
Co-authored-by: NIO <nocodet@mail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit 51064bda4d)
Route OpenRouter video submit and poll success JSON through the shared bounded provider JSON reader, preserving malformed-response mapping and SSRF request policy coverage.
(cherry picked from commit 48f34b1d4d)
* fix(minimax): bound image/video success response reads
MiniMax image generation and video generation (task submit + status poll)
read their success responses through unbounded `await response.json()`, so
a misbehaving or hostile endpoint could stream an arbitrarily large body
into memory before parsing and exhaust the process. Read those success
bodies through the shared bounded reader (16 MiB cap, the same limit other
bundled providers and the sibling MiniMax web-search provider already use)
and cancel the stream on overflow. The error-body path is already bounded
via assertOkOrThrowHttpError; this closes the matching success-JSON gap.
MiniMax TTS is already bounded and is left unchanged.
AI-assisted.
* fix(minimax): bound video metadata response reads
* fix(minimax): leave image response sizing to image hardening
* fix(minimax): bound image/video success response reads
MiniMax image generation and video generation (task submit + status poll)
read their success responses through unbounded `await response.json()`, so
a misbehaving or hostile endpoint could stream an arbitrarily large body
into memory before parsing and exhaust the process. Read those success
bodies through the shared bounded reader (16 MiB cap, the same limit other
bundled providers and the sibling MiniMax web-search provider already use)
and cancel the stream on overflow. The error-body path is already bounded
via assertOkOrThrowHttpError; this closes the matching success-JSON gap.
MiniMax TTS is already bounded and is left unchanged.
AI-assisted.
* fix(minimax): bound video metadata response reads
(cherry picked from commit 25e184aeab)
* fix(mattermost): bound successful REST JSON/text response reads
The Mattermost REST client already bounds error bodies
(readResponseTextLimited) and streams guarded responses without buffering,
but the success path still called `await res.json()` / `await res.text()`,
reading the whole body into memory before parsing. A self-hosted or
compromised Mattermost server can return an arbitrarily large (or
never-terminating, content-length-less) JSON/text body and force the plugin
to buffer it unbounded.
Read successful JSON through the shared readProviderJsonResponse (16 MiB cap,
cancels the stream and throws a bounded error on overflow, same as the
provider HTTP path) and cap non-JSON success bodies with readResponseTextLimited.
uploadMattermostFile's file-info JSON is bounded the same way.
Symmetric follow-up to the #95103 / #95108 response-limit campaign.
AI-assisted.
* fix(mattermost): bound probe success JSON reads
* fix(mattermost): reject oversized success text bodies
(cherry picked from commit 9241b9701d)
* fix(github-copilot): bound model discovery and embeddings JSON response reads
The GitHub Copilot embeddings plugin already bounds its error response
bodies via readResponseTextLimited, but the success JSON reads for both
model discovery and the embeddings call used unbounded response.json().
Route both through readProviderJsonResponse (16 MiB cap).
Update isCopilotSetupError to recognise the new error label prefix so
auto-selection still falls through on malformed discovery responses.
Update tests to use proper Response objects and the new error messages.
AI-assisted.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(github-copilot): use memory embedding response cap
Signed-off-by: sallyom <somalley@redhat.com>
---------
Signed-off-by: sallyom <somalley@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: sallyom <somalley@redhat.com>
(cherry picked from commit 1aa7cafc35)
Route success JSON reads through readProviderJsonResponse (16 MiB cap) in
azure-speech, elevenlabs, microsoft, minimax/tts, xai/stt, and
openrouter/media-understanding to prevent OOM from oversized or hostile
endpoint responses. Mirrors the response-limit campaign already applied to
other provider paths.
AI-assisted.
Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit 66e2fcc6f8)