Skip to content

Instantly share code, notes, and snippets.

@cgrothaus
Last active September 22, 2025 06:06
Show Gist options
  • Select an option

  • Save cgrothaus/d421a38a713cf40c2e51933901afc341 to your computer and use it in GitHub Desktop.

Select an option

Save cgrothaus/d421a38a713cf40c2e51933901afc341 to your computer and use it in GitHub Desktop.
Script to check for compromised npm packages (npm supply chain attack of September 2025)
#!/bin/zsh
#
# Script to check for compromised npm packages in monorepos using pnpm, npm, or yarn.
# - Detects package manager from root lock file and checks all sub-projects.
# - Properly handles transitive dependencies.
# - Hardcoded list of compromised packages and versions.
#
# Usage: Run this script from the root of your monorepo with `zsh check-compromised-npm-packages.zsh`
#
# Background: news on supply chain attacks in npm packages of September 2025
# 1.
# - https://www.aikido.dev/blog/npm-debug-and-chalk-packages-compromised
# - https://news.ycombinator.com/item?id=45169794
# 2.
# - https://socket.dev/blog/tinycolor-supply-chain-attack-affects-40-packages
# 3.
# - https://www.ox.security/blog/npm-2-0-hack-40-npm-packages-hit-in-major-supply-chain-attack/
# - https://www.aikido.dev/blog/s1ngularity-nx-attackers-strike-again
# 4.
# - https://socket.dev/blog/ongoing-supply-chain-attack-targets-crowdstrike-npm-packages
# List of compromised packages and versions
# Format: package:version
compromised=(
# first attack compromised packages:
"@duckdb/node-api:1.3.3"
"@duckdb/node-bindings:1.3.3"
"ansi-regex:6.2.1"
"ansi-styles:6.2.2"
"backslash:0.2.1"
"chalk-template:1.1.1"
"chalk:5.6.1"
"color-convert:3.1.1"
"color-name:2.0.1"
"color-string:2.1.1"
"color:5.0.1"
"debug:4.4.2"
"duckdb:1.3.3"
"error-ex:1.3.3"
"has-ansi:6.0.1"
"is-arrayish:0.3.3"
"simple-swizzle:0.2.3"
"slice-ansi:7.1.1"
"strip-ansi:7.1.1"
"supports-color:10.2.1"
"supports-hyperlinks:4.1.1"
"wrap-ansi:9.0.1"
# second attack compromised packages:
"@ctrl/deluge:7.2.2"
"@ctrl/golang-template:1.4.3"
"@ctrl/magnet-link:4.0.4"
"@ctrl/ngx-codemirror:7.0.2"
"@ctrl/ngx-csv:6.0.2"
"@ctrl/ngx-emoji-mart:9.2.2"
"@ctrl/ngx-rightclick:4.0.2"
"@ctrl/qbittorrent:9.7.2"
"@ctrl/react-adsense:2.0.2"
"@ctrl/shared-torrent:6.3.2"
"@ctrl/tinycolor:4.1.1"
"@ctrl/tinycolor:4.1.2"
"@ctrl/torrent-file:4.1.2"
"@ctrl/transmission:7.3.1"
"@ctrl/ts-base32:4.0.2"
"@nativescript-community/gesturehandler:2.0.35"
"@nativescript-community/sentry:4.6.43"
"@nativescript-community/text:1.6.13"
"@nativescript-community/ui-collectionview:6.0.6"
"@nativescript-community/ui-drawer:0.1.30"
"@nativescript-community/ui-image:4.5.6"
"@nativescript-community/ui-material-bottomsheet:7.2.72"
"@nativescript-community/ui-material-core-tabs:7.2.76"
"@nativescript-community/ui-material-core:7.2.76"
"angulartics2:14.1.2"
"encounter-playground:0.0.5"
"json-rules-engine-simplified:0.2.1"
"json-rules-engine-simplified:0.2.4"
"koa2-swagger-ui:5.11.1"
"koa2-swagger-ui:5.11.2"
"ngx-color:10.0.2"
"ngx-toastr:19.0.2"
"ngx-trend:8.0.1"
"react-complaint-image:0.0.35"
"react-jsonschema-form-conditionals:0.3.21"
"react-jsonschema-form-extras:1.0.4"
"rxnt-authentication:0.0.6"
"rxnt-healthchecks-nestjs:1.0.5"
"rxnt-kue:1.0.7"
"swc-plugin-component-annotate:1.9.2"
"ts-gaussian:3.0.6"
# third attack compromised packages:
"@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"
"@art-ws/config-eslint:2.0.5"
"@art-ws/config-ts:2.0.7"
"@art-ws/config-ts:2.0.8"
"@art-ws/db-context:2.0.24"
"@art-ws/di:2.0.28"
"@art-ws/di:2.0.32"
"@art-ws/di-node:2.0.13"
"@art-ws/eslint:1.0.5"
"@art-ws/eslint:1.0.6"
"@art-ws/fastify-http-server:2.0.24"
"@art-ws/fastify-http-server:2.0.27"
"@art-ws/http-server:2.0.21"
"@art-ws/http-server:2.0.25"
"@art-ws/openapi:0.1.9"
"@art-ws/openapi:0.1.12"
"@art-ws/package-base:1.0.5"
"@art-ws/package-base:1.0.6"
"@art-ws/prettier:1.0.5"
"@art-ws/prettier:1.0.6"
"@art-ws/slf:2.0.15"
"@art-ws/slf:2.0.22"
"@art-ws/ssl-info:1.0.9"
"@art-ws/ssl-info:1.0.10"
"@art-ws/web-app:1.0.3"
"@art-ws/web-app:1.0.4"
"@crowdstrike/commitlint:8.1.1"
"@crowdstrike/commitlint:8.1.2"
"@crowdstrike/falcon-shoelace:0.4.1"
"@crowdstrike/falcon-shoelace:0.4.2"
"@crowdstrike/foundry-js:0.19.1"
"@crowdstrike/foundry-js:0.19.2"
"@crowdstrike/glide-core:0.34.2"
"@crowdstrike/glide-core:0.34.3"
"@crowdstrike/logscale-dashboard:1.205.1"
"@crowdstrike/logscale-dashboard:1.205.2"
"@crowdstrike/logscale-file-editor:1.205.1"
"@crowdstrike/logscale-file-editor:1.205.2"
"@crowdstrike/logscale-parser-edit:1.205.1"
"@crowdstrike/logscale-parser-edit:1.205.2"
"@crowdstrike/logscale-search:1.205.1"
"@crowdstrike/logscale-search:1.205.2"
"@crowdstrike/tailwind-toucan-base:5.0.1"
"@crowdstrike/tailwind-toucan-base:5.0.2"
"@ctrl/deluge:7.2.1"
"@ctrl/golang-template:1.4.2"
"@ctrl/magnet-link:4.0.3"
"@ctrl/ngx-codemirror:7.0.1"
"@ctrl/ngx-csv:6.0.1"
"@ctrl/ngx-emoji-mart:9.2.1"
"@ctrl/ngx-rightclick:4.0.1"
"@ctrl/qbittorrent:9.7.1"
"@ctrl/react-adsense:2.0.1"
"@ctrl/shared-torrent:6.3.1"
"@ctrl/torrent-file:4.1.1"
"@ctrl/ts-base32:4.0.1"
"@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"
"@nativescript-community/arraybuffers:1.1.7"
"@nativescript-community/arraybuffers:1.1.8"
"@nativescript-community/perms:3.0.5"
"@nativescript-community/perms:3.0.6"
"@nativescript-community/perms:3.0.7"
"@nativescript-community/perms:3.0.8"
"@nativescript-community/perms:3.0.9"
"@nativescript-community/sqlite:3.5.2"
"@nativescript-community/sqlite:3.5.3"
"@nativescript-community/sqlite:3.5.4"
"@nativescript-community/sqlite:3.5.5"
"@nativescript-community/text:1.6.9"
"@nativescript-community/text:1.6.10"
"@nativescript-community/text:1.6.11"
"@nativescript-community/text:1.6.12"
"@nativescript-community/typeorm:0.2.30"
"@nativescript-community/typeorm:0.2.31"
"@nativescript-community/typeorm:0.2.32"
"@nativescript-community/typeorm:0.2.33"
"@nativescript-community/ui-document-picker:1.1.27"
"@nativescript-community/ui-document-picker:1.1.28"
"@nativescript-community/ui-label:1.3.35"
"@nativescript-community/ui-label:1.3.36"
"@nativescript-community/ui-label:1.3.37"
"@nativescript-community/ui-material-bottom-navigation:7.2.72"
"@nativescript-community/ui-material-bottom-navigation:7.2.73"
"@nativescript-community/ui-material-bottom-navigation:7.2.74"
"@nativescript-community/ui-material-bottom-navigation:7.2.75"
"@nativescript-community/ui-material-core:7.2.72"
"@nativescript-community/ui-material-core:7.2.73"
"@nativescript-community/ui-material-core:7.2.74"
"@nativescript-community/ui-material-core:7.2.75"
"@nativescript-community/ui-material-core-tabs:7.2.72"
"@nativescript-community/ui-material-core-tabs:7.2.73"
"@nativescript-community/ui-material-core-tabs:7.2.74"
"@nativescript-community/ui-material-core-tabs:7.2.75"
"@nativescript-community/ui-material-ripple:7.2.72"
"@nativescript-community/ui-material-ripple:7.2.73"
"@nativescript-community/ui-material-ripple:7.2.74"
"@nativescript-community/ui-material-ripple:7.2.75"
"@nativescript-community/ui-material-tabs:7.2.72"
"@nativescript-community/ui-material-tabs:7.2.73"
"@nativescript-community/ui-material-tabs:7.2.74"
"@nativescript-community/ui-material-tabs:7.2.75"
"@nativescript-community/ui-pager:14.1.36"
"@nativescript-community/ui-pager:14.1.37"
"@nativescript-community/ui-pager:14.1.38"
"@nativescript-community/ui-pager:14.1.35"
"@nativescript-community/ui-pulltorefresh:2.5.4"
"@nativescript-community/ui-pulltorefresh:2.5.5"
"@nativescript-community/ui-pulltorefresh:2.5.6"
"@nativescript-community/ui-pulltorefresh: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"
"@nstudio/angular:20.0.5"
"@nstudio/angular:20.0.6"
"@nstudio/focus:20.0.4"
"@nstudio/focus:20.0.5"
"@nstudio/focus:20.0.6"
"@nstudio/nativescript-checkbox:2.0.6"
"@nstudio/nativescript-checkbox:2.0.7"
"@nstudio/nativescript-checkbox:2.0.8"
"@nstudio/nativescript-checkbox:2.0.9"
"@nstudio/nativescript-loading-indicator:5.0.1"
"@nstudio/nativescript-loading-indicator:5.0.2"
"@nstudio/nativescript-loading-indicator:5.0.3"
"@nstudio/nativescript-loading-indicator:5.0.4"
"@nstudio/ui-collectionview:5.1.11"
"@nstudio/ui-collectionview:5.1.12"
"@nstudio/ui-collectionview:5.1.13"
"@nstudio/ui-collectionview:5.1.14"
"@nstudio/web:20.0.4"
"@nstudio/web-angular:20.0.4"
"@nstudio/xplat:20.0.5"
"@nstudio/xplat:20.0.6"
"@nstudio/xplat:20.0.7"
"@nstudio/xplat:20.0.4"
"@nstudio/xplat-utils:20.0.5"
"@nstudio/xplat-utils:20.0.6"
"@nstudio/xplat-utils:20.0.7"
"@nstudio/xplat-utils:20.0.4"
"@operato/board:9.0.36"
"@operato/board:9.0.37"
"@operato/board:9.0.38"
"@operato/board:9.0.39"
"@operato/board:9.0.40"
"@operato/board:9.0.41"
"@operato/board:9.0.42"
"@operato/board:9.0.43"
"@operato/board:9.0.44"
"@operato/board:9.0.45"
"@operato/board:9.0.46"
"@operato/data-grist:9.0.29"
"@operato/data-grist:9.0.35"
"@operato/data-grist:9.0.36"
"@operato/data-grist:9.0.37"
"@operato/graphql:9.0.22"
"@operato/graphql:9.0.35"
"@operato/graphql:9.0.36"
"@operato/graphql:9.0.37"
"@operato/graphql:9.0.38"
"@operato/graphql:9.0.39"
"@operato/graphql:9.0.40"
"@operato/graphql:9.0.41"
"@operato/graphql:9.0.42"
"@operato/graphql:9.0.43"
"@operato/graphql:9.0.44"
"@operato/graphql:9.0.45"
"@operato/graphql:9.0.46"
"@operato/headroom:9.0.2"
"@operato/headroom:9.0.35"
"@operato/headroom:9.0.36"
"@operato/headroom:9.0.37"
"@operato/help:9.0.35"
"@operato/help:9.0.36"
"@operato/help:9.0.37"
"@operato/help:9.0.38"
"@operato/help:9.0.39"
"@operato/help:9.0.40"
"@operato/help:9.0.41"
"@operato/help:9.0.42"
"@operato/help:9.0.43"
"@operato/help:9.0.44"
"@operato/help:9.0.45"
"@operato/help:9.0.46"
"@operato/i18n:9.0.35"
"@operato/i18n:9.0.36"
"@operato/i18n:9.0.37"
"@operato/input:9.0.27"
"@operato/input:9.0.35"
"@operato/input:9.0.36"
"@operato/input:9.0.37"
"@operato/input:9.0.38"
"@operato/input:9.0.39"
"@operato/input:9.0.40"
"@operato/input:9.0.41"
"@operato/input:9.0.42"
"@operato/input:9.0.43"
"@operato/input:9.0.44"
"@operato/input:9.0.45"
"@operato/input:9.0.46"
"@operato/layout:9.0.35"
"@operato/layout:9.0.36"
"@operato/layout:9.0.37"
"@operato/popup:9.0.22"
"@operato/popup:9.0.35"
"@operato/popup:9.0.36"
"@operato/popup:9.0.37"
"@operato/popup:9.0.38"
"@operato/popup:9.0.39"
"@operato/popup:9.0.40"
"@operato/popup:9.0.41"
"@operato/popup:9.0.42"
"@operato/popup:9.0.43"
"@operato/popup:9.0.44"
"@operato/popup:9.0.45"
"@operato/popup:9.0.46"
"@operato/pull-to-refresh:9.0.36"
"@operato/pull-to-refresh:9.0.37"
"@operato/pull-to-refresh:9.0.38"
"@operato/pull-to-refresh:9.0.39"
"@operato/pull-to-refresh:9.0.40"
"@operato/pull-to-refresh:9.0.41"
"@operato/pull-to-refresh:9.0.42"
"@operato/shell:9.0.22"
"@operato/shell:9.0.35"
"@operato/shell:9.0.36"
"@operato/shell:9.0.37"
"@operato/shell:9.0.38"
"@operato/shell:9.0.39"
"@operato/styles:9.0.2"
"@operato/styles:9.0.35"
"@operato/styles:9.0.36"
"@operato/styles:9.0.37"
"@operato/utils:9.0.22"
"@operato/utils:9.0.35"
"@operato/utils:9.0.36"
"@operato/utils:9.0.37"
"@operato/utils:9.0.38"
"@operato/utils:9.0.39"
"@operato/utils:9.0.40"
"@operato/utils:9.0.41"
"@operato/utils:9.0.42"
"@operato/utils:9.0.43"
"@operato/utils:9.0.44"
"@operato/utils:9.0.45"
"@operato/utils:9.0.46"
"@teselagen/bounce-loader:0.3.16"
"@teselagen/bounce-loader:0.3.17"
"@teselagen/liquibase-tools:0.4.1"
"@teselagen/range-utils:0.3.14"
"@teselagen/range-utils:0.3.15"
"@teselagen/react-list:0.8.19"
"@teselagen/react-list:0.8.20"
"@teselagen/react-table:6.10.19"
"@teselagen/react-table:6.10.21"
"@thangved/callback-window:1.1.4"
"@things-factory/attachment-base:9.0.43"
"@things-factory/attachment-base:9.0.44"
"@things-factory/attachment-base:9.0.45"
"@things-factory/attachment-base:9.0.46"
"@things-factory/attachment-base:9.0.47"
"@things-factory/attachment-base:9.0.48"
"@things-factory/attachment-base:9.0.49"
"@things-factory/attachment-base:9.0.50"
"@things-factory/auth-base:9.0.43"
"@things-factory/auth-base:9.0.44"
"@things-factory/auth-base:9.0.45"
"@things-factory/email-base:9.0.42"
"@things-factory/email-base:9.0.43"
"@things-factory/email-base:9.0.44"
"@things-factory/email-base:9.0.45"
"@things-factory/email-base:9.0.46"
"@things-factory/email-base:9.0.47"
"@things-factory/email-base:9.0.48"
"@things-factory/email-base:9.0.49"
"@things-factory/email-base:9.0.50"
"@things-factory/email-base:9.0.51"
"@things-factory/email-base:9.0.52"
"@things-factory/email-base:9.0.53"
"@things-factory/email-base:9.0.54"
"@things-factory/env:9.0.42"
"@things-factory/env:9.0.43"
"@things-factory/env:9.0.44"
"@things-factory/env:9.0.45"
"@things-factory/integration-base:9.0.43"
"@things-factory/integration-base:9.0.44"
"@things-factory/integration-base:9.0.45"
"@things-factory/integration-marketplace:9.0.43"
"@things-factory/integration-marketplace:9.0.44"
"@things-factory/integration-marketplace:9.0.45"
"@things-factory/shell:9.0.43"
"@things-factory/shell:9.0.44"
"@things-factory/shell: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"
"browser-webdriver-downloader:3.0.8"
"capacitor-notificationhandler:0.0.2"
"capacitor-notificationhandler:0.0.3"
"capacitor-plugin-healthapp:0.0.2"
"capacitor-plugin-healthapp:0.0.3"
"capacitor-plugin-ihealth:1.1.8"
"capacitor-plugin-ihealth:1.1.9"
"capacitor-plugin-vonage:1.0.2"
"capacitor-plugin-vonage:1.0.3"
"capacitorandroidpermissions:0.0.4"
"capacitorandroidpermissions: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"
"db-evo:1.1.5"
"devextreme-angular-rpk:21.2.8"
"ember-browser-services:5.0.2"
"ember-browser-services:5.0.3"
"ember-headless-form:1.1.2"
"ember-headless-form:1.1.3"
"ember-headless-form-yup:1.0.1"
"ember-headless-table:2.1.5"
"ember-headless-table:2.1.6"
"ember-url-hash-polyfill:1.0.12"
"ember-url-hash-polyfill:1.0.13"
"ember-velcro:2.2.1"
"ember-velcro:2.2.2"
"encounter-playground:0.0.2"
"encounter-playground:0.0.3"
"encounter-playground:0.0.4"
"eslint-config-crowdstrike:11.0.2"
"eslint-config-crowdstrike:11.0.3"
"eslint-config-crowdstrike-node:4.0.3"
"eslint-config-crowdstrike-node: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.3"
"json-rules-engine-simplified:0.2.2"
"jumpgate:0.0.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"
"monorepo-next: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"
"ng2-file-upload:7.0.3"
"ng2-file-upload:8.0.1"
"ng2-file-upload:8.0.2"
"ng2-file-upload:8.0.3"
"ng2-file-upload:9.0.1"
"ngx-bootstrap:18.1.4"
"ngx-bootstrap:19.0.3"
"ngx-bootstrap:19.0.4"
"ngx-bootstrap:20.0.3"
"ngx-bootstrap:20.0.4"
"ngx-bootstrap:20.0.5"
"ngx-bootstrap:20.0.6"
"ngx-color:10.0.1"
"ngx-toastr:19.0.1"
"ngx-ws:1.1.5"
"ngx-ws:1.1.6"
"oradm-to-gql:35.0.14"
"oradm-to-gql:35.0.15"
"oradm-to-sqlz:1.1.2"
"oradm-to-sqlz:1.1.3"
"oradm-to-sqlz:1.1.4"
"ove-auto-annotate:0.0.9"
"pm2-gelf-json:1.0.4"
"pm2-gelf-json:1.0.5"
"printjs-rpk:1.6.1"
"react-complaint-image:0.0.32"
"react-complaint-image:0.0.33"
"react-complaint-image:0.0.34"
"react-jsonschema-form-conditionals:0.3.18"
"react-jsonschema-form-conditionals:0.3.19"
"react-jsonschema-form-conditionals:0.3.20"
"remark-preset-lint-crowdstrike:4.0.1"
"remark-preset-lint-crowdstrike:4.0.2"
"rxnt-authentication:0.0.3"
"rxnt-authentication:0.0.4"
"rxnt-authentication:0.0.5"
"rxnt-healthchecks-nestjs:1.0.2"
"rxnt-healthchecks-nestjs:1.0.3"
"rxnt-healthchecks-nestjs:1.0.4"
"rxnt-kue:1.0.4"
"rxnt-kue:1.0.5"
"rxnt-kue:1.0.6"
"swc-plugin-component-annotate:1.9.1"
"tbssnch:1.0.2"
"teselagen-interval-tree:1.1.2"
"tg-client-query-builder:2.14.4"
"tg-client-query-builder:2.14.5"
"tg-redbird:1.3.1"
"tg-seq-gen:1.0.9"
"tg-seq-gen:1.0.10"
"thangved-react-grid:1.0.3"
"ts-gaussian:3.0.5"
"ts-imports:1.0.1"
"ts-imports: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"
"voip-callkit:1.0.3"
"wdio-web-reporter:0.1.3"
"yargs-help-output:5.0.3"
"yoo-styles:6.0.326"
# fourth article compromised packages:
"@operato/board:9.0.35"
"@operato/board:9.0.47"
"@operato/board:9.0.48"
"@operato/board:9.0.49"
"@operato/board:9.0.50"
"@operato/board:9.0.51"
"@operato/graphql:9.0.47"
"@operato/graphql:9.0.48"
"@operato/graphql:9.0.49"
"@operato/graphql:9.0.50"
"@operato/graphql:9.0.51"
"@operato/help:9.0.47"
"@operato/help:9.0.48"
"@operato/help:9.0.49"
"@operato/help:9.0.50"
"@operato/help:9.0.51"
"@operato/input:9.0.47"
"@operato/input:9.0.48"
"@operato/popup:9.0.47"
"@operato/popup:9.0.48"
"@operato/popup:9.0.49"
"@operato/popup:9.0.50"
"@operato/popup:9.0.51"
"@operato/pull-to-refresh:9.0.35"
"@operato/pull-to-refresh:9.0.43"
"@operato/pull-to-refresh:9.0.44"
"@operato/pull-to-refresh:9.0.45"
"@operato/pull-to-refresh:9.0.46"
"@operato/pull-to-refresh:9.0.47"
"@operato/utils:9.0.47"
"@operato/utils:9.0.48"
"@operato/utils:9.0.49"
"@operato/utils:9.0.50"
"@operato/utils:9.0.51"
"@rxap/ngx-bootstrap:19.0.3"
"@rxap/ngx-bootstrap:19.0.4"
"@teriyakibomb/ember-velcro:2.2.1"
"@teselagen/bio-parsers:0.4.30"
"@teselagen/file-utils:0.3.22"
"@teselagen/ove:0.7.40"
"@teselagen/react-table:6.10.20"
"@teselagen/react-table:6.10.22"
"@teselagen/sequence-utils:0.3.34"
"@teselagen/ui:0.9.10"
"@things-factory/attachment-base:9.0.42"
"@things-factory/attachment-base:9.0.51"
"@things-factory/attachment-base:9.0.52"
"@things-factory/attachment-base:9.0.53"
"@things-factory/attachment-base:9.0.54"
"@things-factory/attachment-base:9.0.55"
"@things-factory/auth-base:9.0.42"
"@things-factory/email-base:9.0.55"
"@things-factory/email-base:9.0.56"
"@things-factory/email-base:9.0.57"
"@things-factory/email-base:9.0.58"
"@things-factory/email-base:9.0.59"
"@things-factory/integration-base:9.0.42"
"@things-factory/shell:9.0.42"
"another-shai:1.0.1"
"eslint-config-teselagen:6.1.8"
"graphql-sequelize-teselagen:5.3.9"
"ove-auto-annotate:0.0.10"
"react-jsonschema-rxnt-extras:0.4.9"
"tg-redbird:1.3.2"
"ve-bamreader:0.2.7"
"ve-editor:1.0.2"
)
# Store the original directory
original_dir=$(pwd)
# Detect package manager from root lock file
if [ -f "pnpm-lock.yaml" ]; then
pkgmgr="pnpm"
elif [ -f "package-lock.json" ]; then
pkgmgr="npm"
elif [ -f "yarn.lock" ]; then
pkgmgr="yarn"
else
echo "No recognized lockfile found in root directory (pnpm, npm, or yarn)."
echo "This script expects to be run from the monorepo root."
exit 1
fi
echo "Detected package manager: $pkgmgr"
# Find all package.json files excluding node_modules
echo "Finding sub-projects..."
subprojects=()
while IFS= read -r -d '' file; do
# Get the directory containing the package.json
dir=$(dirname "$file")
# Skip if it's in node_modules or if it's the root package.json and there are other subprojects
if [[ "$dir" != *"/node_modules"* && "$dir" != *"/node_modules" ]]; then
subprojects+=("$dir")
fi
done < <(find . -name "package.json" -type f -print0)
if [ ${#subprojects[@]} -eq 0 ]; then
echo "No package.json files found."
exit 1
fi
echo "Found ${#subprojects[@]} sub-project(s):"
for project in "${subprojects[@]}"; do
echo " $project"
done
echo ""
# Check for jq
if command -v jq >/dev/null 2>&1; then
use_jq=1
else
use_jq=0
echo "⚠️ Warning: jq not found. Falling back to basic text parsing. Install jq for more reliable results."
fi
# Global counters
total_affected=()
total_checked=0
total_found=0
total_projects_affected=0
# Function to check dependencies for a single project
check_project() {
local project_dir="$1"
local project_affected=()
local project_found=0
echo "Checking project: $project_dir"
# Change to project directory
cd "$original_dir/$project_dir" || {
echo " Error: Cannot access directory $project_dir"
return 1
}
# Get dependency list as JSON with proper error handling
echo " Fetching dependency list..."
local jsonout
case $pkgmgr in
"pnpm")
jsonout=$(pnpm list --depth=Infinity --json 2>/dev/null)
;;
"npm")
jsonout=$(npm ls --all --json 2>/dev/null)
;;
"yarn")
jsonout=$(yarn list --json 2>/dev/null)
;;
esac
# Validate that we got valid output
if [ $? -ne 0 ] || [ -z "$jsonout" ]; then
echo " ⚠️ Warning: Failed to get dependency list from $pkgmgr in $project_dir"
echo " This might be normal if dependencies are not installed for this sub-project."
return 0
fi
# Basic JSON validation
if ! echo "$jsonout" | jq empty >/dev/null 2>&1; then
if [ $use_jq -eq 1 ]; then
echo " ⚠️ Warning: Package manager returned invalid JSON for $project_dir"
return 0
fi
# If no jq, we'll continue with text parsing but warn
echo " ⚠️ Warning: Cannot validate JSON format without jq"
fi
for entry in $compromised; do
pkg=${entry%%:*}
ver=${entry##*:}
echo " Checking $pkg@$ver..."
if [ $use_jq -eq 1 ]; then
# Use jq to search for package and version - handle different JSON formats
match=""
case $pkgmgr in
"npm")
# FIXED: npm format with recursive search through nested dependencies
match=$(echo "$jsonout" | jq -r --arg pkg "$pkg" --arg ver "$ver" '
def find_package($pkg; $ver):
if .dependencies then
(.dependencies | to_entries[] |
if .key == $pkg and .value.version == $ver then "found"
else (.value | find_package($pkg; $ver))
end
)
else empty
end;
find_package($pkg; $ver)' 2>/dev/null | head -1)
;;
"pnpm")
# pnpm format: recursive search through nested dependencies
match=$(echo "$jsonout" | jq -r --arg pkg "$pkg" --arg ver "$ver" '
def find_package($pkg; $ver):
if .dependencies then
(.dependencies | to_entries[] |
if .key == $pkg and .value.version == $ver then "found"
else (.value | find_package($pkg; $ver))
end
)
else empty
end;
.[0] | find_package($pkg; $ver)' 2>/dev/null | head -1)
;;
"yarn")
# yarn format: {data: {trees: [{name: "package@version"}]}}
match=$(echo "$jsonout" | jq -r --arg pkg "$pkg" --arg ver "$ver" '
.data.trees[]?.name // empty |
select(. == ($pkg + "@" + $ver)) |
"found"' 2>/dev/null | head -1)
;;
esac
if [[ "$match" == "found" ]]; then
echo " 🚨 Found $pkg@$ver: WARNING - Project is affected!"
project_affected+=("$project_dir: $pkg@$ver")
project_found=$((project_found+1))
else
# Check if package is present but version does not match
present=""
case $pkgmgr in
"npm")
# FIXED: Also check for different versions recursively
present=$(echo "$jsonout" | jq -r --arg pkg "$pkg" '
def find_package_version($pkg):
if .dependencies then
(.dependencies | to_entries[] |
if .key == $pkg then .value.version
else (.value | find_package_version($pkg))
end
)
else empty
end;
find_package_version($pkg)' 2>/dev/null | head -1)
;;
"pnpm")
present=$(echo "$jsonout" | jq -r --arg pkg "$pkg" '
def find_package_version($pkg):
if .dependencies then
(.dependencies | to_entries[] |
if .key == $pkg then .value.version
else (.value | find_package_version($pkg))
end
)
else empty
end;
.[0] | find_package_version($pkg)' 2>/dev/null | head -1)
;;
"yarn")
present=$(echo "$jsonout" | jq -r --arg pkg "$pkg" '
.data.trees[]?.name // empty |
select(startswith($pkg + "@")) |
split("@")[1]' 2>/dev/null | head -1)
;;
esac
if [[ -n "$present" && "$present" != "null" ]]; then
echo " βœ… Found $pkg@$present: Not affected (version does not match)"
else
echo " βšͺ $pkg not found in dependencies."
fi
fi
else
# Fallback parsing without jq - handle different JSON formats
found_version=""
case $pkgmgr in
"npm"|"pnpm")
# npm/pnpm format: look for "packageName": {"version": "x.x.x"}
# This fallback still has the original limitation for npm
pkg_pattern="\"$pkg\"[[:space:]]*:[[:space:]]*{"
if echo "$jsonout" | grep -q "$pkg_pattern"; then
# Extract version from the package entry using more compatible awk
found_version=$(echo "$jsonout" | awk -v pkg="$pkg" '
BEGIN { in_pkg = 0 }
# Look for the package name
$0 ~ "\"" pkg "\"[[:space:]]*:[[:space:]]*{" {
in_pkg = 1
next
}
# When we are in the package block, look for version
in_pkg && /"version"[[:space:]]*:[[:space:]]*"/ {
# Use gsub and string manipulation instead of match with array
line = $0
gsub(/.*"version"[[:space:]]*:[[:space:]]*"/, "", line)
gsub(/".*/, "", line)
if (line) {
print line
exit
}
}
# End of package block
in_pkg && /^[[:space:]]*}/ {
in_pkg = 0
}
')
fi
;;
"yarn")
# yarn format: look for "package@version" in name fields
name_pattern="\"name\"[[:space:]]*:[[:space:]]*\"$pkg@"
if echo "$jsonout" | grep -q "$name_pattern"; then
found_version=$(echo "$jsonout" | grep "$name_pattern" | head -n1 | \
sed -n 's/.*"name"[[:space:]]*:[[:space:]]*"'$pkg'@\([^"]*\)".*/\1/p')
fi
;;
esac
if [[ "$found_version" == "$ver" ]]; then
echo " 🚨 Found $pkg@$ver: WARNING - Project is affected!"
project_affected+=("$project_dir: $pkg@$ver")
project_found=$((project_found+1))
elif [[ -n "$found_version" ]]; then
echo " βœ… Found $pkg@$found_version: Not affected (version does not match)"
else
echo " βšͺ $pkg not found in dependencies."
fi
fi
done
# Update global counters
if [ $project_found -gt 0 ]; then
total_projects_affected=$((total_projects_affected+1))
total_affected+=("${project_affected[@]}")
fi
total_found=$((total_found+project_found))
if [ $project_found -gt 0 ]; then
echo " 🚨 Project summary: $project_found compromised package(s) found"
else
echo " βœ… Project summary: No compromised packages found"
fi
echo ""
# Return to original directory
cd "$original_dir"
}
# Check each sub-project
for project in "${subprojects[@]}"; do
check_project "$project"
total_checked=$((total_checked+1))
done
echo "\nπŸ“Š Overall Summary:"
echo "Checked $total_checked sub-project(s)."
echo "Projects affected: $total_projects_affected"
echo "Total compromised packages found: $total_found"
if [ $total_found -gt 0 ]; then
echo "\n🚨 Detailed findings:"
for finding in "${total_affected[@]}"; do
echo " $finding"
done
echo "\n⚠️ Action required: Review and update the affected packages."
exit 1
else
echo "βœ… No compromised packages found in any sub-project."
exit 0
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment