Harden command execution in init wizard beyond string-based validation
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 25/100
- Issue type
- Feature
- Clarity
- Needs clarification
- Activity status
- Stale
- Tech stack
- node.js, shell, typescript
- Domain
- cli, operating-systems, security
Research direction
Read cli/src/lib/init/local-ops.ts, starting with validateCommand() and runSingleCommand() to understand the existing validation, shell usage, timeout, and output limits. Compare the Linux and macOS sandboxing options listed in the issue and determine which command paths need OS-specific handling. Done means the chosen hardening approach is implemented without removing the existing checks and its security boundaries are verified.
Written by the indexing model from the issue text.
Description
Summary
cli/src/lib/init/local-ops.ts executes commands from the API server using shell: true. The current protections are string-based and could be bypassed by a sufficiently creative payload. Before sentry init exits experimental mode, we should add OS-level sandboxing.
Current protections
validateCommand() (line 140) — three layers of string validation:
- Shell metacharacter blocking — rejects commands containing
;,&&,||,|,`,$(,$, quotes, redirects (>,<), braces, globs, newlines, backslashes - Env var injection blocking — rejects commands where the first token contains
= - Dangerous executable blocklist — rejects 40+ executables (
rm,curl,sudo,ssh,bash,eval, etc.) but only checks the first token — e.g.npm exec -- rm -rf /would pass becausenpmis the primary executable
safePath() / path validation — prevents path traversal and symlink escapes outside the project root.
runSingleCommand() (line 426) — spawns via spawn(command, [], { shell: true, cwd, ... }) with a timeout and output truncation (64KB cap).
Gap
All protections are string-based pattern matching. The command still runs under shell: true with full user privileges. Potential bypasses:
- Package manager subcommands that execute arbitrary code (e.g.
npm exec, lifecycle scripts) - Unicode/encoding tricks that survive validation but are interpreted differently by the shell
- Future commands the server might send that don't match current patterns
Suggested hardening (Linux)
These are additive layers — the string validation remains as a fast first pass.
| Technique | How | Benefit |
|---|---|---|
Linux namespaces / unshare |
unshare --net --mount --pid wrapper |
Network isolation, mount isolation, PID isolation |
| Landlock LSM | Restrict filesystem access to project dir + node_modules + package manager cache | Prevents reads/writes outside expected paths even if shell escapes |
bubblewrap (bwrap) |
Lightweight sandbox with readonly / bind-mount, writable project dir only |
Strong filesystem + namespace isolation in one tool |
| Drop to restricted user | runuser / setuid to a no-login user for spawned commands |
Limits damage from any escape |
| Read-only filesystem mounts | Bind-mount / as read-only, whitelist project dir as writable |
Commands can't modify system files |
| Network namespace isolation | Empty network namespace for commands that don't need network (e.g. codemods) | Prevents data exfiltration |
--shell=false where possible |
For simple commands like npx @sentry/wizard, split into [executable, ...args] and avoid shell entirely |
Eliminates shell injection surface |
macOS considerations
macOS lacks namespaces/Landlock. Options are more limited:
sandbox-exec(deprecated but functional) with a restrictive profile- Avoid
shell: truewhere possible by splitting commands into argv arrays
Priority
Low — the current string validation is a reasonable defense for an experimental feature where commands originate from our own API server. This becomes more important if/when the command surface expands or the feature exits experimental.
- Dominant language
- TypeScript
- Stars
- 121
- Forks
- 14
- Avg merge
- 22h 3m
- Merged PRs (30d)
- 94
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from getsentry/cli
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
Difficulty 3/5 1-2 days Newbie friendliness 68/100
-
Difficulty 3/5 1-2 days Newbie friendliness 65/100
-
bug jared
-
jared
Difficulty 4/5 3-5 days Newbie friendliness 52/100
Similar issues
-
S: triage
Difficulty 1/5 Under an hour Newbie friendliness 85/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
fix(errors): EHOSTUNREACH from a happy-eyeballs connect is reported as a resolver error (STAMP-80) Open
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
snapshot-labs/stamp#666 ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
GauravKarakoti/SecureFlow#1070 · 1 comment ·
-
feature:Languages/Translations good first issue ready Web
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
digitalfabrik/integreat-app#4394 ·