-
Notifications
You must be signed in to change notification settings - Fork 5.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Permissionless static relative imports are dangerous #3401
Comments
Yes it's a good point. If we are to take security seriously we can't allow reading random JSON... |
I would say instead it might be more reasonable to require (I remember hearing Myles saying something about JSON imports support being blocked in browsers) |
|
I think the relevant thread is: WICG/webcomponents#839 |
@kevinkassimo Update: I mean I would want to know that my program is "importing" a random JSON API or something. |
Also, I see two other arguments for requiring
Thinking about CORS analogy, @ry do you think that importing JSON by a module in the same directory, i.e. |
There is a really interesting "HTTP 203" episode where they get into imports from the browser and in particular why JSON import was dropped. https://www.youtube.com/watch?v=jG7VfbqqTGw
I think a local import of any type (JS/TS/WASM/JSON/CSS/...) that is outside the root of the script (meaning: it probably wasn't downloaded along with the script) should require an Even if JS/TS are "just code" usually and will be limited by the permissions given to the called script, that's not a reason to give a script the ability to execute them and get their results. JS can be data too: think of Another advantage is that this way static and dynamic local imports are beholden to the same rules, which makes things easier to reason about. In the end, since Deno is meant to be safe a script should have only the most limited set of default permissions to run. Being able to import a JS from outside its directory is not a good default. Just my 0.02 |
Any real-world app is going to need the ability to import a file from a sibling directory. How do you decide where the module's root is, anyway? In Node, it's easy because you can just look upwards in the filesystem until you find a package.json. I guess in Deno, the best we could do is use the directory of the entry point. But that wouldn't work for all project structures, such as files in |
I think that's the idea.
I would think Deno projects would be structured so that all files necessary for code to run will be at or below the entry-point. |
That You can also bypass
(Sure, it's only a DoS-ish / timing attack thing, but still...) Also, it's not enough to just solve JSON. You can also check for existence of directories outside of your sandbox:
I propose everything is just made explicit and require For a real non-sanboxed app, you'll need a wrapper anyways (shebang doesn't work for anything non-trivial, e.g. you can't specify a relative importmap). Only thing affected will be the sexy one-liners. Meanwhile, the other way, securing things if defaults are loose, is nearly impossible. |
@jakajancar Good example - this is a problem. What if alternatively we required a special flag for dynamic imports? |
This look like a bug in implementation of TS compiler, we explicitly check each dynamic import from V8 for permissions; either read or net, depending on scheme. In provided example request is made by TS compiler which is priviledged and doesn't check permissions for downloaded files; we should change that. |
The same could happen in JS. You write a file with a static URL import, then dynamically import that file. This allows a request to be made to a dynamically defined URL without |
So TS compiler runs outside the sandbox? |
No, V8 will call |
TS compiler is run in a separate isolate and thus separate thread. It is considered priviledged because it can load source files and write to disk cache. It looks like case of dynamic import was not covered with permission check, but it should be an easy fix with example you provided 👍 |
I don't think it is a loophole in TypeScript. Something like |
Looking at the example, it is a The fix would be to keep a set of dependencies and short circuit the cyclical nature of the importing. I will try to fix it today/tomorrow. |
I don't see an infinite loop in my example. I just put a |
Ok, then, the problem isn't with the compiler per-say @bartlomieju. It appears to be with a dynamic import, we aren't properly awaiting the compilation to complete and updating the cache. Because it should compile once, and then be a cache hit every subsequent import. Any |
Could these recent findings be moved to their own issue? Thanks. |
@teleclimber Yes, these are very different and important issues. I created #4383 for the |
Closed in #5037 |
In the current version of Deno (0.24) it is possible to import a module from outside the directory of the running script:
My understanding is that this is justified because a malicious script can not know where it is going to be run and therefore an attempt to load a relative path as above will almost always result in Deno exiting with an error.
Although it's true that in some uses of Deno a malicious script author can not know where a json with secrets might be located relative to where it is run, there is one situation where this can be known: if Deno is used as the sandbox subsystem of a larger system, then Deno will always be called in the same way (predictable cwd and path to potential secrets).
Imagine for example an application platform that uses Deno as its sandbox. It has the following data directory:
The application platform would probably call Deno with very limited permissions, and it certainly will not provide an
allow-read
permission that includes config.json.However the malicious app will not need that. If it's designed to run on the platform it can read
config.json
like this:I believe Deno should disallow relative static imports outside of the main script's directory unless
--allow-read
allows it.Thanks,
✌️
The text was updated successfully, but these errors were encountered: