[HN Gopher] Node.js packages don't deserve trust
       ___________________________________________________________________
        
       Node.js packages don't deserve trust
        
       Author : lewisjoe
       Score  : 385 points
       Date   : 2022-04-11 12:57 UTC (10 hours ago)
        
 (HTM) web link (josephg.com)
 (TXT) w3m dump (josephg.com)
        
       | mc4ndr3 wrote:
       | Got tired of constantly being the one to patch upstream problems,
       | ended up deprecating the majority of my (alt)JS problems.
        
       | tlhunter wrote:
       | I was once part of a startup named Intrinsic. We had built out a
       | complex product that protected Node.js processes with extreme
       | granularity. Not much information exists about the product after
       | it was acquired but the blog posts are still up:
       | 
       | https://medium.com/intrinsic-blog/securing-node-js-applicati...
        
       | a9h74j wrote:
       | Is there a need for ... _NPM: The Good Parts_
        
         | acheron wrote:
         | "Would you like something to read?"
         | 
         | "Do you have anything light?"
         | 
         | "How about this leaflet, _NPM: The Good Parts_? "
         | 
         | https://www.youtube.com/watch?v=IoACIIz33II
        
       | [deleted]
        
       | ecmascript wrote:
       | I don't get why this happens to node all the time. They have a
       | very extensive code of conduct that forbids this /s
        
       | jameshart wrote:
       | This is a noble proposal but it requires far too many systems to
       | collaborate on adopting it.
       | 
       | A capabilities system based on optional package metadata seems
       | easier to introduce (think about how typings have been able to
       | progressively layer on top of existing packages)
        
       | cryptonector wrote:
       | > A modest proposal
       | 
       | > It seems .. frankly, solvable.
       | 
       | I don't think TFA understands the reference.
       | 
       | > Capabilities to the rescue
       | 
       | And that's the summary of TFA: sandbox everything!
       | 
       | Except there's always sandbox escapes. OK, those are harder, so
       | sandboxing more is a start, for sure.
       | 
       | There's no panacea here. Dependencies are costly. Open coding is
       | costly. Curating external source and packages is costly. It's all
       | costly. We need to recognize a lot of these costs.
        
       | andrewmcwatters wrote:
       | I made another post here, but in a separate note, some of the
       | developers of the most used software in the Node.js ecosystem, as
       | people, don't deserve your trust either.
       | 
       | For the past decade or more I've watched and personally
       | interacted with the personalities of some of these developers and
       | the last thing they seem to have on their mind is the stability
       | of your software. They just do not care.
       | 
       | Many of them are totally willing to throw away years of work that
       | you also built years of work on for you to chase after their new
       | toy.
       | 
       | These are guys that built popular packages in their early to mid
       | 20s. They weren't thinking about software that lasts any
       | meaningful duration of time.
        
       | etchalon wrote:
       | Seeing how there are dozens of package management toolchains out
       | there, why is NPM so uniquely absolutely horrible?
       | 
       | The only behavioral difference I see is that with most other
       | package tools, installing a dependency pins the specific version
       | of the dependency you installed.
       | 
       | So why does NPM instead pin "any version at least this, or
       | greater"?
       | 
       | Feels like a decision that was made when the tooling was young to
       | help improve reliability that has since created this nightmare.
       | Is Yarn better about it?
        
         | feross wrote:
         | JavaScript is so much larger than every other ecosystem, so
         | even a very small probability event (somebody introducing
         | malware into a package) can happen surprisingly often given the
         | scale of the ecosystem. Supply chain attacks are a problem in
         | all open source ecosystems - not just JS - but they are a bit
         | rarer and don't effect as many people so fewer people take
         | note.
        
         | neurotrace wrote:
         | > why is NPM so uniquely absolutely horrible?
         | 
         | I'm not sure that it is. I think it's a numbers game. I've
         | read* that npm is the largest repository in the world. More
         | actors means more good and bad actors.
         | 
         | * Taken from this link. Old article and I'm not sure what the
         | original data source is: https://www.linux.com/news/state-
         | union-npm/
        
         | CraigJPerry wrote:
         | >> why is NPM so uniquely absolutely horrible?
         | 
         | I don't think it's uniquely bad. For example, Python's story is
         | just as bad.
         | 
         | The Java world is the best i know of but even relatively modern
         | stacks like Rust are still making the same errors long-solved
         | in the java world.
         | 
         | For example, nothing stops me publishing a google-grpc crate
         | today (first come first served on names and that name's not
         | taken and i don't need to prove ownership of my domain like in
         | the java world).
         | 
         | C# and nuget is a blind spot for me so I don't know. Go gets it
         | right arguably even better than Java. Deno borrows the same
         | decentralised idea.
         | 
         | The whole situation is a stack of cards right now though. I'm
         | genuinely surprised we haven't seen more specific targeting of
         | high value maintainers. Github is arguably ahead of the curve
         | here a little bit (e.g. the ability to flag and act on unsigned
         | commits).
        
           | etchalon wrote:
           | I've been working with Python for 15 years and I don't
           | remember any period of constant attacks as I see with NPM.
           | Did I just miss them?
        
             | CraigJPerry wrote:
             | I guess so - there's certainly been enough examples of
             | various attacks, from typo squatting and dependency
             | confusion right through to crypto mining and exfiltration
             | of user's data from their machine.
        
         | gedy wrote:
         | > So why does NPM instead pin "any version at least this, or
         | greater"?
         | 
         | It doesn't, it follows semver and you can pin to point, minor,
         | major, etc versions depending on your package.json
        
         | hinkley wrote:
         | There is a flag you can override to be less aggressive, and
         | it's in our onboarding docs, but only because I added it after
         | a conversation with the only other individual on our team who
         | was using it.
         | 
         | It's the classical situation of the framework having defaults
         | that end up being actively hostile to developer success. These
         | Primrose Path situations get under my skin like few things do.
        
           | etchalon wrote:
           | Wasn't aware of the flag. Thanks!
        
       | jcmontx wrote:
       | I remember reading articles like this since 2016 or 2017. How
       | come nothing has changed in so many years? Is this issue not
       | important enough? Or is the solution just to
       | expensive/unpractical to implement?
        
         | marcus_cemes wrote:
         | I think the machine has just gotten too big, it would require a
         | huge effort, even the original creator or Node.js, Ryan Dahl,
         | is strugling create sufficient displacement with Deno.
         | Innovation over safety? Like cheap products I guess, either you
         | join them or get driven out of business, leaving only the cheap
         | products anyway.
         | 
         | Also, if you want to spend a lot of time, effort or money to
         | vastly improve something, would you put it into JS or would you
         | perhaps focus on another language that has better foundations
         | and hope it catches on, where there is the potential to
         | outperform (in quality and consistency) the JS ecosystem?
         | Perhaps those with more sense move to other ecosystems, such as
         | C#, Go, Java, Elixir? I say this as a long-time user of JS who
         | has recently enjoyed his foray into Elixir.
        
       | macspoofing wrote:
       | What's different between the NPM ecosystem, and, say, java/maven?
       | What is the latter doing that the former isn't?
        
         | imtringued wrote:
         | The difference is that non malicious NPM package authors are
         | trying to destroy you with saturation attacks (throw a huge
         | mass of packages at you so you cannot possibly check all of
         | them) so that malware can slip through more easily.
        
         | rootlocus wrote:
         | Most packages you'd pull from maven are developed by large
         | companies or foundations like apache. The dependencies you're
         | pulling are simple jar files that get loaded at runtime and
         | don't execute anything at build or install time.
        
         | ecmascript wrote:
         | I have forgot about how maven works, but at least in npm you
         | have the post install script that lets a package run anything
         | after the dependency is downloaded. So that means package
         | creators can run any code they want on your machine.
         | 
         | I don't really remember if maven has something similar since it
         | was years I did anything in the jvm ecosystem but I think some
         | package managers (like composer if I remember correctly)
         | doesn't give this opportunity.
         | 
         | But since node doesn't have a large standard library it means
         | people will reach out for third party packages for stuff that
         | is small tasks in most languages / runtimes.
        
           | capableweb wrote:
           | Having post-install step available to maliciously use doesn't
           | really solve anything. As you're a programmer downloading a
           | dependency, you're allowing the dependency full control of
           | your system (in most language, Deno seems to try to address
           | this at least) at runtime (at least), so they could do
           | whatever they want as soon as you include the dependency in
           | your application and run it once.
        
             | ecmascript wrote:
             | True true, but at least you will have to check the api and
             | start implement the library itself. Chances increase that
             | you will see something weird about it the more you have to
             | look at it.
        
         | [deleted]
        
         | lucideer wrote:
         | Currently working on this general problem for a large corp:
         | java & js are our two main languages (alongside a lot of python
         | & go, small amounts of swift, groovy, kotljn & c, and some very
         | very old php). Trust me when I say Maven/Gradle etc. are orders
         | of magnitude more painful to solve for than others.
         | 
         | Nothing at all unique to NPM about supply chain risk.
         | 
         | Fwiw, I find Composer to be one of the better of the lot.
        
           | shadowgovt wrote:
           | How tractable is it to proxy the npm package sources?
           | 
           | Were I to try and solve this as an enterprise project, that's
           | the first thing I'd try: have a team declare the specific
           | subset of packages we have hand-vetted and host them off a
           | corporate package manager. Our software builds from only
           | those packages; if devs need more, they petition to get them
           | vetted. If they need new versions, they petition to get them
           | updated. Our team keeps an eye out for hotfixes and
           | periodically might mandate an upgrade if a vulnerability
           | comes around.
        
             | lucideer wrote:
             | We do, but auditing the mirrored sources is still not
             | straighforward. It's a trade-off between completely
             | blocking all production builds on CVEs and fully
             | transparent mirroring, one we're still trying to balance.
             | It's also expensive - proprietary SaaS offerings in this
             | space are not particularly competitive, and managing it in-
             | house is intensive.
             | 
             | In terms of mandated hand-vetted packages, unless your
             | hand-vetting team is inhumanly well-resourced, you're
             | looking at a stifling corporate environment for engineers
             | there, and/or a lot of attrition. Again, needs to be a
             | balance between central control and autonomy.
             | 
             | Our current approach is simply auditing packages we know
             | are deployed in production and assigning tickets to
             | update/remove within a fixed time period, rather than
             | blocking deployments completely. Probably looking to block
             | select deployments based on criticality in the near future,
             | but again distinguishing between a theoretical exploit and
             | one our code triggers in practice is still pretty difficult
             | to automate without significant noise. And the other issue
             | here is differentiating newly discovered vulns (already in
             | prod - blocking deployment doesn't help) vs newly
             | introduced vulns (not yet deployed).
        
         | moffkalast wrote:
         | Throw apt/snap/pacman/whatever into the mix and the answer is
         | still "nothing". People act like package managers are somehow
         | the end all but they're no more secure than going to a random
         | official site and downloading some shit, it's just streamlined
         | the process somewhat.
         | 
         | In fact the latter is probably more secure, since the more the
         | packages depend between each other the worse it gets. One
         | random dependency can be hijacked and will be autoinstalled
         | everywhere. Or someone can delete it and break half the
         | internet as we've seen time and time again.
        
           | capableweb wrote:
           | You're kind of right, but there is a different between the
           | default repositories used by apt (Debian & Ubuntu) and pacman
           | (Arch) and things like npm, in that they are indeed reviewed
           | and can't disappear overnight. You have some guarantee that
           | it won't disappear overnight, because of the organizations
           | behind them. With npm, anyone can publish/unpublish without
           | any sort of review, while packages in the default
           | repositories are reviewed by others.
        
         | tirpen wrote:
         | > What is the latter doing that the former isn't?
         | 
         | Java has a standard library. A really big and well written
         | standard library.
         | 
         | npm has a package called "is-number".
        
         | matsemann wrote:
         | - Bigger packages in java, developed by big organizations one
         | can trust. Seldom depend on small ad-hoc packages.
         | 
         | - installing a dependency with maven is downloading a jar-file.
         | In npm you often need to run arbitrary code as part of
         | installation, making the attack surface far greater.
         | 
         | - usually use a specific version of dependencies, so no updates
         | unless explicitly wanted.
         | 
         | - in theory, a securitymanager can also be used to give code
         | from different libraries different permissions. Not seen it
         | used much in practice, though. Only seen it be used for plugin
         | systems.
        
           | bilbo0s wrote:
           | _In npm you often need to run arbitrary code as part of
           | installation, making the attack surface far greater._
           | 
           | This really is the key. It makes it so you can't even really
           | compare JS to java in an intellectually honest way. It's just
           | not even close to the same. One downloads a Jar that will
           | only ever execute at runtime, whereas node downloads
           | arbitrary code that will execute on your machine immediately
           | if the attacker so chooses. Not only that, but a user may
           | legitimately not even know that node downloaded that module.
           | The dependency tree is so ridiculous that the user would have
           | to look through it with a fine toothed comb to spot the
           | unimaginably big security hole.
           | 
           | On the one hand, yes, the user should have looked through
           | his/her dependency tree, familiarized themselves with what
           | was in those dependencies code-wise, and known what he/she
           | was doing. On the other, come on man. That's kind of like
           | these 100 page EULA's that take away all your rights. I'm not
           | sure that it's reasonable to expect everyone to read those as
           | carefully as you'd need to read them to avoid the problem?
        
             | ryukafalz wrote:
             | The difference between code running at install-time and at
             | runtime is not that big, all things considered. How often
             | do you install a dependency without intending to run it
             | almost immediately afterward?
        
               | matsemann wrote:
               | It's an order of magnitude difference. Code running at
               | install time is almost 100% guaranteed to run. So a
               | transitive dependency 10 layers down is just as dangerous
               | as any other.
               | 
               | Runtime, however, you need some code path to actually hit
               | some part of that library/import it to be affected.
        
               | bilbo0s wrote:
               | _The difference between code running at install-time and
               | at runtime is not that big_
               | 
               | It is in java, and rust, and other languages that have
               | securitymanagers or make security guarantees. Node is
               | running code not only in a context that the dev never
               | intended code to be run in, but also a context the dev
               | has no control over. In rust or java, (or a lot of
               | languages actually), code only runs in a context
               | controlled by the dev.
               | 
               | I mean, in the worst case, with node, you may not even
               | get the opportunity to run the app you were trying to
               | install. The module may just own you at the outset. The
               | dev would be powerless to stop any malicious behavior in
               | the library.
               | 
               | From a security perspective, these are huge differences.
        
         | feross wrote:
         | (Reposting a comment I posted a few days ago.)
         | 
         | There's a few reasons that NPM sees more attacks than other
         | ecosystems.
         | 
         | First, the scale of the JavaScript ecosystem. JavaScript is so
         | much larger than every other ecosystem, so even a very small
         | probability event (somebody introducing malware into a package)
         | can happen surprisingly often given the scale of the ecosystem.
         | Supply chain attacks are a problem in all open source
         | ecosystems - not just JS - but they are a bit rarer and don't
         | effect as many people so fewer people take note.
         | 
         | Second, npm was one of the first package managers to solve the
         | classic "dependency hell" problem. In Python, if you have two
         | dependencies, A and B, which both depend on different versions
         | of C, say C@1.0.0 and C@2.0.0, respectively, then you're in
         | trouble. You have an broken project. Python can only install
         | one version of C. So now you're in dependency hell.
         | 
         | Npm on the other hand just installs both versions of C and it
         | gives A the version that it wants, C@1.0.0. And it gives B the
         | version that it wants, C@2.0.0. Both packages are happy -
         | problem solved.
         | 
         | This caused Python maintainers to think twice before adding a
         | new dependency lest they cause "dependency hell" for their
         | users. Much better to just copy paste these 50 lines of code
         | rather than adding a dependency. So there was an intrinsic sort
         | of resistance - some pain is involved in adding new
         | dependencies.
         | 
         | Npm maintainers had no such constraints. In a way, npm's better
         | developer experience led to the whole module ecosystem scaling
         | "too well". Thus, you end up needing to trust more total
         | maintainers, increasing the risk of supply chain attacks.
         | 
         | Disclosure: I started Socket (https://socket.dev) to help solve
         | open source supply chain security. To learn more, see:
         | https://news.ycombinator.com/item?id=30521913
        
         | michaelt wrote:
         | The short answer is nothing - it would be very easy to
         | introduce a malicious Maven package.
         | 
         | The slightly longer answer is maven artefacts are immutable;
         | the default behaviour is to pin precise versions; and norms
         | among java programmers don't favour using libraries for one-
         | liners like left-pad - meaning there are fewer people in a
         | position to launch a supply chain attack.
         | 
         | The dam still has cracks in it, but there are fewer cracks and
         | some have sticking plasters over them.
        
           | bogwog wrote:
           | > and norms among java programmers don't favour using
           | libraries for one-liners like left-pad - meaning there are
           | fewer people in a position to launch a supply chain attack.
           | 
           | Javascript is the only ecosystem I've seen people doing stuff
           | like that. I know this is going to sound elitist, but maybe
           | the problem is that the bar for learning javascript is low,
           | and the incentives for a javascript developer to improve
           | themselves is also low. You can get away with lazy and bad
           | practices for your entire career, even as a senior full-stack
           | developer. Typescript kinda raises that bar a little bit, but
           | not by much.
           | 
           | I don't think there's a solution to that particular problem,
           | short of deprecating javascript once WASM reaches a point
           | where it can fully replace it. But even in that scenario,
           | we'll probably start to see JS interpreters ported to WASM
           | anyways.
           | 
           | Maybe those "No Code" products are the solution? Replace all
           | of those JS web/app developer positions with people trained
           | on specific No Code platforms that require basically the same
           | amount of programming knowledge, but outsource things like
           | security and architectural decisions to the platform.
        
             | dmatech wrote:
             | I never remember hearing about this sort of behavior in the
             | CPAN scene. There's something different at a cultural level
             | with Node. With Node, there have been several instances of
             | otherwise competent coders destroy their own work to make a
             | statement.
        
             | shadowgovt wrote:
             | The bar to learning is low, the payout in the industry is
             | high ( _everyone_ wants a web site, web service, or web
             | app), and (key in this problem-space) the JavaScript
             | standard library is basically a tiny raisin of
             | functionality.
             | 
             | It's not so much "incentive to improve self is low" as "it
             | doesn't make sense to rewrite something that exists," and
             | since JS developers, to stereotype, tend to be extremely
             | online, they will tend to solve problems by asking "Is this
             | written yet?" instead of writing Yet Another YAML Parser.
        
           | bilbo0s wrote:
           | _it would be very easy to introduce a malicious Maven
           | package_
           | 
           | From the perspective of a cybersecurity researcher, this is
           | just not true. At a minimum, it's not true in the same way.
           | 
           | Node executes arbitrary code on _install_. The best an
           | attacker can do in Java is execute arbitrary code at
           | _runtime_ , and even then, only insofar as the developer has
           | directed the securitymanager to execute arbitrary code.
           | 
           | This is a massive difference that I believe people need to be
           | at once more aware of, and more wary about. Don't believe the
           | hype. Don't let people on the internet telling you there is
           | no difference lull you into a false sense of security.
           | 
           | If you work on a machine, or on a project where security is
           | important, check your dependencies people.
        
             | michaelt wrote:
             | I'd wager 99% of Maven projects run unit tests on build, so
             | I'm not sure the distinction between install-time and run-
             | time is all that meaningful.
             | 
             | And the Security Manager might have been relevant back in
             | the days of Java Applets and Web Start, but I've never seen
             | it used outside of the OpenJDK test suite - and certainly
             | not for protection against malicious code.
        
               | paulmd wrote:
               | > I'd wager 99% of Maven projects run unit tests on
               | build, so I'm not sure the distinction between install-
               | time and run-time is all that meaningful.
               | 
               | Most Java projects don't build their dependencies from
               | source though (unless it's a local project included via
               | gradle/maven). So yes, unit tests run when dependencies
               | are built, but nobody is building dependencies when their
               | web app gets built.
        
               | michaelt wrote:
               | But if a library is among your dependencies, I'd wager
               | you're going to call some of its functions.
               | 
               | So you run a maven build, maven retrieves the library,
               | maven runs your tests, your tests call functions from the
               | library - and the library code you've just downloaded
               | gets run.
        
       | mark_and_sweep wrote:
       | I'm surprised the author doesn't mention Node's Policies:
       | https://nodejs.org/dist/latest/docs/api/policy.html
       | 
       | "Policies are a security feature intended to allow guarantees
       | about what code Node.js is able to load."
        
         | josephg wrote:
         | Author here! I didn't even know that existed. Thanks for the
         | link!
         | 
         | That looks like it has the same problem as Deno's solution, in
         | that its too coarse for my taste. I want to explicitly give
         | permission to a _library_ , not to the process as a whole.
         | (Since I don't want some errant library deep in my dependency
         | tree to nuke my production databases.)
         | 
         | I love the definitions of scope though - that looks like
         | exactly the sort of thing that I want here.
        
           | zozbot234 wrote:
           | > I want to explicitly give permission to a library, not to
           | the process as a whole. (Since I don't want some errant
           | library deep in my dependency tree to nuke my production
           | databases.)
           | 
           | Doesn't Java have a SecurityManager feature that can do this?
           | Perhaps we need a JS equivalent.
        
             | vbezhenar wrote:
             | SecurityManager was a bad idea. It might be useful to
             | prevent accidental bound trespassing. But it turned out too
             | flaky to serve as a security foundation. Basically nobody
             | uses it for security.
        
             | gorjusborg wrote:
             | Also, the security manager is getting removed:
             | https://openjdk.java.net/jeps/411
        
             | jameshart wrote:
             | The trouble with security manager is it's only as good as
             | it's widespread support.
             | 
             | If it worked, and was widely used, for example, nobody
             | would have had to worry about the possibility of their
             | logging library downloading code from an LDAP server and
             | executing it.
        
           | mark_and_sweep wrote:
           | > Author here! I didn't even know that existed. Thanks for
           | the link!
           | 
           | You're welcome! To be fair, Policies are still experimental.
           | That's probably why they didn't get much press despite being
           | in Node since v11.
           | 
           | > I want to explicitly give permission to a library, not to
           | the process as a whole.
           | 
           | You can enable/change/disable imports for a given library,
           | though this might be quite cumbersome for a wide or deep
           | dependency tree.
        
       | vlunkr wrote:
       | Pasting a comment I made about this a while ago:
       | 
       | The reason this keeps happening with NPM is because of absurd
       | number of dependencies in the average node app. I have a tiny app
       | I've been playing with using create-react-app. There are over 800
       | directories in node_modules. That absolutely dwarfs the number of
       | any other language I've used. Even in a medium sized rails app,
       | you likely have some awareness of what every dependency is. It's
       | just impossible with npm.
       | 
       | This makes it easier for someone to inject their package into the
       | ecosystem whether it's actually very useful of not (like the
       | colors package).
       | 
       | One thought I've had to "reboot" the npm culture is to somehow
       | curate packages that are proven to have minimal and safe
       | dependencies, probably through manual review. Maybe it could be
       | recursive, so that safe projects only rely on other safe
       | projects.
        
         | davman wrote:
         | I think curated packages is part of what https://tidelift.com/
         | is trying to achieve.
        
         | [deleted]
        
         | dgb23 wrote:
         | Can we determine whether the package/LoC ratio actually has the
         | quality impact that people think it has? Or
         | dependencies/project?
         | 
         | Because I don't think those are useful metrics at all. In fact
         | this is just code re-use and should probably be happening to a
         | significant degree.
         | 
         | Some actually interesting properties are:
         | 
         | - basic performance
         | 
         | - signal to noise ratio
         | 
         | - API stability
         | 
         | - robustness
         | 
         | - clarity and simplicity...
         | 
         | And more of that. But just saying "a lot of dependencies bad"
         | is not useful.
         | 
         | Maybe in an ideal world we have even _more_ dependencies? I
         | don't know. What I do know is that I care about the above
         | points much more.
        
           | vlunkr wrote:
           | > And more of that. But just saying "a lot of dependencies
           | bad" is not useful.
           | 
           | I disagree. The primary problem is that proper dependency
           | auditing is incredibly time-consuming, especially if you want
           | to stay up-to-date. The reality is that most people simply
           | don't do it all.
        
         | adolph wrote:
         | In the below graph, create-react-app has 66 package
         | dependencies and 88 links between packages. The purpose of the
         | graph isn't to disagree with the statement of "800 directories"
         | but to illustrate that part of the problem is depth. The
         | maximum depth (in my hand count) is seven.
         | 
         | https://npm.anvaka.com/#/view/2d/create-react-app
        
         | Zababa wrote:
         | > That absolutely dwarfs the number of any other language I've
         | used.
         | 
         | Rust has this flaw too. Last time I wanted to compile a simple
         | project using actix-web and probably a database library, I had
         | 200 crates to compile. In both cases I think it's due partially
         | to preferring small packages/crates.
        
           | zozbot234 wrote:
           | It's ultimately Rust's reliance on generic code that forces
           | us to deal with so many packages at build time. In idiomatic
           | C/C++ much of the reusable code you compile ultimately turns
           | into reusable shared objects, that sit in binary form on your
           | fs with their corresponding include files. This is only
           | possible in rare cases with Rust, because the "library"
           | equivalents (crates) don't know how their generic types and
           | code will be instantiated downstream. Everything is the
           | equivalent of a "header only" lib.
        
           | geodel wrote:
           | Yeah, in language design they might be on cutting edge. But
           | in software engineering they want to be close to 90's, 00's
           | cultish software design patterns, DRY principle and so on.
        
           | deaddabe wrote:
           | Note that this separation is necessary if you want to achieve
           | somewhat parallel compilation in Rust. Every proc_macro needs
           | its own separate crate, so I end up providing two crates for
           | macro-based stuff. At least, the stdlib seems to have enough
           | batteries included for the dependency count to not be as high
           | as NodeJS projects.
           | 
           | From my experience I would rate the count of dependencies in
           | this order:
           | 
           | NodeJS > Rust > Python > C/C++
           | 
           | Though I cannot explain why this is ordered this way between
           | {Node, Rust, Python}. Again, batteries included? Language
           | popularity? Beginners-friendly programming language?
        
             | Zababa wrote:
             | I would argue that it's ordered by a mix between
             | popularity, need for those packages and ease of installing
             | packages. JS and Python are around the same order of
             | magnitude of popularity, installing packages is way easier
             | in JS than in Python (NPM might not be great but pip is
             | hell). Rust is less popular but it's very easy to have lots
             | of packages, plus what you mentioned about procedural
             | macros. And the standard library, like JS, is relatively
             | small. If you add Go to that (relatively easy to add
             | package, less popular than Python and JS but more than
             | Rust, lots of stuff in the standard library) which would be
             | higher than C/C++ and lower than Python, it does seem to
             | fit.
             | 
             | I don't know anything about the C#/Java ecosystems, same
             | with Ruby and Perl. I'd like to know if they fit this
             | "model" too, that would be interesting. It could give some
             | pointers on how to design/make a language evolve to avoid
             | having lots of packages.
             | 
             | I'm not sure about the standard library of Rust being
             | bigger. Node ships with an HTTP module, random number
             | generation and regexes. Rust has neither of those. Maybe
             | more functions in each modules?
        
             | josephg wrote:
             | > I cannot explain why this is ordered this way between
             | {Node, Rust, Python}. Again, batteries included?
             | 
             | My vote is that its cultural. Both for "batteries included"
             | python, and for nodejs.
             | 
             | Its easy to forget now, but npm was extremely innovative
             | when it first came out. It absolutely went out of its way
             | to make adding dependencies as easy as possible. And the
             | culture (especially in the early days of nodejs) went
             | bananas for this philosophy of programming. I had a chat
             | with @isaacs on a bus one time. (He was the maintainer of
             | nodejs at the time). I asked him what he thought about
             | package documentation, and what to do when a README isn't
             | enough. He said that he thinks if a readme isn't enough
             | documentation for your library, your library is probably
             | too big and should be split up.
             | 
             | You still see this today with packages like "isobject" (a
             | tiny function published as a package) which still gets 53M
             | downloads / week[1].
             | 
             | Correct me if I'm wrong, but as I understand it python and
             | ruby still don't support parallel dependencies with
             | different versions in the build tree like npm does. If a
             | python or ruby package transitively depends on foo@1.0 and
             | foo@2.0 then my understanding is that ruby and python lose
             | their minds. And this problem is almost impossible for the
             | end user to solve. So libraries like rails sort of need to
             | be designed as one big block of software.
             | 
             | Nodejs has no problem with this - if you do this in nodejs,
             | npm will just quietly install both versions and node will
             | happily wire everything up correctly. The only constraint
             | is that no single package can have a _direct_ dependency on
             | both foo@1 and foo@2 at the same time. But thats not
             | something that you ever really want to do in practice.
             | 
             | The tooling supported this too. It was quite common to have
             | a library with some optional features that not many people
             | used. If you wanted to exclude them from your javascript
             | bundle, until recently the main way to do that was by
             | breaking your library up into small pieces so your
             | library's consumer could pick and choose what they wanted.
             | 
             | Building a dependency tree out of with thousands of tiny
             | modules is exactly what nodejs is designed for. It should
             | come as no surprise that thats what we got.
             | 
             | [1] https://www.npmjs.com/package/isobject
        
               | zozbot234 wrote:
               | Mentioned in a sibling comment already, but Rust has
               | these features as well, and I think Go ended up getting
               | them too although there was some controversy about the
               | need for them.
        
             | zozbot234 wrote:
             | AIUI, rustc now has ad-hoc parallelization of compile
             | within a single crate. The defined compilation unit is
             | still the crate though, as opposed to the single file in
             | C/C++.
        
         | zozbot234 wrote:
         | Package curation exists in other languages, too. C++ has Boost,
         | and Haskell has its Haskell Platform. It helps avoid the
         | pitfalls of languages with large standard libraries (where
         | stability guarantees make "batteries included" turn into
         | obsolete and bitrotting "dead batteries").
        
           | regularfry wrote:
           | This is an idea that every ecosystem eventually realises it
           | needs. Once you've got enough versions of enough libraries
           | that A and B both need C but at different versions, you start
           | to need curation, although that need might not become
           | pressing enough to do anything about for a while. But once
           | you've got curation, cryptographically trusting that curation
           | becomes viable in a way that cryptographic trust of the
           | original packages often isn't.
           | 
           | Putting a layer of "distributions" over language ecosystems,
           | in the same way that "distributions" solve the problem of
           | putting enough mutually-compatible library versions together
           | to get Linux to work, is, I think, inevitable.
        
             | derefr wrote:
             | > Once you've got enough versions of enough libraries that
             | A and B both need C but at different versions, you start to
             | need curation
             | 
             | Node specifically doesn't have this constraint, though, as
             | A's C and B's C can be loaded independently into the same
             | VM, each one hermetic and only visible to its parent. (This
             | is probably half of why Node's ecosystem became the way it
             | did, now that I think about it; every other ecosystem hits
             | increasing numbers of constraint-resolution conflict
             | problems as dependency hierarchy depth increases, and so
             | limits itself in hierarchy depth to avoid this.)
        
               | zozbot234 wrote:
               | > Node specifically doesn't have this constraint, though,
               | as A's C and B's C can be loaded independently into the
               | same VM, each one hermetic and only visible to its
               | parent.
               | 
               | Rust can do this as well, though most version variation
               | is handled via semver rules wrt. compatibility. I think
               | this exact requirement led to some controversy in the Go
               | community at some point? Though they should now have a
               | module system that allows for this?
        
               | regularfry wrote:
               | It's less of a problem, but it bites as soon as you need
               | to pass a data structure from one C to the other. That's
               | less frequent, but does happen.
        
             | ljm wrote:
             | Another aspect I think is, how much open source dependency
             | do you actually _need_ for your project. You can probably
             | get by with fewer packages than you expect, which also
             | makes it more feasible to run a curated package system.
             | 
             | For example, you can't submit any old Haskell library you
             | put together to Hackage, or you couldn't at the last time I
             | checked. It had to meet certain minimum standards to be
             | considered.
             | 
             | I honestly don't know where you would start in terms of
             | curating NPM, precisely because of the dependency on
             | dependencies. You'd end up curating half of the ecosystem.
        
               | zozbot234 wrote:
               | > For example, you can't submit any old Haskell library
               | you put together to Hackage
               | 
               | You can, but you need to figure out what your library is
               | doing and translate it to category theory.
        
           | vlunkr wrote:
           | Cool, I'm glad there's precedent for it. I'm not opposed to
           | adding guardrails as the post suggests, but it feels like a
           | band-aid on much deeper problem.
        
           | dimitrios1 wrote:
           | Then you have languages like Go, where it feels like the norm
           | is relying on the standard lib, and _any_ dependency really
           | must be providing value to justify it.
           | 
           | My node projects typically have thousands of dependencies, my
           | java and C++ projects have hundreds. My go projects typically
           | have 8-12, and have never once exceeded 40.
           | 
           | Not to stan the Go language itself, I just wish this
           | philosophy was the prevalent one in languages.
        
         | ragnese wrote:
         | > The reason this keeps happening with NPM is because of absurd
         | number of dependencies in the average node app.
         | 
         | But why does _that_ happen? There are now lots of languages
         | that make it trivial to add dependencies. While I find projects
         | in those other languages to _also_ have too many dependencies,
         | it 's no where near what happens in JS apps. I'm thinking of
         | projects I've recently worked on in Rust, PHP, and Java. Java
         | projects seem to be a distant second-place to JavaScript
         | projects when it comes to willy-nilly dependencies.
         | 
         | It's not a rhetorical question: Why is the culture with JS so
         | much worse about this?
         | 
         | I absolutely hate that I'm going to suggest this, but is it
         | just because of the average skill level and experience of
         | people working on JS projects?
         | 
         | Or is it because JS is such a bug-prone programming language
         | that we're all afraid of actually authoring any more code than
         | we absolutely have to, because we know we'll waste hours
         | debugging things that should be relatively simple?
         | 
         | I honestly don't know.
        
           | CamouflagedKiwi wrote:
           | The process of uploading packages onto Maven Central is...
           | not modern. npm (arguably?) makes this a lot easier than any
           | other language, therefore npm developers do more of it.
        
           | yoz-y wrote:
           | I think it's the sheer amount of programmers using JS. It's
           | also a very approachable language, so it makes it easy to
           | learn the language before learning good practices.
        
           | dschooh wrote:
           | This may not be the whole story, but one of the reasons is
           | that JavaScript does not have a standard library. Corollaries
           | are: several different module systems, application frameworks
           | and bundlers exist.
        
             | lucideer wrote:
             | Node does have a standard library.
             | 
             | I've seen this same weird line parroted by so many people
             | in the comments here: it's very odd.
             | 
             | If you're talking about server & browser having different
             | APIs that's equivalent to any other language having
             | different platform apis.
        
             | vlunkr wrote:
             | I think that's a big part. The standard library isn't
             | great, and progress is tied to browsers. Additionally, tiny
             | packages became the norm early on. Is even, is odd, is
             | negative zero, left pad, etc.
             | 
             | I don't know all the reasons for that, but I think part of
             | it is that developers create them in order to put it on
             | their resume. "My package is downloaded 500k times a week"
        
           | warent wrote:
           | Can't find it now but I remember an interview or article by
           | Ryan Dahl, describing his original vision of Node.js. He
           | related it to childhood enjoyment of building and piecing
           | things together, and used tinker toys as an analogy saying he
           | wanted code to be the same.
           | 
           | In other words it has been an intentional design decision
           | from the start.
        
           | majidazimi wrote:
           | Simply because blast radius for Java is limited to a set of
           | very high quality libraries -- in terms of code not
           | functionality. These libraries come from Apache Foundation,
           | Eclipse Foundation, Google, Facebook, Spring, etc. Literally
           | every single Java application depends on something from
           | Apache [ok I understand stuff like Log4Shell can still
           | happen].
           | 
           | The same is not true for JS. The most mature libraries depend
           | on absurdly vague libraries that no one has ever reviewed.
        
             | ragnese wrote:
             | I was going to ask the obvious question of why the Java
             | ecosystem ended up differently than the JavaScript
             | ecosystem, but I think I know the answer.
             | 
             | It's a giant pain in the ass to publish a Java library.
             | That's already weeding out a ton of low-effort projects. By
             | itself, I wouldn't exactly call that a good thing, but it
             | seems to have a silver lining...
        
           | codecurve wrote:
           | My take is that the lack of experience for the average
           | JavaScript developer is absolutely a factor here. I don't
           | think it's the only factor though. Here are some of the other
           | pieces of the puzzle.
           | 
           | JavaScript's standard library is so thin on the ground that
           | there's already a culture of "reaching for a library" to
           | accomplish tasks that many languages do out of the box.
           | 
           | The monoculture is wide enough that the language caters to
           | lots of paradigms and schools of thought. If there's one
           | library that uses classes and method chaining, you can be
           | sure that another will pop-up to re-implement the same
           | functionality in a pure functional style. One will focus on
           | type safety and another will abuse the dynamic bits of the
           | language to make the code you write as terse as possible.
           | 
           | Amount of code shipped has always been a more important
           | metric for JS than other languages because the nature of the
           | web means that users have to wait whilst the source code is
           | downloaded before your page becomes interactive (for a huge
           | class of applications). This encourages developers to favour
           | smaller libraries that solve for narrower problem domains.
           | 
           | It's become very trendy to write a smaller, faster, better,
           | smarter version of existing libraries. The JavaScript
           | community loves the process of picking a catchy name,
           | registering a domain, designing a logo, and publishing
           | packages as though they were businesses. This creates an
           | abundance of packages that look great on paper, but with no
           | users, patchy/non-existent tests and maintainers that haven't
           | ever used the code in a professional context.
           | 
           | Finally, I think JavaScript is a deceptively simple language.
           | It doesn't take very long before people (mistakenly) think
           | they're close to mastering the language. By comparison,
           | contributing to an open source project in a meaningful way is
           | quite difficult, so these developers assume that other
           | libraries must be written badly if they find it hard to
           | contribute. Then they write their own, because they believe
           | they can do a better job.
           | 
           | The ecosystem as a whole sees a lot of innovation, and pays
           | for that with a lot of churn and a lot of dependencies. From
           | a theoretical standpoint, it's a fascinating corner of modern
           | programming. In a professional context, it horrifies me and I
           | wish I could sanely cut npm out of the chain.
        
           | Vinnl wrote:
           | One potentially contributing factor is that npm makes it very
           | easy to avoid conflicts with duplicate dependencies, i.e. if
           | one dependency has a transitive version on some other
           | package, and another dependency _also_ has a transitive
           | dependency on that package, but on an incompatible version,
           | that 's not a problem in npm: it'll just store both on your
           | disk. That might remove some pressure to minimise the number
           | of dependencies on library authors.
           | 
           | And of course, there are just far more people working on it,
           | I believe.
        
             | zkldi wrote:
             | This reduces package-installing friction, but this is a
             | good thing anyway -- there's no reason why you shouldn't be
             | able to use foobar@1.0 and foobar@2.0 in the same project.
             | For all intents and purposes they're different packages,
             | viz. the version is just as important as the package name.
        
               | Vinnl wrote:
               | Yeah to be clear, I wasn't trying to say it was a bad
               | feature, just that this might be one of the negative
               | sides part of its trade-off.
        
           | chefandy wrote:
           | I've mostly worked with JS in the browser and my
           | understanding of node lacks nuance, but it seems like all of
           | this would be mitigated drastically by building out the
           | standard library. For comparison, here's pythons list of
           | standard modules:
           | 
           | https://docs.python.org/3/library/
           | 
           | They don't make third party libraries obsolete-- for example,
           | I tried using the built-in IMAP library the other day and
           | it's definitely too low-level to make sense for most quick
           | projects that check email, so I used a third party Library--
           | but all of those modules are vetted, stable, and require no
           | external dependencies.
           | 
           | I believe the node maintainers have staunchly opposed such
           | measures. I don't know what their reasoning is so I don't
           | have an opinion on whether or not it's worth it.
        
             | sobjornstad wrote:
             | I'm also pretty new to JS but totally agree. Half my Python
             | projects have zero dependencies (except for development
             | tools which don't get packaged with the app) because
             | everything I want is already in the standard library.
             | 
             | It feels like a quarter of the time I spend working on
             | projects that use npm is spent debugging my toolchain
             | because of excessive complexity or weird problems in random
             | dependencies. Doesn't seem like it should be too much to
             | ask that I can spend most of my development time working on
             | actual code.
        
           | stank345 wrote:
           | Part of it is that JS's standard library is terrible (eg. no
           | built-in union/intersection when working with sets, bizarre
           | default sorting behavior see
           | (https://developer.mozilla.org/en-
           | US/docs/Web/JavaScript/Refe...), groupBy (see
           | https://developer.mozilla.org/en-
           | US/docs/Web/JavaScript/Refe...) is new and not well-
           | supported, etc). You almost have to reach for some utility
           | library or build your own ad-hoc one just to use the
           | language.
        
             | protonfish wrote:
             | I don't see that as the case. Modern JS can do a lot out of
             | the box but the culture looks down on "vanilla" JavaScript.
             | I've seen way too many libraries that are nothing but a
             | thin wrapper around native functionality. When your first
             | (and only) technique is to look for a library, this is
             | where you end up.
        
               | stank345 wrote:
               | I agree and am very pro vanilla JS FWIW. I just find
               | myself reaching for something like lodash's
               | `intersection`/`difference` functions when working with
               | sets, `sortBy` to get more normal (and not in-place)
               | sorting behavior, and `groupBy` to do group by.
        
             | zkldi wrote:
             | > Bizarre default sorting behaviour
             | 
             | You are meant to pass an argument to [].sort; If you don't,
             | it falls back to the most generic thing that makes sense --
             | which is turning everything into a string and sorting
             | lexicographically.
             | 
             | Arrays in JS can have any type in them, such as [number,
             | string, function]. That's obviously very unlikely, but the
             | only thing in common between all types in JS is that they
             | can all be explicitly turned into strings.
             | 
             | And yes, I agree that throwing an error here for no
             | argument would be better here (a linter WILL enforce this),
             | but this is hardly a critical shortcoming of JS's standard
             | library, and you DEFINITELY do not need a utility library
             | just to use the language (especially for your examples).
        
               | stank345 wrote:
               | I hear what you're saying, but it still seems pretty
               | bonkers to me that if you try to sort an array of numbers
               | it will cast them to strings and sort alphabetically (!):
               | > [1, 2, 10, 3].sort()       [ 1, 10, 2, 3 ]
               | 
               | > And yes, I agree that throwing an error here for no
               | argument would be better here (a linter WILL enforce
               | this), but this is hardly a critical shortcoming of JS's
               | standard library
               | 
               | I suppose "critical" is debatable but this seems very
               | fundamental and very unexpected to me.
               | 
               | > you DEFINITELY do not need a utility library just to
               | use the language (especially for your examples)
               | 
               | I hadn't actually considered using a linter to avoid
               | these types of standard library footguns... that's
               | actually a pretty great idea!
        
               | hombre_fatal wrote:
               | Since arrays can contain a mix of anything, I don't think
               | it's that bonkers that the default impl chose to
               | canonicalize values into consistent comparables with
               | String(). It's just not useful for numbers.
               | 
               | But for example, they probably decided that it was more
               | useful defaulting to having a sort order for things that
               | otherwise aren't comparable:                   a =
               | [undefined, null, undefined, null]         a.sort()
               | // [ null, null, undefined, undefined ]
               | 
               | Since null < undefined and undefined < null are both
               | false, then a simple `a < b` comparator wouldn't sort
               | them at all. Same for objects.
               | 
               | If you have an array that's a huge mix of random values,
               | from null to undefined to [] to {} and you sort() it, all
               | of those values will now be grouped together by type.
               | 
               | This doesn't seem like a bonkers trade-off.
        
               | zkldi wrote:
               | There would be a significant performance impact if
               | `.sort()` argumentless had to traverse the list and
               | checked all inputs were numbers. It's better to pass a
               | sort function -- that is how the API is meant to be used.
               | 
               | I agree that without an argument it should just be a
               | fatal error, but if it were to have any sort of
               | functionality, it should convert all to strings.
               | 
               | This is consistent among JS's apis, and pretty much the
               | origin of all the 'js wut' moments on the internet.
               | Everything in JS can be converted into a string. If you
               | do something stupid with disparate types, it will likely
               | turn operands into strings and compare them that way.
        
             | yardstick wrote:
             | > You almost have to reach for some utility library or
             | build your own ad-hoc one just to use the language.
             | 
             | Sure. But are hundreds of dependencies really required for
             | this?
             | 
             | In Java you would use tools like Guava or Spring for
             | general quality of life improvements, and there would be a
             | few deps for them (under a dozen iirc).
             | 
             | The solution is for the "top tier" libraries and frameworks
             | in the JS world to be designed with minimal dependencies.
             | And where they do have a need for a dependency, they
             | undertake serious consideration of the best option that
             | minimises dependency hell.
        
               | stank345 wrote:
               | Yeah, there's definitely a separate cultural/inexperience
               | problem at play as well.
        
               | ragnese wrote:
               | And those "top tier" libraries do exist for some stuff-
               | especially if we're talking about the anemic standard
               | library. The famous lodash library doesn't have any (non-
               | lodash-umbrella) dependencies AFAIK.
        
           | dolni wrote:
           | What I think is happening: the JS ecosystem has been flooded
           | by developers with minimal experience and/or education.
           | 
           | They learn engineering principles like "don't repeat
           | yourself" and take that to mean installing an entire
           | dependency to implement left pad is a good idea.
           | 
           | You are probably on to something with JavaScript being bug-
           | prone as a factor in that.
           | 
           | JS as an ecosystem has a really big problem with developers
           | not knowing the value of simplicity.
        
             | ugjka wrote:
             | You can't have simplicity when every other week someone is
             | shoving a new framework down your throat. And in fact you
             | are made look like a looser if you dare do things in
             | vanilla JS
        
               | dolni wrote:
               | This is a big problem in the tech industry, in general.
               | Some weeks back I read a comment that described some
               | behavior as "high intelligence, low wisdom". I believe
               | that fits pretty well here.
               | 
               | People design new frameworks (presumably) because they
               | see an array of problems with existing frameworks. In
               | designing their new framework, they try to address the
               | shortcomings that the existing frameworks have.
               | 
               | What they don't realize is that the problems in existing
               | frameworks were _known tradeoffs_. Now, instead of the
               | One True Perfect Framework, we have yet another framework
               | with its own set of problems.
               | 
               | People think that every problem is solvable
               | simultaneously, but that's simply not true. You can make
               | tradeoffs. And this isn't just true in engineering, it's
               | true in life generally.
               | 
               | Some tradeoffs make sense nearly always. Others only make
               | sense in certain contexts.
               | 
               | An example here is the tradeoff between simplicity and
               | high availability. It doesn't matter what you do -- the
               | simplest high availability configuration for an app will
               | ALWAYS be more complex than the simplest non-HA
               | configuration. You're making a trade here. It's a trade
               | that is absolutely sensible, but it's a trade
               | nonetheless.
               | 
               | The lesson to be learned here is: stop thinking you can
               | solve every problem at once. You can't.
        
           | nirvdrum wrote:
           | I think the reason is JS doesn't have much of a standard
           | library. Java, C#, Ruby, Python, Rust, Go, and many others
           | come with a large library you can use to write non-trivial
           | applications without ever needing to fetch an external
           | dependency. JS, particularly outside of Node, doesn't have
           | that. To get functionality most other languages/runtimes
           | include out of the box, you need to write a bunch of code
           | yourself or pull in a dependency to use someone else's
           | implementation.
        
             | ragnese wrote:
             | It may be more about the quality rather than the quantity,
             | though. Rust's standard library isn't very big. People
             | sometimes complain that we have to fetch dependencies that
             | are so ubiquitous that they are effectively part of
             | standard Rust: crates like rand, futures, bytes, etc.
             | 
             | But even JS's built in string stuff isn't so bad that it
             | somehow justifies leftpad existing, so I don't know...
        
           | [deleted]
        
           | jerf wrote:
           | The interaction between a language and its culture is really
           | complicated, partially because it's an ongoing iterative
           | process, and thus chaotic, in both the English and
           | mathematical senses of the term.
           | 
           | Not having a standard library made people accept needing
           | libraries for even very small things that in most other
           | languages developers would make at least _some_ effort for
           | using the standard library before reaching for something
           | else.
           | 
           | I think another aspect is that the initial leadership of a
           | language community sets the tone for a long time, but JS in a
           | lot of ways didn't have that, not through any fault of any
           | particular person but simply because Node grew so explosively
           | at the beginning that the usage growth dominated the
           | available leadership growth, and so there was a lot of very
           | wild, woolly growth that got written into the earliest
           | culture. This creates something a lot like a "seed crystal"
           | that has outsized impacts on future paths for a long time.
           | 
           | Finally, I do think it is definitely an issue that JS is
           | somewhere where you get a lot of people who are not
           | "programmers" per se and they are making big decisions about
           | code bases and libraries. They're young in the art. And while
           | there's nothing _intrinsically_ bad about that, people have
           | to start somewhere, there 's a lot of ways in which it's good
           | that JS is relatively easy to get into, etc., it is also
           | absolutely true that at scale, as the language and the
           | libraries iterate on each other and seek out their stable
           | points, that's going to affect the landscape. This is an
           | "is", not an "ought". It is what it is. JS has also continued
           | explosive growth, so even as someone who got into JS and
           | programming for the first time in 2017 is now a 5-year
           | "senior" (a crack about our industry terminology, not the dev
           | here) developer who has learned and might be inclined to do
           | things differently than they did 5 years ago, there's another
           | 2.5 newbies "voting" in the community as well.
           | 
           | It's a hard problem, I salute the leaders in the JS and npm
           | world working on it, I wis them the best and advocate people
           | giving them grace working in a very hard situation. But I'm
           | also glad not to be part of it.
        
       | phendrenad2 wrote:
       | Of course this is a problem for all package repositories. Ruby,
       | Python, Docker, Node, etc. But Node seems especially bad. Why? It
       | seems to me that Node packages are mostly the work of a single
       | person, and lack a community around them. You know you can trust
       | Nokogiri because there are many developers working on it, and
       | they're visible and accessible on Twitter et al. But many Node
       | modules are just one person's passion project and/or resume
       | padder. It's too easy for someone to have their npm publish keys
       | stolen and not notice, whereas if there were more people on the
       | project someone would probably notice sooner.
        
         | zozbot234 wrote:
         | Many C libraries are maintained by a single person, too. But
         | C/C++ programming has no equivalent to the Node culture of
         | making trivial releases, so every single update to a C package
         | gets quite a bit of attention from its users.
        
       | bjpbakker wrote:
       | A capabilities system like pledge could be a way to safer use
       | _existing_ packages. However, I think that it's not a very nice
       | way to continue. Every application will end up doing its own
       | capability pledging, and mistakes will be made. A lot.
       | 
       | Another approach could be to use an effect system like PureScript
       | does. The main problem with Node.js packages is that any function
       | you use can execute arbitrary code (such as wiping systems with
       | an IP that is from the Russian region). Having an effect system
       | in place the library author has no other means than to come
       | forward with the side-effect, or code won't compile.
        
       | filleokus wrote:
       | Could a path forward also be to unify the most depended upon
       | small packages into one large dependency, managed by some
       | trustworthy entity?
       | 
       | I guess some plug-in to npm could handle the resolution-mapping
       | between the (9 line) strip-ansi package and the node-standard-
       | library package?
       | 
       | Of course this don't solve all problem, but if a create-react
       | style app could lower its number of dependencies by 80% or
       | something, it would be easier to keep track of the remaining.
       | 
       | Because even if you have some kind of capability system, you are
       | still pretty vulnerable to miss behaving packages. Even if the
       | scope of badness is dramatically lower, chaos would ensue in many
       | build pipelines if some of these "core" package just started
       | throwing exceptions / returning empty objects
        
         | a9h74j wrote:
         | ActiveState does something comparable for Python, Perl and TCL.
         | 
         | https://www.activestate.com/
        
         | hulitu wrote:
         | The tendency is to make for every function a library. Just look
         | at lib directory in the Slackware install tree. The
         | functionality (of the distribution) has not changed too much
         | but the number of libraries needed is astonishing.
        
       | mikece wrote:
       | "The fundamental problem with npm is that any package you install
       | has full access to do whatever it wants on your computer."
       | 
       | Is it uncommon to use dedicated VMs for development for this very
       | reason?
        
         | mschuster91 wrote:
         | These days, for performance reasons most of the old
         | "development VMs" (=Vagrant or worse) got replaced by Docker
         | containers - orders of magnitude less effort.
         | 
         | And in any case a dedicated VM is not going to protect you
         | against attacks on your network, unless you go the full route
         | of using a VPN to provide internet connectivity to the VM, and
         | let's be honest almost no developer is going to do that simply
         | because how much effort and maintenance it requires.
        
         | progx wrote:
         | Linux solves a part of the problem, it has user accounts with
         | restricted access.
         | 
         | Create a dev account and run node within that account.
         | 
         | This solves the full access problem, but not possible backdoors
         | or leaks of your app.
        
           | bogwog wrote:
           | And an even more convenient and more safe option is to use
           | rootless containers via podman or
           | [toolbx](https://containertoolbx.org/)
        
         | saurik wrote:
         | > Is it uncommon to use dedicated VMs for development for this
         | very reason?
         | 
         | ... or on the production deployment's computer (which
         | presumably is also "your" computer, and has a similar set of
         | problems). (... or, if you go there, in production inside a VM,
         | but in the same context and with access to everything from and
         | all the memory and capabilities as the rest of the code you
         | wrote, such as access to networking and the database or
         | arbitrary CPU utilization.)
        
         | XorNot wrote:
         | It's hard to make it practical without linking a whole lot of
         | your local environment in. Remember it's death by a thousand
         | cuts - every time you need some new thing, you just add it
         | without considering the consequences too much. Probably lots of
         | people doing this with their whole home directory linked in
         | read/write.
         | 
         | Recently I got a little concerned about this and made myself a
         | basic safety harness with the bubblewrap[1] tool: rather then
         | going all out, I just lock the mount namespace to readonly for
         | everything except the directory I execute it in. Which is at
         | least some protection against system mods or wide-spread home
         | directory destruction.
         | 
         | What's a lot more of a problem is trying to protect truly vital
         | files - i.e. SSH keys and the like - which are also things
         | you're likely to have bound into your VM anyway. selinux is a
         | much better solution there (but so hard to administer as to be
         | almost useless, though I do really like Fedora's default scopes
         | and have used them successfully).
         | 
         | [1] https://github.com/containers/bubblewrap
        
         | kklisura wrote:
         | Are we bringing back mainframes, yet?
        
         | skybrian wrote:
         | I haven't actually done development within a VM. How practical
         | is it? What's a good way to get started? (In this case, on a
         | Mac.)
        
           | mikece wrote:
           | When I was still a consultant I spun up new VMs for each
           | client engagement -- both macOS and Windows -- using
           | Parallels. When the engagement was over I would dump that to
           | an external HDD. It meant over-spending on my MacBook Pro to
           | have the SSD and RAM space to support multiple VMs but there
           | was never any question of client work corrupting my system
           | (or vice versa) or needing to wipe and reload my machine
           | after a client engagement.
        
           | [deleted]
        
           | nukemaster wrote:
           | You just do everything over ssh. It's one of the reasons
           | everyone still like CLI/VT tools so much.
        
           | mhoad wrote:
           | Codespaces is great in my experience. VSCode devcontainers is
           | another very similar approach that will keep it on your
           | machine but at least containerize the development environment
           | raising the bar fairly significantly for a rogue package to
           | do serious damage on your machine.
        
       | feross wrote:
       | Founder of Socket (https://socket.dev) here, a new tool built by
       | npm maintainers to help solve JavaScript supply chain security.
       | 
       | I totally agree with the idea that we should assume all open
       | source packages may be malicious. Socket.dev uses "deep package
       | inspection" to characterize the behavior of an open source
       | package. By actually analyzing the package code, Socket can
       | detect when packages use security-relevant platform capabilities,
       | such as the network, filesystem, or shell.
       | 
       | For instance, to detect if a package uses the network, Socket
       | looks at whether fetch(), or Node's net, dgram, dns, http or
       | https modules are used within the package or any of its
       | dependencies.
       | 
       | This entails running static analysis (and soon, dynamic analysis)
       | on a package - and all of its dependencies - to look for specific
       | risk markers.
       | 
       | In this way, Socket can detect the tell-tale signs of a supply
       | chain attack, including the introduction of install scripts,
       | obfuscated code, high entropy strings, or usage of privileged
       | APIs such as shell, filesystem, eval(), and environment
       | variables.
       | 
       | We are taking an entirely new approach to one of the hardest
       | problems in security in a stagnant part of the industry that has
       | historically been obsessed with just reporting on known
       | vulnerabilities.
        
         | vbezhenar wrote:
         | What prevents malicious person to craft their code until it
         | evades your analysis? It's the same with antiviruses. They're
         | not that useful because adversaries adapt their viruses to pass
         | antivirus heuristics. And, as viruses show, you can make your
         | heuristics whatever complex, someone smart will find a way
         | around. Especially in that wild JavaScript environment.
        
           | feross wrote:
           | This is a fair question. The answer is that most malware
           | behaves in ways that are deterministically detectable. For
           | example, 93% of malware uses install scripts, which must be
           | declared in the package.json file and are not possible to
           | hide from our analysis.
           | 
           | From recent research:
           | 
           | > We found 93.9% (3,412) of malicious packages had at least
           | one install scripts, indicating that malicious attackers use
           | install scripts frequently [1]
           | 
           | When malware authors adapt and start doing fancy dynamic
           | stuff, we might not be able to figure out exactly what
           | they're doing, but we can detect the usage of obfuscated
           | code, dynamic requires, and other signals of compromise.
           | 
           | [1]: https://arxiv.org/pdf/2112.10165.pdf
        
         | caseyross wrote:
         | This, in my opinion, is the right answer for the problem
         | identified in the parent blogpost. Rather than trying to get
         | every single package author to adopt some unified capability
         | token scheme in their code, just statically analyze all
         | dependencies from the outside and report the capabilities they
         | actually use.
         | 
         | It would be even better if something like this could be
         | integrated directly into the package management tool itself, so
         | that you could run `npm update` and get back "New dangerous API
         | usage in package X version a.b.c: filesystem access. Type
         | package name to acknowledge and upgrade."
        
           | feross wrote:
           | Thanks. Glad you like our approach.
           | 
           | > It would be even better if something like this could be
           | integrated directly into the package management tool itself
           | 
           | We're planning to build this. However right now, the primary
           | way to consume Socket.dev data is through our GitHub app
           | (https://socket.dev/integrations).
        
       | lucideer wrote:
       | TL;DR: There's nothing fundamentally less worthy of trust about
       | node's supply chain than any other popular mainstream language
       | ecosystem. They've all got some badness.
       | 
       | ---
       | 
       | Here's a trope I'm tired of:
       | 
       | Take X general problem that affects a wide range of systems,
       | attribute it to one narrow system Y. Usually because that system
       | Y's general accessibility and success leads to a higher number of
       | high profile incidents related to problem X.
       | 
       | One of the practical outcomes of this trope is people trying to
       | solve this problem in narrow, ecosystem-specific, non-portable
       | ways.
        
         | danShumway wrote:
         | > One of the practical outcomes of this trope is people trying
         | to solve this problem in narrow, ecosystem-specific, non-
         | portable ways.
         | 
         | In general I agree, but I disagree that conversations about
         | dependency scopes are ecosystem-specific. Frankly, figuring out
         | how to limit the capabilities of imports is a discussion that
         | literally every language with a package manager should be
         | having right now.
         | 
         | Sure, the implementation is always going to be
         | platform/language-specific, but this should be a serious
         | consideration that people have any time that they're designing
         | a new package system, we should expect new package managers to
         | have an answer about how they scope dependencies and handle
         | dependency permissions.
        
         | zozbot234 wrote:
         | Other popular mainstream languages don't need a "leftpad"
         | package in the first place.
        
           | ferdowsi wrote:
           | Node has a more robust standard library than Rust, which
           | forces devs to download third party libraries to compute a
           | regular expression or generate a SHA-256.
           | 
           | For now Node is a richer target due to its popularity but the
           | same issues will hit any language ecosystem that suffer the
           | same flaws should they become popular.
        
             | jhugo wrote:
             | "Third-party" isn't really correct for regex:
             | https://github.com/rust-lang/regex (note the org)
             | 
             | Rust's stdlib is deliberately small in order to allow it to
             | have very strong stability guarantees.
        
           | onion2k wrote:
           | JS didn't _need_ it either. A dev made the library, shared
           | it, and some developers decided to use it. That 's on them.
           | Nothing about JS made it necessary.
        
             | hombre_fatal wrote:
             | Also, here's how to publish a package:
             | mkdir my-package         npm init         npm publish
        
               | colejohnson66 wrote:
               | Same with Rust. Any language with a package manager has
               | this issue.                   cargo new my-crate
               | cargo login ...         cargo publish
        
         | dahfizz wrote:
         | The issue is especially bad, and needs to be especially called
         | out, for npm because of the insane dependency bloat.
         | 
         | You can write fully featured and useful python apps with just
         | the standard packages that come with python. In JS you need a
         | handful of third party packages just to tell if a number is
         | odd.
         | 
         | A difference in magnitude becomes a difference in kind.
        
           | pfraze wrote:
           | I think you have to appreciate the history with node to know
           | why there are so many packages. Node's growth coincided with
           | GitHub's and node's community really adopted the "social
           | programming" trope. You could really make a name for yourself
           | with a popular node module. Javascript had more limitations
           | to work around at the time, and then the desire to reuse code
           | in node and the browser created even more need to abstract
           | common tasks. The "modularize everything" philosophy resulted
           | and it became a kind of game to make as many modules as you
           | could think to make; after all, isn't code sharing the joy of
           | the FOSS revolution?
           | 
           | That era has since peaked and declined. Now I see people make
           | way fewer modules because of the difficult of managing them
           | all. There's much less cred to earn from a node package.
           | People who did gain social capital from modules are now stuck
           | as maintainers, gaining very little additional value -- thus
           | more conversation about paying maintainers with monetary
           | capital, along with abandoned or ownership-transferred code.
           | And, of course, we're now suffering from the security issues.
           | 
           | It's still an incredibly valuable corpus of modules, but it's
           | post-bubble. It wasn't just "js programmers are too novice to
           | know better." It was people having fun, playing the social
           | game, trying silly ideas, and chasing a meme-wisdom of
           | programming (modules = good).
        
           | bavell wrote:
           | JS has a weak stdlib so it's definitely more common to need
           | to pull in some deps vs python. Of course, nothing is forcing
           | you to install silly packages that are wrappers for one-
           | liners.
           | 
           | > In JS you need a handful of third party packages just to
           | tell if a number is odd.
           | 
           | I have no doubt some clueless interns have done this but
           | there is no _need_ to self-inflict this kind of unnecessary
           | pain.
        
             | rndgermandude wrote:
             | >Of course, nothing is forcing you to install silly
             | packages that are wrappers for one-liners.
             | 
             | That's the thing... I am effectively forced to install a
             | lot of silly packages, because I need some not-so-silly
             | packages, and these in turn pull in all the silly packages
             | as dependencies of their own (a few levels down the chain).
             | 
             | I never installed leftpad (or any package like that)
             | myself, and yet, at one point it was present in basically
             | every node project I ever did, because of indirect
             | dependencies.
             | 
             | While this kind of dependency bloat could happen in any
             | language ecosystem, in node/npm it is from my experience by
             | far the worst. I think it's because the javascript and node
             | standard libraries were/are so very limited combined with
             | npm making it too easy to publish and consume packages, and
             | being early enough in the game so supply chain attacks
             | weren't yet on most people's mind.
             | 
             | I think, aside from node package maintainers being too
             | nonchalant about pulling in basically silly dependencies,
             | it's also a matter of a lot of package maintainers being
             | very laissez-faire when it comes to maintaining the cruft
             | and doing the tedious work of removing dependencies that
             | are no-longer needed.
             | 
             | An example of that - because it bugs me every time I see
             | this show up in my logs, package lock or node_modules - is
             | the isarray package. It's another one-liner, Array.isArray
             | is part of JS since a long time (even IE 9 supports it and
             | IE 9 was EOL in 2016) and the isarray package will just use
             | it when present (i.e. virtually everywhere), and the author
             | recommends to just use the built-in Array.isArray, and yet
             | it's still omnipresent with almost 63 million weekly
             | downloads, 858 direct dependents on npm (with countless
             | other indirect dependents, and dependents not published and
             | therefore not tracked on npm). And the number of weekly
             | downloads still goes up week-by-week, month-by-month.
        
           | lucideer wrote:
           | Node has horrific dependency bloat, but this is a strawman
           | here because the right response is not "let's assume the
           | dependency bloat is inherent/justified and come up with
           | specific security mitigations" but rather "let's ask
           | ourselves: WHY does Node have such terrible dependency
           | bloat?"
           | 
           | I've no idea why, but I've some personal theories:
           | 
           | Similar to how PHP has historically been associated with bad
           | code, not all of which can realistically be attributed to the
           | spellings of its API identifiers, I think whenever you have a
           | system that solves the ease-of-use/developer-accessibility
           | problem _well_ , you will end up with the standard of
           | contributor to that system being less skilled, since it's
           | easier for less experienced people to start using it. You see
           | this throughout the NPM package ecosystem: packages developed
           | by very inexperienced engineers being relied upon by big
           | popular projects.
           | 
           | This is ultimately a "good problem". You strive to make your
           | tools easy to use, and when you succeed, you end up with more
           | people using them badly.
           | 
           | You can argue that tools should be both easy to use and also
           | foolproof, but that's utopian. Let's work towards that but
           | not expect it as a baseline.
        
           | capableweb wrote:
           | > In JS you need a handful of third party packages just to
           | tell if a number is odd
           | 
           | Maybe it's hyperbole, but you definitely don't "need" any
           | third-party package to tell if a number is odd (`i % 2` does
           | the trick). That there exists a package for it doesn't mean
           | that the majority of users actually use it.
           | 
           | The standard library for JS is pretty small in general, but I
           | don't think hyperbole is the right way of getting your point
           | across, as I agree with you in general.
        
             | dahfizz wrote:
             | is-odd gets between 400k and 500k downloads _every week_
             | [1]. Maybe you don't need it, but lots of JS developers
             | have decided that they do. is-odd also depends on is-
             | number, and is-even depends on is-odd.
             | 
             | I agree that these packages are trivially implemented by a
             | first party instead of imported, but ~half a million JS
             | developers every week choose to import it instead. _That is
             | the problem_.
             | 
             | [1] https://www.npmjs.com/package/is-odd
        
               | dvlsg wrote:
               | I'm fairly certain those download numbers include CI runs
               | redownloading the not-cached package over and over,
               | likely via a transitive dependency. I don't think it's
               | fair to say approximately half a million unique JS
               | developers are choosing to import it every week.
        
               | imtringued wrote:
               | That's the damn problem with npm.
               | 
               | This library is responsible for 50% of all downloads to
               | is-even which calls is-odd.
               | https://www.npmjs.com/package/handlebars-helpers
               | 
               | Who cares? https://github.com/helpers/handlebars-
               | helpers/issues/315
        
               | lucideer wrote:
               | These numbers aren't too hard to rack up for well-known
               | packages (even the meme ones like this). e.g. is-odd is a
               | transitive dependency of stuff like handlebars-helpers
               | which gets a lot of downloads and will pull in is-odd
               | automatically.
        
           | Hamuko wrote:
           | > _In JS you need a handful of third party packages just to
           | tell if a number is odd._
           | 
           | You really don't, but for some reason JS developers have
           | decided that this is the optimal way to check if a number is
           | odd.
        
             | bavell wrote:
             | > You really don't, but for some reason JS _interns_ have
             | decided that this is the optimal way to check if a number
             | is odd.
             | 
             | FTFY :)
        
               | Hamuko wrote:
               | is-odd gets about two million downloads a month. is-
               | number gets 278 million downloads a month.
               | 
               | I guess the interns have been very busy.
        
               | bryanrasmussen wrote:
               | is-odd package.json contains
               | 
               | "dependencies": { "is-number": "^6.0.0" },
        
               | pfraze wrote:
               | Those numbers are a little deceiving. It's likely that
               | those modules are upstream of a popular module or two.
               | It's not like they are installed by projects directly.
        
               | hombre_fatal wrote:
               | It's only directly depended on by 40 or so npm modules,
               | and most of them look like beginner/joke modules like
               | "is-ten-thousand" where they brag about being featured in
               | a "worst npm libraries" article.
               | 
               | Quickly in these sorts of conversations, we're really
               | just making fun of beginners for using dumb packages.
               | With the popularity of Node, it makes sense to me that
               | you can have beginners searching npm/google for how to
               | tell if a number is odd, and for whatever reason they
               | find is-odd or whatever.
               | 
               | And perhaps the idea of even collecting packages to do
               | simple things is something fun for them. I remember
               | having a weird maximalist attitude when I was a beginner
               | using Rails. I'd install a Ruby gem and I could use it
               | anywhere in my files without even importing it, usually a
               | single line of abstraction. For some reason that appealed
               | to me even though I could have done it myself. I think I
               | had this idea that people writing libraries were doing
               | things right, and I was right for tapping into them.
               | 
               | I think we should save our denigration for the serious,
               | popular projects that use packages rather than the fact
               | that beginners use them. Like, this sort of package has
               | no business being a transitive dep of Express (it isn't)
               | and popular Express middleware, and mainly because x-deps
               | are security issues.
        
               | pfraze wrote:
               | Very good points, and I think you're right on the numbers
               | here! I went dependent spelunking - almost half of it is
               | because is-even is a dependent (naturally) but then I
               | didn't notice any dependents of is-even to explain it. I
               | guess people do just google it and grab the module.
               | 
               | Mea culpa for the unsubstantiated assertion before.
        
               | lucideer wrote:
               | handlebars-helpers is one of the big dependents afaik.
        
             | marcosdumay wrote:
             | If I remember correctly, that library was a joke. That
             | people immediately started exhorting as "the right way to
             | do it" as a joke. That due to Poe's law other people
             | immediately understood as the right way to do it...
        
             | zkldi wrote:
             | This is absolutely not true, and I'm tired of seeing this.
             | 
             | is-odd, alongside a bunch of other microdependencies are
             | almost _all_ the work of one person, who made as many
             | micropackages as possible and then PRd them into other more
             | popular libraries. There are _not_ 6 million people
             | directly downloading `is-odd` a day. At all.
             | 
             | When this person could make one library to do something
             | (like an ANSI-Colouring package), they would fractalise it
             | into as many dependencies as possible, because that boosts
             | their download count on NPM. I should note that this is
             | just one person who has managed to nestle their way into
             | some larger projects. I apologise for the spam, but this
             | point really needs hammering home:
             | 
             | https://github.com/jonschlinkert/ansi-black
             | 
             | https://github.com/jonschlinkert/ansi-reset
             | 
             | https://github.com/jonschlinkert/ansi-bold
             | 
             | https://github.com/jonschlinkert/ansi-dim
             | 
             | https://github.com/jonschlinkert/ansi-italic
             | 
             | https://github.com/jonschlinkert/ansi-underline
             | 
             | https://github.com/jonschlinkert/ansi-inverse
             | 
             | https://github.com/jonschlinkert/ansi-hidden
             | 
             | https://github.com/jonschlinkert/ansi-strikethrough
             | 
             | https://github.com/jonschlinkert/ansi-black
             | 
             | https://github.com/jonschlinkert/ansi-red
             | 
             | https://github.com/jonschlinkert/ansi-green
             | 
             | https://github.com/jonschlinkert/ansi-yellow
             | 
             | https://github.com/jonschlinkert/ansi-blue
             | 
             | https://github.com/jonschlinkert/ansi-magenta
             | 
             | https://github.com/jonschlinkert/ansi-cyan
             | 
             | https://github.com/jonschlinkert/ansi-white
             | 
             | https://github.com/jonschlinkert/ansi-gray
             | 
             | https://github.com/jonschlinkert/ansi-grey
             | 
             | https://github.com/jonschlinkert/ansi-bgblack
             | 
             | https://github.com/jonschlinkert/ansi-bgred
             | 
             | https://github.com/jonschlinkert/ansi-bggreen
             | 
             | https://github.com/jonschlinkert/ansi-bgyellow
             | 
             | https://github.com/jonschlinkert/ansi-bgblue
             | 
             | https://github.com/jonschlinkert/ansi-bgmagenta
             | 
             | https://github.com/jonschlinkert/ansi-bgcyan
             | 
             | https://github.com/jonschlinkert/ansi-bgwhite
        
               | dahfizz wrote:
               | I think you're missing the point. These packages are
               | stupid and too small, _but they get millions of downloads
               | a month_. That 's the problem with the JS community.
               | Instead of rejecting a dependency for being silly, JS
               | devs will happily import them to save two lines of code.
        
               | lucideer wrote:
               | No. The JS community won't happily import stupid packages
               | to save two lines of code, any more than devs in any
               | other community. A very small minority of devs will do
               | this, and publish their larger packages. Those millions
               | of downloads a month are _transitive_.
        
               | infamouscow wrote:
               | This is the same kind of defense C programmers make
               | regarding their beloved footguns.
               | 
               | That might be true in aggregate, but the exceptions are
               | exceptionally bad, impact a lot of people, and then fools
               | try to rationalize the footgun on HN.
        
               | paulhodge wrote:
               | The question is, are we seeing that bad behavior because
               | there is something inherent about the platform that
               | encourages it, or are we seeing more with Node just
               | because that community is enormous.
               | 
               | Because people love to bring up is-even as a reason why
               | Node & NPM suck. What exactly did NPM do to create the
               | is-even situation? (other than making it super easy to
               | publish). What should they do differently?
        
             | kybernetikos wrote:
             | Can you cite anyone who actually thinks this?
             | 
             | People complain about these tiny packages they find on npm
             | the whole time, but usually these packages come from people
             | learning how to create their first npm packages, or
             | creating or following tutorials. They aren't serious
             | packages used by typical developers for production apps.
             | 
             | If you go to the isEven github repository you can even see
             | "I created this in 2014, when I was learning how to
             | program." If you hover over his 'organisation' you'll see
             | the text "This is a joke. You'll only see this org if you
             | are attempting to troll me about repositories I created
             | when I was learning to program."
        
               | dahfizz wrote:
               | is-odd gets between 400k and 500k downloads every
               | week[1].
               | 
               | The package was written as an exercise in learning how to
               | create a small, useless package. But a huge portion of
               | the JS development community choosed to import and use
               | the package anyway.
               | 
               | [1] https://www.npmjs.com/package/is-odd
        
               | kybernetikos wrote:
               | Have a look at where those downloads are coming from and
               | which packages are actually using it:
               | https://www.npmjs.com/browse/depended/is-even
               | 
               | They're jokes or satire or learning repositories, like
               | `install-is`: "Installing this package installs a bunch
               | of useless packages" or Stalinsort or module-practice-
               | january. The most serious looking dependents are by the
               | same author.
        
               | imtringued wrote:
               | You aren't looking hard enough. The joke libraries don't
               | get any downloads. install-is gets 1 weekly download.
               | 
               | https://news.ycombinator.com/item?id=30991487
        
               | kybernetikos wrote:
               | handlebars-helpers falls under my last sentence. The
               | linked thread is literally with the guy who wrote is-odd.
        
           | fbrchps wrote:
           | > In JS you need a handful of third party packages just to
           | tell if a number is odd
           | 
           | But you absolutely don't.
           | 
           | I get that you're taking a worst-case example, but it also
           | stands that if Node developers would actually take some time
           | to write stuff themselves -- the leftpad situation was
           | absolutely stupid, because `.padStart()` exists -- then the
           | situation wouldn't be nearly as bad.
           | 
           | At my workplace, if you can write the functionality in
           | (depending on the scale) a day to a week, then you're not
           | allowed to use a 3rd party package for it. You can _look_ at
           | what other people have done, but doing it in-house leads to
           | less work overall in the future.
        
             | eropple wrote:
             | _> the leftpad situation was absolutely stupid, because
             | `.padStart()` exists_
             | 
             | String.prototype.padStart hit Chrome in January 2017 and
             | Firefox in June 2016. `left-pad` was published in March
             | 2014.
             | 
             | A lot of these small packages that seem ridiculous now
             | addressed (sometimes poorly!) missing aspects of the
             | library specification for pretty good reasons. The JS
             | specification has improved a lot over time--I just chuck
             | `ESNext` into the library tsconfig.json for every Node
             | project--but there is a lot of historical baggage with
             | which this kind of dismissal doesn't adequately come to
             | grips.
        
             | Zababa wrote:
             | .padStart() was added precisely because of the leftpad
             | situation.
        
         | [deleted]
        
         | mikece wrote:
         | Is the trope inaccurate in this case? If so, how? What would a
         | path toward solution look like?
        
           | lucideer wrote:
           | The post has two things to communicate:
           | 
           | 1. The Node supply chain has serious problems when it comes
           | to security: this is true
           | 
           | 2. The strong implication (throughout the article but most
           | prominently in the title) that these problems are either
           | unique to Node or at least worse in Node than elsewhere. This
           | is definitely not true.
           | 
           | ---
           | 
           | The solutions cited in the article are good, and definitely
           | benefit the Node ecosystem: --ignore-scripts is a well known
           | option that would be great on-by-default but tends to be
           | enforced in large corp CICD currently. The capabilities model
           | is not too different to what Deno do (this article's idea is
           | somewhat more fine grained but much more complex which could
           | hamper adoption or lead to misuse).
           | 
           | Overall both would be of somewhat limited, but still very
           | worthwhile benefit.
           | 
           | The _hard_ problem imo is solving more generally for supply
           | chain trust. We implemented --ignore-scripts enforcement last
           | year and have only really see it have a relatively small
           | impact, despite it being relatively new as an idea in the
           | past few years (which motivates more exploits due to its
           | novelty). In reality, most supply chain attacks are either
           | targeting prod envs, or integrated build dependencies, rather
           | than piggybacking CICD repo install steps: the later attack
           | gives you the same env access and has no package-manager-
           | specific mitigation, so it needs a more general solution.
           | Capabilities also have limited use outside install scripts
           | because limiting them also limits the  "benign" functionality
           | of your normal application code.
        
         | josephg wrote:
         | Hi! Author here. I write a lot more Rust than Javascript these
         | days, and I agree - this is absolutely a problem I'd love to
         | eventually see addressed in the rust ecosystem as well.
         | (Although dependency madness hasn't kicked in anywhere near as
         | much in the rust ecosystem).
         | 
         | But rust and javascript each have unique challenges. Per-
         | library sandboxing is hard in javascript because of the
         | language's dynamism. Its hard in rust because any code you pull
         | in can always drop down to unsafe, and in unsafe land you can
         | really do whatever you want. We could probably selectively ban
         | eval() in modern server-side javascript pretty easily because
         | its just not used that much in modern code. But we can't ban
         | unsafe in rust because its sprinkled everywhere.
         | 
         | > One of the practical outcomes of this trope is people trying
         | to solve this problem in narrow, ecosystem-specific, non-
         | portable ways.
         | 
         | I think trying to solve this in every language at the same time
         | would be an exercise in boiling the ocean. There's no need to
         | implement something like this in every language all at once.
         | Much better to start somewhere, experiment, and hopefully if
         | the solution works well we can see how it might apply in other
         | languages.
        
           | jakelazaroff wrote:
           | _> Its hard in rust because any code you pull in can always
           | drop down to unsafe, and in unsafe land you can really do
           | whatever you want._
           | 
           | Not super familiar with Rust, so this may be a silly
           | question: could Rust mitigate this by forbidding third-party
           | packages to use unsafe code, except for ones you specifically
           | allowlist? e.g. your Cargo.toml might look like this
           | [dependencies]         trusted = { version = "1.0.0", unsafe
           | = true }         untrusted = "1.0.0"
           | 
           | The trusted library would be allowed to use unsafe code, but
           | the untrusted one would not.
        
           | jhugo wrote:
           | Talking about `unsafe` in this context seems like changing
           | the discussion completely.
           | 
           | All of the concerning things done in the JS supply-chain
           | attacks can be done perfectly well in safe Rust. You don't
           | need `unsafe` to exfiltrate secrets or to encrypt or delete
           | files.
           | 
           | Auditing the use of unsafe in dependencies is worthwhile for
           | mostly unrelated reasons; it's not the same thing as
           | sandboxing them, auditing them for malicious code, or
           | assessing how much you trust them.
        
             | josephg wrote:
             | Right; I'm skipping ahead and imagining an alternate
             | universe standard library for rust with capability support.
             | Like, what if we did the thing I proposed in this blog
             | post, but did it to rust instead of javascript? How would
             | that work?
             | 
             | In that world, all the privileged operations (filesystem,
             | OS, etc) in std would require an extra capability object to
             | be passed at runtime. The capability would grant permission
             | to the caller to perform the privileged action (like
             | writing to a file).
             | 
             | That would stop rogue rust crates from making syscalls that
             | they shouldn't be making. There might also be a way to
             | enforce those permissions at compile time instead of at
             | runtime.
             | 
             | But even if we were willing to do that, it wouldn't matter
             | if any library could still use unsafe blocks. The reason is
             | that there's dozens of ways to make syscalls from unsafe
             | blocks without needing to go through std. For example, you
             | could dynamically execute methods in glibc / musl, use an
             | asm! block, or (probably) manually compile a function into
             | a byte array, transmuting it into a function pointer and
             | then execute it.
             | 
             | If you changed std, and also banned 3rd party libraries
             | code from using unsafe you could probably make the system
             | secure. But I'm worried that the rust community might not
             | be willing to pay that cost for extra security.
        
               | jhugo wrote:
               | > If you changed std, and also banned 3rd party libraries
               | code from using unsafe you could probably make the system
               | secure. But I'm worried that the rust community might not
               | be willing to pay that cost for extra security.
               | 
               | Anyone will be willing to pay a cost when the benefit is
               | well-defined and exceeds that cost. "Make the system
               | secure" doesn't have a well-defined meaning, because
               | security isn't binary like that.
               | 
               | Banning `unsafe` from all non-std deps is pretty hard for
               | most real-world Rust software unless std grows a lot
               | larger. Even apart from domains with a lot of necessary
               | unsafe, like embedded, you have crates like `bytes` or
               | `tokio` that use unsafe for performance reasons.
               | 
               | A realistic discussion about this stuff always has to
               | involve trust. I don't have much issue with tokio
               | containing unsafe, given its track record and the quality
               | of the code. OTOH, I don't allow actix in my dependency
               | trees because its history of using unsafe unnecessarily
               | and unsoundly means that I don't trust it.
               | 
               | To me, a more interesting approach for improving supply
               | chain security in software builds on the issue of trust
               | rather than things like technical capabilities. Rust has
               | some interesting work in this area already, like cargo-
               | crev for distributed code review, cargo-deny for applying
               | rules to your dependency tree, cargo-geiger for seeing
               | which dependencies are using unsafe, etc.
        
               | ryukafalz wrote:
               | > I'm skipping ahead and imagining an alternate universe
               | standard library for rust with capability support.
               | 
               | FWIW there is https://github.com/bytecodealliance/cap-std
               | 
               | (...but yes it's more limited than what you're thinking
               | of)
        
               | zozbot234 wrote:
               | > There might also be a way to enforce those permissions
               | at compile time instead of at runtime.
               | 
               | AIUI, a compile time capability is just a custom unit
               | type, perhaps using PhantomData to depend at compile time
               | on some generic type or consteval parameter. Then
               | ordinary type checking is enough to ensure that this gets
               | "threaded" correctly throughout the code, as required.
               | 'Narrowing' a capability is just a one-way type
               | conversion, e.g. via .into(). Since you're doing this via
               | a unit type that carries no information, everything
               | should disappear at runtime, with no effects on the ABI.
               | You'd effectively be using the type checker to prove
               | things about what your code is allowed to do.
               | 
               | (It turns out that there's a (2021) article expanding on
               | this: https://www.hardmo.de/article/2021-03-14-zst-proof-
               | types.md )
        
           | zozbot234 wrote:
           | > Its hard in rust because any code you pull in can always
           | drop down to unsafe, and in unsafe land you can really do
           | whatever you want
           | 
           | There is a quasi-standard "cargo geiger" tool that can manage
           | unsafety requirements in Rust imported crates. (Of course,
           | this should really be an officially provided feature in the
           | first place.)
        
           | lucideer wrote:
           | > _I think trying to solve this in every language at the same
           | time would be an exercise in boiling the ocean._
           | 
           | I actually think the opposite is true.
           | 
           | Capabilities are _great_ but limited in their applicability
           | within actual application code. Ultimately, your main
           | application that may require some  "dangerous" APIs to do
           | something benign, may be loading 3rd party code to wrap those
           | APIs, and we need a way to trust that code. That's a general
           | problem, and - while it is a very hard one to solve - coming
           | up with novel and unique solutions in every ecosystem is
           | going to reinvent a lot more wheels (& boil some seas at
           | least) compared to looking at software composition
           | holistically.
        
             | josephg wrote:
             | Thats a really interesting perspective. I'd love to read
             | more about that if you're willing to flesh it out more. Up
             | for writing a blog post response?
        
         | marcosdumay wrote:
         | Of course node is less trustworthy than other platforms. It it
         | wasn't, we would see active exploits on other platforms as
         | well, but they are either incredibly rare or non-existent.
         | 
         | But you are right in that it's not a difference of kind. Except
         | for the inane idea of executing random code on library
         | installation, node doesn't have any kind of vulnerability that
         | isn't shared with every package manager out there. The
         | important differences are social on the community, and of
         | exposure area, because JS programs tend to rely on 1 or 2
         | orders of magnitude more developers than other languages.
        
           | lucideer wrote:
           | 100%
           | 
           | You've summarised it much more succinctly than I could.
           | Fundamentally it's about misattributing the cause of
           | untrustworthiness.
        
         | goodpoint wrote:
         | Distributions like Debian are very trustworthy especially when
         | compared to NPM.
        
           | codedokode wrote:
           | Sadly, in Debian every package which gets installed, can run
           | a script with root privileges so this is even more dangerous.
           | Although most packages don't need such privileges.
        
             | goodpoint wrote:
             | Nonsense. Package managers need to be able to run scripts
             | as root to do the installation. And yet, in the last 25 or
             | 30 years there's never been a case of a malicious
             | contributor successfully inserting a backdoor in the
             | installation script of any package in any major
             | distribution.
             | 
             | Because there is a vetting process, nothing else.
             | 
             | [And yes, of course, it would be possible to sandbox each
             | package installation to access some very specific paths but
             | so far it's really unnecessary]
        
           | lucideer wrote:
           | Debian has ~20,000-30,000 packages total. NPM has well over a
           | million. Contribution frequency and overall contributor
           | numbers are also much much higher.
           | 
           | NPM is a victim of ease of use and popularity. It's a bigger
           | target.
           | 
           | But both systems would benefit from a holistic approach to
           | supply chain security.
        
             | tonyedgecombe wrote:
             | Part of the problem there is JS doesn't have much of a
             | standard library so everybody and their dog tries to fill
             | that hole.
        
         | hinkley wrote:
         | > They've all got some badness.
         | 
         | They're all based on the same design, as far as I can see.
         | 
         | Is there another architecture we could be using?
        
         | mschuster91 wrote:
         | The proposed system itself should be fairly portable and in its
         | idea reminds me of SELinux. The problem is SELinux knowledge
         | isn't widespread, it's Linux-only (so no support for OS X and
         | Windows which are the majority of JS developer machines), and
         | especially it operates on a syscall level - which is perfectly
         | fine to limit filesystem access (=for all nodejs processes,
         | limit file I/O to the parent directory of node_modules?), but
         | only barely unusable to sandbox network access.
         | 
         | Therefore it makes the most sense to define a standard for
         | configuring capabilities/permission whitelists/grant
         | requests/(however else you want to name it) and leave the
         | implementation up to the platform/language.
        
         | nukemaster wrote:
         | I'd disagree:
         | 
         | The way modern Linux distros work is that a number of
         | volunteers who pay attention to what's going on upstream
         | package software for users and developers. When something gets
         | weird these volunteers change their behavior and prevent the
         | users from being harmed (the recent Audacity mess is a great
         | example of this.) I don't think people pushing eg
         | cargo/node/snap appreciate how safe this has made their OS and
         | languages that rely on distro package managers (such as C.)
         | 
         | You get the safety that you might expect from an app store
         | without actually restricting anyone's freedom. It's very much
         | like a church with elders preventing people from being captured
         | by vices and whatnot. Yes it has issues but it works shockingly
         | well, much better than many of the alternatives.
        
           | rlpb wrote:
           | Also, most distros have the concept of stable releases. This
           | provides a very valuable "focus point". It means that we
           | don't just have to rely on the maintainers. Users can review
           | packages for reasonable behavior too, and this isn't made
           | futile by constantly changing packages. Users and maintainers
           | can focus on just the stable release and at a reasonable
           | cadence, and this focus point being the same for all users
           | means that it has value to everyone else, too.
        
           | danShumway wrote:
           | The problem is that as you get deeper into Linux, you become
           | progressively more and more likely to install your own
           | packages from source, and then all of that curation goes out
           | the window.
           | 
           | I'm not 100% convinced that the number of volunteers for
           | package managers like Arch are actually sufficient to catch
           | malware even in its current form; I think they get a lot of
           | benefit out of desktop Linux being a relatively low-value
           | target. But I'm really not convinced that their approach
           | would be scalable if they actually had to scale at the level
           | of npm. From what I can tell, Arch only has in the
           | neighborhood of 13,000 packages[0], and it doesn't easily
           | allow installing arbitrary versions[1]. I have nothing but
           | praise for Linux, but none of the main distros have anything
           | close to the amount of developer activity that the npm
           | ecosystem has.
           | 
           | And that's when curation breaks down: Arch solves the problem
           | of not having a ton of packages in the main repos by allowing
           | multiple upstreams, by supplying AUR, and by compiling
           | packages from source. But once you drop down into AUR, it's a
           | lot more dangerous and would be a lot easier for people to
           | push malicious code. And if you're pulling Makefiles off of
           | Github, all of that goes out the window -- and there are
           | _good_ Linux software packages that encourage that behavior.
           | 
           | Not to mention the number of Linux software packages that
           | straight up just give you a shell script to run that
           | configures and installs the rest of the program (looking at
           | you, Calibre). If you're lucky and you're using Arch, then
           | you might be able to just install Calibre from the main repo.
           | But that's also kind of Arch-specific, on Debian systems it's
           | much more likely that you jump out of the main repos because
           | they're out of date and you want the most recent version; you
           | either start pulling from a dev-controlled upstream or you
           | start running the shell scripts to install that software.
           | 
           | ----
           | 
           | Don't get me wrong, I actually think that from a curation
           | perspective, the way Linux package managers work is the
           | _best_ available solution we have for human moderation for
           | software. A single large curated list that fits everyone 's
           | needs is impossible, it does not scale[2]. The only scalable
           | solution for curation is to have a lot of separate curated
           | lists that people can subscribe to; and then to recursively
           | have curated lists of lists.
           | 
           | However, curation is not a magical catch-all solution against
           | malware, particularly when you get AUR and source compilation
           | in the mix. Curated lists are _one layer_ of security, and
           | need to be combined with other sandboxing techniques, with
           | user education, and with (when possible) minimizing the
           | number of packages people need to install. It 's not as
           | simple as saying, "the volunteers won't let anything bad
           | happen" -- and I definitely wouldn't say that Linux package
           | security is a solved issue, I think a recognition of some of
           | the weaknesses of that model is part of the reason we're
           | seeing so much effort going into Flatpak[3].
           | 
           | ----
           | 
           | [0]: https://archlinux.org/packages/
           | 
           | [1]: Yes, you can roll back but it's not really something
           | that's advised to do for specific packages. Generally, your
           | system will run smoother if you keep everything up-to-date
           | and don't pin specific versions.
           | 
           | [2]: We've seen this with both iOS and Android, you either
           | make a limited list that doesn't meet everyone's needs, or
           | you have bad curation. Sometimes both. Splitting up lists
           | does a lot to help solve that problem.
           | 
           | [3]: Although in the spirit of having multiple curated lists,
           | I wish we'd start to see more popular upstreams than just
           | Flathub.
        
             | rlpb wrote:
             | > The problem is that as you get deeper into Linux, you
             | become progressively more and more likely to install your
             | own packages from source, and then all of that curation
             | goes out the window.
             | 
             | It needn't go out of the window. The key thing is to keep
             | the set of packages on which you deviate small. Then you
             | can curate the exceptions yourself, or a community can form
             | that share the same needs and they can do it.
             | 
             | It's when you do throw the curation out of the window, or
             | subscribe to an ecosystem that effectively requires it
             | [that curation be thrown out the window], that the problem
             | arises.
        
           | lucideer wrote:
           | This is ultimately about scale though. It's easier for Linux
           | because of the relative number of contributors to distro
           | repos.
           | 
           | Ubuntu has 10s of thousands of packages. NPM has well over a
           | million. The average update frequency is also much higher, as
           | is the number of contributors per-package.
        
             | infamia wrote:
             | > Ubuntu has 10s of thousands of packages. NPM has well
             | over a million. The average update frequency is also much
             | higher, as is the number of contributors per-package.
             | 
             | Node's culture of tiny libraries (partly caused by
             | Javascript's tiny standard lib) is a big part of the
             | problem and increases the number of potential supply chain
             | issues.
        
               | lucideer wrote:
               | Node's culture of tiny libraries is overstated. See
               | https://news.ycombinator.com/item?id=30989539
               | 
               | What standard libs are you comparing? Node's to what
               | other language? I've seen so many commenters say this,
               | but still not sure what the magical thing that can't be
               | achieved with Node built-ins is...
               | 
               | Developers don't write libs because you can't do it with
               | built-ins, the write libs because developers like to
               | write code and NPM is easy to use.
        
               | infamia wrote:
               | > Node's culture of tiny libraries is overstated. See
               | https://news.ycombinator.com/item?id=30989539
               | 
               | You need thousands of packages for a pretty standard
               | React application (which requires hundreds of base
               | packages). That's a cultural problem in Node's packaging
               | community that injects risk into the packaging ecosystem.
               | 
               | > What standard libs are you comparing? Node's to what
               | other language? I've seen so many commenters say this,
               | but still not sure what the magical thing that can't be
               | achieved with Node built-ins is...
               | 
               | Brandon Eich himself has said that JS has a purposefully
               | small standard library.
               | https://www.infoworld.com/article/3048833/brendan-eich-
               | javas...
               | 
               | JS' standard library is small compared with Python's for
               | example.
               | 
               | https://developer.mozilla.org/en-
               | US/docs/Web/JavaScript/Refe...
               | 
               | https://docs.python.org/3/library/
        
               | danShumway wrote:
               | It's not that Ubuntu has fewer packages because it's 30x
               | more efficient with how it packages software -- it has
               | fewer libraries because it has less software and less
               | developer attention. It's not at all uncommon for me in
               | Debian systems to have to search out non-distro
               | repositories to pull from. And that's even before we get
               | into the issue that Ubuntu/Debian repositories aren't
               | rolling release. I often find myself jumping outside of
               | the official Ubuntu repos even for software that they
               | provide, just because they're out of date; it's one of
               | the biggest reasons why I eventually moved to Arch.
               | 
               | Yes, JS dependency chains are out of control. No, that's
               | not the only reason why there are over a million packages
               | on NPM. No, the solution to the scalability problem of
               | human-curated package managers can't be, "well, we just
               | won't scale."
               | 
               | Adding a bigger standard library to JS would not be
               | enough to get rid of 970,000 npm packages.
        
           | horsawlarway wrote:
           | This is literally _EXACTLY_ how releases are supposed to work
           | for companies using any package manager out there.
           | 
           | A number of [employees] who pay attention to what's going on
           | upstream package software for users. When something gets
           | weird these [employees] change their behavior and prevent the
           | users from being harmed.
           | 
           | The problem is - much like a church with elders (and linux
           | distros - frankly) - quality varies dramatically.
           | 
           | Some of them prevent people from being captured by vices,
           | some of them diddle the kids.
           | 
           | Same here: Some companies take the appropriate steps to lock
           | down dependencies and only update after a thorough vetting.
           | Some pull the latest packages on every push to master.
        
       | systemvoltage wrote:
       | This is similar to electronics supply chain. If you source from
       | shady component distributors, you're going to get bitten with
       | something like a faulty capacitor with an annoying frequency in
       | volume. The difference here is that a single vulnerability can
       | take down your entire app or worse. It's like sourcing components
       | that can potentially do irreversible fire damage to your
       | customers.
       | 
       | This is why they rely on trust worthy distributors and
       | manufacturers. Without payment incentive and with high
       | expectations for free stuff, this is not possible to solve.
       | 
       | Billion dollar companies building on people's hobby projects. In
       | any other industry, this would be unprofessional.
        
         | zozbot234 wrote:
         | > This is similar to electronics supply chain.
         | 
         | Not a new idea. In fact, "Software IC's" as a notion goes back
         | to the late 1980s.
        
       | cosmiccatnap wrote:
       | Sort of a cake and eat it too mentality if you ask me. If you're
       | not willing to build your own JS glue then you should at least
       | take time to audit the glue you are pulling in from some
       | thankless developer who probably started this as a hobby project
       | in college and now it is the backbone of 10 fortune 500
       | frontends.
       | 
       | I'm sorry that they can then do whatever the hell they want with
       | that package but this ecosystem exists because companies want
       | free stuff but don't want to provide back into it.
        
       | porsager wrote:
       | This won't prevent packages you're already giving capabilities to
       | - to later do something evil in a small patch update.
       | 
       | So it's not enough - you should also not rely on "automatic
       | security updates" through some sort of semver trust. Lock your
       | dependencies up completely, choose packages with a small
       | dependency tree or zero dependencies, and at the same time use
       | the newly launched https://socket.dev to know what packages do.
       | Also reading through the source of your dependencies should be on
       | the list.
        
         | feross wrote:
         | Thanks for sharing Socket.dev! Totally agree that a key part of
         | any supply chain security strategy must be understanding what
         | packages actually do when they run.
         | 
         | For example, see the package `angular-calendar` which is a
         | calendar/date picker web component. When you look it up on
         | Socket.dev [1], you'll see that it actually uses:
         | 
         | - Install scripts
         | 
         | - Telemetry to track you
         | 
         | - Network access
         | 
         | - Shell access
         | 
         | - Environment variable access
         | 
         | - File system access
         | 
         | Which is waaay more capabilities than you'd expect. All of
         | these capabilities turn out to be caused by a single dependency
         | which implements telemetry to track the package usage a la
         | Google Analytics.
         | 
         | [1]: https://socket.dev/npm/package/angular-
         | calendar/issues/0.29....
        
           | porsager wrote:
           | Wow.. Yeah that's a great example of exposing what's actually
           | going on!
           | 
           | Btw, is there a specific reason you're not listing the
           | "yellow issues" from a packages dependencies on its front
           | page? For instance https://socket.dev/npm/package/mongoose
           | doesn't really show anything on the front page, but if you go
           | to "dependency issues" you get "uses network, eval etc.". I
           | think it'd be necessary to treat "dependency issues" the same
           | as the packages own issues.
        
             | feross wrote:
             | We're not happy with the noisiness of filesystem and
             | network issues, so we mark them as a bit lower priority for
             | the moment. The specific issue is that we currently only
             | detect when 'fs', 'net', etc. are required and not whether
             | they're actually used and which specific functions are
             | used.
             | 
             | We're working on improving our analysis and are close to
             | shipping a big update at which point we'll increase the
             | severity of these issues.
        
         | josephg wrote:
         | > This won't prevent packages you're already giving
         | capabilities to - to later do something evil in a small patch
         | update.
         | 
         | True - its not perfect! But the principle of least privilege
         | should help limit the blast radius. How many packages in npm
         | dependency tree need access to the filesystem? Or to the
         | network? I bet its a vanishingly small percentage of the
         | packages in the average nodejs project. Being vulnerable to
         | malicious code in 3 hand selected packages is much, much better
         | than being vulnerable to malicious code in _any_ of the
         | packages in your dependency tree.
         | 
         | And even then, we can be very specific about what those
         | packages have direct access to. Right now the situation is
         | "every package can read and write to any file on my computer".
         | The fine grained permissions I'm proposing in this post would
         | let us say "only package X can access the filesystem at all,
         | and when it does it only has access to this subdirectory".
         | 
         | Much better!
        
       | kerneloops wrote:
        
       | keb_ wrote:
       | The impression I get from the companies I've worked at is to not
       | trust packages that are mostly maintained by one person.
       | Ironically, those packages are usually the ones that don't have
       | outrageously large dependency trees and can usually be audited by
       | a developer on a weekend, and the more "trustworthy" larger
       | packages with hundreds of maintainers typically have monstrous
       | dependency trees.
       | 
       | Not saying 1-man tools are inherently better, but in my
       | experience, these tools seems to have a tighter focus and less
       | chances of scope/dependency creep.
        
         | dylan604 wrote:
         | >can usually be audited by a developer on a weekend
         | 
         | why are devs expected to do this on a weekend and not just as
         | part of the work week? are we coming from the perspective of
         | the dev working on a side project?
        
           | keb_ wrote:
           | I said "weekend" to communicate a short amount of time;
           | apologies, I don't think anyone should be doing unpaid labor.
        
             | seanw444 wrote:
             | I knew what you meant ;)
        
         | danShumway wrote:
         | It's really context dependent.
         | 
         | 1-person tools make it easier to audit the person involved, and
         | like you said in general I also find that those tools tend to
         | be smaller and have fewer dependencies, which makes a big
         | difference for security. Limited scope is good when looking at
         | packages/dependencies, and side-projects are kind of required
         | to have limited scope just by virtue of not having a ton of
         | resources.
         | 
         | However, 1-person tools also have less accountability and less
         | bandwidth to respond to emergencies, and (particularly if there
         | aren't a lot of eyes on them), you need to evaluate whether the
         | developer is qualified to build the package -- ie, are they
         | likely to inadvertently introduce a security vulnerability or
         | abandon the package if it needs security updates? People
         | (myself included) have an instinctive bias to assume that 3rd-
         | party code is written by people who know what they're doing.
         | Part of the evaluation process needs to be asking, "does this
         | person actually have the skill to do what they're trying to
         | do?"
         | 
         | I also try to look at how documented the project is -- in an
         | emergency, could I fork the project myself? If a project is
         | being run by only 1 person, then it's more likely that the
         | codebase isn't massive and that it wouldn't require a full team
         | to update. But it's also less likely to be well-documented or
         | extensively tested. Again, balancing act.
         | 
         | I'm not sure there's a single correct answer, I think it
         | depends a lot on the project and on what kinds of packages
         | you're looking at.
        
         | hinkley wrote:
         | Node has a peculiar place in the pantheon for me where there
         | are a few well known individuals who are maintaining a mountain
         | of code that is still mostly theirs even with external
         | contributions coming in.
         | 
         | TJ in particular, before he fucked off, and Sindre Sorhus, who
         | has a little kingdom of tools in the Unix philosophy but more
         | useful than leftpad.
         | 
         | I probably use code from others but those are the only two I am
         | keenly aware of.
        
           | keb_ wrote:
           | lukeed is another one I think; I've depended on his packages
           | for years, and have yet to be burnt by one.
        
             | hinkley wrote:
             | I've not heard of him but it looks like I use a few
             | libraries that use his stuff.
        
           | jitl wrote:
           | When I find an issue with one of the little packages Sorhus
           | publishes, I often need to traverse 3-5 different GitHub
           | repos to understand how the code works. Many times I find it
           | better to copy paste the "base" code into my repo once I find
           | it than continue to use the web of NPM packages.
        
         | the_biot wrote:
         | > The impression I get from the companies I've worked at is to
         | not trust packages that are mostly maintained by one person.
         | 
         | That is hardly the point. Regardless of what you think of the
         | solution presented, the author is utterly right in saying any
         | solution has to involve not trusting any packages at all. How
         | many people wrote the package is irrelevant.
        
         | btmcnellis wrote:
         | The big problem with one-person packages isn't so much security
         | as it is support. I have been burned more than once by old
         | applications where key features rely on random packages with
         | one maintainer who disappeared years ago. At least with a
         | group, you have options to keep things moving without having to
         | fork the library yourself.
         | 
         | (Of course the root cause here is arguably too much reliance on
         | third-party dependencies, but searchable dropdowns are _such_ a
         | pain to make on your own, and it's so tempting...)
         | 
         | The Sangria GraphQL library in Scala ran into a version of
         | this. The libraries were primarily maintained by one person,
         | who wrote the vast majority of the code and was the only person
         | with write privileges in the main repos. Sadly, he passed away
         | unexpectedly, and it took months (maybe a year or so) before
         | his colleagues and other contributors were able to get access
         | to the GitHub org.
        
           | ratww wrote:
           | Well, for what is worth, we have a lot of dependencies
           | maintained by Microsoft of all companies, with lots of
           | production-breaking bugs and they're not too interested in
           | fixing or letting us fix. Even getting fully-functional PRs
           | (with good test coverage and community support) looked at
           | takes a lot of work and time, let alone getting fixes after
           | reporting issues.
           | 
           | One of those packages is a JS package that is hosted by them,
           | so we can't even fork it and host ourselves.
           | 
           | On the other hand, with simple packages that get abandoned,
           | we just fork, publish ourselves with another name or
           | namespaced, and it's solved.
        
             | btmcnellis wrote:
             | Solo maintainer vs. organization is definitely an imperfect
             | heuristic for long-term support. But it's a decent
             | approximation for dependencies that are low ROI but
             | potentially high impact if they break, like a UI widget
             | that gets used everywhere in your app.
             | 
             | It's the problem with any third-party dependency (ask
             | anyone who's used certain Google products). But then if you
             | build everything in-house, a) it's expensive, and b) you
             | end up with homegrown frameworks written by somebody who
             | left the company five years ago and now everyone is afraid
             | to touch it.
             | 
             | The laws of software thermodynamics come for all of us.
             | Eventually, old systems decay, and you need to roll up your
             | sleeves and do the work to keep them going.
        
               | ratww wrote:
               | _> But it 's a decent approximation for dependencies that
               | are low ROI but potentially high impact if they break,
               | like a UI widget that gets used everywhere in your app._
               | 
               | Not really, it's not decent at all. What is a great
               | approximation, however, is the heuristic presented by the
               | grandparent poster: projects that are easy to audit, easy
               | to fork (if necessary) and don't have outrageously large
               | dependency trees. _Everything else_ is a liability.
        
         | Macha wrote:
         | Unfortunately, as recent incidents have shown, these many
         | party, otherwise reliable, projects often have dependency
         | chains that have these one person that can have a breakdown
         | projects as dependencies.
         | 
         | Would I ever include left-pad as a direct dependency? No. But
         | as we found out, the person who provided a library that was
         | used by react-dom might.
        
       | jamesfisher wrote:
       | I almost didn't read this because the title sounds like a
       | whingeing rant. But it turned out to be a carefully considered
       | proposed solution.
        
       | numbsafari wrote:
       | If, instead of loading library code directly into the same
       | process and heap, package code were instead loaded and run in
       | their own dedicated processes with their own dedicated memory and
       | you communicated via message passing (a la actors) then this
       | approach would really start to make sense.
       | 
       | But most node packages today are designed to run directly in the
       | same process and memory space as the code that is being imported.
       | 
       | Also, any package manager that runs arbitrary code should not be
       | trusted.
        
       | egeozcan wrote:
       | Why put a sensational headline instead of something that
       | describes what the post is really about?
       | 
       | Something like "A capabilities based system for node.js
       | packages", perhaps?
        
         | geniium wrote:
         | Click bait is the reason
        
           | josephg wrote:
           | Author here. I totally hear the criticism. I had
           | "Capabilities for nodejs packages" or something like it as a
           | draft title for the post. But its a boring title. And I
           | believe this article would have had far less reach with a
           | name like that.
           | 
           | I hear you. I don't believe the answer is to just
           | sensationalize everything to 110%. But I personally
           | absolutely can't stand boring writing. Like, holding my
           | attention on anything which isn't at least a bit human and
           | engaging feels like torture. My job is 70% researcher, and
           | one of the worst parts of my job is reading academic papers -
           | I just can't hold my attention on a boring paper for more
           | than a few sentences. My old supervisor used to tease me
           | about using 2 weeks in the lab to save 5 minutes in the
           | library. I think he's more right than he knows.
           | 
           | So, I really don't have a good answer. What feels like
           | sensationalist clickbait for you might seem like a catchy
           | title to me, with a promise of engaging writing for someone
           | else. And what sounds like an accurate title for someone else
           | might turn me off completely because of its blandness.
           | 
           | I suspect there's no middleground here where everyone is
           | happy, and at the end of the day when I write its up to me
           | and my judgement. And you, the audience to complain more
           | loudly if you feel like like I'm taking it too far. I need
           | that, because only have my own judgement for calibration. But
           | thats pretty unsatisfying!
           | 
           | Veritasium had a great video talking about this for his
           | youtube thumbnails. I found it quite thought provoking:
           | https://www.youtube.com/watch?v=S2xHZPH5Sng
        
       | jFriedensreich wrote:
       | there is already a project that does most of this: deno. there
       | are of course other motivations that differentiate it from nodejs
       | but i think this should be part of a new runtime that was build
       | with this in mind, deno is the way forward. Of course putting
       | your projects in appropriate jails/zones/containers/vms is also
       | an option, but that has always been true
        
       | havkom wrote:
       | I would suggest that one tries to limit dependancies. The entire
       | node ecosystem contraviene this so maybe people should not use
       | it.
        
       | roryrjb wrote:
       | I was exploring the actual implementation[0] of a capabilities
       | feature in Nodejs and was utilising seccomp (via libseccomp) on
       | Linux at least to achieve a greater degree of security than might
       | otherwise be possible by remaining in userland code. The idea is
       | that you'd write your code, import whatever you like and define
       | your capabilities upfront at initialisation. The problem is
       | there's quite a big disconnect between what you are doing in
       | JavaScript and what's happening with system calls in v8, libuv
       | and the other native parts that it's difficult to predict what
       | you need to block and what's actually going to happen. So I don't
       | think my approach is really viable in a general sense, although
       | capabilities in general I think would improve the situation if
       | the wider community were to adopt the approach.
       | 
       | [0]. https://github.com/roryrjb/node-seccomp
        
         | jgerrish wrote:
         | Cool.
         | 
         | OS level code, realistic discussion of issues and approaches.
         | 
         | Thanks for the contribution.
        
       | andrewmcwatters wrote:
       | No, Go solves this problem and Deno explicitly copies its design.
       | Just use Go. Seriously. The stupidest thing npm still does today
       | is naively assume SemVer is an appropriate strategy for automatic
       | updates.
       | 
       | There is so such thing as an appropriate strategy for automatic
       | updates for developers. For end users? Sure. Because the
       | assumption for them is that things are stable.
       | 
       | Developers need to check. Developers need to pin.
       | 
       | If you're working in the Node.js ecosystem the solution isn't to
       | throw the baby out with the bath water and use Deno, it's to
       | `--save-exact`.
       | 
       | I'm sure as hell not throwing away all of the hours invested in
       | now stable JavaScript software.
        
       | andix wrote:
       | We need one or a few well maintained standard library for Node
       | and/or Browser-JS.
       | 
       | Those must not depend on any other libraries. And then packages
       | can reduce their dependencies vastly, by just referencing one
       | standard library, that provides a lot of features.
        
         | tempest_ wrote:
         | JS's small standard lib is the cause of some of these issues.
         | 
         | When I look at one of the JS projects I have worked on there is
         | a non trivial amount of 'is-*' packages whose only job is to
         | identify the type of the object.
        
           | dolni wrote:
           | The small stdlib is part of it. The fact that identifying an
           | object's type is so difficult is another part of it. It's
           | also more foundational to JavaScript.
        
           | andix wrote:
           | Exactly. Somebody would need to put together a standard lib,
           | maybe even by repacking some already de-facto standard
           | libraries. But only one library per category (one ,,is"
           | library, one date library, ...). It's a very opinionated
           | task, but it could really help.
        
           | shagie wrote:
           | is-odd, is-even...
           | 
           | The radical code reuse where people don't ever want to write
           | a one liner that exists in some other package is at issue
           | too.
        
       | pojzon wrote:
       | Every time I read anything about JS, WAT comes to my mind and
       | that explains why everything in web UI domain would require a
       | complete bottom-up rewrite.
       | 
       | And ofcourse that is why it will never happen. We just keep on
       | digging deeper in mud same as with climate..
        
         | cryptonector wrote:
         | Gary Bernhardt's WAT video never fails to make me laugh.
        
       | jollybean wrote:
       | Systems not designed for security are hard to secure after the
       | fact.
        
       | donatj wrote:
       | I think Go modules version resolution opting for _lowest_ common
       | release rather than the standard _highest_ is a reasonable and
       | sane option though not a total solution. It prevents users of
       | your library from pulling unvetted versions of your dependencies
       | just by pulling your library alone.
        
         | jaitsu wrote:
         | https://go.dev/blog/supply-chain is a very good write-up on
         | this topic too.
        
       | didip wrote:
       | In the beginning of node.js life, NPM the company had a guerilla
       | marketing that says: The smaller the package, the better for
       | reusability.
       | 
       | That stained the node.js ecosystem forever, until now.
       | 
       | This is why I am excited about Deno, Ryan wants to break away
       | from this culture.
        
         | silon42 wrote:
         | IMO, npm is correct/better.
         | 
         | The solution is to 'vendor' everything and have package updates
         | as part of normal code review.
        
           | throw_m239339 wrote:
           | > IMO, npm is correct/better.
           | 
           | NPM was never correct. NPM as a business cared about growth.
           | The more packages on NPM servers the more valuable their
           | company, it's as simple as that.
        
         | elondaits wrote:
         | I think that's throwing the baby out with the bathwater.
         | Wouldn't it be logical that when people use snippets from Stack
         | Overflow or classical algorithms from Wikipedia the pasted code
         | was actually treated as a dependency so you can get warnings
         | and updates in case errors or security issues are found? It
         | also helps for proper licensing and attribution.
         | 
         | The problem is that code in npm should allow for trust. Either
         | based on signatures, or code reviews by trusted parties, or
         | something like that. Code signed by a long time trusted
         | developer that's been published for a month should not be
         | treated the same as a 3 minute old commit to a repo by a
         | mysterious developer. These verifications should be automated
         | and npm could give a final ranking.
        
           | throw_m239339 wrote:
           | > I think that's throwing the baby out with the bathwater.
           | Wouldn't it be logical that when people use snippets from
           | Stack Overflow or classical algorithms from Wikipedia the
           | pasted code was actually treated as a dependency so you can
           | get warnings and updates in case errors or security issues
           | are found? It also helps for proper licensing and
           | attribution.
           | 
           | Why are you assuming devs don't just both use random snippets
           | from Stack Overflow and also download packages with 900
           | transitive dependencies from NPM at the same time? It's not
           | one or the other.
        
       | Hackbraten wrote:
       | Vanilla JS is not something we should be afraid of. I wish we'd
       | consider it more often in projects.
       | 
       | The latest generations, ES2020 and newer, are pretty useful and
       | pleasant to use. If you leverage their features, it goes a long
       | way. Implementing the odd missing function yourself along the way
       | is perfectly doable if you embrace TDD.
       | 
       | My feeling is that people in the JS ecosystem tend to
       | overestimate how much time they'd allegedly waste re-implementing
       | stuff, and grossly underestimate the true cost of deeply-nested
       | dependency trees.
        
       | marcus_cemes wrote:
       | It makes me deeply sad to see these sort of interactions in open
       | source [1].
       | 
       | > Hmm, I think it's a worthwhile fix. Where did you see malware
       | here?
       | 
       | > I think the author of this repo is free to decide what code he
       | publishes. Say thanks to that it's for free
       | 
       | An incredible amount of people have dedicated sweat and tears and
       | foreheads (from banging against the desk in frustration) to open
       | source across the entire stack, from the contributers to OSs such
       | as Linux to those working their arses off to create better
       | frameworks, languages and runtimes, that we can _all_ benefit
       | from and use with a reasonable expectation of security, respect
       | and privacy.
       | 
       | As a university student, I feel privileged to have been able to
       | grow up in a world where so much work and knowledge is provided
       | for free with no strings attached, regardless of
       | demographic/location, I would not be where I am without it. A
       | century ago this would not have been possible. To all of you who
       | have tirelessly and selflessly worked on OSS for others, without
       | expecting anything in return or imposing politics, ideologies,
       | infringing on privacy, causing damage, collecting vast quantities
       | of marketable personal information or monopolisation, I give you
       | my heartfelt thanks for your efforts, you know who you are. You
       | have created something that will have forever helped to improve
       | our society and empower those that want to learn and create their
       | own designs.
       | 
       | From my own personal experience, I want to give a shout-out to
       | the smaller projects of Rust, Svelte and Elixir. I think it's
       | incredible that the work and ideas of (often) a single person
       | (Rich Harris, Jose Valim) can grow into larger extremely
       | welcoming and helpful communities with many more motivated
       | contributors that are proud of being parts of those projets and
       | put in an extraordinary effort to try and do things _better_ than
       | before. I 'm sure there are plently of other worthy names I'm too
       | young/ignorant to know.
       | 
       | Love it or hate it, Node.js has been very empowering for a large
       | number of people to learn and publish their own full-stack
       | applications, the JavaScript ecosystem has improved enormously
       | since its beginnings, but has a tendancy to change slowly due to
       | its size, unless a disruptive technology comes along such as
       | TypeScript. Websites are a great way to introduce people to the
       | joy of programming with its visual feedback, you can make a small
       | penguin move across the screen, then move on to play tic tac toe.
       | Even as a younger developer, I admit that the days of FTP, no-
       | build-step pages with a sprinkle of JQuery were easier to
       | understand and actually _safer_ for newcomers than introducing
       | someone to a SPA stack (which can easily have thousands of
       | transient dependencies) nowadays.
       | 
       | [1]: https://github.com/Yaffle/EventSource/issues/202
        
       | jmull wrote:
       | I think making the capabilities the responsibility of the app
       | (i.e., having the app handle tokens and pass them around) won't
       | work, for a couple reasons:
       | 
       | (A) it's tricky for the app to protect itself. The executing app
       | code tells the system what the executing app code is allowed to
       | do. It's tricky to get right -- you have to manage the trust
       | boundaries internally yourself, and it will be easy to get wrong.
       | 
       | (B) capabilities tend to depend on the environment, not just the
       | app. E.g., production vs. staging vs. dev environments may well
       | want different capabilities.
       | 
       | (C) capabilities are probably best expressed declaratively, but
       | putting tokens into into APIs makes the app handle them
       | procedurally. Once an apps capabilities need to go beyond the
       | trivial, this will really explode the complexity, making it
       | brittle and very difficult to handle correctly. E.g. suppose you
       | use express... how would it know how to pass tokens to its
       | dependencies? It would sort of need to understand your app's
       | security model, which it can't do. What would actually happen is
       | express would presume a certain model and pass tokens it receives
       | accordingly. App dev would adjust the tokens it passes to
       | express, adjusting until it works. So that at least works even if
       | responsibilities are mixed up. But now express can't make
       | internal changes to the way it calls it dependencies without risk
       | of breaking apps that take the update.
       | 
       | Anyway, I'm thinking the capabilities need to be specified
       | external to the app.
       | 
       | Say, in a config file (or files) of some sort. The app could only
       | be allowed at most read access to the capabilities file(s); (A).
       | There are some pretty common and straightforward ways to
       | customize config for different environments; (B). A config file
       | lends itself naturally to a declarative form; (C).
        
       | bspammer wrote:
       | This token system requires changing code, why not extend
       | package.json with a declared list of permissions instead?
        
         | josephg wrote:
         | Author here. That could well be a better design - especially if
         | we need to explicitly "bless" packages anyway.
         | 
         | One tricky part about that approach is it generates some weird
         | semver problems. Lets say package A uses package B to interact
         | with the filesystem. Package B has some problems, so the author
         | of package A replaces B with B2 (a fork of B).
         | 
         | From a semver perspective, this is totally fine because the
         | exposed API of package A hasn't changed. And this is also true
         | with the capabilities system I explained. But how would we do
         | it in package.json? If the root package needed to explicitly
         | bless B2 instead of B, that means package A must have broken
         | semver compatibility. Maybe each package expresses the
         | permissions its direct dependencies have, and it sort of
         | ripples out getting more specific in the dependency tree?
         | 
         | I think its a good idea, and there probably is a solution here
         | somewhere. But I'm not quite seeing it. Want to write up a
         | sketch of how you imagine that working?
        
       | [deleted]
        
       | cryptica wrote:
       | It doesn't make sense to build this 'capabilities' feature into
       | the program itself. It feels a bit like taping your mouth shut in
       | order to lose weight.
       | 
       | It doesn't make sense for a program to not trust its own code any
       | more than it makes sense for a person to not trust their own
       | thoughts.
       | 
       | There is no need to pollute your code like this. It should be
       | implemented as an external tool which analyzes dependencies when
       | executed on demand. A company could just run this tool as part of
       | their CI pipeline before code is deployed or executed. It could
       | be a default hook which runs automatically as part of npm
       | install. It should not be part of the code itself. It's ugly and
       | adds unnecessary overhead and complexity.
       | 
       | It's possible that an external tool executed at compile-time
       | would not be able to verify modules which come with C/C++
       | bindings, but I think it would be difficult to stop these anyway
       | (even at runtime). C/C++ bindings will always be less secure
       | because it's harder to understand what's going on if you don't
       | have access to the code. C/C++ is too powerful; you can do some
       | crazy stuff with buffer overflows which would be difficult to
       | detect anyway even at runtime. The solution is to try to stick to
       | modules which rely only on native Node.js functionality and not
       | on custom C/C++ bindings.
        
       | cabirum wrote:
       | Can we identify nodejs projects, which - a) are trivial to
       | implement and, b) are most depended upon?
       | 
       | I'd love to contribute to a coordinated community effort to
       | minimize the number of npm dependencies.
        
         | flanbiscuit wrote:
         | This is not the exact thing you're looking for but it's a place
         | to start:
         | 
         | https://gist.github.com/anvaka/8e8fa57c7ee1350e3491
         | 
         | That code in that gist is generated by this:
         | https://github.com/anvaka/npmrank
         | 
         | Just found that from a quick google search. It would be nice if
         | it had a way to search by max package size.
        
         | feross wrote:
         | We maintain an automatically updated list of "trivial packages"
         | (defined as less than 10 lines of code) here:
         | https://socket.dev/npm/issue/trivialPackage
         | 
         | Is this the type of list you were looking for?
         | 
         | It's not a perfect metric and isn't meant to disparage any of
         | the maintainers of these packages, but it's one factor in our
         | Socket.dev risk analysis.
        
           | cabirum wrote:
           | Yes, exactly!
        
             | feross wrote:
             | Great!
        
       | j0057 wrote:
       | Capabilities, chroots and whatnot look like containers, but with
       | extra steps.
        
       | Kaze404 wrote:
       | This article made me wonder about packages calling user code.
       | Using express' example, what if I don't want to give it
       | filesystem access but need it in one of my callbacks? Is the
       | engine capable of distinguishing my (privileged) code from
       | express' (unprivileged) code?
        
         | kilovoltaire wrote:
         | Your callback would have your capabilities in scope, so your
         | user code would still be able to do whatever you want, even
         | when called by an untrusted library.
         | 
         | I.e. something like:                   import capabilities;
         | import fs;         import someLibrary;              const root
         | =  capabilities.claimRootToken();
         | someLibrary.onEvent(() => {           fs.writeSync(root, ...);
         | });
        
           | Kaze404 wrote:
           | Makes sense, thank you
        
       | danShumway wrote:
       | Good article. This is what I was excited about the first time I
       | heard about Deno before I eventually learned what Deno's
       | sandboxing model actually was. I'm not sure this is the exact
       | proposal I would want, but I do want something vaguely like
       | scopes or capabilities in Node, and even if it wasn't perfect I
       | think it would go a long way towards mitigating at least some of
       | the current risk in the ecosystem.
       | 
       | Also agreed that for all their use, it would have been better in
       | the long run if install scripts had never existed. It's not just
       | that they're a security vulnerability, they also get in the way
       | of vendoring code, and can introduce additional non-JS
       | dependencies and errors on other systems/platforms. Again, not to
       | say that they don't have any use, I get why they're there. I just
       | wonder if the benefits are worth the downsides.
        
       | nilsbunger wrote:
       | I'm surprised we haven't had many crypto key thefts via npm
       | supply chain exploits.
        
       | z3t4 wrote:
       | There are only two solutions: 1) Trust someone 2) Carefully read
       | through their changes
       | 
       | node_modules should be banned from .gitignore
       | 
       | npm should stop moving folders around. If I have placed a module
       | in node_modules/foo/bar it should stay there! Not get moved to
       | node_modules/foo@1.0.0 etc
        
         | zkldi wrote:
         | You aren't meant to put your own files manually inside
         | node_modules. What are you trying to achieve with that?
        
           | silon42 wrote:
           | Have it in git and be part of code review.
        
             | nishs wrote:
             | If the code is managed manually and not by package.json, I
             | would consider putting such code in a "vendor" or
             | "third_party" directory instead of putting into
             | "node_modules".
        
       | chha wrote:
       | No packages or repositiories deserves trust, at least not in the
       | current state of affairs. There is no magic fix that will enable
       | you to trust packages just by adding a new framework or anything
       | similar, and what the linked article is addressing is only half
       | the problem.
       | 
       | We also need a way to make packages auditable. Package signing by
       | the publisher and the repository needs to be mandatory. Having an
       | actual link between the package and the commit it was built on
       | and a way to reproduce the build[1] also needs to be possible.
       | This would allow for proper code reviews, not just of your own
       | code but also audits of whatever extra components you are using.
       | 
       | Organizations need to define sensible thresholds for when you can
       | use a package and when you implement the code yourself, to avoid
       | adding a library only to use one function. They also need to
       | define trust; what is needed to trust a package or a maintainer.
       | 
       | And we need a system and proper best practice to guide us on what
       | to trust; do we really want to add a package with 100 direct or
       | transitive dependencies, where some hasn't been updated for the
       | past two years, some are maintained by solo developers and some
       | are just implementing already existing functionality?
       | 
       | All of these are hard to implement and justify, when the current
       | situation seem to work for a lot of people.
       | 
       | [1] - https://wiki.debian.org/ReproducibleBuilds
        
       | lucacasonato wrote:
       | Deno core team member and TC39 delegate here.
       | 
       | We have pondered about capability based security for Deno in the
       | past. Our conclusion has always been that this is not possible to
       | do securely in JS without freezing all prototypes and objects by
       | default. The reasoning for this is that you need to make sure the
       | capability token does not ever leak. For example as a malicious
       | user I could override `globalThis.fetch` to exfiltrate the
       | capability token destined for `fetch` and use it myself later.
       | 
       | One could also override `Map.prototype.set` / `Map.prototype.get`
       | to exfiltrate a token every time it is added or removed from a
       | `Map` (people will want to store tokens in a `Map`).
       | 
       | One could also override `Array.prototype[Symbol.iterator]` to
       | exfiltrate tokens stored in arrays if those arrays are
       | destructored, spread, etc.
       | 
       | There are many more cases like this, where one can exfiltrate
       | tokens because of the very dynamic nature of JavaScript.
       | 
       | It is unlikely that freezing all intrinsic prototypes and objects
       | is even enough. People will find ways to exfiltrate tokens.
        
         | bakkoting wrote:
         | o/ Luca! Fellow TC39 delegate here.
         | 
         | > It is unlikely that freezing all intrinsic prototypes and
         | objects is even enough. People will find ways to exfiltrate
         | tokens.
         | 
         | This is probably true, but frozen intrinsics would make it a
         | _lot_ harder. Right now it's not reasonable to ask a library to
         | be defensive against capability exfiltration, since it means
         | not using any built-ins, but I think with frozen intrinsics it
         | would be reasonable to treat a library leaking its capabilities
         | as a security bug. There would still absolutely be leaks - most
         | significantly in libraries which export classes and don't
         | freeze the class prototype - but things would no longer be
         | completely insecure by default. It would make malicious code
         | have to work a _lot_ harder.
         | 
         | I think it's worth a shot. Deno removed the __proto__
         | getter/setter, and that did require a bunch of libraries to
         | update, but it worked out OK.
         | 
         | Node already has --frozen-intrinsics, if anyone feels like
         | experimenting with whether that would break your code.
        
           | lucacasonato wrote:
           | Yeah, I agree it is definitely worth trying! I think all the
           | talk around SES will push JS as a whole further towards
           | something that could support capability based permissions
           | securely in the future.
        
         | white_dragon88 wrote:
         | Why not just freeze anyway? At the end of the day you don't
         | know what you don't know. Deno is well positioned to make these
         | sorts of restrictions, how often is someone doing something as
         | mental as modifying prototypes in a nodejs environment anyway?
         | 
         | (Moment comes to mind actually, but does it really matter? That
         | library is deprecated anyway)
        
           | joshAg wrote:
           | Part of the draw of deno is that for better or worse it's
           | javascript. If you start changing things about the language
           | used in the deno runtime such that it's no longer compliant
           | ecmascript, then you no longer get the benefits of it being
           | ecmascript. The devs' mental modal of the language isn't a
           | drop-in, libraries and modules including popular ones, are no
           | longer guaranteed to work out of the box, etc.
           | 
           | You might say that all that is worth the benefit, but in that
           | case why not just use another language that already gives you
           | the feature you want or why stop there? Why not also fix
           | other issues with javascript at the same time since we're no
           | longer preserving compatibility? Something mental like the
           | automatic type coercions? Or getting rid of var?
        
             | bakkoting wrote:
             | Deno has already made similar changes, like
             | https://github.com/denoland/deno/pull/4341. That particular
             | change happens to be allowed by the JS standard. The change
             | discussed here isn't currently allowed, but I suspect TC39
             | would be open to making it allowed (though obviously it
             | would not be allowed for browsers, in the same way the
             | linked change to __proto__ is not allowed for browsers).
             | 
             | If you change something that most code isn't relying on,
             | most code will still work. This change is plausible because
             | it's very rare for code to be mutating built-ins. That's
             | not true for most other possible "fixes". And most other
             | changes would not have a benefit to consumers of the
             | application (who cares if the library you're using has
             | `var`s?), so they're much less well motivated.
        
               | joshAg wrote:
               | I wouldn't consider that a similar change, because it's
               | fully compliant with a newer js spec, which deprecated
               | that feature. It sounds like, what deno has done is
               | removed native support for older js specs and instead
               | makes you transpile to an earlier spec. And by removing
               | support for the earlier specs, they are able to drop
               | support for deprecated features.
               | 
               | "allowed by the js standard" is key. As long as it's
               | allowed by the standard they're still fully compliant
               | with it. The compliance is necessary because no one wants
               | to deal with "mostly compliant". Users want certainty, so
               | "mostly compliant" becomes "fork their spec and make your
               | own, so i can know what guarantees you make". That's why
               | each change to the spec results in a new version. It's a
               | self-fork of the previous spec.
               | 
               | If the change made it into the ts or js spec, I'm sure
               | they'd add in support for freezing protoype chains, even
               | if just as an option that can be toggled, but i doubt
               | they'd ever want to break the spec just because they
               | don't like parts of it. That opens the door to more
               | changes because they don't like the spec, and eventually
               | you have a new language, or worse, the original language
               | changes out from under you to support something similar
               | in a newer spec (eg typescript and namespaces/modules).
        
               | bakkoting wrote:
               | > That's why each change to the spec results in a new
               | version. It's a self-fork of the previous spec.
               | 
               | That's not how it works, no. There is just the spec [1],
               | which is updated frequently. I am editor of the
               | specification. (There are annual editions as well, but no
               | one should pay attention to these.)
               | 
               | > I'm sure they'd add in support for freezing protoype
               | chains, even if just as an option that can be toggled,
               | but i doubt they'd ever want to break the spec just
               | because they don't like parts of it.
               | 
               | Well, like I said, if Deno's only concern is breaking
               | with the spec here, I expect the spec could be updated to
               | allow this behavior.
               | 
               | [1] https://tc39.es/ecma262/
        
               | joshAg wrote:
               | >>> It is the fourteenth edition of the ECMAScript
               | Language Specification
               | 
               | Fine, then sed s/version/edition/g
               | 
               | From the point of view of a maintainer that makes sense,
               | but for the users each annual edition or feature moving
               | into stage 4 its own spec version, and unless the feature
               | is absolutely groundbreaking, thinking in terms of annual
               | editions makes it possible for users to grab various
               | tools with confidence that things will work together
               | smoothly.
               | 
               | Look at how Mozilla interacts with the spec [1]. They're
               | not thinking in terms of the nightly version of the spec.
               | They're looking at the annualized editions and making
               | sure they support them as fully as possible. And then
               | they communicate that support to their own users in terms
               | of that annualized edition.
               | 
               | V8 consumes from nightly and using the up-to-date test
               | suites and they explain their reasoning here[2], but
               | notably they're still conceptualizing things to their
               | users through the lens of annualized editions even though
               | they're still grabbing features when they're only at
               | stage 3. They even tag blog posts about js with tags for
               | the annualized editions that added the feature: [3].
               | 
               | As a user of the users of the spec, the annualized
               | editions are super super helpful. I can only use features
               | that have actually been implemented. And I have to make
               | sure that each tool I use is only getting code that uses
               | features it's implemented. Can you imagine if every tool
               | had feature-by-feature implementation matrices? "OK,
               | ESlint understands new features A, B and D, but babel
               | only implements B, C, and D, but library X doesn't
               | support feature D yet, so since we want to use that we
               | have to use bluebird instead of the native feature D for
               | now" and on an on. It'd be madness, and we'd end up
               | picking a handful of tools we like enough and then
               | transpiling everything to es5 because our own users don't
               | actually care if we transpiled down to es5 or shaved
               | enough yaks that we realized that our toolchain natively
               | supports featurs B but everything else must be transpiled
               | out or replacing the native implementations with our our
               | in code implementation. Instead each tool picks an
               | annualized edition and while slower tool release cycles
               | be annoying, I can actually turn that guarantee of
               | standardized features into a toolchain with transpilation
               | steps as necessary, which means as a dev I know i can
               | safely use any feature in that annualized edition without
               | worrying if this feature i haven't really used before is
               | going to blow up somewhere in my toolchain because the
               | implementer hasn't gotten around to implementing that
               | feature yet.
               | 
               | So while I like looking at the draft proposals and
               | consider it important to know how the language is
               | evolving, I have to wait for implementations to permeate
               | enough of my tools before I can use the shiny new
               | feature, which means annualized editions of the spec.
               | 
               | [1]: https://blog.mozilla.org/javascript/2017/02/22/ecmas
               | cript-20... [2]: https://v8.dev/blog/modern-javascript
               | [3]: https://v8.dev/features/tags/ecmascript
        
               | bakkoting wrote:
               | Mozilla is absolutely thinking in terms of the nightly
               | version of the spec. I agree that public messaging
               | sometimes talks about annual editions, but this is mostly
               | because it's a convenient way to talk about when features
               | were added to the language, not because it reflects any
               | underlying reality.
               | 
               | Anyway, that's not really the relevant thing. What I'm
               | addressing is:
               | 
               | > deno has done is removed native support for older js
               | specs and instead makes you transpile to an earlier spec.
               | And by removing support for the earlier specs, they are
               | able to drop support for deprecated features.
               | 
               | And that's just not a thing. That has no relationship to
               | the ES specification works. The __proto__ accessor was
               | never mandatory; it was added in browsers a long time
               | before it was specified, and then specified as optional
               | (Annex B) when it was first added to the specification,
               | and has been optional since then. This is true whether or
               | not you think of there being a single specification or
               | annual editions.
               | 
               | So, with that said, to address the specific topic of
               | annual editions:
               | 
               | > "OK, ESlint understands new features A, B and D, but
               | babel only implements B, C, and D, but library X doesn't
               | support feature D yet, so since we want to use that we
               | have to use bluebird instead of the native feature D for
               | now" and on an on.
               | 
               | That's exactly how it works. eslint implements proposals
               | at stage 4. Babel implements proposals as they come out
               | and people contribute, but only adds them to preset-env
               | at stage 3. The output for present-env is based on what's
               | actually supported in the browsers you're using. They
               | both take a variable amount of time to land features once
               | they hit the appropriate stage. Neither of them gates
               | anything on annual editions. Neither do browsers. And
               | browsers will frequently not have implemented features
               | from multiple editions ago; for example, regex lookbehind
               | was added in ES2018 and is still not implemented in
               | Safari.
        
           | lucacasonato wrote:
           | If it was 2000, or even 2010, I'd agree with you. But that
           | ship has unfortunately sailed. People modify the prototype
           | all the time. If we were to freeze all prototypes right now
           | in JS, a lot of existing code would break.
        
           | yonixw wrote:
           | > something as mental as modifying prototypes
           | 
           | This is exactly what Jest (mocks) is built on top of. You
           | will fail every enterprise security scan if you have Jest in
           | the production package/docker.
        
         | prakis wrote:
         | Why not lock the core libs like Map.prototpy Array.prototype ?
         | Can't we have sandbox like environment (which is not default
         | but which can be enabled on application basis). Java Applets
         | which run inside the browser had this kind of sandbox
         | restritions.
        
         | throw_m239339 wrote:
         | Hi, first thanks for your work both in TC39 and Deno.
         | 
         | But that's not the heart of the problem for node. The problem
         | with node ecosystem and is a combination of a refusal from Core
         | to provide a meaningful standard library, a set of packages
         | people can trust so they don't have to download random things
         | from the internet just to parse the body of a multi-part
         | request for instance, and the fact that NPM was badly
         | architectured as it's going to fetch as many versions of a same
         | package as it needs to solve dependencies. Good package
         | managers don't do that, period. Of course that, given the
         | dynamic nature of Javascript, anybody can monkeypatch anything.
         | But the language itself isn't at fault, it REALLY IS both the
         | politics of a paper thin STD lib and bad package management
         | with stupid dependency resolution.
         | 
         | And frankly Node.js should stop shipping with a package manager
         | and its infrastructure entirely controlled by Microsoft. it's
         | so bizarre how nobody seems to mind that in the Node
         | community... NPM servers aren't open source.
        
         | zozbot234 wrote:
         | These seem like features that could work a bit better in the
         | context of WASM modules and/or components. AIUI, WASM was
         | designed with the expectation that support for capabilities
         | would be required.
        
           | lucacasonato wrote:
           | Yup, for sure. I think capability based ShadowRealms are also
           | totally doable. I'm sure we'll add those for Deno once we add
           | support for ShadowRealms.
        
         | akhmatova wrote:
         | Your input is appreciated. But to anyone not using JS on a
         | daily basis, the above reads as a blur of detail, rather than a
         | clear, actionable message about to do about the overall
         | situation.
         | 
         | So I'd be curious to know if you have a holistic response to
         | primary claim made by the post, which is:
         | 
         |  _The fundamental problem with npm is that any package you
         | install has full access to do whatever it wants on your
         | computer._
        
           | hackerfromthefu wrote:
           | I think what he is saying is that Javascript is a broken
           | language. Fundamentally un-securable when third party code is
           | used.
        
             | bastawhiz wrote:
             | That's a pretty extreme way to say it. Python is in
             | essentially the same boat, as is bash, Ruby, PHP, dart...
             | Calling languages "broken" is to say that one hard problem
             | makes them unsuitable for use, which is hardly true.
        
               | akhmatova wrote:
               | I think the main concern here is that it isn't that the
               | language is too permissive -- but that the primary
               | _installer_ (and what many people cite as one of the
               | greatest strengths of JS, largely responsible for
               | ushering in its Golden Age) -- is structurally (and
               | perhaps irreparably) insecure.
               | 
               | Whether this is really so (and in a significant way
               | compared to its other competitors, out in dynamic
               | programming land) -- is what people seem to be trying to
               | suss out in this thread.
        
               | bastawhiz wrote:
               | The top comment mentions Deno, which doesn't have NPM or
               | even use a package manager. So I'm not sure how the
               | "installer" is the relevant problem
        
               | johnny22 wrote:
               | how is it any less problematic than python or ruby in
               | this regard?
        
             | akhmatova wrote:
             | Yup, that's how I read it too. :) Was just wondering what a
             | comprehensive counter-argument would look like.
             | 
             | If there is one, that is.
        
               | koonsolo wrote:
               | So which language doesn't have this problem when
               | including 3rd party libraries?
        
             | koonsolo wrote:
             | So when you include a 3rd party C++ library, how do you
             | know it's not doing some malicious things?
        
           | lucacasonato wrote:
           | > The fundamental problem with npm is that any package you
           | install has full access to do whatever it wants on your
           | computer.
           | 
           | Let me try to answer more concisely at a higher level:
           | 
           | Due to JS being a very dynamic language, it is at this time
           | not possible to give different packages inside of the same JS
           | runtime a different set of permissions or capabilities
           | securely.
           | 
           | Because of this, the best we can do for sandboxing right now
           | is permissions / capabilities that are set per JS runtime,
           | rather than per dependency / module.
           | 
           | This is what Deno does. It allows you to set capabilites for
           | the set of your code and all of it's dependencies at once. If
           | you want to run a certain module with lesser permissions, you
           | need to move it into a seperate JS runtime, either by running
           | it on a seperate thread with a web worker, or in a seperate
           | Deno process. Both web workers and subprocesses can have
           | different (lesser) permissions to their parent.
        
             | paulmd wrote:
             | it's been fascinating watching the JS people reinvent the
             | concept of an OS from scratch. Today it's the security
             | model, yesterday it's the task scheduling, etc.
             | 
             | Interesting little bit of outsider art, "what if front-end
             | people designed an OS" and all, and it's going about as
             | well as you'd expect with that.
             | 
             | I mostly wouldn't mind, but there's certain thresholds that
             | I think are noxious or dangerous as a user. WebGL strikes
             | me as much more dangerous than most people anticipate, GPU
             | drivers are not really hardened against hostile shaders
             | very well, it's quite likely that various escapes and data-
             | leaks exist imo. Someone I know who worked on implementing
             | that and said that out of all the graphics vendors, none of
             | them liked it but the blue one's engineers had genuine fear
             | in their eyes when the functionality was laid out to them.
             | 
             | I am also not really looking forward to when javascript
             | realizes the need for persistent services... but thankfully
             | I think web architecture mostly means that's somebody
             | else's computer.
        
           | armchairhacker wrote:
           | Dynamic languages such as JS allow you to override almost
           | anything. It's essentially impossible to sandbox "part" of JS
           | code, you must either sandbox the entire runtime (e.g. in
           | browsers) or allow everything (e.g. in node.js).
           | 
           | Individual packages want to run arbitrary JS code to do
           | "safe" things during installation. But it's very hard to
           | allow them to do anything meaningful like file access, even
           | if it's "safe", without potentially allowing exploits.
        
             | throwawayboise wrote:
             | Run in a chroot/jail or container?
        
           | joshAg wrote:
           | Not the OP, but the approach sounds roughly similar to SE
           | linux, but for node.
           | 
           | A major problem with a half-secure security solution is that
           | it's not actually secure. You might do everything right, and
           | still get owned. For the threat model the solution needs to
           | be complete (or able to be complete, but turned down to less
           | than complete by the user*).
           | 
           | Part of the way this manifests with SElinux is that to have a
           | fully locked down box with SElinux you have to consider
           | access controls for _everything_ on the box on top of regular
           | unix permissions. And to actually be a full solution, you
           | have to install kernel headers because anything user-space
           | isn't good enough to guarantee full security.
           | 
           | For node to make a similar guarantee locking down everything
           | means turning off a lot of the features that allow javascript
           | to be so dynamic or making major changes to the run-time
           | implementation to support being able to use those features
           | without making the security swiss-cheese.
           | 
           | And then, even though the system is securable, there's the
           | issue of turning it on for all the modules you use. And not
           | just the module's you use, but the modules your modules use
           | and on and on. You could delegate the module-module to the
           | module you import but that means either granting overly broad
           | permissions to the module and hoping they don't screw
           | something up (and that any module they delegate to does the
           | same) or not delegating anything and personally granting
           | explicit permissions to every module, no matter how deep in
           | your hierarchy of requirements. If that sounds exhausting, it
           | kind of is.
           | 
           | In SElinux's case what that leads to instead usually is
           | rearchitecting things such that you can use virtualization to
           | sandbox things and limiting access across the sandboxes
           | (ohai, it's the deno solution). There's still places where a
           | VM or other sandbox isn't appropriate though and if you want
           | to be on linux you have to use SElinux (or a competitor, but
           | i've only touched selinux), and it's not uncommon to have an
           | entire team whose only job is to configure SElinux and
           | support other teams that interact with it (eg, coaching them
           | on how SElinux interacts with their codebase and what they
           | need to tune, auditing teams' SElinux configs, and keeping
           | SElinux working for the base system as that gets upgraded).
           | And if you screw up or get lazy with auditing permissions,
           | you've just limited the effectiveness of SElinux, possibly
           | rendering it useless.
           | 
           | A large part of the reason SElinux is so hard to use and use
           | right (and that directly translates to js and node) is that
           | it's attempting to bolt-on security to an existing system
           | that wasn't designed with (that kind of) security in mind.
           | That's a monumentally hard thing to do in a way that doesn't
           | require rewriting everything that uses it. And not having to
           | rewrite everything is a hard requirement, because if you're
           | going to rewrite everything that uses it, it's usually
           | cheaper and easier to just make something new from scratch
           | (in SElinux's case a new OS, in js's case a new language).
           | 
           | So holistic options: 1) remove all the dynamism that makes
           | javascript javascript. this (potentially) breaks all existing
           | code. Call it rustscript and get that to ship in all the
           | browsers, and get all the websites to use that instead of
           | javascript, and then make a serverside environment for
           | rustscript. Now you can do fine-grained module permissioning.
           | 1.5) remove only the dynamism that breaks this sort of
           | security access control as part of a new ECMAScript spec and
           | add support for the security access control at the same time.
           | This breaks existing code, but the old code can still run in
           | a runtime for the earlier spec. New code can take advantage
           | of the new spec features. Old code can be modified to work
           | with the new features. Broken code can be rewritten to the
           | new spec. This makes rustscript ESNext. It will be up to
           | various runtime to support this new runtime, so nodeNext will
           | have support for it but it won't get backported. Browsers
           | will require transpilation from ESNext to an earlier ES
           | version as they do now, but eventually even they would drop
           | support for the older js versions. 2) accept that module
           | permissioning systems are easy enough to get around in JS
           | that anything attempting to implement them is at best
           | security theater. The deno solution isn't security theater,
           | but that's because it makes much less stringent guarantees
           | (ie only runtime granularity and not module granularity).
           | 
           | * why allow the user to make themselves insecure? In some
           | cases, the user will choose to be less secure for some
           | external reason or will be using the security solution as a
           | part of a more holistic security solution, so some other part
           | guarantees the security that is given up.
        
             | [deleted]
        
           | yonixw wrote:
           | Based on your use-case, it may already been solved.
           | Microservices, for example, are put in a container, which
           | allow you to give read-only\full access to selected files.
           | They also usually have some kind of Mesh side container that
           | limits connectivity between whitelisted containers or public
           | DNS. And since containers can limit memory and throttle CPU,
           | you are more or less at the best you can get using untrusted
           | 3rd party code.
        
           | asiachick wrote:
           | That is special about JS here? AFAICT the same is true for
           | every python package, every rust crate, every linux package,
           | every ruby gem, etc....
           | 
           | Maybe all of those need the same scrutiny but AFAIK they
           | fundamentally have the same issue.
        
             | forty wrote:
             | Exactly, I'm not sure what people are trying to fix here.
             | They want code they import in their projects to be
             | sandboxed so they import untrusted code. That seems a bit
             | f*cked up to me. Running untrusted apps is already pretty
             | hard, running apps where some code is trusted and some not,
             | seems... a bad idea.
             | 
             | IMO since the problem is generic to all languages we should
             | have a generic solution, using standard sandboxing technics
             | (containers, VMs, jail, etc)
        
             | ryukafalz wrote:
             | That is correct, these all fundamentally have the same
             | issue and all need the same scrutiny.
        
             | Sohcahtoa82 wrote:
             | Python developers are slightly better behaved.
             | 
             | I can't possibly come up with a reason WHY (Maybe Python's
             | standard library is that much better than Node?), but
             | Python dependency trees just tend to be so much simpler. If
             | I "pip install $PACKAGE", it's usually only a couple
             | additional dependencies that get included unless it's one
             | of the huge ones like numpy. If I "npm install $PACKAGE", I
             | can expect 50+ additional dependencies to get added.
             | 
             | For some reason, Node engineers are much more likely to
             | "npm install leftpad" than write the 2 lines of code it
             | takes to implement string padding.
        
               | verelo wrote:
               | The JS standard library is very slim. I think this is
               | both its greatest strength and weakness, and to your
               | point, makes it likely that people just install some
               | random package v's solve the problem themselves. I'm not
               | sure which is better philosophically, but personally
               | having done a lot of PHP dev in the last decade I find
               | the JS code i'm writing to be easier to read but much
               | harder to understand and run from a package management
               | perspective. I think the developers working on solutions
               | in JS would probably behave very different if they were
               | given more built in tools to work with.
        
               | paulmd wrote:
               | the same is true of java - less so in newer versions, but
               | older versions the stdlib was pretty slim and low-level
               | and there was a massive amount of library code written to
               | wrap around it.
               | 
               | it still doesn't devolve to the javascript phenomenon of
               | one-file (or one-function) libraries and so on. Dozens of
               | libraries, sure, hundreds, maybe, but nobody actually has
               | thousands of dependencies like you do on node.
               | 
               | Apparently "number of repos maintained" is a KPI so
               | there's some gamesmanship there. Maybe it's also being
               | used as a caching thing, smaller files might be more
               | cacheable if you don't minify?
               | 
               | More generally though this may be the result of that
               | "enterprise culture" that is sometimes looked down on in
               | other situations, that at least stuff is getting bundled
               | into appropriate packages for distribution vs just
               | chucking every single file into a node package.
        
         | inglor wrote:
         | Hey luca, I assume you are familiar with Mark's extensive work
         | on capability based security for ES?
        
           | lucacasonato wrote:
           | Yup, SES is very interesting, and I think it can solve this
           | problem in the future. As this post is about the here and now
           | though, and SES is not yet ready for widespread adoption, I
           | think my point stands that at this current time it is not
           | possible to securely do capability based security in JS.
        
         | 3np wrote:
         | Doesn't SES address that? The only fundamental barrier right
         | now seems to be performance, which could be addressed by
         | runtime support.
         | 
         | https://github.com/tc39/proposal-ses
         | 
         | https://github.com/endojs/endo
        
           | lucacasonato wrote:
           | Yup, SES would address this. But SES also needs to bring with
           | it a paradigm shift for JS:
           | 
           | a) Folks would have to load each bit of code they want
           | separate permissions for in a separate compartment. This
           | won't be easy. b) Runtimes will need to provide an immutable
           | global realm, which is not something that is the case right
           | now.
           | 
           | As I said in a different commeent, I think a lot of this can
           | already be addressed by ShadowRealms. Deno will likely allow
           | users to specify per ShadowRealm permissions, which is
           | probably as granular as most people will want to get.
        
         | ex3ndr wrote:
         | You can do this in almost any language, like Java, ObjC/Swift,
         | etc
        
       | chubot wrote:
       | I think this needs to be solved at the OS level, not the language
       | level.
       | 
       | It's a problem for every language -- packages you download can
       | write to any file or make network connections. It might be a
       | little worse in NPM because of the culture, but the problem is
       | pervasive.
       | 
       | Personally I'm interested in the direction of lightweight
       | containers that behave like executables, and that have composable
       | dependencies. Developing in Docker-like containers can work but
       | there are a bunch of downsides to be mitigated.
       | 
       | (copy of lobste.rs comment)
        
       | mike_hearn wrote:
       | Hmm, where to begin? This is an old idea. It has all been tried
       | before in the JVM world and yet support for it is now being
       | _removed_ , which is in my view a pity given that Now Is The
       | Time. But the problems encountered trying to make it work well
       | were real and would need to be understood by anyone trying the
       | same in the JS world.
       | 
       | Understand that Java had it relatively easy. Java was designed
       | with a sandbox as part of the design from day one, the venerable
       | SecurityManager. The language has carefully controlled dynamism
       | and is relatively easy to statically and dynamically analyze, at
       | least compared to JavaScript. The libraries were designed more or
       | less with this in mind, and so on.
       | 
       | So what went wrong?
       | 
       | Firstly, the model whereby you start with a powerful "root"
       | capability and then shave bits off doesn't have particularly good
       | developer usability. It requires you to manually thread these
       | little capabilities through the call stack and heap, which is a
       | nightmare refactoring job even in a language like Java let alone
       | something with sketchy refactoring tooling like JavaScript.
       | _Lots_ of APIs become awkward or impossible, something as basic
       | as:                   var lines = readFile("library-data.txt");
       | 
       | is now impossible because there's no capability there, yet,
       | developers do expect to be able to write such code. Instead it
       | would have look like this:                   function
       | readFile(appDataPath) {             var url =
       | appDataPath.resolve("library-data.txt");             var lines =
       | appDataPath.readLines();         }
       | readFile(rootFileSystem.resolve("/app/data"));
       | 
       | Can you do it? Yes. Does it make code that was once concise and
       | obvious verbose and non-obvious? Also yes.
       | 
       | Consider also the pain that occurs when you need a module that
       | has higher privileges than the code calling it (e.g. a graphics
       | library that needs to load native code, but you don't want to let
       | the sandboxed code do that). In the pure caps model you end up
       | needing a master process that "tunnels" powerful caps through to
       | the lower layers of the system, breaking abstractions all over
       | the place.
       | 
       | Secondly, this model means you can never add new permissions,
       | change the permissions model or have different approaches because
       | refining permissions == refactoring all your code, globally,
       | which isn't feasible.
       | 
       | Thirdly, this model imposes cap management costs on _everyone_
       | even if they don 't care about security because they know the
       | code is trustworthy e.g. because their colleagues wrote it, it
       | came from a trustworthy vendor, or because it'll run in a process
       | sandbox. Even if you know the code is good it doesn't matter, you
       | still have to supply it with lots of capabilities, you still have
       | to implement callbacks to give it the capabilities it needs on
       | demand and so on.
       | 
       | These problems caused Java to adopt a mixed capability/ambient
       | permissions model. In the SecurityManager approach you assigned
       | permissions based on _where_ code came from and stack walks were
       | used to intersect all the sources on the stack. Java also allowed
       | libraries to bundle data files within them, and granted libraries
       | read access to their resources by default. That solved the above
       | problems but introduced new ones, in particular, it lowered
       | performance due to the stack walking, plus now library developers
       | had to document what permissions they needed and actually test
       | the code in a sandboxed context. They never did this. Also the
       | approach was beaten from time to time by people finding clever
       | ways to construct pseudo-interpreters out of highly dynamic code,
       | such that malicious code could get run without the bad guy being
       | on the stack at all.
       | 
       | Fourthly, it's dependent on everyone playing defense all the
       | time. If your object might get passed in to malicious code, then
       | it has to be designed with that in mind. A classic mistake:
       | class Foo {            private ArrayList<String> commands;
       | void addCommand(String command) { commands.add(command); }
       | List<String> getCommands() { return commands; }          }
       | 
       | The author's intent was to make an object in which you can read
       | the list of commands but not write them. But, they're returning
       | the collection directly instead of using an immutable wrapper.
       | Fine in normal code, but oops, in sandboxed code now you have a
       | CVE. Bugs like this are non obvious and the tooling needed to
       | find them isn't straightforward. These bugs are a drain on
       | development.
       | 
       | Fifthly, Spectre attacks mean that a library that can get data to
       | an attacker via any route can exfiltrate data from anywhere in
       | the process. You may not care about this, and for many libraries
       | there may be no plausible way they can exfiltrate data. But it's
       | another sharp edge.
       | 
       | Finally, it all depends on the ecosystem having minimal native
       | code dependencies. The moment you have native code in the mix,
       | you can't do this kind of sandboxing at all.
       | 
       | Now. All these are _challenges_ but they don 't mean it's
       | impossible. Sandboxing of libraries is clearly and obviously
       | where we have to go as an industry. The Java approach didn't fail
       | only due to the fundamental difficulties outlined above - the
       | SecurityManager was poorly documented and not well tuned for the
       | malicious libraries use case, because it was really meant for
       | applets. After the industry gave the Java team so much shit over
       | that, they just sort of gave up on the whole technology rather
       | than continuing to iterate on it. It may be that a team with
       | fresh eyes and fresh enthusiasm can figure out solutions for the
       | above issues and make in-process sandboxing really happen. I wish
       | them the best, but anyone who wants to work on that should start
       | by spending time understanding the SecurityManager architecture
       | and how it ended up the way it did.
       | 
       | https://dl.acm.org/doi/pdf/10.1145/2030256.2034639
        
         | ryukafalz wrote:
         | > Lots of APIs become awkward or impossible, something as basic
         | as[...]
         | 
         | I mean, wouldn't you use a `readFile()` function like that by
         | passing in the file handle? So:                   var lines =
         | readFile(fs.open("library-data.txt"));
         | 
         | ...where, if you're in a library somewhere, `fs` may be a
         | capability to a directory that you've been passed rather than a
         | global granting access to the entire filesystem. This doesn't
         | feel much more awkward than your example of:
         | var lines = readFile("library-data.txt");
         | 
         | EDIT: I am assuming you have an `fs.open()` that returns a file
         | handle here; Node's doesn't seem to and instead takes a
         | callback as an argument. You get the idea though.
        
           | mike_hearn wrote:
           | That's pretty much what I said, no? It gets awkward: now your
           | library can't just load some data table it needs from a file,
           | it has to have either some sort of initialization step where
           | you give it the capabilities it needs or it has to take them
           | in the API call itself.
           | 
           | Now let's say you change the implementation such that it
           | needs a new permission. You have to pass that in, which may
           | well mean passing it in from the root of the app through a
           | long call stack. Quite painful. Programmers like conveniences
           | such as being able to give a string instead of a file handle.
        
             | ryukafalz wrote:
             | I'm sure programmers do like that convenience, but if the
             | consequence is that we're giving every library access to
             | everything the rest of the app has access to, I don't think
             | that's tenable long-term.
             | 
             | > Now let's say you change the implementation such that it
             | needs a new permission. You have to pass that in, which may
             | well mean passing it in from the root of the app through a
             | long call stack.
             | 
             | Sure, but put another way: you can't change the
             | implementation of your library to grant yourself more
             | access to the system without the calling application being
             | aware of it. Is this potentially inconvenient? Sure. But it
             | does mean that the developer of the calling program knows
             | pretty dang well what access they're handing over to the
             | library.
        
         | josephg wrote:
         | Author here. Thankyou so much for this summary of java's
         | approach. I learned Java when I was a kid in the 90s and I
         | remember seeing some SecurityManager stuff in the java standard
         | library and I remember having no idea what that was or why I
         | would want any of it. Its funny to think that decades later I
         | would propose re-inventing it.
         | 
         | As for the code, surely something like this could work?
         | var lines = readFile("library-data.txt", capabilityToken);
         | 
         | But yeah, even in the example in my post the capability tokens
         | are annoying and feel cumbersome.
         | 
         | Another poster in this thread suggested maybe expressing
         | capabilities in your package.json file. Maybe when you pull in
         | a dependency you can say "oh, and rather than inheriting all my
         | capabilities, only give this library access to capability X.
         | That would provide a nice ramp, but there's a whole new set of
         | problems that way, since you'd need to be able to express
         | something like "the capability you need for the redis client is
         | network access to this specific IP address". And that specific
         | capability needs to be passed all the way through the
         | dependency tree to whatever finally opens the socket.
         | 
         | Expressing this in a granular way in code is easy, but noisy.
         | But if we do it in package.json, maybe thats not going to be
         | expressive enough.
         | 
         | Anyway, like you, I hope someone smart takes the time to have
         | another stab at this. The security model where we trust all
         | software engineers is obviously breaking down at this point.
         | Short of a model like this, I'm not sure how we can really
         | solve this problem at all. In any case, thankyou for sharing
         | your wisdom.
        
           | mike_hearn wrote:
           | Re: your example. What is "capabilityToken" in this case?
           | What does it grant you, precisely? Is it a directory? A file?
           | Something else? The classical approach to using caps with
           | files is you create a File type of some kind, which
           | encapsulates the permissions and lets you derive from it e.g.
           | sub-directories, files in that directory but not navigate up
           | the tree. Or it's associated with some whitelist of files.
           | 
           | For that to work you need not only a carefully designed set
           | of types but also they must be able to protect their
           | internals. JavaScript historically hasn't had this, I don't
           | know about modern versions, but the ability to restrict
           | monkey-patching, reflection over private fields etc is a
           | must.
        
             | josephg wrote:
             | > For that to work you need not only a carefully designed
             | set of types but also they must be able to protect their
             | internals. JavaScript historically hasn't had this, I don't
             | know about modern versions, but the ability to restrict
             | monkey-patching, reflection over private fields etc is a
             | must.
             | 
             | At the bottom of the post I sketched out how we could make
             | this work in practice in javascript. We can use a
             | Symbol[1], and then have that be a key into a Map owned by
             | the builtin capabilities library. That would make the token
             | itself safe from being messed with.
             | 
             | But so long as the capabilities library uses whatever the
             | object is as a key in a JS Map (with the value being the
             | token's scope), we could just as easily use anonymous
             | objects or something else.
             | 
             | [1] https://developer.mozilla.org/en-
             | US/docs/Web/JavaScript/Refe...
        
               | mike_hearn wrote:
               | I think the issue is more the code that uses the
               | capability itself. Like, if I can just read the
               | capability straight out of the object that owns it, or
               | monkey-patch the definition of some other object it calls
               | into so I can use its capabilities indirectly, then you
               | still lose. That's what I meant by playing defense all
               | the time. If you give a bit of sandboxed code a generic
               | utility object, it can all go wrong.
        
       | grnmamba wrote:
       | The offending event-source-polyfill version is still on
       | npmjs.com.
        
       | [deleted]
        
       ___________________________________________________________________
       (page generated 2022-04-11 23:01 UTC)