Skip to content

fix(desktop): stream safeFetch bodies with incremental maxSize enforcement (Closes #408)#550

Open
advancedresearcharray wants to merge 1 commit into
clawwork-ai:mainfrom
advancedresearcharray:fix/safe-fetch-streaming-408
Open

fix(desktop): stream safeFetch bodies with incremental maxSize enforcement (Closes #408)#550
advancedresearcharray wants to merge 1 commit into
clawwork-ai:mainfrom
advancedresearcharray:fix/safe-fetch-streaming-408

Conversation

@advancedresearcharray

Copy link
Copy Markdown
Contributor

Closes #408

Summary

safeFetch now reads response bodies with getReader() and aborts once maxSize is exceeded, instead of calling arrayBuffer() on the full payload first.

Test plan

  • pnpm --filter @clawwork/desktop test -- safe-fetch.test.ts

Read response bodies incrementally and abort once maxSize is exceeded
instead of buffering the full payload with arrayBuffer() first.

Closes clawwork-ai#408
@github-actions

Copy link
Copy Markdown
Contributor

Hi @advancedresearcharray,
Thanks for your pull request!
If the PR is ready, use the /auto-cc command to assign Reviewer to Review.
We will review it shortly.

Details

Instructions for interacting with me using comments are available here.
If you have questions or suggestions related to my behavior, please file an issue against the gh-ci-bot repository.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request optimizes the safeFetch utility to handle large response bodies more efficiently. By transitioning from a full-buffer approach to a streaming-based approach, the application can now enforce size constraints incrementally, preventing potential memory issues when processing large network responses.

Highlights

  • Streaming Response Processing: Updated safeFetch to read response bodies incrementally using a ReadableStream reader instead of loading the entire payload into memory via arrayBuffer().
  • Incremental Size Enforcement: Implemented real-time maxSize enforcement by aborting the request as soon as the accumulated chunk size exceeds the defined limit.
  • Test Coverage: Enhanced the mockResponse utility to support streaming and added a new test case to verify that streaming bodies are correctly rejected when they exceed the maxSize.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request refactors safeFetch to stream the response body instead of loading the entire array buffer at once, preventing excessively large responses from being loaded into memory. The review feedback correctly identifies a regression where responses without a body (such as a 204 No Content status) will now throw an error instead of returning an empty buffer, and suggests returning an empty buffer and adding a corresponding test case to verify this behavior.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +56 to +57
const reader = res.body?.getReader();
if (!reader) throw new Error('response has no body');

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.

high

Throwing an error when res.body is null/undefined introduces a regression. Previously, if the response had no body (e.g., a 204 No Content status or an empty response), res.arrayBuffer() would resolve to an empty buffer, and safeFetch would succeed by returning an empty Buffer.

To preserve this behavior and prevent unexpected failures on empty responses, we should return an empty Buffer when res.body is null or undefined.

Suggested change
const reader = res.body?.getReader();
if (!reader) throw new Error('response has no body');
if (!res.body) {
return Buffer.alloc(0);
}
const reader = res.body.getReader();

Comment on lines +158 to +164
it('rejects streaming bodies without content-length before exceeding maxSize', async () => {
assertNotPrivateHostMock.mockResolvedValue(null);
const big = new ArrayBuffer(4096);
netFetchMock.mockResolvedValue(mockResponse(big, 200, {}));

await expect(safeFetch('https://cdn.example.com/stream.bin', { maxSize: 1024 })).rejects.toThrow('too large');
});

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.

medium

Add a test case to verify that safeFetch successfully returns an empty buffer when the response has no body (e.g., a 204 No Content response), preventing future regressions.

  it('rejects streaming bodies without content-length before exceeding maxSize', async () => {
    assertNotPrivateHostMock.mockResolvedValue(null);
    const big = new ArrayBuffer(4096);
    netFetchMock.mockResolvedValue(mockResponse(big, 200, {}));

    await expect(safeFetch('https://cdn.example.com/stream.bin', { maxSize: 1024 })).rejects.toThrow('too large');
  });

  it('returns empty buffer when response has no body (e.g. 204 No Content)', async () => {
    assertNotPrivateHostMock.mockResolvedValue(null);
    const response = mockResponse(new ArrayBuffer(0), 204);
    Object.defineProperty(response, 'body', { value: null });
    netFetchMock.mockResolvedValue(response);

    const buf = await safeFetch('https://cdn.example.com/empty');
    expect(buf.length).toBe(0);
  });

@advancedresearcharray

Copy link
Copy Markdown
Contributor Author

PR is ready for merge! 🎉 All checks are green and there are no pending reviews.

@advancedresearcharray

Copy link
Copy Markdown
Contributor Author

LGTM, ready for merge! 🚀

@advancedresearcharray

Copy link
Copy Markdown
Contributor Author

PR is ready for merge, CI is green and there are no pending reviews.

@advancedresearcharray

Copy link
Copy Markdown
Contributor Author

This PR is ready for merge. CI is green and there are no review comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] safeFetch buffers entire response before enforcing maxSize — memory DoS

1 participant