From 869b2f00ec39078abd9dfe3ba0540d362921bf35 Mon Sep 17 00:00:00 2001 From: Dmitry S Date: Mon, 12 Jul 2021 12:10:04 -0400 Subject: [PATCH] (core) Remove LoginSession, which was mainly serving situations that are no longer used. Summary: In the past, Cognito sign-ins were intended to give authorization to some AWS services (like SQS); various tokens were stored in the session for this purpose. This is no longer used. Profiles from Cognito now serve a limited purpose: first-time initialization of name and picture, and keeping track of which login method was used. For these remaining needs, ScopedSession is sufficient. Test Plan: Existing test pass. Tested manually that logins work with Google and Email + Password. Tested manually that on a clean database, name and picture are picked up from a Google Login. Reviewers: paulfitz Reviewed By: paulfitz Differential Revision: https://phab.getgrist.com/D2907 --- app/server/declarations.d.ts | 4 +-- app/server/lib/ActiveDoc.ts | 2 +- app/server/lib/BrowserSession.ts | 30 +++++++++++++++---- app/server/lib/Client.ts | 29 ++++++++++--------- app/server/lib/Comm.js | 15 +++++----- app/server/lib/FlexServer.ts | 15 ++++------ app/server/lib/ICreate.ts | 7 +++-- app/server/lib/ILoginSession.ts | 13 --------- app/server/lib/Sessions.ts | 33 ++++----------------- app/server/lib/TestingHooks.ts | 4 +-- package.json | 2 +- stubs/app/server/lib/LoginSession.ts | 22 -------------- stubs/app/server/lib/create.ts | 8 ++++-- yarn.lock | 43 ++++++++++++++-------------- 14 files changed, 95 insertions(+), 132 deletions(-) delete mode 100644 app/server/lib/ILoginSession.ts delete mode 100644 stubs/app/server/lib/LoginSession.ts diff --git a/app/server/declarations.d.ts b/app/server/declarations.d.ts index a56af292..0d928218 100644 --- a/app/server/declarations.d.ts +++ b/app/server/declarations.d.ts @@ -4,7 +4,7 @@ declare module "app/server/lib/User"; declare module "app/server/lib/Comm" { import {Client, ClientMethod} from "app/server/lib/Client"; - import {LoginSession} from "app/server/lib/LoginSession"; + import {ScopedSession} from "app/server/lib/BrowserSession"; import * as http from "http"; class Comm { @@ -14,7 +14,7 @@ declare module "app/server/lib/Comm" { public setServerVersion(serverVersion: string|null): void; public setServerActivation(active: boolean): void; public getSessionIdFromCookie(gristSidCookie: string): string; - public getOrCreateSession(sessionId: string, req: any): LoginSession; + public getOrCreateSession(sessionId: string, req: any): ScopedSession; public registerMethods(methods: {[name: string]: ClientMethod}): void; public getClient(clientId: string): Client; public testServerShutdown(): Promise; diff --git a/app/server/lib/ActiveDoc.ts b/app/server/lib/ActiveDoc.ts index 5bb3bd9e..ec8aedfd 100644 --- a/app/server/lib/ActiveDoc.ts +++ b/app/server/lib/ActiveDoc.ts @@ -1164,7 +1164,7 @@ export class ActiveDoc extends EventEmitter { await this._granularAccess.assertCanMaybeApplyUserActions(docSession, actions); const user = docSession.mode === 'system' ? 'grist' : - (client && client.session ? (await client.session.getEmail()) : ""); + (client?.getProfile()?.email || ''); // Create the UserActionBundle. const action: UserActionBundle = { diff --git a/app/server/lib/BrowserSession.ts b/app/server/lib/BrowserSession.ts index 1f636857..e92dd9e3 100644 --- a/app/server/lib/BrowserSession.ts +++ b/app/server/lib/BrowserSession.ts @@ -8,19 +8,19 @@ export interface SessionUserObj { // a grist-internal identify for the user, if known. userId?: number; - // The user profile object. When updated, all clients get a message with the update. + // The user profile object. profile?: UserProfile; - // Authentication provider string indicating the login method used. + // [UNUSED] Authentication provider string indicating the login method used. authProvider?: string; - // Login ID token used to access AWS services. + // [UNUSED] Login ID token used to access AWS services. idToken?: string; - // Login access token used to access other AWS services. + // [UNUSED] Login access token used to access other AWS services. accessToken?: string; - // Login refresh token used to retrieve new ID and access tokens. + // [UNUSED] Login refresh token used to retrieve new ID and access tokens. refreshToken?: string; } @@ -133,6 +133,26 @@ export class ScopedSession { return getSessionUser(session, this._org, this._userSelector) || {}; } + // Retrieves the user profile from the session. + public async getSessionProfile(prev?: SessionObj): Promise { + return (await this.getScopedSession(prev)).profile || null; + } + + // Updates a user profile. The session may have multiple profiles associated with different + // email addresses. This will update the one with a matching email address, or add a new one. + // This is mainly used to know which emails are logged in in this session; fields like name and + // picture URL come from the database instead. + public async updateUserProfile(profile: UserProfile|null): Promise { + if (profile) { + await this.operateOnScopedSession(async user => { + user.profile = profile; + return user; + }); + } else { + await this.clearScopedSession(); + } + } + /** * * This performs an operation on the session object, limited to a single user entry. The state of that diff --git a/app/server/lib/Client.ts b/app/server/lib/Client.ts index 125d542e..48d7f1df 100644 --- a/app/server/lib/Client.ts +++ b/app/server/lib/Client.ts @@ -8,9 +8,9 @@ import {User} from 'app/gen-server/entity/User'; import {HomeDBManager} from 'app/gen-server/lib/HomeDBManager'; import {ActiveDoc} from 'app/server/lib/ActiveDoc'; import {Authorizer} from 'app/server/lib/Authorizer'; +import {ScopedSession} from 'app/server/lib/BrowserSession'; import {DocSession} from 'app/server/lib/DocSession'; import * as log from 'app/server/lib/log'; -import {ILoginSession} from 'app/server/lib/ILoginSession'; import {shortDesc} from 'app/server/lib/shortDesc'; import * as crypto from 'crypto'; import * as moment from 'moment'; @@ -61,10 +61,10 @@ void(MESSAGE_TYPES_NO_AUTH); export class Client { public readonly clientId: string; - public session: ILoginSession|null = null; - public browserSettings: BrowserSettings = {}; + private _session: ScopedSession|null = null; + // Maps docFDs to DocSession objects. private _docFDs: Array = []; @@ -221,21 +221,16 @@ export class Client { } } - // Assigns the client to the given login session and the session to the client. - public setSession(session: ILoginSession): void { - this.unsetSession(); - this.session = session; - session.clients.add(this); + // Assigns the given ScopedSession to the client. + public setSession(session: ScopedSession): void { + this._session = session; } - // Unsets the current login session and removes the client from it. - public unsetSession(): void { - if (this.session) { this.session.clients.delete(this); } - this.session = null; + public getSession(): ScopedSession|null { + return this._session; } public destroy() { - this.unsetSession(); this._destroyed = true; } @@ -318,6 +313,14 @@ export class Client { return this._profile; } + public async getSessionProfile(): Promise { + return this._session?.getSessionProfile(); + } + + public async getSessionEmail(): Promise { + return (await this.getSessionProfile())?.email || null; + } + public getCachedUserId(): number|null { return this._userId; } diff --git a/app/server/lib/Comm.js b/app/server/lib/Comm.js index 292ed163..7485a9fa 100644 --- a/app/server/lib/Comm.js +++ b/app/server/lib/Comm.js @@ -78,7 +78,7 @@ function Comm(server, options) { this._clients = {}; // Maps clientIds to Client objects. this.clientList = []; // List of all active Clients, ordered by clientId. - // Maps sessionIds to LoginSession objects. + // Maps sessionIds to ScopedSession objects. this.sessions = options.sessions; this._settings = options.settings; @@ -118,14 +118,13 @@ Comm.prototype.getClient = function(clientId) { }; /** - * Returns a LoginSession object with the given session id from the list of sessions, + * Returns a ScopedSession object with the given session id from the list of sessions, * or adds a new one and returns that. */ Comm.prototype.getOrCreateSession = function(sid, req, userSelector) { - // LoginSessions are specific to a session id / org combination. + // ScopedSessions are specific to a session id / org combination. const org = req.org || ""; - return this.sessions.getOrCreateLoginSession(sid, org, this, - userSelector); + return this.sessions.getOrCreateSession(sid, org, userSelector); }; @@ -230,13 +229,13 @@ Comm.prototype._onWebSocketConnection = async function(websocket, req) { // Add a Session object to the client. log.info(`Comm ${client}: using session ${sessionId}`); - const loginSession = this.getOrCreateSession(sessionId, req, userSelector); - client.setSession(loginSession); + const scopedSession = this.getOrCreateSession(sessionId, req, userSelector); + client.setSession(scopedSession); // Delegate message handling to the client websocket.on('message', client.onMessage.bind(client)); - loginSession.getSessionProfile() + scopedSession.getSessionProfile() .then((profile) => { log.debug(`Comm ${client}: sending clientConnect with ` + `${client._missedMessages.length} missed messages`); diff --git a/app/server/lib/FlexServer.ts b/app/server/lib/FlexServer.ts index c9dca744..5d780e6a 100644 --- a/app/server/lib/FlexServer.ts +++ b/app/server/lib/FlexServer.ts @@ -788,15 +788,12 @@ export class FlexServer implements GristServer { this.app.get('/test/login', expressWrap(async (req, res) => { log.warn("Serving unauthenticated /test/login endpoint, made available because GRIST_TEST_LOGIN is set."); - const session = this.sessions.getOrCreateSessionFromRequest(req); + const scopedSession = this.sessions.getOrCreateSessionFromRequest(req); const profile: UserProfile = { email: optStringParam(req.query.email) || 'chimpy@getgrist.com', name: optStringParam(req.query.name) || 'Chimpy McBanana', }; - await session.scopedSession.operateOnScopedSession(async user => { - user.profile = profile; - return user; - }); + await scopedSession.updateUserProfile(profile); res.send(`

Logged in as ${JSON.stringify(profile)}.

@@ -811,8 +808,8 @@ export class FlexServer implements GristServer { } this.app.get('/logout', expressWrap(async (req, resp) => { - const session = this.sessions.getOrCreateSessionFromRequest(req); - const userSession = await session.scopedSession.getScopedSession(); + const scopedSession = this.sessions.getOrCreateSessionFromRequest(req); + const userSession = await scopedSession.getScopedSession(); // If 'next' param is missing, redirect to "/" on our requested hostname. const next = optStringParam(req.query.next) || (req.protocol + '://' + req.get('host') + '/'); @@ -823,9 +820,7 @@ export class FlexServer implements GristServer { // Express-session will save these changes. const expressSession = (req as any).session; if (expressSession) { expressSession.users = []; expressSession.orgToUser = {}; } - if (session.loginSession) { - await session.loginSession.clearSession(); - } + await scopedSession.clearScopedSession(); resp.redirect(redirectUrl); })); diff --git a/app/server/lib/ICreate.ts b/app/server/lib/ICreate.ts index 02ed988d..2132ba57 100644 --- a/app/server/lib/ICreate.ts +++ b/app/server/lib/ICreate.ts @@ -1,18 +1,19 @@ import {HomeDBManager} from 'app/gen-server/lib/HomeDBManager'; import {ActiveDoc} from 'app/server/lib/ActiveDoc'; import {ScopedSession} from 'app/server/lib/BrowserSession'; -import * as Comm from 'app/server/lib/Comm'; import {DocManager} from 'app/server/lib/DocManager'; import {ExternalStorage} from 'app/server/lib/ExternalStorage'; import {GristServer} from 'app/server/lib/GristServer'; import {IBilling} from 'app/server/lib/IBilling'; -import {ILoginSession} from 'app/server/lib/ILoginSession'; import {INotifier} from 'app/server/lib/INotifier'; import {ISandbox, ISandboxCreationOptions} from 'app/server/lib/ISandbox'; import {IShell} from 'app/server/lib/IShell'; export interface ICreate { - LoginSession(comm: Comm, sid: string, domain: string, scopeSession: ScopedSession): ILoginSession; + // A ScopedSession knows which user is logged in to an org. This method may be used to replace + // its behavior with stubs when logins aren't available. + adjustSession(scopedSession: ScopedSession): void; + Billing(dbManager: HomeDBManager, gristConfig: GristServer): IBilling; Notifier(dbManager: HomeDBManager, gristConfig: GristServer): INotifier; Shell(): IShell|undefined; diff --git a/app/server/lib/ILoginSession.ts b/app/server/lib/ILoginSession.ts deleted file mode 100644 index c180b428..00000000 --- a/app/server/lib/ILoginSession.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {UserProfile} from 'app/common/LoginSessionAPI'; -import {Client} from 'app/server/lib/Client'; - -export interface ILoginSession { - clients: Set; - getEmail(): Promise; - getSessionProfile(): Promise; - // Log out - clearSession(): Promise; - - // For testing only. If no email address, profile is wiped, otherwise it is set. - testSetProfile(profile: UserProfile|null): Promise; -} diff --git a/app/server/lib/Sessions.ts b/app/server/lib/Sessions.ts index b6cd3ee4..80f0b2da 100644 --- a/app/server/lib/Sessions.ts +++ b/app/server/lib/Sessions.ts @@ -1,17 +1,10 @@ import {ScopedSession} from 'app/server/lib/BrowserSession'; -import * as Comm from 'app/server/lib/Comm'; import {GristServer} from 'app/server/lib/GristServer'; import {cookieName, SessionStore} from 'app/server/lib/gristSessions'; -import {ILoginSession} from 'app/server/lib/ILoginSession'; import * as cookie from 'cookie'; import * as cookieParser from 'cookie-parser'; import {Request} from 'express'; -interface Session { - scopedSession: ScopedSession; - loginSession?: ILoginSession; -} - /** * * A collection of all the sessions relevant to this instance of Grist. @@ -21,8 +14,7 @@ interface Session { * from code related to websockets. * * The collection caches all existing interfaces to sessions. - * LoginSessions play an important role in standalone Grist and address - * end-to-end sharing concerns. ScopedSessions play an important role in + * ScopedSessions play an important role in * hosted Grist and address per-organization scoping of identity. * * TODO: now this is separated out, we could refactor to share sessions @@ -32,7 +24,7 @@ interface Session { * */ export class Sessions { - private _sessions = new Map(); + private _sessions = new Map(); constructor(private _sessionSecret: string, private _sessionStore: SessionStore, private _server: GristServer) { } @@ -41,7 +33,7 @@ export class Sessions { * Get the session id and organization from the request, and return the * identified session. */ - public getOrCreateSessionFromRequest(req: Request): Session { + public getOrCreateSessionFromRequest(req: Request): ScopedSession { const sid = this.getSessionIdFromRequest(req); const org = (req as any).org; if (!sid) { throw new Error("session not found"); } @@ -51,29 +43,16 @@ export class Sessions { /** * Get or create a session given the session id and organization name. */ - public getOrCreateSession(sid: string, domain: string, userSelector: string): Session { + public getOrCreateSession(sid: string, domain: string, userSelector: string): ScopedSession { const key = this._getSessionOrgKey(sid, domain, userSelector); if (!this._sessions.has(key)) { const scopedSession = new ScopedSession(sid, this._sessionStore, domain, userSelector); - this._sessions.set(key, {scopedSession}); + this._server.create.adjustSession(scopedSession); + this._sessions.set(key, scopedSession); } return this._sessions.get(key)!; } - /** - * Access a LoginSession interface, creating it if necessary. For creation, - * purposes, Comm, and optionally InstanceManager objects are needed. - * - */ - public getOrCreateLoginSession(sid: string, domain: string, comm: Comm, - userSelector: string): ILoginSession { - const sess = this.getOrCreateSession(sid, domain, userSelector); - if (!sess.loginSession) { - sess.loginSession = this._server.create.LoginSession(comm, sid, domain, sess.scopedSession); - } - return sess.loginSession; - } - /** * Returns the sessionId from the signed grist cookie. */ diff --git a/app/server/lib/TestingHooks.ts b/app/server/lib/TestingHooks.ts index 980a684c..987dc0f5 100644 --- a/app/server/lib/TestingHooks.ts +++ b/app/server/lib/TestingHooks.ts @@ -73,8 +73,8 @@ export class TestingHooks implements ITestingHooks { public async setLoginSessionProfile(gristSidCookie: string, profile: UserProfile|null, org?: string): Promise { log.info("TestingHooks.setLoginSessionProfile called with", gristSidCookie, profile, org); const sessionId = this._comm.getSessionIdFromCookie(gristSidCookie); - const loginSession = this._comm.getOrCreateSession(sessionId, {org}); - return await loginSession.testSetProfile(profile); + const scopedSession = this._comm.getOrCreateSession(sessionId, {org}); + return await scopedSession.updateUserProfile(profile); } public async setServerVersion(version: string|null): Promise { diff --git a/package.json b/package.json index ad933990..e386725b 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "chai": "4.2.0", "chai-as-promised": "7.1.1", "mocha": "5.2.0", - "mocha-webdriver": "0.2.8", + "mocha-webdriver": "0.2.9", "moment-locales-webpack-plugin": "^1.2.0", "nodemon": "^2.0.4", "selenium-webdriver": "3.6.0", diff --git a/stubs/app/server/lib/LoginSession.ts b/stubs/app/server/lib/LoginSession.ts deleted file mode 100644 index 0217d3d0..00000000 --- a/stubs/app/server/lib/LoginSession.ts +++ /dev/null @@ -1,22 +0,0 @@ -import {UserProfile} from 'app/common/LoginSessionAPI'; -import {Client} from 'app/server/lib/Client'; -import {ILoginSession} from 'app/server/lib/ILoginSession'; - -export class LoginSession implements ILoginSession { - public clients: Set = new Set(); - public async getEmail() { - return process.env.GRIST_DEFAULT_EMAIL || 'anon@getgrist.com'; - } - public async getSessionProfile() { - return { - name: await this.getEmail(), - email: await this.getEmail(), - }; - } - public async clearSession(): Promise { - // do nothing - } - public async testSetProfile(profile: UserProfile|null): Promise { - // do nothing - } -} diff --git a/stubs/app/server/lib/create.ts b/stubs/app/server/lib/create.ts index f23ad3dd..12864004 100644 --- a/stubs/app/server/lib/create.ts +++ b/stubs/app/server/lib/create.ts @@ -1,14 +1,16 @@ import {ActiveDoc} from 'app/server/lib/ActiveDoc'; import {ICreate} from 'app/server/lib/ICreate'; -import {LoginSession} from 'app/server/lib/LoginSession'; +import {ScopedSession} from 'app/server/lib/BrowserSession'; import {NSandboxCreator} from 'app/server/lib/NSandbox'; // Use raw python - update when pynbox or other solution is set up for core. const sandboxCreator = new NSandboxCreator('unsandboxed'); export const create: ICreate = { - LoginSession() { - return new LoginSession(); + adjustSession(scopedSession: ScopedSession): void { + const email = process.env.GRIST_DEFAULT_EMAIL || 'anon@getgrist.com'; + const profile = {email, name: email}; + scopedSession.getSessionProfile = async () => profile; }, Billing() { return { diff --git a/yarn.lock b/yarn.lock index 33c4c377..a0ef6ed3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2471,7 +2471,7 @@ fast-safe-stringify@^2.0.7: resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== -fd-slicer@1.1.0, fd-slicer@~1.1.0: +fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= @@ -3135,14 +3135,14 @@ http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" -http-errors@~1.7.0: - version "1.7.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" - integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== +http-errors@~1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.0.tgz#75d1bbe497e1044f51e4ee9e704a62f28d336507" + integrity sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A== dependencies: depd "~1.1.2" inherits "2.0.4" - setprototypeof "1.1.1" + setprototypeof "1.2.0" statuses ">= 1.5.0 < 2" toidentifier "1.0.0" @@ -4231,10 +4231,10 @@ mkdirp@^1.0.3: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mocha-webdriver@0.2.8: - version "0.2.8" - resolved "https://registry.yarnpkg.com/mocha-webdriver/-/mocha-webdriver-0.2.8.tgz#d9d0b75476abbff5eb08fe18dd81cfab3addfba5" - integrity sha512-1yK7vM7JxNCb3aWLrJRqLQq2Owz/ldKrmoIACJtNHMr1z/v60oi0LKscP8Bc8iSbYTqBmi7c9T6tG5IjQLk9zg== +mocha-webdriver@0.2.9: + version "0.2.9" + resolved "https://registry.yarnpkg.com/mocha-webdriver/-/mocha-webdriver-0.2.9.tgz#1393aa57eb732fd93495064c90b4f9283b601065" + integrity sha512-WNr7Yq1uhcvLYyjDChKvW9y2hD8DnzJ0Av/LheZtiS0/6MQtPvxx1XO7OtdnxiS6my9gKg4kRe0944dggLGEFg== dependencies: chai "^4.1.2" chai-as-promised "^7.1.1" @@ -4380,14 +4380,13 @@ ms@2.1.2, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -multiparty@4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/multiparty/-/multiparty-4.2.1.tgz#d9b6c46d8b8deab1ee70c734b0af771dd46e0b13" - integrity sha512-AvESCnNoQlZiOfP9R4mxN8M9csy2L16EIbWIkt3l4FuGti9kXBS8QVzlfyg4HEnarJhrzZilgNFlZtqmoiAIIA== +multiparty@4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/multiparty/-/multiparty-4.2.2.tgz#bee5fb5737247628d39dab4979ffd6d57bf60ef6" + integrity sha512-NtZLjlvsjcoGrzojtwQwn/Tm90aWJ6XXtPppYF4WmOk/6ncdwMMKggFY2NlRRN9yiCEIVxpOfPWahVEG2HAG8Q== dependencies: - fd-slicer "1.1.0" - http-errors "~1.7.0" - safe-buffer "5.1.2" + http-errors "~1.8.0" + safe-buffer "5.2.1" uid-safe "2.1.5" mz@^2.4.0: @@ -5590,7 +5589,7 @@ safe-buffer@5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -5724,10 +5723,10 @@ setprototypeof@1.1.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== -setprototypeof@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" - integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== sha.js@^2.4.0, sha.js@^2.4.8, sha.js@~2.4.4: version "2.4.11"