I am passionate about code, strong teamwork, and good conversation. Open to full-time, part-time and freelance roles, collaborative teams and impactful projects. Basically curious. Connect with me

Protective measures for recent shai hulud attacks

As the recent attacks are getting common, I did some changes and tried to prevent my ongoing apps that I am working on, as much as I can using some steps.

Before, we move forward, just to give context, I left using “npm” for my new projects some years back. I use “pnpm” mostly for my new projects. So, all of these methods are specifically for pnpm. You can search for similar steps in “npm” or can ignore this post if you dont.

I was building an app to learn sveltekit and it was almost done. Suddenly, I found that an attack happened on Aug 4th, specifically on the 2 packages, “keyv” and “cacheable”. Although, it happened few months back as well and I did take some measures in my ongoing apps. But, this measures are for the current app I am building.

My app’s config/tech:

  • Sveltekit
  • pnpm (v10)
  • sqlite
Steps I took:
Step 1:

Firstly, I checked if I have the versions that got attacked or not?

pnpm why keyv
pnpm why cacheable

Both of the commands will list if any of my installed packages have dependency on the mentioned pcakages and if yes, what is the version of the package installed. Fortunately, I had dependency in keyv v4.5.4 which is a safe one, but no dependency on cacheable. Passed in this step.

step 2:

I removed all the symbols of the installed packages in the package.json. For example, changed “typescript”: “^6.0.3” to “typescript”: “6.0.3”

I did this because, I want that if, in future, if I pull it from my repo and do a pnpm install, it will use the exact version of the package rather than some updated or patched version which might contain any amlicious code.

step 3:

I added some configuration in “.npmrc” file. This file helps in handling various things like enforcing strict installations. Although the recent attack tries to check this file as well because, this file can contain authentication tokens or environment defaults which is usually helpful in automated installations. But, I generally do not keep it there. Following are the changes that I made:

save-prefix=""
save-exact=true

This is only for strict installations that I talked earlier. It will help in avoiding the patches installations. It will only be applicable when we install through pnpm install/add command. pnpx/pnpm dlx command will install with ^ or ~ as it is installing via remote registry.

step 4:

Configured “pnpm-workspace.yaml”

packages:
- '.'
allowBuilds:
better-sqlite3: true
esbuild: true
overrides:
keyv: 4.5.4

This is one of the important part. Here, “allowBuilds” makes sure that only the allowed ones can run scripts.

As the recent attack works on this exact thing. When someone runs pnpm install, the malicious code starts its action. In postinstall or preinstall scripts. By default, pnpm prevents allowbuilds to false and only the allowed packages can run the scripts, which is esbuild and sqlite in my case. The override is for future prevention, if any pcakage I install in future, which is dependent on some version other than 4.5.4 of keyv, then it will override it with 4.5.4. This is temporary as of now until the current package is patched/fixed.

step 5:

I removed node modules and ran pnpm install command. If any package needs a script to be ran, the install command will fail and will show the exact package that is asking for scripts to run. If the package is safe, then you can add it in the allowBuilds section.

note:

I was working on pnpm v10, but before I implemented all of the steps, I upgraded to pnpm v11. This whole thing can be done in v10 as well, but workspace.yaml file changes are not required, rather all of the changes of this file is transferred to package.json file under a separate block called “pnpm”. Something like this:

//...rest of the package.json
"pnpm": {
"overrides": {
"keyv": 4.5.4
},
"onlyBuiltDependencies": [
"esbuild", "better-sqlite3"
]
}

This will act as same for my current config in v11. But, as my code was very much recent, I upgraded to v11. Also, even if it wasn’t I would have done it any way. Because, this seems a good setup. I like the concern separation. “.npmrc” and “workspace” have their own functionality in v11.

This might not be the perfect solution, but atleast some measures to prevent the attacks. Also, pnpm restricts the package installation, if a package is updated within 24 hours, which can be configured to 2 days as well. But the default is 24 hours that makes these kind of packages to not install which generally gets identified within 24 hours.

Let me know, if I should have taken any more steps. Curious to learn.

Leave a comment