59 comments

[ 4.0 ms ] story [ 114 ms ] thread
(a) I'm all for more widespread attempts at capability security*

(b) failing that, an alternate approach to the problem of deep and wide supply chains is vertical integration: just spitballing here, but we're programmers (and these days, it's even claimed we have AI support); is it too much to ask that most of us ought to be able to code up wheels that roll?

* free market mechanisms, at least as currently implemented, seem to me much better at figuring out how to compensate people after something bad has happened than at preventing it from happening in the first place.

The vertical integration thing isn’t feasible for anyone besides a giant company that can afford to write its own OS with no outside input.

Let’s say I code all my own dependencies in my particular domain (Java). I still have to run my code in an environment: there’s the JVM, then Linux beneath that, the hypervisor at my cloud provider beneath that, etc. You want me to code all of that myself too? You know, frankly I would love to understand and control the stack all the way down. But that’s a life goal. It’s not something most people can or would be interested in doing. We’re all stuck blindly trusting a vast, incomprehensible software supply chain.

I feel like this is just going to end up like phone (android at least) app permissions where they give some vague justification to ask for every capability under the sun.
That would be a shame. I think the app permissions model, at least as Android does it, isn't a very good solution at all.
The software development community as a whole should look at that permissions inflation trend more closely also.

We've all heard stories of android torch applications that require filesystem access for murky reasons. Or websites with cookie banners that ask to install thousands of cookies and refuse to work without access being granted.

It seems relatively well established that C requires a trusted environment. You need to have faith in your collaborators and in your toolchain, and in your dependencies, to get something that works reliably. This trusted environment is looking increasingly optimistic as time goes on.

The sandbox approach works relatively well where it's applied. If you're given a lua interpreter that doesn't know how to talk to the network, you're going to have a tough time compromising the underlying kernel to gain that capability. But your performance will be grim. Web assembly is roughly what you get if you run far enough down that path trying to engineer out the inherent limitations.

I wonder if the microservices people are onto something here. Some boundary around a unit marked "database" or "hashtable" that can't talk to the internet, something RPC themed to tie the pieces together, possibly all fed into a single address space to bring the performance overheads down to something reasonable. The problem is it's a huge pain to work with and makes the dev process slower, i.e. you get outcompeted by something broken-by-design that shipped faster.

It's an interesting problem space. Agreed with the OP that it seems underserved.

My experience is that C requires strong developers and attention to detail. That requires time, and complexity increases non-linearly per LoC.

If we're talking supply chain, it's great for embedded and edge devices, otherwise I look to the old wizards to provide the foundational libraries for interpreted services.

Supply chain safety is more a function of economics in my uninformed opinion. How can we reduce middlemen while preventing monopolies or bottleneck moats?

Pony has a concept of Object Capabilities[1]. Basically it lets you whitelist capabilities like network access or more granular access like specific protocol access. In the docs it states "If the library is asking for more authority than it needs, do not use the library."

This doesn't solve the problem of people pulling in huge dependencies that they can't reasonably scan, or so many dependencies that they can't scan, but surely it's a step in the right direction.

[1]: https://tutorial.ponylang.io/object-capabilities/object-capa...

Doesn't do much good if the OS doesn't enforce them like seL4 can.
If the FFI is also capability gated why can't the language do it?

Edit: Pony seems to rely on restricting FFI privileges at the package level https://tutorial.ponylang.io/object-capabilities/trust-bound.... Suppose it could have been function by function ("unsafe") but this sounds fine. Not sure what else I could have meant by a capability gated FFI.

In practice it means no redistributable native libraries or binaries.

You would need installation time compilation, jittin or pure interpretation.

Or some sort of trusted compiler-as-a-service with signed binaries.

You can compile to WASM to work around that, with some performance penalty.
Languages with capability-based security may not be supply chain safe. For example, Pony allows third-party libraries to use the unsafe FFI by default.

I think Pony is close to a supply chain safe programming language, but it looks like it's not safe by default. I don't know enough about Pony to understand whether this is a fundamental design issue or just a question of carefully invoking the compiler to avoid unsafe features.

Do not use dependencies. Implement everything from scratch. Maybe having a huge reviewed std library solves this, if you never need to import anything from a third party.

As long as there are supply chains there are attacks. reduce your dependencies to bare minimum.

You can use dependencies. You just need to cache them internally, review them meticulously, and never upgrade them without going through the review process again. It's a huge pain but some companies do it.
Out of date dependencies also introduces vulnerabilities though. So you’re screwed no matter what.
That is true. So you need to continually audit those dependencies as well and upgrade them when security requires it.
Essentially this is was distributions such as Debian do for your (if you trust them and their security enough which depends on your requirements). xz is noteworthy because someone spent more than two years trying to subvert this.

Note that the distributions used by package managers such npm, pip, or cargo do not do this. So be wary of the "all old is bad and needs to be rewritten and C does not even have a proper package manager" crowd. (memory safety is a good thing though)

Or fork and cache with dedicated maintenence, if that's what you were indicating above.
Not forking. There are systems for internal dependency management. You can have an internal npm or maven or whatever with only approved packages and versions.
This is not realistic for most startups. You’d spend all your time reviewing packages and no time building.
100%. It's also awful inside a big corporation. Imagine waiting weeks or months to get a new package approved.
(comment deleted)
A big issue with this is that programmers re-implementing parsing code--be it for network protocols, file formats, or the like--are bound to make the same mistakes that lead to DOS/memory corruption vulnerabilities. Pulling code into the standard library for every possible network protocol, file format, compression standard, etc. would engorge the stdlib to the point that it would be impossible to both maintain and stringently audit incoming changes.

That being said, I am a big proponent of keeping dependencies slim, and library authors should especially aim for minimal dependencies where they can--that would help account for the deeply-nested dependency issue we see in npm and the like.

Almost no one grasps, or interested in, formal verification. It's difficult to get software devs to even do unit testing much less prove their compilers always produce correct code.
I agree. From a technical point of view formal verification would absolutely be the right direction. I haven't given up hope. A long time ago, I was flamed to death for suggesting that reproducible builds are needed, today we almost have this.
This sounds more like a business problem at odds with quarterly planning.
Have you actually tried formal verification of software? It's extremely difficult. 99% of programmers will not be able to do it at all, and even for the elite who do know how to do it, probably 99% of programs are too complex to be formally verified anyway.

Even if that were not true, it has absolutely nothing to do with supply chain security.

.net actually does support a lot of what you are talking about, although the features are rarely used/enforced in reality.
Would you go into some specifics to guide my web searching?
https://learn.microsoft.com/en-us/nuget/concepts/security-be...

However, I've seen some definitely dodgy packages in NuGet. Since "bulk insert" isn't (wasn't?) widely supported in EntityFramework packages, there were some extremely dodgy copies of commercial bulk insert frameworks being made available, along which who knows what else.

I find npm/node.js the scariest package manager combo though. Including something simple that then drags in 20 or 30 other packages is inexplicable.

That is a good list, many of these can also be applied to Rust because it has many of the same affordances in its build and supply chain ecosystem. I thought maybe there was module level capabilities in .net, I don't follow it at all but would be willing to take a look.

I feel gross even using pip in python, because the thing you use as the noun in `pip install <noun>` isn't the same as the package you are importing. Name attacks in Python are trivial to exploit.

JS is even crazier, your gonna get a disease! There is no safe JS.

.NET does have signing capabilities for assemblies...but you have to trust who signed it. In theory that's a bit of extra protection, but in practice how do you know who signed it was trustworthy in the first place.

I like .NET but I often feel it's safer than node.js by accident, because the range of packages you need to get something running is much smaller because the date/time classes are sane and things like networking and security are built in rather than imported. But there is no getting around the fact that you're still vulnerable to all the same supply chain attacks as Javascript.

What helps is companies would sometimes disallow access to external feeds like nuget.org and instead host internal feed with pre-approved packages and their versions.

Luckily, the standard library is so extensive you rarely find yourself reaching for an external package for a piece of basic functionality, and companies often are extremely averse to taking on third-party dependencies to a point of unreasonable (you have to go through sec dept even for otherwise popular and actively maintained packages which really doesn't help as it leads to NIH proliferation, doing more damage than good).

It is probably less prevalent in small size businesses however which has its advantages - the productivity loss and maintenance effort is highly likely outweighs whatever security benefits the above approach may bring, if any (good OSS package is usually more secure and does a better job than a particular team often unequipped with sufficient skills).

Ok, lets be realistic about our threat models.

This attack wouldn't have done much if you were running fast and loose with dependencies but had proper layered network infrastructure. There should never be a single point of failure where a single supply chain attack, 0-day, insider threat, etc should be able to kill you and it's not that hard to achieve this.

Put your shit inside a VPN, secure the systems inside the VPN as if they're connected directly to the internet, vlan or even airgap systems that are hyper critical, etc.

I build satellites where we actually do some of the supply chain caching and review, but it's for very specific things that are absolutely mission critical and the risk of >0days is lower than the risk of a memory leak locking up a system.

Think critically about what it would take to access and pivot within your network.

I have one literally withering on the vine.

I released it today. A week ago, I released a blog post that mentioned [1] a supply chain-safe build system and language.

Despite that post and the traction it got, the 6-hour-old Show HN is already long gone, not even on the first 10 pages.

Long ago, Java had something for supply chain safety in the form of SecurityManager. It was removed.

There are no supply chain-safe languages because the industry doesn't care.

[1]: https://yzena.com/2024/03/what-computers-cannot-do-the-conse...

I think you need to explain what it does better. There are many build systems. Why use rig instead of another build system?

Here is the Show HN post:

https://news.ycombinator.com/item?id=39904895

I set up a whole website for it, but I guess people don't look.

The difference between Rig and other build systems is that it doesn't do anything for you. Others like to magically make your build work.

I guess my problem is that I didn't emphasize the supply chain safety. That is on me. But I didn't because that part isn't ready yet.

Yes, you have start with some advantages, or people won't read any further. Maybe next time?
There won't be a next time for Rig, unfortunately. Gotta move on.

I have heard lots of people say here that they want code upfront, so I put it near the top on the website. Turns out, not so much. Oh well.

Having read the 2 recent blog posts, I was interested in the release and read through most of the rig site.

The hook on the rig page tells me it's an alternative to make, and geared towards C code. It doesn't tell me why this is better than make or anything else, and it doesn't look easy to use. Even if you promised the moon, it's hard to imagine anyone would jump and rewrite their codebase to try a new build system.

I want a build system to take care of as much as possible for me, the package availability nixpkgs with the speed of bazel. Nix is underused because the beginner UX is atrocious, and while the package management is good, it's not as suitable as a build system. Bazel and it's offspring are underused because it requires a huge amount of up front setup, and has a half baked package management story.

Since you have clearly thought a lot about this space, I hope you don't completely drop the ideas you have here.

That post is really, really long. And
I try to make my marketing useful. Shame on me.
It's fine for the explanation to be long, but the hook needs to be short.

Edit: Also, if we're discussing https://news.ycombinator.com/item?id=39904895 - it's not even a question of length, I don't see a hook there at all. If I click into https://rigbuild.dev/ and get halfway down the page then I start to see interesting features, most of which are marked as coming soon (so... not present). But I almost certainly wouldn't click the link, because your actual post just says that it's a build system and that it's proprietary, at which point I lost interest. Some people will put up with a build tool that isn't open source, but you still have to give them some reason to be interested.

It is not proprietary.

I think that is the real reason: if it's not FOSS, people dismiss it out of hand.

Also, I was hamstrung by Show HN rules.

> Note that while Rig is a commercial project, everyone, including any company, is free to try it for 30 days, and as long as companies meet the criteria for contacting me, I am open to helping them with Rig for free for 30 days.

- https://news.ycombinator.com/item?id=39904895

> Proprietary software is software that grants its creator, publisher, or other rightsholder or rightsholder partner a legal monopoly by modern copyright and intellectual property law to exclude the recipient from freely sharing the software or modifying it, and—in some cases, as is the case with some patent-encumbered and EULA-bound software—from making use of the software on their own, thereby restricting their freedoms.

> [...]

> Proprietary software may either be closed-source software or source-available software.

- https://en.wikipedia.org/wiki/Proprietary_software

It sure sounds proprietary. In any event, yes, if you prefer I can phrase that as "I'm not going to use a build system that's not Open Source without an incredibly compelling reason".

No, it's not proprietary.

End users are not restricted from modifying it and sharing their changes, so long as it is not for a commercial purpose. Therefore, it is not a monopoly to share and modify and not proprietary.

And the fact that you and others won't is exactly why xz happened and why we might lose FOSS completely.

> And the fact that you and others won't is exactly why xz happened and why we might lose FOSS completely.

As opposed to your approach, which... is also not FOSS.

But this is a fun thread to pull: You wish to argue that it's necessary to force payment for commercial use of code. Fine; I disagree but that can be coherent. Are you doing that? Your project is commercial. Are you paying the authors of your compilers? Are you paying the authors of the open source code I see copied into your source tree? Or is it just your code that people have to pay for in order to avert the next xz?

I have a list of software that I was going to throw money at, yes. Starting with direct dependencies.

Am I doing it now? No, because I can't afford to. Because people want a free lunch from my software.

I'd like to think that there are supply chain safer languages.

Which are languages that have a large comprehensive official standard library backed by a large corp.

Those corps have the manpower to better audit contributions.

Examples: Microsoft's .NET (C#/F#) and Google's Go.

I see what you mean. A "batteries included" standard library reduces the risk for obscure third-party dependencies.

Qt comes to mind. It includes the kitchen sink and a lot of applications can be written without third-party dependencies.

Go programs tend to have a lot of third-party package dependencies. I don't think it's a good example.

Maybe I am missing something,but it sounds like the features the author is looking for could be part of an OS, not a language. For example:

> An example is that a hash table lookup function should be unable to connect to the internet. That way the function can be used without fear of it becoming a liability if it contains bugs or its source code is manipulated by an attacker.

Reminds me of OpenBSD's pledge [1].

[1] https://man.openbsd.org/pledge.2

Hi, I'm the author. See the "The best approach we have today for run-time isolation is through sandboxing" part in the blog post. It covers sandboxing, which pledge(2) is an example of.

My experience with sandboxing approaches is that they are only used in the most security-critical software because they are difficult to integrate. If the programming language isolates components by default, then the majority of software should be able to benefit.

I feel like the article misses the point, tho. The xz attack didn't happen because of an unsafe language, but for it's legacy build tools and social engineering. The relevant backdoor part about patching a vulnerability into C code, could just as well have been skipped/changed for another form of persistence/RCE. In the end, the original sin of breach was smuggling and execution of the malicious build script and payload, if I understand correctly. I don't see how a capability sensitive language would have prevented this, assuming the build environment remained the same.

Of course certain features of a language are exploited, but for a supply chain attack, I think the real vulnerability is complexity, neglect, carelessness, social pressure and (arcane) code beyond immediate comprehension. Since, before any malicious xz code was shipped, the human element was manipulated and exploited. The same pull request about the introduction of IFUNC, could have been about extending capabilities for another made-up reason. The same exploitation of trust, which allowed packing malicious scripts, would have allowed for... well, packing malicious scripts.

With the xz backdoor, the vulnerability was large chunks of infrastructure being the burden of a single human. I really don't see how this attack dynamic could have been prevented by language design alone. Sure, you wouldn't attack a compression library, if capabilities were tight. You would attack the neglected privileged code base elsewhere...

I liked the idea, but as I was reading it I realised that if we could provide that level of isolation within software, it would solve many of the issues of software security in general. And it would really need to be provided at the O/S layer.

I wondered when it was going to start talking about capabilities and I was not disappointed!

World looks weird. All need infrastructure existed in 1990s in Ada, Modula-2/3, IBM works (even with hardware security, which still exists in Power machines), but nobody care.

Now, sure all these things outdated, somebody have to rewrite ocean of code, add support of modern architectures, device drivers, even to teach people, as most programmers from that niches already retired, etc.

Supply chain security is a tool chain problem, not a language problem. Unfortunately, I don't think this author is able to contribute anything useful to the topic currently.