←back to thread

1208 points jamesberthoty | 7 comments | | HN request time: 0s | source | bottom
Show context
kelnos ◴[] No.45266878[source]
As a user of npm-hosted packages in my own projects, I'm not really sure what to do to protect myself. It's not feasible for me to audit every single one of my dependencies, and every one of my dependencies' dependencies, and so on. Even if I had the time to do that, I'm not a typescript/javascript expert, and I'm certain there are a lot of obfuscated things that an attacker could do that I wouldn't realize was embedded malware.

One thing I was thinking of was sort of a "delayed" mode to updating my own dependencies. The idea is that when I want to update my dependencies, instead of updating to the absolute latest version available of everything, it updates to versions that were released no more than some configurable amount of time ago. As a maintainer, I could decide that a package that's been out in the wild for at least 6 weeks is less likely to have unnoticed malware in it than one that was released just yesterday.

Obviously this is not a perfect fix, as there's no guarantee that the delay time I specify is enough for any particular package. And I'd want the tool to present me with options sometimes: e.g. if my current version of a dep has a vulnerability, and the fix for it came out a few days ago, I might choose to update to it (better eliminate the known vulnerability than refuse to update for fear of an unknown one) rather than wait until it's older than my threshold.

replies(35): >>45266995 #>>45267024 #>>45267360 #>>45267489 #>>45267600 #>>45267697 #>>45267722 #>>45267967 #>>45268218 #>>45268503 #>>45268654 #>>45268764 #>>45269143 #>>45269397 #>>45269398 #>>45269524 #>>45269799 #>>45269945 #>>45270082 #>>45270083 #>>45270420 #>>45270708 #>>45270917 #>>45270938 #>>45272063 #>>45272548 #>>45273074 #>>45273291 #>>45273321 #>>45273387 #>>45273513 #>>45273935 #>>45274324 #>>45275452 #>>45277692 #
gameman144 ◴[] No.45267024[source]
> It's not feasible for me to audit every single one of my dependencies, and every one of my dependencies' dependencies

I think this is a good argument for reducing your dependency count as much as possible, and keeping them to well-known and trustworthy (security-wise) creators.

"Not-invented-here" syndrome is counterproductive if you can trust all authors, but in an uncontrolled or unaudited ecosystem it's actually pretty sensible.

replies(8): >>45267054 #>>45267101 #>>45267444 #>>45268170 #>>45268880 #>>45270337 #>>45273381 #>>45273796 #
Ajedi32 ◴[] No.45267054[source]
If it's not feasible to audit every single dependency, it's probably even less feasible to rewrite every single dependency from scratch. Avoiding that duplicated work is precisely why we import dependencies in the first place.
replies(11): >>45267090 #>>45267094 #>>45267132 #>>45267222 #>>45267415 #>>45267471 #>>45268298 #>>45269164 #>>45270175 #>>45270363 #>>45270519 #
zelphirkalt ◴[] No.45267415[source]
Most dependencies do much more than we need from them. Often it means we only need one or a few functions from them. This means one doesn't need to rewrite whole dependencies usually. Don't use dependencies for things you can trivially write yourself, and use them for cases where it would be too much work to write yourself.
replies(3): >>45267701 #>>45271035 #>>45271065 #
btown ◴[] No.45267701[source]
A brief but important point is that this primarily holds true in the context of rewriting/vendoring utilities yourself, not when discussing importing small vs. large dependencies.

Just because dependencies do a lot more than you need, doesn't mean you should automatically reach for the smallest dependency that fits your needs.

If you need 5 of the dozens of Lodash functions, for instance, it might be best to just install Lodash and let your build step shake out any unused code, rather than importing 5 new dependencies, each with far fewer eyes and release-management best practices than the Lodash maintainers have.

replies(3): >>45268248 #>>45268399 #>>45269728 #
latexr ◴[] No.45269728[source]
The argument wasn’t to import five dependencies, one for each of the functions, but to write the five functions yourself. Heck, you don’t even need to literally write them, check the Lodash source and copy them to your code.
replies(3): >>45269898 #>>45269917 #>>45272318 #
1. cluckindan ◴[] No.45272318[source]
You have obviously never checked the Lodash source.
replies(1): >>45273560 #
2. latexr ◴[] No.45273560[source]
The point here isn’t a specific library. It’s not even one specific language or runtime. No one is talking about literally five functions. Let’s not be pedantic and lose sight of the major point.
replies(1): >>45273568 #
3. cluckindan ◴[] No.45273568[source]
I get that, but if you’ve ever tried to extract a single utility function from lodash, you know that it may not be as simple as copy-pasting a single function.
replies(1): >>45274322 #
4. zelphirkalt ◴[] No.45274322{3}[source]
If you are going to be that specific, then it would be good to post an example. If I remember correctly, lodash has some functions, that would be table stakes in functional languages, or easily built in functional languages. If such a function is difficult to extract, then it might be a good candidate to write in JS itself, which does have some of the typical tools, like map, reduce, and things like compose are easy to write oneself and part of every FP beginner tutorial. If such a function is difficult to extract, then perhaps lodash's design is not all that great. Maybe one could also copy them from elsewhere, where the code is more modular.

But again, if the discussion is going to be that specific, then you would need to provide actual examples, so that we could judge, whether we would implement that ourselves or it would be difficult to do so. Note, that often it is also not required for ones use-case, to have a 100% matching behavior either. The goal is not to duplicate lodash. The purpose of the extracted or reimplemented function would still be ones own project, where the job of that function might be much more limited.

replies(1): >>45275698 #
5. cluckindan ◴[] No.45275698{4}[source]
Let’s start with something simple, like difference().

https://github.com/lodash/lodash/blob/main/dist/lodash.js#L7...

So you also need to copy isArrayLikeObject, baseDifference and baseFlatten.

For baseDifference, you also need to copy arrayMap and baseUnary.

For baseFlatten, you also need to copy arrayPush.

For isArrayLikeObject, you also need to copy isArrayLike and isObjectLike.

For isArrayLike, you also need to copy isLength and isFunction.

For isFunction, you also need to copy isObject and baseGetTag.

For baseGetTag, you also need to copy getRawTag and objectToString.

I don’t have time to dig any deeper, just use tree-shaking ffs.

replies(1): >>45276479 #
6. zelphirkalt ◴[] No.45276479{5}[source]
OK in this case it looks like it is doing a lot of at runtime checking of arguments to treat them differently, based on what type of argument they are. If we restrict use to only work with arrays, or whatever we have in our project, where we need `difference`, then it should become much simpler and an easy rewrite. An alternative could be to have another argument, that is the function that gives us the `next` thing. Then the logic for that is to be specified by the caller.

Tree shaking however, will not help you, if you have to first install a library using NPM. It will only help you reduce overhead in the code served to a browser. Malicious code can run much earlier, and would be avoided, if you rewrite or extract relevant code from a library, avoiding to install the library using NPM. Or is there some pre-installation tree shaking, that I am unaware of? That would actually be interesting.

replies(1): >>45277142 #
7. cluckindan ◴[] No.45277142{6}[source]
I guess that pre-installation tree shaking in this case is installing ’lodash.difference’ instead of ’lodash’. :)