Skip to content

fix(ui): guard sessionStorage access against strict-privacy crash (#1453)#1563

Open
meetp06 wants to merge 2 commits into
rocketride-org:developfrom
meetp06:fix/gh-1453-safe-sessionstorage
Open

fix(ui): guard sessionStorage access against strict-privacy crash (#1453)#1563
meetp06 wants to merge 2 commits into
rocketride-org:developfrom
meetp06:fix/gh-1453-safe-sessionstorage

Conversation

@meetp06

@meetp06 meetp06 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Fixes #1453

Problem

Bare sessionStorage access throws a SecurityError in strict privacy modes (Safari Private Browsing, Chrome Incognito with third-party storage blocked, or when the UI is embedded in a sandboxed iframe). Because these calls run at module-eval time (clientSingleton.ts) and during the initial React render (App.tsx), the unhandled throw aborts the render cycle and the user gets a blank white screen — a denial of service.

Fix

Add a small safeSessionStorage helper (try/catch wrappers falling back to null / no-op) in each affected app and route the previously-unguarded calls through it:

  • apps/chat-ui/src/App.tsx
  • apps/dropper-ui/src/App.tsx
  • apps/dropper-ui/src/hooks/clientSingleton.ts
  • apps/shell-ui/src/util/pkce.ts

This mirrors the safe pattern already used in apps/shell-ui/src/connection/connection.ts.

Scope note

Audited every sessionStorage/localStorage access across chat-ui, dropper-ui, and shell-ui. The shell-ui auth providers (ApiKeyAuthProvider, CloudAuthProvider), WorkspaceContext, connection.ts and Shell.tsx already wrap their access in try/catch, so they are intentionally left unchanged. Only the genuinely-unguarded sites are touched.

Behavior

When storage is unavailable the app now degrades to in-memory/limited functionality instead of crashing — reads return null, writes/removes no-op.

Testing

Static change only (import + identifier swap, signatures identical to native sessionStorage). Local type-check not run — monorepo toolchain (pnpm) is not installed in this environment.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved authentication reliability in restrictive browser, privacy, and sandboxed iframe environments by safely handling session storage access.
    • Prevented failures to read or write session-stored auth data from interrupting startup or initial rendering.
    • Ensured PKCE verifier persistence continues to work when session storage is available, while failing gracefully when it is not.

…cketride-org#1453)

Bare `sessionStorage` access throws a `SecurityError` in strict privacy
modes (Safari Private Browsing, Chrome Incognito with third-party storage
blocked, or when the UI is embedded in a sandboxed iframe). Because these
calls run at module-eval time (clientSingleton) and during the initial
React render (App.tsx), the unhandled throw aborts the render cycle and
the user gets a blank white screen — a denial of service.

Add a `safeSessionStorage` helper (try/catch wrappers that fall back to
null / no-op) in each affected app and route the previously-unguarded
sessionStorage calls through it:

- apps/chat-ui/src/App.tsx
- apps/dropper-ui/src/App.tsx
- apps/dropper-ui/src/hooks/clientSingleton.ts
- apps/shell-ui/src/util/pkce.ts

The shell-ui auth providers, WorkspaceContext, connection.ts and Shell.tsx
already wrap their sessionStorage access, so they are left unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions github-actions Bot added the module:ui Chat UI and Dropper UI label Jul 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor
🤖 Internal: Discord sync marker

Auto-managed by the Discord notification workflow. Stores the linked Discord message ID and forum thread ID. Do not edit or delete.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: cf242f8d-608d-4e58-9f76-d7eb51b3524f

📥 Commits

Reviewing files that changed from the base of the PR and between 77422aa and 5ee8e7d.

📒 Files selected for processing (3)
  • apps/chat-ui/src/utils/safeStorage.ts
  • apps/dropper-ui/src/utils/safeStorage.ts
  • apps/shell-ui/src/util/safeStorage.ts

📝 Walkthrough

Walkthrough

The change adds guarded sessionStorage wrappers to the chat, dropper, and shell UIs, then routes authentication token and PKCE verifier persistence through those wrappers.

Changes

Safe session storage integration

Layer / File(s) Summary
Storage access wrappers
apps/*/src/**/safeStorage.ts
Adds safeSessionStorage wrappers that return null for failed reads and ignore failed writes or removals.
Authentication token persistence
apps/chat-ui/src/App.tsx, apps/dropper-ui/src/App.tsx, apps/dropper-ui/src/hooks/clientSingleton.ts
Replaces direct sessionStorage access for authentication token retrieval and caching.
PKCE verifier persistence
apps/shell-ui/src/util/pkce.ts
Uses safeSessionStorage to store, retrieve, and remove the PKCE verifier.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: jmaionchi, rod-christensen, stepmikhaylov

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly states the main fix: guarding sessionStorage access against strict-privacy crashes.
Linked Issues check ✅ Passed The affected Chat, Dropper, and Shell UI paths now use safe sessionStorage wrappers, matching issue #1453's requirements.
Out of Scope Changes check ✅ Passed The added safeStorage helpers and call-site updates are directly tied to the sessionStorage crash fix.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

apps/chat-ui/src/utils/safeStorage.ts

Oops! Something went wrong! :(

ESLint: 9.39.4

TypeError: expand is not a function
at Minimatch.braceExpand (/node_modules/.pnpm/minimatch@3.1.5/node_modules/minimatch/minimatch.js:271:10)
at Minimatch.make (/node_modules/.pnpm/minimatch@3.1.5/node_modules/minimatch/minimatch.js:180:33)
at new Minimatch (/node_modules/.pnpm/minimatch@3.1.5/node_modules/minimatch/minimatch.js:156:8)
at doMatch (/node_modules/.pnpm/@eslint+config-array@0.21.2/node_modules/@eslint/config-array/dist/cjs/index.cjs:422:13)
at match (/node_modules/.pnpm/@eslint+config-array@0.21.2/node_modules/@eslint/config-array/dist/cjs/index.cjs:756:11)
at /node_modules/.pnpm/@eslint+config-array@0.21.2/node_modules/@eslint/config-array/dist/cjs/index.cjs:772:10
at Array.some ()
at pathMatches (/node_modules/.pnpm/@eslint+config-array@0.21.2/node_modules/@eslint/config-array/dist/cjs/index.cjs:767:44)
at /node_modules/.pnpm/@eslint+config-array@0.21.2/node_modules/@eslint/config-array/dist/cjs/index.cjs:1368:8
at FlatConfigArray.forEach ()

apps/dropper-ui/src/utils/safeStorage.ts

Oops! Something went wrong! :(

ESLint: 9.39.4

TypeError: expand is not a function
at Minimatch.braceExpand (/node_modules/.pnpm/minimatch@3.1.5/node_modules/minimatch/minimatch.js:271:10)
at Minimatch.make (/node_modules/.pnpm/minimatch@3.1.5/node_modules/minimatch/minimatch.js:180:33)
at new Minimatch (/node_modules/.pnpm/minimatch@3.1.5/node_modules/minimatch/minimatch.js:156:8)
at doMatch (/node_modules/.pnpm/@eslint+config-array@0.21.2/node_modules/@eslint/config-array/dist/cjs/index.cjs:422:13)
at match (/node_modules/.pnpm/@eslint+config-array@0.21.2/node_modules/@eslint/config-array/dist/cjs/index.cjs:756:11)
at /node_modules/.pnpm/@eslint+config-array@0.21.2/node_modules/@eslint/config-array/dist/cjs/index.cjs:772:10
at Array.some ()
at pathMatches (/node_modules/.pnpm/@eslint+config-array@0.21.2/node_modules/@eslint/config-array/dist/cjs/index.cjs:767:44)
at /node_modules/.pnpm/@eslint+config-array@0.21.2/node_modules/@eslint/config-array/dist/cjs/index.cjs:1368:8
at FlatConfigArray.forEach ()

apps/shell-ui/src/util/safeStorage.ts

Oops! Something went wrong! :(

ESLint: 9.39.4

TypeError: expand is not a function
at Minimatch.braceExpand (/node_modules/.pnpm/minimatch@3.1.5/node_modules/minimatch/minimatch.js:271:10)
at Minimatch.make (/node_modules/.pnpm/minimatch@3.1.5/node_modules/minimatch/minimatch.js:180:33)
at new Minimatch (/node_modules/.pnpm/minimatch@3.1.5/node_modules/minimatch/minimatch.js:156:8)
at doMatch (/node_modules/.pnpm/@eslint+config-array@0.21.2/node_modules/@eslint/config-array/dist/cjs/index.cjs:422:13)
at match (/node_modules/.pnpm/@eslint+config-array@0.21.2/node_modules/@eslint/config-array/dist/cjs/index.cjs:756:11)
at /node_modules/.pnpm/@eslint+config-array@0.21.2/node_modules/@eslint/config-array/dist/cjs/index.cjs:772:10
at Array.some ()
at pathMatches (/node_modules/.pnpm/@eslint+config-array@0.21.2/node_modules/@eslint/config-array/dist/cjs/index.cjs:767:44)
at /node_modules/.pnpm/@eslint+config-array@0.21.2/node_modules/@eslint/config-array/dist/cjs/index.cjs:1368:8
at FlatConfigArray.forEach ()


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/chat-ui/src/utils/safeStorage.ts`:
- Around line 37-71: Extract the duplicated safeSessionStorage implementation
from apps/chat-ui/src/utils/safeStorage.ts#L37-L71 into a shared package or
workspace utility, then replace the copies in
apps/dropper-ui/src/utils/safeStorage.ts#L37-L71 and
apps/shell-ui/src/util/safeStorage.ts#L37-L71 with imports from that shared
location. Preserve the getItem, setItem, and removeItem behavior; if extraction
is not feasible, add synchronization comments to all three files.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7f8dcdb5-571c-47da-8f72-cd8b8dd29104

📥 Commits

Reviewing files that changed from the base of the PR and between cbe5d12 and 77422aa.

📒 Files selected for processing (7)
  • apps/chat-ui/src/App.tsx
  • apps/chat-ui/src/utils/safeStorage.ts
  • apps/dropper-ui/src/App.tsx
  • apps/dropper-ui/src/hooks/clientSingleton.ts
  • apps/dropper-ui/src/utils/safeStorage.ts
  • apps/shell-ui/src/util/pkce.ts
  • apps/shell-ui/src/util/safeStorage.ts

Comment on lines +37 to +71
export const safeSessionStorage = {
/**
* Read a value from `sessionStorage`, returning `null` if storage is
* unavailable or the key is missing.
*/
getItem(key: string): string | null {
try {
return sessionStorage.getItem(key);
} catch {
return null;
}
},

/**
* Write a value to `sessionStorage`. No-ops if storage is unavailable.
*/
setItem(key: string, value: string): void {
try {
sessionStorage.setItem(key, value);
} catch {
/* storage unavailable — degrade gracefully */
}
},

/**
* Remove a value from `sessionStorage`. No-ops if storage is unavailable.
*/
removeItem(key: string): void {
try {
sessionStorage.removeItem(key);
} catch {
/* storage unavailable — degrade gracefully */
}
},
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Three identical safeStorage.ts files — consider extracting to a shared package.

The same 71-line safeSessionStorage wrapper is duplicated verbatim across three apps. If a bug is found or a method (e.g., clear) needs to be added, all three copies must be updated in lockstep.

  • apps/chat-ui/src/utils/safeStorage.ts#L37-L71: extract to a shared package or workspace utility.
  • apps/dropper-ui/src/utils/safeStorage.ts#L37-L71: re-import from the shared location instead of maintaining a copy.
  • apps/shell-ui/src/util/safeStorage.ts#L37-L71: re-import from the shared location instead of maintaining a copy.

If a shared package isn't feasible in this monorepo right now, at minimum add a comment in each file pointing to the others so future changes stay synchronized.

📍 Affects 3 files
  • apps/chat-ui/src/utils/safeStorage.ts#L37-L71 (this comment)
  • apps/dropper-ui/src/utils/safeStorage.ts#L37-L71
  • apps/shell-ui/src/util/safeStorage.ts#L37-L71
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/chat-ui/src/utils/safeStorage.ts` around lines 37 - 71, Extract the
duplicated safeSessionStorage implementation from
apps/chat-ui/src/utils/safeStorage.ts#L37-L71 into a shared package or workspace
utility, then replace the copies in
apps/dropper-ui/src/utils/safeStorage.ts#L37-L71 and
apps/shell-ui/src/util/safeStorage.ts#L37-L71 with imports from that shared
location. Preserve the getItem, setItem, and removeItem behavior; if extraction
is not feasible, add synchronization comments to all three files.

Address review feedback (CodeRabbit) on rocketride-org#1563: the safeSessionStorage helper
is duplicated across chat-ui, dropper-ui and shell-ui. A shared workspace
package is not a clean fit — chat-ui/dropper-ui do not depend on `shared`
(packages/shared-ui) and the only common dependency, `rocketride`, is the
public SDK client (wrong home for a browser-only UI util, and would expand
its public surface). Rather than add cross-package dependencies, add a sync
note to each copy so future fixes are applied in lockstep.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@meetp06

meetp06 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review. On the duplicated safeSessionStorage helper: I looked at extracting it into a shared workspace package, but it isn't a clean fit right now — chat-ui and dropper-ui don't depend on shared (packages/shared-ui), and the only dependency all three apps share is rocketride, the public SDK client, which is the wrong home for a browser-only UI util (and would grow its public surface). Rather than add cross-package deps in a bugfix PR, I've taken the suggested fallback and added a sync note to each copy pointing at the other two. Happy to do the shared-package extraction as a separate follow-up if you'd prefer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

module:ui Chat UI and Dropper UI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unprotected sessionStorage access causes complete application crash in strict privacy modes (DoS)

1 participant