Skip to content

fix(install): add pipe guard to prevent stdin stealing in curl | bash#82918

Closed
SebTardif wants to merge 4 commits into
openclaw:mainfrom
SebTardif:fix/install-pipe-guard
Closed

fix(install): add pipe guard to prevent stdin stealing in curl | bash#82918
SebTardif wants to merge 4 commits into
openclaw:mainfrom
SebTardif:fix/install-pipe-guard

Conversation

@SebTardif

Copy link
Copy Markdown
Contributor

Problem

When invoked via curl -fsSL https://openclaw.ai/install.sh | bash, bash reads the script incrementally from stdin. Interactive prompts (gum, read) and child processes that also read stdin steal bytes from the script stream, causing:

  1. Truncated function names: warn_shell_path_missing_di instead of warn_shell_path_missing_dir
  2. "command not found" errors for the truncated names
  3. Indefinite hangs (the script stream is corrupted, bash blocks waiting for more input)

Reported on WSL2 Ubuntu 24.04 with 3/3 reproduction rate.

Fix

Add a pipe guard at the top of install.sh that detects piped execution via empty BASH_SOURCE[0] (piped scripts have no source file), buffers the remaining stdin to a temp file using cat, and re-executes from the file via exec. This frees stdin for interactive prompts and child processes.

The guard:

  • Does not fire for direct execution (bash install.sh) or process substitution (bash <(curl ...)) because BASH_SOURCE is set
  • Does not fire for CI/Docker with non-tty stdin when running from a file
  • Cleans up the temp file immediately after re-execution starts
  • Uses _OPENCLAW_PIPE_BUFFERED env var to prevent infinite re-execution loops

Fixes #73814

Real behavior proof

Behavior addressed: the installer now buffers itself to a temp file when piped via curl, preventing child processes from consuming stdin bytes that corrupt the script stream.
Real environment tested: Linux x86_64, OpenClaw 2026.5.12 installed, Node v26.0.0, bash 5.2. Tested both piped execution (cat install.sh | bash -s -- --install-method npm) and direct execution (bash install.sh --install-method npm < /dev/null).
Exact steps or command run after this patch: (1) verified pipe guard pattern with a minimal test script that includes a deliberate stdin steal (head -c 50 < /dev/stdin); (2) ran full installer in piped mode; (3) ran full installer in direct mode with non-tty stdin; (4) confirmed both complete successfully without truncation.
Evidence after fix: terminal output copied below.

Pipe guard pattern verification (minimal test script with deliberate stdin steal):

$ cat /tmp/pipe-test-steal.sh | bash
Before stdin steal
After stdin steal
Function executed correctly
Script completed

Without the guard, the head -c 50 would consume 50 bytes of the script stream, truncating the function definition. With the guard, the script runs from a temp file so stdin stealing has no effect.

Full installer in piped mode (script runs from temp file):

$ cat scripts/install.sh | bash -s -- --install-method npm 2>&1 | tail -10
✓ OpenClaw installed

[3/3] Finalizing setup
· Running doctor to migrate settings
· Running doctor
✓ Doctor complete

🦞 OpenClaw installed successfully (2026.5.12)!

Note: error source shows /tmp/tmp.uuj4JcpyzQ: line 2988 confirming the guard buffered to temp file.

Full installer in direct mode (BASH_SOURCE is set, guard does not fire):

$ bash scripts/install.sh --install-method npm < /dev/null 2>&1 | tail -10
✓ Doctor complete

🦞 OpenClaw installed successfully (2026.5.12)!

Note: error source shows scripts/install.sh: line 3000 confirming guard did not fire.

Observed result after fix: piped execution completes successfully without truncated function names or hangs. Direct execution is unaffected. The pipe guard adds negligible overhead (one mktemp + cat + exec).
What was not tested: macOS bash 3.2 (BASH_SOURCE behavior may differ in very old bash versions). WSL2 Ubuntu 24.04 (the reporter's environment) was not tested directly, but the fix is bash-level and platform-independent.

@openclaw-barnacle openclaw-barnacle Bot added scripts Repository scripts size: XS proof: supplied External PR includes structured after-fix real behavior proof. labels May 17, 2026
@clawsweeper

clawsweeper Bot commented May 17, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed May 28, 2026, 8:05 AM ET / 12:05 UTC.

Summary
Adds a top-of-file scripts/install.sh pipe guard that buffers piped stdin to a temp file and re-executes it, plus a shell harness covering piped, direct, command-string, process-substitution, and /dev/null invocation modes.

PR surface: Other +172. Total +172 across 2 files.

Reproducibility: no. not as a fresh current-main live reproduction in this review. The linked issue has concrete WSL2 logs, current docs still advertise the piped installer path, and current source has the pipe-sensitive installer path that makes the report source-reproducible.

Review metrics: 1 noteworthy metric.

  • Installer entrypoint behavior: 1 top-level guard added; 5 invocation modes covered by the shell harness. The change runs before the installer’s normal safety setup, so maintainers should review the invocation contract rather than treating it as ordinary internal script code.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
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:

  • Let the remaining required checks complete and make an explicit maintainer call on the installer entrypoint compatibility risk.

Risk before merge

  • [P1] The PR changes the public curl | bash entrypoint before set -euo pipefail; existing automation that relies on shell-level debug flags or piped stdin semantics may observe different behavior.
  • [P1] Contributor proof covers Linux bash 5.2 plus direct and piped modes, while the original WSL2 report and macOS bash 3.2 were not directly exercised in the supplied proof.
  • [P1] GitHub reported the head as mergeable but unstable at review time, so merge should still wait for the remaining required checks to finish.

Maintainer options:

  1. Accept the entrypoint behavior change after checks finish (recommended)
    Maintainers can land this once the remaining checks finish and they are comfortable that buffering curl | bash through a temp file is the desired installer contract.
  2. Request one more platform proof before merge
    If maintainers want lower upgrade risk, ask for a WSL2 or macOS bash run of the exact hosted-style piped installer path before landing.
  3. Pause for a narrower stdin handoff design
    If changing the top-level piped entrypoint is too broad, pause this PR and pursue targeted /dev/tty handoff fixes instead.

Next step before merge

  • [P2] This is an open implementation PR with no narrow automated repair finding; maintainers need to accept the installer-entrypoint compatibility tradeoff and wait for remaining checks.

Security
Cleared: No concrete security or supply-chain regression was found; the diff adds no dependencies, workflows, permissions, or downloaded third-party execution beyond the existing installer stream.

Review details

Best possible solution:

Land the installer-side guard after maintainer acceptance of the entrypoint compatibility tradeoff and completed checks, then close the linked bug report as fixed and keep public install examples aligned with the supported invocation.

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

No, not as a fresh current-main live reproduction in this review. The linked issue has concrete WSL2 logs, current docs still advertise the piped installer path, and current source has the pipe-sensitive installer path that makes the report source-reproducible.

Is this the best way to solve the issue?

Yes, mostly. An installer-side guard is a better fix than the closed docs-only workaround because it protects the documented default command, but maintainers still need to accept the entrypoint compatibility tradeoff.

AGENTS.md: found and applied where relevant.

Codex review notes: model gpt-5.5, reasoning high; reviewed against da551463e3e9.

Label changes

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR body supplies structured after-fix terminal output for a minimal stdin-steal repro plus full piped and direct installer runs on Linux bash 5.2.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster 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 PR body supplies structured after-fix terminal output for a minimal stdin-steal repro plus full piped and direct installer runs on Linux bash 5.2.
  • remove rating: 🌊 off-meta tidepool: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.

Label justifications:

  • P2: This is a normal-priority installer reliability fix for a documented setup path with limited blast radius.
  • merge-risk: 🚨 compatibility: Merging changes how the public piped installer entrypoint consumes stdin and re-executes, which can affect existing install automation and shell invocation behavior.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster 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 PR body supplies structured after-fix terminal output for a minimal stdin-steal repro plus full piped and direct installer runs on Linux bash 5.2.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body supplies structured after-fix terminal output for a minimal stdin-steal repro plus full piped and direct installer runs on Linux bash 5.2.
Evidence reviewed

PR surface:

Other +172. Total +172 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 0 0 0 0
Tests 0 0 0 0
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 2 172 0 +172
Total 2 172 0 +172

What I checked:

  • Current installer entrypoint: Current main starts scripts/install.sh with set -euo pipefail and still documents curl ... install.sh | bash; there is no equivalent pipe-buffer guard on main. (scripts/install.sh:1, da551463e3e9)
  • PR patch surface: The PR adds the pipe guard before set -euo pipefail and adds scripts/test-install-pipe-guard.sh for focused shell invocation checks. (scripts/install.sh:2, b847f36074fd)
  • TTY handling context: Current installer prompts and later onboarding paths already use /dev/tty, which supports solving the reported pipe corruption in the installer rather than by docs-only command changes. (scripts/install.sh:1309, da551463e3e9)
  • Related bug state: The linked bug report remains open with concrete WSL2 logs for truncated warn_shell_path_missing_di; the earlier docs-only workaround at docs: add -s -- flags to curl|bash install to prevent stdin consumption #73830 is closed unmerged.
  • Bash invocation contract spot-check: Local Bash 5.2 reports empty BASH_SOURCE[0] and pipe stdin under script | bash, while process substitution sets BASH_SOURCE[0]; the Bash manual entry confirms -p file tests for a named pipe.
  • CI and proof state: GitHub reports the PR head as mergeable but unstable; Real behavior proof and installer checks were successful, with some checks still in progress at review time. (b847f36074fd)

Likely related people:

  • steipete: Recent current-main history includes installer TTY and noninteractive doctor work on the same startup/onboarding surface. (role: recent installer area contributor; confidence: high; commits: 504f0dfa36be, 928a9e491513; files: scripts/install.sh)
  • vincentkoc: Recent current-main commits touch installer shell-option validation and platform install behavior near this compatibility-sensitive surface. (role: recent installer contributor; confidence: medium; commits: 6c5b39291fae, 367d584ee3bc; files: scripts/install.sh)
  • SebTardif: Beyond authoring this PR, this account also appears in current-main installer history with a recent Homebrew install behavior fix. (role: current PR author and recent installer contributor; confidence: medium; commits: 527b7c2eed2f, 4b9e0a511150, e5f038aa3cc6; files: scripts/install.sh, scripts/test-install-pipe-guard.sh)
  • giodl73-repo: Recent merged work bounded installer finalization probes for the related closed hang report, so this person is relevant to neighboring installer-hang behavior. (role: adjacent finalization-hang contributor; confidence: medium; commits: bf1a5c330397; files: scripts/install.sh)
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 proof: sufficient ClawSweeper judged the real behavior proof convincing. P2 Normal backlog priority with limited blast radius. impact:crash-loop Crash, hang, restart loop, or process-level availability failure. labels May 17, 2026
@SebTardif

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented May 22, 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.

Re-review progress:

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. and removed impact:crash-loop Crash, hang, restart loop, or process-level availability failure. labels May 22, 2026
@clawsweeper

clawsweeper Bot commented May 22, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg: ✨ hatched 🥚 common Pearl Crabkin. Rarity: 🥚 common. Trait: purrs at green checks.

Details

Share on X: post this hatch
Copy: My PR egg hatched a 🥚 common Pearl Crabkin in ClawSweeper.
Hatchability:

  • Merged PRs are hatchable.
  • Open PRs are hatchable when they are status: 👀 ready for maintainer look, status: 🚀 automerge armed, or labeled clawsweeper:automerge.
  • Closed unmerged PRs are hatchable only when one of those hatchable labels is still present in the durable record.

About:

  • Eggs appear after real-behavior proof passes. They are collectible flavor only.
  • Review momentum changes the shell state: follow-up work warms it, re-review makes it wobble, and a clean final review lets it hatch.
  • The hatch is seeded from this repository and PR number, so the same PR keeps the same creature; the reviewed head SHA can only change safe visual details.
  • Rarity is just collectible sparkle: 🥚 common, 🌱 uncommon, 💎 rare, ✨ glimmer, and 🌈 legendary.

@SebTardif

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented May 22, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

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

Re-review progress:

@clawsweeper clawsweeper Bot added the merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. label May 22, 2026
@SebTardif
SebTardif force-pushed the fix/install-pipe-guard branch from a7d00f6 to 5087351 Compare May 22, 2026 15:52
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 22, 2026
@SebTardif

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented May 22, 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.

Re-review progress:

@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 22, 2026
@SebTardif
SebTardif force-pushed the fix/install-pipe-guard branch from 5087351 to 49663de Compare May 22, 2026 23:26
@SebTardif

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented May 22, 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.

Re-review progress:

@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 22, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 22, 2026
@SebTardif

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented May 23, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

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

Re-review progress:

@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 24, 2026
@github-actions github-actions Bot added the dependencies-changed PR changes dependency-related files label May 24, 2026
@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation channel: discord Channel integration: discord channel: googlechat Channel integration: googlechat channel: imessage Channel integration: imessage channel: line Channel integration: line channel: matrix Channel integration: matrix channel: mattermost Channel integration: mattermost channel: msteams Channel integration: msteams channel: nextcloud-talk Channel integration: nextcloud-talk channel: nostr Channel integration: nostr channel: signal Channel integration: signal channel: slack Channel integration: slack channel: telegram Channel integration: telegram channel: tlon Channel integration: tlon channel: voice-call Channel integration: voice-call channel: whatsapp-web Channel integration: whatsapp-web channel: zalo Channel integration: zalo channel: zalouser Channel integration: zalouser app: android App: android labels May 24, 2026
SebTardif added 3 commits May 25, 2026 09:15
When invoked via 'curl ... | bash', bash reads the script incrementally
from stdin. Interactive prompts (gum, read) and child processes that also
read stdin steal bytes from the script stream, causing truncated function
names (e.g., 'warn_shell_path_missing_di' instead of
'warn_shell_path_missing_dir') and indefinite hangs.

Add a pipe guard at the top of install.sh that detects piped execution
via empty BASH_SOURCE (piped scripts have no source file), buffers the
remaining stdin to a temp file, and re-executes from the file. This
frees stdin for interactive prompts and child processes.

The guard does not fire when the script is executed directly (bash
install.sh) or via process substitution (bash <(curl ...)), because
BASH_SOURCE is set in those cases.

Fixes openclaw#73814

Signed-off-by: Sebastien Tardif <[email protected]>
…ests

Replace '! -t 0' with '-p /dev/stdin' in the pipe guard condition.
The old check fired in any non-TTY context (bash -c command strings,
CI runners, cron, Docker with stdin from /dev/null). The new check
only fires when stdin is specifically a pipe (FIFO), which is the
actual curl | bash scenario.

Add test-install-pipe-guard.sh with focused invocation tests covering
piped, direct, command-string, process-substitution, and /dev/null
stdin execution.

Signed-off-by: Sebastien Tardif <[email protected]>
@clawsweeper

clawsweeper Bot commented May 25, 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.

Re-review progress:

Signed-off-by: Sebastien Tardif <[email protected]>
@steipete

Copy link
Copy Markdown
Contributor

Thanks for chasing this. I agree the reported curl | bash installer failure is real, and the Crabbox run showed the guard does help the exact piped-script stdin-steal path.

I am closing this PR rather than landing it because the top-level detection is still too broad for a public installer entrypoint. In AWS Crabbox on Linux, the PR's own harness failed the bash -c case: with a pipe-backed parent stdin, BASH_SOURCE[0] was empty and /dev/stdin was a pipe, so the guard fired outside the intended curl | bash script-stream case. That means the installer can unexpectedly consume stdin and re-exec in invocation modes that are not the website one-liner.

Proof reviewed:

  • PR head: b847f36074fd4c0da793f5d18cffab2a046234c1
  • AWS Crabbox failing harness run: cbx_ee30a7d1f433, run run_93bdbf31fde9
  • AWS Crabbox passing focused piped dry-run: cbx_e35546a3635f, run run_abcebdbd2edf
  • Remote environment: Linux, Bash 5.3.9

The website install command is staying unchanged. The safer next fix is to harden the actual stdin consumers instead: prompts/read/gum/onboarding subprocesses should explicitly use /dev/tty when interactive, or /dev/null/noninteractive behavior when prompts are disabled. If we ever revisit a global guard, it needs a tighter contract plus CI coverage for pipe, direct file, process substitution, bash -c, /dev/null, CI/Docker, and ideally WSL/macOS too.

Closing this PR as too risky for the installer public API, but keeping the linked bug open for a less magical fix.

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

Labels

merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. scripts Repository scripts 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.

[Bug]: Installer hangs and truncates function "warn_shell_path_missing_di" in install.sh presumably due to stdin consumption from "curl | bash"

2 participants