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:
2026-07-12 14:13:44 +08:00
parent 4d0e0b75aa
commit 9d73ff019c
11 changed files with 314 additions and 19 deletions
@@ -51,7 +51,7 @@ async function execute() {
try {
// Handle MULTI
if (command === 'MULTI' && !txMode.value) {
const result = await window.electronAPI.redis.execute(connStore.activeId, command, args)
const result = await window.electronAPI.redis.executeUnsafe(connStore.activeId, command, args)
if (result === 'OK') {
txMode.value = true
txQueue.value = []
@@ -64,7 +64,7 @@ async function execute() {
// Handle EXEC (end transaction)
if (command === 'EXEC' && txMode.value) {
const result = await window.electronAPI.redis.execute(connStore.activeId, command, args)
const result = await window.electronAPI.redis.executeUnsafe(connStore.activeId, command, args)
let formatted = ''
if (Array.isArray(result)) {
formatted = result.map((item: any, i: number) => {
@@ -84,7 +84,7 @@ async function execute() {
// Handle DISCARD (cancel transaction)
if (command === 'DISCARD' && txMode.value) {
await window.electronAPI.redis.execute(connStore.activeId, command, args)
await window.electronAPI.redis.executeUnsafe(connStore.activeId, command, args)
txMode.value = false
txQueue.value = []
history.value.push({ cmd, result: t('cli.txDiscarded') })
@@ -93,7 +93,7 @@ async function execute() {
// In transaction mode: queue command
if (txMode.value && !['MULTI', 'EXEC', 'DISCARD', 'WATCH', 'UNWATCH'].includes(command)) {
const result = await window.electronAPI.redis.execute(connStore.activeId, command, args)
const result = await window.electronAPI.redis.executeUnsafe(connStore.activeId, command, args)
txQueue.value.push(cmd)
const queueInfo = t('cli.txQueue', { n: txQueue.value.length })
history.value.push({ cmd, result: `${t('cli.txQueued')}${queueInfo}` })
@@ -138,7 +138,7 @@ async function execute() {
}
}
const result = await window.electronAPI.redis.execute(connStore.activeId, command, args)
const result = await window.electronAPI.redis.executeUnsafe(connStore.activeId, command, args)
let formatted = ''
if (result === null) {
@@ -245,7 +245,7 @@ async function importCommands() {
const cmdArgs = args.slice(1)
try {
const result = await window.electronAPI.redis.execute(connStore.activeId, command, cmdArgs)
const result = await window.electronAPI.redis.executeUnsafe(connStore.activeId, command, cmdArgs)
let formatted = ''
if (result === null) {
formatted = '(nil)'
@@ -234,7 +234,7 @@ async function handleFlushDb() {
inputErrorMessage: 'Type FLUSHDB to confirm',
})
if (input.value !== 'FLUSHDB') return
await window.electronAPI.redis.execute(connStore.activeId, 'FLUSHDB', [])
await window.electronAPI.redis.executeUnsafe(connStore.activeId, 'FLUSHDB', [])
ElMessage.success(t('status.flushDbDone'))
await refresh()
} catch {