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

  1. Sanitize the path string before IPC: strip NUL bytes and reject non-strings up front.
  2. 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).
  3. 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

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.