Skip to content

Handle unauthorized task detail deep links#1287

Merged
priosshrsth merged 6 commits into
mainfrom
cursor/OUT-3831-cursor-automated-triage-response-51f9
Jun 12, 2026
Merged

Handle unauthorized task detail deep links#1287
priosshrsth merged 6 commits into
mainfrom
cursor/OUT-3831-cursor-automated-triage-response-51f9

Conversation

@cursor

@cursor cursor Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Changes

  • Treat APIError 401 responses from loadTask as a non-renderable task on the detail page, matching the existing 404 handling.
  • Preserve the underlying TasksService.checkClientAccessForTask authorization guard so inaccessible tasks are still denied.
  • Added focused loader tests for 404, 401, and unexpected 500 errors.

Testing Criteria

  • yarn test --runTestsByPath 'src/app/detail/[task_id]/[user_type]/loaders.test.ts' --runInBand passes and covers the unauthorized detail-load path from Sentry.
  • yarn lint:check src/app/detail/[task_id]/[user_type]/loaders.ts src/app/detail/[task_id]/[user_type]/loaders.test.ts exits successfully (repo-wide warnings only).
  • yarn tsc was run; it no longer reports errors from the changed loader code, but still fails on pre-existing missing SVG module declarations in src/icons/index.ts.

Notes

  • Investigated Sentry issue TASKS-90 from the tasks project, linked to Linear OUT-3831. The production event was an internal-user detail-page request for an inaccessible task from notification/deep-link traffic.
  • Linear MCP authentication returned auth_revoked during this automation run, so I could not post the required ticket comment from the agent.

Impact & Surface Area of Change

  • Detail page server loader behavior for inaccessible tasks. Expected result: an authorization-denied detail link renders the existing empty/deleted task state instead of throwing during server render and creating an unhandled Sentry event.
  • API/service authorization behavior is unchanged.
Open in Web View Automation 

cursoragent and others added 2 commits June 3, 2026 13:10
Co-authored-by: Neil Raina <makeitraina@users.noreply.github.com>
Co-authored-by: Neil Raina <makeitraina@users.noreply.github.com>
@linear-code

linear-code Bot commented Jun 3, 2026

Copy link
Copy Markdown

OUT-3831

@vercel

vercel Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tasks-app Ready Ready Preview, Comment Jun 12, 2026 11:49am

Request Review

priosshrsth
priosshrsth previously approved these changes Jun 11, 2026

@priosshrsth priosshrsth left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SandipBajracharya @arpandhakal This lgtm. What are your thoughts?

@priosshrsth priosshrsth marked this pull request as ready for review June 11, 2026 09:19
@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Deployment failed with the following error:

Deploying Serverless Functions to multiple regions is restricted to the Pro and Enterprise plans.

Learn More: https://vercel.link/multiple-function-regions

@greptile-apps

greptile-apps Bot commented Jun 11, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes an unhandled Sentry error (TASKS-90) that occurred when an internal user followed a deep link to a task they are not authorized to access. Previously only NOT_FOUND (404) was caught in loadTask; now UNAUTHORIZED (401) is also caught and returns null, causing the detail page to render the existing empty/deleted-task state (DeletedRedirectPage) instead of throwing.

  • loaders.ts: adds httpStatus.UNAUTHORIZED to the catch guard in loadTask, matching the existing 404 path; the underlying TasksService.checkClientAccessForTask authorization check is unchanged.
  • loaders.test.ts: new focused test file verifying that 404 and 401 both resolve to null while unexpected 500 errors are still rethrown.

Confidence Score: 4/5

The loader change is narrow and correct — the 401 path is now handled consistently with 404, and the underlying auth guard is untouched.

The one-line change in loadTask is well-targeted and the new tests confirm the 404/401/500 branches. The open question is whether loadTaskPath and loadSubtaskStatus would stay safe if access-control checks were later added to getTraversalPath or getSubtaskStatus, since those loaders still only catch 404.

src/app/detail/[task_id]/[user_type]/loaders.ts — specifically the sibling loaders loadTaskPath and loadSubtaskStatus and whether their error guards should be kept in sync with loadTask.

Important Files Changed

Filename Overview
src/app/detail/[task_id]/[user_type]/loaders.ts Adds UNAUTHORIZED (401) to the caught error statuses in loadTask so deep links to inaccessible tasks render the empty/deleted state instead of throwing; sibling loaders loadTaskPath and loadSubtaskStatus still only guard against 404.
src/app/detail/[task_id]/[user_type]/loaders.test.ts New test file covering the 404, 401, and unexpected 500 cases for loadTask; mocks are correctly scoped and the test structure is clean.

Sequence Diagram

sequenceDiagram
    participant Browser
    participant Page as page.tsx
    participant LT as loadTask
    participant LTP as loadTaskPath
    participant LS as loadSubtaskStatus
    participant TS as TasksService.getOneTask

    Browser->>Page: GET /detail/:task_id (deep link, unauthorized)
    Page->>Page: Promise.all([loadTask, loadTaskPath, loadSubtaskStatus, loadViewSettings])
    Page->>LT: loadTask(user, taskId)
    LT->>TS: getOneTask(taskId)
    TS->>TS: checkClientAccessForTask throws APIError(401)
    TS-->>LT: throw APIError(401)
    Note over LT: NEW: catches 401, returns null
    LT-->>Page: null
    Page->>LTP: loadTaskPath(user, taskId)
    LTP-->>Page: [] (getTraversalPath has no access check)
    Page->>LS: loadSubtaskStatus(user, taskId)
    LS-->>Page: count 0 (getSubtaskStatus has no access check)
    Page->>Page: "task === null, render DeletedRedirectPage"
    Page-->>Browser: DeletedRedirectPage (no Sentry error)
Loading

Comments Outside Diff (1)

  1. src/app/detail/[task_id]/[user_type]/loaders.ts, line 27-29 (link)

    P2 Sibling loaders don't guard against 401

    loadTaskPath and loadSubtaskStatus only catch NOT_FOUND — neither handles UNAUTHORIZED. Because all four loaders run inside a single Promise.all in page.tsx, an unhandled 401 from either sibling would still reject the whole settlement and re-create the Sentry event this PR is meant to eliminate. Today getTraversalPath and getSubtaskStatus don't perform the same checkClientAccessForTask check that getOneTask does, so the risk is latent rather than immediate, but keeping the error-handling strategy consistent across sibling loaders would prevent a regression if access-control logic is added to those methods later.

Reviews (1): Last reviewed commit: "Merge branch 'main' into cursor/OUT-3831..." | Re-trigger Greptile

Comment thread src/app/detail/[task_id]/[user_type]/loaders.ts Outdated
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
@priosshrsth priosshrsth merged commit b7c195c into main Jun 12, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants