Commit graph

22 commits

Author SHA1 Message Date
A
d2b6fc1ae4
security: fix path traversal in CLI installer file downloads (#1383)
Fixes path traversal vulnerability where unvalidated filenames from
GitHub API could write files outside intended directory.

Attack vector: MITM attack or DNS hijacking could inject filenames
like "../../../../../../tmp/evil.ts" to write arbitrary files.

Fix: Validate filenames before download - block "..", "/", and "\\"
to ensure files are written only within ${dest}/cli/src/

Severity: HIGH/CRITICAL
Affects: All users running installer via curl|bash

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-17 03:09:12 -05:00
A
30138f6a8a
security: fix path traversal in CLI installer and hetzner token extraction (#1379)
Fixes #1376 - HIGH severity path traversal in CLI installer
Fixes #1377 - MEDIUM severity unquoted variable in hetzner token extraction

Changes:
- cli/install.sh: Replace string prefix matching with canonicalized path
  comparison to prevent path traversal in rm -rf cleanup. The previous
  check could be bypassed with sequences like "/tmp/../../home/user".
- hetzner/lib/common.sh: Quote xargs placeholder variable to prevent
  unexpected behavior if hcloud context name contains shell metacharacters.

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-17 01:51:13 -05:00
A
e92522f138
fix: add error logging to empty catch blocks in test helpers (#1334)
* fix: add error logging to empty catch blocks in test helpers

Previously, test helper functions had 14 empty catch blocks that
silently swallowed all errors during cleanup operations (reading and
deleting temporary stderr files).

This change adds error logging that:
- Allows expected errors (ENOENT for missing files, exit code 1 for cat)
- Logs unexpected errors to console for debugging

This improves test reliability by surfacing unexpected filesystem or
permission errors that could indicate real problems, while still
allowing the intended best-effort cleanup behavior.

Fixes: Empty catch blocks in 6 test files
Impact: Better test debugging and error visibility

Agent: code-health
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: improve error handling in Python fallback and directory deletion

1. Python arithmetic fallback (shared/common.sh:713):
   - Changed from: || echo "$((elapsed + 1))"
   - Changed to: explicit if/else with error detection
   - Impact: Python errors are now properly caught instead of masked by ||

2. Unvalidated directory deletion (cli/install.sh:142):
   - Added path validation before rm -rf
   - Checks: path is within dest directory AND directory exists
   - Impact: Prevents accidental deletion if variables are malformed

Both changes improve safety and error visibility without breaking
existing functionality.

Agent: code-health
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: spawn-bot <bot@openrouter.ai>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-16 20:28:30 -05:00
A
de42958eca
ux: fix install and upgrade success messages (#1113)
Fixes two UX issues identified in #1106:

1. Install script: Raw escape codes weren't rendering in log_info
   - Before: "Run \033[1mspawn\033[0m\033[0;32m to get started\033[0m"
   - After: Uses printf with proper color variable interpolation

2. Update command: Confusing message after `spawn update`
   - Before: "Run your spawn command again to use the new version"
   - After: "Run spawn again to use the new version"
   - The word "your" implied the user had run some other command,
     but they explicitly ran `spawn update`

Agent: ux-engineer

Co-authored-by: Spawn Refactor Service <refactor@spawn.service>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-14 12:45:36 -05:00
A
cdf6f1dba5
fix: use log_step (cyan) for in-progress messages instead of log_info (green) (#768)
In-progress actions (installing, starting, connecting...) should use
log_step (cyan) to visually distinguish them from completion messages
which use log_info (green). This makes it easier for users to see at a
glance what is happening vs what has finished.

Changes:
- cli/install.sh: add log_step function, use it for install progress
- shared/common.sh: OAuth flow and non-interactive exec messages
- Cloud libs: interactive_session, auth, and cleanup messages
- Agent scripts: gateway startup and session opening messages

Agent: ux-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-12 16:45:58 -08:00
A
4e33cc39cd
fix: address medium security findings from #753 (#755)
- Replace `echo -e` with `printf` in cli/install.sh for macOS bash 3.x compat
- Remove `-u` (nounset) from test/run.sh — use `${VAR:-}` pattern instead
- Replace `source <(curl ...)` with `eval "$(curl ...)"` in test/run.sh for curl|bash compat
- Add .gitignore patterns for sensitive files (.env, *.pem, *.key, credentials)

Refs #753

Agent: security-auditor

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-12 15:48:52 -08:00
L
d961947983
fix: download pre-built CLI from GitHub release when local build fails (#728)
Root cause: bun install creates empty directories in proot (Termux)
because proot can't intercept bun's symlink/hardlink/copy_file_range
syscalls. This breaks both local build and source-mode fallback.

Fix: when `bun run build` fails, download the pre-built cli.js from
the `cli-latest` GitHub release. The bundled binary is self-contained
(80KB, all deps inlined) and only needs the bun runtime.

- Add CI workflow (.github/workflows/cli-release.yml) that builds and
  uploads cli.js to a rolling `cli-latest` release on every push to main
- Replace broken source-mode fallback with GitHub release download
- Bump CLI version to 0.2.63

Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-12 13:48:45 -08:00
L
ecfd8e2f4e
fix: source-mode wrapper must cd into ~/.spawn for package resolution (#710)
bun 1.3.8 on Termux proot doesn't resolve node_modules by walking up
from the source file directory. Changing cwd to ~/.spawn/ (where
node_modules lives) before exec ensures packages are found.

Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-12 12:17:06 -08:00
L
db72074e0a
fix: fall back to source-mode install when bundled build fails (#707)
bun 1.3.8 in Termux proot cannot resolve packages with --packages bundle
even with bun.lock present and after --force reinstall. When the bundled
build fails, install source + node_modules to ~/.spawn/ and create a
wrapper script that runs via `bun` directly.

Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-12 12:13:19 -08:00
L
ac1f8239c8
fix: install.sh fails on Termux proot due to missing bun.lock (#704)
The non-git download path did not fetch bun.lock, causing bun install
to resolve dependencies from scratch. On older bun versions (e.g. 1.3.8
in Termux proot), this produced a node_modules layout that broke
`bun build --packages bundle`.

- Download bun.lock in the non-git (curl) path
- Add build retry with `bun install --force` fallback
- Enforce minimum bun version (1.2.0) with auto-upgrade
- Bump CLI version to 0.2.60

Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-12 11:49:20 -08:00
A
30f19b7df6
refactor: Drop spawn.sh bash fallback, auto-install bun instead (#163)
The 663-line bash CLI (spawn.sh) has drifted from the TypeScript CLI,
missing --prompt, security validation, download fallback, and other
features. Rather than maintaining two implementations, the installer
now auto-installs bun (~5 seconds) when it's not present, ensuring
every user gets the full-featured TypeScript CLI.

- Remove cli/spawn.sh (663 lines)
- Simplify install.sh: remove npm method, add bun auto-install
- Extract build_and_install() helper to deduplicate build logic
- Update cli/README.md and CLAUDE.md to reflect bun-only strategy

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-09 22:30:48 -08:00
Sprite
0295df0bf0 fix: Install to ~/.local/bin (in PATH) instead of ~/.bun/bin
~/.bun/bin is often NOT in PATH, causing 'command not found' after
install. Now picks the first dir already in PATH from:
  1. ~/.local/bin (most universal)
  2. $(bun pm bin -g)
  3. ~/.bun/bin
  4. ~/bin

Also consolidated PATH detection and instructions into shared helpers,
with clear shell-specific instructions when the dir isn't in PATH.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-09 00:45:18 +00:00
Sprite
d92bf66f51 fix: Install spawn binary directly instead of using bun link
bun link only registers packages for project-level linking — it does
not create global CLI binaries. bun install -g has the same issue.

New approach:
- bun: build cli.js, copy directly to $(bun pm bin -g)/spawn (55KB)
- npm: build with esbuild, copy to $(npm bin -g)/spawn
- Both methods put the binary exactly where the runtime expects it

Also replaced brittle hardcoded file downloads with git sparse-checkout
(with GitHub API fallback), so new source files never break the installer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-09 00:37:55 +00:00
Sprite
c7981d626e fix: Replace hardcoded file list with dynamic clone in installer
The install script had a hardcoded list of source files to download,
which broke whenever a new file was added (e.g., security.ts). Also,
openrouter.ai/lab/spawn served a stale cached version without the fix.

Now uses git sparse-checkout (fast, gets only cli/) with a GitHub API
fallback for environments without git. Adding new source files will
never break the installer again.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-09 00:12:22 +00:00
Sprite
863cfbe711 fix: Add missing security.ts to CLI installer downloads
commands.ts imports ./security.js but install.sh never downloaded
security.ts, causing build to fail with "Could not resolve" error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-09 00:07:58 +00:00
Sprite
7410c8ee4f fix: Build cli.js before linking in install script
package.json bin points to cli.js, but it was never built during
installation. Both bun link and npm install -g failed silently because
the file didn't exist.

- Bun path: run `bun run build` before `bun link`
- npm path: use npx esbuild to bundle cli.js with node shebang

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-08 23:50:19 +00:00
LAB
298065f4ad
UX: Improve error messages, help text, and progress indicators (#55)
Enhance the spawn CLI user experience with actionable error messages,
comprehensive help documentation, and visual progress feedback.

Changes:
- Help text: Add AUTHENTICATION, TROUBLESHOOTING, and MORE INFO sections
- Error messages: Show examples of valid options when validation fails
- Progress indicators: Add spinners for script downloads
- Install flow: Provide clear alternatives when npm install requires sudo
- README: Add environment variable documentation and alternative install URL
- Security errors: Suggest workarounds for false positives

All error messages now follow the pattern:
1. What went wrong
2. Why it matters
3. What to do next

Documentation: .docs/UX_IMPROVEMENTS_20260208.md

Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 12:00:59 -08:00
Sprite
36b3d82d2e refactor: add OAuth timeout and remove unused color variables
- Add --max-time 30 to OAuth key exchange curl to prevent indefinite hangs
- Remove unused DIM variable from cli/install.sh
- Remove unused BLUE variable from cli/spawn.sh

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 03:01:55 +00:00
Sprite
cabdbc37ba refactor: add pipefail to error handling flags
Changed 65 agent scripts from `set -e` to `set -eo pipefail` to ensure
errors in piped commands are properly caught. This prevents silent
failures when commands like `curl | bash` fail in the middle.

Files updated across all cloud providers:
- aws-lightsail: 10 scripts
- digitalocean: 3 scripts
- e2b: 10 scripts
- gcp: 10 scripts
- hetzner: 3 scripts
- lambda: 10 scripts
- linode: 3 scripts
- modal: 10 scripts
- sprite: 3 scripts
- vultr: 3 scripts

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 02:34:45 +00:00
Sprite
39ee858943 security: fix SC2064 trap quoting to prevent early variable expansion
Changed trap commands from double quotes to single quotes so variables
expand at trap execution time instead of definition time. This prevents
security issues where variables could be tampered with between trap
definition and execution.

Fixed 3 instances:
- cli/install.sh (2 instances): trap 'rm -rf "$tmpdir"' EXIT
- test/run.sh (1 instance): trap 'cleanup' EXIT

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 01:29:56 +00:00
L
4087deb14e
Drop nounset (set -u) flag — incompatible with env var checks (#27)
The autonomous refactoring added `set -euo pipefail` but the scripts
check optional env vars with `[[ -n "$VAR" ]]` which is a fatal error
under nounset when the var isn't set (e.g. SPRITE_NAME, OPENROUTER_API_KEY).

Fix: downgrade to `set -eo pipefail` across all 42 affected files.

Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-07 16:22:04 -08:00
L
6ac59e6bb3
Fix OAuth server for macOS bash 3.x (#24)
Three issues broke the OAuth callback server on macOS:

1. echo -e doesn't work in bash 3.x — \r\n appears as literal text
   in the HTTP response, browser gets malformed headers.
   Fix: pre-write response with printf to a file before the subshell.

2. local variables inside ( ... ) & subshell — undefined behavior in
   bash 3.x since subshells aren't function scope.
   Fix: use plain variables in subshells.

3. ((elapsed++)) when elapsed=0 evaluates to falsy — set -e kills
   the script on the first iteration of the timeout loop.
   Fix: use elapsed=$((elapsed + 1)) instead.

Also simplified nc_listen detection to only check for BusyBox
(the -p flag check could misfire on macOS nc).

Applied to all 10 lib/common.sh files.

Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-07 14:21:47 -08:00