However, its interesting to see so many dependencies that are required to build it (73 crates last time I checked) and as shells have always been portable across many operating systems, I wonder how portable nushell would be since it wishes to replace my current shell.
In a perfect world, a language's ecosystem would have a tiny stdlib and allow importing of libraries for everything else. The Linux philosophy would be followed strictly.
The problem is just that the overhead in securing, maintaining, and organizing all those libraries is pretty large, as we've seen by npm's repeated failures. Of course the *nix community seems to have largely solved the problem, but there's also a massive culture of volunteerism benefiting them.
For a FOSS language, a large stdlib can be similar to a curated repo in practice.
- torvalds
I'd imagine a feasible solution being a permission-based system (which might already exist), where programs and their dependencies do not have access to certain facilities, like the filesystem, other processes or the network, without them being declared in a manifest file of sorts. Permissions should be inheritable when explicitly specified, and a sub-dependency could not gain a permission it didn't inherit from its parent. Unfortunately this does not work so well with a shell, since the shell needs the ability to spawn arbitrary processes. At least the shell itself would not have network access, forcing it to rely on tools already installed in the system
Also, if we resort to some magical wishful thinking, then all the tools in the system follow this permission model and the package manager is aware of the permissions. You could then query the package manager for all installed software with a certain capability, like network access, disabling tools like curl and netcat.
That's a bit too many. But it's not often you compile your shell from scratch.
I’m always impressed when people show up with fixes like this (that I wouldn’t even know to look for).
Just had a quick play ... on arch linux `yay nushell-git` already works!!!
One area where I got a bit stuck was around help. `man where` gave me nothing, `help where` also. I tries stuff like `ls | where type = File` and got a type error. I think it would be amazing if this thing onboarded people a bit nicer, "where needs a condition, to learn about where type "help where" ... stuff like that
Overall really enjoying the ideas here and I am absolutely going to be following this!
Most shells work on just one of either Unix or Windows; the blog post specifically mentions Windows support as being something on the radar of the developers, which Rust is arguably a better fit for than C/C++ (which most shells are written in) due there being a single compiler, standard library, and build tool with first-class support for both Unix and Windows in the Rust toolchain.
To ameliorate the risk, the security of all 73 dependencies would need to be at least an order of magnitude greater, just to catch up to shells with no dependencies.
The irony is that this lack of "dependency-safety" is far more easily exploitable than any previous lack of "memory-safety".
Of course, this can be easily fixed if developers stop multiplying third-party dependencies, so that importing a dependency involves O(1) risk, not O(?).
How is this an easy fix? Now developers need to develop and maintain O(N) instead of O(1) software projects.
I guess that if nushell had a zero dependency policy, it would have never happened.
And there is a significant risk to spreading your resources thin when reimplementing sensitive dependencies, like crypto or network / http libraries.
At some point, developers need to take responsibility for the security of their projects. If your project is larger, you will need to maintain more code, whether directly or indirectly. You can't simply import a dependency but never audit it. And if you're taking the trouble to audit a third-party dependency, at some point it becomes worth maintaining directly.
> sensitive dependencies, like crypto or network / http libraries.
I am not suggesting that developers reimplement TCP.
It's pain and sorrow all the way down and I find more bugs in my reinvented wheels than in the actu code I set out to write.
How about this:
0. Rebuild from latest stable dependencies, or latest updates to your LTSB, whenever you or your dependencies have a bug.
1. Prefer static linking or lib incorporation if feasible, or use a package manager that properly guarantees you the right versions.
2. Stick to popular repos with responsive maintainers, or
3. Fork the dependency yourself, audit the code and know how to debug it.
4. Watch upstream for security bugs.
Prefer dependencies with smaller flatter dependency trees.
Agreed, but that could be through careful selection instead of rolling your own. And it would be great if there was better tooling for that.
Imagine an ecosystem where every module author automatically gets not just a public repo and issue tracker, but also a way to deal with security issues, an indication if all contributors used 2FA for code contribution and package uploads. Then there could be a search for packages where you can say "only give me those with 2FA and a defined security disclosure process, including all transitive dependencies".
That wouldn't be perfect, but much, much better than any ecosystem I've seen so far.
The approach the Rust community is taking with a sort of decoupled-STL approach is interesting, but I do wish they found a way to collect the known-good-bits in a common namespace.
Or look at bash:
https://www.archlinux.org/packages/core/x86_64/bash/
I would say many shells that are written in C will have an extremely short list of dependencies in contrast to Rust.
As per your request:
$ ldd /usr/bin/bash
linux-vdso.so.1 (0x00007ffce8b8c000)
libreadline.so.8 => /usr/lib/libreadline.so.8 (0x00007fd76a0dd000)
libdl.so.2 => /usr/lib/libdl.so.2 (0x00007fd76a0d8000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007fd769f15000)
libncursesw.so.6 => /usr/lib/libncursesw.so.6 (0x00007fd769ea6000)
/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007fd76a266000)
Those files are generally installed on all major Linux distributions.For completeness:
$ ldd /usr/bin/dash
linux-vdso.so.1 (0x00007ffdbadce000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007f6f2cd28000)
/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f6f2cf58000)
Note that these files are shared between projects, you do not have to download and compile them per project. Can you do the same using Cargo? According to my experience with it from two years ago, I had to download and build over 400 dependencies for all projects, separately. I do not know how many dependencies Rust projects have on average.The problem is that Rust has two issues:
1) In Rust, you don't pay for what you don't use. This means that a lot of stuff tends to be an external library since not using something means you can exclude it COMPLETELY. The issue is that things tend to get atomized more finely than most languages would do.
2) Rust is trying to not bake things into a standard library too early. This is a good comment about standard libraries from Python:
"The standard libraries constitute one of Python’s great features, but they tend to suck up all the oxygen. Programmers are reluctant to write libraries that duplicate their functions, so poor libraries in the standard set persist. Only a few people, like Reitz, are willing to write modules that compete with the standards."
https://leancrew.com/all-this/2012/04/where-modules-go-to-di...