Rendered at 19:13:21 GMT+0000 (Coordinated Universal Time) with Cloudflare Workers.
jFriedensreich 1 days ago [-]
I was a fan of deno because at least they tried, its not that there was an alternative runtime that tries to do permissions right or that there is an operating system tool that allows async(!) permissions with user interaction. We really need a simple to use "productised" but true open source solution that allows halting any software and evaluating policies for run, fs and network etc and can reason on process and context level.
schlarpc 12 hours ago [-]
I built exactly this at work over the past few months, sadly not open source though. It uses Cedar policies for process lifecycle, network activity (at basically every layer), filesystem access, credential usage through brokers, and so on, with context injected into every evaluation, and cgroup freezer on Linux for stop-and-escalate-to-user. Maybe I should use this as evidence of demand for us to open source it...
ch4s3 1 days ago [-]
I’m working on a capability system for a language that can compile to js[1], pardon the mess.
If I’ve understood this correctly, you’re using “capability” to mean something kind of like what Linux means: there is a collection of capability types, and each type is a singleton. (And at least the beginning of your page does not clarify whether the distinction between “needs IO” and “Cap(IO)”.)
This is genuinely useful in a language, but I suspect it’s also pretty weak. Serious capability systems have capabilities that are instances of types, and two instances are not necessarily the same. For example, the capability to read one file is not the capability to read another file. Singletons may still exist: the capability to read the system clock could reasonably be a singleton. (Or not: one may reasonably debate whether the cycle counter and the realtime clock are instances of the same type or not, and you’ll find that mainstream languages have what amount to very different answers to this sort of question.)
ch4s3 23 hours ago [-]
These are types. Cap(Io) is a type, and used to grant IO, but you can narrow to reads or writes of single files/folders, network requests to single URLs, and so on. Needs is a declaration of the necessary Cap grant. The compiler will error on permissions that aren’t granted.
The Cap system is modeled on Eiffel and a series of papers I can link to later when I’m at my laptop.
It can audit dependencies for capability changes and can inspect binaries for undeclared IO. The run tool can analyze binaries and refuse to run a binary that tries to hide effects.
March’s capability system can be used to build binaries into a Linux sandbox so that you have multiple layers guarding prohibited actions.
kragen 18 hours ago [-]
You probably shouldn't call it a "capability system" if you don't mean ocaps, because despite your best intentions, many people who are familiar with ocap systems will assume you are engaged in some sort of fraud.
ch4s3 17 hours ago [-]
It literally is a capability system it just isn’t OCAP because it’s a purely functional language and there are no objects. You declare capabilities you need and grant them where they’re allowed. It differs in happening at compile time only and not being threaded through every method which I think is ergonomically awful.
It’s heavily inspired by Eiffel and Pony. It is also coincidentally a lot like Bluefin for Haskell[1], and in that spirit I use them beyond security. For example behavioral promises like no_alloc and no_panic.
I’ve shown to some compiler researchers who understood it to be a capability system. In your mind what makes it not a capability system?
It really sounds like you're describing an ambient-authority system, which separates designation from authority. Ocap systems don't separate designation from authority, and purely functional languages do have objects in the sense of "object" that the "o" in "ocap" stands for.
ch4s3 7 hours ago [-]
You’re splitting a weird hair here, every single ocap language I’m aware of has objects in the sense of OOP. And o ap isn’t the only kind of capability system, Pony for example has two separate systems. March works a lot like Austral and Roc. Modules and functions need capacities which can be granted at an app root, a program can have multiple app roots to segregate authority on a machine or a cluster.
Again I said capability system and never claimed ocap.
kragen 2 hours ago [-]
You are rapidly burning through the presumption of good faith. This is not a hairsplitting distinction, but a completely different approach to computer security, and claiming that the difference is insignificant is strongly suggestive of a grift. CHERI's ocaps are largely capabilities to assembly-language memory regions, and Jonathan Rees's seminal ocap work was at the Scheme language level, which also doesn't have OO. Pony's ocaps are also not generally to OO objects.
amluto 15 hours ago [-]
You lost me. If I want to grant read to /etc/passwd, how do I do it without threading it through every call? Is it an implicit parameter system? Do you literally write “/etc/passwd” in the code?
ch4s3 7 hours ago [-]
Authority grants are ambient at an and the compiler makes distinctions at the module level, though you can annotate a function. It’s most like a language called Austral.
sieve 22 hours ago [-]
I have been exploring something on the lines of capsicum for my language+runtime and you want to balance usability and security. Too much friction and people WILL use --all like I do with Deno.
My current thinking is to freeze all caps on startup. pid-0 within the runtime has the max caps the application will ever have and it can parcel out subsets to other processes that need it.
This can show up on the ux side as a single permission request listing all the caps the application claims it needs with a default negative response.
ch4s3 22 hours ago [-]
This is what I do by setting caps at compile time, they aren’t a runtime feature. You either compile out code without a grant or it errors.
sieve 21 hours ago [-]
How will you allow permissions for a `ls` style tool? I want to be able to do `ls /etc`.
amluto 19 hours ago [-]
The tool parses its command line, claims the capabilities it needs, drops everything else, then runs.
Or the language it’s written in allows writing a function that takes a recursive enumerate capability on a directory plus a write capability to stdout.
A while back I sent a brain dump email to LKML about “path capabilities”. Maybe I’ll try to implement it.
sieve 16 hours ago [-]
My current design idea lets the runtime collect all the requests till the application locks its set of demands. This entire set is presented to the user who can then approve/reject/approve-always
The runtime maintains state in the config directory so that approve-always works for `(hash(ls), dir_read(/etc))` without producing that question during the next `ls /etc`
ch4s3 20 hours ago [-]
Probably on a directory read basis. I’d need to give it more thought.
Thanks for the thoughtful question!
thegagne 22 hours ago [-]
Really like this and think we need something like this in every major language, especially to address supply chain attack stuff.
A couple of thoughts:
- would really like to have the option to make things more granular… things like “which dns/tls will this have access to?”, “which file/folder?”
- probably would be helpful to have permissions to be kinda two sided, like “this function needs tls”, and then on the import side “allow this function tls access to this host in particular”.
ch4s3 22 hours ago [-]
Thanks! It can be file and url level granular, there’s a capabilities page that mentions that[1]. Caps and needs are the two sides. A module function needs File.write, and you can grant a narrower cap to a single folder or file.
I did look for “url” on that page and came up empty, then read it with my own eyes and didn’t see it, and even passed it to Claude and it also couldn’t find an answer for my query:
What I would like to be able to do is to say “only let this imported function talk to https://example.com/*”, and deny any tcp connections other than the dns lookup and https calls to that domain.
ch4s3 4 hours ago [-]
I have a draft PR open to enable something like
mod A do
needs IO.NetConnect("example.com")
end
mod B do
needs IO.NetConnect("foo.com", no_redirects)
end
Where A can call "example.com" but not "foo.com".
ch4s3 6 hours ago [-]
Sorry the docs aren’t 100% there yet, the language is still pre-1.0. You can do that but it’s not as well supported as file paths, but will be by 1.0.
szundi 1 days ago [-]
[dead]
hoppp 1 days ago [-]
Deno has 1 year of official support left...
I liked the security model because at least they had something, unlike node.
arbll 1 days ago [-]
Something can be worse than nothing when it tricks users into thinking it provides meanigful security guarentees.
LoganDark 23 hours ago [-]
More than users, too. SMS 2FA for instance will encourage important services like banks and government applications to omit or bypass higher security measures because the visitor is extra verified. When in reality, this is just a boon to attackers, because it's easy to just steal someone's phone number to pass the verification, and then the website's made it even easier by not giving you any additional obstacles. No abuse protection or whatever, because clearly you're the user.
A service I use recently introduced mandatory two-step verification. This is good. However, they implemented this by adding the email factor to all accounts. I had much more secure methods configured, but now you can bypass them with email. This is bad. I'm still not sure they understand what a critical security issue this is.
> It does not provide security guarantees in the presence of malicious code. Malicious code can bypass the permission model and execute arbitrary code without the restrictions imposed by the permission model.
The permission model is more of a "help developers not accidentally "rm -rf /" their local machine than it is something that helps end users.
skybrian 1 days ago [-]
But doesn’t preventing accidents help end users too?
foldor 21 hours ago [-]
Sure, but it's more footgun protection, and less security. Still useful, but not the same.
its-summertime 14 hours ago [-]
with deno being "The main thing it protects you against is malicious code that isn't smart enough.", does this not practically put them on the same footing?
mscdex 1 days ago [-]
You're better off relying on OS-level security mechanisms. Not only is it more likely to be much safer/secure, but it also means you don't have to worry about compatibility, missing features, etc. when you switch runtimes/software stacks.
zahlman 17 hours ago [-]
So, what happens to yt-dlp?
EGreg 1 days ago [-]
If you like having something for security — even in Node — check out something I invented, called SafeScript.
It’s for executing untrusted code written by others… a large subset of Javascript which guarantees the code can only call the Facades you inject, and you can hook and intercept anything. It works with callbacks and promises.
What’s more is that it runs in WebWorker sandboxes on the web, and uses a proxy to transparently make existing code make same method calls, or use existing objects, like window.document, but to safer versions, which only allow eg createElement.
And because SafeScript automatically awaits every async function, your synchronous calls to HTMLElement methods etc. will be proxied asynchronously and “just work” without changing the original JS code. SafeScript is robust enough that most existing JS components will simply work without changes when ported to SafeScript.
PS: It can also be embedded in PHP, so you can run untrusted handlers there, too, without worrying about PHP’s large security surface.
PPS: SafeScript is deterministic by default, so you can also get the same results across all environments or runs. You can set the random number generator seed yourself, or inject Date.now() as one of your APIs if you want.
I actually have a terminal program that needs to ask for sensitive inputs from the user sometimes, potentially after attacker data has been printed. I've been trying to figure out how I should handle that!
My plan was to encode to Unicode (lossily), strip out control characters, and add some visual framing. Anything else I should be considering? Is there a standard approach? Haven't actually done the research on this yet, but since it came up here, I figure someone might know.
Stripping out just the control characters isn’t enough. A lot of terminal control sequences start with a single control character followed by normal printable characters but such printable characters are part of the control sequence. And even when you strip out entire terminal control sequences there are still other Unicode characters that will play tricks. Examples include the Unicode right-to-left override, which is not an ASCII control character but a Unicode control character. There are also dozens of space characters that look like spaces but won’t be split by ASCII word splitting processing, etc.
phyzome 23 hours ago [-]
I'm OK with turning ANSI control sequences into garbage, as long as its safe garbage. But yeah, seems like I should find a library that covers all this.
dingaling911 1 days ago [-]
I don't understand the UX you have in mind, just do it like SSH used to do, a non-echo input field?
phyzome 1 days ago [-]
Oh, it's not like a password (where you'd want to prevent echo). The value is actually a digest of a public key -- one instance of the program prints the digest, and the user has to enter it into the other instance of the program. This is for setting up a secure P2P connection.
I may have misstated the problem above. What I really need to ensure is that a malicious output can't say "the digest is abc123" and trick the user into pasting that into the other program (when the real digest is def456).
dingaling911 22 hours ago [-]
Just have a link that the user can click that copies the correct hash to the clipboard.
phyzome 20 hours ago [-]
Both ends are terminal programs.
dingaling911 19 hours ago [-]
So side A prints out a value, side B has to input it using the user as the go between.
Hmm...
Isn't there a public key based way to avoid needing the user to do this.
phyzome 18 hours ago [-]
Not that I can think of, no. This is the public key based way of doing things.
dingaling911 17 hours ago [-]
I mean the value gets encrypted or signed with a private key.
phyzome 15 hours ago [-]
Not really applicable here. If you want to create an alternative design, I recommend first looking at src/protocol.rs to see what the current design constraints are. (Or the README.md for the overall workflow.)
cadamsdotcom 20 hours ago [-]
Re the first point: stdio only offers one way to interact with users. stdio is what pulls naive designers into this trap - untrusted output sharing a channel with trusted decision input.
Deno should disallow arbitrary output on stdout/stderr. Even prefixing the lines won't work because control characters like ^H are sent in-band and can delete the prefix.
The "in-band vs. out-of-band" is nothing new. Worth looking up 2600hz, the signaling frequency of the US phone system way back when. Pick up a handset, generate a 2600hz tone. Suddenly you're talking to the call routing system. Only a few steps from there to free phone calling anywhere in the world. Call routing signaling should've been separate, out of band - but given the tech at the time they'd have needed extra wires for signal lines ($$$) so the tradeoff was made, risk accepted.
The phone system designers went for in-band signaling to save money. Deno doesn't need to save money by running less copper so my money's on naïveté. Which doesn't bode well.
And the rest of the points made.. well yep the author is on point - made worse by Deno developers publicly defending their wrong choices.
Thankfully there're great alternatives.
kevin_thibedeau 1 days ago [-]
> the permission prompt is “partially visible” (you can see an emoji)
Terminals can be configured to render emoji in text presentation form where they become monochrome glyphs that can be colored like ordinary text with escape sequences. Not something to hinge your security model on.
yoyohello13 1 days ago [-]
Lucky for them. Deno is dead.
proxyscore 1 days ago [-]
Deno is a dead man walking, they cashed out being useful to CF and other than that, they tried parity with nodejs, which was naive, I am not going to redo my backend just for that.
hackerbrother 1 days ago [-]
It was a neat idea but they didn't take it far enough. It would have been neat if you could restrict dependencies from network/FS access, but they didn't make it that granular.
I was a fan of Deno from the beginning, but not targeting NodeJS compatibility at the beginning turned out to be the wrong move. Now Bun gets to be Anthropic's testbed for the future of software development. (Which includes the wholesale plagiarism and optimization of Oxc into its codebase, wowza.)
Side note: `deno fmt` is a great Markdown formatter. I might get around to extracting out just that part of it.
bluegatty 22 hours ago [-]
Isn't this a general TUI kind of problem, endemic to all CLI? What does this have to do with Deno specifically?
ximm 22 hours ago [-]
And as far as I can tell, this is the exact same security model that is now used by OpenCode and similar
two_handfuls 1 days ago [-]
Very well written, and excellent points! Thank you.
dontusepython 5 hours ago [-]
[dead]
genxy 1 days ago [-]
I'm not a fan of opinions in titles. (there I did it)
It is self-centered and narcissistic. Why? WTFRU. An opinion isn't an argument. You have to have reasons beyond your opinions and those are what matter. Lead with those, and your opinion is implied by the fact you are talking about it.
Deno's Security Model is a False Sense of Security
Deno's Permissions Are Not a Sandbox
The Illusion of Deno's "Secure by Default" Model
Why --allow-run and Permission Prompts Don't Make Deno Safe
The title weakens an otherwise pretty good article.
[1] https://march-lang.org/docs/capabilities/#the-problem-they-s...
This is genuinely useful in a language, but I suspect it’s also pretty weak. Serious capability systems have capabilities that are instances of types, and two instances are not necessarily the same. For example, the capability to read one file is not the capability to read another file. Singletons may still exist: the capability to read the system clock could reasonably be a singleton. (Or not: one may reasonably debate whether the cycle counter and the realtime clock are instances of the same type or not, and you’ll find that mainstream languages have what amount to very different answers to this sort of question.)
The Cap system is modeled on Eiffel and a series of papers I can link to later when I’m at my laptop.
It can audit dependencies for capability changes and can inspect binaries for undeclared IO. The run tool can analyze binaries and refuse to run a binary that tries to hide effects.
March’s capability system can be used to build binaries into a Linux sandbox so that you have multiple layers guarding prohibited actions.
It’s heavily inspired by Eiffel and Pony. It is also coincidentally a lot like Bluefin for Haskell[1], and in that spirit I use them beyond security. For example behavioral promises like no_alloc and no_panic.
I’ve shown to some compiler researchers who understood it to be a capability system. In your mind what makes it not a capability system?
[1] https://h2.jaguarpaw.co.uk/posts/bluefin-capability-system/
Again I said capability system and never claimed ocap.
My current thinking is to freeze all caps on startup. pid-0 within the runtime has the max caps the application will ever have and it can parcel out subsets to other processes that need it.
This can show up on the ux side as a single permission request listing all the caps the application claims it needs with a default negative response.
Or the language it’s written in allows writing a function that takes a recursive enumerate capability on a directory plus a write capability to stdout.
A while back I sent a brain dump email to LKML about “path capabilities”. Maybe I’ll try to implement it.
The runtime maintains state in the config directory so that approve-always works for `(hash(ls), dir_read(/etc))` without producing that question during the next `ls /etc`
Thanks for the thoughtful question!
A couple of thoughts:
- would really like to have the option to make things more granular… things like “which dns/tls will this have access to?”, “which file/folder?” - probably would be helpful to have permissions to be kinda two sided, like “this function needs tls”, and then on the import side “allow this function tls access to this host in particular”.
[1] https://march-lang.org/docs/capabilities/#choosing-the-right...
I did look for “url” on that page and came up empty, then read it with my own eyes and didn’t see it, and even passed it to Claude and it also couldn’t find an answer for my query:
> Take a look at and see if there’s a way to limit fetching from a particular hostname over https. https://march-lang.org/docs/capabilities
What I would like to be able to do is to say “only let this imported function talk to https://example.com/*”, and deny any tcp connections other than the dns lookup and https calls to that domain.
mod A do
endmod B do
endWhere A can call "example.com" but not "foo.com".
I liked the security model because at least they had something, unlike node.
A service I use recently introduced mandatory two-step verification. This is good. However, they implemented this by adding the email factor to all accounts. I had much more secure methods configured, but now you can bypass them with email. This is bad. I'm still not sure they understand what a critical security issue this is.
> It does not provide security guarantees in the presence of malicious code. Malicious code can bypass the permission model and execute arbitrary code without the restrictions imposed by the permission model.
The permission model is more of a "help developers not accidentally "rm -rf /" their local machine than it is something that helps end users.
It’s for executing untrusted code written by others… a large subset of Javascript which guarantees the code can only call the Facades you inject, and you can hook and intercept anything. It works with callbacks and promises.
What’s more is that it runs in WebWorker sandboxes on the web, and uses a proxy to transparently make existing code make same method calls, or use existing objects, like window.document, but to safer versions, which only allow eg createElement.
And because SafeScript automatically awaits every async function, your synchronous calls to HTMLElement methods etc. will be proxied asynchronously and “just work” without changing the original JS code. SafeScript is robust enough that most existing JS components will simply work without changes when ported to SafeScript.
PS: It can also be embedded in PHP, so you can run untrusted handlers there, too, without worrying about PHP’s large security surface.
PPS: SafeScript is deterministic by default, so you can also get the same results across all environments or runs. You can set the random number generator seed yourself, or inject Date.now() as one of your APIs if you want.
Here it is. Should I do a Show HN?
https://github.com/SafeScript/Safescript
My plan was to encode to Unicode (lossily), strip out control characters, and add some visual framing. Anything else I should be considering? Is there a standard approach? Haven't actually done the research on this yet, but since it came up here, I figure someone might know.
(This is https://codeberg.org/timmc/breakmancer if anyone is interested.)
I may have misstated the problem above. What I really need to ensure is that a malicious output can't say "the digest is abc123" and trick the user into pasting that into the other program (when the real digest is def456).
Hmm...
Isn't there a public key based way to avoid needing the user to do this.
Deno should disallow arbitrary output on stdout/stderr. Even prefixing the lines won't work because control characters like ^H are sent in-band and can delete the prefix.
The "in-band vs. out-of-band" is nothing new. Worth looking up 2600hz, the signaling frequency of the US phone system way back when. Pick up a handset, generate a 2600hz tone. Suddenly you're talking to the call routing system. Only a few steps from there to free phone calling anywhere in the world. Call routing signaling should've been separate, out of band - but given the tech at the time they'd have needed extra wires for signal lines ($$$) so the tradeoff was made, risk accepted.
The phone system designers went for in-band signaling to save money. Deno doesn't need to save money by running less copper so my money's on naïveté. Which doesn't bode well.
And the rest of the points made.. well yep the author is on point - made worse by Deno developers publicly defending their wrong choices.
Thankfully there're great alternatives.
Terminals can be configured to render emoji in text presentation form where they become monochrome glyphs that can be colored like ordinary text with escape sequences. Not something to hinge your security model on.
I was a fan of Deno from the beginning, but not targeting NodeJS compatibility at the beginning turned out to be the wrong move. Now Bun gets to be Anthropic's testbed for the future of software development. (Which includes the wholesale plagiarism and optimization of Oxc into its codebase, wowza.)
Side note: `deno fmt` is a great Markdown formatter. I might get around to extracting out just that part of it.
It is self-centered and narcissistic. Why? WTFRU. An opinion isn't an argument. You have to have reasons beyond your opinions and those are what matter. Lead with those, and your opinion is implied by the fact you are talking about it.
Deno's Security Model is a False Sense of Security
Deno's Permissions Are Not a Sandbox
The Illusion of Deno's "Secure by Default" Model
Why --allow-run and Permission Prompts Don't Make Deno Safe
The title weakens an otherwise pretty good article.