Security Advisory | NPM Packages Using Secret Scanning Tools to Steal Credentials

A new npm supply chain worm Shai-Hulud was designed to install and use secret scanning tools to exfiltrate tokens and keys and then repeat.

September 15th, 2025
Share

We are aware of a number of compromised npm packages using a novel secret scanning tool method. We are investigating if any of our customers are impacted. We'll share additional analysis from our security research team about this latest compromise that may prove helpful.

  • 187+ packages discovered so far, the compromise uses a self-replicating worm though so we do expect this number to increase. We're updating Supply Chain rules as the signs of compromise are detected. See Compromised Package inventory below.

  • One initial compromised package (@ctrl/tinycolor) has > 8 million monthly downloads.

  • Extends beyond single namespace and includes @ngx, @nativescript-community, and more.

  • Steals credentials by fetching from the process environment (AWS keys, GitHub/NPM tokens, etc.) using Trufflehog to scan the filesystem and check if keys are valid.

  • Exfiltrates secrets to a webhook.site endpoint, and a public GitHub repo.

  • NPM is rapidly unpublishing compromised versions, but this attack behaves like a worm, scanning hosts for secrets, credentials and tokens. It then exfiltrates this plus environment variables and other system info creating a public repository called Shai-Hulud. Using GitHub actions it then uses a workflow to POST results to an attacker controlled website. If additional valid npm tokens are found, it will then enumerate packages and update them to further spread the malware.

  • Similar to recent the Nx incident, private repositories are being exposed as public migration copies.

Semgrep Supply Chain has released Semgrep Rules to detect the use of these vulnerable versions. Supply Chain customers can run scans to update Semgrep's record of their dependencies, then use findings filters to check whether they're vulnerable. If you see findings in that filter, we recommend upgrading to the latest safe version of the affected dependency or downgrading to the last safe version from before the attack took place. There are some additional triage and best practice recommendations to consider included below.

Security Advisory Updates

If additional guidance is necessary we’ll provide updates here as they are available.

2025-09-22 | 17:40PM PDT (12:40 AM UTC)

Updated blog to include updated findings filters

2025-09-22 | 11:49am PDT (18:49pm UTC)

Updated blog to include more indicator of compromises (IoCs)

2025-09-16 | 01:57am PDT (08:56am UTC)

Aikido is currently tracking additional packages affected in their malware tracker and their blog.

We updated the list of affected versions in this post and rules from discovery of additional waves of compromised packages. We are observing cases where multiple versions are malicious, not only a single released version.

2025-09-16 | 12:03am PDT (7:03am UTC)

Step Security published a blog post with a detailed technical analysis of the worm-like behavior to use any captured secrets to infect additional NPM packages.

https://www.stepsecurity.io/blog/ctrl-tinycolor-and-40-npm-packages-compromised

2025-09-15 | 04:23pm PDT (11:23pm UTC)

Socket Research published a blog post with a detailed technical analysis:

https://socket.dev/blog/tinycolor-supply-chain-attack-affects-40-packages

2025-09-15 | 3:45pm PDT (10:23pm UTC)

Reported to the tinycolor project: https://github.com/scttcper/tinycolor/issues/256

2025-09-15 | 11:11am PDT (06:41pm UTC)

Early posts on social media about suspicious activity.

Recommendations for Triage 

Additional steps you could take:

Review your GitHub Security Log

You can use the following GitHub search to find Shai-Hulud related repositories or the encoded exfiltration data. However as GitHub unindexs these search results, this is not enough to assume you have not been compromised.

You should review the https://github.com/settings/security-log for irregular activity within your organizations, looking for newly created repositories.

Check Logs for Activity

  • Review if you leveraged either @latest at the time of compromise, or utilized a compromised version from the list below.

  • Review npm logs (~/.npm/_logs), and review if the postinstall node bundle.js is executed

  • You can also look at your system to see if the bundle.js hash exists

    • find . -type f -name "*.js" -exec sha256sum {} \; | grep -E "de0e25a3e6c1e1e5998b306b7141b3dc4c0088da9d7bb47c1c00c91e6e4f85d6|81d2a004a1bca6ef87a1caf7d0e0b355ad1764238e40ff6d1b1cb77ad4f595c3|83a650ce44b2a9854802a7fb4c202877815274c129af49e6c2d1d5d5d55c501e|4b2399646573bb737c4969563303d8ee2e9ddbd1b271f1ca9e35ea78062538db|dc67467a39b70d1cd4c1f7f7a459b35058163592f4a9e8fb4dffcbba98ef210c|46faab8ab153fae6e80e7cca38eab363075bb524edd79e42269217a083628f09|b74caeaa75e077c99f7d44f46daaf9796a3be43ecf24f2a1fd381844669da777"

  • Review your system logs, and CI/CD build systems to verify no calls were made to webhook[.]site to the path bb8ca5f6-4175-45d2-b042-fc9ebb8170b7.

  • If you use endpoint software such as Crowdstrike, you can search for command line queries for the commands used in the exploit payload and domains queried during compromise

Rotate Credentials if Concerned

Rotate all credentials that were compromised and audit logs to determine if additional actions or persistence were attempted with the compromised tokens.

Remove Malicious Code

If you believe you are impacted you can remove the malicious package via:

  • rm -rf node_modules && npm cache clean --force

The shai-hulud-workflow.yml may be left behind as an artifact. Remove it if discovered.

rm .github/workflows/shai-hulud-workflow.yml

Additional NPM Registry Security Advice

Reducing Run Scripts

If your application doesn’t require them from dependencies regularly, you can ignore run-scripts and avoid executing arbitrary code in post install hooks. 

  • Executing builds with npm install –ignore-scripts

  • Setting ignore-scripts=true in .npmrc

Some packages do require installation steps though so this may not be feasible for all cases.

Maintainers Should Audit Publish History

If you maintain any packages on npm it may be a good idea to check your publishing history to ensure no unauthorized distribution of builds.

npm view <package-name> time

Consider Using pnpm

The pnpm tool has some benefits for performance and disk utilization. Added in the 10.16 release notes was a new minimumReleaseAge feature. It specifies the number of minutes a version should be live before considering it for installation. For instance, setting to a value of 1440 can ensure malicious dependencies have time to be caught before installed. There is also a minimumReleaseAgeExclude setting for exceptions where you really do want the latest.

Vulnerable Package Inventory

Here is a list of the packages and first versions known to be compromised.

@ahmedhfarag/ngx-perfect-scrollbar@20.0.20
@ahmedhfarag/ngx-virtual-scroller@4.0.4
@art-ws/common@2.0.28
@art-ws/config-eslint@2.0.4-2.0.5
@art-ws/config-ts@2.0.7-2.0.8
@art-ws/db-context@2.0.24
@art-ws/di@2.0.28,2.0.32
@art-ws/di-node@2.0.13
@art-ws/eslint@1.0.5-1.0.6
@art-ws/fastify-http-server@2.0.24,2.0.27
@art-ws/http-server@2.0.21,2.0.25
@art-ws/openapi@0.1.9,0.1.12
@art-ws/package-base@1.0.5-1.0.6
@art-ws/prettier@1.0.5-1.0.6
@art-ws/slf@2.0.15,2.0.22
@art-ws/ssl-info@1.0.9-1.0.10
@art-ws/web-app@1.0.3-1.0.4
@crowdstrike/commitlint@8.1.1-8.1.2
@crowdstrike/falcon-shoelace@0.4.1-0.4.2
@crowdstrike/foundry-js@0.19.1-0.19.2
@crowdstrike/glide-core@0.34.2-0.34.3
@crowdstrike/logscale-dashboard@1.205.1-1.205.2
@crowdstrike/logscale-file-editor@1.205.1-1.205.2
@crowdstrike/logscale-parser-edit@1.205.1-1.205.2
@crowdstrike/logscale-search@1.205.1-1.205.2
@crowdstrike/tailwind-toucan-base@5.0.1-5.0.2
@ctrl/deluge@7.2.1-7.2.2
@ctrl/golang-template@1.4.2-1.4.3
@ctrl/magnet-link@4.0.3-4.0.4
@ctrl/ngx-codemirror@7.0.1-7.0.2
@ctrl/ngx-csv@6.0.1-6.0.2
@ctrl/ngx-emoji-mart@9.2.1-9.2.2
@ctrl/ngx-rightclick@4.0.1-4.0.2
@ctrl/qbittorrent@9.7.1-9.7.2
@ctrl/react-adsense@2.0.1-2.0.2
@ctrl/shared-torrent@6.3.1-6.3.2
@ctrl/tinycolor@4.1.1-4.1.2
@ctrl/torrent-file@4.1.1-4.1.2
@ctrl/transmission@7.3.1
@ctrl/ts-base32@4.0.1-4.0.2
@hestjs/core@0.2.1
@hestjs/cqrs@0.1.6
@hestjs/demo@0.1.2
@hestjs/eslint-config@0.1.2
@hestjs/logger@0.1.6
@hestjs/scalar@0.1.7
@hestjs/validation@0.1.6
@nativescript-community/arraybuffers@1.1.6-1.1.8
@nativescript-community/gesturehandler@2.0.35
@nativescript-community/perms@3.0.5-3.0.8
@nativescript-community/sqlite@3.5.2-3.5.5
@nativescript-community/text@1.6.9-1.6.12
@nativescript-community/typeorm@0.2.30-0.2.33
@nativescript-community/ui-collectionview@6.0.6
@nativescript-community/ui-document-picker@1.1.27-1.1.28
@nativescript-community/ui-drawer@0.1.30
@nativescript-community/ui-image@4.5.6
@nativescript-community/ui-label@1.3.35, 1.3.36, 1.3.37
@nativescript-community/ui-material-bottom-navigation@7.2.72-7.2.75
@nativescript-community/ui-material-bottomsheet@7.2.72
@nativescript-community/ui-material-core@7.2.72-7.2.75
@nativescript-community/ui-material-core-tabs@7.2.72-7.2.75
@nativescript-community/ui-material-ripple@7.2.72-7.2.75
@nativescript-community/ui-material-tabs@7.2.72-7.2.75
@nativescript-community/ui-pager@14.1.36-14.1.38
@nativescript-community/ui-pulltorefresh@2.5.4-2.5.7
@nexe/config-manager@0.1.1
@nexe/eslint-config@0.1.1
@nexe/logger@0.1.3
@nstudio/angular@20.0.4-20.0.6
@nstudio/focus@20.0.4-20.0.6
@nstudio/nativescript-checkbox@2.0.6-2.0.9
@nstudio/nativescript-loading-indicator@5.0.1-5.0.4
@nstudio/ui-collectionview@5.1.11-5.1.14
@nstudio/web@20.0.4
@nstudio/web-angular@20.0.4
@nstudio/xplat@20.0.5-20.0.7
@nstudio/xplat-utils@20.0.5-20.0.7
@operato/board@9.0.36-9.0.46
@operato/data-grist@9.0.29,9.0.35-9.0.37
@operato/graphql@9.0.22,9.0.35-9.0.46
@operato/headroom@9.0.2,9.0.35-9.0.37
@operato/help@9.0.35-9.0.46
@operato/i18n@9.0.35-9.0.37
@operato/input@9.0.27,9.0.35-9.0.46
@operato/layout@9.0.35-9.0.37
@operato/popup@9.0.22,9.0.35-9.0.46
@operato/pull-to-refresh@9.0.36-9.0.42
@operato/shell@9.0.22,9.0.35-9.0.39
@operato/styles@9.0.2,9.0.35-9.0.37
@operato/utils@9.0.22,9.0.35-9.0.46
@teselagen/bounce-loader@0.3.16-0.3.17
@teselagen/liquibase-tools@0.4.1
@teselagen/range-utils@0.3.14-0.3.15
@teselagen/react-list@0.8.19-0.8.20
@teselagen/react-table@6.10.19
@thangved/callback-window@1.1.4
@things-factory/attachment-base@9.0.43-9.0.50
@things-factory/auth-base@9.0.43-9.0.45
@things-factory/email-base@9.0.42-9.0.54
@things-factory/env@9.0.42-9.0.45
@things-factory/integration-base@9.0.43-9.0.45
@things-factory/integration-marketplace@9.0.43-9.0.45
@things-factory/shell@9.0.43-9.0.45
@tnf-dev/api@1.0.8
@tnf-dev/core@1.0.8
@tnf-dev/js@1.0.8
@tnf-dev/mui@1.0.8
@tnf-dev/react@1.0.8
@ui-ux-gang/devextreme-angular-rpk@24.1.7
@yoobic/design-system@6.5.17
@yoobic/jpeg-camera-es6@1.0.13
@yoobic/yobi@8.7.53
airchief@0.3.1
airpilot@0.8.8
angulartics2@14.1.1-14.1.2
browser-webdriver-downloader@3.0.8
capacitor-notificationhandler@0.0.2-0.0.3
capacitor-plugin-healthapp@0.0.2-0.0.3
capacitor-plugin-ihealth@1.1.8-1.1.9
capacitor-plugin-vonage@1.0.2-1.0.3
capacitorandroidpermissions@0.0.4-0.0.5
config-cordova@0.8.5
cordova-plugin-voxeet2@1.0.24
cordova-voxeet@1.0.32
create-hest-app@0.1.9
db-evo@1.1.4-1.1.5
devextreme-angular-rpk@21.2.8
ember-browser-services@5.0.2-5.0.3
ember-headless-form@1.1.2-1.1.3
ember-headless-form-yup@1.0.1
ember-headless-table@2.1.5-2.1.6
ember-url-hash-polyfill@1.0.12-1.0.13
ember-velcro@2.2.1-2.2.2
encounter-playground@0.0.2-0.0.5
eslint-config-crowdstrike@11.0.2-11.0.3
eslint-config-crowdstrike-node@4.0.3-4.0.4
eslint-config-teselagen@6.1.7
globalize-rpk@1.7.4
graphql-sequelize-teselagen@5.3.8
html-to-base64-image@1.0.2
json-rules-engine-simplified@0.2.1
jumpgate@0.0.2
koa2-swagger-ui@5.11.1-5.11.2
mcfly-semantic-release@1.3.1
mcp-knowledge-base@0.0.2
mcp-knowledge-graph@1.2.1
mobioffice-cli@1.0.3
monorepo-next@13.0.1-13.0.2
mstate-angular@0.4.4
mstate-cli@0.4.7
mstate-dev-react@1.1.1
mstate-react@1.6.5
ng2-file-upload@7.0.2-7.0.3,8.0.1-8.0.3,9.0.1
ngx-bootstrap@18.1.4,19.0.3-19.0.4,20.0.3-20.0.5
ngx-color@10.0.1-10.0.2
ngx-toastr@19.0.1-19.0.2
ngx-trend@8.0.1
ngx-ws@1.1.5-1.1.6
oradm-to-gql@35.0.14-35.0.15
oradm-to-sqlz@1.1.2
ove-auto-annotate@0.0.9
pm2-gelf-json@1.0.4-1.0.5
printjs-rpk@1.6.1
react-complaint-image@0.0.32
react-jsonschema-form-conditionals@0.3.18
remark-preset-lint-crowdstrike@4.0.1-4.0.2
rxnt-authentication@0.0.3-0.0.6
rxnt-healthchecks-nestjs@1.0.2-1.0.5
rxnt-kue@1.0.4-1.0.7
swc-plugin-component-annotate@1.9.1-1.9.2
tbssnch@1.0.2
teselagen-interval-tree@1.1.2
tg-client-query-builder@2.14.4-2.14.5
tg-redbird@1.3.1
tg-seq-gen@1.0.9-1.0.10
thangved-react-grid@1.0.3
ts-gaussian@3.0.5-3.0.6
ts-imports@1.0.1-1.0.2
tvi-cli@0.1.5
ve-bamreader@0.2.6
ve-editor@1.0.1
verror-extra@6.0.1
voip-callkit@1.0.2, 1.0.3
wdio-web-reporter@0.1.3
yargs-help-output@5.0.3
yoo-styles@6.0.326

About

semgrep logo

Semgrep enables teams to use industry-leading AI-assisted static application security testing (SAST), supply chain dependency scanning (SCA), and secrets detection. The Semgrep AppSec Platform is built for teams that struggle with noise by helping development teams apply secure coding practices.