passbolt-browser-extension 5.10.4 → 5.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (296) hide show
  1. package/.devcontainer/Dockerfile +15 -0
  2. package/.devcontainer/devcontainer.json +31 -0
  3. package/.devcontainer/docker-compose.yml +11 -0
  4. package/.devcontainer/post-start.sh +20 -0
  5. package/.devcontainer/safe-chain-config.json +6 -0
  6. package/.devcontainer/safe-chain-shims.sh +10 -0
  7. package/.pre-commit-config.yaml +5 -0
  8. package/CHANGELOG.md +131 -4
  9. package/RELEASE_NOTES.md +3 -5
  10. package/am_i_compromised.py +1036 -0
  11. package/am_i_compromised.sh +688 -0
  12. package/build-safari-extension/Passbolt-Safari-Extension/Passbolt - password manager Extension/services/fetch/fetchService.swift +90 -2
  13. package/build-safari-extension/Passbolt-Safari-Extension/Passbolt - password manager.xcodeproj/project.pbxproj +13 -11
  14. package/build-safari-extension/Passbolt-Safari-Extension/Passbolt - password manager.xcodeproj/xcshareddata/xcschemes/Passbolt-Safari-Extension.xcscheme +1 -1
  15. package/eslint.config.mjs +0 -6
  16. package/package.json +25 -24
  17. package/{.gitlab-ci/scripts → scripts}/bin/publish.sh +2 -2
  18. package/{.gitlab-ci/scripts → scripts}/bin/publish_npm.sh +2 -6
  19. package/{.gitlab-ci/scripts → scripts}/bin/review.sh +1 -1
  20. package/src/all/background_page/controller/InformMenuController/InformMenuController.test.js +11 -11
  21. package/src/all/background_page/controller/account/verifyAccountPassphraseController.test.js +3 -3
  22. package/src/all/background_page/controller/accountRecovery/accountRecoveryGenerateOrganizationKeyController.test.js +1 -1
  23. package/src/all/background_page/controller/accountRecovery/accountRecoveryGetRequestController.test.js +3 -5
  24. package/src/all/background_page/controller/accountRecovery/accountRecoveryGetUserRequestsController.test.js +1 -1
  25. package/src/all/background_page/controller/accountRecovery/accountRecoveryLoginController.test.js +5 -6
  26. package/src/all/background_page/controller/accountRecovery/accountRecoverySaveOrganizationPolicyController.test.js +4 -5
  27. package/src/all/background_page/controller/accountRecovery/accountRecoverySaveUserSettingController.test.js +2 -2
  28. package/src/all/background_page/controller/accountRecovery/accountRecoveryValidateOrganizationPrivateKeyController.test.js +5 -6
  29. package/src/all/background_page/controller/accountRecovery/accountRecoveryValidatePublicKeyController.test.js +2 -3
  30. package/src/all/background_page/controller/accountRecovery/continueAccountRecoveryController.test.js +1 -1
  31. package/src/all/background_page/controller/accountRecovery/downloadOrganizationGenerateKeyController.test.js +1 -1
  32. package/src/all/background_page/controller/accountRecovery/recoverAccountController.test.js +7 -8
  33. package/src/all/background_page/controller/accountRecovery/reviewRequestController.test.js +13 -19
  34. package/src/all/background_page/controller/applicationOverlaid/IsApplicationOverlaidController.test.js +2 -4
  35. package/src/all/background_page/controller/auth/authLoginController.test.js +5 -6
  36. package/src/all/background_page/controller/auth/authLogoutController.js +11 -2
  37. package/src/all/background_page/controller/auth/authLogoutController.test.js +14 -5
  38. package/src/all/background_page/controller/auth/authVerifyServerKeyController.test.js +7 -16
  39. package/src/all/background_page/controller/autofill/AutofillController.test.js +2 -2
  40. package/src/all/background_page/controller/comment/createCommentController.test.js +3 -3
  41. package/src/all/background_page/controller/comment/deleteCommentController.test.js +3 -3
  42. package/src/all/background_page/controller/comment/getCommentsByRessourceidController.test.js +3 -5
  43. package/src/all/background_page/controller/crypto/checkPassphraseController.test.js +2 -2
  44. package/src/all/background_page/controller/crypto/downloadUserPrivateKeyController.test.js +1 -2
  45. package/src/all/background_page/controller/crypto/downloadUserPublicKeyController.test.js +1 -2
  46. package/src/all/background_page/controller/crypto/getKeyInfoController.test.js +1 -1
  47. package/src/all/background_page/controller/crypto/validatePrivateGpgKeySetupController.test.js +5 -6
  48. package/src/all/background_page/controller/export/exportResourcesFileController.test.js +6 -7
  49. package/src/all/background_page/controller/exportPolicies/findExportPoliciesSettingsController.test.js +1 -1
  50. package/src/all/background_page/controller/extension/isExtensionAllowedOnEveryWebsiteController.test.js +1 -1
  51. package/src/all/background_page/controller/extension/openSafariExtensionSettingsController.test.js +1 -1
  52. package/src/all/background_page/controller/extension/startCheckingForPermissionUpdateController.test.js +1 -1
  53. package/src/all/background_page/controller/extension/stopCheckingForPermissionUpdateController.test.js +1 -1
  54. package/src/all/background_page/controller/folder/findFolderDetailsController.test.js +1 -1
  55. package/src/all/background_page/controller/group/groupCreateController.js +58 -0
  56. package/src/all/background_page/controller/group/groupCreateController.test.js +67 -0
  57. package/src/all/background_page/controller/import/importResourcesFileController.test.js +17 -19
  58. package/src/all/background_page/controller/metadata/enableEncryptedMetadataForExistingInstanceController.test.js +1 -1
  59. package/src/all/background_page/controller/metadata/enableMetadataSetupSettingsController.test.js +2 -2
  60. package/src/all/background_page/controller/metadata/findMetadataSetupSettingsController.test.js +1 -1
  61. package/src/all/background_page/controller/metadata/keepCleartextMetadataForExistingInstanceController.test.js +1 -1
  62. package/src/all/background_page/controller/move/moveResourcesController.test.js +2 -2
  63. package/src/all/background_page/controller/passwordExpiry/deletePasswordExpirySettingsController.test.js +2 -2
  64. package/src/all/background_page/controller/recover/generateRecoverAccountRecoveryRequestKeyController.test.js +1 -1
  65. package/src/all/background_page/controller/recover/startRecoverController.test.js +2 -2
  66. package/src/all/background_page/controller/resource/findAllByIdsForDisplayPermissionsController.test.js +1 -1
  67. package/src/all/background_page/controller/resource/findAllIdsByIsSharedWithGroupController.test.js +1 -1
  68. package/src/all/background_page/controller/resource/resourceCreateController.js +1 -1
  69. package/src/all/background_page/controller/resource/resourceCreateController.test.js +1 -1
  70. package/src/all/background_page/controller/resource/resourceUpdateController.test.js +1 -1
  71. package/src/all/background_page/controller/resource/updateResourceLocalStorageByFolderParentIdController.test.js +1 -1
  72. package/src/all/background_page/controller/role/findAllRoleControler.test.js +1 -1
  73. package/src/all/background_page/controller/role/rolesUpdateLocalStorageController.test.js +1 -1
  74. package/src/all/background_page/controller/scimSettings/createScimSettingsController.test.js +2 -2
  75. package/src/all/background_page/controller/scimSettings/disableScimSettingsController.test.js +1 -1
  76. package/src/all/background_page/controller/scimSettings/findScimSettingsController.test.js +1 -1
  77. package/src/all/background_page/controller/scimSettings/updateScimSettingsController.test.js +1 -1
  78. package/src/all/background_page/controller/secretRevision/findResourceSecretRevisionsForDisplayController.test.js +3 -3
  79. package/src/all/background_page/controller/setup/downloadRecoverKitController.test.js +1 -1
  80. package/src/all/background_page/controller/setup/setSetupAccountRecoveryUserSettingController.test.js +1 -1
  81. package/src/all/background_page/controller/setup/setSetupLocaleController.test.js +1 -1
  82. package/src/all/background_page/controller/setup/signInSetupController.test.js +3 -3
  83. package/src/all/background_page/controller/setup/startSetupController.test.js +3 -3
  84. package/src/all/background_page/controller/setup/verifyImportedKeyPassphraseController.test.js +3 -3
  85. package/src/all/background_page/controller/share/shareOneFolderController.test.js +3 -5
  86. package/src/all/background_page/controller/share/shareResourcesController.test.js +5 -7
  87. package/src/all/background_page/controller/sso/hasSsoLoginErrorController.test.js +1 -2
  88. package/src/all/background_page/controller/sso/ssoAuthenticationController.test.js +1 -2
  89. package/src/all/background_page/controller/tab/openAdministrationPageController.test.js +1 -1
  90. package/src/all/background_page/controller/tab/openResourceUriTabController.js +60 -0
  91. package/src/all/background_page/controller/tab/openResourceUriTabController.test.js +108 -0
  92. package/src/all/background_page/controller/tab/openTrustedDomainTabController.test.js +1 -1
  93. package/src/all/background_page/controller/tab/openWebsiteGettingStartedPageController.test.js +1 -1
  94. package/src/all/background_page/controller/tag/addTagsToResourcesController.test.js +4 -6
  95. package/src/all/background_page/controller/tag/findTagsController.test.js +1 -1
  96. package/src/all/background_page/error/timeoutError.js +23 -0
  97. package/src/all/background_page/event/appEvents.js +12 -0
  98. package/src/all/background_page/event/appSignOutEvents.js +23 -0
  99. package/src/all/background_page/event/groupEvents.js +3 -10
  100. package/src/all/background_page/model/actionLog/findActionLogService.test.js +2 -2
  101. package/src/all/background_page/model/comment/commentService.test.js +3 -3
  102. package/src/all/background_page/model/entity/accountRecovery/accountRecoveryOrganizationPolicyChangeEntity.test.js +2 -3
  103. package/src/all/background_page/model/entity/accountRecovery/accountRecoveryOrganizationPolicyEntity.test.js +2 -3
  104. package/src/all/background_page/model/entity/accountRecovery/accountRecoveryOrganizationPublicKeyEntity.test.js +2 -3
  105. package/src/all/background_page/model/entity/gpgkey/generate/generateGpgKeyPairOptionsEntity.test.js +2 -2
  106. package/src/all/background_page/model/entity/group/update/groupUpdatesCollection.test.js +1 -1
  107. package/src/all/background_page/model/entity/passwordPolicies/passphraseGeneratorSettingsEntity.test.js +2 -3
  108. package/src/all/background_page/model/entity/passwordPolicies/passwordGeneratorSettingsEntity.test.js +2 -3
  109. package/src/all/background_page/model/entity/passwordPolicies/passwordPoliciesEntity.test.js +4 -5
  110. package/src/all/background_page/model/entity/plaintext/plaintextEntity.js +9 -0
  111. package/src/all/background_page/model/entity/plaintext/plaintextEntity.test.js +33 -2
  112. package/src/all/background_page/model/entity/rememberMe/userRememberMeLatestChoiceEntity.test.js +1 -1
  113. package/src/all/background_page/model/entity/resource/external/externalResourceEntity.js +33 -0
  114. package/src/all/background_page/model/entity/resource/external/externalResourceEntity.test.js +64 -1
  115. package/src/all/background_page/model/entity/resource/resourceEntity.js +1 -1
  116. package/src/all/background_page/model/entity/sso/ssoLoginUrlEntity.js +2 -0
  117. package/src/all/background_page/model/entity/sso/ssoLoginUrlEntity.test.js +6 -5
  118. package/src/all/background_page/model/entity/totp/totpEntity.test.js +2 -3
  119. package/src/all/background_page/model/export/resources/resourcesKdbxExporter.test.js +68 -0
  120. package/src/all/background_page/model/group/groupModel.js +0 -15
  121. package/src/all/background_page/model/import/resources/csvRowParser/csv1PasswordRowParser.js +1 -0
  122. package/src/all/background_page/model/import/resources/csvRowParser/csvBitWardenRowParser.js +1 -0
  123. package/src/all/background_page/model/import/resources/csvRowParser/csvChromiumRowParser.js +1 -0
  124. package/src/all/background_page/model/import/resources/csvRowParser/csvDashlaneRowParser.js +1 -0
  125. package/src/all/background_page/model/import/resources/csvRowParser/csvKdbxRowParser.js +1 -0
  126. package/src/all/background_page/model/import/resources/csvRowParser/csvLastPassRowParser.js +1 -0
  127. package/src/all/background_page/model/import/resources/csvRowParser/csvLogMeOnceRowParser.js +1 -0
  128. package/src/all/background_page/model/import/resources/csvRowParser/csvMozillaPlatformRowParser.js +1 -0
  129. package/src/all/background_page/model/import/resources/csvRowParser/csvNordpassRowParser.js +1 -0
  130. package/src/all/background_page/model/import/resources/csvRowParser/csvSafariRowParser.js +1 -0
  131. package/src/all/background_page/model/import/resources/resourcesCsvImportParser.test.js +6 -7
  132. package/src/all/background_page/model/import/resources/resourcesKdbxImportParser.js +1 -0
  133. package/src/all/background_page/model/import/resources/resourcesTypeImportParser.js +60 -0
  134. package/src/all/background_page/model/import/resources/resourcesTypeImportParser.test.js +398 -42
  135. package/src/all/background_page/model/passwordExpiry/passwordExpirySettingsModel.test.js +2 -2
  136. package/src/all/background_page/model/resourceType/resourceTypeModel.test.js +1 -1
  137. package/src/all/background_page/model/userPassphrasePolicies/userPassphrasePoliciesModel.test.js +1 -1
  138. package/src/all/background_page/model/userSettings/userSettings.test.js +2 -2
  139. package/src/all/background_page/pagemod/accountRecoveryBootstrapPagemod.test.js +2 -3
  140. package/src/all/background_page/pagemod/appBootstrapPagemod.test.js +2 -3
  141. package/src/all/background_page/pagemod/appPagemod.js +9 -1
  142. package/src/all/background_page/pagemod/appPagemod.test.js +32 -2
  143. package/src/all/background_page/pagemod/authBootstrapPagemod.test.js +2 -3
  144. package/src/all/background_page/pagemod/publicWebsiteSignInPagemod.test.js +2 -3
  145. package/src/all/background_page/pagemod/recoverBootstrapPagemod.test.js +4 -5
  146. package/src/all/background_page/pagemod/setupBootstrapPagemod.test.js +4 -5
  147. package/src/all/background_page/pagemod/webIntegrationPagemod.test.js +2 -3
  148. package/src/all/background_page/service/accountRecovery/decryptPrivateKeyPasswordDataService.test.js +6 -6
  149. package/src/all/background_page/service/accountRecovery/decryptResponseDataService.test.js +5 -7
  150. package/src/all/background_page/service/accountRecovery/parseAccountRecoveryUrlService.test.js +6 -7
  151. package/src/all/background_page/service/accountRecovery/validateOrganizationPublicKeyService.test.js +2 -3
  152. package/src/all/background_page/service/api/accountRecovery/validateAccountRecoveryOrganizationPrivateKeyService.test.js +2 -2
  153. package/src/all/background_page/service/api/favorite/favoriteApiService.test.js +9 -9
  154. package/src/all/background_page/service/api/group/groupApiService.js +6 -3
  155. package/src/all/background_page/service/api/metadata/metadataKeysApiService.test.js +6 -6
  156. package/src/all/background_page/service/api/metadata/metadataRotateKeysResourcesApiService.test.js +2 -2
  157. package/src/all/background_page/service/api/metadata/metadataSetupSettingsApiService.test.js +1 -1
  158. package/src/all/background_page/service/api/secretRevision/resourceSecretRevisionApiService.test.js +4 -4
  159. package/src/all/background_page/service/api/secretRevision/secretRevisionsSettingsApiService.test.js +6 -6
  160. package/src/all/background_page/service/app/parseAppUrlService.test.js +5 -6
  161. package/src/all/background_page/service/auth/authVerifyLoginChallengeService.test.js +1 -1
  162. package/src/all/background_page/service/auth/authVerifyServerChallengeService.test.js +2 -4
  163. package/src/all/background_page/service/auth/decryptUserAuthTokenService.js +2 -2
  164. package/src/all/background_page/service/auth/decryptUserAuthTokenService.test.js +8 -10
  165. package/src/all/background_page/service/auth/parseAuthUrlService.test.js +4 -5
  166. package/src/all/background_page/service/authenticationStatusService.test.js +2 -4
  167. package/src/all/background_page/service/clipboard/copyToClipboardService.test.js +2 -2
  168. package/src/all/background_page/service/crypto/decryptPrivateKeyService.test.js +1 -1
  169. package/src/all/background_page/service/crypto/decryptSecretsService.js +4 -0
  170. package/src/all/background_page/service/crypto/decryptSecretsService.test.js +8 -3
  171. package/src/all/background_page/service/crypto/encryptPrivateKeyService.test.js +1 -1
  172. package/src/all/background_page/service/crypto/generateGpgKeyPairService.test.js +1 -1
  173. package/src/all/background_page/service/crypto/generateSsoKeyService.test.js +2 -3
  174. package/src/all/background_page/service/crypto/getGpgKeyCreationDateService.test.js +1 -1
  175. package/src/all/background_page/service/crypto/getSessionKeyService.test.js +5 -5
  176. package/src/all/background_page/service/crypto/signMessageService.test.js +4 -10
  177. package/src/all/background_page/service/crypto/verifyMessageSign.test.js +8 -10
  178. package/src/all/background_page/service/execute/executeConcurrentlyService.test.js +4 -4
  179. package/src/all/background_page/service/exportPolicies/findExportPoliciesSettingsService.test.js +1 -1
  180. package/src/all/background_page/service/extension/openSafariExtensionSettingsService.test.js +1 -1
  181. package/src/all/background_page/service/folder/findFoldersService.test.js +7 -11
  182. package/src/all/background_page/service/folder/getOrFindFoldersService.test.js +1 -3
  183. package/src/all/background_page/service/group/createGroupService.js +48 -0
  184. package/src/all/background_page/service/group/createGroupService.test.js +68 -0
  185. package/src/all/background_page/service/group/groupUpdateService.js +34 -2
  186. package/src/all/background_page/service/group/groupUpdateService.test.js +2 -5
  187. package/src/all/background_page/service/local_storage/folderLocalStorage.test.js +1 -1
  188. package/src/all/background_page/service/local_storage/groupLocalStorage.test.js +9 -15
  189. package/src/all/background_page/service/local_storage/resourceLocalStorage.test.js +1 -1
  190. package/src/all/background_page/service/metadata/configureMetadataSettingsService.test.js +11 -11
  191. package/src/all/background_page/service/metadata/createMetadataKeyService.test.js +2 -2
  192. package/src/all/background_page/service/metadata/decryptMetadataPrivateKeysService.test.js +10 -8
  193. package/src/all/background_page/service/metadata/decryptMetadataService.test.js +9 -7
  194. package/src/all/background_page/service/metadata/deleteMetadataKeyService.test.js +1 -1
  195. package/src/all/background_page/service/metadata/encryptMetadataPrivateKeysService.test.js +14 -10
  196. package/src/all/background_page/service/metadata/encryptMetadataService.test.js +14 -10
  197. package/src/all/background_page/service/metadata/expireMetadataKeyService.test.js +1 -1
  198. package/src/all/background_page/service/metadata/findMetadataKeysService.test.js +4 -4
  199. package/src/all/background_page/service/metadata/findMetadataSetupSettingsService.test.js +1 -1
  200. package/src/all/background_page/service/move/moveOneFolderService.test.js +3 -3
  201. package/src/all/background_page/service/move/moveResourcesService.js +2 -2
  202. package/src/all/background_page/service/move/moveResourcesService.test.js +72 -0
  203. package/src/all/background_page/service/publicWebsite/parsePublicWebsiteUrlService.test.js +4 -5
  204. package/src/all/background_page/service/rbac/findRbacService.test.js +2 -2
  205. package/src/all/background_page/service/recover/buildAccountRecoverService.test.js +2 -2
  206. package/src/all/background_page/service/recover/parseRecoverUrlService.test.js +10 -11
  207. package/src/all/background_page/service/resource/create/resourceCreateService.js +1 -1
  208. package/src/all/background_page/service/resource/create/resourceCreateService.test.js +2 -2
  209. package/src/all/background_page/service/resource/export/exportResourcesService.js +1 -1
  210. package/src/all/background_page/service/resource/export/exportResourcesService.test.data.js +16 -5
  211. package/src/all/background_page/service/resource/export/exportResourcesService.test.js +48 -13
  212. package/src/all/background_page/service/resource/findResourcesService.test.js +7 -9
  213. package/src/all/background_page/service/resource/import/ImportResourcesService.test.js +4 -5
  214. package/src/all/background_page/service/resource/update/resourceUpdateLocalStorageService.test.js +2 -6
  215. package/src/all/background_page/service/resourceType/updateResourceTypesService.test.js +3 -2
  216. package/src/all/background_page/service/role/findRolesService.test.js +2 -2
  217. package/src/all/background_page/service/scimSettings/updateScimSettingsService.test.js +1 -1
  218. package/src/all/background_page/service/secretRevisions/deleteSecretRevisionsSettingsService.test.js +1 -1
  219. package/src/all/background_page/service/secretRevisions/findAndDecryptSecretRevisionsService.test.js +5 -7
  220. package/src/all/background_page/service/secretRevisions/findSecretRevisionsService.test.js +7 -7
  221. package/src/all/background_page/service/secretRevisions/saveSecretRevisionsSettingsService.test.js +2 -2
  222. package/src/all/background_page/service/sessionKey/decryptSessionKeysBundlesService.test.js +6 -6
  223. package/src/all/background_page/service/sessionKey/encryptSessionKeysBundlesService.test.js +4 -4
  224. package/src/all/background_page/service/sessionKey/findSessionKeysService.test.js +1 -1
  225. package/src/all/background_page/service/sessionKey/getOrFindSessionKeysService.test.js +2 -6
  226. package/src/all/background_page/service/sessionStorage/sessionKeysBundlesSessionStorageService.test.js +1 -1
  227. package/src/all/background_page/service/session_storage/metadataKeysSessionStorage.test.js +4 -4
  228. package/src/all/background_page/service/setup/buildAccountSetupService.test.js +2 -2
  229. package/src/all/background_page/service/setup/parseSetupUrlService.test.js +6 -9
  230. package/src/all/background_page/service/share/shareResourceService.test.js +10 -13
  231. package/src/all/background_page/service/sso/generateSsoKitService.test.js +1 -1
  232. package/src/all/background_page/service/sso/popupHandlerService.test.js +2 -7
  233. package/src/all/background_page/service/tab/tabService.js +37 -27
  234. package/src/all/background_page/service/tab/tabService.test.js +132 -59
  235. package/src/all/background_page/service/tag/deleteTagService.test.js +6 -6
  236. package/src/all/background_page/service/tag/findTagsService.test.js +1 -1
  237. package/src/all/background_page/service/tag/updateResourceTagsService.test.js +25 -19
  238. package/src/all/background_page/service/tag/updateTagService.test.js +6 -6
  239. package/src/all/background_page/service/ui/browserTab.service.test.js +10 -10
  240. package/src/all/background_page/service/ui/openWebsiteGettingStartedPageService.test.js +1 -1
  241. package/src/all/background_page/service/webIntegration/parseWebIntegrationUrlService.test.js +4 -5
  242. package/src/all/background_page/service/webNavigation/webNavigationService.js +4 -3
  243. package/src/all/background_page/service/webNavigation/webNavigationService.test.js +2 -2
  244. package/src/all/background_page/utils/assertions.test.js +20 -21
  245. package/src/all/background_page/utils/format/formDataUtils.js +0 -16
  246. package/src/all/background_page/utils/format/formDataUtils.test.data.js +0 -7
  247. package/src/all/background_page/utils/format/formDataUtils.test.js +1 -39
  248. package/src/all/background_page/utils/openpgp/openpgpAssertions.test.js +11 -17
  249. package/src/all/background_page/utils/promise/promiseTimeoutService.js +3 -1
  250. package/src/all/background_page/vendors/locutus/stripslashes.js +25 -0
  251. package/src/all/background_page/vendors/locutus/stripslashes.test.js +64 -0
  252. package/src/all/background_page/vendors/locutus/urldecode.js +21 -0
  253. package/src/all/background_page/vendors/locutus/urldecode.test.js +64 -0
  254. package/src/all/background_page/vendors/locutus/urlencode.test.data.js +23 -0
  255. package/src/all/contentScripts/js/app/App.js +11 -0
  256. package/src/all/contentScripts/js/app/Login.js +11 -0
  257. package/src/all/contentScripts/js/message/messageEventHandler.test.js +7 -11
  258. package/src/all/locales/cs-CZ/common.json +1 -1
  259. package/src/all/locales/de-DE/common.json +1 -1
  260. package/src/all/locales/en-UK/common.json +1 -1
  261. package/src/all/locales/es-ES/common.json +1 -1
  262. package/src/all/locales/fr-FR/common.json +1 -1
  263. package/src/all/locales/it-IT/common.json +1 -1
  264. package/src/all/locales/ja-JP/common.json +1 -1
  265. package/src/all/locales/ko-KR/common.json +1 -1
  266. package/src/all/locales/lt-LT/common.json +1 -1
  267. package/src/all/locales/nl-NL/common.json +1 -1
  268. package/src/all/locales/pl-PL/common.json +1 -1
  269. package/src/all/locales/pt-BR/common.json +1 -1
  270. package/src/all/locales/ro-RO/common.json +1 -1
  271. package/src/all/locales/ru-RU/common.json +1 -1
  272. package/src/all/locales/sl-SI/common.json +1 -1
  273. package/src/all/locales/sv-SE/common.json +1 -1
  274. package/src/all/locales/uk-UA/common.json +1 -1
  275. package/src/chrome/manifest.json +1 -1
  276. package/src/chrome-mv3/manifest.json +1 -1
  277. package/src/chrome-mv3/offscreens/service/network/fetchOffscreenService.test.js +4 -5
  278. package/src/chrome-mv3/serviceWorker/service/network/requestFetchOffscreenService.test.js +14 -18
  279. package/src/chrome-mv3/serviceWorker/service/network/responseFetchOffscreenService.test.js +8 -9
  280. package/src/firefox/manifest.json +5 -2
  281. package/src/safari/background_page/index.js +2 -2
  282. package/src/safari/background_page/service/cookies/cookiesService.test.js +2 -2
  283. package/src/safari/background_page/service/nativeMessage/sendNativeMessageService.test.js +4 -4
  284. package/src/safari/common/polyfill/fetchPolyfill.js +1 -1
  285. package/src/safari/common/polyfill/fetchPolyfill.test.js +8 -4
  286. package/src/safari/manifest.json +1 -1
  287. package/.gitlab-ci/jobs/build.yml +0 -60
  288. package/.gitlab-ci/jobs/publish.yml +0 -48
  289. package/.gitlab-ci/jobs/release.yml +0 -21
  290. package/.gitlab-ci/jobs/review.yml +0 -25
  291. package/.gitlab-ci/jobs/test.yml +0 -32
  292. package/.gitlab-ci.yml +0 -21
  293. /package/{.gitlab-ci/scripts → scripts}/bin/slack-status-messages.sh +0 -0
  294. /package/{.gitlab-ci/scripts → scripts}/lib/git-helpers.sh +0 -0
  295. /package/{.gitlab-ci/scripts → scripts}/lib/set-env.sh +0 -0
  296. /package/{.gitlab-ci/scripts → scripts}/lib/version-check.sh +0 -0
@@ -0,0 +1,1036 @@
1
+ #!/usr/bin/env python3
2
+ """am_i_compromised.py - supply chain graph analysis for Mini Shai-Hulud (May 2025)
3
+
4
+ Ref: https://www.aikido.dev/blog/mini-shai-hulud-is-back-tanstack-compromised
5
+
6
+ Scans npm/yarn/pnpm lockfiles and classifies findings into 4 tiers:
7
+ T1 COMPROMISED exact match against known-bad pkg@version
8
+ T2 DIRECT root dep/devDep from a compromised namespace
9
+ T3 TRANSITIVE compromised namespace pkg in tree via transitive chain
10
+ T4 SHADOW installed pkg's own devDependencies reference compromised namespace
11
+ (not installed, but indicates build tooling was targeted)
12
+
13
+ Usage: ./am_i_compromised.py [path ...] [--deep]
14
+ path lockfile or directory (default: .)
15
+ --deep fetch package metadata from npm registry for T4 (slow)
16
+ """
17
+
18
+ import json
19
+ import os
20
+ import re
21
+ import sys
22
+ import urllib.request
23
+ import urllib.error
24
+ from collections import defaultdict, deque
25
+ from concurrent.futures import ThreadPoolExecutor, as_completed
26
+ from pathlib import Path
27
+
28
+ # ── Known-bad pkg@version pairs (complete as of 2025-05-12) ─────────────────
29
+
30
+ BAD_VERSIONS = {
31
+ "@beproduct/nestjs-auth@0.1.10",
32
+ "@beproduct/nestjs-auth@0.1.11",
33
+ "@beproduct/nestjs-auth@0.1.12",
34
+ "@beproduct/nestjs-auth@0.1.13",
35
+ "@beproduct/nestjs-auth@0.1.14",
36
+ "@beproduct/nestjs-auth@0.1.15",
37
+ "@beproduct/nestjs-auth@0.1.16",
38
+ "@beproduct/nestjs-auth@0.1.17",
39
+ "@beproduct/nestjs-auth@0.1.18",
40
+ "@beproduct/nestjs-auth@0.1.19",
41
+ "@beproduct/nestjs-auth@0.1.2",
42
+ "@beproduct/nestjs-auth@0.1.3",
43
+ "@beproduct/nestjs-auth@0.1.4",
44
+ "@beproduct/nestjs-auth@0.1.5",
45
+ "@beproduct/nestjs-auth@0.1.6",
46
+ "@beproduct/nestjs-auth@0.1.7",
47
+ "@beproduct/nestjs-auth@0.1.8",
48
+ "@beproduct/nestjs-auth@0.1.9",
49
+ "@dirigible-ai/sdk@0.6.2",
50
+ "@dirigible-ai/sdk@0.6.3",
51
+ "@draftauth/client@0.2.1",
52
+ "@draftauth/client@0.2.2",
53
+ "@draftauth/core@0.13.1",
54
+ "@draftauth/core@0.13.2",
55
+ "@draftlab/auth-router@0.5.1",
56
+ "@draftlab/auth-router@0.5.2",
57
+ "@draftlab/auth@0.24.1",
58
+ "@draftlab/auth@0.24.2",
59
+ "@draftlab/db@0.16.1",
60
+ "@mesadev/rest@0.28.3",
61
+ "@mesadev/saguaro@0.4.22",
62
+ "@mesadev/sdk@0.28.3",
63
+ "@mistralai/mistralai-azure@1.7.1",
64
+ "@mistralai/mistralai-azure@1.7.2",
65
+ "@mistralai/mistralai-azure@1.7.3",
66
+ "@mistralai/mistralai-gcp@1.7.1",
67
+ "@mistralai/mistralai-gcp@1.7.2",
68
+ "@mistralai/mistralai-gcp@1.7.3",
69
+ "@mistralai/mistralai@2.2.2",
70
+ "@mistralai/mistralai@2.2.3",
71
+ "@mistralai/mistralai@2.2.4",
72
+ "@ml-toolkit-ts/preprocessing@1.0.2",
73
+ "@ml-toolkit-ts/preprocessing@1.0.3",
74
+ "@ml-toolkit-ts/xgboost@1.0.3",
75
+ "@ml-toolkit-ts/xgboost@1.0.4",
76
+ "@squawk/airport-data@0.7.4",
77
+ "@squawk/airport-data@0.7.5",
78
+ "@squawk/airport-data@0.7.6",
79
+ "@squawk/airport-data@0.7.7",
80
+ "@squawk/airports@0.6.2",
81
+ "@squawk/airports@0.6.3",
82
+ "@squawk/airports@0.6.4",
83
+ "@squawk/airports@0.6.5",
84
+ "@squawk/airspace-data@0.5.3",
85
+ "@squawk/airspace-data@0.5.4",
86
+ "@squawk/airspace-data@0.5.5",
87
+ "@squawk/airspace-data@0.5.6",
88
+ "@squawk/airspace@0.8.1",
89
+ "@squawk/airspace@0.8.2",
90
+ "@squawk/airspace@0.8.3",
91
+ "@squawk/airspace@0.8.4",
92
+ "@squawk/airway-data@0.5.4",
93
+ "@squawk/airway-data@0.5.5",
94
+ "@squawk/airway-data@0.5.6",
95
+ "@squawk/airway-data@0.5.7",
96
+ "@squawk/airways@0.4.2",
97
+ "@squawk/airways@0.4.3",
98
+ "@squawk/airways@0.4.4",
99
+ "@squawk/airways@0.4.5",
100
+ "@squawk/fix-data@0.6.4",
101
+ "@squawk/fix-data@0.6.5",
102
+ "@squawk/fix-data@0.6.6",
103
+ "@squawk/fix-data@0.6.7",
104
+ "@squawk/fixes@0.3.2",
105
+ "@squawk/fixes@0.3.3",
106
+ "@squawk/fixes@0.3.4",
107
+ "@squawk/fixes@0.3.5",
108
+ "@squawk/flight-math@0.5.4",
109
+ "@squawk/flight-math@0.5.5",
110
+ "@squawk/flight-math@0.5.6",
111
+ "@squawk/flight-math@0.5.7",
112
+ "@squawk/flightplan@0.5.2",
113
+ "@squawk/flightplan@0.5.3",
114
+ "@squawk/flightplan@0.5.4",
115
+ "@squawk/flightplan@0.5.5",
116
+ "@squawk/geo@0.4.4",
117
+ "@squawk/geo@0.4.5",
118
+ "@squawk/geo@0.4.6",
119
+ "@squawk/geo@0.4.7",
120
+ "@squawk/icao-registry-data@0.8.4",
121
+ "@squawk/icao-registry-data@0.8.5",
122
+ "@squawk/icao-registry-data@0.8.6",
123
+ "@squawk/icao-registry-data@0.8.7",
124
+ "@squawk/icao-registry@0.5.2",
125
+ "@squawk/icao-registry@0.5.3",
126
+ "@squawk/icao-registry@0.5.4",
127
+ "@squawk/icao-registry@0.5.5",
128
+ "@squawk/mcp@0.9.1",
129
+ "@squawk/mcp@0.9.2",
130
+ "@squawk/mcp@0.9.3",
131
+ "@squawk/mcp@0.9.4",
132
+ "@squawk/navaid-data@0.6.4",
133
+ "@squawk/navaid-data@0.6.5",
134
+ "@squawk/navaid-data@0.6.6",
135
+ "@squawk/navaid-data@0.6.7",
136
+ "@squawk/navaids@0.4.2",
137
+ "@squawk/navaids@0.4.3",
138
+ "@squawk/navaids@0.4.4",
139
+ "@squawk/navaids@0.4.5",
140
+ "@squawk/notams@0.3.6",
141
+ "@squawk/notams@0.3.7",
142
+ "@squawk/notams@0.3.8",
143
+ "@squawk/notams@0.3.9",
144
+ "@squawk/procedure-data@0.7.3",
145
+ "@squawk/procedure-data@0.7.4",
146
+ "@squawk/procedure-data@0.7.5",
147
+ "@squawk/procedure-data@0.7.6",
148
+ "@squawk/procedures@0.5.2",
149
+ "@squawk/procedures@0.5.3",
150
+ "@squawk/procedures@0.5.4",
151
+ "@squawk/procedures@0.5.5",
152
+ "@squawk/types@0.8.2",
153
+ "@squawk/types@0.8.3",
154
+ "@squawk/types@0.8.4",
155
+ "@squawk/units@0.4.3",
156
+ "@squawk/units@0.4.4",
157
+ "@squawk/units@0.4.5",
158
+ "@squawk/units@0.4.6",
159
+ "@squawk/weather@0.5.6",
160
+ "@squawk/weather@0.5.7",
161
+ "@squawk/weather@0.5.8",
162
+ "@squawk/weather@0.5.9",
163
+ "@supersurkhet/cli@0.0.2",
164
+ "@supersurkhet/cli@0.0.3",
165
+ "@supersurkhet/cli@0.0.4",
166
+ "@supersurkhet/cli@0.0.5",
167
+ "@supersurkhet/cli@0.0.6",
168
+ "@supersurkhet/cli@0.0.7",
169
+ "@supersurkhet/sdk@0.0.2",
170
+ "@supersurkhet/sdk@0.0.3",
171
+ "@supersurkhet/sdk@0.0.4",
172
+ "@supersurkhet/sdk@0.0.5",
173
+ "@supersurkhet/sdk@0.0.6",
174
+ "@supersurkhet/sdk@0.0.7",
175
+ "@tallyui/components@1.0.1",
176
+ "@tallyui/components@1.0.2",
177
+ "@tallyui/components@1.0.3",
178
+ "@tallyui/connector-medusa@1.0.1",
179
+ "@tallyui/connector-medusa@1.0.2",
180
+ "@tallyui/connector-medusa@1.0.3",
181
+ "@tallyui/connector-shopify@1.0.1",
182
+ "@tallyui/connector-shopify@1.0.2",
183
+ "@tallyui/connector-shopify@1.0.3",
184
+ "@tallyui/connector-vendure@1.0.1",
185
+ "@tallyui/connector-vendure@1.0.2",
186
+ "@tallyui/connector-vendure@1.0.3",
187
+ "@tallyui/connector-woocommerce@1.0.1",
188
+ "@tallyui/connector-woocommerce@1.0.2",
189
+ "@tallyui/connector-woocommerce@1.0.3",
190
+ "@tallyui/core@0.2.1",
191
+ "@tallyui/core@0.2.2",
192
+ "@tallyui/core@0.2.3",
193
+ "@tallyui/database@1.0.1",
194
+ "@tallyui/database@1.0.2",
195
+ "@tallyui/database@1.0.3",
196
+ "@tallyui/pos@0.1.1",
197
+ "@tallyui/pos@0.1.2",
198
+ "@tallyui/pos@0.1.3",
199
+ "@tallyui/storage-sqlite@0.2.1",
200
+ "@tallyui/storage-sqlite@0.2.2",
201
+ "@tallyui/storage-sqlite@0.2.3",
202
+ "@tallyui/theme@0.2.1",
203
+ "@tallyui/theme@0.2.2",
204
+ "@tallyui/theme@0.2.3",
205
+ "@tanstack/arktype-adapter@1.166.12",
206
+ "@tanstack/arktype-adapter@1.166.15",
207
+ "@tanstack/eslint-plugin-router@1.161.9",
208
+ "@tanstack/eslint-plugin-start@0.0.4",
209
+ "@tanstack/eslint-plugin-start@0.0.7",
210
+ "@tanstack/history@1.161.12",
211
+ "@tanstack/history@1.161.9",
212
+ "@tanstack/nitro-v2-vite-plugin@1.154.12",
213
+ "@tanstack/nitro-v2-vite-plugin@1.154.15",
214
+ "@tanstack/react-router-devtools@1.166.16",
215
+ "@tanstack/react-router-devtools@1.166.19",
216
+ "@tanstack/react-router-ssr-query@1.166.15",
217
+ "@tanstack/react-router-ssr-query@1.166.18",
218
+ "@tanstack/react-router@1.169.5",
219
+ "@tanstack/react-router@1.169.8",
220
+ "@tanstack/react-start-client@1.166.51",
221
+ "@tanstack/react-start-client@1.166.54",
222
+ "@tanstack/react-start-rsc@0.0.47",
223
+ "@tanstack/react-start-rsc@0.0.50",
224
+ "@tanstack/react-start-server@1.166.55",
225
+ "@tanstack/react-start-server@1.166.58",
226
+ "@tanstack/react-start@1.167.68",
227
+ "@tanstack/react-start@1.167.71",
228
+ "@tanstack/router-cli@1.166.46",
229
+ "@tanstack/router-cli@1.166.49",
230
+ "@tanstack/router-core@1.169.5",
231
+ "@tanstack/router-core@1.169.8",
232
+ "@tanstack/router-devtools-core@1.167.6",
233
+ "@tanstack/router-devtools-core@1.167.9",
234
+ "@tanstack/router-devtools@1.166.16",
235
+ "@tanstack/router-devtools@1.166.19",
236
+ "@tanstack/router-generator@1.166.45",
237
+ "@tanstack/router-generator@1.166.48",
238
+ "@tanstack/router-plugin@1.167.38",
239
+ "@tanstack/router-plugin@1.167.41",
240
+ "@tanstack/router-ssr-query-core@1.168.3",
241
+ "@tanstack/router-ssr-query-core@1.168.6",
242
+ "@tanstack/router-utils@1.161.11",
243
+ "@tanstack/router-utils@1.161.14",
244
+ "@tanstack/router-vite-plugin@1.166.53",
245
+ "@tanstack/router-vite-plugin@1.166.56",
246
+ "@tanstack/solid-router-devtools@1.166.16",
247
+ "@tanstack/solid-router-devtools@1.166.19",
248
+ "@tanstack/solid-router-ssr-query@1.166.15",
249
+ "@tanstack/solid-router-ssr-query@1.166.18",
250
+ "@tanstack/solid-router@1.169.5",
251
+ "@tanstack/solid-router@1.169.8",
252
+ "@tanstack/solid-start-client@1.166.50",
253
+ "@tanstack/solid-start-client@1.166.53",
254
+ "@tanstack/solid-start-server@1.166.54",
255
+ "@tanstack/solid-start-server@1.166.57",
256
+ "@tanstack/solid-start@1.167.65",
257
+ "@tanstack/solid-start@1.167.68",
258
+ "@tanstack/start-client-core@1.168.5",
259
+ "@tanstack/start-client-core@1.168.8",
260
+ "@tanstack/start-fn-stubs@1.161.12",
261
+ "@tanstack/start-fn-stubs@1.161.9",
262
+ "@tanstack/start-plugin-core@1.169.23",
263
+ "@tanstack/start-plugin-core@1.169.26",
264
+ "@tanstack/start-server-core@1.167.33",
265
+ "@tanstack/start-server-core@1.167.36",
266
+ "@tanstack/start-static-server-functions@1.166.44",
267
+ "@tanstack/start-static-server-functions@1.166.47",
268
+ "@tanstack/start-storage-context@1.166.38",
269
+ "@tanstack/start-storage-context@1.166.41",
270
+ "@tanstack/valibot-adapter@1.166.12",
271
+ "@tanstack/valibot-adapter@1.166.15",
272
+ "@tanstack/virtual-file-routes@1.161.10",
273
+ "@tanstack/virtual-file-routes@1.161.13",
274
+ "@tanstack/vue-router-devtools@1.166.16",
275
+ "@tanstack/vue-router-devtools@1.166.19",
276
+ "@tanstack/vue-router-ssr-query@1.166.15",
277
+ "@tanstack/vue-router-ssr-query@1.166.18",
278
+ "@tanstack/vue-router@1.169.5",
279
+ "@tanstack/vue-router@1.169.8",
280
+ "@tanstack/vue-start-client@1.166.46",
281
+ "@tanstack/vue-start-client@1.166.49",
282
+ "@tanstack/vue-start-server@1.166.50",
283
+ "@tanstack/vue-start-server@1.166.53",
284
+ "@tanstack/vue-start@1.167.61",
285
+ "@tanstack/vue-start@1.167.64",
286
+ "@tanstack/zod-adapter@1.166.12",
287
+ "@tanstack/zod-adapter@1.166.15",
288
+ "@taskflow-corp/cli@0.1.24",
289
+ "@taskflow-corp/cli@0.1.25",
290
+ "@taskflow-corp/cli@0.1.26",
291
+ "@taskflow-corp/cli@0.1.27",
292
+ "@taskflow-corp/cli@0.1.28",
293
+ "@taskflow-corp/cli@0.1.29",
294
+ "@tolka/cli@1.0.2",
295
+ "@tolka/cli@1.0.3",
296
+ "@tolka/cli@1.0.4",
297
+ "@tolka/cli@1.0.5",
298
+ "@tolka/cli@1.0.6",
299
+ "@uipath/access-policy-sdk@0.3.1",
300
+ "@uipath/access-policy-tool@0.3.1",
301
+ "@uipath/admin-tool@0.1.1",
302
+ "@uipath/agent-sdk@1.0.2",
303
+ "@uipath/agent-tool@1.0.1",
304
+ "@uipath/agent.sdk@0.0.18",
305
+ "@uipath/aops-policy-tool@0.3.1",
306
+ "@uipath/ap-chat@1.5.7",
307
+ "@uipath/api-workflow-tool@1.0.1",
308
+ "@uipath/apollo-core@5.9.2",
309
+ "@uipath/apollo-react@4.24.5",
310
+ "@uipath/apollo-wind@2.16.2",
311
+ "@uipath/auth@1.0.1",
312
+ "@uipath/case-tool@1.0.1",
313
+ "@uipath/cli@1.0.1",
314
+ "@uipath/codedagent-tool@1.0.1",
315
+ "@uipath/codedagents-tool@0.1.12",
316
+ "@uipath/codedapp-tool@1.0.1",
317
+ "@uipath/common@1.0.1",
318
+ "@uipath/context-grounding-tool@0.1.1",
319
+ "@uipath/data-fabric-tool@1.0.2",
320
+ "@uipath/docsai-tool@1.0.1",
321
+ "@uipath/filesystem@1.0.1",
322
+ "@uipath/flow-tool@1.0.2",
323
+ "@uipath/functions-tool@1.0.1",
324
+ "@uipath/gov-tool@0.3.1",
325
+ "@uipath/identity-tool@0.1.1",
326
+ "@uipath/insights-sdk@1.0.1",
327
+ "@uipath/insights-tool@1.0.1",
328
+ "@uipath/integrationservice-sdk@1.0.2",
329
+ "@uipath/integrationservice-tool@1.0.2",
330
+ "@uipath/llmgw-tool@1.0.1",
331
+ "@uipath/maestro-sdk@1.0.1",
332
+ "@uipath/maestro-tool@1.0.1",
333
+ "@uipath/orchestrator-tool@1.0.1",
334
+ "@uipath/packager-tool-apiworkflow@0.0.19",
335
+ "@uipath/packager-tool-bpmn@0.0.9",
336
+ "@uipath/packager-tool-case@0.0.9",
337
+ "@uipath/packager-tool-connector@0.0.19",
338
+ "@uipath/packager-tool-flow@0.0.19",
339
+ "@uipath/packager-tool-functions@0.1.1",
340
+ "@uipath/packager-tool-webapp@1.0.6",
341
+ "@uipath/packager-tool-workflowcompiler-browser@0.0.34",
342
+ "@uipath/packager-tool-workflowcompiler@0.0.16",
343
+ "@uipath/platform-tool@1.0.1",
344
+ "@uipath/project-packager@1.1.16",
345
+ "@uipath/resource-tool@1.0.1",
346
+ "@uipath/resourcecatalog-tool@0.1.1",
347
+ "@uipath/resources-tool@0.1.11",
348
+ "@uipath/robot@1.3.4",
349
+ "@uipath/rpa-legacy-tool@1.0.1",
350
+ "@uipath/rpa-tool@0.9.5",
351
+ "@uipath/solution-packager@0.0.35",
352
+ "@uipath/solution-tool@1.0.1",
353
+ "@uipath/solutionpackager-sdk@1.0.11",
354
+ "@uipath/solutionpackager-tool-core@0.0.34",
355
+ "@uipath/tasks-tool@1.0.1",
356
+ "@uipath/telemetry@0.0.7",
357
+ "@uipath/test-manager-tool@1.0.2",
358
+ "@uipath/tool-workflowcompiler@0.0.12",
359
+ "@uipath/traces-tool@1.0.1",
360
+ "@uipath/ui-widgets-multi-file-upload@1.0.1",
361
+ "@uipath/uipath-python-bridge@1.0.1",
362
+ "@uipath/vertical-solutions-tool@1.0.1",
363
+ "@uipath/vss@0.1.6",
364
+ "@uipath/widget.sdk@1.2.3",
365
+ "agentwork-cli@0.1.4",
366
+ "agentwork-cli@0.1.5",
367
+ "cmux-agent-mcp@0.1.3",
368
+ "cmux-agent-mcp@0.1.4",
369
+ "cmux-agent-mcp@0.1.5",
370
+ "cmux-agent-mcp@0.1.6",
371
+ "cmux-agent-mcp@0.1.7",
372
+ "cmux-agent-mcp@0.1.8",
373
+ "cross-stitch@1.1.3",
374
+ "cross-stitch@1.1.4",
375
+ "cross-stitch@1.1.5",
376
+ "cross-stitch@1.1.6",
377
+ "git-branch-selector@1.3.3",
378
+ "git-branch-selector@1.3.4",
379
+ "git-branch-selector@1.3.5",
380
+ "git-branch-selector@1.3.6",
381
+ "git-branch-selector@1.3.7",
382
+ "git-git-git@1.0.10",
383
+ "git-git-git@1.0.11",
384
+ "git-git-git@1.0.12",
385
+ "git-git-git@1.0.8",
386
+ "git-git-git@1.0.9",
387
+ "ml-toolkit-ts@1.0.4",
388
+ "ml-toolkit-ts@1.0.5",
389
+ "nextmove-mcp@0.1.3",
390
+ "nextmove-mcp@0.1.4",
391
+ "nextmove-mcp@0.1.5",
392
+ "nextmove-mcp@0.1.6",
393
+ "nextmove-mcp@0.1.7",
394
+ "safe-action@0.8.3",
395
+ "safe-action@0.8.4",
396
+ "ts-dna@3.0.1",
397
+ "ts-dna@3.0.2",
398
+ "ts-dna@3.0.3",
399
+ "ts-dna@3.0.4",
400
+ "wot-api@0.8.1",
401
+ "wot-api@0.8.2",
402
+ "wot-api@0.8.3",
403
+ "wot-api@0.8.4",
404
+ }
405
+
406
+ # Scoped namespace prefixes and unscoped package names
407
+ SCOPED_NAMESPACES = [
408
+ "@beproduct/", "@dirigible-ai/", "@draftauth/", "@draftlab/",
409
+ "@mesadev/", "@mistralai/", "@ml-toolkit-ts/", "@squawk/",
410
+ "@supersurkhet/", "@tallyui/", "@tanstack/", "@taskflow-corp/",
411
+ "@tolka/", "@uipath/",
412
+ ]
413
+ UNSCOPED_NAMES = [
414
+ "agentwork-cli", "cmux-agent-mcp", "cross-stitch", "git-branch-selector",
415
+ "git-git-git", "ml-toolkit-ts", "nextmove-mcp", "safe-action",
416
+ "ts-dna", "wot-api",
417
+ ]
418
+
419
+ IOC_MARKERS = [
420
+ "router_init.js",
421
+ "router_runtime.js",
422
+ "tanstack_runner.js",
423
+ "@tanstack/setup",
424
+ "tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c",
425
+ "ab4fcadaec49c03278063dd269ea5eef82d24f2124a8e15d7b90f2fa8601266c",
426
+ "2ec78d556d696e208927cc503d48e4b5eb56b31abc2870c2ed2e98d6be27fc96",
427
+ "getsession.org",
428
+ "bun run tanstack_runner",
429
+ ]
430
+
431
+ # ── Colors ───────────────────────────────────────────────────────────────────
432
+
433
+ if sys.stdout.isatty():
434
+ C_RED = "\033[0;31m"
435
+ C_YEL = "\033[1;33m"
436
+ C_GRN = "\033[0;32m"
437
+ C_DIM = "\033[0;90m"
438
+ C_CYN = "\033[0;36m"
439
+ C_RST = "\033[0m"
440
+ C_BLD = "\033[1m"
441
+ else:
442
+ C_RED = C_YEL = C_GRN = C_DIM = C_CYN = C_RST = C_BLD = ""
443
+
444
+
445
+ def is_compromised_namespace(name):
446
+ for ns in SCOPED_NAMESPACES:
447
+ if name.startswith(ns):
448
+ return True
449
+ return name in UNSCOPED_NAMES
450
+
451
+
452
+ def matching_namespace(name):
453
+ for ns in SCOPED_NAMESPACES:
454
+ if name.startswith(ns):
455
+ return ns
456
+ if name in UNSCOPED_NAMES:
457
+ return name
458
+ return None
459
+
460
+
461
+ # ── Lockfile parsers ─────────────────────────────────────────────────────────
462
+ # Each returns: {pkg_name: {version, dev, dependencies: {name: range}, peers: {name: range}}}
463
+
464
+ def parse_npm_v3(data):
465
+ """Parse npm lockfile v2/v3 (flat packages section)."""
466
+ pkgs = data.get("packages", {})
467
+ result = {}
468
+ root_entry = pkgs.get("", {})
469
+ root_deps = set(root_entry.get("dependencies", {}).keys())
470
+ root_dev_deps = set(root_entry.get("devDependencies", {}).keys())
471
+
472
+ for key, val in pkgs.items():
473
+ if not key or not isinstance(val, dict):
474
+ continue
475
+ name = key.rsplit("node_modules/", 1)[-1]
476
+ version = val.get("version", "")
477
+ if not version:
478
+ continue
479
+
480
+ is_dev = val.get("dev", False) or val.get("devOptional", False)
481
+ deps = {}
482
+ for dep_name in val.get("dependencies", {}):
483
+ deps[dep_name] = val["dependencies"][dep_name]
484
+ for dep_name in val.get("optionalDependencies", {}):
485
+ deps[dep_name] = val["optionalDependencies"][dep_name]
486
+
487
+ peers = dict(val.get("peerDependencies", {}))
488
+
489
+ result[name] = {
490
+ "version": version,
491
+ "dev": is_dev,
492
+ "dependencies": deps,
493
+ "peers": peers,
494
+ }
495
+
496
+ return result, root_deps, root_dev_deps
497
+
498
+
499
+ def parse_npm_v1(data):
500
+ """Parse npm lockfile v1 (nested dependencies)."""
501
+ result = {}
502
+ root_pkg = data.get("dependencies", {})
503
+ root_deps = set(root_pkg.keys())
504
+ root_dev_deps = set(data.get("devDependencies", {}).keys())
505
+
506
+ def walk(deps, dev_context=False):
507
+ for name, val in deps.items():
508
+ if not isinstance(val, dict):
509
+ continue
510
+ version = val.get("version", "")
511
+ if not version:
512
+ continue
513
+ is_dev = val.get("dev", False) or dev_context
514
+ dep_deps = {}
515
+ for d, v in val.get("requires", {}).items():
516
+ dep_deps[d] = v
517
+ result[name] = {
518
+ "version": version,
519
+ "dev": is_dev,
520
+ "dependencies": dep_deps,
521
+ "peers": {},
522
+ }
523
+ walk(val.get("dependencies", {}), is_dev)
524
+
525
+ walk(root_pkg)
526
+ return result, root_deps, root_dev_deps
527
+
528
+
529
+ def parse_npm_lockfile(path):
530
+ """Parse npm/shrinkwrap lockfile, auto-detecting v1 vs v2/v3."""
531
+ with open(path) as f:
532
+ data = json.load(f)
533
+ if data.get("packages") is not None:
534
+ return parse_npm_v3(data)
535
+ return parse_npm_v1(data)
536
+
537
+
538
+ def parse_yarn_lockfile(path):
539
+ """Parse yarn.lock (classic or berry)."""
540
+ result = {}
541
+ root_deps = set()
542
+ root_dev_deps = set()
543
+
544
+ with open(path) as f:
545
+ content = f.read()
546
+
547
+ current_pkg = None
548
+ for line in content.splitlines():
549
+ if not line or line.startswith("#"):
550
+ continue
551
+
552
+ if not line[0].isspace() and "@" in line:
553
+ raw = line.rstrip(":")
554
+ raw = raw.strip('"')
555
+ parts = raw.split(",")
556
+ first = parts[0].strip().strip('"')
557
+ if first.startswith("@"):
558
+ at_pos = first.index("@", 1)
559
+ current_pkg = first[:at_pos]
560
+ else:
561
+ at_pos = first.index("@")
562
+ current_pkg = first[:at_pos]
563
+ root_deps.add(current_pkg)
564
+ continue
565
+
566
+ if current_pkg and line.strip().startswith("version"):
567
+ ver = line.split()[-1].strip('"').strip("'")
568
+ if ver and ver[0].isdigit():
569
+ result[current_pkg] = {
570
+ "version": ver,
571
+ "dev": False,
572
+ "dependencies": {},
573
+ "peers": {},
574
+ }
575
+ current_pkg = None
576
+
577
+ return result, root_deps, root_dev_deps
578
+
579
+
580
+ def parse_pnpm_lockfile(path):
581
+ """Parse pnpm-lock.yaml (v6 and v9 formats)."""
582
+ result = {}
583
+ root_deps = set()
584
+ root_dev_deps = set()
585
+
586
+ with open(path) as f:
587
+ content = f.read()
588
+
589
+ # v6: /@scope/name/1.2.3: or /name/1.2.3:
590
+ for m in re.finditer(r"/(@[^/]+/[^/]+|[^@/\s][^/\s]*)/([0-9][^:\s]+)", content):
591
+ name, ver = m.group(1), m.group(2)
592
+ if name not in result:
593
+ result[name] = {
594
+ "version": ver,
595
+ "dev": False,
596
+ "dependencies": {},
597
+ "peers": {},
598
+ }
599
+
600
+ # v9: '@scope/name@1.2.3': or 'name@1.2.3':
601
+ for m in re.finditer(r"'([^']+)@(\d[^']*)'", content):
602
+ name, ver = m.group(1), m.group(2)
603
+ if name not in result:
604
+ result[name] = {
605
+ "version": ver,
606
+ "dev": False,
607
+ "dependencies": {},
608
+ "peers": {},
609
+ }
610
+
611
+ return result, root_deps, root_dev_deps
612
+
613
+
614
+ def detect_and_parse(path):
615
+ """Auto-detect lockfile format and parse it."""
616
+ name = os.path.basename(path).lower()
617
+
618
+ if "yarn" in name and name.endswith(".lock"):
619
+ return parse_yarn_lockfile(path)
620
+ if "pnpm" in name and name.endswith(".yaml"):
621
+ return parse_pnpm_lockfile(path)
622
+ if "package-lock" in name or "shrinkwrap" in name:
623
+ return parse_npm_lockfile(path)
624
+
625
+ with open(path) as f:
626
+ head = f.read(512)
627
+ if head.lstrip().startswith("{"):
628
+ return parse_npm_lockfile(path)
629
+ if "lockfileVersion" in head:
630
+ return parse_pnpm_lockfile(path)
631
+ return parse_yarn_lockfile(path)
632
+
633
+
634
+ # ── Graph analysis ───────────────────────────────────────────────────────────
635
+
636
+ def build_forward_graph(packages):
637
+ """Build name -> set of dependency names from lockfile data."""
638
+ graph = defaultdict(set)
639
+ for pkg_name, info in packages.items():
640
+ for dep_name in info.get("dependencies", {}):
641
+ graph[pkg_name].add(dep_name)
642
+ for dep_name in info.get("peers", {}):
643
+ graph[pkg_name].add(dep_name)
644
+ return graph
645
+
646
+
647
+ def build_reverse_graph(packages):
648
+ """For each package, find all packages that depend on it."""
649
+ rev = defaultdict(set)
650
+ for pkg_name, info in packages.items():
651
+ for dep_name in info.get("dependencies", {}):
652
+ if dep_name in packages:
653
+ rev[dep_name].add(pkg_name)
654
+ for dep_name in info.get("peers", {}):
655
+ if dep_name in packages:
656
+ rev[dep_name].add(pkg_name)
657
+ return rev
658
+
659
+
660
+ def trace_chains_to_root(target, reverse_graph, root_deps, root_dev_deps):
661
+ """BFS from target back to root. Returns list of (chain, dep_type) tuples.
662
+ dep_type is 'dep' or 'devDep' based on whether the root-level entry is
663
+ in dependencies or devDependencies."""
664
+ chains = []
665
+ queue = deque()
666
+ queue.append([target])
667
+ visited = {target}
668
+
669
+ while queue:
670
+ path = queue.popleft()
671
+ head = path[-1]
672
+
673
+ if head in root_deps or head in root_dev_deps:
674
+ dep_type = "devDep" if head in root_dev_deps and head not in root_deps else "dep"
675
+ chain = list(reversed(path))
676
+ chains.append((chain, dep_type))
677
+ continue
678
+
679
+ parents = reverse_graph.get(head, set())
680
+ if not parents:
681
+ chain = list(reversed(path))
682
+ chains.append((chain, "?"))
683
+ continue
684
+
685
+ for parent in parents:
686
+ if parent not in visited:
687
+ visited.add(parent)
688
+ queue.append(path + [parent])
689
+
690
+ return chains
691
+
692
+
693
+ # ── T4 Shadow: check devDependencies of installed packages ──────────────────
694
+
695
+ def check_shadow_from_node_modules(lockfile_path, packages):
696
+ """Check node_modules on disk near the lockfile for T4 shadow deps."""
697
+ lockdir = os.path.dirname(os.path.abspath(lockfile_path))
698
+ nm_dir = os.path.join(lockdir, "node_modules")
699
+ shadows = {}
700
+
701
+ if not os.path.isdir(nm_dir):
702
+ return shadows
703
+
704
+ for pkg_name in packages:
705
+ parts = pkg_name.split("/")
706
+ if pkg_name.startswith("@"):
707
+ pjson = os.path.join(nm_dir, parts[0], parts[1], "package.json")
708
+ else:
709
+ pjson = os.path.join(nm_dir, parts[0], "package.json")
710
+
711
+ if not os.path.isfile(pjson):
712
+ continue
713
+
714
+ try:
715
+ with open(pjson) as f:
716
+ meta = json.load(f)
717
+ except (json.JSONDecodeError, OSError):
718
+ continue
719
+
720
+ dev_deps = meta.get("devDependencies", {})
721
+ if not dev_deps:
722
+ continue
723
+
724
+ refs = []
725
+ for dep_name in dev_deps:
726
+ ns = matching_namespace(dep_name)
727
+ if ns:
728
+ refs.append(dep_name)
729
+
730
+ if refs:
731
+ shadows[pkg_name] = refs
732
+
733
+ return shadows
734
+
735
+
736
+ def _fetch_shadow_for_pkg(pkg_name, version):
737
+ """Fetch one package from registry, return (pkg_name, refs) or None."""
738
+ url = f"https://registry.npmjs.org/{pkg_name}/{version}"
739
+ try:
740
+ req = urllib.request.Request(url, headers={"Accept": "application/json"})
741
+ with urllib.request.urlopen(req, timeout=10) as resp:
742
+ meta = json.loads(resp.read())
743
+ except (urllib.error.URLError, urllib.error.HTTPError, OSError, json.JSONDecodeError):
744
+ return None
745
+
746
+ dev_deps = meta.get("devDependencies", {})
747
+ if not dev_deps:
748
+ return None
749
+
750
+ refs = [d for d in dev_deps if matching_namespace(d)]
751
+ return (pkg_name, refs) if refs else None
752
+
753
+
754
+ def check_shadow_from_registry(packages):
755
+ """Fetch package metadata from npm registry for T4 shadow deps."""
756
+ shadows = {}
757
+ candidates = {k: v for k, v in packages.items() if not is_compromised_namespace(k)}
758
+ total = len(candidates)
759
+ checked = 0
760
+
761
+ with ThreadPoolExecutor(max_workers=20) as pool:
762
+ futures = {
763
+ pool.submit(_fetch_shadow_for_pkg, name, info["version"]): name
764
+ for name, info in candidates.items()
765
+ }
766
+ for future in as_completed(futures):
767
+ checked += 1
768
+ if sys.stderr.isatty():
769
+ sys.stderr.write(f"\r registry: {checked}/{total} ")
770
+ sys.stderr.flush()
771
+ result = future.result()
772
+ if result:
773
+ shadows[result[0]] = result[1]
774
+
775
+ if sys.stderr.isatty():
776
+ sys.stderr.write("\r" + " " * 60 + "\r")
777
+ sys.stderr.flush()
778
+
779
+ return shadows
780
+
781
+
782
+ # ── IOC scan ─────────────────────────────────────────────────────────────────
783
+
784
+ def scan_iocs_in_file(path):
785
+ """Grep for IOC markers in a file."""
786
+ hits = []
787
+ try:
788
+ with open(path, errors="replace") as f:
789
+ content = f.read()
790
+ except OSError:
791
+ return hits
792
+
793
+ for marker in IOC_MARKERS:
794
+ if marker in content:
795
+ hits.append(marker)
796
+ return hits
797
+
798
+
799
+ def scan_payload_files(root):
800
+ """Find payload files on disk."""
801
+ payloads = []
802
+ bad_names = {"router_init.js", "router_runtime.js", "tanstack_runner.js"}
803
+ bad_hashes = {
804
+ "ab4fcadaec49c03278063dd269ea5eef82d24f2124a8e15d7b90f2fa8601266c",
805
+ "2ec78d556d696e208927cc503d48e4b5eb56b31abc2870c2ed2e98d6be27fc96",
806
+ }
807
+
808
+ for dirpath, dirnames, filenames in os.walk(root):
809
+ dirnames[:] = [d for d in dirnames if d not in (".git",)]
810
+ for fname in filenames:
811
+ if fname in bad_names:
812
+ fpath = os.path.join(dirpath, fname)
813
+ try:
814
+ import hashlib
815
+ sha = hashlib.sha256(open(fpath, "rb").read()).hexdigest()
816
+ except OSError:
817
+ sha = ""
818
+ if sha in bad_hashes:
819
+ payloads.append((fpath, "COMPROMISED"))
820
+ else:
821
+ payloads.append((fpath, "IOC"))
822
+ return payloads
823
+
824
+
825
+ # ── Output formatting ────────────────────────────────────────────────────────
826
+
827
+ def fmt_chain(chain, dep_type):
828
+ arrow = f" {C_DIM}->{C_RST} "
829
+ return f"({dep_type}): {arrow.join(chain)}"
830
+
831
+
832
+ def print_finding(tier, label, pkg, detail, chains=None):
833
+ tier_colors = {
834
+ "T1": C_RED,
835
+ "T2": C_YEL,
836
+ "T3": C_YEL,
837
+ "T4": C_CYN,
838
+ }
839
+ color = tier_colors.get(tier, "")
840
+ print(f" {color}{tier} {label}{C_RST} {C_BLD}{pkg}{C_RST}")
841
+ if detail:
842
+ print(f" {C_DIM}{detail}{C_RST}")
843
+ if chains:
844
+ for chain, dep_type in chains:
845
+ print(f" chain {fmt_chain(chain, dep_type)}")
846
+
847
+
848
+ # ── Main analysis ────────────────────────────────────────────────────────────
849
+
850
+ def find_lockfiles(root):
851
+ lockfile_names = {
852
+ "package-lock.json", "yarn.lock", "pnpm-lock.yaml", "npm-shrinkwrap.json",
853
+ }
854
+ results = []
855
+ for dirpath, dirnames, filenames in os.walk(root):
856
+ dirnames[:] = [d for d in dirnames if d not in ("node_modules", ".git")]
857
+ for fname in filenames:
858
+ if fname in lockfile_names:
859
+ results.append(os.path.join(dirpath, fname))
860
+ return results
861
+
862
+
863
+ def analyze_lockfile(path, deep=False):
864
+ """Analyze one lockfile. Returns (t1_count, t2_count, t3_count, t4_count, ioc_count)."""
865
+ counts = {"t1": 0, "t2": 0, "t3": 0, "t4": 0, "ioc": 0}
866
+
867
+ try:
868
+ packages, root_deps, root_dev_deps = detect_and_parse(path)
869
+ except (json.JSONDecodeError, OSError) as e:
870
+ print(f" {C_RED}ERROR{C_RST} failed to parse: {e}")
871
+ return counts
872
+
873
+ if not packages:
874
+ print(f" {C_DIM}(no packages found){C_RST}")
875
+ return counts
876
+
877
+ reverse_graph = build_reverse_graph(packages)
878
+
879
+ # T1: exact bad version match
880
+ for pkg_name, info in packages.items():
881
+ pair = f"{pkg_name}@{info['version']}"
882
+ if pair in BAD_VERSIONS:
883
+ chains = trace_chains_to_root(pkg_name, reverse_graph, root_deps, root_dev_deps)
884
+ print_finding("T1", "COMPROMISED", pair,
885
+ "exact match against known-bad version", chains)
886
+ counts["t1"] += 1
887
+
888
+ # T2/T3: packages from compromised namespaces in the tree
889
+ for pkg_name, info in packages.items():
890
+ pair = f"{pkg_name}@{info['version']}"
891
+ if pair in BAD_VERSIONS:
892
+ continue
893
+ if not is_compromised_namespace(pkg_name):
894
+ continue
895
+
896
+ if pkg_name in root_deps:
897
+ print_finding("T2", "DIRECT", pair,
898
+ "root dependency from compromised namespace")
899
+ counts["t2"] += 1
900
+ elif pkg_name in root_dev_deps:
901
+ print_finding("T2", "DIRECT", pair,
902
+ "root devDependency from compromised namespace")
903
+ counts["t2"] += 1
904
+ else:
905
+ chains = trace_chains_to_root(pkg_name, reverse_graph, root_deps, root_dev_deps)
906
+ print_finding("T3", "TRANSITIVE", pair,
907
+ "transitive dep from compromised namespace", chains)
908
+ counts["t3"] += 1
909
+
910
+ # Peer/optional deps referencing compromised namespaces (not installed but declared)
911
+ try:
912
+ with open(path, errors="replace") as f:
913
+ raw = f.read()
914
+ except OSError:
915
+ raw = ""
916
+
917
+ for ns in SCOPED_NAMESPACES + UNSCOPED_NAMES:
918
+ if ns in raw:
919
+ installed = any(
920
+ pkg_name.startswith(ns) if ns.endswith("/") else pkg_name == ns
921
+ for pkg_name in packages
922
+ )
923
+ if not installed:
924
+ for pkg_name, info in packages.items():
925
+ peers = info.get("peers", {})
926
+ for peer_name in peers:
927
+ if peer_name.startswith(ns) if ns.endswith("/") else peer_name == ns:
928
+ chains = trace_chains_to_root(pkg_name, reverse_graph, root_deps, root_dev_deps)
929
+ print_finding("T3", "PEER REF", f"{pkg_name}@{info['version']}",
930
+ f"peerDependency references {peer_name} (not installed)",
931
+ chains)
932
+ counts["t3"] += 1
933
+
934
+ # T4: shadow devDependencies
935
+ lockdir = os.path.dirname(os.path.abspath(path))
936
+ has_nm = os.path.isdir(os.path.join(lockdir, "node_modules"))
937
+ shadows = check_shadow_from_node_modules(path, packages) if has_nm else {}
938
+ if not shadows and deep:
939
+ print(f" {C_DIM}(fetching from npm registry...){C_RST}")
940
+ shadows = check_shadow_from_registry(packages)
941
+ if not has_nm and not deep:
942
+ print(f" {C_DIM}(T4 skipped: no node_modules on disk; use --deep for registry check){C_RST}")
943
+
944
+ for pkg_name, refs in shadows.items():
945
+ info = packages[pkg_name]
946
+ chains = trace_chains_to_root(pkg_name, reverse_graph, root_deps, root_dev_deps)
947
+ ref_str = ", ".join(refs)
948
+ print_finding("T4", "SHADOW", f"{pkg_name}@{info['version']}",
949
+ f"devDependencies reference {ref_str}", chains)
950
+ counts["t4"] += 1
951
+
952
+ # IOC markers in lockfile
953
+ iocs = scan_iocs_in_file(path)
954
+ for marker in iocs:
955
+ print(f" {C_RED}T1 IOC{C_RST} {marker}")
956
+ counts["ioc"] += 1
957
+
958
+ return counts
959
+
960
+
961
+ def main():
962
+ args = sys.argv[1:]
963
+ deep = "--deep" in args
964
+ if deep:
965
+ args.remove("--deep")
966
+
967
+ paths = args if args else ["."]
968
+
969
+ lockfiles = []
970
+ for p in paths:
971
+ if os.path.isfile(p):
972
+ lockfiles.append(p)
973
+ elif os.path.isdir(p):
974
+ lockfiles.extend(find_lockfiles(p))
975
+ else:
976
+ print(f"{C_RED}Not found:{C_RST} {p}", file=sys.stderr)
977
+
978
+ if not lockfiles:
979
+ print("No lockfiles found.")
980
+ sys.exit(0)
981
+
982
+ print(f"Scanning {len(lockfiles)} lockfile(s) for Mini Shai-Hulud supply chain attack...\n")
983
+
984
+ totals = {"t1": 0, "t2": 0, "t3": 0, "t4": 0, "ioc": 0}
985
+
986
+ for lf in lockfiles:
987
+ print(f"{C_DIM}== {lf} =={C_RST}")
988
+ counts = analyze_lockfile(lf, deep=deep)
989
+ for k in totals:
990
+ totals[k] += counts[k]
991
+ if all(v == 0 for v in counts.values()):
992
+ print(f" {C_GRN}Clean{C_RST}")
993
+ print()
994
+
995
+ # Payload files on disk
996
+ dirs_checked = set()
997
+ for lf in lockfiles:
998
+ d = os.path.dirname(os.path.abspath(lf))
999
+ if d not in dirs_checked:
1000
+ dirs_checked.add(d)
1001
+ payloads = scan_payload_files(d)
1002
+ for fpath, severity in payloads:
1003
+ if severity == "COMPROMISED":
1004
+ print(f" {C_RED}T1 PAYLOAD{C_RST} Malicious file: {fpath}")
1005
+ totals["t1"] += 1
1006
+ else:
1007
+ print(f" {C_YEL}T1 IOC{C_RST} Suspicious filename: {fpath}")
1008
+ totals["ioc"] += 1
1009
+
1010
+ # Summary
1011
+ critical = totals["t1"] + totals["ioc"]
1012
+ review = totals["t2"] + totals["t3"]
1013
+ shadow = totals["t4"]
1014
+
1015
+ print(f"{C_BLD}Summary:{C_RST}")
1016
+ if critical > 0:
1017
+ print(f" {C_RED}{critical} confirmed finding(s) (T1/IOC){C_RST}")
1018
+ print(" If npm/yarn/pnpm install ran with a compromised version, assume credential theft.")
1019
+ print(" Rotate: npm tokens, GitHub tokens/OIDC, AWS creds, Vault tokens, CI/CD secrets.")
1020
+ if review > 0:
1021
+ print(f" {C_YEL}{review} namespace hit(s) in dependency tree (T2/T3){C_RST}")
1022
+ print(" Verify these packages are on clean versions.")
1023
+ if shadow > 0:
1024
+ print(f" {C_CYN}{shadow} shadow devDependency reference(s) (T4){C_RST}")
1025
+ print(" These packages' build tooling was targeted. Monitor for future compromise.")
1026
+ if critical == 0 and review == 0 and shadow == 0:
1027
+ print(f" {C_GRN}Clean: no compromised versions, IOC markers, or affected namespaces found.{C_RST}")
1028
+
1029
+ if critical > 0:
1030
+ print(f"\n Details: https://www.aikido.dev/blog/mini-shai-hulud-is-back-tanstack-compromised")
1031
+ sys.exit(1)
1032
+ sys.exit(0)
1033
+
1034
+
1035
+ if __name__ == "__main__":
1036
+ main()