Security: Unsafe deserialization in kb-store.ts via JSON.parse without validation#422
Conversation
The `KbStore._load()` method reads a JSON file and parses it with `JSON.parse()` without any schema validation or sanitization. While this is a local file, if an attacker can modify the state.json file (e.g., via another compromised process or symlink attack), they could inject malicious data. More critically, the spread operator `{ ...DEFAULTS, ...JSON.parse(...) }` only does shallow merging, which could lead to prototype pollution if the JSON contains `__proto__` or `constructor` keys, potentially affecting downstream code that iterates over objects.
Signed-off-by: tomaioo <203048277+tomaioo@users.noreply.github.com>
📝 WalkthroughWalkthroughThe ChangesState Loading Safety
Estimated code review effort: 1 (Trivial) | ~5 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ 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.
🧹 Nitpick comments (2)
desktop/src/main/kb-store.ts (2)
38-48: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueDedupe the DEFAULTS deep-clone.
JSON.parse(JSON.stringify(DEFAULTS))is duplicated between the invalid-shape fallback and the catch block. Extract a smallcloneDefaults()helper (or usestructuredClone(DEFAULTS)if targeting Node ≥ 17) to avoid the repetition.♻️ Proposed dedupe
+function cloneDefaults(): StoreData { + return JSON.parse(JSON.stringify(DEFAULTS)); +} + class KbStore { ... private _load(): StoreData { try { const raw = JSON.parse(fs.readFileSync(this.file, "utf8")); if (raw == null || typeof raw !== "object" || Array.isArray(raw)) { - return JSON.parse(JSON.stringify(DEFAULTS)); + return cloneDefaults(); } ... return { ...DEFAULTS, ...safe }; } catch { - return JSON.parse(JSON.stringify(DEFAULTS)); + return cloneDefaults(); } }🤖 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 `@desktop/src/main/kb-store.ts` around lines 38 - 48, The DEFAULTS deep-clone logic is duplicated in the invalid-shape fallback and the catch block inside the kb-store loading flow. Extract that repeated cloning into a small helper such as cloneDefaults() in kb-store.ts, or switch both call sites to structuredClone(DEFAULTS) if supported, and use that helper consistently wherever DEFAULTS needs to be returned.
41-45: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low valueOptional: also filter
"prototype"for defense-in-depth.Static analysis flags the missing
"prototype"check alongside__proto__/constructor. In this specific shallow, single-level spread into a plain object literal, copying aprototypekey isn't itself exploitable, but adding it costs nothing and matches the common CWE-1321 mitigation triad.🛡️ Proposed tweak
- if (key !== "__proto__" && key !== "constructor") { + if (key !== "__proto__" && key !== "constructor" && key !== "prototype") { safe[key] = raw[key]; }🤖 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 `@desktop/src/main/kb-store.ts` around lines 41 - 45, The safe-copy loop in kb-store should also exclude the "prototype" key alongside "__proto__" and "constructor" for defense-in-depth. Update the filtering logic in the raw-to-safe object copy so the Object.keys(raw) iteration skips "prototype" too, keeping the same shallow copy behavior while matching the common CWE-1321 mitigation pattern.Source: Linters/SAST tools
🤖 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.
Nitpick comments:
In `@desktop/src/main/kb-store.ts`:
- Around line 38-48: The DEFAULTS deep-clone logic is duplicated in the
invalid-shape fallback and the catch block inside the kb-store loading flow.
Extract that repeated cloning into a small helper such as cloneDefaults() in
kb-store.ts, or switch both call sites to structuredClone(DEFAULTS) if
supported, and use that helper consistently wherever DEFAULTS needs to be
returned.
- Around line 41-45: The safe-copy loop in kb-store should also exclude the
"prototype" key alongside "__proto__" and "constructor" for defense-in-depth.
Update the filtering logic in the raw-to-safe object copy so the
Object.keys(raw) iteration skips "prototype" too, keeping the same shallow copy
behavior while matching the common CWE-1321 mitigation pattern.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: a36d42bc-a82d-4fed-92ef-fbbb9afb056f
📒 Files selected for processing (1)
desktop/src/main/kb-store.ts
Summary
Security: Unsafe deserialization in kb-store.ts via JSON.parse without validation
Problem
Severity:
Medium| File:desktop/src/main/kb-store.ts:L22The
KbStore._load()method reads a JSON file and parses it withJSON.parse()without any schema validation or sanitization. While this is a local file, if an attacker can modify the state.json file (e.g., via another compromised process or symlink attack), they could inject malicious data. More critically, the spread operator{ ...DEFAULTS, ...JSON.parse(...) }only does shallow merging, which could lead to prototype pollution if the JSON contains__proto__orconstructorkeys, potentially affecting downstream code that iterates over objects.Solution
Add validation for parsed data using a schema validator like zod, or at minimum check that the parsed result is a plain object and sanitize dangerous keys. Consider using
Object.create(null)for internal maps, or explicitly delete__proto__andconstructorfrom parsed objects before merging.Changes
desktop/src/main/kb-store.ts(modified)Summary by CodeRabbit