feat(cli): add sentry cli import for .sentryclirc migration#987
Conversation
|
Codecov Results 📊✅ 7075 passed | Total: 7075 | Pass Rate: 100% | Execution Time: 0ms 📊 Comparison with Base Branch
All tests are passing successfully. ❌ Patch coverage is 44.68%. Project has 14819 uncovered lines. Files with missing lines (4)
Coverage diff@@ Coverage Diff @@
## main #PR +/-##
==========================================
- Coverage 77.10% 76.56% -0.54%
==========================================
Files 322 324 +2
Lines 62208 63212 +1004
Branches 0 0 —
==========================================
+ Hits 47963 48393 +430
- Misses 14245 14819 +574
- Partials 0 0 —Generated by Codecov Action |
613ee43 to
5be20aa
Compare
Post-Review FixesAddressed findings from a thorough self-review: Critical fixes
Medium fixes
Minor fixes
|
de6ce74 to
fed87ea
Compare
4b23d81 to
6124f60
Compare
2f767d2 to
f4c287a
Compare
7c8e024 to
11bb64d
Compare
f6f7f59 to
ff3f635
Compare
d9f9654 to
32d44fb
Compare
46b6f80 to
ad645a7
Compare
1cb4cc6 to
967798e
Compare
967798e to
a0547d3
Compare
Add a one-time import path for users migrating from the old Rust-based sentry-cli. Two complementary features: 1. `sentry cli import` — explicit command that scans for .sentryclirc files, shows what was found, and imports settings into SQLite with proper host scoping. Supports --yes (CI), --dry-run, --url (trust override), and --skip-validation. 2. Auto-detect middleware — when any command hits AuthError and a .sentryclirc token passes the trust gate, prompts to import before falling back to the OAuth login flow. Security: content-based trust model (same-file rule). Token and URL must originate from the same file — no path is inherently trusted. SHA-256 file hashes stored at import time detect post-import tampering. Auto-prompt disabled in CI (isatty check); project-local files excluded. Also updates the login command's rcTokenHint to mention `sentry cli import` as an alternative to `--token`.
a0547d3 to
43c2e22
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d5a0667. Configure here.
| return "*".repeat(token.length); | ||
| } | ||
| return `${token.slice(0, 4)}${"*".repeat(token.length - 8)}${token.slice(-4)}`; | ||
| } |
There was a problem hiding this comment.
Duplicate maskToken with divergent behavior across files
Medium Severity
A new maskToken is exported from sentryclirc-import.ts while an existing maskToken already exists in src/lib/formatters/human.ts. They have different behavior: the existing one returns "****" for short tokens and shows first 8 + last 4 for long tokens; the new one returns "*".repeat(length) for short tokens and shows first 4 + last 4 for long tokens. Two exported functions with the same name but different masking semantics in the same codebase creates confusion about which to use and risks inconsistent token display.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit d5a0667. Configure here.
| plan.newFields.push("url"); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Stale cross-file warnings persist after --url override
Low Severity
applyUrlOverride mutates the plan's effective.url, effectiveSources, trusted, and isSaas fields but does not clear stale warnings from plan.warnings that buildSecurityWarnings already built from the original plan state. When --url is passed to resolve a cross-file trust issue, the user still sees the "Token and URL come from different files" warning — even though --url exists precisely to bypass that concern. Similarly, a stale sntrys_ claim mismatch warning about the original URL may remain alongside a new one for the override URL.
Reviewed by Cursor Bugbot for commit d5a0667. Configure here.
| // If auth was cleared since the import (e.g., logout), re-offer import | ||
| if (!hasStoredAuth()) { | ||
| return true; |
There was a problem hiding this comment.
markImportDeclined never breaks re-prompt loop when a prior import record exists
When token validation fails inside executeImport, markImportDeclined is called to suppress future auto-prompts, but the existing completed import record is never cleared. On every subsequent command, isImportNeededAsync finds the old import record first, sees no stored auth, and returns true again — the decline record branch is only reached when there is no import record. The user is stuck in an infinite auto-prompt loop until they fix the token or explicitly run sentry cli import.
Evidence
isImportNeededAsync(line 784): checksgetImportRecord()first; if a record exists, the function returns without ever consulting the decline record.- When
hasStoredAuth()returnsfalse(line 792) — e.g., after a 401 cleared auth — the function returnstrue(import needed), regardless of any decline record. executeImport(line ~654, confirmed read): callsmarkImportDeclined(plan.sources)after validation failure but does not callclearImportRecord(), leaving the old completed import record in the DB.- The inline comment on that same line reads "Declined is checked before hasStoredAuth, breaking the loop" — this is incorrect; the decline record is only consulted in the
elsebranch at line 799, which is unreachable when the import record exists. - Reproducer: successful import → logout → re-prompt fires → user accepts → token is invalid (401) →
markImportDeclinedruns → next run: old import record still present, no auth →isImportNeededAsyncreturnstrue→ loop.
Identified by Warden find-bugs · L3M-JM9


Summary
Adds a one-time import path for users migrating from the old Rust-based
sentry-cli, addressing #975. Two complementary features share a core import engine:Feature A:
sentry cli import— Explicit CommandScans for
.sentryclircfiles, shows what was found (masked token, URL, org, project with file provenance), and imports settings into SQLite with proper host scoping.Feature B: Auto-Detect Middleware
When any command hits
AuthError("not_authenticated")and a non-project-local.sentryclirchas a token that passes the trust gate, prompts to import before falling back to the OAuth login flow. Disabled in CI (isattycheck). User can decline once (never asked again).Security: Content-Based Trust Model
No file path is inherently trusted (on CI, even
~/.sentryclirccan be planted). Trust is determined by the same-file rule: the effective token and URL must originate from the same.sentryclircfile (co-presence means an attacker who planted both already has the token — nothing to steal via redirect).--urlSHA-256 hash change detection: File content hashes stored at import time. Post-import mutations (e.g., attacker appends URL) clear the record and trigger re-evaluation.
Login Hint
Updates
rcTokenHintto mentionsentry cli importas an alternative to--token.Changes
src/lib/sentryclirc-import.ts(new) — Core import engine: discover, classify, plan (merge + trust analysis), execute (store + validate), state tracking (hash-verified completion, decline)src/commands/cli/import.ts(new) —sentry cli importcommand with formatterssrc/commands/cli/index.ts— Registerimportroutesrc/cli.ts— AddrcImportMiddleware+tryRcImport()in error middleware chainsrc/lib/sentryclirc.ts— ExportgetGlobalPaths()andtryReadSentryCliRc()src/commands/auth/login.ts— UpdatercTokenHintto mention import commandtest/lib/sentryclirc-import.test.ts(new) — 31 unit teststest/lib/sentryclirc-import.property.test.ts(new) — 7 property-based testsCloses #975