| Beschreibung | # Hermes Desktop Data URL Image Decode Resource Exhaustion
## Summary
Hermes Desktop's Electron main process decodes `data:` image URLs with `Buffer.from(...)` before applying any size limit. The vulnerable helper is reachable from both the renderer-exposed `saveImageFromUrl` IPC bridge and the image context-menu actions that pass `params.srcURL` through unchanged.
An attacker who can cause Hermes Desktop to process an oversized `data:image/...;base64,...` URL can force large allocations and CPU work in the privileged main process, causing the desktop app to hang, crash, or be killed by the operating system.
## Details
The vulnerable code is in `apps/desktop/electron/main.cjs`.
`resourceBufferFromUrl()` handles `data:` URLs by matching the entire payload and decoding it into a `Buffer` without a size gate:
```js
const encoded = match[3] || ''
const buffer = match[2] ? Buffer.from(encoded, 'base64') : Buffer.from(decodeURIComponent(encoded), 'utf8')
```
Source: `apps/desktop/electron/main.cjs:3686`
This decode occurs before any size check, streaming boundary, or allocation limit. The regex also captures the entire payload as a JavaScript string before `Buffer.from(...)` materializes the decoded bytes.
The decoded buffer is consumed directly by both image actions:
```js
async function copyImageFromUrl(rawUrl) {
const { buffer } = await resourceBufferFromUrl(rawUrl)
const image = nativeImage.createFromBuffer(buffer)
if (image.isEmpty()) throw new Error('Could not read image')
clipboard.writeImage(image)
}
async function saveImageFromUrl(rawUrl) {
const { buffer, mimeType } = await resourceBufferFromUrl(rawUrl)
...
await fs.promises.writeFile(result.filePath, buffer)
return true
}
```
Sources:
- `apps/desktop/electron/main.cjs:3725`
- `apps/desktop/electron/main.cjs:3732`
There are two reachable entry points.
### Entry Point 1: Renderer IPC Bridge
The preload bridge exposes `saveImageFromUrl` to renderer JavaScript:
```js
saveImageFromUrl: url => ipcRenderer.invoke('hermes:saveImageFromUrl', url),
```
Source: `apps/desktop/electron/preload.cjs:55`
The Electron main-process IPC handler forwards the supplied URL directly into `saveImageFromUrl()`:
```js
ipcMain.handle('hermes:saveImageFromUrl', (_event, url) => saveImageFromUrl(String(url || '')))
```
Source: `apps/desktop/electron/main.cjs:6679`
### Entry Point 2: Image Context Menu
The image context menu passes `params.srcURL` directly into the same helpers:
```js
{
label: 'Copy Image',
click: () => {
void copyImageFromUrl(params.srcURL).catch(error => rememberLog(`Copy image failed: ${error.message}`))
}
},
...
{
label: 'Save Image As...',
click: () => {
void saveImageFromUrl(params.srcURL).catch(error => rememberLog(`Save image failed: ${error.message}`))
}
}
```
Sources:
- `apps/desktop/electron/main.cjs:4246`
- `apps/desktop/electron/main.cjs:4256`
This is therefore not dead code. The vulnerable path is reachable from normal desktop image-handling flows.
## Attack Path
An attacker needs a way to make Hermes Desktop process an attacker-controlled image URL. The security-relevant cases are:
- Renderer script execution in Hermes Desktop, such as a renderer XSS or compromised third-party script path.
- Attacker-controlled or untrusted content rendered as an image in the desktop app, followed by user interaction with the image context menu.
- Model, tool, artifact, embed, or remote content that can surface a `data:image/...` URL in the renderer.
A typical attack path is:
1. The attacker prepares an oversized `data:image/...;base64,...` URL.
2. The URL reaches Hermes Desktop's renderer, or a renderer script constructs it directly.
3. The renderer calls `window.hermesDesktop.saveImageFromUrl(hugeDataUrl)`, or the user right-clicks the rendered image and chooses **Copy Image** or **Save Image As**.
4. The Electron main process receives the request through `hermes:saveImageFromUrl`, or through the context-menu callback.
5. `resourceBufferFromUrl()` matches the `data:` branch and decodes the entire payload with `Buffer.from(...)`.
6. The main process performs large string handling, base64 decoding, `Buffer` allocation, and possibly image parsing or file writing.
7. The desktop app can hang, crash, or be killed by the OS due to memory or CPU exhaustion.
## Security Policy Scope
This is not the same class as `candidate-6c0f0e`. It does not directly match the `SECURITY.md` section 2.6 external-surface authorization bypass model because it is not an unauthorized caller dispatching work, receiving output, or resolving approvals.
The better scope argument is `SECURITY.md` section 3.1, "Trust-model documentation violations", especially the consuming-layer handling of Hermes output.
Source: `SECURITY.md:239`
Hermes Desktop is a consuming layer for model output, tool results, artifacts, embeds, and renderer-controlled UI content. Processing renderer/model-controlled image URLs in the Electron main process without a size limit allows untrusted content rendered by Hermes Desktop to exhaust privileged application resources.
`SECURITY.md` section 4 also emphasizes that operators should match isolation to the trust of content the agent ingests.
Source: `SECURITY.md:300`
That supports reporting this as a Desktop consuming-layer resource exhaustion issue: untrusted input that reaches the renderer should not be able to trivially crash the privileged desktop main process.
### Recommended Scope Wording
> This is in scope as a Desktop consuming-layer resource exhaustion issue. The vulnerable path processes renderer/model-controlled image URLs in the Electron main process without a size limit, allowing untrusted content rendered by Hermes Desktop to exhaust privileged application resources. This is not a prompt-injection report by itself; the security outcome is main-process denial of service.
### Important Limitation
> If the only trigger is a fully trusted local user intentionally saving their own huge data URL, the issue is lower severity. The security-relevant case is attacker-controlled or untrusted content reaching the renderer, or renderer script execution in the desktop app.
## Proof of Concept
The following reproduction uses the renderer-exposed IPC path. It should be run only in a test desktop session.
1. Construct a large `data:image/...;base64,...` URL in the Hermes Desktop renderer:
```js
const payload = 'A'.repeat(300 * 1024 * 1024)
const hugeDataUrl = `data:image/png;base64,${payload}`
```
2. Trigger the exposed desktop bridge:
```js
await window.hermesDesktop.saveImageFromUrl(hugeDataUrl)
```
Expected result: the Electron main process attempts to decode the full data URL before any size cap, causing severe memory pressure, UI hang, crash, or OS-level termination depending on host resources.
The same underlying sink is reachable through the context menu when a rendered image has an oversized `data:image/...` `src` and the user selects **Copy Image** or **Save Image As**.
## Impact
This is a denial-of-service vulnerability affecting Hermes Desktop.
The impacted component is the Electron main process. Because the main process owns desktop app lifecycle and privileged IPC handlers, exhausting its memory or CPU can make the entire desktop app unresponsive or crash.
There is no direct evidence of code execution or data disclosure from this path. The impact is availability loss.
Deployments are most affected when Hermes Desktop renders untrusted or attacker-influenced content, including model output, tool results, artifacts, embeds, or any renderer content path that can surface image URLs.
## Notes
The HTTP/HTTPS branch of `resourceBufferFromUrl()` also accumulates all response chunks and calls `Buffer.concat(chunks)` without a response-size cap.
Source: `apps/desktop/electron/main.cjs:3711`
That is a related resource-exhaustion issue, but this report focuses on the `data:` URL branch because it performs eager in-process decoding before any boundary.
|
|---|