fix: guard localStorage access against restricted browser contexts#333
fix: guard localStorage access against restricted browser contexts#333upendra512 wants to merge 1 commit intoAOSSIE-Org:mainfrom
Conversation
…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>
WalkthroughThe PR adds safe localStorage access helpers to prevent crashes in restricted browser contexts. It replaces direct Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (3)
src/routes/settings.tsxsrc/routes/trackpad.tsxsrc/utils/safeStorage.ts
| import { useEffect, useState } from "react" | ||
| import { APP_CONFIG, THEMES } from "../config" | ||
| import serverConfig from "../server-config.json" | ||
| import { safeGetItem, safeSetItem } from "../utils/safeStorage" |
There was a problem hiding this comment.
🧹 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.
| export function safeGetItem(key: string, fallback: string | null = null): string | null { | ||
| try { | ||
| return localStorage.getItem(key) | ||
| } catch { | ||
| return fallback | ||
| } |
There was a problem hiding this comment.
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.
Closes #331
In private browsing or storage-blocked contexts, localStorage throws SecurityError. The existing typeof window check does not cover this.
Changes:
Checklist:
Summary by CodeRabbit