Beware when merging Pull Requests with a changed lockfile

When watching a diff that contains a lockfile (say: a yarn.lock for example) on GitHub, GitHub doesn’t always show the differences (see screenshot above) as the changes in such files tend to be quite big. And even if it were to show the changes, does one really take a close look into it? With this in mind, Liran Tal started playing around to create a vector of attack using those lock files.

Take this diff for example:

What becomes clear when you look closer, is that I replaced the original ms npm package to resolve it with my own version, which is stored in my GitHub repository. I should have gotten it from the official npm registry, just as was originally set in the lockfile of the project.

When this pull request gets merged, I inject my malicious version of [email protected] into the code in order to control its behavior during runtime.

In this way, I could introduce a backdoor, alter the logic of the ms module or I could run some postinstall scripts.

To prevent such commits from being merged, you can resort to lockfile-lint which will warn you for such issues.

As an end-user it’s wise to run npm install with --ignore-scripts.

Why npm lockfiles can be a security blindspot for injecting malicious modules →

Use a Github repository branch or commit as a dependency in package.json

Recently I needed to test a branch of a forked GitHub repository inside a project. Instead of cloning the fork and symlinking the package locally, I installed the remote dependency directly into the project.

To achieve I used the following command:

Using NPM:

npm install user/repo.git#branchname

Using Yarn:

yarn add ssh://[email protected]:user/repo.git#branchname

💡 If you’re targeting a specific commit or tag, replace branchname with the commmithash or tagname

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

☕️ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

Why I’m excited about Yarn

yarn-kitten-full

Today, Facebook – in collaboration with Google and others – released Yarn, a new package manager for JavaScript.

In comparison to npm, the Yarn website pushes these three main benefits forwards:

  1. Speed
  2. Reliability
  3. Security

Not in that list, and something that I’m really super excited about, is the fact that Yarn is architected in such a way that it basically is a drop-in replacement for npm (the binary) itself. Yarn does not bring extra fatigue to the JavaScript game:

  • You don’t need to learn an entirely new (*) syntax (unlike the switch from grunt to gulp to webpack).
  • You don’t need to find your packages on a different repository/website (unlike the switch from bower to npm).
  • You don’t need to change your directory structure (Yarn still uses package.json and puts everything in the node_modules subfolder).

For us, the end user, not much changes. Everything that was, still is. From a UX perspective that can count.

unix-system
Working with Yarn: “It’s a Unix System, I know this!”

(*) I know. The syntax is a tad different, but not that much: Once yarn is installed, just run yarn (or yarn install) instead of npm install. To add a package to your package.json run yarn add packagename (instead of npm i packagename --save). Slightly different indeed, but nothing big.

Of course I’m also excited about the initial benefits listed. Take reliability for example. Coming from a PHP background – where we have Composer for dependency management – I applaud the fact that Yarn – like Composer – automatically generates lock files.

Thanks to the yarn.lock file you can no longer end up with different versions of dependencies on different machines should a new version of a dependency be released in between two runs of yarn install. The lock file, as its name indicates, locks the dependencies down to exact versions (cfr. npm shrinkwrap, but then by default and on steroids). That way you have reproducible installs, on all machines.

The magic clue behind it? Whenever you run yarn install, the yarn.lock file has precedence over the package.json.

  • If the yarn.lock file exists, the (exact) versions defined in it will be used.
  • If no yarn.lock exists, the (loosely defined) versions defined in package.json will be used, and a yarn.lock is generated.

That way you, as a developer, have true control over which exact versions will be installed on every machine. As long as you commit your yarn.lock file into Git (or whatever VCS floats your boat) of course.

Let me rephrase and repeat that for you, as it’s really important: you must commit the yarn.lock file into Git.

To update the versions locked in the yarn.lock file run yarn upgrade. After having verified that your project Works On My Machine™, it’s safe to commit the updated lock file into Git.

Oh, and about the aforementioned speed benefit (thanks to parallelization and the use of a global cache on disk), let this tweet convince you:

So, what are you waiting for?

Facebook Engineering Blog: Yarn: A new package manager for JavaScript →
Yarn – Fast, reliable, and secure dependency management →
The npm blog: Hello, Yarn! →

This post has also been cross-posted to Medium. If you like it, feel free to give it a <3 on there.