Hacker News new | ask | show | jobs
by danenania 1508 days ago
The security model is a big one for me. If they could extend the permissions system to work for individual dependencies, they could solve one of the biggest security issues facing developers right now. Especially if policies could also be applied to node packages.

Are there any plans to move in this direction? It seems like if you can do it for the full app, you should hypothetically have the capability to make it library-specific. Or perhaps there are non-obvious blockers that make it too hard?

If there are plans to do this, isn't it better to do it sooner rather than later? Better to get library authors in the habit of specifying permissions/policies now while the ecosystem is still small. If you wait too long, it will be a ton of work to retrofit all the existing libs.

5 comments

You should check out Lavamoat: https://github.com/LavaMoat/LavaMoat

It attempts to do what you're essentially describing. It was built by the MetaMask team, where supply chain attacks are an obviously huge risk.

I've spent some time trying to get it working in an app, but haven't been able to get it all the way working. It's still pretty beta and not well documented.

Thanks, I'm super interested in anything that tackles this problem.

Deno's approach seems most promising so far since it's really ideal to have this built in to the core runtime, but it's not really very useful yet as implemented and I don't know whether taking it further is a priority for them.

I, too, would like to have per-dependency security policies. But I can't really see where you would realistically draw the line between dependencies, other dependencies, and your own code. Is a callback, created in your own code, that potentially accesses dangerous APIs allowed? Presumably yes, but what about when it is passed to a dependency that proceeds to call it? What if the dependency returns the callback back to your own code, can you call it then? What if the dependency wrapped the callback in another callback that calls the original callback with altered arguments, and returns that to your code? Do you enforce any potential restrictions statically, necessitating much stricter restrictions on what you can do than typescript on its own does, or at runtime, necessitating some sort of non-trivial bookkeeping tracking all the parts of the codebase that any piece of behavior has touched?
While I don't have the answers to all these questions, I imagine we could come up with some sane defaults.

Even if you have to 'eject' and provide overly broad permissions to certain libraries, I'd imagine these would be quite a small percentage and you'd still get the huge win that the 90% (or whatever) of your dependencies that don't need any system or network access at all and don't have the kind of issues you describe can effectively be removed as viable targets for attack.

For callbacks, Deno could provide a wrapper function that is only available to the top-level app (not dependencies) and causes the permissions in the callback to be evaluated at the app level, not the dependency level. There may be a better way, but that's one idea.

Static checking would be great too. I think a combination of static and runtime enforcement would be ideal.

> Or perhaps there are non-obvious blockers that make it too hard?

To me, it seems like you'd need a new language.

Why is that? I may be missing something, but they're already enforcing permissions at runtime, which seems like the hard part to me. It would 'just' need to be integrated with the call stack so you know which dependency(ies) want system/network access.
IIRC there are all sorts of issues around monkey-patching prototypes, shared objects between modules, etc. which would readily allow escaping any sort of module / dependency level permissions system. You'd probably be better off pitching a typescript subset language with its own compiler / interpreter rather than trying to shoehorn it into V8.
These seem like solvable problems. Prototype modifications are rare these days and should probably be restricted in the same way that system/network access is. Shared objects between modules also seem like an edge case apart from callbacks? I posted an idea on how to handle callbacks upthread a bit: https://news.ycombinator.com/item?id=31326123#31332061
System and network access are all done via the runtime library functions, which are easy to control.

Changing prototype access almost certainly involves modifying V8 in unpleasant ways, and I'm not sure how you would get around the overhead of deno needing to the call stack on every function call- statically analyzing when a function is operating in one context or another is certainly not a trivial problem.

you will not solve the dependency problem with permissions. You will only solve it by reducing dependencies and reviewing code before you updated dependencies

adding permissions will do nothing except add ridiculous overhead and complexity such that to get anything done devs will just give all permissions

Reducing dependencies is generally good (though often at odds with productivity), but I doubt we'll solve it through just reviewing code. Even with a small number of dependencies, the full tree can be absolutely enormous, and there are many ways to obfuscate attacks. It's a severe needle-in-a-haystack problem.

I don't see why permissions have to add "ridiculous overhead and complexity". Most dependencies need very limited (if any) system or network access. Locking those down would be a huge win, and it makes reviewing updates in large dependency trees realistic since you can zero in on permission changes.