From 277bb4e4eca1d1288ba72a03b3c484cee83a71ce Mon Sep 17 00:00:00 2001
From: Matthew <matthew@staff.libraryofcode.org>
Date: Thu, 30 Jan 2025 21:36:21 -0500
Subject: [PATCH] changes to Partner.ts and TLS.ts

---
 database/Partner.ts     |   5 +-
 discord/commands/TLS.ts | 293 ++++++++++++++++++++++++++++++++++++++++
 package-lock.json       | 134 +++++++++++++++++-
 3 files changed, 424 insertions(+), 8 deletions(-)
 create mode 100644 discord/commands/TLS.ts

diff --git a/database/Partner.ts b/database/Partner.ts
index d84a9f0..53bfcb6 100644
--- a/database/Partner.ts
+++ b/database/Partner.ts
@@ -14,7 +14,7 @@ export type PartnerTitle =
   | "Deputy Director of Engineering"
   | "Deputy Director of Operations"
   | "Services Manager"
-  | "Project Manager"
+  | "Community Manager"
   | "Engineering Core Partner"
   | "Operations Core Partner"
   | "Community Moderator"
@@ -51,6 +51,9 @@ export default class Partner implements SharedMemberAttributes {
   @prop({ required: true })
   public roleType: PartnerRoleType | undefined;
 
+  @prop()
+  public isKeyHolder: boolean | undefined;
+
   @prop({ required: true })
   public commissionType: PartnerCommissionType | undefined;
 
diff --git a/discord/commands/TLS.ts b/discord/commands/TLS.ts
new file mode 100644
index 0000000..76d175e
--- /dev/null
+++ b/discord/commands/TLS.ts
@@ -0,0 +1,293 @@
+import DiscordInteractionCommand from "../../util/DiscordInteractionCommand";
+import { ChatInputCommandInteraction, EmbedBuilder } from "discord.js";
+import axios from "axios";
+
+interface CertificateDetails {
+  bitLength: number;
+  connection: {
+    cipherSuite: string;
+    tlsVersion: string;
+  };
+  emailAddresses: [];
+  extendedKeyUsage: number[];
+  extendedKeyUsageAsText: string[];
+  fingerprint: string;
+  issuer: {
+    commonName: string;
+    country: string[];
+    locality: never; // TODO: needs clarification
+    province: string[];
+    organization: string[];
+    organizationalUnit: never; // TODO: needs clarification
+  };
+  keyUsageAsText: string[];
+  notAfter: Date;
+  notBefore: Date;
+  publicKeyAlgorithm: string;
+  san: string[];
+  serialNumber: string;
+  signatureAlgorithm: string;
+  status: boolean;
+  subject: {
+    commonName: string;
+    country: string[];
+    locality: never; // TODO: needs clarification
+    province: string[];
+    organization: string[];
+    organizationalUnit: never; // TODO: needs clarification
+  };
+  validationType: "DV" | "OV" | "EV";
+}
+
+// Define an enum for security levels
+enum SecurityLevel {
+  MostSecure,
+  Secure,
+  LessSecure,
+  NotSecure,
+}
+
+interface CipherSuite {
+  cipher: string;
+  securityLevel: SecurityLevel;
+}
+
+const CipherSuites: CipherSuite[] = [
+  // Most Secure (TLS 1.3 AEAD Ciphers)
+  { cipher: "TLS_AES_256_GCM_SHA384", securityLevel: SecurityLevel.MostSecure },
+  { cipher: "TLS_CHACHA20_POLY1305_SHA256", securityLevel: SecurityLevel.MostSecure },
+  { cipher: "TLS_AES_128_GCM_SHA256", securityLevel: SecurityLevel.MostSecure },
+
+  // Secure (TLS 1.2 AEAD Ciphers with Forward Secrecy)
+  { cipher: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", securityLevel: SecurityLevel.Secure },
+  { cipher: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", securityLevel: SecurityLevel.Secure },
+  { cipher: "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", securityLevel: SecurityLevel.Secure },
+  { cipher: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", securityLevel: SecurityLevel.Secure },
+  { cipher: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", securityLevel: SecurityLevel.Secure },
+  { cipher: "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", securityLevel: SecurityLevel.Secure },
+  { cipher: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", securityLevel: SecurityLevel.Secure },
+  { cipher: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", securityLevel: SecurityLevel.Secure },
+  { cipher: "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", securityLevel: SecurityLevel.Secure },
+
+  // Less Secure (CBC with TLS 1.2 and SHA-256/SHA-384)
+  { cipher: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", securityLevel: SecurityLevel.LessSecure },
+  { cipher: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", securityLevel: SecurityLevel.LessSecure },
+  { cipher: "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", securityLevel: SecurityLevel.LessSecure },
+  { cipher: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", securityLevel: SecurityLevel.LessSecure },
+  { cipher: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", securityLevel: SecurityLevel.LessSecure },
+  { cipher: "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", securityLevel: SecurityLevel.LessSecure },
+  { cipher: "TLS_RSA_WITH_AES_256_CBC_SHA256", securityLevel: SecurityLevel.LessSecure },
+  { cipher: "TLS_RSA_WITH_AES_128_CBC_SHA256", securityLevel: SecurityLevel.LessSecure },
+
+  // Not Secure (CBC with TLS 1.0/1.1 or SHA-1)
+  { cipher: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
+  { cipher: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
+  { cipher: "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
+  { cipher: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
+  { cipher: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
+  { cipher: "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
+  { cipher: "TLS_RSA_WITH_AES_256_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
+  { cipher: "TLS_RSA_WITH_AES_128_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
+  { cipher: "TLS_RSA_WITH_3DES_EDE_CBC_SHA", securityLevel: SecurityLevel.NotSecure },
+  { cipher: "TLS_RSA_WITH_RC4_128_SHA", securityLevel: SecurityLevel.NotSecure },
+  { cipher: "TLS_RSA_WITH_RC4_128_MD5", securityLevel: SecurityLevel.NotSecure },
+];
+
+export default class TLS extends DiscordInteractionCommand {
+  constructor() {
+    super("tls", "Receives TLS information about an HTTP server with a FQDN.");
+    this.builder.addStringOption((option) => {
+      return option
+        .setName("fqdn")
+        .setDescription(
+          "The Fully Qualified Domain Name (FQDN) for the server you want to perform the TLS/SSL lookup for."
+        )
+        .setRequired(true)
+        .setMinLength(3);
+    });
+  }
+
+  public getCipherSecurityLevel(cipher: string): SecurityLevel | null {
+    const result = CipherSuites.find((entry) => entry.cipher === cipher);
+    return result ? result.securityLevel : null;
+  }
+
+  public async execute(interaction: ChatInputCommandInteraction) {
+    await interaction.deferReply({ ephemeral: false });
+    try {
+      const certAPIReq = await axios.get(`https://certapi.libraryofcode.org/`, {
+        params: { q: interaction.options.getString("fqdn", true) },
+      });
+      if (certAPIReq.status !== 200) {
+        return interaction.editReply({
+          content:
+            "Could not fetch information for this FQDN's HTTP server. Please check the FQDN, its server, and try again.",
+        });
+      }
+      const certData: CertificateDetails = certAPIReq.data;
+
+      if (!certData.status) {
+        return interaction.editReply({
+          content: "Issue when fetching this FQDN's TLS certificate. Please try again later.",
+        });
+      }
+      const embed = new EmbedBuilder();
+      embed.setAuthor({
+        name: interaction.options.getString("fqdn", true),
+        iconURL: `https://${interaction.options.getString("fqdn", true)}/favicon.ico`,
+      });
+      let desc = "";
+      if (certData.validationType === "EV") {
+        desc += `**Certificate issued to:** __${certData.subject.organization[0]} [${certData.subject.country[0]}]__\n**Verified by:** __${certData.issuer.organization[0]}__\n\n`;
+      } else if (certData.issuer.organization) {
+        desc += `**Verified by:** __${certData.issuer.organization[0]}__\n\n`;
+      }
+      if (certData.subject) {
+        desc += "## Subject\n";
+        if (certData.subject.organization && certData.issuer.commonName) {
+          desc += `__**${certData.subject.organization[0]} (${certData.subject.commonName})**__\n`;
+        }
+        if (certData.subject.commonName) {
+          desc += `**Common Name:** ${certData.subject.commonName}\n`;
+        }
+        if (certData.subject.organization) {
+          desc += `**Organization:** ${certData.subject.organization[0]}\n`;
+        }
+        if (certData.subject.organizationalUnit) {
+          desc += `**Organizational Unit:** ${certData.subject.organizationalUnit[0]}\n`;
+        }
+        if (certData.subject.locality) {
+          desc += `**Locality:** ${certData.subject.locality[0]}\n`;
+        }
+        if (certData.subject.province) {
+          desc += `**State/Province:** ${certData.subject.province[0]}\n`;
+        }
+        if (certData.subject.country) {
+          desc += `**Country:** ${certData.subject.country[0]}\n`;
+        }
+      }
+      if (certData.issuer) {
+        desc += "## Issuer\n";
+        if (certData.issuer.organization && certData.issuer.commonName) {
+          desc += `__**${certData.issuer.organization[0]} (${certData.issuer.commonName})**__\n`;
+        }
+        if (certData.issuer.commonName) {
+          desc += `**Common Name:** ${certData.issuer.commonName}\n`;
+        }
+        if (certData.issuer.organization) {
+          desc += `**Organization:** ${certData.issuer.organization[0]}\n`;
+        }
+        if (certData.issuer.organizationalUnit) {
+          desc += `**Organizational Unit:** ${certData.issuer.organizationalUnit[0]}\n`;
+        }
+        if (certData.subject.locality) {
+          desc += `**Locality:** ${certData.subject.locality[0]}\n`;
+        }
+        if (certData.issuer.province) {
+          desc += `**State/Province:** ${certData.issuer.province[0]}\n`;
+        }
+        if (certData.issuer.country) {
+          desc += `**Country:** ${certData.issuer.country[0]}\n`;
+        }
+      }
+      embed.setDescription(desc);
+      let validationType:
+        | "Domain Validation (DV)"
+        | "Organization Validation (OV)"
+        | ":lock: Extended Validation (EV)"
+        | string;
+      switch (certData.validationType) {
+        case "DV":
+          validationType = "Domain Validation (DV)";
+          break;
+        case "OV":
+          validationType = "Organization Validation (OV)";
+          embed.setColor("#4287f5");
+          break;
+        case "EV":
+          embed.setColor("#42f554");
+          validationType = ":lock: Extended Validation (EV)";
+          break;
+        default:
+          validationType = "N/A';";
+          break;
+      }
+      let cipherSuiteText: string = "";
+      switch (this.getCipherSecurityLevel(certData.connection.cipherSuite)) {
+        case SecurityLevel.MostSecure:
+          cipherSuiteText = `:green_circle: ${certData.connection.cipherSuite} (${certData.connection.tlsVersion})`;
+          break;
+
+        case SecurityLevel.Secure:
+          cipherSuiteText = `:yellow_circle: ${certData.connection.cipherSuite} (${certData.connection.tlsVersion})`;
+          break;
+
+        case SecurityLevel.LessSecure:
+          cipherSuiteText = `:orange_circle: ${certData.connection.cipherSuite} (${certData.connection.tlsVersion})`;
+          break;
+
+        case SecurityLevel.NotSecure:
+          cipherSuiteText = `:red_circle: ${certData.connection.cipherSuite} (${certData.connection.tlsVersion})`;
+          break;
+
+        default:
+          cipherSuiteText = `:grey_question: ${certData.connection.cipherSuite} (${certData.connection.tlsVersion})`;
+          break;
+      }
+      embed.addFields(
+        {
+          name: "Validation Type",
+          value: validationType,
+          inline: true,
+        },
+        {
+          name: "Cipher Suite",
+          value: cipherSuiteText,
+          inline: false,
+        },
+        {
+          name: "Public Key Algorithm",
+          value: `${certData.publicKeyAlgorithm} ${certData.bitLength}`,
+          inline: true,
+        },
+        {
+          name: "Signature Algorithm",
+          value: certData.signatureAlgorithm,
+          inline: true,
+        },
+        { name: "Not Before", value: new Date(certData.notBefore).toUTCString(), inline: true },
+        { name: "Not After", value: new Date(certData.notAfter).toUTCString(), inline: true },
+        {
+          name: "Serial Number",
+          value: certData.serialNumber,
+          inline: true,
+        }
+      );
+      console.log(certData); // TODO: Remove after testing.
+      if (certData.keyUsageAsText?.length)
+        embed.addFields({
+          name: "Key Usages",
+          value: certData.keyUsageAsText.join(", "),
+          inline: true,
+        });
+      if (certData.extendedKeyUsageAsText?.length)
+        embed.addFields({
+          name: "Extended Key Usages",
+          value: certData.extendedKeyUsageAsText.join(", "),
+          inline: true,
+        });
+      // embed.addField('Common Name', x509.data.subject.commonName, true);
+      // embed.addField('Issuer', x509.data.issuer.commonName, true);
+      // embed.addBlankField();
+      // embed.addField('Public Key Algorithm', x509.data.publicKeyAlgorithm, true);
+      // embed.addField('Not Before', new Date(x509.data.notBefore).toUTCString(), true);
+      // embed.addField('Not After', new Date(x509.data.notAfter).toUTCString(), true);
+      // if (x509.data.keyUsageAsText.length) embed.addField('Key Usages', x509.data.keyUsageAsText.join(', '), true);
+      // if (x509.data.extendedKeyUsageAsText.length) embed.addField('Extended Key Usages', x509.data.extendedKeyUsageAsText.join(', '), true);
+
+      return await interaction.editReply({ embeds: [embed] });
+    } catch (err) {
+      return interaction.editReply({ content: `Error processing retrieval from FQDN: ${err}` });
+    }
+  }
+}
diff --git a/package-lock.json b/package-lock.json
index 407e07e..597487a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,11 +1,15 @@
 {
-  "name": "CRRA",
+  "name": "crv2",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
+      "name": "crv2",
+      "license": "AGPL-3.0-or-later",
       "dependencies": {
         "@typegoose/typegoose": "^12.2.0",
+        "auth0": "^4.12.0",
+        "axios": "^1.7.8",
         "discord.js": "^14.14.1",
         "mongoose": "^8.2.2",
         "stripe": "^14.21.0",
@@ -325,9 +329,9 @@
       }
     },
     "node_modules/@eslint/plugin-kit": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.1.tgz",
-      "integrity": "sha512-HFZ4Mp26nbWk9d/BpvP0YNL6W4UoZF0VFcTw/aPPA8RpOxeFQgK+ClABGgAUXs9Y/RGX/l1vOmrqz1MQt9MNuw==",
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz",
+      "integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==",
       "dev": true,
       "dependencies": {
         "levn": "^0.4.1"
@@ -933,6 +937,39 @@
       "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
       "dev": true
     },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
+    "node_modules/auth0": {
+      "version": "4.12.0",
+      "resolved": "https://registry.npmjs.org/auth0/-/auth0-4.12.0.tgz",
+      "integrity": "sha512-5WDAHb8EvWSmRyA9D+FTBrHdEL1RM48PTPHVPxSmzbiAXrhR4pSgwSJyoGjia2+rvMR2NMXhtMfuRRqosEp7PA==",
+      "dependencies": {
+        "jose": "^4.13.2",
+        "undici-types": "^6.15.0",
+        "uuid": "^9.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/auth0/node_modules/undici-types": {
+      "version": "6.20.0",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
+      "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="
+    },
+    "node_modules/axios": {
+      "version": "1.7.8",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.8.tgz",
+      "integrity": "sha512-Uu0wb7KNqK2t5K+YQyVCLM76prD5sRFjKHbJYCP1J7JFGEQ6nN7HWn9+04LAeiJ3ji54lgS/gZCH1oxyrf1SPw==",
+      "dependencies": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
     "node_modules/balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -1300,6 +1337,17 @@
       "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
       "dev": true
     },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
     "node_modules/commander": {
       "version": "12.1.0",
       "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
@@ -1322,9 +1370,9 @@
       "devOptional": true
     },
     "node_modules/cross-spawn": {
-      "version": "7.0.3",
-      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
-      "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
       "dependencies": {
         "path-key": "^3.1.0",
         "shebang-command": "^2.0.0",
@@ -1377,6 +1425,14 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/diff": {
       "version": "4.0.2",
       "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
@@ -1871,6 +1927,25 @@
       "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
       "dev": true
     },
+    "node_modules/follow-redirects": {
+      "version": "1.15.9",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
+      "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/foreground-child": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
@@ -1886,6 +1961,19 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/form-data": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
+      "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
     "node_modules/function-bind": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@@ -2234,6 +2322,14 @@
         "@pkgjs/parseargs": "^0.11.0"
       }
     },
+    "node_modules/jose": {
+      "version": "4.15.9",
+      "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz",
+      "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==",
+      "funding": {
+        "url": "https://github.com/sponsors/panva"
+      }
+    },
     "node_modules/js-yaml": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
@@ -2618,6 +2714,25 @@
         "node": ">=8.6"
       }
     },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
     "node_modules/mimic-fn": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
@@ -3114,6 +3229,11 @@
         "node": ">=6.0.0"
       }
     },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+    },
     "node_modules/punycode": {
       "version": "2.3.1",
       "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",