{"id":"JLSEC-2026-655","summary":"Deno: Permission Bypass via Unicode Normalization Mismatch on macOS (APFS)","details":"## Summary\n\nDeno's permission system enforces filesystem and execution restrictions by\ncomparing the requested path against the path supplied to `--deny-read`,\n`--deny-write`, `--deny-run`, or `--deny-ffi`. On macOS, that comparison was\ndone at the raw-byte level while the APFS filesystem treats different Unicode\nspellings of the same name as the same file.\n\nThat means a program could reach a denied path by spelling it differently than\nthe deny rule. For example, with `--deny-read=/secrets/passwörter.txt`, a\nscript could still read the file by opening `/secrets/passwo\\u0308rter.txt`\n(NFD instead of NFC), or `/SECRETS/PASSWÖRTER.txt` (different case, since\ndefault APFS volumes are case-insensitive). Other forms include ligature\ncharacters (`ﬁ` vs `fi`, `ﬀ` vs `ff`, …) and German `ß` vs `ss`.\n\nThe denied path and the requested path differed at the byte level, so Deno's\npermission check passed; the kernel then resolved them to the same inode and\nserved the file anyway. The same flaw affected `--deny-write`, `--deny-run`,\nand `--deny-ffi`, which share the same path-comparison code.\n\n## Am I affected?\n\nYou are potentially affected if **all** of the following are true:\n\n 1. You run Deno on **macOS** (the issue is specific to APFS path-equivalence\n    rules; Linux and Windows are not affected by this variant).\n 2. You rely on `--deny-read`, `--deny-write`, `--deny-run`, or `--deny-ffi`\n    as a security boundary against less-trusted code — a dependency, plugin,\n    or attacker-controlled input.\n 3. The protected path contains characters that have alternate Unicode\n    spellings — most commonly accented characters (`é`, `ñ`, `ö`, …), German\n    `ß`, or Latin ligatures — or you rely on case-sensitivity on a default\n    APFS volume.\n\nIf you only run fully trusted code, or your deny rules cover paths that are\npure ASCII with no case-sensitive aliases, you are not exposed to this\nspecific bypass.\n\n## Impact\n\nA program running with broad `--allow-read` (or `--allow-write` /\n`--allow-run` / `--allow-ffi`) but with `--deny-*` carve-outs for specific\npaths could read, write, execute, or load via FFI those denied paths by\nreferring to them through a Unicode- or case-equivalent spelling. The sandbox\nmodel on macOS was weaker than the flags suggested.\n\n## Workaround\n\nIf you cannot upgrade immediately:\n\n  - Prefer `--allow-*` allowlists over `--deny-*` denylists. Allow rules match\n    against the original specifier, so an attacker-supplied alternate spelling\n    will not match a path you didn't explicitly grant.\n  - Do not rely on case-sensitivity of paths on macOS for security boundaries;\n    default APFS volumes are case-insensitive.\n\n## Fix\n\nOn macOS, Deno now normalizes both the deny-rule path and the requested path\nto NFC and applies Unicode case folding before comparing them. This matches\nhow APFS resolves paths at the inode level, so byte-different but equivalent\nspellings are now rejected by the same deny rule.","modified":"2026-07-07T13:49:38.319174358Z","published":"2026-07-07T13:43:33.746Z","upstream":["CVE-2026-49401","EUVD-2026-38547","GHSA-8xpq-cjcf-3wh9"],"database_specific":{"sources":[{"html_url":"https://nvd.nist.gov/vuln/detail/CVE-2026-49401","published":"2026-06-23T18:18:03.033Z","url":"https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2026-49401","modified":"2026-06-26T17:43:03.033Z","id":"CVE-2026-49401","database_specific":{"status":"Analyzed"},"imported":"2026-06-27T08:36:01.311Z"},{"html_url":"https://github.com/advisories/GHSA-8xpq-cjcf-3wh9","published":"2026-06-16T19:11:52Z","url":"https://api.github.com/advisories/GHSA-8xpq-cjcf-3wh9","modified":"2026-06-16T19:11:54Z","id":"GHSA-8xpq-cjcf-3wh9","imported":"2026-06-27T08:36:14.648Z"},{"html_url":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-38547","published":"2026-06-23T17:22:32Z","url":"https://euvdservices.enisa.europa.eu/api/enisaid?id=EUVD-2026-38547","modified":"2026-06-23T17:35:51Z","id":"EUVD-2026-38547","imported":"2026-06-27T08:36:01.407Z"}],"license":"CC-BY-4.0"},"references":[{"type":"WEB","url":"https://github.com/advisories/GHSA-8xpq-cjcf-3wh9"},{"type":"WEB","url":"https://github.com/denoland/deno/security/advisories/GHSA-8xpq-cjcf-3wh9"}],"affected":[{"package":{"name":"Deno_jll","ecosystem":"Julia","purl":"pkg:julia/Deno_jll?uuid=04572ae6-984a-583e-9378-9577a1c2574d"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"2.8.1+0"}]}],"database_specific":{"source":"https://github.com/JuliaLang/SecurityAdvisories.jl/tree/generated/osv/2026/JLSEC-2026-655.json"}}],"schema_version":"1.7.5","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:L/I:H/A:N"}],"credits":[{"name":"tomasilluminati","contact":["https://github.com/tomasilluminati"],"type":"REPORTER"}]}