From 159e5119b875afdf9161216d6dc646c46bd9b8e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?JimmyLv=5F=E5=90=95=E7=AB=8B=E9=9D=92?= Date: Wed, 7 Sep 2022 11:28:13 +0300 Subject: [PATCH] feat: Parabol Icebreaker Zoom App (#12) * feat: migrate the /auth and /install route logic * fix: rename function * feat: add the verifyzoom.html * feat: add the zoom & github entries * fix: fix the lint-staged lint issue * fix: update Missing OWASP Secure Headers * fix: add 'unsafe-inline' style-src CSP * fix: allow all CSP policy --- .gitignore | 1 + components/icons/GitHub.tsx | 16 +++ components/icons/Zoom.tsx | 16 +++ lib/config.ts | 37 +++++++ lib/zoom/with-session.ts | 29 +++++ lib/zoom/zoom-api.ts | 156 ++++++++++++++++++++++++++ next.config.js | 26 +++++ package.json | 11 +- pages/api/zoom/auth.ts | 34 ++++++ pages/api/zoom/install.ts | 17 +++ pages/index.tsx | 67 ++++++++---- public/verifyzoom.html | 1 + yarn.lock | 213 +++++++++++++++++++++++++++++++++++- 13 files changed, 596 insertions(+), 28 deletions(-) create mode 100644 components/icons/GitHub.tsx create mode 100644 components/icons/Zoom.tsx create mode 100644 lib/config.ts create mode 100644 lib/zoom/with-session.ts create mode 100644 lib/zoom/zoom-api.ts create mode 100644 pages/api/zoom/auth.ts create mode 100644 pages/api/zoom/install.ts create mode 100644 public/verifyzoom.html diff --git a/.gitignore b/.gitignore index 30cbd6d..dd4b741 100755 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ # misc .DS_Store *.pem +.idea/ # debug npm-debug.log* diff --git a/components/icons/GitHub.tsx b/components/icons/GitHub.tsx new file mode 100644 index 0000000..026ec96 --- /dev/null +++ b/components/icons/GitHub.tsx @@ -0,0 +1,16 @@ +interface Props { + className?: string; +} + +export const GitHub = ({ className }: Props) => { + return ( + + + + ); +}; diff --git a/components/icons/Zoom.tsx b/components/icons/Zoom.tsx new file mode 100644 index 0000000..0829147 --- /dev/null +++ b/components/icons/Zoom.tsx @@ -0,0 +1,16 @@ +interface Props { + className?: string; +} + +export const Zoom = ({ className }: Props) => { + return ( + + + + ); +}; diff --git a/lib/config.ts b/lib/config.ts new file mode 100644 index 0000000..293e55b --- /dev/null +++ b/lib/config.ts @@ -0,0 +1,37 @@ +export const zoomApp = { + host: process.env.ZM_HOST || 'https://zoom.us', + clientId: process.env.ZM_CLIENT_ID || '', + clientSecret: process.env.ZM_CLIENT_SECRET || '', + redirectUrl: process.env.ZM_REDIRECT_URL || '', + sessionSecret: process.env.SESSION_SECRET || '', +}; + +//{ +// name: 'session', +// httpOnly: true, +// keys: [zoomApp.sessionSecret], +// maxAge: 24 * 60 * 60 * 1000, +// secure: process.env.NODE_ENV === 'production', +// } +export const sessionOptions = { + cookieName: "parabol_session", + password: zoomApp.sessionSecret, + // secure: true should be used in production (HTTPS) but can't be used in development (HTTP) + cookieOptions: { + secure: process.env.NODE_ENV === "production", + }, +}; + +// Zoom App Info +export const appName = process.env.APP_NAME || 'zoom-app'; +export const redirectUri = zoomApp.redirectUrl; + +// HTTP +export const port = process.env.PORT || '3000'; + +// require secrets are explicitly imported +export default { + appName, + redirectUri, + port, +}; diff --git a/lib/zoom/with-session.ts b/lib/zoom/with-session.ts new file mode 100644 index 0000000..b3d7f41 --- /dev/null +++ b/lib/zoom/with-session.ts @@ -0,0 +1,29 @@ +import { withIronSessionApiRoute, withIronSessionSsr } from "iron-session/next"; +import { + GetServerSidePropsContext, + GetServerSidePropsResult, + NextApiHandler, +} from "next"; +import { sessionOptions } from '../config' + +declare module "iron-session" { + interface IronSessionData { + state: string | null + verifier: string | null + } +} + +export function withSessionRoute(handler: NextApiHandler) { + return withIronSessionApiRoute(handler, sessionOptions); +} + +// Theses types are compatible with InferGetStaticPropsType https://nextjs.org/docs/basic-features/data-fetching#typescript-use-getstaticprops +export function withSessionSsr< + P extends { [key: string]: unknown } = { [key: string]: unknown }, + >( + handler: ( + context: GetServerSidePropsContext, + ) => GetServerSidePropsResult

| Promise>, +) { + return withIronSessionSsr(handler, sessionOptions); +} diff --git a/lib/zoom/zoom-api.ts b/lib/zoom/zoom-api.ts new file mode 100644 index 0000000..3ca8e08 --- /dev/null +++ b/lib/zoom/zoom-api.ts @@ -0,0 +1,156 @@ +// @ts-nocheck +import axios from "axios"; +import { URL } from "url"; +import { zoomApp } from "../config"; +import crypto from "crypto"; + +// returns a base64 encoded url +const base64URL = (s) => + s + .toString("base64") + .replace(/\+/g, "-") + .replace(/\//g, "_") + .replace(/=/g, ""); + +// returns a random string of format fmt +const rand = (fmt, depth = 32) => crypto.randomBytes(depth).toString(fmt); + +// Get Zoom API URL from Zoom Host value +const host = new URL(zoomApp.host); +host.hostname = `api.${host.hostname}`; + +const baseURL = host.href; + +/** + * Generic function for getting access or refresh tokens + * @param {string} [id=''] - Username for Basic Auth + * @param {string} [secret=''] - Password for Basic Auth + * @param {Object} params - Request parameters (form-urlencoded) + */ +function tokenRequest(params, id = "", secret = "") { + const username = id || zoomApp.clientId; + const password = secret || zoomApp.clientSecret; + + return axios({ + data: new URLSearchParams(params).toString(), + baseURL: zoomApp.host, + url: "/oauth/token", + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + auth: { + username, + password, + }, + }).then(({ data }) => Promise.resolve(data)); +} + +/** + * Generic function for making requests to the Zoom API + * @param {string} method - Request method + * @param {string | URL} endpoint - Zoom API Endpoint + * @param {string} token - Access Token + * @param {object} [data=null] - Request data + */ +function apiRequest(method, endpoint, token, data = null) { + return axios({ + data, + method, + baseURL, + url: `/v2${endpoint}`, + headers: { + Authorization: `Bearer ${token}`, + }, + }).then(({ data }) => Promise.resolve(data)); +} + +/** + * Return the url, state and verifier for the Zoom App Install + * @return {{verifier: string, state: string, url: module:url.URL}} + */ +export function getInstallURL() { + const state = rand("base64"); + const verifier = rand("ascii"); + + const digest = crypto + .createHash("sha256") + .update(verifier) + .digest("base64") + .toString(); + + const challenge = base64URL(digest); + + const url = new URL("/oauth/authorize", zoomApp.host); + + url.searchParams.set("response_type", "code"); + url.searchParams.set("client_id", zoomApp.clientId); + url.searchParams.set("redirect_uri", zoomApp.redirectUrl); + url.searchParams.set("code_challenge", challenge); + url.searchParams.set("code_challenge_method", "S256"); + url.searchParams.set("state", state); + + return { url, state, verifier }; +} + +/** + * Obtains an OAuth access token from Zoom + * @param {string} code - Authorization code from user authorization + * @param verifier - code_verifier for PKCE + * @return {Promise} Promise resolving to the access token object + */ +export async function getToken(code, verifier) { + if (!code || typeof code !== "string") + throw new Error("authorization code must be a valid string"); + + if (!verifier || typeof verifier !== "string") + throw new Error("code verifier code must be a valid string"); + + return tokenRequest({ + code, + code_verifier: verifier, + redirect_uri: zoomApp.redirectUrl, + grant_type: "authorization_code", + }); +} + +/** + * Obtain a new Access Token from a Zoom Refresh Token + * @param {string} token - Refresh token to use + * @return {Promise} + */ +export async function refreshToken(token) { + if (!token || typeof token !== "string") + throw new Error("refresh token must be a valid string"); + + return tokenRequest({ + refresh_token: token, + grant_type: "refresh_token", + }); +} + +/** + * Use the Zoom API to get a Zoom User + * @param {string} uid - User ID to query on + * @param {string} token Zoom App Access Token + */ +export function getZoomUser(uid, token) { + return apiRequest("GET", `/users/${uid}`, token); +} + +/** + * Return the DeepLink for opening Zoom + * @param {string} token - Zoom App Access Token + * @return {Promise} + */ +export function getDeeplink(token) { + // @ts-ignore + return apiRequest("POST", "/zoomapp/deeplink", token, { + action: JSON.stringify({ + url: "/", + role_name: "Owner", + verified: 1, + role_id: 0, + }), + }).then((data) => Promise.resolve(data.deeplink)); +} diff --git a/next.config.js b/next.config.js index e58b70e..5a41729 100755 --- a/next.config.js +++ b/next.config.js @@ -5,12 +5,38 @@ const CACHE_CONTROL_HEADER = { key: "Cache-Control", value: `public, max-age=${ONE_YEAR_SECONDS}, immutable`, }; +const ContentSecurityPolicy = ` + default-src * 'unsafe-inline' 'unsafe-eval'; + script-src * 'unsafe-inline' 'unsafe-eval'; + connect-src * 'unsafe-inline'; + img-src * data: blob: 'unsafe-inline'; + frame-src *; + style-src * 'unsafe-inline'; +`; +const SECURITY_HEADERS = [ + { + key: "X-Content-Type-Options", + value: "nosniff", + }, + { + key: "Content-Security-Policy", + value: ContentSecurityPolicy.replace(/\s{2,}/g, " ").trim(), + }, + { + key: "Referrer-Policy", + value: "origin-when-cross-origin", + }, +]; const nextConfig = { reactStrictMode: true, swcMinify: true, headers: async () => { return [ + { + source: "/:path*", + headers: SECURITY_HEADERS, + }, { source: "/img/:path*", headers: [CACHE_CONTROL_HEADER], diff --git a/package.json b/package.json index 671720c..ca7e911 100644 --- a/package.json +++ b/package.json @@ -10,11 +10,16 @@ "prepare": "husky install" }, "lint-staged": { - "*.{ts,tsx}": "yarn lint --fix" + "*.{ts,tsx}": [ + "eslint --fix", + "git add" + ] }, "dependencies": { + "axios": "^0.27.2", "chrome-aws-lambda": "^10.1.0", "clsx": "^1.2.1", + "iron-session": "^6.2.0", "next": "12.2.2", "next-seo": "^5.5.0", "puppeteer-core": "^15.5.0", @@ -30,12 +35,12 @@ "eslint": "8.20.0", "eslint-config-next": "12.2.2", "eslint-config-prettier": "^8.5.0", + "husky": "^8.0.0", "lint-staged": "^13.0.3", "postcss": "^8.4.14", "prettier": "^2.7.1", "prettier-plugin-tailwindcss": "^0.1.12", "tailwindcss": "^3.1.6", - "typescript": "4.7.4", - "husky": "^8.0.0" + "typescript": "4.7.4" } } diff --git a/pages/api/zoom/auth.ts b/pages/api/zoom/auth.ts new file mode 100644 index 0000000..b60e906 --- /dev/null +++ b/pages/api/zoom/auth.ts @@ -0,0 +1,34 @@ +import { NextApiRequest, NextApiResponse } from "next"; +import { withSessionRoute } from "../../../lib/zoom/with-session"; +import { getDeeplink, getToken } from "../../../lib/zoom/zoom-api"; + +type Data = { + name?: string; + error?: string; +}; + +export default withSessionRoute(authRoute); + +async function authRoute(req: NextApiRequest, res: NextApiResponse) { + req.session.state = null; + + console.log('========auth req.session========', req.session) + try { + const code = req.query.code; + const verifier = req.session.verifier; + + req.session.verifier = null; + + // get Access Token from Zoom + const { access_token: accessToken } = await getToken(code, verifier); + + // fetch deeplink from Zoom API + const deeplink = await getDeeplink(accessToken); + + await req.session.save(); + // redirect the user to the Zoom Client + res.redirect(deeplink); + } catch (e: any) { + res.status(500).send({ error: e.message }); + } +} diff --git a/pages/api/zoom/install.ts b/pages/api/zoom/install.ts new file mode 100644 index 0000000..c4ca923 --- /dev/null +++ b/pages/api/zoom/install.ts @@ -0,0 +1,17 @@ +import { NextApiRequest, NextApiResponse } from "next"; +import { withSessionRoute } from "../../../lib/zoom/with-session"; +import { getInstallURL } from "../../../lib/zoom/zoom-api"; + +export default withSessionRoute(installRoute); + +interface Data {} + +async function installRoute(req: NextApiRequest, res: NextApiResponse) { + const { url, state, verifier } = getInstallURL(); + + req.session.state = state; + req.session.verifier = verifier; + await req.session.save(); + + res.redirect(url.href); +} diff --git a/pages/index.tsx b/pages/index.tsx index f26112b..7c0805d 100755 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,13 +1,13 @@ -import type { - GetServerSideProps, - NextPage, -} from "next"; +import type { GetServerSideProps, NextPage } from "next"; import { NextSeo } from "next-seo"; +import Link from "next/link"; import { useRouter } from "next/router"; import React, { useCallback } from "react"; import { useHotkeys } from "react-hotkeys-hook"; import { Card } from "../components/Card"; import { IcebreakerGenerator } from "../components/IcebreakerGenerator"; +import { GitHub } from "../components/icons/GitHub"; +import { Zoom } from "../components/icons/Zoom"; import { Mark } from "../components/Mark"; import { LinkIcon } from "../components/LinkIcon"; import { generateRandomActionLabel } from "../lib/actions"; @@ -99,28 +99,49 @@ const Icebreaker: NextPage = ({ handleGenerateClick={handleGenerateClick} /> -

- -
+
+ +
); diff --git a/public/verifyzoom.html b/public/verifyzoom.html new file mode 100644 index 0000000..98ca761 --- /dev/null +++ b/public/verifyzoom.html @@ -0,0 +1 @@ +6a95c9af0d1e440bbfad658193ce2a91 \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index b4c12a8..9302b46 100644 --- a/yarn.lock +++ b/yarn.lock @@ -144,6 +144,33 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@peculiar/asn1-schema@^2.1.6": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.0.tgz#5368416eb336138770c692ffc2bab119ee3ae917" + integrity sha512-DtNLAG4vmDrdSJFPe7rypkcj597chNQL7u+2dBtYo5mh7VW2+im6ke+O0NVr8W1f4re4C3F71LhoMb0Yxqa48Q== + dependencies: + asn1js "^3.0.5" + pvtsutils "^1.3.2" + tslib "^2.4.0" + +"@peculiar/json-schema@^1.1.12": + version "1.1.12" + resolved "https://registry.yarnpkg.com/@peculiar/json-schema/-/json-schema-1.1.12.tgz#fe61e85259e3b5ba5ad566cb62ca75b3d3cd5339" + integrity sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w== + dependencies: + tslib "^2.0.0" + +"@peculiar/webcrypto@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.0.tgz#f941bd95285a0f8a3d2af39ccda5197b80cd32bf" + integrity sha512-U58N44b2m3OuTgpmKgf0LPDOmP3bhwNz01vAnj1mBwxBASRhptWYK+M3zG+HBkDqGQM+bFsoIihTW8MdmPXEqg== + dependencies: + "@peculiar/asn1-schema" "^2.1.6" + "@peculiar/json-schema" "^1.1.12" + pvtsutils "^1.3.2" + tslib "^2.4.0" + webcrypto-core "^1.7.4" + "@rushstack/eslint-patch@^1.1.3": version "1.1.4" resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.1.4.tgz#0c8b74c50f29ee44f423f7416829c0bf8bb5eb27" @@ -156,21 +183,80 @@ dependencies: tslib "^2.4.0" +"@types/body-parser@*": + version "1.19.2" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" + integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.35" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" + integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + dependencies: + "@types/node" "*" + +"@types/cookie@^0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.5.1.tgz#b29aa1f91a59f35e29ff8f7cb24faf1a3a750554" + integrity sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g== + +"@types/express-serve-static-core@^4.17.18": + version "4.17.30" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.30.tgz#0f2f99617fa8f9696170c46152ccf7500b34ac04" + integrity sha512-gstzbTWro2/nFed1WXtf+TtrpwxH7Ggs4RLYTLbeVgIkUQOI3WG/JKjgeOU1zXDvezllupjrf8OPIdvTbIaVOQ== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@^4.17.13": + version "4.17.13" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" + integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.18" + "@types/qs" "*" + "@types/serve-static" "*" + "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== +"@types/mime@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" + integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== + "@types/node@*", "@types/node@18.0.6": version "18.0.6" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.6.tgz#0ba49ac517ad69abe7a1508bc9b3a5483df9d5d7" integrity sha512-/xUq6H2aQm261exT6iZTMifUySEt4GR5KX8eYyY+C4MSNPqSh9oNIP7tz2GLKTlFaiBbgZNxffoR3CVRG+cljw== +"@types/node@^17.0.41": + version "17.0.45" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" + integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== + "@types/prop-types@*": version "15.7.5" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== +"@types/qs@*": + version "6.9.7" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + +"@types/range-parser@*": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" + integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + "@types/react-dom@18.0.6": version "18.0.6" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.6.tgz#36652900024842b74607a17786b6662dd1e103a1" @@ -192,6 +278,14 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== +"@types/serve-static@*": + version "1.15.0" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155" + integrity sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg== + dependencies: + "@types/mime" "*" + "@types/node" "*" + "@types/yauzl@^2.9.1": version "2.10.0" resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.0.tgz#b3248295276cf8c6f153ebe6a9aba0c988cb2599" @@ -388,6 +482,15 @@ array.prototype.flatmap@^1.3.0: es-abstract "^1.19.2" es-shim-unscopables "^1.0.0" +asn1js@^3.0.1, asn1js@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.5.tgz#5ea36820443dbefb51cc7f88a2ebb5b462114f38" + integrity sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ== + dependencies: + pvtsutils "^1.3.2" + pvutils "^1.1.3" + tslib "^2.4.0" + ast-types-flow@^0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" @@ -398,6 +501,11 @@ astral-regex@^2.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + autoprefixer@^10.4.7: version "10.4.7" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.7.tgz#1db8d195f41a52ca5069b7593be167618edbbedf" @@ -415,6 +523,14 @@ axe-core@^4.4.2: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.3.tgz#11c74d23d5013c0fa5d183796729bc3482bd2f6f" integrity sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w== +axios@^0.27.2: + version "0.27.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" + integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== + dependencies: + follow-redirects "^1.14.9" + form-data "^4.0.0" + axobject-query@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be" @@ -482,6 +598,14 @@ buffer@^5.2.1, buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" +buffer@^6: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -590,6 +714,13 @@ colorette@^2.0.16, colorette@^2.0.17: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + commander@^9.3.0: version "9.4.0" resolved "https://registry.yarnpkg.com/commander/-/commander-9.4.0.tgz#bc4a40918fefe52e22450c111ecd6b7acce6f11c" @@ -600,6 +731,11 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +cookie@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== + core-js-pure@^3.20.2: version "3.23.5" resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.23.5.tgz#23daaa9af9230e50f10b0fa4b8e6b87402be4c33" @@ -675,6 +811,11 @@ defined@^1.0.0: resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" integrity sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ== +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + detective@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.1.tgz#6af01eeda11015acb0e73f933242b70f24f91034" @@ -1119,6 +1260,20 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.6.tgz#022e9218c637f9f3fc9c35ab9c9193f05add60b2" integrity sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ== +follow-redirects@^1.14.9: + version "1.15.1" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" + integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + fraction.js@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" @@ -1309,7 +1464,7 @@ husky@^8.0.0: resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.1.tgz#511cb3e57de3e3190514ae49ed50f6bc3f50b3e9" integrity sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw== -ieee754@^1.1.13: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -1359,6 +1514,25 @@ internal-slot@^1.0.3: has "^1.0.3" side-channel "^1.0.4" +iron-session@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/iron-session/-/iron-session-6.2.0.tgz#75279df32ad31bf302aaf294ee87ad0bea69355a" + integrity sha512-3prk7mfh5XVzWFF1lgWuyYqYX7GnlxfpO3dkMEz2tAYLn37XJUUj+NYXBOP4MKvTFU/qA+qfkuCJD4X5wh7N6Q== + dependencies: + "@peculiar/webcrypto" "^1.4.0" + "@types/cookie" "^0.5.1" + "@types/express" "^4.17.13" + "@types/node" "^17.0.41" + cookie "^0.5.0" + iron-webcrypto "^0.1.0" + +iron-webcrypto@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/iron-webcrypto/-/iron-webcrypto-0.1.0.tgz#670982b366fae073c6d36d62e776f621bfd3a002" + integrity sha512-Um9JTgZRJWY/Ngr5SP+D4uMu7OFOO+iwytm5hZlKmszYVJEodIMtZn1/LDJ4wA5l9bwSdKj/3wuUw1MxjTpEzQ== + dependencies: + buffer "^6" + is-bigint@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" @@ -1654,6 +1828,18 @@ micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.2" picomatch "^2.3.1" +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -2110,6 +2296,18 @@ puppeteer-core@^15.5.0: unbzip2-stream "1.4.3" ws "8.8.0" +pvtsutils@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.2.tgz#9f8570d132cdd3c27ab7d51a2799239bf8d8d5de" + integrity sha512-+Ipe2iNUyrZz+8K/2IOo+kKikdtfhRKzNpQbruF2URmqPtoqAs8g3xS7TJvFF2GcPXjh7DkqMnpVveRFq4PgEQ== + dependencies: + tslib "^2.4.0" + +pvutils@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.1.3.tgz#f35fc1d27e7cd3dfbd39c0826d173e806a03f5a3" + integrity sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -2531,7 +2729,7 @@ tslib@^1.8.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.1.0, tslib@^2.4.0: +tslib@^2.0.0, tslib@^2.1.0, tslib@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== @@ -2613,6 +2811,17 @@ v8-compile-cache@^2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== +webcrypto-core@^1.7.4: + version "1.7.5" + resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.5.tgz#c02104c953ca7107557f9c165d194c6316587ca4" + integrity sha512-gaExY2/3EHQlRNNNVSrbG2Cg94Rutl7fAaKILS1w8ZDhGxdFOaw6EbCfHIxPy9vt/xwp5o0VQAx9aySPF6hU1A== + dependencies: + "@peculiar/asn1-schema" "^2.1.6" + "@peculiar/json-schema" "^1.1.12" + asn1js "^3.0.1" + pvtsutils "^1.3.2" + tslib "^2.4.0" + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"