solvox / SECURITY.md
muthuk1's picture
🎀 SolVox: Voice-First Private AI Wallet for Solana β€” Powered by QVAC SDK
945e815 verified
# πŸ›‘οΈ SolVox Security Model
## Threat Model
SolVox operates under the assumption that:
1. The **renderer process is untrusted** β€” it could be compromised via XSS
2. The **main process is the trust boundary** β€” all key operations happen here
3. The **local filesystem is semi-trusted** β€” OS-level encryption protects at-rest data
4. The **network is adversarial** β€” only Solana RPC calls leave the device
## Security Controls
### 1. Process Isolation
| Control | Setting | Purpose |
|---|---|---|
| `contextIsolation` | `true` | Renderer cannot access Node.js globals |
| `nodeIntegration` | `false` | No `require()` in renderer |
| `sandbox` | `true` | OS-level process sandbox (Chromium) |
| `webSecurity` | `true` | Same-origin policy enforced |
| `allowRunningInsecureContent` | `false` | No HTTP content in HTTPS context |
| `navigateOnDragDrop` | `false` | Prevents file drag-and-drop navigation |
### 2. IPC Channel Allowlist
The preload script exposes ONLY these namespaced channels via `contextBridge`:
```
wallet:create, wallet:import, wallet:getPublicKey, wallet:getBalance,
wallet:sendSOL, wallet:sendUSDT, wallet:getHistory, wallet:isUnlocked,
wallet:lock, wallet:exists
auth:biometric, auth:unlock, auth:setPin, auth:biometricAvailable
security:getSettings, security:updateSettings, security:addWhitelist,
security:removeWhitelist, security:getWhitelist, security:getAnomalies
ai:initialize, ai:processVoice, ai:chat, ai:parseIntent, ai:speak,
ai:translate, ai:embed, ai:ocr, ai:getStatus
rag:search, rag:addDocument
```
**No raw `ipcRenderer` is ever exposed.** The renderer cannot invoke arbitrary IPC channels.
### 3. Key Storage
```
Mnemonic/Private Key
β”‚
β–Ό
PIN-based AES-256-GCM (PBKDF2, 600K iterations, SHA-512)
β”‚
β–Ό
Electron safeStorage (OS keychain: macOS Keychain / Windows DPAPI / Linux libsecret)
β”‚
β–Ό
File on disk (0600 permissions, owner-only)
```
The mnemonic is **double-encrypted**: first with the user's PIN (AES-256-GCM with PBKDF2-derived key), then with OS-level `safeStorage`. An attacker who obtains the vault file still needs:
1. The user's OS login credentials (to decrypt safeStorage)
2. The user's PIN (to decrypt the inner AES layer)
### 4. In-Memory Key Handling
- The `Keypair` object lives exclusively in the main process's `_sessionKeypair` variable
- On lock: `secretKey.fill(0)` zeroes the key buffer (best-effort; V8 GC is non-deterministic)
- Auto-lock triggers after 5 minutes of inactivity
- Key material is NEVER sent over IPC β€” only the public key (base58 string) and signed transaction bytes
### 5. Transaction Guards
| Guard | Protection |
|---|---|
| **Input validation** | Regex-validated addresses, bounds-checked amounts |
| **Per-tx limits** | Configurable maximum per transaction |
| **Daily volume** | Cumulative daily spend limit |
| **Velocity** | Max transactions per hour |
| **Cooldown** | Minimum time between transactions |
| **Whitelist** | Optional β€” only send to pre-approved addresses |
| **Anomaly detection** | AI-powered pattern analysis (high amounts, odd hours, rapid sequences) |
| **Confirmation** | Every transaction requires explicit user confirmation |
### 6. Content Security Policy
```
default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://api.mainnet-beta.solana.com https://api.devnet.solana.com;
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
```
- No `unsafe-eval` β€” prevents `eval()` and `new Function()` attacks
- No external scripts β€” blocks CDN-based script injection
- Limited connect-src β€” only Solana RPC endpoints allowed
- No frames β€” prevents clickjacking
### 7. Navigation Protection
- All navigation to non-local URLs is blocked
- New window creation is denied (`setWindowOpenHandler(() => ({ action: 'deny' }))`)
- DevTools are disabled in production builds
## Data Flow
```
Voice Input ──→ QVAC (local) ──→ Intent ──→ Main Process validates ──→ Wallet signs ──→ Solana RPC
β”‚
β–Ό
TransactionGuard checks:
β”œβ”€β”€ Address format valid?
β”œβ”€β”€ Amount within limits?
β”œβ”€β”€ Daily volume OK?
β”œβ”€β”€ Velocity limit OK?
β”œβ”€β”€ Address whitelisted?
β”œβ”€β”€ Anomaly detected?
└── User confirmed?
```
**Only the signed transaction bytes leave the device** β€” via Solana RPC. All AI processing, intent parsing, and validation happen locally.
## Known Limitations
1. **V8 garbage collection** means `secretKey.fill(0)` doesn't guarantee the key is erased from all memory pages
2. **Electron renderer** could theoretically be compromised via a 0-day in Chromium β€” contextIsolation + sandbox mitigate this
3. **Transaction privacy** is limited by Solana's public blockchain β€” recipients and amounts are on-chain
4. **PIN brute-force** is mitigated by PBKDF2 (600K iterations) + lockout, but a sufficiently weak PIN (e.g., `123456`) can still be guessed
## Recommendations for Production
- Use a hardware security module (HSM) or secure enclave for key storage
- Implement Shamir's Secret Sharing for mnemonic backup
- Add a dead man's switch (auto-transfer if inactive for N days)
- Integrate with hardware wallets (Ledger/Trezor) for signing
- Add transaction simulation (Solana `simulateTransaction`) before signing