Deno's permission system enforces filesystem and execution restrictions by
comparing the requested path against the path supplied to --deny-read,
--deny-write, --deny-run, or --deny-ffi. On macOS, that comparison was
done at the raw-byte level while the APFS filesystem treats different Unicode
spellings of the same name as the same file.
That means a program could reach a denied path by spelling it differently than
the deny rule. For example, with --deny-read=/secrets/passwörter.txt, a
script could still read the file by opening /secrets/passwo\u0308rter.txt
(NFD instead of NFC), or /SECRETS/PASSWÖRTER.txt (different case, since
default APFS volumes are case-insensitive). Other forms include ligature
characters (fi vs fi, ff vs ff, …) and German ß vs ss.
The denied path and the requested path differed at the byte level, so Deno's
permission check passed; the kernel then resolved them to the same inode and
served the file anyway. The same flaw affected --deny-write, --deny-run,
and --deny-ffi, which share the same path-comparison code.
You are potentially affected if all of the following are true:
--deny-read, --deny-write, --deny-run, or --deny-ffi
as a security boundary against less-trusted code — a dependency, plugin,
or attacker-controlled input.é, ñ, ö, …), German
ß, or Latin ligatures — or you rely on case-sensitivity on a default
APFS volume.If you only run fully trusted code, or your deny rules cover paths that are pure ASCII with no case-sensitive aliases, you are not exposed to this specific bypass.
A program running with broad --allow-read (or --allow-write /
--allow-run / --allow-ffi) but with --deny-* carve-outs for specific
paths could read, write, execute, or load via FFI those denied paths by
referring to them through a Unicode- or case-equivalent spelling. The sandbox
model on macOS was weaker than the flags suggested.
If you cannot upgrade immediately:
--allow-* allowlists over --deny-* denylists. Allow rules match
against the original specifier, so an attacker-supplied alternate spelling
will not match a path you didn't explicitly grant.On macOS, Deno now normalizes both the deny-rule path and the requested path to NFC and applies Unicode case folding before comparing them. This matches how APFS resolves paths at the inode level, so byte-different but equivalent spellings are now rejected by the same deny rule.
{
"github_reviewed": true,
"nvd_published_at": "2026-06-23T18:18:03Z",
"cwe_ids": [
"CWE-176",
"CWE-41"
],
"severity": "HIGH",
"github_reviewed_at": "2026-06-16T19:11:52Z"
}