🔒 Security: Unsafe JSON Parsing in fs.ts
Summary
The readJson function in src/core/fs.ts performs JSON parsing without validation against prototype pollution attacks. While this requires user action to exploit (installing malicious config), it should be fixed for defense in depth.
Risk Assessment: LOW-MEDIUM - Requires user to install malicious configuration, but follows security best practices.
🐛 Vulnerability
File: src/core/fs.ts:13
export const readJson = <T>(filePath: string): T | null => {
try {
return JSON.parse(fs.readFileSync(filePath, 'utf8')) as T; // ← VULNERABLE
} catch {
return null;
}
};
Problem
- No validation against
__proto__, constructor, or prototype pollution
- No size limits (DoS via massive JSON)
- No schema validation
Attack Vector
A malicious .claude.json or settings.json with prototype pollution:
{
"__proto__": {
"env": {
"ANTHROPIC_API_KEY": "attacker-controlled-key"
}
}
}
Impact
- ✅ Could inject malicious object properties
- ✅ Could alter application behavior
- ✅ Could override environment variables
- ❌ Not remotely exploitable (requires local malicious config)
- ⚠️ Requires user to install/configure untrusted variant
✅ Recommended Fix
Option 1: Use secure-json-parse (RECOMMENDED)
import { parse } from 'secure-json-parse';
export const readJson = <T>(filePath: string): T | null => {
try {
const content = fs.readFileSync(filePath, 'utf8');
return parse(content, { protoAction: 'remove' }) as T;
} catch {
return null;
}
};
Option 2: Manual sanitization
export const readJson = <T>(filePath: string): T | null => {
try {
const raw = fs.readFileSync(filePath, 'utf8');
const parsed = JSON.parse(raw);
// Sanitize by removing prototype chain
return JSON.parse(JSON.stringify(parsed)) as T;
} catch {
return null;
}
};
Option 3: Add size limit + schema validation
import { z } from 'zod';
const MAX_JSON_SIZE = 1024 * 1024; // 1MB
export const readJson = <T>(filePath: string): T | null => {
try {
const stats = fs.statSync(filePath);
if (stats.size > MAX_JSON_SIZE) {
throw new Error('JSON file too large');
}
const content = fs.readFileSync(filePath, 'utf8');
return JSON.parse(content) as T;
} catch {
return null;
}
};
📊 Additional Considerations
Dependency Installation
npm install secure-json-parse
Alternative: Zero-dependency approach
If you prefer not to add a dependency, Option 2 provides basic protection without external packages.
🔍 Verification
After fix:
# Test that normal JSON still works
npm test
# Manual verification
node -e "const fs = require('fs'); console.log(JSON.parse(fs.readFileSync('package.json', 'utf8')));"
📚 References
🎯 Context
Why this matters for cc-mirror:
- This tool creates isolated variants with custom configurations
- Users may download/share variant configurations from untrusted sources
- Defense in depth prevents accidental compromise
- Zero performance impact (fix is simple and safe)
Why NOT critical:
- No remote attack surface (CLI tool, not a network service)
- Requires user action to exploit (installing malicious configs)
- Development-focused tool (not exposed to untrusted input)
🔒 Security: Unsafe JSON Parsing in fs.ts
Summary
The
readJsonfunction insrc/core/fs.tsperforms JSON parsing without validation against prototype pollution attacks. While this requires user action to exploit (installing malicious config), it should be fixed for defense in depth.Risk Assessment: LOW-MEDIUM - Requires user to install malicious configuration, but follows security best practices.
🐛 Vulnerability
File:
src/core/fs.ts:13Problem
__proto__,constructor, orprototypepollutionAttack Vector
A malicious
.claude.jsonorsettings.jsonwith prototype pollution:{ "__proto__": { "env": { "ANTHROPIC_API_KEY": "attacker-controlled-key" } } }Impact
✅ Recommended Fix
Option 1: Use secure-json-parse (RECOMMENDED)
Option 2: Manual sanitization
Option 3: Add size limit + schema validation
📊 Additional Considerations
Dependency Installation
Alternative: Zero-dependency approach
If you prefer not to add a dependency, Option 2 provides basic protection without external packages.
🔍 Verification
After fix:
📚 References
🎯 Context
Why this matters for cc-mirror:
Why NOT critical: