This commit is contained in:
parent
a8fb92b367
commit
2f342b31f7
@ -24,7 +24,7 @@
|
||||
"scripts": {
|
||||
"lint": "eslint . --ext .js,.jsx,.ts,.tsx,.vue,.mjs",
|
||||
"test": "nyc jasmine-ts --config=\"jasmine.json\"",
|
||||
"mutate": "stryker run --fileLogLevel trace --logLevel debug",
|
||||
"mutate": "stryker run",
|
||||
"compile": "tsc"
|
||||
},
|
||||
"files": [
|
||||
@ -39,7 +39,7 @@
|
||||
"middleware"
|
||||
],
|
||||
"dependencies": {
|
||||
"@sapphirecode/crypto-helper": "^1.2.0",
|
||||
"@sapphirecode/crypto-helper": "^1.2.2",
|
||||
"@sapphirecode/utilities": "^1.8.8"
|
||||
},
|
||||
"engines": {
|
||||
|
23
test/Helper.ts
Normal file
23
test/Helper.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import http from 'http';
|
||||
|
||||
class Response extends http.IncomingMessage {
|
||||
body?: string;
|
||||
}
|
||||
|
||||
export
|
||||
function get (
|
||||
headers: http.OutgoingHttpHeaders = {}
|
||||
): Promise<Response> {
|
||||
return new Promise ((resolve) => {
|
||||
http.get ('http://localhost:3000', { headers }, (res: Response) => {
|
||||
let body = '';
|
||||
res.on ('data', (d) => {
|
||||
body += d;
|
||||
});
|
||||
res.on ('end', () => {
|
||||
res.body = body;
|
||||
resolve (res);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
@ -5,15 +5,13 @@
|
||||
* Created by Timo Hocker <timo@scode.ovh>, December 2020
|
||||
*/
|
||||
|
||||
import { hash_sha512 } from '@sapphirecode/crypto-helper';
|
||||
import auth from '../../lib/Authority';
|
||||
import bl from '../../lib/Blacklist';
|
||||
|
||||
function modify_signature (signature: string): string {
|
||||
const dec = decodeURIComponent (signature)
|
||||
.split ('.');
|
||||
dec[1] = hash_sha512 ('', '');
|
||||
return encodeURIComponent (dec.join ('.'));
|
||||
const dec = signature.split ('.');
|
||||
dec[1] = '';
|
||||
return dec.join ('.');
|
||||
}
|
||||
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
|
@ -2,42 +2,7 @@ import http from 'http';
|
||||
import gateway from '../../lib/Gateway';
|
||||
import authority from '../../lib/Authority';
|
||||
import blacklist from '../../lib/Blacklist';
|
||||
|
||||
interface Response {
|
||||
body: string
|
||||
status?: number
|
||||
location?: string
|
||||
}
|
||||
|
||||
|
||||
function get (
|
||||
url: string,
|
||||
token?: string,
|
||||
mode = 0
|
||||
): Promise<Response> {
|
||||
const headers: http.OutgoingHttpHeaders = {};
|
||||
if (mode === 1)
|
||||
headers.cookie = `cookie_jar=${token}`;
|
||||
else if (mode === 0 && typeof token === 'string')
|
||||
headers.authorization = `Bearer ${token}`;
|
||||
else if (mode === 2)
|
||||
headers.authorization = `Basic ${token}`;
|
||||
return new Promise ((resolve) => {
|
||||
http.get (url, { headers }, (res) => {
|
||||
let body = '';
|
||||
res.on ('data', (d) => {
|
||||
body += d;
|
||||
});
|
||||
res.on ('end', () => {
|
||||
resolve ({
|
||||
body,
|
||||
status: res.statusCode,
|
||||
location: res.headers.location
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
import { get } from '../Helper';
|
||||
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
describe ('gateway', () => {
|
||||
@ -76,17 +41,17 @@ describe ('gateway', () => {
|
||||
});
|
||||
|
||||
it ('should redirect any unauthorized request', async () => {
|
||||
const resp = await get ('http://localhost:3000');
|
||||
expect (resp.status)
|
||||
const resp = await get ();
|
||||
expect (resp.statusCode)
|
||||
.toEqual (302);
|
||||
expect (resp.location)
|
||||
expect (resp.headers.location)
|
||||
.toEqual ('http://localhost/auth');
|
||||
});
|
||||
|
||||
it ('should allow a valid access token', async () => {
|
||||
const token = authority.sign ('access_token', 60);
|
||||
const resp = await get ('http://localhost:3000', token.signature);
|
||||
expect (resp.status)
|
||||
const resp = await get ({ authorization: `Bearer ${token.signature}` });
|
||||
expect (resp.statusCode)
|
||||
.toEqual (200);
|
||||
expect (resp.body)
|
||||
.toEqual ('passed');
|
||||
@ -94,8 +59,8 @@ describe ('gateway', () => {
|
||||
|
||||
it ('should allow a valid access token using cookies', async () => {
|
||||
const token = authority.sign ('access_token', 60);
|
||||
const resp = await get ('http://localhost:3000', token.signature, 1);
|
||||
expect (resp.status)
|
||||
const resp = await get ({ cookie: `cookie_jar=${token.signature}` });
|
||||
expect (resp.statusCode)
|
||||
.toEqual (200);
|
||||
expect (resp.body)
|
||||
.toEqual ('passed');
|
||||
@ -105,55 +70,55 @@ describe ('gateway', () => {
|
||||
const token = authority.sign ('access_token', 60);
|
||||
jasmine.clock ()
|
||||
.tick (70000);
|
||||
const resp = await get ('http://localhost:3000', token.signature);
|
||||
expect (resp.status)
|
||||
const resp = await get ({ authorization: `Bearer ${token.signature}` });
|
||||
expect (resp.statusCode)
|
||||
.toEqual (302);
|
||||
expect (resp.location)
|
||||
expect (resp.headers.location)
|
||||
.toEqual ('http://localhost/auth');
|
||||
});
|
||||
|
||||
it ('should reject a blacklisted access token', async () => {
|
||||
const token = authority.sign ('access_token', 60);
|
||||
blacklist.add_signature (token.id);
|
||||
const resp = await get ('http://localhost:3000', token.signature);
|
||||
expect (resp.status)
|
||||
const resp = await get ({ authorization: `Bearer ${token.signature}` });
|
||||
expect (resp.statusCode)
|
||||
.toEqual (302);
|
||||
expect (resp.location)
|
||||
expect (resp.headers.location)
|
||||
.toEqual ('http://localhost/auth');
|
||||
});
|
||||
|
||||
it ('should reject any refresh_token', async () => {
|
||||
const token = authority.sign ('refresh_token', 60);
|
||||
const resp = await get ('http://localhost:3000', token.signature);
|
||||
expect (resp.status)
|
||||
const resp = await get ({ authorization: `Bearer ${token.signature}` });
|
||||
expect (resp.statusCode)
|
||||
.toEqual (302);
|
||||
expect (resp.location)
|
||||
expect (resp.headers.location)
|
||||
.toEqual ('http://localhost/auth');
|
||||
});
|
||||
|
||||
it ('should reject any part_token', async () => {
|
||||
const token = authority.sign ('part_token', 60);
|
||||
const resp = await get ('http://localhost:3000', token.signature);
|
||||
expect (resp.status)
|
||||
const resp = await get ({ authorization: `Bearer ${token.signature}` });
|
||||
expect (resp.statusCode)
|
||||
.toEqual (302);
|
||||
expect (resp.location)
|
||||
expect (resp.headers.location)
|
||||
.toEqual ('http://localhost/auth');
|
||||
});
|
||||
|
||||
it ('should reject any noname token', async () => {
|
||||
const token = authority.sign ('none', 60);
|
||||
const resp = await get ('http://localhost:3000', token.signature);
|
||||
expect (resp.status)
|
||||
const resp = await get ({ authorization: `Bearer ${token.signature}` });
|
||||
expect (resp.statusCode)
|
||||
.toEqual (302);
|
||||
expect (resp.location)
|
||||
expect (resp.headers.location)
|
||||
.toEqual ('http://localhost/auth');
|
||||
});
|
||||
|
||||
it ('should reject non-bearer auth', async () => {
|
||||
const resp = await get ('http://localhost:3000', 'foo:bar', 2);
|
||||
expect (resp.status)
|
||||
const resp = await get ({ authorization: 'Basic foo:bar' });
|
||||
expect (resp.statusCode)
|
||||
.toEqual (302);
|
||||
expect (resp.location)
|
||||
expect (resp.headers.location)
|
||||
.toEqual ('http://localhost/auth');
|
||||
});
|
||||
});
|
||||
|
117
yarn.lock
117
yarn.lock
@ -329,17 +329,19 @@
|
||||
"@nodelib/fs.scandir" "2.1.4"
|
||||
fastq "^1.6.0"
|
||||
|
||||
"@sapphirecode/crypto-helper@^1.2.0":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@sapphirecode/crypto-helper/-/crypto-helper-1.2.1.tgz#d60277b982b7bd023267488e9fb454f41d6c8a30"
|
||||
integrity sha512-qN3q4f+/Q3gjxbVG9/ZGTqC0hP3trxdbePFI08z8a95bgJ45Inv8ieDr8SJRaX/gylIL/DvKeW/wTXdeSnDKCw==
|
||||
"@sapphirecode/crypto-helper@^1.2.2":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@sapphirecode/crypto-helper/-/crypto-helper-1.2.2.tgz#518c763ed069f78850a05c92326672ec8f398ef5"
|
||||
integrity sha512-OOHKrbD+YHriqkLk8snOJWTskmptmImTZH62Q09XeHbit09zSMUSk+B5c5eyPr80L1s7SSETqfNx7iyIAWKQzg==
|
||||
dependencies:
|
||||
"@sapphirecode/encoding-helper" "^1.0.38"
|
||||
"@sapphirecode/encoding-helper" "^1.1.0"
|
||||
|
||||
"@sapphirecode/encoding-helper@^1.0.38":
|
||||
version "1.0.51"
|
||||
resolved "https://registry.yarnpkg.com/@sapphirecode/encoding-helper/-/encoding-helper-1.0.51.tgz#1d07793c995d12459ce2518bd3e6663ca4b11f69"
|
||||
integrity sha512-aYaGbmaI33yGxfF1MP4nQmwREoSwN9EM0u1G6y0UuuryaObd3HzZIWE2N7hhVoHRLWUsUwneRGVhQGaLTwyd3A==
|
||||
"@sapphirecode/encoding-helper@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@sapphirecode/encoding-helper/-/encoding-helper-1.1.0.tgz#938c8e712d540d028a7f6337551b2f0f69bb48d1"
|
||||
integrity sha512-8jhIfjZEo31xhlftVN7XMFcWu/bWO+4T8U8+jnMKs55kATegDHM25ba+lUXekXlmpx8Mi0eqYYt0/lVz5ykiRQ==
|
||||
dependencies:
|
||||
b58 "^4.0.3"
|
||||
|
||||
"@sapphirecode/eslint-config-es6@^1.1.1":
|
||||
version "1.2.2"
|
||||
@ -467,60 +469,60 @@
|
||||
integrity sha512-vwX+/ija9xKc/z9VqMCdbf4WYcMTGsI0I/L/6shIF3qXURxZOhPQlPRHtjTpiNhAwn0paMJzlOQqw6mAGEQnTA==
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^4.1.0":
|
||||
version "4.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.11.0.tgz#bc6c1e4175c0cf42083da4314f7931ad12f731cc"
|
||||
integrity sha512-x4arJMXBxyD6aBXLm3W7mSDZRiABzy+2PCLJbL7OPqlp53VXhaA1HKK7R2rTee5OlRhnUgnp8lZyVIqjnyPT6g==
|
||||
version "4.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.11.1.tgz#7579c6d17ad862154c10bc14b40e5427b729e209"
|
||||
integrity sha512-fABclAX2QIEDmTMk6Yd7Muv1CzFLwWM4505nETzRHpP3br6jfahD9UUJkhnJ/g2m7lwfz8IlswcwGGPGiq9exw==
|
||||
dependencies:
|
||||
"@typescript-eslint/experimental-utils" "4.11.0"
|
||||
"@typescript-eslint/scope-manager" "4.11.0"
|
||||
"@typescript-eslint/experimental-utils" "4.11.1"
|
||||
"@typescript-eslint/scope-manager" "4.11.1"
|
||||
debug "^4.1.1"
|
||||
functional-red-black-tree "^1.0.1"
|
||||
regexpp "^3.0.0"
|
||||
semver "^7.3.2"
|
||||
tsutils "^3.17.1"
|
||||
|
||||
"@typescript-eslint/experimental-utils@4.11.0":
|
||||
version "4.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.11.0.tgz#d1a47cc6cfe1c080ce4ead79267574b9881a1565"
|
||||
integrity sha512-1VC6mSbYwl1FguKt8OgPs8xxaJgtqFpjY/UzUYDBKq4pfQ5lBvN2WVeqYkzf7evW42axUHYl2jm9tNyFsb8oLg==
|
||||
"@typescript-eslint/experimental-utils@4.11.1":
|
||||
version "4.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.11.1.tgz#2dad3535b878c25c7424e40bfa79d899f3f485bc"
|
||||
integrity sha512-mAlWowT4A6h0TC9F+J5pdbEhjNiEMO+kqPKQ4sc3fVieKL71dEqfkKgtcFVSX3cjSBwYwhImaQ/mXQF0oaI38g==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.3"
|
||||
"@typescript-eslint/scope-manager" "4.11.0"
|
||||
"@typescript-eslint/types" "4.11.0"
|
||||
"@typescript-eslint/typescript-estree" "4.11.0"
|
||||
"@typescript-eslint/scope-manager" "4.11.1"
|
||||
"@typescript-eslint/types" "4.11.1"
|
||||
"@typescript-eslint/typescript-estree" "4.11.1"
|
||||
eslint-scope "^5.0.0"
|
||||
eslint-utils "^2.0.0"
|
||||
|
||||
"@typescript-eslint/parser@^4.1.0":
|
||||
version "4.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.11.0.tgz#1dd3d7e42708c10ce9f3aa64c63c0ab99868b4e2"
|
||||
integrity sha512-NBTtKCC7ZtuxEV5CrHUO4Pg2s784pvavc3cnz6V+oJvVbK4tH9135f/RBP6eUA2KHiFKAollSrgSctQGmHbqJQ==
|
||||
version "4.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.11.1.tgz#981e18de2e019d6ca312596615f92e8f6f6598ed"
|
||||
integrity sha512-BJ3jwPQu1jeynJ5BrjLuGfK/UJu6uwHxJ/di7sanqmUmxzmyIcd3vz58PMR7wpi8k3iWq2Q11KMYgZbUpRoIPw==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "4.11.0"
|
||||
"@typescript-eslint/types" "4.11.0"
|
||||
"@typescript-eslint/typescript-estree" "4.11.0"
|
||||
"@typescript-eslint/scope-manager" "4.11.1"
|
||||
"@typescript-eslint/types" "4.11.1"
|
||||
"@typescript-eslint/typescript-estree" "4.11.1"
|
||||
debug "^4.1.1"
|
||||
|
||||
"@typescript-eslint/scope-manager@4.11.0":
|
||||
version "4.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.11.0.tgz#2d906537db8a3a946721699e4fc0833810490254"
|
||||
integrity sha512-6VSTm/4vC2dHM3ySDW9Kl48en+yLNfVV6LECU8jodBHQOhO8adAVizaZ1fV0QGZnLQjQ/y0aBj5/KXPp2hBTjA==
|
||||
"@typescript-eslint/scope-manager@4.11.1":
|
||||
version "4.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.11.1.tgz#72dc2b60b0029ab0888479b12bf83034920b4b69"
|
||||
integrity sha512-Al2P394dx+kXCl61fhrrZ1FTI7qsRDIUiVSuN6rTwss6lUn8uVO2+nnF4AvO0ug8vMsy3ShkbxLu/uWZdTtJMQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "4.11.0"
|
||||
"@typescript-eslint/visitor-keys" "4.11.0"
|
||||
"@typescript-eslint/types" "4.11.1"
|
||||
"@typescript-eslint/visitor-keys" "4.11.1"
|
||||
|
||||
"@typescript-eslint/types@4.11.0":
|
||||
version "4.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.11.0.tgz#86cf95e7eac4ccfd183f9fcf1480cece7caf4ca4"
|
||||
integrity sha512-XXOdt/NPX++txOQHM1kUMgJUS43KSlXGdR/aDyEwuAEETwuPt02Nc7v+s57PzuSqMbNLclblQdv3YcWOdXhQ7g==
|
||||
"@typescript-eslint/types@4.11.1":
|
||||
version "4.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.11.1.tgz#3ba30c965963ef9f8ced5a29938dd0c465bd3e05"
|
||||
integrity sha512-5kvd38wZpqGY4yP/6W3qhYX6Hz0NwUbijVsX2rxczpY6OXaMxh0+5E5uLJKVFwaBM7PJe1wnMym85NfKYIh6CA==
|
||||
|
||||
"@typescript-eslint/typescript-estree@4.11.0":
|
||||
version "4.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.11.0.tgz#1144d145841e5987d61c4c845442a24b24165a4b"
|
||||
integrity sha512-eA6sT5dE5RHAFhtcC+b5WDlUIGwnO9b0yrfGa1mIOIAjqwSQCpXbLiFmKTdRbQN/xH2EZkGqqLDrKUuYOZ0+Hg==
|
||||
"@typescript-eslint/typescript-estree@4.11.1":
|
||||
version "4.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.11.1.tgz#a4416b4a65872a48773b9e47afabdf7519eb10bc"
|
||||
integrity sha512-tC7MKZIMRTYxQhrVAFoJq/DlRwv1bnqA4/S2r3+HuHibqvbrPcyf858lNzU7bFmy4mLeIHFYr34ar/1KumwyRw==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "4.11.0"
|
||||
"@typescript-eslint/visitor-keys" "4.11.0"
|
||||
"@typescript-eslint/types" "4.11.1"
|
||||
"@typescript-eslint/visitor-keys" "4.11.1"
|
||||
debug "^4.1.1"
|
||||
globby "^11.0.1"
|
||||
is-glob "^4.0.1"
|
||||
@ -528,12 +530,12 @@
|
||||
semver "^7.3.2"
|
||||
tsutils "^3.17.1"
|
||||
|
||||
"@typescript-eslint/visitor-keys@4.11.0":
|
||||
version "4.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.11.0.tgz#906669a50f06aa744378bb84c7d5c4fdbc5b7d51"
|
||||
integrity sha512-tRYKyY0i7cMk6v4UIOCjl1LhuepC/pc6adQqJk4Is3YcC6k46HvsV9Wl7vQoLbm9qADgeujiT7KdLrylvFIQ+A==
|
||||
"@typescript-eslint/visitor-keys@4.11.1":
|
||||
version "4.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.11.1.tgz#4c050a4c1f7239786e2dd4e69691436143024e05"
|
||||
integrity sha512-IrlBhD9bm4bdYcS8xpWarazkKXlE7iYb1HzRuyBP114mIaj5DJPo11Us1HgH60dTt41TCZXMaTCAW+OILIYPOg==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "4.11.0"
|
||||
"@typescript-eslint/types" "4.11.1"
|
||||
eslint-visitor-keys "^2.0.0"
|
||||
|
||||
acorn-jsx@^5.3.1:
|
||||
@ -656,11 +658,25 @@ astral-regex@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
|
||||
integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
|
||||
|
||||
b58@^4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/b58/-/b58-4.0.3.tgz#852a28d804b4a5a60685f9b51706315145a48c36"
|
||||
integrity sha512-VDtdiomm0ywbL8YzgevOZ9pcx6LuOZ3d9qYTPDcYUPf7dRYNA8wvK6epYy0FKMWIM5uaDwd3kWt1x+1S9scB1Q==
|
||||
dependencies:
|
||||
base-x "^3.0.2"
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
|
||||
base-x@^3.0.2:
|
||||
version "3.0.8"
|
||||
resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.8.tgz#1e1106c2537f0162e8b52474a557ebb09000018d"
|
||||
integrity sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==
|
||||
dependencies:
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
@ -2260,6 +2276,11 @@ rxjs@^6.6.0, rxjs@~6.6.0:
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
safe-buffer@^5.0.1:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
|
||||
safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
|
Loading…
x
Reference in New Issue
Block a user