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
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:
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.
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