The /.nofollow and /.resolve Magic Directories
John Daniel (via John Siracusa, Reddit):
As of 26.1, when you encode a security-scoped bookmark to “file:///”, what you decode will be a bookmark to “file:///.nofollow/”. So the decode method now succeeds, but the value is wrong. I actually preferred the behaviour of the original bug.
The “.nofollow” syntax is a new part of the core system that allows components to construct paths that the lower level system guarantees will not be resolved or followed. This makes it simpler to protect against TOC/TOU attacks by allowing one component of the system to resolve a particular path, then pass that path to another component while guaranteeing that the second component won’t inadvertently cause a second resolve.
Unfortunately, the bug here is that parts of Foundation aren’t handling this correctly when the path references root.
I expect this will be resolved in the next system update [26.2]; however, it’s not clear to me whether that will mean that resolution will return “/” again or that the new “file:///.nofollow/” construct will start working.
However, even if we revert to “/”, you should be aware that “.nofollow” and “.resolve” paths are not inherently invalid and you should expect to see more of them in the future.
I don’t remember seeing this in the documentation or at WWDC.
The standard way of preventing this attack is by passing one of the “no follow” flags to
open, but in a complex system that can be extremely difficult to guarantee and validate.The new “.nofollow” construct effectively “attaches” the no follow flag to the path itself, forcing that flag on all open calls regardless of the actual flag passed in.
I’m not sure what the current state of things is, but the expectation is that most/all syscalls that interact with paths will “preserve” these “.<flag>” prefixes. I’ll also note that the behavior of
realpathwill change based on “.nofollow”.[…]
In the context of framework code, I think the best option is to treat any “.nofollow” path you receive as “inherently” canonical and simply use it directly.
My general advice here is to treat any URL you receive from the system as a “magic” object. In practice, I generally convert it to a bookmark, then resolve the bookmark again, and use that new URL*, discarding the original (“magic”) URL. *This ensures that the rest of my app is ALWAYS working with “a URL that came from a bookmark”, instead of a “split” flow.
Previously: