From dac1dd007fe38b9efd2725be9bc754c9292a6e22 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 22 Jun 2026 12:16:49 +0000 Subject: [PATCH 1/3] fix(sourcemap): guard JSON.parse in injectDebugId against malformed .map files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The JSON.parse call at line 155 of debug-id.ts was unguarded — if a .map file contained malformed JSON (truncated, corrupt, or not a sourcemap), the entire sourcemap inject command crashed with an opaque SyntaxError. Every other JSON.parse in the codebase is wrapped in try/catch. This adds the same pattern, throwing a descriptive ValidationError that includes the file path so users know which .map file is problematic. Co-authored-by: Miguel Betegón --- src/lib/sourcemap/debug-id.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/lib/sourcemap/debug-id.ts b/src/lib/sourcemap/debug-id.ts index 25608c2df..62f838fcf 100644 --- a/src/lib/sourcemap/debug-id.ts +++ b/src/lib/sourcemap/debug-id.ts @@ -13,6 +13,7 @@ import { createHash } from "node:crypto"; import { readFile, writeFile } from "node:fs/promises"; +import { ValidationError } from "../errors.js"; import { logger } from "../logger.js"; import { type DecodedInlineMap, @@ -152,7 +153,16 @@ export async function injectDebugId( newJs += `\n${DEBUGID_COMMENT_PREFIX}${debugId}\n`; // --- Mutate sourcemap --- - const map = JSON.parse(mapContent) as SourcemapJson; + let map: SourcemapJson; + try { + map = JSON.parse(mapContent) as SourcemapJson; + } catch (error) { + log.debug("Failed to parse sourcemap JSON", error); + throw new ValidationError( + `Failed to parse sourcemap ${mapPath}: file is not valid JSON`, + "mapPath" + ); + } mutateSourcemap(map, debugId, { offsetMappings: !skipSnippet }); // Write both files concurrently From 6ea8f587c849f95ea503d4e3af3f426277e8ad87 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 22 Jun 2026 12:18:33 +0000 Subject: [PATCH 2/3] fix(logs): cap per_page at API_MAX_PER_PAGE in log list APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both listLogs() and listTraceLogs() passed the user's --limit value directly as per_page to the Sentry API. When limit > 100, the API silently caps at 100 items, but the caller's hasMore check (logs.length >= flags.limit) evaluates to false, hiding remaining data. This violates the project convention documented in AGENTS.md: 'Never pass a per_page value larger than API_MAX_PER_PAGE to the API.' Cap both calls with Math.min(limit, API_MAX_PER_PAGE) to match the pattern used in autoPaginate() and other list commands. Co-authored-by: Miguel Betegón --- src/lib/api/logs.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/api/logs.ts b/src/lib/api/logs.ts index d128091f2..f01c8ecd8 100644 --- a/src/lib/api/logs.ts +++ b/src/lib/api/logs.ts @@ -161,7 +161,7 @@ export async function listLogs( field: fields, project: isNumericProject ? [Number(projectSlug)] : undefined, query: fullQuery || undefined, - per_page: options.limit || API_MAX_PER_PAGE, + per_page: Math.min(options.limit || API_MAX_PER_PAGE, API_MAX_PER_PAGE), statsPeriod: options.start || options.end ? undefined @@ -343,7 +343,7 @@ export async function listTraceLogs( : (options.statsPeriod ?? "14d"), start: options.start, end: options.end, - per_page: options.limit ?? API_MAX_PER_PAGE, + per_page: Math.min(options.limit ?? API_MAX_PER_PAGE, API_MAX_PER_PAGE), query: options.query, sort: toApiSort(options.sort), }, From 2da6f173b8759e7961fe24bebff127863fce3856 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 22 Jun 2026 12:20:16 +0000 Subject: [PATCH 3/3] fix(dashboard): drop nextCursor on pagination overshoot in revisions list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When multi-page fetching accumulates more results than flags.limit, the excess items are trimmed with .slice(0, flags.limit) but the API cursor from the last fetched page is still stored. On '-c next', those trimmed items are skipped entirely. This matches the autoPaginate() pattern in infrastructure.ts (lines 419-420) which explicitly drops nextCursor when results overshoot to prevent skipping items. Co-authored-by: Miguel Betegón --- src/commands/dashboard/revisions.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/commands/dashboard/revisions.ts b/src/commands/dashboard/revisions.ts index 874ba27ba..bc06c020a 100644 --- a/src/commands/dashboard/revisions.ts +++ b/src/commands/dashboard/revisions.ts @@ -199,8 +199,12 @@ export const revisionsCommand = buildCommand({ ); const trimmed = results.slice(0, flags.limit); - const hasMore = results.length > flags.limit || !!nextCursor; - const cursorToStore = hasMore ? nextCursor : undefined; + const overshot = results.length > flags.limit; + const hasMore = overshot || !!nextCursor; + // When multi-page fetch overshoots the limit, the API cursor points past + // trimmed items — storing it would skip them on '-c next'. Drop it to + // match autoPaginate() behavior (see infrastructure.ts lines 419-420). + const cursorToStore = !overshot && hasMore ? nextCursor : undefined; advancePaginationState( PAGINATION_KEY,