security(H2): validate IPC dispense input from renderer

Add input validation to hal:dispense IPC handler:
- Reject non-array or empty amounts
- Validate denomination and count are numbers
- Reject non-positive or non-integer counts
- Verify denomination exists in loaded cassettes
- Verify requested count does not exceed available inventory

Prevents a compromised renderer from sending crafted dispense
requests (negative counts, unknown denominations, over-capacity).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Patrick Mulligan 2026-03-07 09:34:20 -05:00
commit 1e3de32c51

View file

@ -204,6 +204,32 @@ ipcMain.handle(
'hal:dispense',
async (_event, amounts: { denomination: number; count: number }[]) => {
if (!halInstance) throw new Error('HAL not initialized')
// Validate input from renderer (untrusted)
if (!Array.isArray(amounts) || amounts.length === 0) {
throw new Error('Invalid dispense request: amounts must be a non-empty array')
}
const inventory = halInstance.getInventory()
for (const item of amounts) {
if (typeof item.denomination !== 'number' || typeof item.count !== 'number') {
throw new Error('Invalid dispense request: denomination and count must be numbers')
}
if (!Number.isInteger(item.count) || item.count <= 0) {
throw new Error(
`Invalid count for denomination ${item.denomination}: must be a positive integer`
)
}
if (!(item.denomination in inventory)) {
throw new Error(`No cassette loaded with denomination: ${item.denomination}`)
}
if (item.count > (inventory[item.denomination] ?? 0)) {
throw new Error(
`Insufficient bills for denomination ${item.denomination}: requested ${item.count}, available ${inventory[item.denomination] ?? 0}`
)
}
}
return await halInstance.dispenseCash(amounts)
}
)