Skip to content

fix: guard localStorage access against restricted browser contexts#333

Open
upendra512 wants to merge 1 commit intoAOSSIE-Org:mainfrom
upendra512:fix/localstorage-restricted-context
Open

fix: guard localStorage access against restricted browser contexts#333
upendra512 wants to merge 1 commit intoAOSSIE-Org:mainfrom
upendra512:fix/localstorage-restricted-context

Conversation

@upendra512
Copy link
Copy Markdown

@upendra512 upendra512 commented Apr 12, 2026

Closes #331

In private browsing or storage-blocked contexts, localStorage throws SecurityError. The existing typeof window check does not cover this.

Changes:

  • Add src/utils/safeStorage.ts with safeGetItem/safeSetItem helpers (try/catch wrappers)
  • settings.tsx: wrap all localStorage reads and writes
  • trackpad.tsx: wrap sensitivity read with NaN guard; wrap rein_invert read + JSON.parse

Checklist:

  • Self-review completed
  • Code follows style guidelines

Summary by CodeRabbit

  • Bug Fixes
    • Improved reliability of settings storage (sensitivity, authentication, and theme) in restricted browser environments where storage access may be unavailable. The app now gracefully handles storage limitations instead of potential failures, ensuring stable functionality across different browsing contexts.

…OSSIE-Org#331)

In private browsing, cross-origin iframes, or storage-blocked environments,
calling localStorage.getItem/setItem throws a SecurityError. The existing
typeof window === "undefined" guard does not cover this case.

Introduce src/utils/safeStorage.ts with safeGetItem and safeSetItem helpers
that wrap every localStorage call in try/catch and return a fallback value
on failure.

Apply across all affected call sites:
- settings.tsx: sensitivity, authToken, invertScroll reads; all setItem calls
- trackpad.tsx: sensitivity read (also adds NaN guard); rein_invert read
  (also wraps JSON.parse which can throw on malformed stored values)

ConnectionProvider.tsx already had its own try/catch — left unchanged.
__root.tsx typeof guard is preserved as-is.

Fixes AOSSIE-Org#331

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 12, 2026

Walkthrough

The PR adds safe localStorage access helpers to prevent crashes in restricted browser contexts. It replaces direct localStorage calls in settings and trackpad pages with safeGetItem and safeSetItem wrappers that gracefully handle SecurityErrors and malformed values by falling back to defaults.

Changes

Cohort / File(s) Summary
Safe Storage Utility
src/utils/safeStorage.ts
New module exporting safeGetItem() and safeSetItem() functions that wrap localStorage access with try/catch error handling for restricted browser contexts.
Settings Route
src/routes/settings.tsx
Replaced direct localStorage.getItem/setItem calls with safeGetItem/safeSetItem for rein_sensitivity, rein_auth_token, rein_invert, and theme storage keys, maintaining existing fallback behavior.
Trackpad Route
src/routes/trackpad.tsx
Replaced direct localStorage.getItem with safeGetItem; added try/catch wrappers and refined parsing for sensitivity (with Number.NaN fallback) and invertScroll (with stricter boolean validation).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

Suggested labels

Typescript Lang

Poem

🐰 A rabbit hops through storage lands so bright,
Where localStorage once caused a fright!
With safeGetItem guards and catches keen,
No crashed app now—just graceful fallback scenes! ✨

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely summarizes the main change: guarding localStorage access against restricted browser contexts, which is the primary objective of this changeset.
Description check ✅ Passed The PR description adequately explains the issue and changes, though it omits functional verification checklist items that the template requires.
Linked Issues check ✅ Passed The PR fully addresses issue #331 by implementing safe localStorage access with try/catch wrappers, providing fallbacks, and guarding against malformed values across affected code paths.
Out of Scope Changes check ✅ Passed All changes are directly scoped to address issue #331: creating safeStorage helpers and applying them to settings.tsx and trackpad.tsx for robust localStorage handling.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/routes/settings.tsx`:
- Line 6: Replace the remaining direct localStorage reads in this module with
the existing helper: change any localStorage.getItem(...) usages to use
safeGetItem(...) and preserve any JSON.parse or fallback logic currently applied
(e.g., if code does JSON.parse(localStorage.getItem(key) ?? "null") or compares
to null/undefined, call safeGetItem(key) and then parse or handle the returned
value the same way). Update all occurrences in src/routes/settings.tsx so all
storage reads use safeGetItem while writes still use safeSetItem, keeping the
same key names and error/fallback behavior.

In `@src/utils/safeStorage.ts`:
- Around line 15-20: safeGetItem currently returns whatever
localStorage.getItem(key) yields (including null) and thus ignores a non-null
fallback; update safeGetItem to store the result of localStorage.getItem(key)
into a variable, then return fallback if that result is null, otherwise return
the result, while preserving the existing try/catch (catch should still return
fallback); reference the function name safeGetItem to locate and modify the
behavior.
🪄 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: 660cacf5-c748-4b43-915e-7b791d547a9f

📥 Commits

Reviewing files that changed from the base of the PR and between 1ae4791 and f8307b6.

📒 Files selected for processing (3)
  • src/routes/settings.tsx
  • src/routes/trackpad.tsx
  • src/utils/safeStorage.ts

Comment thread src/routes/settings.tsx
import { useEffect, useState } from "react"
import { APP_CONFIG, THEMES } from "../config"
import serverConfig from "../server-config.json"
import { safeGetItem, safeSetItem } from "../utils/safeStorage"
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.

🧹 Nitpick | 🔵 Trivial

Complete helper migration for remaining localStorage reads.

You still use direct localStorage.getItem at Line 25 and Line 42 while other reads now use safeGetItem. Please consolidate those reads to the helper for one consistent hardened path.

Proposed refactor
 const [invertScroll, setInvertScroll] = useState(() => {
 	if (typeof window === "undefined") return false
-	try {
-		const saved = localStorage.getItem("rein_invert")
-		return saved === "true"
-	} catch {
-		return false
-	}
+	const saved = safeGetItem("rein_invert")
+	return saved === "true"
 })

 const [theme, setTheme] = useState(() => {
 	if (typeof window === "undefined") return THEMES.DEFAULT
-	try {
-		const saved = localStorage.getItem(APP_CONFIG.THEME_STORAGE_KEY)
-		return saved === THEMES.LIGHT || saved === THEMES.DARK
-			? saved
-			: THEMES.DEFAULT
-	} catch {
-		return THEMES.DEFAULT
-	}
+	const saved = safeGetItem(APP_CONFIG.THEME_STORAGE_KEY)
+	return saved === THEMES.LIGHT || saved === THEMES.DARK
+		? saved
+		: THEMES.DEFAULT
 })

Also applies to: 34-34

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/routes/settings.tsx` at line 6, Replace the remaining direct localStorage
reads in this module with the existing helper: change any
localStorage.getItem(...) usages to use safeGetItem(...) and preserve any
JSON.parse or fallback logic currently applied (e.g., if code does
JSON.parse(localStorage.getItem(key) ?? "null") or compares to null/undefined,
call safeGetItem(key) and then parse or handle the returned value the same way).
Update all occurrences in src/routes/settings.tsx so all storage reads use
safeGetItem while writes still use safeSetItem, keeping the same key names and
error/fallback behavior.

Comment thread src/utils/safeStorage.ts
Comment on lines +15 to +20
export function safeGetItem(key: string, fallback: string | null = null): string | null {
try {
return localStorage.getItem(key)
} catch {
return fallback
}
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.

⚠️ Potential issue | 🟡 Minor

safeGetItem does not honor fallback when the key is absent.

When localStorage.getItem(key) returns null, this currently returns null even if a non-null fallback was provided. That contradicts the helper contract.

Proposed fix
 export function safeGetItem(key: string, fallback: string | null = null): string | null {
 	try {
-		return localStorage.getItem(key)
+		const value = localStorage.getItem(key)
+		return value ?? fallback
 	} catch {
 		return fallback
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/safeStorage.ts` around lines 15 - 20, safeGetItem currently returns
whatever localStorage.getItem(key) yields (including null) and thus ignores a
non-null fallback; update safeGetItem to store the result of
localStorage.getItem(key) into a variable, then return fallback if that result
is null, otherwise return the result, while preserving the existing try/catch
(catch should still return fallback); reference the function name safeGetItem to
locate and modify the behavior.

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.

[Bug]: Guard client localStorage access in restricted browser contexts

2 participants