Skip to content

Add stdin support to exec() - #362

Closed
whoiskatrin wants to merge 2 commits into
mainfrom
feat/stdin-support
Closed

Add stdin support to exec()#362
whoiskatrin wants to merge 2 commits into
mainfrom
feat/stdin-support

Conversation

@whoiskatrin

Copy link
Copy Markdown
Contributor

Summary

Adds stdin option to exec() method, enabling users to pass arbitrary input to commands without shell injection risks.

  • Input is written to a temp file and redirected to the command's stdin
  • Temp file is cleaned up after command execution
  • Passes stdin through all layers: SDK → Handler → Service → Session

Usage

const result = await sandbox.exec('cat', { stdin: 'hello world' });
console.log(result.stdout); // "hello world"

Closes #357

Enables passing arbitrary user input to commands without shell injection
risks. Input is written to a temp file and redirected to the command's
stdin, then cleaned up after execution.

Closes #357
@changeset-bot

changeset-bot Bot commented Jan 25, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a32e460

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@cloudflare/sandbox Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions 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.

OpenCode Review

This is a well-implemented feature that follows the Cloudflare security best practice of writing input to a file instead of interpolating into commands. The implementation correctly flows stdin through all three layers of the architecture.

Security approach is sound: Writing stdin to a temp file and using shell redirection (< file) prevents shell injection, which aligns with Cloudflare's documented security patterns.

Architecture compliance: The changes properly follow the three-layer pattern - SDK → Durable Object → Container - with appropriate type definitions in shared package.

Implementation quality: Good test coverage at the session level, proper parameter threading through all layers, defensive programming with optional chaining, and appropriate file cleanup in finally blocks.

Minor gap: The E2E test infrastructure doesn't support the stdin parameter yet, so there's no end-to-end validation through the full HTTP API stack. Consider updating the test worker to enable comprehensive integration testing.

The changeset follows project conventions. Overall this looks good to merge.

@pkg-pr-new

pkg-pr-new Bot commented Jan 25, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/cloudflare/sandbox-sdk/@cloudflare/sandbox@362

commit: a32e460

@github-actions

Copy link
Copy Markdown
Contributor

🐳 Docker Images Published

Default:

FROM cloudflare/sandbox:0.0.0-pr-362-b25fad1

With Python:

FROM cloudflare/sandbox:0.0.0-pr-362-b25fad1-python

With OpenCode:

FROM cloudflare/sandbox:0.0.0-pr-362-b25fad1-opencode

Version: 0.0.0-pr-362-b25fad1

Use the -python variant if you need Python code execution, or -opencode for the variant with OpenCode AI coding agent pre-installed.


📦 Standalone Binary

For arbitrary Dockerfiles:

COPY --from=cloudflare/sandbox:0.0.0-pr-362-b25fad1 /container-server/sandbox /sandbox
ENTRYPOINT ["/sandbox"]

Download via GitHub CLI:

gh run download 21335195099 -n sandbox-binary

Extract from Docker:

docker run --rm cloudflare/sandbox:0.0.0-pr-362-b25fad1 cat /container-server/sandbox > sandbox && chmod +x sandbox

@ghostwriternr
ghostwriternr self-requested a review February 9, 2026 11:01
@cloudflare cloudflare deleted a comment from mavam Feb 11, 2026
@whoiskatrin

Copy link
Copy Markdown
Contributor Author

/bonk review this PR

@ask-bonk

ask-bonk Bot commented Feb 11, 2026

Copy link
Copy Markdown
Contributor

I've now thoroughly reviewed all 12 changed files. Here's my review:


PR Review: Add stdin support to exec()

Overall Assessment

This is a well-structured feature that correctly threads the stdin option through all three layers of the architecture. The security approach (temp file + shell redirection instead of shell interpolation) is sound.

What's Good

  • Security: Writing stdin to a temp file and using < file redirection avoids shell injection entirely - this is the right approach.
  • Cleanup: Stdin files are cleaned up in both success and error paths (session.ts:372, session.ts:404, session.ts:646, session.ts:659).
  • Type threading: The stdin field is properly added to BaseExecOptions, ExecuteRequest, container ProcessOptions, and ExecOptions in session.ts.
  • Tests: Good coverage at the session level - basic stdin, multiline, special characters, pipes, empty string, and stdin+env combo.
  • Changeset: Correct format, only references @cloudflare/sandbox, user-focused description with usage example.

Issues

1. StartProcessRequest is missing stdin (Medium)

StartProcessRequest in packages/shared/src/request-types.ts:27-36 does not include stdin. While startProcess() currently routes through executeCommandStream which uses ProcessOptions (which does have stdin), the request type contract is incomplete. If someone starts a background process that reads from stdin (e.g., startProcess('python script.py', { stdin: someInput })), the SDK-level ProcessOptions in types.ts inherits stdin from BaseExecOptions and will attempt to send it, but the container-side StartProcessRequest DTO doesn't declare it. This works today because the execute handler path handles both, but it's a type contract gap.

2. No size limit on stdin data (Low-Medium)

There's no validation on the size of the stdin string. A user could pass a very large string (e.g., hundreds of MB) which would:

  • Be serialized in the JSON request body (SDK → container HTTP)
  • Be written to a temp file on the container filesystem
  • Potentially exhaust container disk space or memory

Consider adding a reasonable size limit (e.g., 10MB) with a clear error message, or at minimum documenting the practical limit.

3. Stdin file race with timeout (Low)

If a command times out (session.ts:1152), the timeout rejection in waitForExitCode will cause exec() to throw. The catch block at session.ts:404 does clean up, but the shell script is still running and may still be reading from the stdin file when we rm it. Since rm on an open file is safe on Linux (the inode stays alive until the last fd closes), this is not a correctness bug, but it's worth noting.

4. Missing stdin test for execStream (Low)

The stdin tests only cover the foreground exec() path. There are no tests verifying stdin works through execStream() (the background/streaming path), even though the code supports it (session.ts:446-448, session.ts:468). The generated bash script does handle stdinFile in both foreground and background modes, but the background path is untested.

Nits

  • session.ts:325: The double-check if (stdinFile !== undefined && options?.stdin !== undefined) is redundant since stdinFile is only defined when options?.stdin !== undefined. The same pattern appears at line 446. Not a bug, just unnecessary.

Verdict

This is solid work. The architecture follows established patterns, security is handled correctly, and the implementation is clean. The issues above are improvement suggestions rather than blockers. The StartProcessRequest type gap (issue #1) and the missing execStream test (issue #4) are worth addressing before merge; the rest can be follow-ups.

github run

@ghostwriternr

Copy link
Copy Markdown
Member

As discussed on chat a few weeks ago, closing this in favour of doing a proper PTY API implementation that will be far more flexible and handle all sorts of ways to pass and read from stdin rather than the specific approach this implementation takes.

thazhemadam added a commit to thazhemadam/cloudflare-docs that referenced this pull request Jul 29, 2026
…am(), and startProcess() (cloudflare#27850)"

This reverts merge commit b56f39e.

Remove stale documentation claiming that `exec()`, `execStream()`, and
`startProcess()` accept `stdin` input. The documentation was added in
cloudflare-docs#27850 to match cloudflare/sandbox-sdk#362, but the SDK
change was closed without merging in favor of a future PTY-based design.

The released SDK does not expose this option, so the current examples
direct users toward an unsupported API. Keep the command reference
aligned with the released implementation while
cloudflare/sandbox-sdk#357 remains unresolved.
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.

Add stdin support to exec()

2 participants