diff --git a/CHANGELOG.md b/CHANGELOG.md index 781b11c4fcd4144ba4b92f8f677b1661b7154454..bc4194d83df31b334956f2aa272c8b86f9fb3cb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ VS Code v0.00.0 - chore: cross-compile docker images with buildx #3166 @oxy - chore: update node to v14 #3458 @oxy - chore: update .gitignore #3557 @cuining +- fix: use sufficient computational effort for password hash #3422 @jsjoeio ### Development diff --git a/ci/build/build-standalone-release.sh b/ci/build/build-standalone-release.sh index e85678a2931003b96cd78228605060094e0f72b3..3afde2ead56c81075abad92ea629761d8c641ca5 100755 --- a/ci/build/build-standalone-release.sh +++ b/ci/build/build-standalone-release.sh @@ -1,6 +1,10 @@ #!/usr/bin/env bash set -euo pipefail +# This is due to an upstream issue with RHEL7/CentOS 7 comptability with node-argon2 +# See: https://github.com/cdr/code-server/pull/3422#pullrequestreview-677765057 +export npm_config_build_from_source=true + main() { cd "$(dirname "${0}")/../.." source ./ci/lib.sh diff --git a/ci/build/npm-postinstall.sh b/ci/build/npm-postinstall.sh index 05c936815751a8da83980585162d320cadf26748..5e413d7a670a826b8816dca62eea6ea95328a99e 100755 --- a/ci/build/npm-postinstall.sh +++ b/ci/build/npm-postinstall.sh @@ -18,6 +18,9 @@ detect_arch() { } ARCH="${NPM_CONFIG_ARCH:-$(detect_arch)}" +# This is due to an upstream issue with RHEL7/CentOS 7 comptability with node-argon2 +# See: https://github.com/cdr/code-server/pull/3422#pullrequestreview-677765057 +export npm_config_build_from_source=true main() { # Grabs the major version of node from $npm_config_user_agent which looks like diff --git a/docs/FAQ.md b/docs/FAQ.md index 9f86bb3a7062e5e9e3b821b2b99319ff10403022..b7d57a8cfdb18b7682cf62bf32999d13ca2cb822 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -205,17 +205,18 @@ Again, please follow [./guide.md](./guide.md) for our recommendations on setting Yes you can! Set the value of `hashed-password` instead of `password`. Generate the hash with: -``` -printf "thisismypassword" | sha256sum | cut -d' ' -f1 +```shell +echo -n "password" | npx argon2-cli -e +$argon2i$v=19$m=4096,t=3,p=1$wst5qhbgk2lu1ih4dmuxvg$ls1alrvdiwtvzhwnzcm1dugg+5dto3dt1d5v9xtlws4 ``` -Of course replace `thisismypassword` with your actual password. +Of course replace `thisismypassword` with your actual password and **remember to put it inside quotes**! Example: ```yaml auth: password -hashed-password: 1da9133ab9dbd11d2937ec8d312e1e2569857059e73cc72df92e670928983ab5 # You got this from the command above +hashed-password: "$argon2i$v=19$m=4096,t=3,p=1$wST5QhBgk2lu1ih4DMuxvg$LS1alrVdIWtvZHwnzCM1DUGg+5DTO3Dt1d5v9XtLws4" ``` ## How do I securely access web services? diff --git a/package.json b/package.json index d274e00e6ed259a0eb7f720c1edeaf29e99551ed..4ca3ef36ef8505a9162c567ca6c1dd6a74962df4 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ }, "dependencies": { "@coder/logger": "1.1.16", + "argon2": "^0.28.0", "body-parser": "^1.19.0", "compression": "^1.7.4", "cookie-parser": "^1.4.5", diff --git a/src/node/cli.ts b/src/node/cli.ts index 8278dfedbb0bd1138a7ddfc3c94557823a22b735..a2fac4180beb00b7f1f7c735e777d02a74001724 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -114,7 +114,7 @@ const options: Options> = { "hashed-password": { type: "string", description: - "The password hashed with SHA-256 for password authentication (can only be passed in via $HASHED_PASSWORD or the config file). \n" + + "The password hashed with argon2 for password authentication (can only be passed in via $HASHED_PASSWORD or the config file). \n" + "Takes precedence over 'password'.", }, cert: { @@ -240,6 +240,19 @@ export const optionDescriptions = (): string[] => { }) } +export function splitOnFirstEquals(str: string): string[] { + // we use regex instead of "=" to ensure we split at the first + // "=" and return the following substring with it + // important for the hashed-password which looks like this + // $argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY + // 2 means return two items + // Source: https://stackoverflow.com/a/4607799/3015595 + // We use the ? to say the the substr after the = is optional + const split = str.split(/=(.+)?/, 2) + + return split +} + export const parse = ( argv: string[], opts?: { @@ -250,6 +263,7 @@ export const parse = ( if (opts?.configFile) { msg = `error reading ${opts.configFile}: ${msg}` } + return new Error(msg) } @@ -270,7 +284,7 @@ export const parse = ( let key: keyof Args | undefined let value: string | undefined if (arg.startsWith("--")) { - const split = arg.replace(/^--/, "").split("=", 2) + const split = splitOnFirstEquals(arg.replace(/^--/, "")) key = split[0] as keyof Args value = split[1] } else { diff --git a/src/node/http.ts b/src/node/http.ts index eb8c91f942183f01429b11e83c70f1fc6d523102..87a0be1088ee88155fa3c3cd70bffda4a28fc44c 100644 --- a/src/node/http.ts +++ b/src/node/http.ts @@ -2,13 +2,12 @@ import { field, logger } from "@coder/logger" import * as express from "express" import * as expressCore from "express-serve-static-core" import qs from "qs" -import safeCompare from "safe-compare" import { HttpCode, HttpError } from "../common/http" import { normalize, Options } from "../common/util" import { AuthType, DefaultedArgs } from "./cli" import { commit, rootPath } from "./constants" import { Heart } from "./heart" -import { hash } from "./util" +import { getPasswordMethod, IsCookieValidArgs, isCookieValid, sanitizeString } from "./util" declare global { // eslint-disable-next-line @typescript-eslint/no-namespace @@ -45,8 +44,13 @@ export const replaceTemplates = ( /** * Throw an error if not authorized. Call `next` if provided. */ -export const ensureAuthenticated = (req: express.Request, _?: express.Response, next?: express.NextFunction): void => { - if (!authenticated(req)) { +export const ensureAuthenticated = async ( + req: express.Request, + _?: express.Response, + next?: express.NextFunction, +): Promise => { + const isAuthenticated = await authenticated(req) + if (!isAuthenticated) { throw new HttpError("Unauthorized", HttpCode.Unauthorized) } if (next) { @@ -57,20 +61,27 @@ export const ensureAuthenticated = (req: express.Request, _?: express.Response, /** * Return true if authenticated via cookies. */ -export const authenticated = (req: express.Request): boolean => { +export const authenticated = async (req: express.Request): Promise => { switch (req.args.auth) { - case AuthType.None: + case AuthType.None: { return true - case AuthType.Password: + } + case AuthType.Password: { // The password is stored in the cookie after being hashed. - return !!( - req.cookies.key && - (req.args["hashed-password"] - ? safeCompare(req.cookies.key, req.args["hashed-password"]) - : req.args.password && safeCompare(req.cookies.key, hash(req.args.password))) - ) - default: + const hashedPasswordFromArgs = req.args["hashed-password"] + const passwordMethod = getPasswordMethod(hashedPasswordFromArgs) + const isCookieValidArgs: IsCookieValidArgs = { + passwordMethod, + cookieKey: sanitizeString(req.cookies.key), + passwordFromArgs: req.args.password || "", + hashedPasswordFromArgs: req.args["hashed-password"], + } + + return await isCookieValid(isCookieValidArgs) + } + default: { throw new Error(`Unsupported auth type ${req.args.auth}`) + } } } diff --git a/src/node/routes/domainProxy.ts b/src/node/routes/domainProxy.ts index 6b527255ac397b99267c47887944b282c047be66..56b0ea1bb37f47edbfbb720e1b9236b7ce83373c 100644 --- a/src/node/routes/domainProxy.ts +++ b/src/node/routes/domainProxy.ts @@ -32,14 +32,15 @@ const maybeProxy = (req: Request): string | undefined => { return port } -router.all("*", (req, res, next) => { +router.all("*", async (req, res, next) => { const port = maybeProxy(req) if (!port) { return next() } // Must be authenticated to use the proxy. - if (!authenticated(req)) { + const isAuthenticated = await authenticated(req) + if (!isAuthenticated) { // Let the assets through since they're used on the login page. if (req.path.startsWith("/static/") && req.method === "GET") { return next() @@ -73,14 +74,14 @@ router.all("*", (req, res, next) => { export const wsRouter = WsRouter() -wsRouter.ws("*", (req, _, next) => { +wsRouter.ws("*", async (req, _, next) => { const port = maybeProxy(req) if (!port) { return next() } // Must be authenticated to use the proxy. - ensureAuthenticated(req) + await ensureAuthenticated(req) proxy.ws(req, req.ws, req.head, { ignorePath: true, diff --git a/src/node/routes/index.ts b/src/node/routes/index.ts index c183b42b10cb2cc955fe22b00697b43ab276aa84..42edbe117a2472b63b5f9522b60a26040f7ef9e2 100644 --- a/src/node/routes/index.ts +++ b/src/node/routes/index.ts @@ -98,8 +98,8 @@ export const register = async ( app.all("/proxy/(:port)(/*)?", (req, res) => { pathProxy.proxy(req, res) }) - wsApp.get("/proxy/(:port)(/*)?", (req) => { - pathProxy.wsProxy(req as pluginapi.WebsocketRequest) + wsApp.get("/proxy/(:port)(/*)?", async (req) => { + await pathProxy.wsProxy(req as pluginapi.WebsocketRequest) }) // These two routes pass through the path directly. // So the proxied app must be aware it is running @@ -109,8 +109,8 @@ export const register = async ( passthroughPath: true, }) }) - wsApp.get("/absproxy/(:port)(/*)?", (req) => { - pathProxy.wsProxy(req as pluginapi.WebsocketRequest, { + wsApp.get("/absproxy/(:port)(/*)?", async (req) => { + await pathProxy.wsProxy(req as pluginapi.WebsocketRequest, { passthroughPath: true, }) }) diff --git a/src/node/routes/login.ts b/src/node/routes/login.ts index 017b88307f2fdfb541cacd1b50476a42f660085b..dfd07ce959c3e053454274b3f3b31e9efd853e9f 100644 --- a/src/node/routes/login.ts +++ b/src/node/routes/login.ts @@ -2,10 +2,9 @@ import { Router, Request } from "express" import { promises as fs } from "fs" import { RateLimiter as Limiter } from "limiter" import * as path from "path" -import safeCompare from "safe-compare" import { rootPath } from "../constants" import { authenticated, getCookieDomain, redirect, replaceTemplates } from "../http" -import { hash, humanPath } from "../util" +import { getPasswordMethod, handlePasswordValidation, humanPath, sanitizeString } from "../util" export enum Cookie { Key = "key", @@ -49,9 +48,9 @@ const limiter = new RateLimiter() export const router = Router() -router.use((req, res, next) => { +router.use(async (req, res, next) => { const to = (typeof req.query.to === "string" && req.query.to) || "/" - if (authenticated(req)) { + if (await authenticated(req)) { return redirect(req, res, to, { to: undefined }) } next() @@ -62,24 +61,31 @@ router.get("/", async (req, res) => { }) router.post("/", async (req, res) => { + const password = sanitizeString(req.body.password) + const hashedPasswordFromArgs = req.args["hashed-password"] + try { // Check to see if they exceeded their login attempts if (!limiter.canTry()) { throw new Error("Login rate limited!") } - if (!req.body.password) { + if (!password) { throw new Error("Missing password") } - if ( - req.args["hashed-password"] - ? safeCompare(hash(req.body.password), req.args["hashed-password"]) - : req.args.password && safeCompare(req.body.password, req.args.password) - ) { + const passwordMethod = getPasswordMethod(hashedPasswordFromArgs) + const { isPasswordValid, hashedPassword } = await handlePasswordValidation({ + passwordMethod, + hashedPasswordFromArgs, + passwordFromRequestBody: password, + passwordFromArgs: req.args.password, + }) + + if (isPasswordValid) { // The hash does not add any actual security but we do it for // obfuscation purposes (and as a side effect it handles escaping). - res.cookie(Cookie.Key, hash(req.body.password), { + res.cookie(Cookie.Key, hashedPassword, { domain: getCookieDomain(req.headers.host || "", req.args["proxy-domain"]), path: req.body.base || "/", sameSite: "lax", diff --git a/src/node/routes/pathProxy.ts b/src/node/routes/pathProxy.ts index 789fa5c18294dfbbfc8755f17d34921f0d0022d1..e32001743e19c92bb34d14906878ff2f5541da7e 100644 --- a/src/node/routes/pathProxy.ts +++ b/src/node/routes/pathProxy.ts @@ -45,13 +45,13 @@ export function proxy( }) } -export function wsProxy( +export async function wsProxy( req: pluginapi.WebsocketRequest, opts?: { passthroughPath?: boolean }, -): void { - ensureAuthenticated(req) +): Promise { + await ensureAuthenticated(req) _proxy.ws(req, req.ws, req.head, { ignorePath: true, target: getProxyTarget(req, opts?.passthroughPath), diff --git a/src/node/routes/static.ts b/src/node/routes/static.ts index 30eed03167b6bbc9f19f3e1cff8245106dcfd79f..29a1ad3bc7ed0b2f734719c469e9636041ad90af 100644 --- a/src/node/routes/static.ts +++ b/src/node/routes/static.ts @@ -18,7 +18,7 @@ router.get("/(:commit)(/*)?", async (req, res) => { // Used by VS Code to load extensions into the web worker. const tar = getFirstString(req.query.tar) if (tar) { - ensureAuthenticated(req) + await ensureAuthenticated(req) let stream: Readable = tarFs.pack(pathToFsPath(tar)) if (req.headers["accept-encoding"] && req.headers["accept-encoding"].includes("gzip")) { logger.debug("gzipping tar", field("path", tar)) @@ -43,7 +43,8 @@ router.get("/(:commit)(/*)?", async (req, res) => { // Make sure it's in code-server if you aren't authenticated. This lets // unauthenticated users load the login assets. - if (!resourcePath.startsWith(rootPath) && !authenticated(req)) { + const isAuthenticated = await authenticated(req) + if (!resourcePath.startsWith(rootPath) && !isAuthenticated) { throw new HttpError("Unauthorized", HttpCode.Unauthorized) } diff --git a/src/node/routes/vscode.ts b/src/node/routes/vscode.ts index 40c63e92b26900e0b21dc2e2b4b51a1fad5b7216..7e59d52cc7b08ce9b5e2e65d0cf65aa3be15b669 100644 --- a/src/node/routes/vscode.ts +++ b/src/node/routes/vscode.ts @@ -19,7 +19,8 @@ export const router = Router() const vscode = new VscodeProvider() router.get("/", async (req, res) => { - if (!authenticated(req)) { + const isAuthenticated = await authenticated(req) + if (!isAuthenticated) { return redirect(req, res, "login", { // req.baseUrl can be blank if already at the root. to: req.baseUrl && req.baseUrl !== "/" ? req.baseUrl : undefined, diff --git a/src/node/util.ts b/src/node/util.ts index f1882471d9692378696e68b14c6b78a8cf6007e9..1b7bcd2c89ff390c05fceed0cca8698383eaf486 100644 --- a/src/node/util.ts +++ b/src/node/util.ts @@ -1,3 +1,5 @@ +import { logger } from "@coder/logger" +import * as argon2 from "argon2" import * as cp from "child_process" import * as crypto from "crypto" import envPaths from "env-paths" @@ -5,6 +7,7 @@ import { promises as fs } from "fs" import * as net from "net" import * as os from "os" import * as path from "path" +import safeCompare from "safe-compare" import * as util from "util" import xdgBasedir from "xdg-basedir" @@ -115,10 +118,181 @@ export const generatePassword = async (length = 24): Promise => { return buffer.toString("hex").substring(0, length) } -export const hash = (str: string): string => { +/** + * Used to hash the password. + */ +export const hash = async (password: string): Promise => { + try { + return await argon2.hash(password) + } catch (error) { + logger.error(error) + return "" + } +} + +/** + * Used to verify if the password matches the hash + */ +export const isHashMatch = async (password: string, hash: string) => { + if (password === "" || hash === "") { + return false + } + try { + return await argon2.verify(hash, password) + } catch (error) { + logger.error(error) + return false + } +} + +/** + * Used to hash the password using the sha256 + * algorithm. We only use this to for checking + * the hashed-password set in the config. + * + * Kept for legacy reasons. + */ +export const hashLegacy = (str: string): string => { return crypto.createHash("sha256").update(str).digest("hex") } +/** + * Used to check if the password matches the hash using + * the hashLegacy function + */ +export const isHashLegacyMatch = (password: string, hashPassword: string) => { + const hashedWithLegacy = hashLegacy(password) + return safeCompare(hashedWithLegacy, hashPassword) +} + +const passwordMethods = ["SHA256", "ARGON2", "PLAIN_TEXT"] as const +export type PasswordMethod = typeof passwordMethods[number] + +/** + * Used to determine the password method. + * + * There are three options for the return value: + * 1. "SHA256" -> the legacy hashing algorithm + * 2. "ARGON2" -> the newest hashing algorithm + * 3. "PLAIN_TEXT" -> regular ol' password with no hashing + * + * @returns {PasswordMethod} "SHA256" | "ARGON2" | "PLAIN_TEXT" + */ +export function getPasswordMethod(hashedPassword: string | undefined): PasswordMethod { + if (!hashedPassword) { + return "PLAIN_TEXT" + } + + // This is the new hashing algorithm + if (hashedPassword.includes("$argon")) { + return "ARGON2" + } + + // This is the legacy hashing algorithm + return "SHA256" +} + +type PasswordValidation = { + isPasswordValid: boolean + hashedPassword: string +} + +type HandlePasswordValidationArgs = { + /** The PasswordMethod */ + passwordMethod: PasswordMethod + /** The password provided by the user */ + passwordFromRequestBody: string + /** The password set in PASSWORD or config */ + passwordFromArgs: string | undefined + /** The hashed-password set in HASHED_PASSWORD or config */ + hashedPasswordFromArgs: string | undefined +} + +/** + * Checks if a password is valid and also returns the hash + * using the PasswordMethod + */ +export async function handlePasswordValidation({ + passwordMethod, + passwordFromArgs, + passwordFromRequestBody, + hashedPasswordFromArgs, +}: HandlePasswordValidationArgs): Promise { + const passwordValidation = { + isPasswordValid: false, + hashedPassword: "", + } + + switch (passwordMethod) { + case "PLAIN_TEXT": { + const isValid = passwordFromArgs ? safeCompare(passwordFromRequestBody, passwordFromArgs) : false + passwordValidation.isPasswordValid = isValid + + const hashedPassword = await hash(passwordFromRequestBody) + passwordValidation.hashedPassword = hashedPassword + break + } + case "SHA256": { + const isValid = isHashLegacyMatch(passwordFromRequestBody, hashedPasswordFromArgs || "") + passwordValidation.isPasswordValid = isValid + + passwordValidation.hashedPassword = hashedPasswordFromArgs || (await hashLegacy(passwordFromRequestBody)) + break + } + case "ARGON2": { + const isValid = await isHashMatch(passwordFromRequestBody, hashedPasswordFromArgs || "") + passwordValidation.isPasswordValid = isValid + + passwordValidation.hashedPassword = hashedPasswordFromArgs || "" + break + } + default: + break + } + + return passwordValidation +} + +export type IsCookieValidArgs = { + passwordMethod: PasswordMethod + cookieKey: string + hashedPasswordFromArgs: string | undefined + passwordFromArgs: string | undefined +} + +/** Checks if a req.cookies.key is valid using the PasswordMethod */ +export async function isCookieValid({ + passwordFromArgs = "", + cookieKey, + hashedPasswordFromArgs = "", + passwordMethod, +}: IsCookieValidArgs): Promise { + let isValid = false + switch (passwordMethod) { + case "PLAIN_TEXT": + isValid = await isHashMatch(passwordFromArgs, cookieKey) + break + case "ARGON2": + case "SHA256": + isValid = safeCompare(cookieKey, hashedPasswordFromArgs) + break + default: + break + } + return isValid +} + +/** Ensures that the input is sanitized by checking + * - it's a string + * - greater than 0 characters + * - trims whitespace + */ +export function sanitizeString(str: string): string { + // Very basic sanitization of string + // Credit: https://stackoverflow.com/a/46719000/3015595 + return typeof str === "string" && str.trim().length > 0 ? str.trim() : "" +} + const mimeTypes: { [key: string]: string } = { ".aac": "audio/x-aac", ".avi": "video/x-msvideo", diff --git a/test/config.ts b/test/config.ts index 53b592b71bf140545277ddd9829e8639711b9c0e..d8e11db5e957c0ca33d8bb542de97d0fd276e55e 100644 --- a/test/config.ts +++ b/test/config.ts @@ -8,21 +8,21 @@ import { Config, globalSetup, } from "@playwright/test" -import * as crypto from "crypto" +import * as argon2 from "argon2" import path from "path" import { PASSWORD } from "./utils/constants" import * as wtfnode from "./utils/wtfnode" // Playwright doesn't like that ../src/node/util has an enum in it // so I had to copy hash in separately -const hash = (str: string): string => { - return crypto.createHash("sha256").update(str).digest("hex") +const hash = async (str: string): Promise => { + return await argon2.hash(str) } const cookieToStore = { sameSite: "Lax" as const, name: "key", - value: hash(PASSWORD), + value: "", domain: "localhost", path: "/", expires: -1, @@ -38,6 +38,8 @@ globalSetup(async () => { wtfnode.setup() } + cookieToStore.value = await hash(PASSWORD) + const storage = { cookies: [cookieToStore], } diff --git a/test/package.json b/test/package.json index 2c389c19a24362a1a38579cd9aa0481490a18fe5..842cf4175a658843c660ba31396e0472fcba3abf 100644 --- a/test/package.json +++ b/test/package.json @@ -7,6 +7,7 @@ "@types/jsdom": "^16.2.6", "@types/node-fetch": "^2.5.8", "@types/supertest": "^2.0.10", + "argon2": "^0.28.0", "jest": "^26.6.3", "jsdom": "^16.4.0", "node-fetch": "^2.6.1", diff --git a/test/unit/cli.test.ts b/test/unit/cli.test.ts index 6d4fcafb1d0d51679fc9ee982df23025d2690801..2d6e252adc01cc7f73034cbc9521c7976818e595 100644 --- a/test/unit/cli.test.ts +++ b/test/unit/cli.test.ts @@ -3,7 +3,7 @@ import { promises as fs } from "fs" import * as net from "net" import * as os from "os" import * as path from "path" -import { Args, parse, setDefaults, shouldOpenInExistingInstance } from "../../src/node/cli" +import { Args, parse, setDefaults, shouldOpenInExistingInstance, splitOnFirstEquals } from "../../src/node/cli" import { tmpdir } from "../../src/node/constants" import { paths } from "../../src/node/util" @@ -306,7 +306,8 @@ describe("parser", () => { }) it("should use env var hashed password", async () => { - process.env.HASHED_PASSWORD = "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08" // test + process.env.HASHED_PASSWORD = + "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY" // test const args = parse([]) expect(args).toEqual({ _: [], @@ -316,7 +317,8 @@ describe("parser", () => { expect(defaultArgs).toEqual({ ...defaults, _: [], - "hashed-password": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", + "hashed-password": + "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY", usingEnvHashedPassword: true, }) }) @@ -335,6 +337,33 @@ describe("parser", () => { "proxy-domain": ["coder.com", "coder.org"], }) }) + it("should allow '=,$/' in strings", async () => { + const args = parse([ + "--enable-proposed-api", + "$argon2i$v=19$m=4096,t=3,p=1$0qr/o+0t00hsbjfqcksfdq$ofcm4rl6o+b7oxpua4qlxubypbbpsf+8l531u7p9hyy", + ]) + expect(args).toEqual({ + _: [], + "enable-proposed-api": [ + "$argon2i$v=19$m=4096,t=3,p=1$0qr/o+0t00hsbjfqcksfdq$ofcm4rl6o+b7oxpua4qlxubypbbpsf+8l531u7p9hyy", + ], + }) + }) + it("should parse options with double-dash and multiple equal signs ", async () => { + const args = parse( + [ + "--hashed-password=$argon2i$v=19$m=4096,t=3,p=1$0qr/o+0t00hsbjfqcksfdq$ofcm4rl6o+b7oxpua4qlxubypbbpsf+8l531u7p9hyy", + ], + { + configFile: "/pathtoconfig", + }, + ) + expect(args).toEqual({ + _: [], + "hashed-password": + "$argon2i$v=19$m=4096,t=3,p=1$0qr/o+0t00hsbjfqcksfdq$ofcm4rl6o+b7oxpua4qlxubypbbpsf+8l531u7p9hyy", + }) + }) }) describe("cli", () => { @@ -409,3 +438,28 @@ describe("cli", () => { expect(await shouldOpenInExistingInstance(args)).toStrictEqual(undefined) }) }) + +describe("splitOnFirstEquals", () => { + it("should split on the first equals", () => { + const testStr = "enabled-proposed-api=test=value" + const actual = splitOnFirstEquals(testStr) + const expected = ["enabled-proposed-api", "test=value"] + expect(actual).toEqual(expect.arrayContaining(expected)) + }) + it("should split on first equals regardless of multiple equals signs", () => { + const testStr = + "hashed-password=$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY" + const actual = splitOnFirstEquals(testStr) + const expected = [ + "hashed-password", + "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY", + ] + expect(actual).toEqual(expect.arrayContaining(expected)) + }) + it("should always return the first element before an equals", () => { + const testStr = "auth=" + const actual = splitOnFirstEquals(testStr) + const expected = ["auth"] + expect(actual).toEqual(expect.arrayContaining(expected)) + }) +}) diff --git a/test/unit/node/util.test.ts b/test/unit/node/util.test.ts index bb1884b9cc84097ffa83eaf7cc7350c0cd6c3f58..a067917764006866314f4a73611f34e87a6a9f5a 100644 --- a/test/unit/node/util.test.ts +++ b/test/unit/node/util.test.ts @@ -1,3 +1,15 @@ +import { + hash, + isHashMatch, + handlePasswordValidation, + PasswordMethod, + getPasswordMethod, + hashLegacy, + isHashLegacyMatch, + isCookieValid, + sanitizeString, +} from "../../../src/node/util" + describe("getEnvPaths", () => { describe("on darwin", () => { let ORIGINAL_PLATFORM = "" @@ -145,3 +157,253 @@ describe("getEnvPaths", () => { }) }) }) + +describe("hash", () => { + it("should return a hash of the string passed in", async () => { + const plainTextPassword = "mySecretPassword123" + const hashed = await hash(plainTextPassword) + expect(hashed).not.toBe(plainTextPassword) + }) +}) + +describe("isHashMatch", () => { + it("should return true if the password matches the hash", async () => { + const password = "codeserver1234" + const _hash = await hash(password) + const actual = await isHashMatch(password, _hash) + expect(actual).toBe(true) + }) + it("should return false if the password does not match the hash", async () => { + const password = "password123" + const _hash = await hash(password) + const actual = await isHashMatch("otherPassword123", _hash) + expect(actual).toBe(false) + }) + it("should return true with actual hash", async () => { + const password = "password123" + const _hash = "$argon2i$v=19$m=4096,t=3,p=1$EAoczTxVki21JDfIZpTUxg$rkXgyrW4RDGoDYrxBFD4H2DlSMEhP4h+Api1hXnGnFY" + const actual = await isHashMatch(password, _hash) + expect(actual).toBe(true) + }) + it("should return false if the password is empty", async () => { + const password = "" + const _hash = "$argon2i$v=19$m=4096,t=3,p=1$EAoczTxVki21JDfIZpTUxg$rkXgyrW4RDGoDYrxBFD4H2DlSMEhP4h+Api1hXnGnFY" + const actual = await isHashMatch(password, _hash) + expect(actual).toBe(false) + }) + it("should return false if the hash is empty", async () => { + const password = "hellowpasssword" + const _hash = "" + const actual = await isHashMatch(password, _hash) + expect(actual).toBe(false) + }) +}) + +describe("hashLegacy", () => { + it("should return a hash of the string passed in", () => { + const plainTextPassword = "mySecretPassword123" + const hashed = hashLegacy(plainTextPassword) + expect(hashed).not.toBe(plainTextPassword) + }) +}) + +describe("isHashLegacyMatch", () => { + it("should return true if is match", () => { + const password = "password123" + const _hash = hashLegacy(password) + expect(isHashLegacyMatch(password, _hash)).toBe(true) + }) + it("should return false if is match", () => { + const password = "password123" + const _hash = hashLegacy(password) + expect(isHashLegacyMatch("otherPassword123", _hash)).toBe(false) + }) + it("should return true if hashed from command line", () => { + const password = "password123" + // Hashed using printf "password123" | sha256sum | cut -d' ' -f1 + const _hash = "ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f" + expect(isHashLegacyMatch(password, _hash)).toBe(true) + }) +}) + +describe("getPasswordMethod", () => { + it("should return PLAIN_TEXT for no hashed password", () => { + const hashedPassword = undefined + const passwordMethod = getPasswordMethod(hashedPassword) + const expected: PasswordMethod = "PLAIN_TEXT" + expect(passwordMethod).toEqual(expected) + }) + it("should return ARGON2 for password with 'argon2'", () => { + const hashedPassword = + "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY" + const passwordMethod = getPasswordMethod(hashedPassword) + const expected: PasswordMethod = "ARGON2" + expect(passwordMethod).toEqual(expected) + }) + it("should return SHA256 for password with legacy hash", () => { + const hashedPassword = "936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb944412bb6f8f8f07af" + const passwordMethod = getPasswordMethod(hashedPassword) + const expected: PasswordMethod = "SHA256" + expect(passwordMethod).toEqual(expected) + }) +}) + +describe("handlePasswordValidation", () => { + it("should return true with a hashedPassword for a PLAIN_TEXT password", async () => { + const p = "password" + const passwordValidation = await handlePasswordValidation({ + passwordMethod: "PLAIN_TEXT", + passwordFromRequestBody: p, + passwordFromArgs: p, + hashedPasswordFromArgs: undefined, + }) + + const matchesHash = await isHashMatch(p, passwordValidation.hashedPassword) + + expect(passwordValidation.isPasswordValid).toBe(true) + expect(matchesHash).toBe(true) + }) + it("should return false when PLAIN_TEXT password doesn't match args", async () => { + const p = "password" + const passwordValidation = await handlePasswordValidation({ + passwordMethod: "PLAIN_TEXT", + passwordFromRequestBody: "password1", + passwordFromArgs: p, + hashedPasswordFromArgs: undefined, + }) + + const matchesHash = await isHashMatch(p, passwordValidation.hashedPassword) + + expect(passwordValidation.isPasswordValid).toBe(false) + expect(matchesHash).toBe(false) + }) + it("should return true with a hashedPassword for a SHA256 password", async () => { + const p = "helloworld" + const passwordValidation = await handlePasswordValidation({ + passwordMethod: "SHA256", + passwordFromRequestBody: p, + passwordFromArgs: undefined, + hashedPasswordFromArgs: "936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb944412bb6f8f8f07af", + }) + + const matchesHash = isHashLegacyMatch(p, passwordValidation.hashedPassword) + + expect(passwordValidation.isPasswordValid).toBe(true) + expect(matchesHash).toBe(true) + }) + it("should return false when SHA256 password doesn't match hash", async () => { + const p = "helloworld1" + const passwordValidation = await handlePasswordValidation({ + passwordMethod: "SHA256", + passwordFromRequestBody: p, + passwordFromArgs: undefined, + hashedPasswordFromArgs: "936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb944412bb6f8f8f07af", + }) + + const matchesHash = isHashLegacyMatch(p, passwordValidation.hashedPassword) + + expect(passwordValidation.isPasswordValid).toBe(false) + expect(matchesHash).toBe(false) + }) + it("should return true with a hashedPassword for a ARGON2 password", async () => { + const p = "password" + const passwordValidation = await handlePasswordValidation({ + passwordMethod: "ARGON2", + passwordFromRequestBody: p, + passwordFromArgs: undefined, + hashedPasswordFromArgs: + "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY", + }) + + const matchesHash = await isHashMatch(p, passwordValidation.hashedPassword) + + expect(passwordValidation.isPasswordValid).toBe(true) + expect(matchesHash).toBe(true) + }) + it("should return false when ARGON2 password doesn't match hash", async () => { + const p = "password1" + const passwordValidation = await handlePasswordValidation({ + passwordMethod: "ARGON2", + passwordFromRequestBody: p, + passwordFromArgs: undefined, + hashedPasswordFromArgs: + "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY", + }) + + const matchesHash = await isHashMatch(p, passwordValidation.hashedPassword) + + expect(passwordValidation.isPasswordValid).toBe(false) + expect(matchesHash).toBe(false) + }) +}) + +describe("isCookieValid", () => { + it("should be valid if hashed-password for SHA256 matches cookie.key", async () => { + const isValid = await isCookieValid({ + passwordMethod: "SHA256", + cookieKey: "936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb944412bb6f8f8f07af", + hashedPasswordFromArgs: "936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb944412bb6f8f8f07af", + passwordFromArgs: undefined, + }) + expect(isValid).toBe(true) + }) + it("should be invalid if hashed-password for SHA256 does not match cookie.key", async () => { + const isValid = await isCookieValid({ + passwordMethod: "SHA256", + cookieKey: "936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb9442bb6f8f8f07af", + hashedPasswordFromArgs: "936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb944412bb6f8f8f07af", + passwordFromArgs: undefined, + }) + expect(isValid).toBe(false) + }) + it("should be valid if hashed-password for ARGON2 matches cookie.key", async () => { + const isValid = await isCookieValid({ + passwordMethod: "ARGON2", + cookieKey: "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY", + hashedPasswordFromArgs: + "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY", + passwordFromArgs: undefined, + }) + expect(isValid).toBe(true) + }) + it("should be invalid if hashed-password for ARGON2 does not match cookie.key", async () => { + const isValid = await isCookieValid({ + passwordMethod: "ARGON2", + cookieKey: "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9H", + hashedPasswordFromArgs: + "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY", + passwordFromArgs: undefined, + }) + expect(isValid).toBe(false) + }) + it("should be valid if password for PLAIN_TEXT matches cookie.key", async () => { + const isValid = await isCookieValid({ + passwordMethod: "PLAIN_TEXT", + cookieKey: "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9HYY", + passwordFromArgs: "password", + hashedPasswordFromArgs: undefined, + }) + expect(isValid).toBe(true) + }) + it("should be invalid if hashed-password for PLAIN_TEXT does not match cookie.key", async () => { + const isValid = await isCookieValid({ + passwordMethod: "PLAIN_TEXT", + cookieKey: "$argon2i$v=19$m=4096,t=3,p=1$0qR/o+0t00hsbJFQCKSfdQ$oFcM4rL6o+B7oxpuA4qlXubypbBPsf+8L531U7P9H", + passwordFromArgs: "password1234", + hashedPasswordFromArgs: undefined, + }) + expect(isValid).toBe(false) + }) +}) + +describe("sanitizeString", () => { + it("should return an empty string if passed a type other than a string", () => { + expect(sanitizeString({} as string)).toBe("") + }) + it("should trim whitespace", () => { + expect(sanitizeString(" hello ")).toBe("hello") + }) + it("should always return an empty string", () => { + expect(sanitizeString(" ")).toBe("") + }) +}) diff --git a/test/yarn.lock b/test/yarn.lock index d02c5a768aa13d5c97d4f0b48f63d6ab6756a8ba..45b934614232c942fbc7b676dfa74f9cccb16824 100644 --- a/test/yarn.lock +++ b/test/yarn.lock @@ -1258,6 +1258,26 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@mapbox/node-pre-gyp@^1.0.1": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz#2a0b32fcb416fb3f2250fd24cb2a81421a4f5950" + integrity sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA== + dependencies: + detect-libc "^1.0.3" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.1" + nopt "^5.0.0" + npmlog "^4.1.2" + rimraf "^3.0.2" + semver "^7.3.4" + tar "^6.1.0" + +"@phc/format@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@phc/format/-/format-1.0.0.tgz#b5627003b3216dc4362125b13f48a4daa76680e4" + integrity sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ== + "@playwright/test@^0.1101.0-alpha2": version "0.1101.0-alpha2" resolved "https://registry.yarnpkg.com/@playwright/test/-/test-0.1101.0-alpha2.tgz#a1121ac70e6ac986532ea63c6f565f06fae99364" @@ -1439,6 +1459,11 @@ abab@^2.0.3: resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + acorn-globals@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" @@ -1481,6 +1506,16 @@ ansi-escapes@^4.2.1: dependencies: type-fest "^0.11.0" +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + ansi-regex@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" @@ -1516,6 +1551,29 @@ anymatch@^3.0.3: normalize-path "^3.0.0" picomatch "^2.0.4" +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +argon2@^0.28.0: + version "0.28.0" + resolved "https://registry.yarnpkg.com/argon2/-/argon2-0.28.0.tgz#b32ea7526457f5593dda51c5a8e542bde3ba2621" + integrity sha512-bFgoLJbfNVbceXO9h8wKytoqBWyDsROp0HVlhtpyXCwq/izvMJ7CKZdDQp8y0T4yKAQHKD1vuW0C1INdjYdRsw== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.1" + "@phc/format" "^1.0.0" + node-addon-api "^3.0.2" + opencollective-postinstall "^2.0.3" + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -1845,6 +1903,11 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + ci-info@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" @@ -1879,6 +1942,11 @@ co@^4.6.0: resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + collect-v8-coverage@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" @@ -1948,6 +2016,11 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" @@ -1973,7 +2046,7 @@ core-js-compat@^3.9.0, core-js-compat@^3.9.1: browserslist "^4.16.3" semver "7.0.0" -core-util-is@1.0.2: +core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= @@ -2104,6 +2177,16 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -2442,6 +2525,13 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -2476,6 +2566,20 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -2586,6 +2690,11 @@ has-symbols@^1.0.1: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" @@ -2691,7 +2800,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3, inherits@~2.0.0: +inherits@2, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -2788,6 +2897,18 @@ is-extendable@^1.0.1: dependencies: is-plain-object "^2.0.4" +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -2849,7 +2970,7 @@ is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" -isarray@1.0.0: +isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= @@ -3473,7 +3594,7 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -make-dir@^3.0.0: +make-dir@^3.0.0, make-dir@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -3575,6 +3696,21 @@ minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minipass@^3.0.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" + integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== + dependencies: + yallist "^4.0.0" + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + mixin-deep@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" @@ -3583,7 +3719,7 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@1.x: +mkdirp@1.x, mkdirp@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -3637,6 +3773,11 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +node-addon-api@^3.0.2: + version "3.2.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" + integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== + node-fetch@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" @@ -3669,6 +3810,13 @@ node-releases@^1.1.71: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe" integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw== +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -3705,6 +3853,21 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" +npmlog@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + nwsapi@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" @@ -3715,6 +3878,11 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== +object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + object-copy@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" @@ -3767,6 +3935,11 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" +opencollective-postinstall@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" + integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== + optionator@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" @@ -3938,6 +4111,11 @@ pretty-format@^26.0.0, pretty-format@^26.4.2, pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + progress@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" @@ -4017,6 +4195,19 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" +readable-stream@^2.0.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" @@ -4216,7 +4407,7 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.1: +safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== @@ -4277,7 +4468,14 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -set-blocking@^2.0.0: +semver@^7.3.4: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + +set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= @@ -4486,6 +4684,23 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + string-width@^4.1.0, string-width@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" @@ -4502,6 +4717,27 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + strip-ansi@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" @@ -4576,6 +4812,18 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +tar@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83" + integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + terminal-link@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" @@ -4785,7 +5033,7 @@ use@^3.1.0: resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== -util-deprecate@^1.0.1: +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= @@ -4897,6 +5145,13 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" diff --git a/typings/pluginapi.d.ts b/typings/pluginapi.d.ts index f7797b6d8e1aef312dbc481fc1c7e3d02792c81d..17f3ae1975cb5d9ff76f4ca55c3813576f1456f7 100644 --- a/typings/pluginapi.d.ts +++ b/typings/pluginapi.d.ts @@ -145,12 +145,16 @@ export const proxy: ProxyServer /** * Middleware to ensure the user is authenticated. Throws if they are not. */ -export function ensureAuthenticated(req: express.Request, res?: express.Response, next?: express.NextFunction): void +export function ensureAuthenticated( + req: express.Request, + res?: express.Response, + next?: express.NextFunction, +): Promise /** * Returns true if the user is authenticated. */ -export function authenticated(req: express.Request): boolean +export function authenticated(req: express.Request): Promise /** * Replace variables in HTML: TO, BASE, CS_STATIC_BASE, and OPTIONS. diff --git a/yarn.lock b/yarn.lock index 991ebe347876f2c05fe226fc8ebcc50a5d2ce48f..677b8fab02ef54fd8c1cfee264776163b99ab88c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -896,6 +896,21 @@ resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== +"@mapbox/node-pre-gyp@^1.0.1": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz#2a0b32fcb416fb3f2250fd24cb2a81421a4f5950" + integrity sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA== + dependencies: + detect-libc "^1.0.3" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.1" + nopt "^5.0.0" + npmlog "^4.1.2" + rimraf "^3.0.2" + semver "^7.3.4" + tar "^6.1.0" + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -971,6 +986,11 @@ "@parcel/utils" "^1.11.0" physical-cpu-count "^2.0.0" +"@phc/format@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@phc/format/-/format-1.0.0.tgz#b5627003b3216dc4362125b13f48a4daa76680e4" + integrity sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ== + "@schemastore/package@^0.0.6": version "0.0.6" resolved "https://registry.yarnpkg.com/@schemastore/package/-/package-0.0.6.tgz#9a76713da1c7551293b7e72e4f387f802bfd5d81" @@ -1317,6 +1337,11 @@ abab@^2.0.0: resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + accepts@~1.3.5, accepts@~1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" @@ -1397,6 +1422,11 @@ ansi-colors@^4.1.1: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + ansi-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" @@ -1436,11 +1466,34 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== +argon2@^0.28.0: + version "0.28.0" + resolved "https://registry.yarnpkg.com/argon2/-/argon2-0.28.0.tgz#b32ea7526457f5593dda51c5a8e542bde3ba2621" + integrity sha512-bFgoLJbfNVbceXO9h8wKytoqBWyDsROp0HVlhtpyXCwq/izvMJ7CKZdDQp8y0T4yKAQHKD1vuW0C1INdjYdRsw== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.1" + "@phc/format" "^1.0.0" + node-addon-api "^3.0.2" + opencollective-postinstall "^2.0.3" + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -2110,6 +2163,11 @@ chownr@^1.1.1: resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" @@ -2175,6 +2233,11 @@ coa@^2.0.2: chalk "^2.4.1" q "^1.1.2" +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + codecov@^3.8.1: version "3.8.2" resolved "https://registry.yarnpkg.com/codecov/-/codecov-3.8.2.tgz#ab24f18783998c39e809ea210af899f8dbcc790e" @@ -2316,6 +2379,11 @@ console-browserify@^1.1.0: resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + constants-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" @@ -2848,6 +2916,11 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -2866,6 +2939,11 @@ destroy@~1.0.4: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -3730,6 +3808,13 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -3761,6 +3846,20 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -3949,6 +4048,11 @@ has-symbols@^1.0.1, has-symbols@^1.0.2: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" @@ -4438,6 +4542,18 @@ is-extglob@^2.1.0, is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -4928,6 +5044,13 @@ magic-string@^0.22.4: dependencies: vlq "^0.2.2" +make-dir@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" @@ -5172,6 +5295,21 @@ minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@~1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minipass@^3.0.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" + integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== + dependencies: + yallist "^4.0.0" + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + mixin-deep@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" @@ -5192,6 +5330,11 @@ mkdirp@^0.5.1, mkdirp@~0.5.1: dependencies: minimist "^1.2.5" +mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -5269,6 +5412,11 @@ node-addon-api@^1.7.1: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.2.tgz#3df30b95720b53c24e59948b49532b662444f54d" integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg== +node-addon-api@^3.0.2: + version "3.2.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" + integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== + node-fetch@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" @@ -5313,6 +5461,13 @@ node-releases@^1.1.71: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + normalize-package-data@^2.3.2, normalize-package-data@^2.5.0, normalize-package-data@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.1.tgz#98dc56dfe6755d99b1c53f046e1e3d2dde55a1c7" @@ -5355,6 +5510,16 @@ normalize-url@^4.5.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== +npmlog@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + nth-check@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" @@ -5374,6 +5539,11 @@ num2fraction@^1.2.2: resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + nwsapi@^2.1.3: version "2.2.0" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" @@ -5384,7 +5554,7 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4.1.1: +object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -5482,6 +5652,11 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" +opencollective-postinstall@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" + integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== + opn@^5.1.0: version "5.5.0" resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" @@ -6760,7 +6935,7 @@ readable-stream@1.1.x: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.3, readable-stream@~2.3.6: +readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.3, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -7158,12 +7333,12 @@ semver@^5.4.1, semver@^5.5.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.0.0, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.5: +semver@^7.0.0, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== @@ -7204,7 +7379,7 @@ serve-static@1.14.1, serve-static@^1.12.4: parseurl "~1.3.3" send "0.17.1" -set-blocking@^2.0.0: +set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= @@ -7276,7 +7451,7 @@ shellcheck@^1.0.0: resolved "https://registry.yarnpkg.com/shellcheck/-/shellcheck-1.0.0.tgz#263479d92c3708d63d98883f896481461cf17cd0" integrity sha512-CdKbWXOknBwE1wNQzAnwfLf7QNOu/yqyLSGBKoq2WuChEqfg7dnZJ1pHR2P463PbVpBRz3KGkYnXJCoQrPwtYA== -signal-exit@^3.0.2: +signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== @@ -7559,6 +7734,23 @@ stream-http@^2.7.2: to-arraybuffer "^1.0.0" xtend "^4.0.0" +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" @@ -7603,6 +7795,13 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + strip-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" @@ -7823,6 +8022,18 @@ tar-stream@^2.1.4, tar-stream@^2.2.0: inherits "^2.0.3" readable-stream "^3.1.1" +tar@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83" + integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + teeny-request@7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-7.0.1.tgz#bdd41fdffea5f8fbc0d29392cb47bec4f66b2b4c" @@ -8527,6 +8738,13 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"