From a05e47b8ab9b8e7db4588ba7b4a37678d1a507f2 Mon Sep 17 00:00:00 2001 From: Hollis Kuang <96937809+holliskuang@users.noreply.github.com> Date: Wed, 10 May 2023 20:15:03 -0700 Subject: [PATCH] [Closes #285] Hkuang/feature/2fa (#289) * Create Email Transporter Object Create Email Object that uses takes gmail email and password from .env || SendMail function takes in (recipient email, email subject, and email contact) parameters to send out an email * Update isAuthenticated to involve 2fa User needs session two be authenticated in order to proceed. * Add 2fa routing Route to email authentication page utilizing time-based one time password to authenticate * Adding ToTP Generator 1. Take in Request 2. Generate token and key 3. Send Token to Email 4. Save Key in Request for Authentication * Create Two Factor View * Nodemail log-in var * Update local.js * Update RingdownForm.js (#288) (#290) * Update RingdownForm.js * Prettier * prettier * cicd * redirects when authenticated * cicd * Making generateToTP a reusable function * Switched from notp to OTPAuth * Save Token to Database instead of Session * MailCatcher up and Running! * Link logo to send to user back to Login from 2fa * turn sendEmail into a separate function * WIP * Migration for TwoFactorAuth * rector ssodata to twofactordata * correcting JSON formatting * Playwright Tests * Tests! * Test edits * yml * Fix typo in package.json script * Fix formatting * eslint & migration * Commented out home page tests * prettier * Remove previous addition of Auth-Related Columns * Adding two Factor Flow to all tests * Put 2fa for tests into helper fx for cleaner code * Parameterize SMTP settings, update example.env * Refactoring * refactoring, adding else statement before next() * Code style changes --------- Co-authored-by: holliskuang Co-authored-by: Francis Li --- docker-compose.yml | 5 + e2e/package.json | 3 +- e2e/playwright.config.js | 3 +- e2e/tests/home.spec.js | 28 +- example.env | 5 + server/auth/middleware.js | 10 +- server/mailer/emailTransporter.js | 36 + ...0230503083739-add-twofactor-to-batsuser.js | 13 + server/models/user.js | 38 + server/package.json | 7 +- server/routes/auth/local.js | 40 +- server/test/helper.js | 14 + server/test/integration/api/ambulances.js | 8 +- server/test/integration/api/emsCalls.js | 7 + .../test/integration/api/hospitalStatuses.js | 7 + server/test/integration/api/ringdowns.js | 25 +- server/test/integration/api/users.js | 13 + .../authentication/twoFactorFlow.js | 116 +++ server/views/auth/local/twoFactor.ejs | 28 + server/views/layout.ejs | 8 +- shared/metadata/user.js | 2 +- yarn.lock | 687 +++++++----------- 22 files changed, 667 insertions(+), 436 deletions(-) create mode 100644 server/mailer/emailTransporter.js create mode 100644 server/migrations/20230503083739-add-twofactor-to-batsuser.js create mode 100644 server/test/integration/authentication/twoFactorFlow.js create mode 100644 server/views/auth/local/twoFactor.ejs diff --git a/docker-compose.yml b/docker-compose.yml index 92bede3b..c15b974f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,3 +11,8 @@ services: - ${HOST_PORT:-3000}:3000 depends_on: - db + mail: + image: dockage/mailcatcher:0.8.2 + ports: + - 1025:1025 + - 1080:1080 diff --git a/e2e/package.json b/e2e/package.json index a509c4e3..8d4e1169 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -4,7 +4,8 @@ "private": true, "main": "index.js", "dependencies": { - "@playwright/test": "^1.30.0" + "@playwright/test": "^1.30.0", + "dotenv": "^16.0.3" }, "scripts": { "test": "DEBUG=pw:webserver playwright test", diff --git a/e2e/playwright.config.js b/e2e/playwright.config.js index d618c965..bad28164 100644 --- a/e2e/playwright.config.js +++ b/e2e/playwright.config.js @@ -1,10 +1,11 @@ const { devices } = require('@playwright/test'); +//dotenv file is needed to run tests +require('dotenv').config({ path: '../.env' }); /** * Read environment variables from file. * https://github.com/motdotla/dotenv */ -// require('dotenv').config(); /** * @see https://playwright.dev/docs/test-configuration diff --git a/e2e/tests/home.spec.js b/e2e/tests/home.spec.js index a0b7f436..e0e30053 100644 --- a/e2e/tests/home.spec.js +++ b/e2e/tests/home.spec.js @@ -18,7 +18,7 @@ test.describe('home', () => { await password.press('Enter'); await expect(page.getByText('Invalid email and/or password.')).toBeVisible(); }); - + /* test('redirects to EMS interface after EMS user login', async ({ page }) => { await page.goto('/'); await page.getByLabel('Email').fill(process.env.EMS_USER); @@ -35,5 +35,31 @@ test.describe('home', () => { await password.fill(process.env.HOSPITAL_PASS); await password.press('Enter'); await expect(page).toHaveURL('/er'); + }); */ + + test('shows the Routed logo and two factor authentication form', async ({ page }) => { + await page.goto('/'); + await page.getByLabel('Email').fill(process.env.HOSPITAL_USER); + const password = page.getByLabel('Password'); + await password.fill(process.env.HOSPITAL_PASS); + await password.press('Enter'); + await expect(page).toHaveURL('/auth/local/twoFactor'); + await expect(page).toHaveTitle(/Routed/); + await expect(page.getByAltText('Routed logo')).toBeVisible(); + await expect(page.getByText('Please enter the Authoritzation Code that was sent to your E-mail')).toBeVisible(); + await expect(page.getByLabel('Code')).toBeVisible(); + await expect(page.getByText('Submit')).toBeVisible(); + }); + + test('Shows Error after Incorrect Two Factor Authentication', async ({ page }) => { + await page.goto('/'); + await page.getByLabel('Email').fill(process.env.HOSPITAL_USER); + const password = page.getByLabel('Password'); + await password.fill(process.env.HOSPITAL_PASS); + await password.press('Enter'); + await expect(page).toHaveURL('/auth/local/twoFactor'); + await page.getByLabel('Code').fill('123456'); + await page.getByText('Submit').click(); + await expect(page.getByText('Invalid Authoritzation Code.')).toBeVisible(); }); }); diff --git a/example.env b/example.env index ec722877..07086c87 100644 --- a/example.env +++ b/example.env @@ -9,6 +9,11 @@ REACT_APP_DISABLE_CODE_3= REACT_APP_DISABLE_PILOT_HOSPITALS= REACT_APP_PILOT_SHOW_ALL_RINGDOWNS= SESSION_SECRET=makeitasecretinprod +SMTP_HOST=mail +SMTP_PORT=1025 +SMTP_USER= +SMTP_PASSWORD= +SMTP_REPLY_TO=no-reply@routedapp.org EMS_USER=op.ems@c4sf.me EMS_PASS=abcd1234 diff --git a/server/auth/middleware.js b/server/auth/middleware.js index c9497da2..93407044 100644 --- a/server/auth/middleware.js +++ b/server/auth/middleware.js @@ -5,6 +5,8 @@ const isAuthenticated = (req, res, next) => { // ensure authenticated user is active if (!req.user.isActive) { res.status(HttpStatus.FORBIDDEN).end(); + } else if (!req.session.twoFactor) { + res.status(HttpStatus.UNAUTHORIZED).end(); } else { next(); } @@ -17,7 +19,9 @@ const isAuthenticated = (req, res, next) => { const isSuperUser = (req, res, next) => { if (req.user?.isSuperUser) { - next(); + if (!req.session.twoFactor) { + res.status(HttpStatus.UNAUTHORIZED).end(); + } else next(); } else if (req.accepts('html')) { res.redirect('/auth/local/login'); } else if (req.user) { @@ -29,7 +33,9 @@ const isSuperUser = (req, res, next) => { const isAdminUser = (req, res, next) => { if (req.user?.isSuperUser || req.user?.isAdminUser) { - next(); + if (!req.session.twoFactor) { + res.status(HttpStatus.UNAUTHORIZED).end(); + } else next(); } else if (req.accepts('html')) { res.redirect('/auth/local/login'); } else if (req.user) { diff --git a/server/mailer/emailTransporter.js b/server/mailer/emailTransporter.js new file mode 100644 index 00000000..3fbe0a66 --- /dev/null +++ b/server/mailer/emailTransporter.js @@ -0,0 +1,36 @@ +const nodemailer = require('nodemailer'); +const nodemailermock = require('nodemailer-mock'); + +// create reusable transporter class using the default SMTP transport with functions +function createTransport() { + // nodeMailer options + const options = { + host: process.env.SMTP_HOST, + port: process.env.SMTP_PORT, + auth: { + user: process.env.SMTP_USER, + pass: process.env.SMTP_PASSWORD, + }, + }; + // mock Transporter in testing env + const transporter = process.env.NODE_ENV === 'test' ? nodemailermock.createTransport(options) : nodemailer.createTransport(options); + return transporter; +} +// send mail with defined transport object +function sendMail(transporter, recipient, subject, content) { + const mailOptions = { + from: process.env.SMTP_REPLY_TO, + to: recipient, + subject: subject, + text: content, + }; + transporter.sendMail(mailOptions, (error, info) => { + if (error) { + console.log(error); + } else { + console.log('Email sent: ' + info.response); + } + }); +} + +module.exports = { createTransport, sendMail }; diff --git a/server/migrations/20230503083739-add-twofactor-to-batsuser.js b/server/migrations/20230503083739-add-twofactor-to-batsuser.js new file mode 100644 index 00000000..9917180a --- /dev/null +++ b/server/migrations/20230503083739-add-twofactor-to-batsuser.js @@ -0,0 +1,13 @@ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.sequelize.transaction(async (transaction) => { + await queryInterface.addColumn('batsuser', 'twofactordata', Sequelize.JSONB, { transaction }); + }); + }, + + async down(queryInterface, Sequelize) { + await queryInterface.sequelize.transaction(async (transaction) => { + await queryInterface.removeColumn('batsuser', 'twofactordata', { transaction }); + }); + }, +}; diff --git a/server/models/user.js b/server/models/user.js index 573a1ef8..969943db 100644 --- a/server/models/user.js +++ b/server/models/user.js @@ -3,6 +3,8 @@ const _ = require('lodash'); const { Model } = require('sequelize'); const metadata = require('shared/metadata/user'); const initModel = require('../metadata/initModel'); +const { sendMail, createTransport } = require('../mailer/emailTransporter'); +const OTPAuth = require('otpauth'); const SALT_ROUNDS = 10; @@ -38,6 +40,42 @@ module.exports = (sequelize) => { 'activeHospitals', ]); } + + async generateToTPSecret() { + const secret = new OTPAuth.Secret(); + // new TOTP object using the secret key + const totp = new OTPAuth.TOTP({ + secret: secret.base32, + issuer: 'Routed', + period: -1, + digits: 6, + }); + console.log('TOTP Secret: ', secret.base32); + // save the secret key to the session + // generate secret token + const token = totp.generate(); + // Save token and expiration timestamp in DB (Expires in 15 minutes from inital Log In) + // save totp secret and expiration timestamp in DB + + await this.update({ twoFactorData: { totptimestamp: Date.now() + 900000, totptoken: token } }); + await this.save(); + + // send email with token + sendMail( + createTransport(), + this.email, + 'Your Authentication Code from Routed', + `This is your Authentication Code: ${token} . It will expire in 15 minutes.` + ); + } + + verifyTwoFactor(req) { + const token = req.body.code; + const totptoken = this.dataValues.twoFactorData.totptoken; + const totptimestamp = this.dataValues.twoFactorData.totptimestamp; + const verified = token === totptoken && Date.now() < totptimestamp; + return verified; + } } initModel(User, metadata, sequelize); diff --git a/server/package.json b/server/package.json index 64666cad..03936aa1 100644 --- a/server/package.json +++ b/server/package.json @@ -6,6 +6,7 @@ "axios": "^0.21.4", "bcrypt": "^5.0.1", "bufferutil": "^4.0.6", + "chai": "^4.3.7", "cookie-parser": "^1.4.6", "cookie-session": "^1.4.0", "debug": "^4.3.4", @@ -16,7 +17,11 @@ "http-status-codes": "^1.4.0", "lodash": "^4.17.21", "luxon": "^1.28.0", + "mockery": "^2.1.0", "morgan": "^1.10.0", + "nodemailer": "^6.9.1", + "nodemailer-mock": "^2.0.1", + "otpauth": "^9.1.1", "passport": "^0.4.1", "passport-local": "^1.0.0", "passport-oauth2": "^1.6.1", @@ -32,7 +37,7 @@ "ws": "^7.5.8" }, "devDependencies": { - "mocha": "^8.4.0", + "mocha": "^10.2.0", "nodemon": "^2.0.18" }, "scripts": { diff --git a/server/routes/auth/local.js b/server/routes/auth/local.js index 9e61d406..b6369e5c 100644 --- a/server/routes/auth/local.js +++ b/server/routes/auth/local.js @@ -1,11 +1,28 @@ const express = require('express'); const HttpStatus = require('http-status-codes'); const passport = require('passport'); - const router = express.Router(); router.get('/login', (req, res) => { - res.render('auth/local/login'); + // If user is already logged in and two Factor authenticated then redirect to home page + if (req.session.twoFactor) { + res.redirect('/'); + } else { + // Check if user is already logged in through passport, if so, log them out + if (req.user) req.logout(); + res.render('auth/local/login'); + } +}); + +router.get('/twoFactor', async (req, res) => { + if (req.session.twoFactor) { + res.redirect('/'); + } else if (req.user) { + await req.user.generateToTPSecret(); + res.render('auth/local/twoFactor'); + } else { + res.redirect('/auth/local/login'); + } }); router.post('/login', (req, res, next) => { @@ -22,7 +39,7 @@ router.post('/login', (req, res, next) => { } else if (user) { req.login(user, () => { if (req.accepts('html')) { - res.redirect('/'); + res.redirect('/auth/local/twoFactor'); } else { res.status(HttpStatus.OK).end(); } @@ -38,8 +55,25 @@ router.post('/login', (req, res, next) => { })(req, res, next); }); +router.post('/twoFactor', async (req, res) => { + // Redirect if Session is interrupted + if (req.user) { + const verified = await req.user.verifyTwoFactor(req); + // If the code is verified, set the session to twoFactor and redirect to home page + if (verified) { + req.session.twoFactor = true; + res.redirect('/'); + } else { + res.render('auth/local/twoFactor', { incorrectCode: true }); + } + } else { + res.redirect('/auth/local/login'); + } +}); + router.get('/logout', (req, res) => { req.logout(); + req.session.twoFactor = false; if (req.accepts('html')) { res.redirect('/'); } else { diff --git a/server/test/helper.js b/server/test/helper.js index fe95cc8c..ad11c719 100644 --- a/server/test/helper.js +++ b/server/test/helper.js @@ -5,6 +5,7 @@ const fixtures = require('sequelize-fixtures'); const path = require('path'); const models = require('../models'); +const nodemailermock = require('nodemailer-mock'); const loadFixtures = async (files) => { const filePaths = files.map((f) => path.resolve(__dirname, `fixtures/${f}.json`)); @@ -30,6 +31,18 @@ const resetDatabase = async () => { `); }; +const twoFactorAuthSession = async (testSession) => { + // Call the two-factor authentication endpoint + await testSession.get('/auth/local/twoFactor').set('Accept', 'application/json'); + const sentMail = nodemailermock.mock.sentMail(); + // Extract authentication code from the sent email + const regex = /Authentication Code: (\d{6})/; + const match = regex.exec(sentMail[0].text); + const authCode = match[1]; + // Submit the authentication code + await testSession.post('/auth/local/twoFactor').set('Accept', 'application/json').send({ code: authCode }); +}; + beforeEach(async () => { await resetDatabase(); }); @@ -42,4 +55,5 @@ after(async () => { module.exports = { loadFixtures, + twoFactorAuthSession, }; diff --git a/server/test/integration/api/ambulances.js b/server/test/integration/api/ambulances.js index 5d0500fb..aa3fa1bf 100644 --- a/server/test/integration/api/ambulances.js +++ b/server/test/integration/api/ambulances.js @@ -1,21 +1,27 @@ +/* eslint-env mocha */ + const assert = require('assert'); const HttpStatus = require('http-status-codes'); const session = require('supertest-session'); const helper = require('../../helper'); const app = require('../../../app'); +const nodemailermock = require('nodemailer-mock'); describe('/api/ambulances', () => { let testSession; beforeEach(async () => { await helper.loadFixtures(['organizations', 'users', 'ambulances']); - testSession = session(app); await testSession .post('/auth/local/login') .set('Accept', 'application/json') .send({ username: 'sutter.operational@example.com', password: 'abcd1234' }); + await helper.twoFactorAuthSession(testSession); + }); + afterEach(async () => { + nodemailermock.mock.reset(); }); describe('GET /identifiers', () => { diff --git a/server/test/integration/api/emsCalls.js b/server/test/integration/api/emsCalls.js index fa37c679..8660c32a 100644 --- a/server/test/integration/api/emsCalls.js +++ b/server/test/integration/api/emsCalls.js @@ -1,9 +1,12 @@ +/* eslint-env mocha */ + const assert = require('assert'); const HttpStatus = require('http-status-codes'); const session = require('supertest-session'); const helper = require('../../helper'); const app = require('../../../app'); +const nodemailermock = require('nodemailer-mock'); describe('/api/emscalls', () => { let testSession; @@ -24,6 +27,10 @@ describe('/api/emscalls', () => { .post('/auth/local/login') .set('Accept', 'application/json') .send({ username: 'sutter.operational@example.com', password: 'abcd1234' }); + await helper.twoFactorAuthSession(testSession); + }); + afterEach(async () => { + nodemailermock.mock.reset(); }); describe('GET /dispatch-call-numbers', () => { diff --git a/server/test/integration/api/hospitalStatuses.js b/server/test/integration/api/hospitalStatuses.js index 315a1bb6..8fa1650c 100644 --- a/server/test/integration/api/hospitalStatuses.js +++ b/server/test/integration/api/hospitalStatuses.js @@ -1,9 +1,12 @@ +/* eslint-env mocha */ + const assert = require('assert'); const HttpStatus = require('http-status-codes'); const session = require('supertest-session'); const helper = require('../../helper'); const app = require('../../../app'); +const nodemailermock = require('nodemailer-mock'); describe('/api/hospitalstatuses', () => { let testSession; @@ -26,6 +29,10 @@ describe('/api/hospitalstatuses', () => { .post('/auth/local/login') .set('Accept', 'application/json') .send({ username: 'sutter.operational@example.com', password: 'abcd1234' }); + await helper.twoFactorAuthSession(testSession); + }); + afterEach(async () => { + nodemailermock.mock.reset(); }); describe('GET /', () => { diff --git a/server/test/integration/api/ringdowns.js b/server/test/integration/api/ringdowns.js index dfeed651..cbf647e7 100644 --- a/server/test/integration/api/ringdowns.js +++ b/server/test/integration/api/ringdowns.js @@ -1,3 +1,5 @@ +/* eslint-env mocha */ + const assert = require('assert'); const HttpStatus = require('http-status-codes'); const session = require('supertest-session'); @@ -6,6 +8,7 @@ const helper = require('../../helper'); const app = require('../../../app'); const models = require('../../../models'); const { DeliveryStatus } = require('../../../../shared/constants'); +const nodemailermock = require('nodemailer-mock'); describe('/api/ringdowns', () => { let testSession; @@ -26,6 +29,10 @@ describe('/api/ringdowns', () => { testSession = session(app); }); + afterEach(async () => { + nodemailermock.mock.reset(); + }); + describe('GET /', () => { it('returns a list of all ringdowns', async () => { await testSession @@ -33,6 +40,7 @@ describe('/api/ringdowns', () => { .set('Accept', 'application/json') .send({ username: 'super.user@example.com', password: 'abcd1234' }) .expect(HttpStatus.OK); + await helper.twoFactorAuthSession(testSession); const response = await testSession.get('/api/ringdowns').set('Accept', 'application/json').expect(HttpStatus.OK); assert.deepStrictEqual(response.body.length, 5); @@ -45,6 +53,7 @@ describe('/api/ringdowns', () => { .send({ username: 'sutter.operational@example.com', password: 'abcd1234' }) .expect(HttpStatus.OK); + await helper.twoFactorAuthSession(testSession); const response = await testSession .get('/api/ringdowns') .query({ hospitalId: '7f666fe4-dbdd-4c7f-ab44-d9157379a680' }) @@ -61,6 +70,7 @@ describe('/api/ringdowns', () => { .send({ username: 'amr.paramedic@example.com', password: 'abcd1234' }) .expect(HttpStatus.OK); + await helper.twoFactorAuthSession(testSession); const response = await testSession.get('/api/ringdowns/mine').set('Accept', 'application/json').expect(HttpStatus.OK); assert.deepStrictEqual(response.body.length, 1); assert.deepStrictEqual(response.body[0].id, '4889b0c8-ce48-474a-ac5b-c5aca708451c'); @@ -101,6 +111,7 @@ describe('/api/ringdowns', () => { .send({ username: 'norcal.paramedic@example.com', password: 'abcd1234' }) .expect(HttpStatus.OK); + await helper.twoFactorAuthSession(testSession); const response = await testSession .post('/api/ringdowns') .set('Accept', 'application/json') @@ -135,7 +146,7 @@ describe('/api/ringdowns', () => { .set('Accept', 'application/json') .send({ username: 'norcal.paramedic@example.com', password: 'abcd1234' }) .expect(HttpStatus.OK); - + await helper.twoFactorAuthSession(testSession); const response = await testSession .post('/api/ringdowns') .set('Accept', 'application/json') @@ -199,7 +210,7 @@ describe('/api/ringdowns', () => { .set('Accept', 'application/json') .send({ username: 'norcal.paramedic@example.com', password: 'abcd1234' }) .expect(HttpStatus.OK); - + await helper.twoFactorAuthSession(testSession); const response = await testSession .post('/api/ringdowns') .set('Accept', 'application/json') @@ -235,6 +246,7 @@ describe('/api/ringdowns', () => { .send({ username: 'norcal.paramedic@example.com', password: 'abcd1234' }) .expect(HttpStatus.OK); + await helper.twoFactorAuthSession(testSession); const response = await testSession .post('/api/ringdowns') .set('Accept', 'application/json') @@ -273,6 +285,7 @@ describe('/api/ringdowns', () => { .send({ username: 'sutter.operational@example.com', password: 'abcd1234' }) .expect(HttpStatus.OK); + await helper.twoFactorAuthSession(testSession); let now = new Date(); await testSession .patch('/api/ringdowns/d4fd2478-ecd6-4571-9fb3-842bfc64b511/deliveryStatus') @@ -380,6 +393,8 @@ describe('/api/ringdowns', () => { .send({ username: 'king.paramedic@example.com', password: 'abcd1234' }) .expect(HttpStatus.OK); + await helper.twoFactorAuthSession(testSession); + const now = new Date(); await testSession .patch('/api/ringdowns/d4fd2478-ecd6-4571-9fb3-842bfc64b511/deliveryStatus') @@ -433,6 +448,8 @@ describe('/api/ringdowns', () => { .send({ username: 'sffd.paramedic@example.com', password: 'abcd1234' }) .expect(HttpStatus.OK); + await helper.twoFactorAuthSession(testSession); + const response = await testSession .patch('/api/ringdowns/8b95ea8a-0171-483a-be74-ec17bbc12247') .set('Accept', 'application/json') @@ -481,6 +498,8 @@ describe('/api/ringdowns', () => { .send({ username: 'sffd.paramedic@example.com', password: 'abcd1234' }) .expect(HttpStatus.OK); + await helper.twoFactorAuthSession(testSession); + const response = await testSession .get('/api/ringdowns/8b95ea8a-0171-483a-be74-ec17bbc12247') .set('Accept', 'application/json') @@ -541,6 +560,8 @@ describe('/api/ringdowns', () => { .send({ username: 'second.sffd.paramedic@example.com', password: 'abcd1234' }) .expect(HttpStatus.OK); + await helper.twoFactorAuthSession(testSession); + await testSession.get('/api/ringdowns/8b95ea8a-0171-483a-be74-ec17bbc12247').set('Accept', 'application/json').expect(HttpStatus.OK); }); }); diff --git a/server/test/integration/api/users.js b/server/test/integration/api/users.js index 4c4c2e38..f9c108d1 100644 --- a/server/test/integration/api/users.js +++ b/server/test/integration/api/users.js @@ -1,9 +1,12 @@ +/* eslint-env mocha */ + const assert = require('assert'); const HttpStatus = require('http-status-codes'); const session = require('supertest-session'); const helper = require('../../helper'); const app = require('../../../app'); +const nodemailermock = require('nodemailer-mock'); describe('/api/users', () => { let testSession; @@ -12,6 +15,9 @@ describe('/api/users', () => { await helper.loadFixtures(['organizations', 'users', 'hospitals', 'hospitalUsers']); testSession = session(app); }); + afterEach(async () => { + nodemailermock.mock.reset(); + }); describe('GET /', () => { it('returns a list of users to a superuser', async () => { @@ -22,6 +28,8 @@ describe('/api/users', () => { .send({ username: 'super.user@example.com', password: 'abcd1234' }) .expect(HttpStatus.OK); + await helper.twoFactorAuthSession(testSession); + /// request user list const response = await testSession.get('/api/users').set('Accept', 'application/json').expect(HttpStatus.OK); assert(response.body?.length, 2); @@ -35,6 +43,8 @@ describe('/api/users', () => { .send({ username: 'sutter.operational@example.com', password: 'abcd1234' }) .expect(HttpStatus.OK); + await helper.twoFactorAuthSession(testSession); + /// request user list await testSession.get('/api/users').set('Accept', 'application/json').expect(HttpStatus.FORBIDDEN); }); @@ -47,6 +57,9 @@ describe('/api/users', () => { .set('Accept', 'application/json') .send({ username: 'sutter.operational@example.com', password: 'abcd1234' }) .expect(HttpStatus.OK); + + await helper.twoFactorAuthSession(testSession); + const response = await testSession.get('/api/users/me').set('Accept', 'application/json').expect(HttpStatus.OK); assert.deepStrictEqual(response.body, { id: '449b1f54-7583-417c-8c25-8da7dde65f6d', diff --git a/server/test/integration/authentication/twoFactorFlow.js b/server/test/integration/authentication/twoFactorFlow.js new file mode 100644 index 00000000..00393edc --- /dev/null +++ b/server/test/integration/authentication/twoFactorFlow.js @@ -0,0 +1,116 @@ +/* eslint-env mocha */ + +const { expect } = require('chai'); +const mockery = require('mockery'); +const nodemailermock = require('nodemailer-mock'); +const session = require('supertest-session'); +const app = require('../../../app'); +const HttpStatus = require('http-status-codes'); +const helper = require('../../helper'); +const model = require('../../../models'); +const { createTransport } = require('../../../mailer/emailTransporter'); + +describe('Two Factor Page', async () => { + let testSession = null; + beforeEach(async () => { + // load fixtures + await helper.loadFixtures(['organizations', 'users', 'ambulances']); + }); + + it(' should reach the twoFactor auth page after initial log in ', async () => { + testSession = session(app); + + // After Inital Log In, the user should be redirected to the twoFactor auth page + await testSession + .post('/auth/local/login') + .set('Accept', 'application/json') + .send({ username: 'sutter.operational@example.com', password: 'abcd1234' }) + .expect(HttpStatus.OK); + // Call the two-factor authentication endpoint + const response = await testSession.get('/auth/local/twoFactor').set('Accept', 'application/json'); + // Check that the response has the correct content + expect(response.text).to.contain('Please enter the Authoritzation Code that was sent to your E-mail'); + expect(response.text).to.contain('Submit'); + }); + + it(' should not reach the twoFactor auth page if not initally logged in', async () => { + testSession = session(app); + // Attempt an invalid Log In + await testSession + .post('/auth/local/login') + .set('Accept', 'application/json') + .send({ username: 'incorrect.user@example.com', password: 'barfoo' }) + .expect(HttpStatus.UNAUTHORIZED); + // Call the two-factor authentication endpoint + const response = await testSession.get('/auth/local/twoFactor').set('Accept', 'application/json'); + // Expect 302 redirect + expect(response.status).to.equal(HttpStatus.MOVED_TEMPORARILY); + // Expect redirect to login page + expect(response.header.location).to.equal('/auth/local/login'); + }); +}); + +describe('Email Functionality', async () => { + let testSession = null; + before(async () => { + // Enable mockery to mock objects + mockery.enable({ + warnOnUnregistered: false, + }); + mockery.registerMock('nodemailer', nodemailermock); + }); + beforeEach(async () => { + // load fixtures + await helper.loadFixtures(['organizations', 'users', 'ambulances']); + }); + afterEach(async () => { + // Reset the mock back to the defaults after each test + nodemailermock.mock.reset(); + }); + + after(async () => { + // Remove our mocked nodemailer and disable mockery + mockery.deregisterAll(); + mockery.disable(); + }); + it('should send out intended email with ANY provided Email Transporter', async () => { + // Create a mock transport + const transport = createTransport(); + // Generate a secret and send it to the mock transport + const user = await model.User.findOne({ where: { email: 'sutter.operational@example.com' } }); + user.generateToTPSecret('test123@gmail.com', transport); + // Get the sent message from the mock transport + const sentMail = nodemailermock.mock.sentMail(); + // Expect one message to be sent + expect(sentMail.length).to.equal(1); + // Expect the message to be sent to the correct email address + expect(sentMail[0].to).to.equal('sutter.operational@example.com'); + // Expect the message to have the correct subject + expect(sentMail[0].subject).to.equal('Your Authentication Code from Routed'); + // Expect the message to have the correct text with 6 digit Authentication Code + expect(sentMail[0].text).to.contain('This is your Authentication Code:'); + }); + it('E2E - should correctly authenticate with ToTP secret', async () => { + testSession = session(app); + // reset the mock back to the defaults after each test + nodemailermock.mock.reset(); + // After Inital Log In, the user should be redirected to the twoFactor auth page + await testSession + .post('/auth/local/login') + .set('Accept', 'application/json') + .send({ username: 'sutter.operational@example.com', password: 'abcd1234' }) + .expect(HttpStatus.OK); + // Go to Two Factor Auth Page + await testSession.get('/auth/local/twoFactor').set('Accept', 'application/json'); + const sentMail = nodemailermock.mock.sentMail(); + // Extract authentication code from the sent email + const regex = /Authentication Code: (\d{6})/; + const match = regex.exec(sentMail[0].text); + const authCode = match[1]; + // Submit the authentication code + const response = await testSession.post('/auth/local/twoFactor').set('Accept', 'application/json').send({ code: authCode }); + // Expect to be redirected to the home page + expect(response.status).to.equal(HttpStatus.MOVED_TEMPORARILY); + expect(response.header.location).to.equal('/'); + }); +}); diff --git a/server/views/auth/local/twoFactor.ejs b/server/views/auth/local/twoFactor.ejs new file mode 100644 index 00000000..c9539cb3 --- /dev/null +++ b/server/views/auth/local/twoFactor.ejs @@ -0,0 +1,28 @@ +
+
+
+
+ +

+
+
+ <% if (locals.incorrectCode) { %> +
+
+

Invalid Authoritzation Code.

+
+
+ <% } %> +
+

Please enter the Authoritzation Code that was sent to your E-mail

+
+
+
+ + +
+ +
+
+
+
diff --git a/server/views/layout.ejs b/server/views/layout.ejs index e1e0f74f..e934a163 100644 --- a/server/views/layout.ejs +++ b/server/views/layout.ejs @@ -1,9 +1,9 @@ - + - - - + + + Routed diff --git a/shared/metadata/user.js b/shared/metadata/user.js index ae13beac..9edb417c 100644 --- a/shared/metadata/user.js +++ b/shared/metadata/user.js @@ -63,7 +63,7 @@ const fields = [ type: 'string', }, { - name: 'ssoData', + name: 'twoFactorData', type: 'jsonb', }, { diff --git a/yarn.lock b/yarn.lock index 71eb5198..9dac8879 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,12 +2,12 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.1.0": - version "2.2.0" - resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz" - integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== dependencies: - "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" "@apideck/better-ajv-errors@^0.3.1": @@ -19,33 +19,33 @@ jsonpointer "^5.0.0" leven "^3.1.0" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.8.3": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz" - integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4", "@babel/code-frame@^7.8.3": + version "7.21.4" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz" + integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.1", "@babel/compat-data@^7.20.5": - version "7.20.14" - resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.14.tgz" - integrity sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.1", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.4": + version "7.21.4" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz" + integrity sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g== "@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.20.12" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz" - integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg== - dependencies: - "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helpers" "^7.20.7" - "@babel/parser" "^7.20.7" + version "7.21.4" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz" + integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" + "@babel/helper-compilation-targets" "^7.21.4" + "@babel/helper-module-transforms" "^7.21.2" + "@babel/helpers" "^7.21.0" + "@babel/parser" "^7.21.4" "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.12" - "@babel/types" "^7.20.7" + "@babel/traverse" "^7.21.4" + "@babel/types" "^7.21.4" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -61,22 +61,14 @@ eslint-visitor-keys "^2.1.0" semver "^6.3.0" -"@babel/generator@^7.20.5": - version "7.20.5" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz" - integrity sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA== +"@babel/generator@^7.21.4", "@babel/generator@^7.7.2": + version "7.21.4" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz" + integrity sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA== dependencies: - "@babel/types" "^7.20.5" - "@jridgewell/gen-mapping" "^0.3.2" - jsesc "^2.5.1" - -"@babel/generator@^7.20.7", "@babel/generator@^7.7.2": - version "7.20.14" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz" - integrity sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg== - dependencies: - "@babel/types" "^7.20.7" + "@babel/types" "^7.21.4" "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" "@babel/helper-annotate-as-pure@^7.18.6": @@ -94,13 +86,13 @@ "@babel/helper-explode-assignable-expression" "^7.18.6" "@babel/types" "^7.18.9" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.0", "@babel/helper-compilation-targets@^7.20.7": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz" - integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.0", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.4": + version "7.21.4" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz" + integrity sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg== dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-validator-option" "^7.18.6" + "@babel/compat-data" "^7.21.4" + "@babel/helper-validator-option" "^7.21.0" browserslist "^4.21.3" lru-cache "^5.1.1" semver "^6.3.0" @@ -151,13 +143,13 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0": - version "7.19.0" - resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz" - integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0": + version "7.21.0" + resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz" + integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== dependencies: - "@babel/template" "^7.18.10" - "@babel/types" "^7.19.0" + "@babel/template" "^7.20.7" + "@babel/types" "^7.21.0" "@babel/helper-hoist-variables@^7.18.6": version "7.18.6" @@ -180,10 +172,10 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11": - version "7.20.11" - resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz" - integrity sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg== +"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.2": + version "7.21.2" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz" + integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== dependencies: "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-module-imports" "^7.18.6" @@ -191,8 +183,8 @@ "@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-validator-identifier" "^7.19.1" "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.10" - "@babel/types" "^7.20.7" + "@babel/traverse" "^7.21.2" + "@babel/types" "^7.21.2" "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" @@ -259,10 +251,10 @@ resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== -"@babel/helper-validator-option@^7.18.6": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz" - integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== +"@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0": + version "7.21.0" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz" + integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== "@babel/helper-wrap-function@^7.18.9": version "7.20.5" @@ -274,14 +266,14 @@ "@babel/traverse" "^7.20.5" "@babel/types" "^7.20.5" -"@babel/helpers@^7.20.7": - version "7.20.13" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.13.tgz" - integrity sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg== +"@babel/helpers@^7.21.0": + version "7.21.0" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz" + integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== dependencies: "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.13" - "@babel/types" "^7.20.7" + "@babel/traverse" "^7.21.0" + "@babel/types" "^7.21.0" "@babel/highlight@^7.18.6": version "7.18.6" @@ -292,15 +284,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.13", "@babel/parser@^7.20.7": - version "7.20.15" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.20.15.tgz" - integrity sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg== - -"@babel/parser@^7.18.10", "@babel/parser@^7.20.5", "@babel/parser@^7.7.0": - version "7.20.5" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz" - integrity sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4", "@babel/parser@^7.7.0": + version "7.21.4" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz" + integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" @@ -508,11 +495,11 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-flow@^7.18.6": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz" - integrity sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A== + version "7.21.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.21.4.tgz" + integrity sha512-l9xd3N+XG4fZRxEP3vXdK6RW7vN1Uf5dxzRC/09wV86wqZ/YYQooBIGNsiRdfNR3/q2/5pPzV4B54J/9ctX5jw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-import-assertions@^7.20.0": version "7.20.0" @@ -820,15 +807,15 @@ "@babel/plugin-transform-react-jsx" "^7.18.6" "@babel/plugin-transform-react-jsx@^7.18.6": - version "7.20.13" - resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.20.13.tgz" - integrity sha512-MmTZx/bkUrfJhhYAYt3Urjm+h8DQGrPrnKQ94jLo7NLuOU+T89a7IByhKmrb8SKhrIYIQ0FN0CHMbnFRen4qNw== + version "7.21.0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.0.tgz" + integrity sha512-6OAWljMvQrZjR2DaNhVfRz6dkCAVV+ymcLUmaf8bccGOHn2v5rHJK3tTpij0BuhdYWP4LLaqj5lwcdlpAAPuvg== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-module-imports" "^7.18.6" "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-jsx" "^7.18.6" - "@babel/types" "^7.20.7" + "@babel/types" "^7.21.0" "@babel/plugin-transform-react-pure-annotations@^7.18.6": version "7.18.6" @@ -1053,16 +1040,7 @@ dependencies: regenerator-runtime "^0.13.11" -"@babel/template@^7.18.10": - version "7.18.10" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz" - integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.18.10" - "@babel/types" "^7.18.10" - -"@babel/template@^7.20.7", "@babel/template@^7.3.3": +"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": version "7.20.7" resolved "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz" integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== @@ -1071,51 +1049,26 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" -"@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.13", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.7.2": - version "7.20.13" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz" - integrity sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ== +"@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.2": + version "7.21.4" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz" + integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" + "@babel/helper-function-name" "^7.21.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.13" - "@babel/types" "^7.20.7" + "@babel/parser" "^7.21.4" + "@babel/types" "^7.21.4" debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.7.0": - version "7.20.5" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz" - integrity sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.5" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.5" - "@babel/types" "^7.20.5" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz" - integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== - dependencies: - "@babel/helper-string-parser" "^7.19.4" - "@babel/helper-validator-identifier" "^7.19.1" - to-fast-properties "^2.0.0" - -"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.5", "@babel/types@^7.7.0": - version "7.20.5" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz" - integrity sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg== +"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": + version "7.21.4" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz" + integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA== dependencies: "@babel/helper-string-parser" "^7.19.4" "@babel/helper-validator-identifier" "^7.19.1" @@ -1521,14 +1474,6 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jridgewell/gen-mapping@^0.1.0": - version "0.1.1" - resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz" - integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== - dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": version "0.3.2" resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz" @@ -1543,7 +1488,7 @@ resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== -"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": +"@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== @@ -1561,7 +1506,7 @@ resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== -"@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": version "0.3.17" resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz" integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== @@ -1820,9 +1765,9 @@ loader-utils "^2.0.0" "@testing-library/dom@*": - version "8.20.0" - resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.0.tgz" - integrity sha512-d9ULIT+a4EXLX3UU8FBjauG9NnsZHkHztXoIcTsOKoOw030fyjheN9svkTULjJxtYag9DZz5Jz5qkWZDPxTFwA== + version "9.2.0" + resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-9.2.0.tgz" + integrity sha512-xTEnpUKiV/bMyEsE5bT4oYA0x0Z/colMtxzUY8bKyPXBNLn/e0V4ZjBZkEhms0xE4pv9QsPfSRu9AWS4y5wGvA== dependencies: "@babel/code-frame" "^7.10.4" "@babel/runtime" "^7.12.5" @@ -1830,7 +1775,7 @@ aria-query "^5.0.0" chalk "^4.1.0" dom-accessibility-api "^0.5.9" - lz-string "^1.4.4" + lz-string "^1.5.0" pretty-format "^27.0.2" "@testing-library/dom@^6.15.0": @@ -2345,11 +2290,6 @@ "@typescript-eslint/types" "5.50.0" eslint-visitor-keys "^3.3.0" -"@ungap/promise-all-settled@1.1.2": - version "1.1.2" - resolved "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz" - integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== - "@webassemblyjs/ast@1.11.1": version "1.11.1" resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz" @@ -2782,6 +2722,11 @@ asap@~2.0.6: resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + ast-types-flow@^0.0.7: version "0.0.7" resolved "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz" @@ -3219,12 +3164,7 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001426: - version "1.0.30001451" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001451.tgz" - integrity sha512-XY7UbUpGRatZzoRft//5xOa69/1iGJRBlrieH6QYrkKLIFn3m7OVEJ81dSrKoy2BnKsdbX5cLrOispZNYo9v2w== - -caniuse-lite@^1.0.30001449: +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001426, caniuse-lite@^1.0.30001449: version "1.0.30001450" resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001450.tgz" integrity sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew== @@ -3234,6 +3174,19 @@ case-sensitive-paths-webpack-plugin@^2.4.0: resolved "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz" integrity sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw== +chai@^4.3.7: + version "4.3.7" + resolved "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz" + integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^4.1.2" + get-func-name "^2.0.0" + loupe "^2.3.1" + pathval "^1.1.1" + type-detect "^4.0.5" + chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.1: version "2.4.2" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" @@ -3269,27 +3222,17 @@ char-regex@^2.0.0: resolved "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz" integrity sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw== +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz" + integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== + check-types@^11.1.1: version "11.2.2" resolved "https://registry.npmjs.org/check-types/-/check-types-11.2.2.tgz" integrity sha512-HBiYvXvn9Z70Z88XKjz3AEKd4HJhBXsa3j7xFnITAzoS8+q6eIGi8qDB8FKPBAjtuxjI/zFpwuiCb8oDtKOYrA== -chokidar@3.5.1: - version "3.5.1" - resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz" - integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== - dependencies: - anymatch "~3.1.1" - braces "~3.0.2" - glob-parent "~5.1.0" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.5.0" - optionalDependencies: - fsevents "~2.3.1" - -"chokidar@>=3.0.0 <4.0.0", chokidar@^3.3.0, chokidar@^3.4.2, chokidar@^3.5.2, chokidar@^3.5.3: +chokidar@3.5.3, chokidar@^3.5.2, chokidar@^3.5.3: version "3.5.3" resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -3304,6 +3247,21 @@ chokidar@3.5.1: optionalDependencies: fsevents "~2.3.2" +"chokidar@>=3.0.0 <4.0.0", chokidar@^3.3.0, chokidar@^3.4.2: + version "3.5.1" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz" + integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.5.0" + optionalDependencies: + fsevents "~2.3.1" + chownr@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz" @@ -3909,20 +3867,13 @@ debug@2.6.9, debug@^2.6.0: dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: +debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" -debug@4.3.1: - version "4.3.1" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== - dependencies: - ms "2.1.2" - debug@^3.1.0, debug@^3.2.7: version "3.2.7" resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" @@ -3955,6 +3906,13 @@ dedent@^0.7.0: resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz" integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== +deep-eql@^4.1.2: + version "4.1.3" + resolved "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz" + integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== + dependencies: + type-detect "^4.0.0" + deep-equal@^2.0.5: version "2.2.0" resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.0.tgz" @@ -4335,46 +4293,7 @@ error-stack-parser@^2.0.6: dependencies: stackframe "^1.3.4" -es-abstract@^1.17.2: - version "1.21.1" - resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz" - integrity sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - es-set-tostringtag "^2.0.1" - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.1.3" - get-symbol-description "^1.0.0" - globalthis "^1.0.3" - gopd "^1.0.1" - has "^1.0.3" - has-property-descriptors "^1.0.0" - has-proto "^1.0.1" - has-symbols "^1.0.3" - internal-slot "^1.0.4" - is-array-buffer "^3.0.1" - is-callable "^1.2.7" - is-negative-zero "^2.0.2" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - is-string "^1.0.7" - is-typed-array "^1.1.10" - is-weakref "^1.0.2" - object-inspect "^1.12.2" - object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.4.3" - safe-regex-test "^1.0.0" - string.prototype.trimend "^1.0.6" - string.prototype.trimstart "^1.0.6" - typed-array-length "^1.0.4" - unbox-primitive "^1.0.2" - which-typed-array "^1.1.9" - -es-abstract@^1.19.0, es-abstract@^1.20.4: +es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.20.4: version "1.20.5" resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.5.tgz" integrity sha512-7h8MM2EQhsCA7pU/Nv78qOXFpD8Rhqd12gYiSJVkrH9+e8VuA8JlPJK/hQjjlLv6pJvx/z1iRFKzYb0XT/RuAQ== @@ -4430,15 +4349,6 @@ es-module-lexer@^0.9.0: resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz" integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== -es-set-tostringtag@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz" - integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== - dependencies: - get-intrinsic "^1.1.3" - has "^1.0.3" - has-tostringtag "^1.0.0" - es-shim-unscopables@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz" @@ -4716,52 +4626,7 @@ eslint-webpack-plugin@^3.1.1: normalize-path "^3.0.0" schema-utils "^4.0.0" -eslint@^8.3.0: - version "8.34.0" - resolved "https://registry.npmjs.org/eslint/-/eslint-8.34.0.tgz" - integrity sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg== - dependencies: - "@eslint/eslintrc" "^1.4.1" - "@humanwhocodes/config-array" "^0.11.8" - "@humanwhocodes/module-importer" "^1.0.1" - "@nodelib/fs.walk" "^1.2.8" - ajv "^6.10.0" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.3.2" - doctrine "^3.0.0" - escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" - eslint-utils "^3.0.0" - eslint-visitor-keys "^3.3.0" - espree "^9.4.0" - esquery "^1.4.0" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" - find-up "^5.0.0" - glob-parent "^6.0.2" - globals "^13.19.0" - grapheme-splitter "^1.0.4" - ignore "^5.2.0" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - is-path-inside "^3.0.3" - js-sdsl "^4.1.4" - js-yaml "^4.1.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.1.2" - natural-compare "^1.4.0" - optionator "^0.9.1" - regexpp "^3.2.0" - strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" - text-table "^0.2.0" - -eslint@^8.33.0: +eslint@^8.3.0, eslint@^8.33.0: version "8.33.0" resolved "https://registry.npmjs.org/eslint/-/eslint-8.33.0.tgz" integrity sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA== @@ -5336,7 +5201,12 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz" + integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz" integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== @@ -5392,10 +5262,10 @@ glob-to-regexp@^0.4.1: resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@7.1.6: - version "7.1.6" - resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== +glob@7.2.0: + version "7.2.0" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -5405,14 +5275,14 @@ glob@7.1.6: path-is-absolute "^1.0.0" glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: - version "7.2.3" - resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + version "7.1.6" + resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.1.1" + minimatch "^3.0.4" once "^1.3.0" path-is-absolute "^1.0.0" @@ -5467,13 +5337,6 @@ globals@^13.19.0: dependencies: type-fest "^0.20.2" -globalthis@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz" - integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== - dependencies: - define-properties "^1.1.3" - globby@^11.0.4, globby@^11.1.0: version "11.1.0" resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" @@ -5532,11 +5395,6 @@ grapheme-splitter@^1.0.4: resolved "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz" integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== -growl@1.10.5: - version "1.10.5" - resolved "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz" - integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== - gzip-size@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz" @@ -5576,11 +5434,6 @@ has-property-descriptors@^1.0.0: dependencies: get-intrinsic "^1.1.1" -has-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz" - integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== - has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" @@ -5894,16 +5747,7 @@ ini@^1.3.4, ini@^1.3.5: resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== -internal-slot@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz" - integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== - dependencies: - get-intrinsic "^1.1.0" - has "^1.0.3" - side-channel "^1.0.4" - -internal-slot@^1.0.4: +internal-slot@^1.0.3, internal-slot@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz" integrity sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ== @@ -6128,7 +5972,7 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" -is-typed-array@^1.1.10, is-typed-array@^1.1.9: +is-typed-array@^1.1.10: version "1.1.10" resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz" integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== @@ -6144,6 +5988,11 @@ is-typedarray@^1.0.0: resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + is-weakmap@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz" @@ -6770,10 +6619,10 @@ js-sdsl@^4.1.4: resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz" - integrity sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q== +js-yaml@4.1.0, js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" @@ -6785,13 +6634,6 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - jsdom@^16.6.0: version "16.7.0" resolved "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz" @@ -6893,6 +6735,11 @@ jsonpointer@^5.0.0: resolved "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz" integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== +jssha@~3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/jssha/-/jssha-3.3.0.tgz" + integrity sha512-w9OtT4ALL+fbbwG3gw7erAO0jvS5nfvrukGPMWIAoea359B26ALXGpzy4YJSp9yGnpUvuvOw1nSjSoHDfWSr1w== + "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.3: version "3.3.3" resolved "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz" @@ -7059,12 +6906,13 @@ lodash@^4.17.11, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz" - integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== +log-symbols@4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== dependencies: - chalk "^4.0.0" + chalk "^4.1.0" + is-unicode-supported "^0.1.0" loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" @@ -7073,6 +6921,13 @@ loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4 dependencies: js-tokens "^3.0.0 || ^4.0.0" +loupe@^2.3.1: + version "2.3.6" + resolved "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz" + integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== + dependencies: + get-func-name "^2.0.0" + lower-case@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz" @@ -7114,10 +6969,10 @@ luxon@^1.28.0: resolved "https://registry.npmjs.org/luxon/-/luxon-1.28.1.tgz" integrity sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw== -lz-string@^1.4.4: - version "1.4.4" - resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz" - integrity sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ== +lz-string@^1.5.0: + version "1.5.0" + resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz" + integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== magic-string@^0.25.0, magic-string@^0.25.7: version "0.25.9" @@ -7248,14 +7103,14 @@ minimalistic-assert@^1.0.0: resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -minimatch@3.0.4: - version "3.0.4" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== dependencies: - brace-expansion "^1.1.7" + brace-expansion "^2.0.1" -minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -7306,37 +7161,38 @@ mkdirp@~0.5.1: dependencies: minimist "^1.2.6" -mocha@^8.4.0: - version "8.4.0" - resolved "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz" - integrity sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ== +mocha@^10.2.0: + version "10.2.0" + resolved "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz" + integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== dependencies: - "@ungap/promise-all-settled" "1.1.2" ansi-colors "4.1.1" browser-stdout "1.3.1" - chokidar "3.5.1" - debug "4.3.1" + chokidar "3.5.3" + debug "4.3.4" diff "5.0.0" escape-string-regexp "4.0.0" find-up "5.0.0" - glob "7.1.6" - growl "1.10.5" + glob "7.2.0" he "1.2.0" - js-yaml "4.0.0" - log-symbols "4.0.0" - minimatch "3.0.4" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "5.0.1" ms "2.1.3" - nanoid "3.1.20" - serialize-javascript "5.0.1" + nanoid "3.3.3" + serialize-javascript "6.0.0" strip-json-comments "3.1.1" supports-color "8.1.1" - which "2.0.2" - wide-align "1.1.3" - workerpool "6.1.0" + workerpool "6.2.1" yargs "16.2.0" yargs-parser "20.2.4" yargs-unparser "2.0.0" +mockery@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/mockery/-/mockery-2.1.0.tgz" + integrity sha512-9VkOmxKlWXoDO/h1jDZaS4lH33aWfRiJiNT/tKj+8OGzrcFDLo8d0syGdbsc3Bc4GvRXPb+NMMvojotmuGJTvA== + module-not-found-error@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/module-not-found-error/-/module-not-found-error-1.0.1.tgz" @@ -7370,12 +7226,12 @@ ms@2.0.0: resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== -ms@2.1.2: +ms@2.1.2, ms@^2.1.1: version "2.1.2" resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3, ms@^2.1.1: +ms@2.1.3: version "2.1.3" resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -7393,10 +7249,10 @@ mustache@^2.2.1: resolved "https://registry.npmjs.org/mustache/-/mustache-2.3.2.tgz" integrity sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ== -nanoid@3.1.20: - version "3.1.20" - resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz" - integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== +nanoid@3.3.3: + version "3.3.3" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz" + integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== nanoid@^3.3.4: version "3.3.4" @@ -7473,6 +7329,18 @@ node-releases@^2.0.8: resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.9.tgz" integrity sha512-2xfmOrRkGogbTK9R6Leda0DGiXeY3p2NJpy4+gNCffdUvV6mdEJnaDEic1i3Ec2djAo8jWYoJMR5PB0MSMpxUA== +nodemailer-mock@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/nodemailer-mock/-/nodemailer-mock-2.0.1.tgz#93831d6f5b0426c776bb3ad7e4edb380543f6c20" + integrity sha512-2gkdW/ApexF/Y9EwJ/FXF4Df+1n/Y6Oek29QzsVJf/kMXshDW7q4CVzK/yTFFUw3Z0XnxNZNi/hG9RmMe6HHpg== + dependencies: + debug "^4.3.4" + +nodemailer@^6.9.1: + version "6.9.1" + resolved "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.1.tgz" + integrity sha512-qHw7dOiU5UKNnQpXktdgQ1d3OFgRAekuvbJLcdG5dnEo/GtcTHRYM7+UfJARdOFU9WUQO8OiIamgWPmiSFHYAA== + nodemon@^2.0.18: version "2.0.20" resolved "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz" @@ -7720,6 +7588,13 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" +otpauth@^9.1.1: + version "9.1.1" + resolved "https://registry.npmjs.org/otpauth/-/otpauth-9.1.1.tgz" + integrity sha512-XhimxmkREwf6GJvV4svS9OVMFJ/qRGz+QBEGwtW5OMf9jZlx9yw25RZMXdrO6r7DHgfIaETJb1lucZXZtn3jgw== + dependencies: + jssha "~3.3.0" + p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" @@ -7894,6 +7769,11 @@ path-type@^4.0.0: resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + pause@0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz" @@ -8752,16 +8632,11 @@ pstree.remy@^1.1.8: resolved "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz" integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== -punycode@^2.1.0: +punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -punycode@^2.1.1: - version "2.3.0" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz" - integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== - q@^1.1.2: version "1.5.1" resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz" @@ -9516,10 +9391,10 @@ sequelize@^6.21.2: validator "^13.7.0" wkx "^0.5.0" -serialize-javascript@5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz" - integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== +serialize-javascript@6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== dependencies: randombytes "^2.1.0" @@ -9590,16 +9465,11 @@ shebang-regex@^3.0.0: resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@^1.6.1: +shell-quote@^1.6.1, shell-quote@^1.7.3: version "1.7.4" resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.4.tgz" integrity sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw== -shell-quote@^1.7.3: - version "1.8.0" - resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.0.tgz" - integrity sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ== - side-channel@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" @@ -9816,7 +9686,7 @@ string-natural-compare@^3.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -9964,7 +9834,7 @@ superagent@^3.8.3: supertest-session@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/supertest-session/-/supertest-session-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/supertest-session/-/supertest-session-4.1.0.tgz#81d851ca311e12cc0b34c294012615649a55c159" integrity sha512-zJmc2+WBpT772Pk6InGg90n0l1E+8rr5ue8PAyxefIEJWqbrn0RC02brrw7EC1wecoY+UH/AaQxMeo3sPrnPPA== dependencies: cookiejar "^2.1.2" @@ -10311,7 +10181,7 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-detect@4.0.8: +type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5: version "4.0.8" resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== @@ -10354,15 +10224,6 @@ type@^2.7.2: resolved "https://registry.npmjs.org/type/-/type-2.7.2.tgz" integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== -typed-array-length@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz" - integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== - dependencies: - call-bind "^1.0.2" - for-each "^0.3.3" - is-typed-array "^1.1.9" - typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz" @@ -10703,9 +10564,9 @@ webpack-sources@^3.2.3: integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== webpack@^5.64.4: - version "5.75.0" - resolved "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz" - integrity sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ== + version "5.78.0" + resolved "https://registry.npmjs.org/webpack/-/webpack-5.78.0.tgz" + integrity sha512-gT5DP72KInmE/3azEaQrISjTvLYlSM0j1Ezhht/KLVkrqtv10JoP/RXhwmX/frrutOPuSq3o5Vq0ehR/4Vmd1g== dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^0.0.51" @@ -10827,13 +10688,6 @@ which-typed-array@^1.1.9: has-tostringtag "^1.0.0" is-typed-array "^1.1.10" -which@2.0.2, which@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - which@^1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" @@ -10841,20 +10695,20 @@ which@^1.3.1: dependencies: isexe "^2.0.0" -wide-align@1.1.3: +which@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.2: version "1.1.3" resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz" integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== dependencies: string-width "^1.0.2 || 2" -wide-align@^1.1.2: - version "1.1.5" - resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz" - integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== - dependencies: - string-width "^1.0.2 || 2 || 3 || 4" - wkx@^0.5.0: version "0.5.0" resolved "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz" @@ -11036,10 +10890,10 @@ workbox-window@6.5.4: "@types/trusted-types" "^2.0.2" workbox-core "6.5.4" -workerpool@6.1.0: - version "6.1.0" - resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz" - integrity sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg== +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== wrap-ansi@^6.2.0: version "6.2.0" @@ -11134,7 +10988,7 @@ yaml@^2.1.1: resolved "https://registry.npmjs.org/yaml/-/yaml-2.2.1.tgz" integrity sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw== -yargs-parser@20.2.4: +yargs-parser@20.2.4, yargs-parser@^20.2.2: version "20.2.4" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== @@ -11147,11 +11001,6 @@ yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^20.2.2: - version "20.2.9" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz"