diff --git a/packages/backend-core/src/users/db.ts b/packages/backend-core/src/users/db.ts index 04d3264e6f..6165a68e57 100644 --- a/packages/backend-core/src/users/db.ts +++ b/packages/backend-core/src/users/db.ts @@ -45,6 +45,7 @@ type GroupFns = { getGroupBuilderAppIds: GroupBuildersFn } type CreateAdminUserOpts = { + password?: string ssoId?: string hashPassword?: boolean requirePassword?: boolean @@ -501,9 +502,9 @@ export class UserDB { static async createAdminUser( email: string, tenantId: string, - password?: string, opts?: CreateAdminUserOpts ) { + const password = opts?.password const user: User = { email: email, password, diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts index aa96a30b00..75da54cbaf 100644 --- a/packages/server/src/app.ts +++ b/packages/server/src/app.ts @@ -21,7 +21,7 @@ async function start() { app = koa.app server = koa.server // startup includes automation runner - if enabled - await startup(app, server) + await startup({ app, server }) } start().catch(err => { diff --git a/packages/server/src/startup.ts b/packages/server/src/startup/index.ts similarity index 80% rename from packages/server/src/startup.ts rename to packages/server/src/startup/index.ts index 2cedda1099..48d500a0cf 100644 --- a/packages/server/src/startup.ts +++ b/packages/server/src/startup/index.ts @@ -1,6 +1,6 @@ -import env from "./environment" -import * as redis from "./utilities/redis" -import { generateApiKey, getChecklist } from "./utilities/workerRequests" +import env from "../environment" +import * as redis from "../utilities/redis" +import { generateApiKey, getChecklist } from "../utilities/workerRequests" import { events, installation, @@ -9,22 +9,22 @@ import { users, cache, } from "@budibase/backend-core" -import fs from "fs" -import { watch } from "./watch" -import * as automations from "./automations" -import * as fileSystem from "./utilities/fileSystem" -import { default as eventEmitter, init as eventInit } from "./events" -import * as migrations from "./migrations" -import * as bullboard from "./automations/bullboard" +import { watch } from "../watch" +import * as automations from "../automations" +import * as fileSystem from "../utilities/fileSystem" +import { default as eventEmitter, init as eventInit } from "../events" +import * as migrations from "../migrations" +import * as bullboard from "../automations/bullboard" import * as pro from "@budibase/pro" -import * as api from "./api" -import sdk from "./sdk" -import { initialise as initialiseWebsockets } from "./websockets" -import { automationsEnabled, printFeatures } from "./features" +import * as api from "../api" +import sdk from "../sdk" +import { initialise as initialiseWebsockets } from "../websockets" +import { automationsEnabled, printFeatures } from "../features" +import * as jsRunner from "../jsRunner" import Koa from "koa" import { Server } from "http" import { AddressInfo } from "net" -import * as jsRunner from "./jsRunner" +import fs from "fs" let STARTUP_RAN = false @@ -61,8 +61,11 @@ function shutdown(server?: Server) { } } -export async function startup(app?: Koa, server?: Server) { - if (STARTUP_RAN) { +export async function startup( + opts: { app?: Koa; server?: Server; rerun?: boolean } = {} +) { + const { app, server, rerun } = opts + if (STARTUP_RAN && !rerun) { return } printFeatures() @@ -139,9 +142,9 @@ export async function startup(app?: Koa, server?: Server) { try { const user = await users.UserDB.createAdminUser( bbAdminEmail, - bbAdminPassword, tenantId, { + password: bbAdminPassword, hashPassword: true, requirePassword: true, skipPasswordValidation: true, diff --git a/packages/server/src/startup/tests/startup.spec.ts b/packages/server/src/startup/tests/startup.spec.ts new file mode 100644 index 0000000000..ed31bc45b7 --- /dev/null +++ b/packages/server/src/startup/tests/startup.spec.ts @@ -0,0 +1,34 @@ +import TestConfiguration from "../../tests/utilities/TestConfiguration" +import { startup } from "../index" +import { users, utils, tenancy } from "@budibase/backend-core" + +describe("check BB_ADMIN environment variables", () => { + const config = new TestConfiguration() + beforeAll(async () => { + await config.init() + }) + + it("should be able to create a user with the BB_ADMIN environment variables", async () => { + const EMAIL = "budibase@budibase.com", + PASSWORD = "budibase" + await tenancy.doInTenant(tenancy.DEFAULT_TENANT_ID, async () => { + await config.withEnv( + { + BB_ADMIN_USER_EMAIL: EMAIL, + BB_ADMIN_USER_PASSWORD: PASSWORD, + MULTI_TENANCY: "0", + SELF_HOSTED: "1", + }, + async () => { + await startup({ rerun: true }) + const user = await users.getGlobalUserByEmail(EMAIL, { + cleanup: false, + }) + expect(user).toBeDefined() + expect(user?.password).toBeDefined() + expect(await utils.compare(PASSWORD, user?.password!)).toEqual(true) + } + ) + }) + }) +}) diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index 0c1342fa08..4c1af90d38 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -146,16 +146,12 @@ export const adminUser = async ( } try { - const finalUser = await userSdk.db.createAdminUser( - email, - tenantId, + const finalUser = await userSdk.db.createAdminUser(email, tenantId, { password, - { - ssoId, - hashPassword, - requirePassword, - } - ) + ssoId, + hashPassword, + requirePassword, + }) // events let account: CloudAccount | undefined diff --git a/yarn.lock b/yarn.lock index 3497a55b0a..f4c0f56414 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5098,15 +5098,6 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== -"@trendyol/jest-testcontainers@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@trendyol/jest-testcontainers/-/jest-testcontainers-2.1.1.tgz#dced95cf9c37b75efe0a65db9b75ae8912f2f14a" - integrity sha512-4iAc2pMsev4BTUzoA7jO1VvbTOU2N3juQUYa8TwiSPXPuQtxKwV9WB9ZEP+JQ+Pj15YqfGOXp5H0WNMPtapjiA== - dependencies: - cwd "^0.10.0" - node-duration "^1.0.4" - testcontainers "4.7.0" - "@trysound/sax@0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" @@ -5296,13 +5287,6 @@ "@types/node" "*" "@types/ssh2" "*" -"@types/dockerode@^2.5.34": - version "2.5.34" - resolved "https://registry.yarnpkg.com/@types/dockerode/-/dockerode-2.5.34.tgz#9adb884f7cc6c012a6eb4b2ad794cc5d01439959" - integrity sha512-LcbLGcvcBwBAvjH9UrUI+4qotY+A5WCer5r43DR5XHv2ZIEByNXFdPLo1XxR+v/BjkGjlggW8qUiXuVEhqfkpA== - dependencies: - "@types/node" "*" - "@types/dockerode@^3.3.24": version "3.3.24" resolved "https://registry.yarnpkg.com/@types/dockerode/-/dockerode-3.3.24.tgz#bea354a4fcd0824a80fd5ea5ede3e8cda71137a7" @@ -9152,14 +9136,6 @@ curlconverter@3.21.0: string.prototype.startswith "^1.0.0" yamljs "^0.3.0" -cwd@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/cwd/-/cwd-0.10.0.tgz#172400694057c22a13b0cf16162c7e4b7a7fe567" - integrity sha512-YGZxdTTL9lmLkCUTpg4j0zQ7IhRB5ZmqNBbGCl3Tg6MP/d5/6sY7L5mmTjzbc6JKgVZYiqTQTNhPFsbXNGlRaA== - dependencies: - find-pkg "^0.1.2" - fs-exists-sync "^0.1.0" - dargs@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" @@ -9781,7 +9757,7 @@ docker-compose@0.24.0: dependencies: yaml "^1.10.2" -docker-compose@^0.23.5, docker-compose@^0.23.6: +docker-compose@^0.23.6: version "0.23.19" resolved "https://registry.yarnpkg.com/docker-compose/-/docker-compose-0.23.19.tgz#9947726e2fe67bdfa9e8efe1ff15aa0de2e10eb8" integrity sha512-v5vNLIdUqwj4my80wxFDkNH+4S85zsRuH29SO7dCWVWPCMt/ohZBsGN6g6KXWifT0pzQ7uOxqEKCYCDPJ8Vz4g== @@ -9805,7 +9781,7 @@ docker-modem@^3.0.0: split-ca "^1.0.1" ssh2 "^1.11.0" -dockerode@^3.2.1, dockerode@^3.3.5: +dockerode@^3.3.5: version "3.3.5" resolved "https://registry.yarnpkg.com/dockerode/-/dockerode-3.3.5.tgz#7ae3f40f2bec53ae5e9a741ce655fff459745629" integrity sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA== @@ -10830,13 +10806,6 @@ expand-template@^2.0.3: resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== -expand-tilde@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-1.2.2.tgz#0b81eba897e5a3d31d1c3d102f8f01441e559449" - integrity sha512-rtmc+cjLZqnu9dSYosX9EWmSJhTwpACgJQTfj4hgg2JjOD/6SIQalZrt4a3aQeh++oNxkazcaxrhPUj6+g5G/Q== - dependencies: - os-homedir "^1.0.1" - expand-tilde@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" @@ -11175,26 +11144,11 @@ filter-obj@^1.1.0: resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ== -find-file-up@^0.1.2: - version "0.1.3" - resolved "https://registry.yarnpkg.com/find-file-up/-/find-file-up-0.1.3.tgz#cf68091bcf9f300a40da411b37da5cce5a2fbea0" - integrity sha512-mBxmNbVyjg1LQIIpgO8hN+ybWBgDQK8qjht+EbrTCGmmPV/sc7RF1i9stPTD6bpvXZywBdrwRYxhSdJv867L6A== - dependencies: - fs-exists-sync "^0.1.0" - resolve-dir "^0.1.0" - find-free-port@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/find-free-port/-/find-free-port-2.0.0.tgz#4b22e5f6579eb1a38c41ac6bcb3efed1b6da9b1b" integrity sha512-J1j8gfEVf5FN4PR5w5wrZZ7NYs2IvqsHcd03cAeQx3Ec/mo+lKceaVNhpsRKoZpZKbId88o8qh+dwUwzBV6WCg== -find-pkg@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/find-pkg/-/find-pkg-0.1.2.tgz#1bdc22c06e36365532e2a248046854b9788da557" - integrity sha512-0rnQWcFwZr7eO0513HahrWafsc3CTFioEB7DRiEYCUM/70QXSY8f3mCST17HXLcPvEhzH/Ty/Bxd72ZZsr/yvw== - dependencies: - find-file-up "^0.1.2" - find-up@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" @@ -11362,11 +11316,6 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs-exists-sync@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add" - integrity sha512-cR/vflFyPZtrN6b38ZyWxpWdhlXrzZEBawlpBQMq7033xVY7/kg0GDMBK5jg8lDYQckdJ5x/YC88lM3C7VMsLg== - fs-extra@^10.0.0: version "10.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" @@ -11863,24 +11812,6 @@ global-dirs@^3.0.0: dependencies: ini "2.0.0" -global-modules@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-0.2.3.tgz#ea5a3bed42c6d6ce995a4f8a1269b5dae223828d" - integrity sha512-JeXuCbvYzYXcwE6acL9V2bAOeSIGl4dD+iwLY9iUx2VBJJ80R18HCn+JCwHM9Oegdfya3lEkGCdaRkSyc10hDA== - dependencies: - global-prefix "^0.1.4" - is-windows "^0.2.0" - -global-prefix@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-0.1.5.tgz#8d3bc6b8da3ca8112a160d8d496ff0462bfef78f" - integrity sha512-gOPiyxcD9dJGCEArAhF4Hd0BAqvAe/JzERP7tYumE4yIkmIedPUVXcJFWbV3/p/ovIIvKjkrTk+f1UVkq7vvbw== - dependencies: - homedir-polyfill "^1.0.0" - ini "^1.3.4" - is-windows "^0.2.0" - which "^1.2.12" - global@~4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" @@ -12311,7 +12242,7 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -homedir-polyfill@^1.0.0, homedir-polyfill@^1.0.1: +homedir-polyfill@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== @@ -13285,11 +13216,6 @@ is-whitespace@^0.3.0: resolved "https://registry.yarnpkg.com/is-whitespace/-/is-whitespace-0.3.0.tgz#1639ecb1be036aec69a54cbb401cfbed7114ab7f" integrity sha512-RydPhl4S6JwAyj0JJjshWJEFG6hNye3pZFBRZaTUfZFwGHxzppNaNOVgQuS/E/SlhrApuMXrpnK1EEIXfdo3Dg== -is-windows@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" - integrity sha512-n67eJYmXbniZB7RF4I/FTjK1s6RPOCTxhYrVYLRaCt3lF0mpWZPKr3T2LSZAqyjQsxR2qMmGYXXzK0YWwcPM1Q== - is-wsl@^2.1.1, is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" @@ -16116,7 +16042,7 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -nock@^13.5.4: +nock@13.5.4, nock@^13.5.4: version "13.5.4" resolved "https://registry.yarnpkg.com/nock/-/nock-13.5.4.tgz#8918f0addc70a63736170fef7106a9721e0dc479" integrity sha512-yAyTfdeNJGGBFxWdzSKCBYxs5FxLbCg5X5Q4ets974hcQzG1+qCxvIyOo4j2Ry6MUlhWVMX4OoYDefAIIwupjw== @@ -16152,11 +16078,6 @@ node-addon-api@^6.1.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76" integrity sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA== -node-duration@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/node-duration/-/node-duration-1.0.4.tgz#3e94ecc0e473691c89c4560074503362071cecac" - integrity sha512-eUXYNSY7DL53vqfTosggWkvyIW3bhAcqBDIlolgNYlZhianXTrCL50rlUJWD1eRqkIxMppXTfiFbp+9SjpPrgA== - node-fetch@2.6.0, node-fetch@2.6.7, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7, node-fetch@^2.6.9, node-fetch@^2.7.0: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" @@ -16903,11 +16824,6 @@ oracledb@5.3.0: resolved "https://registry.yarnpkg.com/oracledb/-/oracledb-5.3.0.tgz#a15e6cd16757d8711a2c006a28bd7ecd3b8466f7" integrity sha512-HMJzQ6lCf287ztvvehTEmjCWA21FQ3RMvM+mgoqd4i8pkREuqFWO+y3ovsGR9moJUg4T0xjcwS8rl4mggWPxmg== -os-homedir@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - integrity sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ== - os-locale@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" @@ -19227,14 +19143,6 @@ resolve-dependency-path@^2.0.0: resolved "https://registry.yarnpkg.com/resolve-dependency-path/-/resolve-dependency-path-2.0.0.tgz#11700e340717b865d216c66cabeb4a2a3c696736" integrity sha512-DIgu+0Dv+6v2XwRaNWnumKu7GPufBBOr5I1gRPJHkvghrfCGOooJODFvgFimX/KRxk9j0whD2MnKHzM1jYvk9w== -resolve-dir@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-0.1.1.tgz#b219259a5602fac5c5c496ad894a6e8cc430261e" - integrity sha512-QxMPqI6le2u0dCLyiGzgy92kjkkL6zO0XyvHzjdTNH3zM6e5Hz3BwG6+aEyNgiQ5Xz6PwTwgQEj3U50dByPKIA== - dependencies: - expand-tilde "^1.2.2" - global-modules "^0.2.3" - resolve-from@5.0.0, resolve-from@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" @@ -20924,7 +20832,7 @@ tapable@^2.1.1, tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -tar-fs@2.1.1, tar-fs@^2.0.0, tar-fs@^2.1.0: +tar-fs@2.1.1, tar-fs@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== @@ -21102,23 +21010,6 @@ testcontainers@10.7.2, testcontainers@^10.7.2: tar-fs "^3.0.5" tmp "^0.2.1" -testcontainers@4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/testcontainers/-/testcontainers-4.7.0.tgz#5a9a864b1b0cc86984086dcc737c2f5e73490cf3" - integrity sha512-5SrG9RMfDRRZig34fDZeMcGD5i3lHCOJzn0kjouyK4TiEWjZB3h7kCk8524lwNRHROFE1j6DGjceonv/5hl5ag== - dependencies: - "@types/dockerode" "^2.5.34" - byline "^5.0.0" - debug "^4.1.1" - docker-compose "^0.23.5" - dockerode "^3.2.1" - get-port "^5.1.1" - glob "^7.1.6" - node-duration "^1.0.4" - slash "^3.0.0" - stream-to-array "^2.3.0" - tar-fs "^2.1.0" - text-extensions@^1.0.0: version "1.9.0" resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" @@ -22357,7 +22248,7 @@ which-typed-array@^1.1.11, which-typed-array@^1.1.13, which-typed-array@^1.1.9: gopd "^1.0.1" has-tostringtag "^1.0.0" -which@^1.2.12, which@^1.2.9: +which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==