Merge pull request #3422 from cdr/jsjoeio/fix-password-hash

fix: use sufficient computational effort for password hash
This commit is contained in:
Joe Previte 2021-06-09 14:32:05 -07:00 committed by GitHub
commit 717eaa6470
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1083 additions and 69 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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?

View File

@ -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",

View File

@ -114,7 +114,7 @@ const options: Options<Required<Args>> = {
"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 {

View File

@ -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 = <T extends object>(
/**
* 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<void> => {
const isAuthenticated = await authenticated(req)
if (!isAuthenticated) {
throw new HttpError("Unauthorized", HttpCode.Unauthorized)
}
if (next) {
@ -57,22 +61,29 @@ 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<boolean> => {
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}`)
}
}
}
/**
* Get the relative path that will get us to the root of the page. For each

View File

@ -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,

View File

@ -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,
})
})

View File

@ -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",

View File

@ -45,13 +45,13 @@ export function proxy(
})
}
export function wsProxy(
export async function wsProxy(
req: pluginapi.WebsocketRequest,
opts?: {
passthroughPath?: boolean
},
): void {
ensureAuthenticated(req)
): Promise<void> {
await ensureAuthenticated(req)
_proxy.ws(req, req.ws, req.head, {
ignorePath: true,
target: getProxyTarget(req, opts?.passthroughPath),

View File

@ -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)
}

View File

@ -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,

View File

@ -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<string> => {
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<string> => {
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<PasswordValidation> {
const passwordValidation = <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<boolean> {
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",

View File

@ -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<string> => {
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],
}

View File

@ -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",

View File

@ -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))
})
})

View File

@ -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("")
})
})

View File

@ -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"

View File

@ -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<void>
/**
* Returns true if the user is authenticated.
*/
export function authenticated(req: express.Request): boolean
export function authenticated(req: express.Request): Promise<void>
/**
* Replace variables in HTML: TO, BASE, CS_STATIC_BASE, and OPTIONS.

230
yarn.lock
View File

@ -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"