aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2022-07-12 10:54:21 +0200
committerChocobozzz <me@florianbigard.com>2022-07-12 12:08:12 +0200
commit5d7cb63ede7c4bba93954c0586f589ad9748d5ea (patch)
tree12856de40417d16ebe86c16a9febe7cc9f36091b
parent0667dbaf268e05b5c9d22f662532e86a1233741c (diff)
downloadPeerTube-5d7cb63ede7c4bba93954c0586f589ad9748d5ea.tar.gz
PeerTube-5d7cb63ede7c4bba93954c0586f589ad9748d5ea.tar.zst
PeerTube-5d7cb63ede7c4bba93954c0586f589ad9748d5ea.zip
Add compat with openssl 3
-rw-r--r--package.json4
-rw-r--r--server/helpers/core-utils.ts56
-rw-r--r--server/helpers/custom-validators/activitypub/actor.ts4
-rw-r--r--server/helpers/peertube-crypto.ts9
-rw-r--r--server/initializers/migrations/0605-actor-missing-keys.ts7
-rw-r--r--yarn.lock58
6 files changed, 65 insertions, 73 deletions
diff --git a/package.json b/package.json
index 79bc7cf1f..bf83daa89 100644
--- a/package.json
+++ b/package.json
@@ -102,7 +102,7 @@
102 "@opentelemetry/sdk-trace-node": "^1.3.1", 102 "@opentelemetry/sdk-trace-node": "^1.3.1",
103 "@opentelemetry/semantic-conventions": "^1.3.1", 103 "@opentelemetry/semantic-conventions": "^1.3.1",
104 "@peertube/feed": "^5.0.1", 104 "@peertube/feed": "^5.0.1",
105 "@peertube/http-signature": "^1.6.0", 105 "@peertube/http-signature": "^1.7.0",
106 "@uploadx/core": "^5.1.2", 106 "@uploadx/core": "^5.1.2",
107 "async": "^3.0.1", 107 "async": "^3.0.1",
108 "async-lru": "^1.1.1", 108 "async-lru": "^1.1.1",
@@ -151,7 +151,6 @@
151 "nodemailer": "^6.0.0", 151 "nodemailer": "^6.0.0",
152 "parse-torrent": "^9.1.0", 152 "parse-torrent": "^9.1.0",
153 "password-generator": "^2.0.2", 153 "password-generator": "^2.0.2",
154 "pem": "^1.12.3",
155 "pg": "^8.2.1", 154 "pg": "^8.2.1",
156 "piscina": "^3.2.0", 155 "piscina": "^3.2.0",
157 "prompt": "^1.0.0", 156 "prompt": "^1.0.0",
@@ -206,7 +205,6 @@
206 "@types/node": "^14.14.31", 205 "@types/node": "^14.14.31",
207 "@types/nodemailer": "^6.2.0", 206 "@types/nodemailer": "^6.2.0",
208 "@types/oauth2-server": "^3.0.8", 207 "@types/oauth2-server": "^3.0.8",
209 "@types/pem": "^1.9.3",
210 "@types/request": "^2.0.3", 208 "@types/request": "^2.0.3",
211 "@types/supertest": "^2.0.3", 209 "@types/supertest": "^2.0.3",
212 "@types/validator": "^13.0.0", 210 "@types/validator": "^13.0.0",
diff --git a/server/helpers/core-utils.ts b/server/helpers/core-utils.ts
index 4bbf0228d..c762f6a29 100644
--- a/server/helpers/core-utils.ts
+++ b/server/helpers/core-utils.ts
@@ -6,9 +6,8 @@
6*/ 6*/
7 7
8import { exec, ExecOptions } from 'child_process' 8import { exec, ExecOptions } from 'child_process'
9import { randomBytes } from 'crypto' 9import { ED25519KeyPairOptions, generateKeyPair, randomBytes, RSAKeyPairOptions } from 'crypto'
10import { truncate } from 'lodash' 10import { truncate } from 'lodash'
11import { createPrivateKey as createPrivateKey_1, getPublicKey as getPublicKey_1 } from 'pem'
12import { pipeline } from 'stream' 11import { pipeline } from 'stream'
13import { URL } from 'url' 12import { URL } from 'url'
14import { promisify } from 'util' 13import { promisify } from 'util'
@@ -242,6 +241,51 @@ function toEven (num: number) {
242 241
243// --------------------------------------------------------------------------- 242// ---------------------------------------------------------------------------
244 243
244function generateRSAKeyPairPromise (size: number) {
245 return new Promise<{ publicKey: string, privateKey: string }>((res, rej) => {
246 const options: RSAKeyPairOptions<'pem', 'pem'> = {
247 modulusLength: size,
248 publicKeyEncoding: {
249 type: 'spki',
250 format: 'pem'
251 },
252 privateKeyEncoding: {
253 type: 'pkcs1',
254 format: 'pem'
255 }
256 }
257
258 generateKeyPair('rsa', options, (err, publicKey, privateKey) => {
259 if (err) return rej(err)
260
261 return res({ publicKey, privateKey })
262 })
263 })
264}
265
266function generateED25519KeyPairPromise () {
267 return new Promise<{ publicKey: string, privateKey: string }>((res, rej) => {
268 const options: ED25519KeyPairOptions<'pem', 'pem'> = {
269 publicKeyEncoding: {
270 type: 'spki',
271 format: 'pem'
272 },
273 privateKeyEncoding: {
274 type: 'pkcs8',
275 format: 'pem'
276 }
277 }
278
279 generateKeyPair('ed25519', options, (err, publicKey, privateKey) => {
280 if (err) return rej(err)
281
282 return res({ publicKey, privateKey })
283 })
284 })
285}
286
287// ---------------------------------------------------------------------------
288
245function promisify0<A> (func: (cb: (err: any, result: A) => void) => void): () => Promise<A> { 289function promisify0<A> (func: (cb: (err: any, result: A) => void) => void): () => Promise<A> {
246 return function promisified (): Promise<A> { 290 return function promisified (): Promise<A> {
247 return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => { 291 return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
@@ -268,8 +312,6 @@ function promisify2<T, U, A> (func: (arg1: T, arg2: U, cb: (err: any, result: A)
268} 312}
269 313
270const randomBytesPromise = promisify1<number, Buffer>(randomBytes) 314const randomBytesPromise = promisify1<number, Buffer>(randomBytes)
271const createPrivateKey = promisify1<number, { key: string }>(createPrivateKey_1)
272const getPublicKey = promisify1<string, { publicKey: string }>(getPublicKey_1)
273const execPromise2 = promisify2<string, any, string>(exec) 315const execPromise2 = promisify2<string, any, string>(exec)
274const execPromise = promisify1<string, string>(exec) 316const execPromise = promisify1<string, string>(exec)
275const pipelinePromise = promisify(pipeline) 317const pipelinePromise = promisify(pipeline)
@@ -298,8 +340,10 @@ export {
298 promisify2, 340 promisify2,
299 341
300 randomBytesPromise, 342 randomBytesPromise,
301 createPrivateKey, 343
302 getPublicKey, 344 generateRSAKeyPairPromise,
345 generateED25519KeyPairPromise,
346
303 execPromise2, 347 execPromise2,
304 execPromise, 348 execPromise,
305 pipelinePromise, 349 pipelinePromise,
diff --git a/server/helpers/custom-validators/activitypub/actor.ts b/server/helpers/custom-validators/activitypub/actor.ts
index a4b152722..f43c35b23 100644
--- a/server/helpers/custom-validators/activitypub/actor.ts
+++ b/server/helpers/custom-validators/activitypub/actor.ts
@@ -41,9 +41,9 @@ function isActorPreferredUsernameValid (preferredUsername: string) {
41function isActorPrivateKeyValid (privateKey: string) { 41function isActorPrivateKeyValid (privateKey: string) {
42 return exists(privateKey) && 42 return exists(privateKey) &&
43 typeof privateKey === 'string' && 43 typeof privateKey === 'string' &&
44 privateKey.startsWith('-----BEGIN RSA PRIVATE KEY-----') && 44 (privateKey.startsWith('-----BEGIN RSA PRIVATE KEY-----') || privateKey.startsWith('-----BEGIN PRIVATE KEY-----')) &&
45 // Sometimes there is a \n at the end, so just assert the string contains the end mark 45 // Sometimes there is a \n at the end, so just assert the string contains the end mark
46 privateKey.includes('-----END RSA PRIVATE KEY-----') && 46 (privateKey.includes('-----END RSA PRIVATE KEY-----') || privateKey.includes('-----END PRIVATE KEY-----')) &&
47 validator.isLength(privateKey, CONSTRAINTS_FIELDS.ACTORS.PRIVATE_KEY) 47 validator.isLength(privateKey, CONSTRAINTS_FIELDS.ACTORS.PRIVATE_KEY)
48} 48}
49 49
diff --git a/server/helpers/peertube-crypto.ts b/server/helpers/peertube-crypto.ts
index 1a7ee24a7..1d9cab2ce 100644
--- a/server/helpers/peertube-crypto.ts
+++ b/server/helpers/peertube-crypto.ts
@@ -5,7 +5,7 @@ import { cloneDeep } from 'lodash'
5import { sha256 } from '@shared/extra-utils' 5import { sha256 } from '@shared/extra-utils'
6import { BCRYPT_SALT_SIZE, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers/constants' 6import { BCRYPT_SALT_SIZE, HTTP_SIGNATURE, PRIVATE_RSA_KEY_SIZE } from '../initializers/constants'
7import { MActor } from '../types/models' 7import { MActor } from '../types/models'
8import { createPrivateKey, getPublicKey, promisify1, promisify2 } from './core-utils' 8import { generateRSAKeyPairPromise, promisify1, promisify2 } from './core-utils'
9import { jsonld } from './custom-jsonld-signature' 9import { jsonld } from './custom-jsonld-signature'
10import { logger } from './logger' 10import { logger } from './logger'
11 11
@@ -15,13 +15,10 @@ const bcryptHashPromise = promisify2<any, string | number, string>(hash)
15 15
16const httpSignature = require('@peertube/http-signature') 16const httpSignature = require('@peertube/http-signature')
17 17
18async function createPrivateAndPublicKeys () { 18function createPrivateAndPublicKeys () {
19 logger.info('Generating a RSA key...') 19 logger.info('Generating a RSA key...')
20 20
21 const { key } = await createPrivateKey(PRIVATE_RSA_KEY_SIZE) 21 return generateRSAKeyPairPromise(PRIVATE_RSA_KEY_SIZE)
22 const { publicKey } = await getPublicKey(key)
23
24 return { privateKey: key, publicKey }
25} 22}
26 23
27// User password checks 24// User password checks
diff --git a/server/initializers/migrations/0605-actor-missing-keys.ts b/server/initializers/migrations/0605-actor-missing-keys.ts
index 72d9b359d..aa89a500c 100644
--- a/server/initializers/migrations/0605-actor-missing-keys.ts
+++ b/server/initializers/migrations/0605-actor-missing-keys.ts
@@ -1,5 +1,5 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { createPrivateKey, getPublicKey } from '../../helpers/core-utils' 2import { generateRSAKeyPairPromise } from '../../helpers/core-utils'
3import { PRIVATE_RSA_KEY_SIZE } from '../constants' 3import { PRIVATE_RSA_KEY_SIZE } from '../constants'
4 4
5async function up (utils: { 5async function up (utils: {
@@ -15,10 +15,9 @@ async function up (utils: {
15 const actors = await utils.sequelize.query<any>(query, options) 15 const actors = await utils.sequelize.query<any>(query, options)
16 16
17 for (const actor of actors) { 17 for (const actor of actors) {
18 const { key } = await createPrivateKey(PRIVATE_RSA_KEY_SIZE) 18 const { privateKey, publicKey } = await generateRSAKeyPairPromise(PRIVATE_RSA_KEY_SIZE)
19 const { publicKey } = await getPublicKey(key)
20 19
21 const queryUpdate = `UPDATE "actor" SET "publicKey" = '${publicKey}', "privateKey" = '${key}' WHERE id = ${actor.id}` 20 const queryUpdate = `UPDATE "actor" SET "publicKey" = '${publicKey}', "privateKey" = '${privateKey}' WHERE id = ${actor.id}`
22 await utils.sequelize.query(queryUpdate) 21 await utils.sequelize.query(queryUpdate)
23 } 22 }
24 } 23 }
diff --git a/yarn.lock b/yarn.lock
index 530482b22..c02606375 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1780,10 +1780,10 @@
1780 dependencies: 1780 dependencies:
1781 xml-js "^1.6.11" 1781 xml-js "^1.6.11"
1782 1782
1783"@peertube/http-signature@^1.6.0": 1783"@peertube/http-signature@^1.7.0":
1784 version "1.6.0" 1784 version "1.7.0"
1785 resolved "https://registry.yarnpkg.com/@peertube/http-signature/-/http-signature-1.6.0.tgz#22bef028384e6437e8dbd94052ba7b8bd7f7f1ae" 1785 resolved "https://registry.yarnpkg.com/@peertube/http-signature/-/http-signature-1.7.0.tgz#12a84f3fc62e786aa3a2eb09426417bad65736dc"
1786 integrity sha512-Bx780c7FPYtkV4LgCoaJcXYcKQqaMef2iQR2V2r5klkYkIQWFxbTOpyhKxvVXYIBIFpj5Cb8DGVDAmhkm7aavg== 1786 integrity sha512-aGQIwo6/sWtyyqhVK4e1MtxYz4N1X8CNt6SOtCc+Wnczs5S5ONaLHDDR8LYaGn0MgOwvGgXyuZ5sJIfd7iyoUw==
1787 dependencies: 1787 dependencies:
1788 assert-plus "^1.0.0" 1788 assert-plus "^1.0.0"
1789 jsprim "^1.2.2" 1789 jsprim "^1.2.2"
@@ -2221,13 +2221,6 @@
2221 "@types/node" "*" 2221 "@types/node" "*"
2222 "@types/parse-torrent-file" "*" 2222 "@types/parse-torrent-file" "*"
2223 2223
2224"@types/pem@^1.9.3":
2225 version "1.9.6"
2226 resolved "https://registry.yarnpkg.com/@types/pem/-/pem-1.9.6.tgz#c3686832e935947fdd9d848dec3b8fe830068de7"
2227 integrity sha512-IC67SxacM9fxEi/w7hf98dTun83OwUMeLMo1NS2gE0wdM9MHeg73iH/Pp9nB02OUCQ7Zb2UuKE/IpFCmQw9jxw==
2228 dependencies:
2229 "@types/node" "*"
2230
2231"@types/pg-pool@2.0.3": 2224"@types/pg-pool@2.0.3":
2232 version "2.0.3" 2225 version "2.0.3"
2233 resolved "https://registry.yarnpkg.com/@types/pg-pool/-/pg-pool-2.0.3.tgz#3eb8df2933f617f219a53091ad4080c94ba1c959" 2226 resolved "https://registry.yarnpkg.com/@types/pg-pool/-/pg-pool-2.0.3.tgz#3eb8df2933f617f219a53091ad4080c94ba1c959"
@@ -3332,11 +3325,6 @@ character-parser@^2.2.0:
3332 dependencies: 3325 dependencies:
3333 is-regex "^1.0.3" 3326 is-regex "^1.0.3"
3334 3327
3335charenc@0.0.2:
3336 version "0.0.2"
3337 resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
3338 integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==
3339
3340charset-detector@0.0.2: 3328charset-detector@0.0.2:
3341 version "0.0.2" 3329 version "0.0.2"
3342 resolved "https://registry.yarnpkg.com/charset-detector/-/charset-detector-0.0.2.tgz#1cd5ddaf56e83259c6ef8e906ccf06f75fe9a1b2" 3330 resolved "https://registry.yarnpkg.com/charset-detector/-/charset-detector-0.0.2.tgz#1cd5ddaf56e83259c6ef8e906ccf06f75fe9a1b2"
@@ -3778,11 +3766,6 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3:
3778 shebang-command "^2.0.0" 3766 shebang-command "^2.0.0"
3779 which "^2.0.1" 3767 which "^2.0.1"
3780 3768
3781crypt@0.0.2:
3782 version "0.0.2"
3783 resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
3784 integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==
3785
3786css-select@^5.1.0: 3769css-select@^5.1.0:
3787 version "5.1.0" 3770 version "5.1.0"
3788 resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" 3771 resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6"
@@ -4314,11 +4297,6 @@ es6-iterator@^2.0.3:
4314 es5-ext "^0.10.35" 4297 es5-ext "^0.10.35"
4315 es6-symbol "^3.1.1" 4298 es6-symbol "^3.1.1"
4316 4299
4317es6-promisify@^6.0.0:
4318 version "6.1.1"
4319 resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-6.1.1.tgz#46837651b7b06bf6fff893d03f29393668d01621"
4320 integrity sha512-HBL8I3mIki5C1Cc9QjKUenHtnG0A5/xA8Q/AllRcfiwl2CZFXGK7ddBiCoRwAix4i2KxcQfjtIVcrVbB3vbmwg==
4321
4322es6-symbol@^3.1.1, es6-symbol@^3.1.3: 4300es6-symbol@^3.1.1, es6-symbol@^3.1.3:
4323 version "3.1.3" 4301 version "3.1.3"
4324 resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" 4302 resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18"
@@ -5604,11 +5582,6 @@ is-boolean-object@^1.1.0:
5604 call-bind "^1.0.2" 5582 call-bind "^1.0.2"
5605 has-tostringtag "^1.0.0" 5583 has-tostringtag "^1.0.0"
5606 5584
5607is-buffer@~1.1.6:
5608 version "1.1.6"
5609 resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
5610 integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
5611
5612is-callable@^1.1.4, is-callable@^1.2.4: 5585is-callable@^1.1.4, is-callable@^1.2.4:
5613 version "1.2.4" 5586 version "1.2.4"
5614 resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" 5587 resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945"
@@ -6348,15 +6321,6 @@ maxmind@^4.3.6:
6348 mmdb-lib "2.0.2" 6321 mmdb-lib "2.0.2"
6349 tiny-lru "8.0.2" 6322 tiny-lru "8.0.2"
6350 6323
6351md5@^2.2.1:
6352 version "2.3.0"
6353 resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f"
6354 integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==
6355 dependencies:
6356 charenc "0.0.2"
6357 crypt "0.0.2"
6358 is-buffer "~1.1.6"
6359
6360mdurl@^1.0.1: 6324mdurl@^1.0.1:
6361 version "1.0.1" 6325 version "1.0.1"
6362 resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" 6326 resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
@@ -6988,7 +6952,7 @@ optionator@^0.9.1:
6988 type-check "^0.4.0" 6952 type-check "^0.4.0"
6989 word-wrap "^1.2.3" 6953 word-wrap "^1.2.3"
6990 6954
6991os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: 6955os-tmpdir@~1.0.2:
6992 version "1.0.2" 6956 version "1.0.2"
6993 resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" 6957 resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
6994 integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== 6958 integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==
@@ -7236,16 +7200,6 @@ peek-stream@^1.1.1:
7236 duplexify "^3.5.0" 7200 duplexify "^3.5.0"
7237 through2 "^2.0.3" 7201 through2 "^2.0.3"
7238 7202
7239pem@^1.12.3:
7240 version "1.14.6"
7241 resolved "https://registry.yarnpkg.com/pem/-/pem-1.14.6.tgz#89babca3a73466fb844df70666dbf1b25eb0dc56"
7242 integrity sha512-I5GKUer2PPv5qzUfxaZ6IGRkhp+357Kyv2t1JJg9vP8hGGI13qU34N2QupmggbpIZGPuudH0jn8KU5hjFpPk3g==
7243 dependencies:
7244 es6-promisify "^6.0.0"
7245 md5 "^2.2.1"
7246 os-tmpdir "^1.0.1"
7247 which "^2.0.2"
7248
7249pg-connection-string@^2.5.0: 7203pg-connection-string@^2.5.0:
7250 version "2.5.0" 7204 version "2.5.0"
7251 resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.5.0.tgz#538cadd0f7e603fc09a12590f3b8a452c2c0cf34" 7205 resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.5.0.tgz#538cadd0f7e603fc09a12590f3b8a452c2c0cf34"
@@ -9332,7 +9286,7 @@ which@^1.1.1:
9332 dependencies: 9286 dependencies:
9333 isexe "^2.0.0" 9287 isexe "^2.0.0"
9334 9288
9335which@^2.0.1, which@^2.0.2: 9289which@^2.0.1:
9336 version "2.0.2" 9290 version "2.0.2"
9337 resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" 9291 resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
9338 integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== 9292 integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==