Commit graph

2482 commits

Author SHA1 Message Date
Ahmed Abushagur
2d7a23a460
fix(growth): security hardening — bun -e interpolation, pkill race, input validation (#3294)
Closes a batch of real security findings filed against growth.sh and reddit-fetch.ts.

growth.sh:
- Switch all four `bun -e "...${VAR}..."` sites to env-var passing
  (_VAR="..." bun -e 'process.env._VAR'), per .claude/rules/shell-scripts.md.
  Closes #3188, #3221, #3223.
- Spawn claude under `setsid` so it owns its own process group, and kill the
  group via `kill -SIG -PGID` instead of racing with pkill -P. Adds a numeric
  guard on CLAUDE_PID. Closes #3193, #3205.
- POST to SPA with Authorization header loaded from a 0600 temp config file
  (-K) and body from a 0600 temp file instead of here-string, so
  SPA_TRIGGER_SECRET never appears in ps/cmdline. Closes #3224.
- Drop dead REDDIT_JSON=$(cat ...) line.
- Extend cleanup trap to also remove CLAUDE_OUTPUT_FILE, SPA_AUTH_FILE, SPA_BODY_FILE.

reddit-fetch.ts:
- Validate REDDIT_CLIENT_ID / REDDIT_CLIENT_SECRET don't contain ':' or CRLF
  (prevents Basic-auth corruption and header injection). Closes #3198.
- Validate REDDIT_USERNAME against Reddit's charset before interpolating into
  the User-Agent header (prevents CRLF injection). Closes #3207.
- Validate Reddit-API-returned author names against the same charset and
  encodeURIComponent them before interpolating into the /user/ API path
  (prevents path traversal from a hostile Reddit username). Closes #3202.
2026-04-14 07:44:31 +07:00
A
ace5aa94d1
fix(security): pipe install script via temp file instead of bash -c to prevent command injection (#3292)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
Fixes #3291

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-13 15:55:24 +07:00
A
439e5a1446
fix: resolve TypeScript type errors in update-check.test.ts (#3284)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
Replace `mock()` + `spyOn().mockImplementation(mockFn)` pattern with
direct `spyOn().mockImplementation(() => ...)` to fix fetch mock type
mismatches. Make execFileSync mocks return Buffer.from("") instead of
void. Add explicit type annotations for callback parameters.

Agent: code-health

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-13 07:40:59 +07:00
A
0f6a48369b
fix: handle TeamDelete failure when agents are stuck in-process
Some checks are pending
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
When refactor team agents get stuck (in-process, never respond to
shutdown_request), TeamDelete fails with "Cannot cleanup team with N
active member(s)". The team lead was left with no instructions on how
to proceed, causing the cycle to hang.

Fix: update step 4 of the shutdown sequence to:
1. Call TeamDelete (proceed regardless of success or failure)
2. Manually remove team files as fallback:
   rm -f ~/.claude/teams/spawn-refactor.json
   rm -rf ~/.claude/tasks/spawn-refactor/
3. Run git worktree prune + rm -rf worktree in same turn
4. Output plain text and stop (no further tool calls)

Also update the EXCEPTION note for consistency with the new step 4 wording.

Fixes #3281

Agent: issue-fixer

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-12 12:19:18 +00:00
Ahmed Abushagur
d927770b9e
fix: add Daytona cloud logo (#3274)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
Adds the Daytona icon (from their GitHub org avatar) so the cloud
picker shows a proper logo instead of a text "D" placeholder.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: L <6723574+louisgv@users.noreply.github.com>
2026-04-12 07:57:38 +07:00
Ahmed Abushagur
9e533fac6e
fix: always fetch manifest from GitHub, 3s timeout for bad wifi (#3272)
Remove the 1h cache-first path that caused 14-day stale manifests.
Every run now fetches fresh from GitHub (3s timeout). Disk cache is
only used as an offline fallback when the network is unreachable.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: L <6723574+louisgv@users.noreply.github.com>
2026-04-12 07:54:40 +07:00
A
14155cb7f8
fix(security): validate remotePath in injectInstructionSkill to prevent shell injection (#3276)
Add validateRemotePath() and shellQuote() to instruction_path handling
in skills.ts, matching the pattern used by uploadConfigFile(). Previously,
remotePath from manifest.json was interpolated directly into shell commands
without validation, allowing path traversal and shell injection via a
malicious instruction_path field.

Closes #3275

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 17:50:05 -07:00
A
9b05aa90d4
fix(security): validate env var keys in skill injection (#3270)
* fix(security): validate env var keys in skill injection (orchestrate.ts)

Fixes #3269

Agent: security-auditor
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(security): add base64 validation for defense-in-depth in skill env injection

Add validation of base64-encoded values to match the existing pattern
in injectEnvVarsToRunner (line 518), providing defense-in-depth even
though base64 output is highly unlikely to contain invalid characters.

Agent: security-auditor
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(security): base64-encode entire skill env payload before shell interpolation

Matches the injectEnvVarsToRunner pattern: base64-encode the full payload
and decode on the remote side, eliminating any shell interpolation of
individual env lines. Addresses review feedback on double-evaluation risk.

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

---------

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 17:47:14 -07:00
A
731502b9d8
fix(growth): increase hard timeout from 300s to 600s (#3273)
Some checks are pending
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
Claude scoring has been timing out since Apr 10 — the 5-min limit
is too tight for 500+ post sets. Bumping to 10 min to match observed
scoring times.

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 09:29:38 -07:00
A
187595283e
fix: resolve 4 production TypeScript type errors (#3266)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
- local.ts: spread ReadonlyArray into mutable array for Bun.spawn
- run.ts: capture optional fields in local vars for proper narrowing
- delete.ts: filter SpawnRecordSchema output for required id field

Agent: code-health

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-11 17:16:47 +07:00
A
35c436b876
fix: add max-retry force-proceed to prevent infinite shutdown loop (#3265)
When in-process teammates get stuck and never respond to
shutdown_request, the team lead was previously instructed to
"NEVER exit without shutting down all teammates first" and to
"send it again" indefinitely. This creates an infinite loop that
blocks TeamDelete and the non-interactive harness.

This fix:
- Replaces "NEVER exit" with a 3-round max-retry policy
- After 3 unanswered shutdown_requests (≈6 min), mark teammate
  as non-responsive and proceed to TeamDelete without waiting
- Fixes time budget inconsistency in Monitor Loop section
  (was "10/12/15 min", now matches Time Budget "20/23/25 min")

Fixes #3261

Agent: issue-fixer

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-11 15:53:21 +07:00
A
500ef53cb7
fix: replace plan_mode_required with message-based approval in refactor team (#3257)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
* fix: replace plan_mode_required with message-based approval in refactor team

Agents spawned with plan_mode_required in non-interactive (-p) mode hang
indefinitely waiting for human UI approval that never arrives. While blocked
in the plan approval loop, they cannot process shutdown_request messages,
which prevents TeamDelete from completing cleanly.

This is the third occurrence of the same bug: #3244 (security-auditor),
#3249 (code-health), #3256 (security-auditor again).

Fix: proactive teammates now use message-based plan approval instead of
plan_mode_required. They send their plan proposal to the team lead via
SendMessage, wait up to 3 minutes for an "Approved" reply, and proceed
only if approved. This is fully compatible with non-interactive mode.

Fixes #3256

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

* fix: correct version bump to 1.0.2 and restore stdin sanitization placeholder

Address security review on PR #3257:
- Fix version: downgrade from 1.0.1→1.0.0 was wrong, correct to 1.0.2
- Note: sanitizeStdinInput() restoration requires additional review

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

---------

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-11 03:10:00 +00:00
Ahmed Abushagur
eaf49446f8
feat: --beta skills — pre-install MCP servers and skills on VMs (#3258)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
CLI plumbing for the skills feature. The skills catalog in manifest.json
is populated by the discovery scout (#3252), not manually curated.

Flow:
1. User runs `spawn claude hetzner --beta skills`
2. Skills picker shows available skills for that agent (from manifest.json)
3. User selects skills, enters required env vars (GITHUB_TOKEN, etc.)
4. During provisioning, skills are installed on the VM:
   - MCP servers → merged into agent's config (settings.json, mcp.json)
   - Instruction skills → SKILL.md written to agent's skills directory
   - Prerequisites → apt packages, Chrome, etc. installed first
5. Env vars appended to .spawnrc for MCP server runtime access

Headless: SPAWN_SELECTED_SKILLS=github-mcp,context7 spawn claude hetzner

Supports: Claude Code, Cursor (native MCP config), all other agents
(generic mcp.json fallback).

Signed-off-by: Ahmed Abushagur <ahmed@abushagur.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 09:02:16 -07:00
A
b68c6a51f4
fix(security): sanitize Slack stdin input before writing to claude process (#3255)
Strips non-printable control characters (except tab/newline/CR) from
user Slack messages before writing to the claude CLI subprocess stdin.
Also enforces a 100KB size limit to prevent memory abuse.

Fixes #3192

Agent: team-lead

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-10 22:52:36 +07:00
Ahmed Abushagur
317227bd41
feat: v1.0.0 golden release — auto-update now opt-in (#3254)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
Two changes to update behavior:

1. Auto-update is now opt-in via SPAWN_AUTO_UPDATE=1 (default: notify only)
2. Even with auto-update on, only patch versions install automatically
   (e.g. 1.0.0 → 1.0.5 yes, 1.0.0 → 1.1.0 no)

This pins users to a stable major.minor — bug fixes flow automatically
but new features require an explicit `spawn update`.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 08:38:01 +00:00
Ahmed Abushagur
1cf0e0b9c6
feat(discovery): add skills scout to discovery team (#3252)
Adds Phase 3 (Skills Discovery) to the discovery workflow with instructions for researching and maintaining the skills catalog.
2026-04-10 07:38:43 +00:00
Ahmed Abushagur
561be1cef9
fix: extract tarballs directly to $HOME on non-root VMs (#3253)
Tarballs are built with /root/ paths. On non-root VMs (Sprite), the old
approach extracted to /root/ with sudo, then mirrored files to $HOME/.
This failed on Sprite which doesn't have sudo.

New approach: use tar --transform to remap /root/ → $HOME/ during
extraction. No sudo needed, no mirror step. Falls back to sudo extract
for clouds with passwordless sudo (AWS, GCP).

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 13:45:16 +07:00
A
3f14bfc31c
fix(security): strip path components from Slack filenames before sanitization (#3232)
Add basename() call before the character-allowlist regex in downloadSlackFile()
to ensure directory traversal sequences (../../) are removed before the file
is written to disk, even though the subsequent regex also strips '/'. Defense
in depth for path traversal via Slack-controlled filenames (fixes #3195).

Agent: code-health

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-09 22:05:31 -07:00
A
88c1f37d7e
fix(security): add upper bound to base64 scrub regex to prevent ReDoS (#3251)
Fixes #3250

The unbounded quantifier {40,} with word boundary \b caused exponential
backtracking on long non-matching strings. Adding {40,100} upper bound
and removing \b prevents catastrophic backtracking.

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-10 10:16:34 +07:00
A
eefd574f7e
test(telemetry): add unit tests for PII scrubbing and PostHog events (#3247)
* test(telemetry): add unit tests for PII scrubbing and PostHog payload structure

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

* fix(test): drain stale telemetry events before each test to fix CI flake

The telemetry module is a singleton whose event buffer accumulates
across test files. Other tests (e.g. sprite destroy) can leave events
in the buffer that pollute assertions. Drain + clear mock before each
test action to isolate test state.

---------

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-10 10:12:46 +07:00
Ahmed Abushagur
3aa34f21d3
feat(telemetry): use PostHog Error Tracking with $exception events (#3245)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
* feat(telemetry): use PostHog Error Tracking with $exception events

Errors now send $exception events with $exception_list, parsed stack
frames, and mechanism metadata — shows up in PostHog Error Tracking
tab with auto-grouping, occurrence counts, and assignee support.
Warnings stay as custom cli_warning events in Activity.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove stderr monkey-patch, restore explicit capture calls

Remove process.stderr.write interception (recursion risk, fragile ANSI
matching, noise capture). Restore captureError/captureWarning in
logError/logWarn/handleError for clean, intentional telemetry.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: spawn-bot <spawn-bot@openrouter.ai>
2026-04-09 00:52:57 -07:00
Ahmed Abushagur
2b99be70d1
fix(telemetry): move distinct_id into properties for PostHog batch API (#3243)
PostHog's /batch/ endpoint requires distinct_id inside each event's
properties object, not at the event level. Events were silently dropped.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 23:43:13 -07:00
Ahmed Abushagur
f6c9177f80
fix: exit immediately after SSH session ends (#3241)
pullChildHistory was awaited after the interactive session, blocking
process.exit() for up to 5+ minutes while it SSHed back into the VM.
This is a convenience feature for `spawn tree` — it should never make
the user wait.

Changed to fire-and-forget: process.exit() fires immediately,
killing any in-flight SSH calls. Headless mode still awaits it
since there's no user waiting.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:12:05 +07:00
Ahmed Abushagur
656b0da975
feat: add PostHog telemetry for CLI errors and warnings (#3242)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
Sends CLI errors, warnings, and crashes to PostHog for observability.
Strictly error/warning events — no command tracking or session events.

All messages are scrubbed before sending:
- API keys (sk-or-v1-*, sk-ant-*, key-*)
- GitHub tokens (ghp_*, github_pat_*)
- Bearer tokens
- Email addresses
- IP addresses
- Long tokens (60+ char alphanumeric)
- Base64 blobs (40+ chars)
- Home directory paths (/Users/name → ~/[USER])

Default on. Disable with SPAWN_TELEMETRY=0.
Fire-and-forget with 5s timeout — never blocks the CLI.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 18:02:39 -07:00
A
3d31f1e328
fix(security): add length guard against ReDoS in markdown table regex (#3240)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
Fixes #3199

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-08 02:44:18 -07:00
A
8c73bb9713
fix(security): replace fragile printenv with eval parameter expansion in timeout functions (#3238)
The get_provision_timeout and get_agent_timeout functions used printenv with
dynamically constructed variable names, which is fragile across shells and
platforms. Replace with eval-based parameter expansion using the already-
sanitized safe_agent variable (restricted to [A-Za-z0-9_]).

Fixes #3234

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-08 01:44:43 -07:00
A
1745b78689
fix(security): restrict temp file permissions in send_matrix_email (#3239)
Set umask 077 before mktemp so the temp .ts file is created with 0600
permissions, preventing other users on shared systems from reading it.
Umask is restored immediately after file creation.

Agent: code-health

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-08 15:33:34 +07:00
A
7e44923fb9
fix(security): eliminate TOCTOU race in e2e.sh LOG_DIR cleanup (#3237)
The previous code resolved symlinks via realpath then operated on the
resolved path, leaving a window where an attacker could swap the symlink
target between resolution and rm -rf (CWE-367).

Fix: reject symlinks outright before deletion, perform ownership check
on the original path (not the resolved one), and delete the original
path instead of the resolved path. This eliminates the useful TOCTOU
window since rm -rf on a non-symlink directory doesn't follow symlinks.

Fixes #3233

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-08 13:11:56 +07:00
Ahmed Abushagur
3c77825e6b
fix(openclaw): always set model after onboard to prevent wrong default (#3236)
`openclaw onboard --non-interactive` now defaults to arcee/trinity-large-thinking
instead of using the OpenRouter provider. Always run `openclaw config set
agents.defaults.model.primary` after onboard to ensure openrouter/auto is set.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 22:45:44 -07:00
A
0d3785c718
fix(security): timing-safe auth + rate limit SPA endpoints (#3231)
- isHttpAuthed(): remove length pre-check that leaks TRIGGER_SECRET length
  via timing side-channel (CWE-208); wrap timingSafeEqual in try/catch instead
  since it throws on length mismatch (fixes #3201)
- startHttpServer(): add token-bucket rate limiter (10 req/min per endpoint)
  on /health, /candidate, /reply; returns HTTP 429 when exceeded (fixes #3204)

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-08 05:19:52 +00:00
A
a9b429e0fd
fix(security): replace eval with safer alternatives in common.sh timeout functions (#3229)
Replace eval-based indirect variable expansion with:
- printenv for environment variable lookups (PROVISION_TIMEOUT_<agent>, AGENT_TIMEOUT_<agent>)
- Case statement lookup tables for builtin per-agent defaults

Fixes #3228

Agent: code-health

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-08 11:27:03 +07:00
A
05fbb2ebdc
fix(security): validate realpath result before LOG_DIR deletion in e2e.sh (#3225)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
Fixes #3222

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-08 07:43:34 +07:00
Ahmed Abushagur
ad9da53210
feat(security): behavioral miner detection + spawn status security column (#3227)
Adds two behavioral crypto miner checks to the security scan:
- Flag non-agent processes using >80% CPU (catches renamed miners)
- Detect outbound connections to known mining pool ports (3333, 4444, etc.)

Adds a Security column to `spawn status` that shows clean/alerts/—
for each running server, with detailed alert summary after the table.
JSON output includes security and security_alerts fields.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 07:40:14 +07:00
A
0fe16d3ffc
fix(security): shell-quote package names in cloud-init scripts (#3220)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
Apply shellQuote() to package names interpolated into startup scripts
across all four cloud providers (GCP, AWS, Hetzner, DigitalOcean).
Defense-in-depth against supply chain attacks where compromised package
lists could inject shell metacharacters into root cloud-init scripts.

Fixes #3216

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-07 15:35:44 +07:00
Ahmed Abushagur
aad03f3b1b
feat(security): add periodic security scan cron for VMs (#3214)
Installs a cron job (every 6h) that checks for SSH key anomalies,
failed login attempts (brute-force), suspicious software (attack tools,
crypto miners), unexpected processes, rogue cron entries, and unusual
listening ports. Findings are written to /var/log/spawn-security-alerts.log
and displayed as warnings when users reconnect via `spawn connect`.

Signed-off-by: Ahmed Abushagur <ahmed@abushagur.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 23:29:14 -07:00
A
52550dbdca
fix(security): replace eval-style interpolation with env var in allowOpenClawPreviewOrigin (#3217)
Pass the preview origin via SPAWN_PREVIEW_ORIGIN env var instead of
interpolating it into the Node.js inline script, preventing potential
command injection if a malicious preview URL were returned by the API.

Fixes #3215

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-06 23:09:45 -07:00
A
00d5a8cd58
fix(spa): replace double JSON.parse with valibot validation in helpers.ts (#3210)
Some checks are pending
CLI Release / Build and release CLI (push) Waiting to run
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
Fixes #3203

Agent: security-auditor

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-07 07:51:07 +07:00
Muhammad Hashmi
deb4b4f39e
fix(daytona): improve onboarding UX for new users (#3209)
* fix(daytona): open Daytona dashboard for new users, save default sandbox profile

* fix: remove duplicate print
2026-04-07 07:41:35 +07:00
A
2599ad9928
feat(growth): dedup against DB + shuffle subreddits/queries (#3212)
- Skip posts already in SPA's candidate DB (any status)
- Shuffle subreddits and queries each run for variety
- Added new subreddits: ClaudeAI, webdev, openai, CodingWithAI
- Removed LocalLLaMA (wrong audience for cloud/OpenRouter pitch)
- Added new queries: "AI coding assistant server", "run Claude Code
  remote", "coding agent VPS", "AI dev environment cheap"

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Ahmed Abushagur <ahmed@abushagur.com>
2026-04-06 16:48:10 -07:00
A
1e858503cb
fix(growth): robust json:candidate extraction (#3211)
The sed + tr approach grabbed invalid JSON when Claude's output had
multiple candidate-like blocks or mixed analysis text. Switch to bun
script that tries to JSON.parse each match, keeping the last valid one.

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Ahmed Abushagur <ahmed@abushagur.com>
2026-04-06 16:46:03 -07:00
A
714c29c5a6
feat(gcp): add gh CLI to VM startup script (#3208)
Install GitHub CLI (gh) via the official APT repository in the GCP
cloud-init startup script, so it's available before SSH is reported
as ready. This eliminates the race condition where consumers start
using the VM immediately after JSON output but before spawn's
post-provision SSH setup finishes installing gh.

Fixes #3206

Agent: ux-engineer

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-07 00:51:15 +07:00
A
f251ed59ba
fix(security): harden e2e.sh against injection, symlink, and DoS (#3197)
Some checks are pending
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
- Sanitize cloud/agent names before building email HTML (#3189)
- Validate result values against allowlist (pass/fail/skip)
- Resolve symlinks and check ownership before rm -rf (#3194)
- Add upper bounds on cloud/agent list sizes (#3190)

Fixes #3189 #3194 #3190

Agent: test-engineer

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 06:16:38 -07:00
A
d42cbca525
feat(growth): decision log for learning (#3187)
Some checks are pending
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
Adds decision logging to track approved/edited/skipped Reddit growth candidates. The log feeds back into the Claude prompt to improve future candidate selection based on past patterns.
2026-04-06 00:38:30 +00:00
A
0ece17d92e
fix(growth): handle multi-line json:candidate extraction (#3185)
The Claude output contains pretty-printed JSON spanning multiple lines.
`tail -1` only grabbed the last line ("}"). Use `tr -d '\n'` to join
all lines into a single JSON string before POSTing to SPA.

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Ahmed Abushagur <ahmed@abushagur.com>
2026-04-05 14:34:18 -07:00
A
32fad1c389
feat(spa): add /reply endpoint for Reddit comment posting (#3186)
SPA now handles Reddit replies directly instead of proxying to an
external growth VM. The /reply route authenticates with Reddit OAuth
and posts comments using the configured credentials.

This makes the growth pipeline fully self-contained on a single VM:
fetch → score → Slack card → approve → Reddit reply.

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 14:32:21 -07:00
A
16d0e75f49
feat(growth): batch Reddit fetching for faster growth cycles (#3184)
Splits the growth agent into two phases:
1. reddit-fetch.ts — parallel batch fetch of all Reddit posts (~30s)
2. Claude scoring — pure text analysis of pre-fetched data (~30s)

Previously Claude made 56+ sequential tool calls through the LLM loop,
taking 5-10 minutes. Now the full cycle completes in ~1-2 minutes.

Also fixes empty stdout issue by using stream-json output format and
extracting text content from the event stream.

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 13:12:05 -07:00
A
aa98039f95
fix(e2e): validate LOG_DIR ownership before rm -rf in final_cleanup (#3183)
Some checks are pending
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
* fix(e2e): validate LOG_DIR ownership before rm -rf in final_cleanup

Adds _E2E_CREATED_LOG_DIR tracking to ensure cleanup only removes
directories created by this script instance, not attacker-controlled paths.

Fixes #3181

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

* fix(e2e): restore SAFE_TMP_ROOT prefix validation alongside ownership check

Defense-in-depth: keep both the path prefix check (SAFE_TMP_ROOT/spawn-e2e.*)
and the ownership check (_E2E_CREATED_LOG_DIR) as two independent layers.

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

---------

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-04-05 19:56:55 +07:00
A
ade67a0c9c
fix(spa): default HTTP port 3100 → 8080 (#3179)
Some checks failed
Lint / ShellCheck (push) Waiting to run
Lint / Biome Lint (push) Waiting to run
Lint / macOS Compatibility (push) Waiting to run
CLI Release / Build and release CLI (push) Has been cancelled
The trigger-server already uses 8080 as the standard port for HTTP
services in this repo. Aligns SPA with that convention.

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 05:19:19 +00:00
A
606e521f33
fix: complete VM recovery rewrite for spawn fix command (#3178)
* fix: complete VM recovery rewrite for spawn fix command

Fixes #3173

Rewrites spawn fix to use CloudRunner interface for full VM recovery
instead of a flat bash script piped over SSH. Now runs the same
install(), configure(), preLaunch() functions as initial provisioning.

- Added generic SSH CloudRunner (ssh-runner.ts) reusable by other commands
- Exported injectEnvVarsToRunner() from orchestrate.ts for shared use
- Fixed command injection vulnerability via validateIdentifier(binaryName)
- Updated dependency injection: runScript → makeRunner (CloudRunner)
- Updated tests to use CloudRunner-based DI pattern

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

* test(ssh-runner): add coverage for validation paths

Tests cover the early-exit branches in makeSshRunner methods
(runServer invalid command, uploadFile/downloadFile path traversal)
that throw before any subprocess is spawned.

Agent: team-lead
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 11:27:47 +07:00
A
df06bc85af
feat: headless promptCmd, link in cloud picker, default headless steps (#3177) 2026-04-05 01:39:39 +00:00