fix(security): patch all P0 vulnerabilities from project review
- credential: encrypt/decrypt now throw when safeStorage unavailable instead of base64 fallback
- ipc-handlers: encrypt auth/sshPassword/sshPassphrase before storing, decrypt on read (avoid mutation)
- ipc-handlers: block FLUSHALL/FLUSHDB/SHUTDOWN/DEBUG in redis:execute, add redis:executeUnsafe for CLI
- format: whitelist allowed external formatters (xxd/jq/python3/python/php/column), clean up tmpDir
- connection: clearTimeout on ready/error paths to fix timeout race in Sentinel/Cluster/normal modes
- index: add app.on('will-quit') to disconnectAll + stopAllPubSubClients on exit
- pubsub: add stopAllPubSubClients() to clean up all sub/monitor connections
- docs: add PROJECT_REVIEW.md with full audit report and fix status
This commit is contained in:
@@ -51,12 +51,39 @@ export function registerIpcHandlers(): void {
|
||||
ipcMain.handle('credential:decrypt', (_e, encoded: string) => decrypt(encoded))
|
||||
|
||||
// Storage - connections
|
||||
ipcMain.handle('storage:getConnections', () => store.get('connections', []))
|
||||
ipcMain.handle('storage:saveConnection', (_e, conn: ConnectionConfig) => {
|
||||
ipcMain.handle('storage:getConnections', async () => {
|
||||
const connections = store.get('connections', [])
|
||||
const idx = connections.findIndex((c: ConnectionConfig) => c.id === conn.id)
|
||||
if (idx >= 0) connections[idx] = conn
|
||||
else connections.push(conn)
|
||||
// Decrypt sensitive fields
|
||||
for (const conn of connections) {
|
||||
if (conn.auth && conn.auth.startsWith('enc:')) {
|
||||
try { conn.auth = await decrypt(conn.auth.slice(4)) } catch { /* keep as-is */ }
|
||||
}
|
||||
if (conn.sshPassword && conn.sshPassword.startsWith('enc:')) {
|
||||
try { conn.sshPassword = await decrypt(conn.sshPassword.slice(4)) } catch { /* keep as-is */ }
|
||||
}
|
||||
if (conn.sshPassphrase && conn.sshPassphrase.startsWith('enc:')) {
|
||||
try { conn.sshPassphrase = await decrypt(conn.sshPassphrase.slice(4)) } catch { /* keep as-is */ }
|
||||
}
|
||||
}
|
||||
return connections
|
||||
})
|
||||
ipcMain.handle('storage:saveConnection', async (_e, conn: ConnectionConfig) => {
|
||||
// Encrypt sensitive fields before storing (create copy to avoid mutation)
|
||||
const encrypted: ConnectionConfig = { ...conn }
|
||||
if (encrypted.auth && !encrypted.auth.startsWith('enc:')) {
|
||||
encrypted.auth = 'enc:' + await encrypt(encrypted.auth)
|
||||
}
|
||||
if (encrypted.sshPassword && !encrypted.sshPassword.startsWith('enc:')) {
|
||||
encrypted.sshPassword = 'enc:' + await encrypt(encrypted.sshPassword)
|
||||
}
|
||||
if (encrypted.sshPassphrase && !encrypted.sshPassphrase.startsWith('enc:')) {
|
||||
encrypted.sshPassphrase = 'enc:' + await encrypt(encrypted.sshPassphrase)
|
||||
}
|
||||
|
||||
const connections = store.get('connections', [])
|
||||
const idx = connections.findIndex((c: ConnectionConfig) => c.id === encrypted.id)
|
||||
if (idx >= 0) connections[idx] = encrypted
|
||||
else connections.push(encrypted)
|
||||
store.set('connections', connections)
|
||||
return connections
|
||||
})
|
||||
@@ -101,8 +128,22 @@ export function registerIpcHandlers(): void {
|
||||
return redis.isClientConnected(id)
|
||||
})
|
||||
|
||||
// Redis - 服务器
|
||||
// Blocked dangerous commands — blocked in redis:execute but allowed via redis:executeUnsafe (CLI)
|
||||
const BLOCKED_COMMANDS = new Set([
|
||||
'FLUSHALL', 'FLUSHDB', 'SHUTDOWN', 'DEBUG',
|
||||
])
|
||||
|
||||
// Redis - 服务器 (safe execute — blocks dangerous commands)
|
||||
ipcMain.handle('redis:execute', withLog('EXEC', async (_e, id: string, command: string, args: string[]) => {
|
||||
const cmdUpper = command.toUpperCase().split(/\s+/)[0]
|
||||
if (BLOCKED_COMMANDS.has(cmdUpper)) {
|
||||
throw new Error(`Command "${command}" is blocked for safety. Use CLI for administrative commands.`)
|
||||
}
|
||||
return redis.execute(id, command, args)
|
||||
}))
|
||||
|
||||
// Redis - unsafe execute (bypasses block — for CLI use only)
|
||||
ipcMain.handle('redis:executeUnsafe', withLog('EXEC', async (_e, id: string, command: string, args: string[]) => {
|
||||
return redis.execute(id, command, args)
|
||||
}))
|
||||
ipcMain.handle('redis:getInfo', async (_e, id: string) => {
|
||||
|
||||
Reference in New Issue
Block a user