From 2955d935e8d1d9888bb6a1606511e0bdcc6a8c61 Mon Sep 17 00:00:00 2001 From: Saleel Date: Wed, 17 Jan 2024 00:15:51 +0530 Subject: [PATCH] chore: add dkim verification with arc handling --- packages/helpers/package.json | 2 + packages/helpers/src/dkim.ts | 103 +++++++++++ packages/helpers/src/index.ts | 1 + packages/helpers/tsconfig.json | 8 +- packages/helpers/types/mailauth.d.ts | 257 +++++++++++++++++++++++++++ yarn.lock | 234 ++++++++++++++++++++++-- 6 files changed, 585 insertions(+), 20 deletions(-) create mode 100644 packages/helpers/src/dkim.ts create mode 100644 packages/helpers/types/mailauth.d.ts diff --git a/packages/helpers/package.json b/packages/helpers/package.json index f512adb..ada3e87 100644 --- a/packages/helpers/package.json +++ b/packages/helpers/package.json @@ -16,8 +16,10 @@ "libmime": "^5.2.1", "localforage": "^1.10.0", "lodash": "^4.17.21", + "mailauth": "^4.6.0", "node-forge": "^1.3.1", "pako": "^2.1.0", + "pki": "^1.1.0", "psl": "^1.9.0", "snarkjs": "https://github.com/sampritipanda/snarkjs.git#fef81fc51d17a734637555c6edbd585ecda02d9e" }, diff --git a/packages/helpers/src/dkim.ts b/packages/helpers/src/dkim.ts new file mode 100644 index 0000000..c6dd648 --- /dev/null +++ b/packages/helpers/src/dkim.ts @@ -0,0 +1,103 @@ +import { authenticate } from "mailauth"; +import { pki } from "node-forge"; + +interface DKIMVerificationResult { + publicKey: bigint; + signature: bigint; + message: Buffer; + bodyHash: string; + signingDomain: string; + selector: string; + algo: string; + format: string; + modulusLength: number; +} + +export async function verifyDKIMSignature( + email: string, + domain: string = "", + tryRevertForwarderChanges: boolean = true +): Promise { + let dkimResult = await tryVerifyDKIM(email, domain); + + if (dkimResult.status.result !== "pass" && tryRevertForwarderChanges) { + const modified = await revertForwarderChanges(email); + dkimResult = await tryVerifyDKIM(modified, domain); + } + + if (dkimResult.status.result !== "pass") { + throw new Error( + `DKIM signature verification failed for domain ${dkimResult.signingDomain}` + ); + } + + return { + signature: BigInt("0x" + Buffer.from(dkimResult.signature, "base64").toString("hex")), + message: Buffer.from(dkimResult.signingHeaders.canonicalizedHeader, "base64"), + bodyHash: dkimResult.bodyHash, + signingDomain: dkimResult.signingDomain, + publicKey: BigInt(pki.publicKeyFromPem(dkimResult.publicKey).n.toString()), + selector: dkimResult.selector, + algo: dkimResult.algo, + format: dkimResult.format, + modulusLength: dkimResult.modulusLength, + }; +} + +async function tryVerifyDKIM(email: string, domain: string = "") { + const authResult = await authenticate(email, { + disableArc: true, + disableDmarc: true, + disableBimi: true, + }); + + const { dkim } = authResult; + + let domainToVerifyDKIM = domain; + if (!domainToVerifyDKIM) { + if (dkim.headerFrom.length > 1) { + throw new Error( + "Multiple From header in email and domain for verification not specified" + ); + } + + domainToVerifyDKIM = dkim.headerFrom[0].split("@")[1]; + } + + const dkimResult = dkim.results.find( + (d: any) => d.signingDomain === domainToVerifyDKIM + ); + + if (!dkimResult) { + throw new Error( + `DKIM signature not found for domain ${domainToVerifyDKIM}` + ); + } + + return dkimResult; +} + +function getHeaderValue(email: string, header: string) { + const headerStartIndex = email.indexOf(`${header}: `) + header.length + 2; + const headerEndIndex = email.indexOf("\n", headerStartIndex); + const headerValue = email.substring(headerStartIndex, headerEndIndex); + + return headerValue; +} + +function setHeaderValue(email: string, header: string, value: string) { + return email.replace(getHeaderValue(email, header), value); +} + +async function revertForwarderChanges(email: string) { + // Google sets their own Message-ID and put the original one in X-Google-Original-Message-ID when forwarding + const googleReplacedMessageId = getHeaderValue( + email, + "X-Google-Original-Message-ID" + ); + if (googleReplacedMessageId) { + email = setHeaderValue(email, "Message-ID", googleReplacedMessageId); + } + + return email; +} diff --git a/packages/helpers/src/index.ts b/packages/helpers/src/index.ts index 8025ad1..d3b04df 100644 --- a/packages/helpers/src/index.ts +++ b/packages/helpers/src/index.ts @@ -1,3 +1,4 @@ +export * from "./dkim"; export * from "./binaryFormat"; export * from "./constants"; export * from "./fast-sha256"; diff --git a/packages/helpers/tsconfig.json b/packages/helpers/tsconfig.json index 982bec2..c60e08e 100644 --- a/packages/helpers/tsconfig.json +++ b/packages/helpers/tsconfig.json @@ -13,6 +13,10 @@ "module": "commonjs", "declaration": true, "baseUrl": "./src", - "outDir": "dist" - } + "outDir": "dist", + "typeRoots": [ + "./types", + "../../node_modules/@types" + ], + }, } diff --git a/packages/helpers/types/mailauth.d.ts b/packages/helpers/types/mailauth.d.ts new file mode 100644 index 0000000..eea2a00 --- /dev/null +++ b/packages/helpers/types/mailauth.d.ts @@ -0,0 +1,257 @@ +declare module "mailauth" { + export interface MailAuthResult { + dkim: Dkim; + spf: Spf; + dmarc: Dmarc; + arc: Arc; + bimi: Bimi; + receivedChain: ReceivedChain[]; + headers: string; + } + + export interface Dkim { + headerFrom: string[]; + envelopeFrom: string; + results: Result[]; + } + + export interface Result { + signingDomain: string; + selector: string; + signature: string; + algo: string; + format: string; + bodyHash: string; + bodyHashExpecting: string; + signingHeaders: SigningHeaders; + status: Status; + canonBodyLength: number; + publicKey: string; + modulusLength: number; + rr: string; + info: string; + } + + export interface SigningHeaders { + keys: string; + headers: string[]; + canonicalizedHeader: string; + } + + export interface Status { + result: string; + comment: string; + header: Header; + aligned: any; + } + + export interface Header { + i: string; + s: string; + a: string; + b: string; + } + + export interface Spf { + domain: string; + "envelope-from": string; + status: Status2; + header: string; + info: string; + lookups: Lookups; + } + + export interface Status2 { + result: string; + comment: string; + smtp: Smtp; + } + + export interface Smtp { + mailfrom: string; + } + + export interface Lookups { + limit: number; + count: number; + void: number; + subqueries: Subqueries; + } + + export interface Subqueries {} + + export interface Dmarc { + status: Status3; + domain: string; + policy: string; + p: string; + sp: string; + rr: string; + alignment: Alignment; + info: string; + } + + export interface Status3 { + result: string; + comment: string; + header: Header2; + } + + export interface Header2 { + from: string; + d: string; + } + + export interface Alignment { + spf: Spf2; + dkim: Dkim2; + } + + export interface Spf2 { + result: boolean; + strict: boolean; + } + + export interface Dkim2 { + result: boolean; + strict: boolean; + } + + export interface Arc { + status: Status4; + i: number; + signature: Signature; + authenticationResults: AuthenticationResults; + info: string; + authResults: string; + } + + export interface Status4 { + result: string; + comment: string; + } + + export interface Signature { + signingDomain: string; + selector: string; + signature: string; + algo: string; + format: string; + bodyHash: string; + bodyHashExpecting: string; + signingHeaders: SigningHeaders2; + status: Status5; + canonBodyLength: number; + publicKey: string; + modulusLength: number; + rr: string; + } + + export interface SigningHeaders2 { + keys: string; + headers: string[]; + canonicalizedHeader: string; + } + + export interface Status5 { + result: string; + header: Header3; + } + + export interface Header3 { + i: string; + s: string; + a: string; + b: string; + } + + export interface AuthenticationResults { + mta: string; + dkim: Dkim3[]; + spf: Spf3; + dmarc: Dmarc2; + } + + export interface Dkim3 { + result: string; + header: Header4; + } + + export interface Header4 { + i: string; + s: string; + b: string; + } + + export interface Spf3 { + result: string; + smtp: Smtp2; + comment: string; + } + + export interface Smtp2 { + mailfrom: string; + } + + export interface Dmarc2 { + result: string; + header: Header5; + comment: string; + } + + export interface Header5 { + from: string; + } + + export interface Bimi { + status: Status6; + info: string; + } + + export interface Status6 { + header: Header6; + result: string; + comment: string; + } + + export interface Header6 {} + + export interface ReceivedChain { + by: By; + with: With; + id: Id; + timestamp: string; + full: string; + from?: From; + tls?: Tls; + for?: For; + } + + export interface By { + value: string; + } + + export interface With { + value: string; + } + + export interface Id { + value: string; + } + + export interface From { + value: string; + comment: string; + } + + export interface Tls { + value: string; + comment: string; + } + + export interface For { + value: string; + } + + export function authenticate(email: string, options: any) : Promise; +} diff --git a/yarn.lock b/yarn.lock index 7f7e1d0..0d96f9c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2403,6 +2403,13 @@ __metadata: languageName: node linkType: hard +"@fastify/busboy@npm:^2.0.0": + version: 2.1.0 + resolution: "@fastify/busboy@npm:2.1.0" + checksum: 3233abd10f73e50668cb4bb278a79b7b3fadd30215ac6458299b0e5a09a29c3586ec07597aae6bd93f5cbedfcef43a8aeea51829cd28fc13850cdbcd324c28d5 + languageName: node + linkType: hard + "@gar/promisify@npm:^1.1.3": version: 1.1.3 resolution: "@gar/promisify@npm:1.1.3" @@ -3052,6 +3059,77 @@ __metadata: languageName: node linkType: hard +"@peculiar/asn1-schema@npm:2.3.0": + version: 2.3.0 + resolution: "@peculiar/asn1-schema@npm:2.3.0" + dependencies: + asn1js: ^3.0.5 + pvtsutils: ^1.3.2 + tslib: ^2.4.0 + checksum: aa510c68de83be94a8d0e96ca1f7c92d2bd790867eed887c0db32d6b2fc49c5dd9d8269648e9665686ba9b2fd8469324c61e04fed50f7aad3f68a0ca0e1cde4b + languageName: node + linkType: hard + +"@peculiar/asn1-schema@npm:^2.3.0, @peculiar/asn1-schema@npm:^2.3.8": + version: 2.3.8 + resolution: "@peculiar/asn1-schema@npm:2.3.8" + dependencies: + asn1js: ^3.0.5 + pvtsutils: ^1.3.5 + tslib: ^2.6.2 + checksum: 1f4dd421f1411df8bc52bca12b1cef710434c13ff0a8b5746ede42b10d62b5ad06a3925c4a6db53102aaf1e589947539a6955fa8554a9b8ebb1ffa38b0155a24 + languageName: node + linkType: hard + +"@peculiar/asn1-x509-logotype@npm:2.3.0": + version: 2.3.0 + resolution: "@peculiar/asn1-x509-logotype@npm:2.3.0" + dependencies: + "@peculiar/asn1-schema": ^2.3.0 + "@peculiar/asn1-x509": ^2.3.0 + asn1js: ^3.0.5 + tslib: ^2.4.0 + checksum: 657eda43eb264b4593e346d2ae6a0d65969dd44977ef2a92bb4ccbb4c45a0a2c5808e060cc25d5964ab931934cbbeb9303e0d440f5b189c05d6f61be010885e8 + languageName: node + linkType: hard + +"@peculiar/asn1-x509@npm:2.3.0": + version: 2.3.0 + resolution: "@peculiar/asn1-x509@npm:2.3.0" + dependencies: + "@peculiar/asn1-schema": ^2.3.0 + asn1js: ^3.0.5 + ipaddr.js: ^2.0.1 + pvtsutils: ^1.3.2 + tslib: ^2.4.0 + checksum: 9feb2ed049a8fdf836f14298f0922eb28db8bff99cd3ac08f973883d017f17303c3ae243457c8eaa73795bf05c46b7d3555dcf6729cff2a51b134f3f6e9784ec + languageName: node + linkType: hard + +"@peculiar/asn1-x509@npm:^2.3.0": + version: 2.3.8 + resolution: "@peculiar/asn1-x509@npm:2.3.8" + dependencies: + "@peculiar/asn1-schema": ^2.3.8 + asn1js: ^3.0.5 + ipaddr.js: ^2.1.0 + pvtsutils: ^1.3.5 + tslib: ^2.6.2 + checksum: 23856e5d024298447afca55bd68d19a7440c0ae076437aee5ced26a0fa2e4efa3e0e4a354fa6ee9968d62ac21ee1c2186fc427942bacfc824d3a3a4d2e80d14b + languageName: node + linkType: hard + +"@postalsys/vmc@npm:1.0.6": + version: 1.0.6 + resolution: "@postalsys/vmc@npm:1.0.6" + dependencies: + "@peculiar/asn1-schema": 2.3.0 + "@peculiar/asn1-x509": 2.3.0 + "@peculiar/asn1-x509-logotype": 2.3.0 + checksum: 2ffa498326cdcaf980721daf28246741c049f5855ce20c6150346f593827d5529f611c777818ba9ded326dbe45d7fd74ba9ba84b66f8f0311e7c2c59f341746b + languageName: node + linkType: hard + "@puppeteer/browsers@npm:1.7.1": version: 1.7.1 resolution: "@puppeteer/browsers@npm:1.7.1" @@ -4784,9 +4862,11 @@ __metadata: libmime: ^5.2.1 localforage: ^1.10.0 lodash: ^4.17.21 + mailauth: ^4.6.0 msw: ^1.2.2 node-forge: ^1.3.1 pako: ^2.1.0 + pki: ^1.1.0 psl: ^1.9.0 snarkjs: "https://github.com/sampritipanda/snarkjs.git#fef81fc51d17a734637555c6edbd585ecda02d9e" languageName: unknown @@ -5311,6 +5391,17 @@ __metadata: languageName: node linkType: hard +"asn1js@npm:^3.0.5": + version: 3.0.5 + resolution: "asn1js@npm:3.0.5" + dependencies: + pvtsutils: ^1.3.2 + pvutils: ^1.1.3 + tslib: ^2.4.0 + checksum: 3b6af1bbadd5762ef8ead5daf2f6bda1bc9e23bc825c4dcc996aa1f9521ad7390a64028565d95d98090d69c8431f004c71cccb866004759169d7c203cf9075eb + languageName: node + linkType: hard + "assert-plus@npm:1.0.0, assert-plus@npm:^1.0.0": version: 1.0.0 resolution: "assert-plus@npm:1.0.0" @@ -8597,6 +8688,17 @@ __metadata: languageName: node linkType: hard +"fast-xml-parser@npm:4.3.2": + version: 4.3.2 + resolution: "fast-xml-parser@npm:4.3.2" + dependencies: + strnum: ^1.0.5 + bin: + fxparser: src/cli/cli.js + checksum: d507ce2efa5fd13d0a5ba28bd76dd68f2fc30ad8748357c37b70f360d19417866d79e35a688af067d5bceaaa796033fa985206aef9692f7a421e1326b6e73309 + languageName: node + linkType: hard + "fastest-stable-stringify@npm:^2.0.2": version: 2.0.2 resolution: "fastest-stable-stringify@npm:2.0.2" @@ -9846,6 +9948,13 @@ __metadata: languageName: node linkType: hard +"ipaddr.js@npm:2.1.0, ipaddr.js@npm:^2.0.1, ipaddr.js@npm:^2.1.0": + version: 2.1.0 + resolution: "ipaddr.js@npm:2.1.0" + checksum: 807a054f2bd720c4d97ee479d6c9e865c233bea21f139fb8dabd5a35c4226d2621c42e07b4ad94ff3f82add926a607d8d9d37c625ad0319f0e08f9f2bd1968e2 + languageName: node + linkType: hard + "is-arguments@npm:^1.0.4, is-arguments@npm:^1.1.1": version: 1.1.1 resolution: "is-arguments@npm:1.1.1" @@ -10994,6 +11103,19 @@ __metadata: languageName: node linkType: hard +"joi@npm:17.11.0": + version: 17.11.0 + resolution: "joi@npm:17.11.0" + dependencies: + "@hapi/hoek": ^9.0.0 + "@hapi/topo": ^5.0.0 + "@sideway/address": ^4.1.3 + "@sideway/formula": ^3.0.1 + "@sideway/pinpoint": ^2.0.0 + checksum: 3a4e9ecba345cdafe585e7ed8270a44b39718e11dff3749aa27e0001a63d578b75100c062be28e6f48f960b594864034e7a13833f33fbd7ad56d5ce6b617f9bf + languageName: node + linkType: hard + "joi@npm:^17.7.0": version: 17.9.2 resolution: "joi@npm:17.9.2" @@ -11466,7 +11588,7 @@ __metadata: languageName: node linkType: hard -"libmime@npm:^5.2.1": +"libmime@npm:5.2.1, libmime@npm:^5.2.1": version: 5.2.1 resolution: "libmime@npm:5.2.1" dependencies: @@ -11757,6 +11879,26 @@ __metadata: languageName: node linkType: hard +"mailauth@npm:^4.6.0": + version: 4.6.0 + resolution: "mailauth@npm:4.6.0" + dependencies: + "@postalsys/vmc": 1.0.6 + fast-xml-parser: 4.3.2 + ipaddr.js: 2.1.0 + joi: 17.11.0 + libmime: 5.2.1 + nodemailer: 6.9.7 + psl: 1.9.0 + punycode: 2.3.1 + undici: 5.27.0 + yargs: 17.7.2 + bin: + mailauth: bin/mailauth.js + checksum: 1c656d4d5237cb6d39a3e8fcd4cd83565da5348fe4dd9709576f3cbc0bce3f91644258b67d55d887563cbc81ff377a3909c9345227979a5e8da71f60e6627449 + languageName: node + linkType: hard + "make-dir@npm:^3.0.0": version: 3.1.0 resolution: "make-dir@npm:3.1.0" @@ -12519,6 +12661,13 @@ __metadata: languageName: node linkType: hard +"nodemailer@npm:6.9.7": + version: 6.9.7 + resolution: "nodemailer@npm:6.9.7" + checksum: 0cf66d27aed3bd2cbdff9939402cec3d2119c31b2b9ff4af3bcd59f48287ea75b90c0ce2cd9eb0df838164972cd25581b4b723c91fd673e2608bcb28445ccb1b + languageName: node + linkType: hard + "nopt@npm:^6.0.0": version: 6.0.0 resolution: "nopt@npm:6.0.0" @@ -13139,6 +13288,16 @@ __metadata: languageName: node linkType: hard +"pki@npm:^1.1.0": + version: 1.1.0 + resolution: "pki@npm:1.1.0" + peerDependencies: + "@pulumi/kubernetes": ^0.25.6 + "@pulumi/pulumi": ^0.17.28 + checksum: 31b4ddc413be8508e816204d0eb52597ad23b02c6c0713b486967de6e7e4c42b560d1a8422702d32374c613c9302e6061a35ffbd1c1c5986d546798d775927a9 + languageName: node + linkType: hard + "pluralize@npm:^8.0.0": version: 8.0.0 resolution: "pluralize@npm:8.0.0" @@ -13420,7 +13579,7 @@ __metadata: languageName: node linkType: hard -"psl@npm:^1.1.28, psl@npm:^1.1.33, psl@npm:^1.9.0": +"psl@npm:1.9.0, psl@npm:^1.1.28, psl@npm:^1.1.33, psl@npm:^1.9.0": version: 1.9.0 resolution: "psl@npm:1.9.0" checksum: 20c4277f640c93d393130673f392618e9a8044c6c7bf61c53917a0fddb4952790f5f362c6c730a9c32b124813e173733f9895add8d26f566ed0ea0654b2e711d @@ -13451,6 +13610,13 @@ __metadata: languageName: node linkType: hard +"punycode@npm:2.3.1": + version: 2.3.1 + resolution: "punycode@npm:2.3.1" + checksum: bb0a0ceedca4c3c57a9b981b90601579058903c62be23c5e8e843d2c2d4148a3ecf029d5133486fb0e1822b098ba8bba09e89d6b21742d02fa26bda6441a6fb2 + languageName: node + linkType: hard + "punycode@npm:^1.3.2, punycode@npm:^1.4.1": version: 1.4.1 resolution: "punycode@npm:1.4.1" @@ -13525,6 +13691,22 @@ __metadata: languageName: node linkType: hard +"pvtsutils@npm:^1.3.2, pvtsutils@npm:^1.3.5": + version: 1.3.5 + resolution: "pvtsutils@npm:1.3.5" + dependencies: + tslib: ^2.6.1 + checksum: e734516b3cb26086c18bd9c012fefe818928a5073178842ab7e62885a090f1dd7bda9c7bb8cd317167502cb8ec86c0b1b0ccd71dac7ab469382a4518157b0d12 + languageName: node + linkType: hard + +"pvutils@npm:^1.1.3": + version: 1.1.3 + resolution: "pvutils@npm:1.1.3" + checksum: 2ee26a9e5176c348977d6ec00d8ee80bff62f51743b1c5fe8abeeb4c5d29d9959cdfe0ce146707a9e6801bce88190fed3002d720b072dc87d031c692820b44c9 + languageName: node + linkType: hard + "qrcode@npm:1.5.0": version: 1.5.0 resolution: "qrcode@npm:1.5.0" @@ -15241,6 +15423,13 @@ __metadata: languageName: node linkType: hard +"strnum@npm:^1.0.5": + version: 1.0.5 + resolution: "strnum@npm:1.0.5" + checksum: 651b2031db5da1bf4a77fdd2f116a8ac8055157c5420f5569f64879133825915ad461513e7202a16d7fec63c54fd822410d0962f8ca12385c4334891b9ae6dd2 + languageName: node + linkType: hard + "styled-components@npm:^5.3.5": version: 5.3.11 resolution: "styled-components@npm:5.3.11" @@ -15728,7 +15917,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.1, tslib@npm:^2.3.1": +"tslib@npm:^2.0.1, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.6.1, tslib@npm:^2.6.2": version: 2.6.2 resolution: "tslib@npm:2.6.2" checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad @@ -15908,6 +16097,15 @@ __metadata: languageName: node linkType: hard +"undici@npm:5.27.0": + version: 5.27.0 + resolution: "undici@npm:5.27.0" + dependencies: + "@fastify/busboy": ^2.0.0 + checksum: 3acad25bfe5957aa5edc24eb160b5da7a9c67a5061e2e001929bef4bafed07d93a2accb36d407179c35b3ae56adbe89b49e1dd80d8cea9fdc44dca2037174330 + languageName: node + linkType: hard + "unicode-canonical-property-names-ecmascript@npm:^2.0.0": version: 2.0.0 resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.0" @@ -17060,6 +17258,21 @@ __metadata: languageName: node linkType: hard +"yargs@npm:17.7.2, yargs@npm:^17.3.1, yargs@npm:^17.5.1": + version: 17.7.2 + resolution: "yargs@npm:17.7.2" + dependencies: + cliui: ^8.0.1 + escalade: ^3.1.1 + get-caller-file: ^2.0.5 + require-directory: ^2.1.1 + string-width: ^4.2.3 + y18n: ^5.0.5 + yargs-parser: ^21.1.1 + checksum: 73b572e863aa4a8cbef323dd911d79d193b772defd5a51aab0aca2d446655216f5002c42c5306033968193bdbf892a7a4c110b0d77954a7fdf563e653967b56a + languageName: node + linkType: hard + "yargs@npm:^15.3.1": version: 15.4.1 resolution: "yargs@npm:15.4.1" @@ -17079,21 +17292,6 @@ __metadata: languageName: node linkType: hard -"yargs@npm:^17.3.1, yargs@npm:^17.5.1": - version: 17.7.2 - resolution: "yargs@npm:17.7.2" - dependencies: - cliui: ^8.0.1 - escalade: ^3.1.1 - get-caller-file: ^2.0.5 - require-directory: ^2.1.1 - string-width: ^4.2.3 - y18n: ^5.0.5 - yargs-parser: ^21.1.1 - checksum: 73b572e863aa4a8cbef323dd911d79d193b772defd5a51aab0aca2d446655216f5002c42c5306033968193bdbf892a7a4c110b0d77954a7fdf563e653967b56a - languageName: node - linkType: hard - "yauzl@npm:^2.10.0": version: 2.10.0 resolution: "yauzl@npm:2.10.0"