jgraph/drawio-desktop · error · Error
path not authorised
Error message
path not authorised
What it means
First guard in assertWritablePath (src/main/electron.js:3220-3223): rejects any p that is not a non-empty string or that contains a NUL byte (defence against null-byte injection into Node fs APIs). This runs before realpath/blessedPaths checks, so no path ever reaches the filesystem with an embedded \0.
Source
Thrown at src/main/electron.js:3222
}
}
return false;
}
// The renderer is semi-untrusted: it parses attacker-controlled diagram XML,
// .vsdx, SVG, Mermaid, etc. validateSender is necessary but not sufficient,
// because a renderer-side XSS attacker would also pass it. So write-side IPC
// handlers must additionally confirm the requested path is one the user has
// authorised through OS chrome (file picker, file association, argv) — see
// blessPath. This function realpath-canonicalises the requested path
// (defeating symlink traversal) and accepts only paths in blessedPaths or
// their draft/backup siblings.
async function assertWritablePath(p)
{
if (typeof p !== 'string' || !p || p.includes('\0'))
{
throw new Error('path not authorised');
}
const resolved = path.resolve(p);
let realpath;
try
{
realpath = await fsProm.realpath(resolved);
}
catch (e)
{
// File doesn't exist yet (e.g. Save As to a new file). Canonicalise
// the parent directory so symlinks in the directory chain are still
// resolved.
try
{
const parentReal = await fsProm.realpath(path.dirname(resolved));
realpath = path.join(parentReal, path.basename(resolved));View on GitHub (pinned to 403a2cb79f)
Solutions
- Sanitize the path string before IPC: strip NUL bytes and reject non-strings up front.
- Verify the field is set on the renderer side (assertWritablePath is the last line of defence; the IPC switch's reqStr usually catches missing strings first, but \0 slips past reqStr).
- If the path comes from argv, validate it is a real filesystem string before calling blessPath.
Example fix
// before
electron.request({action: 'writeFile', path: tainted, data});
// after
const safe = typeof tainted === 'string' && !tainted.includes('\0') ? tainted : null;
if (!safe) throw new TypeError('invalid path');
electron.request({action: 'writeFile', path: safe, data}); Defensive patterns
Strategy: validation
Validate before calling
function isSafePath(p) {
return typeof p === 'string' && p.length > 0 && !p.includes('\0');
}
if (!isSafePath(target)) throw new TypeError('unsafe path'); Type guard
const isSafePath = (p) => typeof p === 'string' && p.length > 0 && !p.includes('\0'); Try / catch
try { await writeFile(target, data); }
catch (e) {
if (e.message === 'path not authorised') { /* prompt user to pick a real path */ }
else throw e;
} Prevention
- Strip NUL bytes from any path derived from argv or untrusted input before IPC.
- Validate path type at the preload boundary, not just in main.
- Treat 'path not authorised' as a security event, log it for audit.
When it happens
Trigger: saveFile/saveDraft/writeFile/deleteFile invoked with a path that is undefined, null, a number, '', or a string containing '\0' (e.g. from a malformed file association argv or a corrupted draft filename).
Common situations: A draft/backup filename was assembled from untrusted input that injected a NUL; an automated integration passed a non-string path; argv parsing yielded undefined for a CLI-opened file.
Related errors
AI-assisted analysis of jgraph/drawio-desktop@403a2cb79f (2026-08-13).
Data as JSON: /api/errors/37a10dc80872a67c.
Report an issue: GitHub.