From 0b8a0b7fe32c83ae416a3aa043ae73f749a05984 Mon Sep 17 00:00:00 2001 From: Nathan Rajlich Date: Wed, 24 May 2023 01:02:53 -0700 Subject: [PATCH] [https-proxy-agent] Properly reject errors during proxy CONNECT response (#184) Fixes #162. --- .changeset/eleven-seas-rule.md | 5 + packages/https-proxy-agent/package.json | 2 +- .../src/parse-proxy-response.ts | 26 ++-- packages/https-proxy-agent/test/test.ts | 51 ++++++-- pnpm-lock.yaml | 117 ++++++++++-------- 5 files changed, 131 insertions(+), 70 deletions(-) create mode 100644 .changeset/eleven-seas-rule.md diff --git a/.changeset/eleven-seas-rule.md b/.changeset/eleven-seas-rule.md new file mode 100644 index 00000000..8fe15c23 --- /dev/null +++ b/.changeset/eleven-seas-rule.md @@ -0,0 +1,5 @@ +--- +"https-proxy-agent": patch +--- + +Properly reject errors during proxy `CONNECT` response diff --git a/packages/https-proxy-agent/package.json b/packages/https-proxy-agent/package.json index c724d5ef..4c913d1e 100644 --- a/packages/https-proxy-agent/package.json +++ b/packages/https-proxy-agent/package.json @@ -36,7 +36,7 @@ "@types/debug": "4", "@types/jest": "^29.5.1", "@types/node": "^14.18.45", - "async-listen": "^2.1.0", + "async-listen": "^3.0.0", "async-retry": "^1.3.3", "jest": "^29.5.0", "proxy": "workspace:*", diff --git a/packages/https-proxy-agent/src/parse-proxy-response.ts b/packages/https-proxy-agent/src/parse-proxy-response.ts index 5215260c..309e695b 100644 --- a/packages/https-proxy-agent/src/parse-proxy-response.ts +++ b/packages/https-proxy-agent/src/parse-proxy-response.ts @@ -30,16 +30,17 @@ export function parseProxyResponse( function cleanup() { socket.removeListener('end', onend); socket.removeListener('error', onerror); - socket.removeListener('close', onclose); socket.removeListener('readable', read); } - function onclose(err?: Error) { - debug('onclose had error %o', err); - } - function onend() { + cleanup(); debug('onend'); + reject( + new Error( + 'Proxy connection ended before receiving CONNECT response' + ) + ); } function onerror(err: Error) { @@ -65,7 +66,10 @@ export function parseProxyResponse( const headerParts = buffered.toString('ascii').split('\r\n'); const firstLine = headerParts.shift(); if (!firstLine) { - throw new Error('No header received'); + socket.destroy(); + return reject( + new Error('No header received from proxy CONNECT response') + ); } const firstLineParts = firstLine.split(' '); const statusCode = +firstLineParts[1]; @@ -75,7 +79,12 @@ export function parseProxyResponse( if (!header) continue; const firstColon = header.indexOf(':'); if (firstColon === -1) { - throw new Error(`Invalid header: "${header}"`); + socket.destroy(); + return reject( + new Error( + `Invalid header from proxy CONNECT response: "${header}"` + ) + ); } const key = header.slice(0, firstColon).toLowerCase(); const value = header.slice(firstColon + 1).trimStart(); @@ -88,7 +97,7 @@ export function parseProxyResponse( headers[key] = value; } } - debug('got proxy server response: %o', firstLine); + debug('got proxy server response: %o %o', firstLine, headers); cleanup(); resolve({ connect: { @@ -101,7 +110,6 @@ export function parseProxyResponse( } socket.on('error', onerror); - socket.on('close', onclose); socket.on('end', onend); read(); diff --git a/packages/https-proxy-agent/test/test.ts b/packages/https-proxy-agent/test/test.ts index 8152ea4a..1027c876 100644 --- a/packages/https-proxy-agent/test/test.ts +++ b/packages/https-proxy-agent/test/test.ts @@ -1,4 +1,5 @@ import fs from 'fs'; +import net from 'net'; import http from 'http'; import https from 'https'; import assert from 'assert'; @@ -32,25 +33,25 @@ describe('HttpsProxyAgent', () => { beforeAll(async () => { // setup target HTTP server server = http.createServer(); - serverUrl = (await listen(server)) as URL; + serverUrl = await listen(server); }); beforeAll(async () => { // setup HTTP proxy server proxy = createProxy(); - proxyUrl = (await listen(proxy)) as URL; + proxyUrl = await listen(proxy); }); beforeAll(async () => { // setup target HTTPS server sslServer = https.createServer(sslOptions); - sslServerUrl = (await listen(sslServer)) as URL; + sslServerUrl = await listen(sslServer); }); beforeAll(async () => { // setup SSL HTTP proxy server sslProxy = createProxy(https.createServer(sslOptions)); - sslProxyUrl = (await listen(sslProxy)) as URL; + sslProxyUrl = await listen(sslProxy); }); beforeEach(() => { @@ -197,7 +198,11 @@ describe('HttpsProxyAgent', () => { const connectPromise = once(server, 'connect'); - http.get({ agent }); + http.get({ agent }).on('error', () => { + // "error" happens because agent didn't receive proper + // CONNECT response before the socket was closed. + // We can safely ignore that. + }); const [req, socket] = await connectPromise; assert.equal('CONNECT', req.method); @@ -212,7 +217,11 @@ describe('HttpsProxyAgent', () => { }); const connectPromise = once(server, 'connect'); - http.get({ agent }); + http.get({ agent }).on('error', () => { + // "error" happens because agent didn't receive proper + // CONNECT response before the socket was closed. + // We can safely ignore that. + }); const [req, socket] = await connectPromise; assert.equal('CONNECT', req.method); @@ -220,7 +229,11 @@ describe('HttpsProxyAgent', () => { socket.destroy(); const connectPromise2 = once(server, 'connect'); - http.get({ agent }); + http.get({ agent }).on('error', () => { + // "error" happens because agent didn't receive proper + // CONNECT response before the socket was closed. + // We can safely ignore that. + }); const [req2, socket2] = await connectPromise2; assert.equal('CONNECT', req2.method); @@ -252,6 +265,30 @@ describe('HttpsProxyAgent', () => { agent.destroy(); } }); + + it('should emit "error" on request if proxy has invalid header', async () => { + const badProxy = net.createServer((socket) => { + socket.write( + 'HTTP/1.1 200 Connection established\r\nbadheader\r\n\r\n' + ); + }); + const addr = await listen(badProxy); + let err: Error | undefined; + try { + const agent = new HttpsProxyAgent( + addr.href.replace('tcp', 'http') + ); + await req('http://example.com', { agent }); + } catch (_err) { + err = _err as Error; + } finally { + badProxy.close(); + } + assert(err); + expect(err.message).toEqual( + 'Invalid header from proxy CONNECT response: "badheader"' + ); + }); }); describe('"https" module', () => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bbea783a..07677fa7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,10 +9,10 @@ importers: version: 2.26.1 '@typescript-eslint/eslint-plugin': specifier: ^5.59.1 - version: 5.59.1(@typescript-eslint/parser@5.59.1)(eslint@7.32.0) + version: 5.59.1(@typescript-eslint/parser@5.59.1)(eslint@7.32.0)(typescript@5.0.4) '@typescript-eslint/parser': specifier: ^5.59.1 - version: 5.59.1(eslint@7.32.0) + version: 5.59.1(eslint@7.32.0)(typescript@5.0.4) eslint: specifier: ^7.32.0 version: 7.32.0 @@ -27,7 +27,7 @@ importers: version: 2.8.8 turbo: specifier: latest - version: 1.9.8 + version: 1.9.3 packages/agent-base: dependencies: @@ -58,7 +58,7 @@ importers: version: 29.5.0(@types/node@14.18.45) ts-jest: specifier: ^29.1.0 - version: 29.1.0(jest@29.5.0)(typescript@5.0.4) + version: 29.1.0(@babel/core@7.21.4)(jest@29.5.0)(typescript@5.0.4) tsconfig: specifier: workspace:* version: link:../tsconfig @@ -82,7 +82,7 @@ importers: version: 29.5.0(@types/node@14.18.45) ts-jest: specifier: ^29.1.0 - version: 29.1.0(jest@29.5.0)(typescript@5.0.4) + version: 29.1.0(@babel/core@7.21.4)(jest@29.5.0)(typescript@5.0.4) tsconfig: specifier: workspace:* version: link:../tsconfig @@ -122,7 +122,7 @@ importers: version: 29.5.0(@types/node@14.18.45) ts-jest: specifier: ^29.1.0 - version: 29.1.0(jest@29.5.0)(typescript@5.0.4) + version: 29.1.0(@babel/core@7.21.4)(jest@29.5.0)(typescript@5.0.4) tsconfig: specifier: workspace:* version: link:../tsconfig @@ -174,7 +174,7 @@ importers: version: 1.2.2 ts-jest: specifier: ^29.1.0 - version: 29.1.0(jest@29.5.0)(typescript@5.0.4) + version: 29.1.0(@babel/core@7.21.4)(jest@29.5.0)(typescript@5.0.4) tsconfig: specifier: workspace:* version: link:../tsconfig @@ -211,7 +211,7 @@ importers: version: link:../proxy ts-jest: specifier: ^29.1.0 - version: 29.1.0(jest@29.5.0)(typescript@5.0.4) + version: 29.1.0(@babel/core@7.21.4)(jest@29.5.0)(typescript@5.0.4) tsconfig: specifier: workspace:* version: link:../tsconfig @@ -241,8 +241,8 @@ importers: specifier: ^14.18.45 version: 14.18.45 async-listen: - specifier: ^2.1.0 - version: 2.1.0 + specifier: ^3.0.0 + version: 3.0.0 async-retry: specifier: ^1.3.3 version: 1.3.3 @@ -254,7 +254,7 @@ importers: version: link:../proxy ts-jest: specifier: ^29.1.0 - version: 29.1.0(jest@29.5.0)(typescript@5.0.4) + version: 29.1.0(@babel/core@7.21.4)(jest@29.5.0)(typescript@5.0.4) tsconfig: specifier: workspace:* version: link:../tsconfig @@ -309,7 +309,7 @@ importers: version: 0.0.6 ts-jest: specifier: ^29.1.0 - version: 29.1.0(jest@29.5.0)(typescript@5.0.4) + version: 29.1.0(@babel/core@7.21.4)(jest@29.5.0)(typescript@5.0.4) tsconfig: specifier: workspace:* version: link:../tsconfig @@ -346,7 +346,7 @@ importers: version: 29.5.0(@types/node@14.18.45) ts-jest: specifier: ^29.1.0 - version: 29.1.0(jest@29.5.0)(typescript@5.0.4) + version: 29.1.0(@babel/core@7.21.4)(jest@29.5.0)(typescript@5.0.4) tsconfig: specifier: workspace:* version: link:../tsconfig @@ -386,7 +386,7 @@ importers: version: 29.5.0(@types/node@14.18.45) ts-jest: specifier: ^29.1.0 - version: 29.1.0(jest@29.5.0)(typescript@5.0.4) + version: 29.1.0(@babel/core@7.21.4)(jest@29.5.0)(typescript@5.0.4) tsconfig: specifier: workspace:* version: link:../tsconfig @@ -450,7 +450,7 @@ importers: version: github.com/TooTallNate/socksv5/d937368b28e929396166d77a06d387a4a902bd51 ts-jest: specifier: ^29.1.0 - version: 29.1.0(jest@29.5.0)(typescript@5.0.4) + version: 29.1.0(@babel/core@7.21.4)(jest@29.5.0)(typescript@5.0.4) tsconfig: specifier: workspace:* version: link:../tsconfig @@ -508,7 +508,7 @@ importers: version: github.com/TooTallNate/socksv5/d937368b28e929396166d77a06d387a4a902bd51 ts-jest: specifier: ^29.1.0 - version: 29.1.0(jest@29.5.0)(typescript@5.0.4) + version: 29.1.0(@babel/core@7.21.4)(jest@29.5.0)(typescript@5.0.4) tsconfig: specifier: workspace:* version: link:../tsconfig @@ -1642,7 +1642,7 @@ packages: '@types/yargs-parser': 21.0.0 dev: true - /@typescript-eslint/eslint-plugin@5.59.1(@typescript-eslint/parser@5.59.1)(eslint@7.32.0): + /@typescript-eslint/eslint-plugin@5.59.1(@typescript-eslint/parser@5.59.1)(eslint@7.32.0)(typescript@5.0.4): resolution: {integrity: sha512-AVi0uazY5quFB9hlp2Xv+ogpfpk77xzsgsIEWyVS7uK/c7MZ5tw7ZPbapa0SbfkqE0fsAMkz5UwtgMLVk2BQAg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -1654,22 +1654,23 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.5.0 - '@typescript-eslint/parser': 5.59.1(eslint@7.32.0) + '@typescript-eslint/parser': 5.59.1(eslint@7.32.0)(typescript@5.0.4) '@typescript-eslint/scope-manager': 5.59.1 - '@typescript-eslint/type-utils': 5.59.1(eslint@7.32.0) - '@typescript-eslint/utils': 5.59.1(eslint@7.32.0) + '@typescript-eslint/type-utils': 5.59.1(eslint@7.32.0)(typescript@5.0.4) + '@typescript-eslint/utils': 5.59.1(eslint@7.32.0)(typescript@5.0.4) debug: 4.3.4 eslint: 7.32.0 grapheme-splitter: 1.0.4 ignore: 5.2.0 natural-compare-lite: 1.4.0 semver: 7.3.8 - tsutils: 3.21.0 + tsutils: 3.21.0(typescript@5.0.4) + typescript: 5.0.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@5.59.1(eslint@7.32.0): + /@typescript-eslint/parser@5.59.1(eslint@7.32.0)(typescript@5.0.4): resolution: {integrity: sha512-nzjFAN8WEu6yPRDizIFyzAfgK7nybPodMNFGNH0M9tei2gYnYszRDqVA0xlnRjkl7Hkx2vYrEdb6fP2a21cG1g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -1681,9 +1682,10 @@ packages: dependencies: '@typescript-eslint/scope-manager': 5.59.1 '@typescript-eslint/types': 5.59.1 - '@typescript-eslint/typescript-estree': 5.59.1 + '@typescript-eslint/typescript-estree': 5.59.1(typescript@5.0.4) debug: 4.3.4 eslint: 7.32.0 + typescript: 5.0.4 transitivePeerDependencies: - supports-color dev: true @@ -1696,7 +1698,7 @@ packages: '@typescript-eslint/visitor-keys': 5.59.1 dev: true - /@typescript-eslint/type-utils@5.59.1(eslint@7.32.0): + /@typescript-eslint/type-utils@5.59.1(eslint@7.32.0)(typescript@5.0.4): resolution: {integrity: sha512-ZMWQ+Oh82jWqWzvM3xU+9y5U7MEMVv6GLioM3R5NJk6uvP47kZ7YvlgSHJ7ERD6bOY7Q4uxWm25c76HKEwIjZw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -1706,11 +1708,12 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 5.59.1 - '@typescript-eslint/utils': 5.59.1(eslint@7.32.0) + '@typescript-eslint/typescript-estree': 5.59.1(typescript@5.0.4) + '@typescript-eslint/utils': 5.59.1(eslint@7.32.0)(typescript@5.0.4) debug: 4.3.4 eslint: 7.32.0 - tsutils: 3.21.0 + tsutils: 3.21.0(typescript@5.0.4) + typescript: 5.0.4 transitivePeerDependencies: - supports-color dev: true @@ -1720,7 +1723,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/typescript-estree@5.59.1: + /@typescript-eslint/typescript-estree@5.59.1(typescript@5.0.4): resolution: {integrity: sha512-lYLBBOCsFltFy7XVqzX0Ju+Lh3WPIAWxYpmH/Q7ZoqzbscLiCW00LeYCdsUnnfnj29/s1WovXKh2gwCoinHNGA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -1735,12 +1738,13 @@ packages: globby: 11.1.0 is-glob: 4.0.3 semver: 7.3.8 - tsutils: 3.21.0 + tsutils: 3.21.0(typescript@5.0.4) + typescript: 5.0.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@5.59.1(eslint@7.32.0): + /@typescript-eslint/utils@5.59.1(eslint@7.32.0)(typescript@5.0.4): resolution: {integrity: sha512-MkTe7FE+K1/GxZkP5gRj3rCztg45bEhsd8HYjczBuYm+qFHP5vtZmjx3B0yUCDotceQ4sHgTyz60Ycl225njmA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -1751,7 +1755,7 @@ packages: '@types/semver': 7.3.13 '@typescript-eslint/scope-manager': 5.59.1 '@typescript-eslint/types': 5.59.1 - '@typescript-eslint/typescript-estree': 5.59.1 + '@typescript-eslint/typescript-estree': 5.59.1(typescript@5.0.4) eslint: 7.32.0 eslint-scope: 5.1.1 semver: 7.3.8 @@ -1925,6 +1929,11 @@ packages: engines: {node: '>= 14'} dev: true + /async-listen@3.0.0: + resolution: {integrity: sha512-V+SsTpDqkrWTimiotsyl33ePSjA5/KrithwupuvJ6ztsqPvGv6ge4OredFhPffVXiLN/QUWvE0XcqJaYgt6fOg==} + engines: {node: '>= 14'} + dev: true + /async-retry@1.3.3: resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} dependencies: @@ -4928,7 +4937,7 @@ packages: engines: {node: '>=8'} dev: true - /ts-jest@29.1.0(jest@29.5.0)(typescript@5.0.4): + /ts-jest@29.1.0(@babel/core@7.21.4)(jest@29.5.0)(typescript@5.0.4): resolution: {integrity: sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -4949,6 +4958,7 @@ packages: esbuild: optional: true dependencies: + '@babel/core': 7.21.4 bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 jest: 29.5.0(@types/node@14.18.45) @@ -4969,13 +4979,14 @@ packages: resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} dev: false - /tsutils@3.21.0: + /tsutils@3.21.0(typescript@5.0.4): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' dependencies: tslib: 1.14.1 + typescript: 5.0.4 dev: true /tty-table@4.2.1: @@ -4992,65 +5003,65 @@ packages: yargs: 17.7.1 dev: true - /turbo-darwin-64@1.9.8: - resolution: {integrity: sha512-PkTdBjPfgpj/Dob/6SjkzP0BBP80/KmFjLEocXVEECCLJE6tHKbWLRdvc79B0N6SufdYdZ1uvvoU3KPtBokSPw==} + /turbo-darwin-64@1.9.3: + resolution: {integrity: sha512-0dFc2cWXl82kRE4Z+QqPHhbEFEpUZho1msHXHWbz5+PqLxn8FY0lEVOHkq5tgKNNEd5KnGyj33gC/bHhpZOk5g==} cpu: [x64] os: [darwin] requiresBuild: true dev: true optional: true - /turbo-darwin-arm64@1.9.8: - resolution: {integrity: sha512-sLwqOx3XV57QCEoJM9GnDDnnqidG8wf29ytxssBaWHBdeJTjupyrmzTUrX+tyKo3Q+CjWvbPLyqVqxT4g5NuXQ==} + /turbo-darwin-arm64@1.9.3: + resolution: {integrity: sha512-1cYbjqLBA2zYE1nbf/qVnEkrHa4PkJJbLo7hnuMuGM0bPzh4+AnTNe98gELhqI1mkTWBu/XAEeF5u6dgz0jLNA==} cpu: [arm64] os: [darwin] requiresBuild: true dev: true optional: true - /turbo-linux-64@1.9.8: - resolution: {integrity: sha512-AMg6VT6sW7aOD1uOs5suxglXfTYz9T0uVyKGKokDweGOYTWmuTMGU5afUT1tYRUwQ+kVPJI+83Atl5Ob0oBsgw==} + /turbo-linux-64@1.9.3: + resolution: {integrity: sha512-UuBPFefawEwpuxh5pM9Jqq3q4C8M0vYxVYlB3qea/nHQ80pxYq7ZcaLGEpb10SGnr3oMUUs1zZvkXWDNKCJb8Q==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /turbo-linux-arm64@1.9.8: - resolution: {integrity: sha512-tLnxFv+OIklwTjiOZ8XMeEeRDAf150Ry4BCivNwgTVFAqQGEqkFP6KGBy56hb5RRF1frPQpoPGipJNVm7c8m1w==} + /turbo-linux-arm64@1.9.3: + resolution: {integrity: sha512-vUrNGa3hyDtRh9W0MkO+l1dzP8Co2gKnOVmlJQW0hdpOlWlIh22nHNGGlICg+xFa2f9j4PbQlWTsc22c019s8Q==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /turbo-windows-64@1.9.8: - resolution: {integrity: sha512-r3pCjvXTMR7kq2E3iqwFlN1R7pFO/TOsuUjMhOSPP7HwuuUIinAckU4I9foM3q7ZCQd1XXScBUt3niDyHijAqQ==} + /turbo-windows-64@1.9.3: + resolution: {integrity: sha512-0BZ7YaHs6r+K4ksqWus1GKK3W45DuDqlmfjm/yuUbTEVc8szmMCs12vugU2Zi5GdrdJSYfoKfEJ/PeegSLIQGQ==} cpu: [x64] os: [win32] requiresBuild: true dev: true optional: true - /turbo-windows-arm64@1.9.8: - resolution: {integrity: sha512-CWzRbX2TM5IfHBC6uWM659qUOEDC4h0nn16ocG8yIq1IF3uZMzKRBHgGOT5m1BHom+R08V0NcjTmPRoqpiI0dg==} + /turbo-windows-arm64@1.9.3: + resolution: {integrity: sha512-QJUYLSsxdXOsR1TquiOmLdAgtYcQ/RuSRpScGvnZb1hY0oLc7JWU0llkYB81wVtWs469y8H9O0cxbKwCZGR4RQ==} cpu: [arm64] os: [win32] requiresBuild: true dev: true optional: true - /turbo@1.9.8: - resolution: {integrity: sha512-dTouGZBm4a2fE0OPafcTQERCp4i3ZOow0Pr0JlOyxKmzJy0JRwXypH013kbZoK6k1ET5tS/g9rwUXIM/AmWXXQ==} + /turbo@1.9.3: + resolution: {integrity: sha512-ID7mxmaLUPKG/hVkp+h0VuucB1U99RPCJD9cEuSEOdIPoSIuomcIClEJtKamUsdPLhLCud+BvapBNnhgh58Nzw==} hasBin: true requiresBuild: true optionalDependencies: - turbo-darwin-64: 1.9.8 - turbo-darwin-arm64: 1.9.8 - turbo-linux-64: 1.9.8 - turbo-linux-arm64: 1.9.8 - turbo-windows-64: 1.9.8 - turbo-windows-arm64: 1.9.8 + turbo-darwin-64: 1.9.3 + turbo-darwin-arm64: 1.9.3 + turbo-linux-64: 1.9.3 + turbo-linux-arm64: 1.9.3 + turbo-windows-64: 1.9.3 + turbo-windows-arm64: 1.9.3 dev: true /type-check@0.3.2: