From 9affb478d313bb2f611a25744ffe9eb116a3e551 Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Mon, 9 Aug 2021 08:36:44 +0200 Subject: [PATCH 01/33] Move files --- lib/{ => pdp}/azf.js | 0 lib/{ => pdp}/idm.js | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename lib/{ => pdp}/azf.js (100%) rename lib/{ => pdp}/idm.js (100%) diff --git a/lib/azf.js b/lib/pdp/azf.js similarity index 100% rename from lib/azf.js rename to lib/pdp/azf.js diff --git a/lib/idm.js b/lib/pdp/idm.js similarity index 100% rename from lib/idm.js rename to lib/pdp/idm.js From ae0119ba1e5f87872e8cf97623e2ca7e5fba04bb Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Mon, 9 Aug 2021 08:40:49 +0200 Subject: [PATCH 02/33] - Format Codebase - Add copyrights - Replace in-memory object with node-cache - Add Got, remove xmlhttptrequest - promisify requests - Reduce cyclometric complexity --- bin/healthcheck.js | 21 +- bin/www | 145 +++++----- controllers/root.js | 377 ++++++++++---------------- lib/HTTPClient.js | 117 --------- lib/access_functions.js | 125 +++++++++ lib/authorization_functions.js | 107 ++++++++ lib/cache.js | 89 +++++++ lib/config_service.js | 14 +- lib/payload.js | 106 ++++++++ lib/pdp/azf.js | 467 +++++++++++++-------------------- lib/pdp/idm.js | 412 ++++++++++++----------------- lib/pdp/ishare.js | 94 +++++++ lib/pdp/xacml.js | 102 +++++++ 13 files changed, 1203 insertions(+), 973 deletions(-) delete mode 100644 lib/HTTPClient.js create mode 100644 lib/access_functions.js create mode 100644 lib/authorization_functions.js create mode 100644 lib/cache.js create mode 100644 lib/payload.js create mode 100644 lib/pdp/ishare.js create mode 100644 lib/pdp/xacml.js diff --git a/bin/healthcheck.js b/bin/healthcheck.js index 5d20833..fb0ec59 100644 --- a/bin/healthcheck.js +++ b/bin/healthcheck.js @@ -1,5 +1,12 @@ #!/usr/bin/env node +/* + * Copyright 2021 - Universidad Politécnica de Madrid. + * + * This file is part of PEP-Proxy + * + */ + const http = require('http'); const config = require('../config'); const http_code = process.env.HEALTHCHECK_CODE || 200; @@ -8,19 +15,17 @@ function to_array(env, default_value) { return env !== undefined ? env.split(',') : default_value; } -const public_paths = to_array(process.env.PEP_PROXY_PUBLIC_PATHS, [ - '/iot/about', -]); +const public_paths = to_array(process.env.PEP_PROXY_PUBLIC_PATHS, ['/iot/about']); const options = { host: 'localhost', port: process.env.PEP_PROXY_PORT || config.port, timeout: 2000, method: 'GET', - path: public_paths[0] || '/', + path: public_paths[0] || '/' }; -const request = http.request(options, result => { +const request = http.request(options, (result) => { // eslint-disable-next-line no-console console.info(`Performed health check, result ${result.statusCode}`); if (result.statusCode === http_code) { @@ -30,11 +35,9 @@ const request = http.request(options, result => { } }); -request.on('error', err => { +request.on('error', (err) => { // eslint-disable-next-line no-console - console.error( - `An error occurred while performing health check, error: ${err}` - ); + console.error(`An error occurred while performing health check, error: ${err}`); process.exit(1); }); diff --git a/bin/www b/bin/www index d0ac0c2..12c42be 100644 --- a/bin/www +++ b/bin/www @@ -1,20 +1,25 @@ -const config_service = require('../lib/config_service.js'); -config_service.set_config(require('../config.js'), true); -const config = config_service.get_config(); -const cors = require('cors'); +#!/usr/bin/env node + +/* + * Copyright 2021 - Universidad Politécnica de Madrid. + * + * This file is part of PEP-Proxy + * + */ -const fs = require('fs'); -const https = require('https'); -const Root = require('../controllers/root').Root; -const IDM = require('../lib/idm.js').IDM; +const config_service = require('../lib/config_service'); +config_service.set_config(require('../config'), true); +const config = config_service.get_config(); +const IDM = require('../lib/pdp/idm'); +const app = require('../app'); +const Authorize = require('../lib/authorization_functions'); const errorhandler = require('errorhandler'); config.azf = config.azf || {}; config.https = config.https || {}; -const logger = require('morgan'); -const debug = require('debug')('pep-proxy:app'); -const express = require('express'); +const debug = require('debug')('pep-proxy:www'); + const os = require('os'); const cluster = require('cluster'); const clusterWorkerSize = os.cpus().length; @@ -24,88 +29,60 @@ process.on('uncaughtException', function (err) { }); process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; -let port = config.pep_port || 80; -if (config.https.enabled) { - port = config.https.port || 443; -} - -function start_server() { - const app = express(); - - // Set logs in development - if (config.debug) { - app.use(logger('dev')); - } - - app.use(function (req, res, next) { - const bodyChunks = []; - req.on('data', function (chunk) { - bodyChunks.push(chunk); - }); - - req.on('end', function () { - if (bodyChunks.length > 0) { - req.body = Buffer.concat(bodyChunks); - } - next(); - }); - }); - - app.disable('x-powered-by'); - app.use(errorhandler({ log: debug })); - app.use(cors(config.cors)); - app.set('port', port); - - for (const p in config.public_paths) { - debug('Public paths', config.public_paths[p]); - app.all(config.public_paths[p], Root.public); +function logConfig (idm_config) { + if (idm_config) { + debug('IDM authorization configuration: ' + config.authorization.pdp); + debug(' + Authzforce enabled: ' + idm_config.authzforce); + switch (idm_config.level) { + case 'payload': + debug(' + Authorization rules allowed: HTTP Verb+Resource and Payload'); + break; + case 'advanced': + debug(' + Authorization rules allowed: HTTP Verb+Resource and Advanced'); + break; + default: + debug(' + Authorization rules allowed: HTTP Verb+Resource'); + break; + } } +}; - app.all('/*', Root.pep); - - if (config.https.enabled === true) { - const options = { - key: fs.readFileSync(config.https.key_file), - cert: fs.readFileSync(config.https.cert_file) - }; - - https - .createServer(options, function (req, res) { - app.handle(req, res); - }) - .listen(app.get('port')); - } else { - app.listen(app.get('port')); - } -} +/** + * Check that the IDM is responding and the PEP is recognized within the IDM + * @return an auth token representing the PEP itself to be used in subsequent requests + */ function connect() { let retry = 20; return new Promise((resolve, reject) => { - const connect_with_retry = () => { - IDM.authenticate( - (token) => { - debug('Success authenticating PEP proxy.'); - resolve(token); - }, - (status, e) => { - debug('Error in IDM communication', e); - retry--; - if (retry === 0) { - reject(e); - } else { - debug('retry after 5 seconds.'); - //eslint-disable-next-line snakecase/snakecase - setTimeout(connect_with_retry, 5000); - } + const connect_with_retry = async () => { + try { + await IDM.checkConnectivity(); + debug('IDM is now available - requesting PEP authentication'); + + IDM.authenticatePEP() + .then((response) => { + logConfig(response.config); + return resolve(response.pepToken); + }) + .catch((error) => { + return reject('IDM rejected PEP authentication: ' + error.message); + }); + } catch (e) { + debug(e.message); + retry--; + if (retry === 0) { + return reject('IDM is not available. Giving up after 20 attempts'); } - ); + debug('retry after 5 seconds.'); + //eslint-disable-next-line snakecase/snakecase + setTimeout(connect_with_retry, 5000); + } }; connect_with_retry(); }); } -debug('Starting PEP proxy in port ' + port + '. IdM authentication ...'); connect().then( (token) => { debug('Success authenticating PEP proxy. Proxy Auth-token: ', token); @@ -115,14 +92,14 @@ connect().then( cluster.fork(); } } else { - start_server(); + app.start_server(token, config); } } else { - start_server(); + app.start_server(token,config); } }, (err) => { - debug('Error found after [%d] attempts: %s', 20, err); + debug(err); process.exit(1); } ); diff --git a/controllers/root.js b/controllers/root.js index fe129e9..6fc892b 100644 --- a/controllers/root.js +++ b/controllers/root.js @@ -1,244 +1,151 @@ +/* + * Copyright 2021 - Universidad Politécnica de Madrid. + * + * This file is part of PEP-Proxy + * + */ + const config_service = require('../lib/config_service.js'); -const config = config_service.get_config(); -const proxy = require('../lib/HTTPClient.js'); -const IDM = require('../lib/idm.js').IDM; -const AZF = require('../lib/azf.js').AZF; +let config; +const IDM = require('../lib/pdp/idm.js'); const jsonwebtoken = require('jsonwebtoken'); +const access = require('../lib/access_functions.js'); +const authorize = require('../lib/authorization_functions.js').authorize; const debug = require('debug')('pep-proxy:root'); -const Root = (function() { - //{token: {userInfo: {}, date: Date, verb1: [res1, res2, ..], verb2: [res3, res4, ...]}} - const tokensCache = {}; - - const pep = function(req, res) { - const tokenHeader = req.headers.authorization; - let authToken = tokenHeader - ? tokenHeader.split('Bearer ')[1] - : req.headers['x-auth-token']; - - if (authToken === undefined && req.headers.authorization !== undefined) { - const headerAuth = req.headers.authorization.split(' ')[1]; - authToken = new Buffer(headerAuth, 'base64').toString(); - } - - const organizationToken = req.headers[config.organizations.header] - ? req.headers[config.organizations.header] - : null; - - if (authToken === undefined) { - debug('Auth-token not found in request header'); - const authHeader = 'IDM uri = ' + config.idm_host; - res.set('WWW-Authenticate', authHeader); - res.status(401).send('Auth-token not found in request header'); - } else { - if (config.magic_key && config.magic_key === authToken) { - const options = { - host: config.app.host, - port: config.app.port, - path: req.url, - method: req.method, - headers: proxy.getClientIp(req, req.headers), - }; - const protocol = config.app.ssl ? 'https' : 'http'; - proxy.sendData(protocol, options, req.body, res); - return; - } - - let action; - let resource; - let authzforce; - - if (config.authorization.enabled) { - if (config.authorization.pdp === 'authzforce') { - authzforce = true; - } else { - action = req.method; - resource = req.path; - } - } - - if (config.pep.token.secret) { - jsonwebtoken.verify(authToken, config.pep.token.secret, function( - err, - userInfo - ) { - if (err) { - if (err.name === 'TokenExpiredError') { - res.status(401).send('Invalid token: jwt token has expired'); - } else { - debug('Error in JWT ', err.message); - debug('Or JWT secret bad configured'); - debug('Validate Token with Keyrock'); - checkToken( - req, - res, - authToken, - null, - action, - resource, - authzforce, - organizationToken - ); - } - } else if (config.authorization.enabled) { - if (config.authorization.pdp === 'authzforce') { - authorizeAzf(req, res, authToken, userInfo); - } else if (config.authorization.pdp === 'idm') { - checkToken( - req, - res, - authToken, - userInfo.exp, - action, - resource, - authzforce, - organizationToken - ); - } else { - res.status(401).send('User access-token not authorized'); - } - } else { - setHeaders(req, userInfo); - redirRequest(req, res, userInfo); - } - }); +/** + * Authenticate the JWT token and then authorize the action if necessary. + * + * @param req - the incoming request + * @param res - the response to return + * @param tokens - a collection of auth tokens to use for this verification + */ +function validateAccessJWT(req, res, tokens) { + jsonwebtoken.verify(tokens.authToken, config.pep.token.secret, function (err, userInfo) { + if (err) { + if (err.name === 'TokenExpiredError') { + access.deny(res, 'Invalid token: jwt token has expired'); } else { - checkToken( - req, - res, - authToken, - null, - action, - resource, - authzforce, - organizationToken - ); + debug('Error in JWT ', err.message); + debug('Or JWT secret misconfigured'); + debug('Validate Token with Keyrock'); + // Fallback to AuthToken access validation + validateAccessIDM(req, res, tokens); + return; } } - }; - - const checkToken = function( - req, - res, - authToken, - jwtExpiration, - action, - resource, - authzforce, - organizationToken - ) { - IDM.checkToken( - authToken, - jwtExpiration, - action, - resource, - authzforce, - organizationToken, - function(userInfo) { - setHeaders(req, userInfo); - if (config.authorization.enabled) { - if (config.authorization.pdp === 'authzforce') { - authorizeAzf(req, res, authToken, userInfo); - } else if (userInfo.authorization_decision === 'Permit') { - redirRequest(req, res, userInfo); - } else { - res.status(401).send('User access-token not authorized'); - } - } else { - redirRequest(req, res, userInfo); - } - }, - function(status, e) { - if (status === 404 || status === 401) { - debug(e); - res.status(401).send(e); - } else { - debug('Error in IDM communication ', e); - res.status(503).send('Error in IDM communication'); - } - }, - tokensCache - ); - }; - - const setHeaders = function(req, userInfo) { - // Set headers with user information - req.headers['X-Nick-Name'] = userInfo.id ? userInfo.id : ''; - req.headers['X-Display-Name'] = userInfo.displayName - ? userInfo.displayName - : ''; - req.headers['X-Roles'] = userInfo.roles - ? JSON.stringify(userInfo.roles) - : []; - req.headers['X-Organizations'] = userInfo.organizations - ? JSON.stringify(userInfo.organizations) - : []; - req.headers['X-Eidas-Profile'] = userInfo.eidas_profile - ? JSON.stringify(userInfo.eidas_profile) - : {}; - req.headers['X-App-Id'] = userInfo.app_id; - }; - - const authorizeAzf = function(req, res, authToken, userInfo) { - // Check decision through authzforce - AZF.checkPermissions( - authToken, - userInfo, - req, - function() { - redirRequest(req, res, userInfo); - }, - function(status, e) { - if (status === 401) { - debug('User access-token not authorized: ', e); - res.status(401).send('User token not authorized'); - } else if (status === 404) { - debug('Domain not found: ', e); - res.status(404).send(e); - } else { - debug('Error in AZF communication ', e); - res.status(503).send('Error in AZF communication'); - } - }, - tokensCache - ); - }; - - const publicFunc = function(req, res) { - redirRequest(req, res); - }; - - const redirRequest = ('auth_for_nginx' in config && config.auth_for_nginx) - ? - // eslint-disable-next-line no-unused-vars - function(req, res, userInfo) { - debug('Access-token OK. Response 204'); - res.sendStatus(204); - } - : function(req, res, userInfo) { - if (userInfo) { - debug('Access-token OK. Redirecting to app...'); - } else { - debug('Public path. Redirecting to app...'); - } - - const protocol = config.app.ssl ? 'https' : 'http'; - - const options = { - host: config.app.host, - port: config.app.port, - path: req.url, - method: req.method, - headers: proxy.getClientIp(req, req.headers), - }; - proxy.sendData(protocol, options, req.body, res); - }; - - return { - pep, - public: publicFunc, - }; -})(); + req.user = userInfo; + if (!config.authorization.enabled) { + // JWT Authentication Access granted + setHeaders(req); + access.permit(req, res); + return; + } -exports.Root = Root; + const policy_decision_point = config.authorization.pdp; + if (policy_decision_point === 'authzforce') { + // JWT Authorization by Authzforce + authorize(req, res, tokens.authToken); + } else { + // JWT Authorization by IDM, the user will already exist. + tokens.jwtExpiry = userInfo.exp; + validateAccessIDM(req, res, tokens); + } + }); +} + +/** + * Authenticate the user token via Keyrock and then authorize the action if necessry. + * + * @param req - the incoming request + * @param res - the response to return + * @param tokens - a collection of auth tokens to use for this verification + */ +async function validateAccessIDM(req, res, tokens) { + const tenant_header = + config.authorization.enabled && config.authorization.header ? req.get(config.authorization.header) : undefined; + + try { + req.user = await IDM.authenticateUser(tokens, req.method, req.path, tenant_header); + setHeaders(req); + if (config.authorization.enabled) { + authorize(req, res, tokens.authToken); + } else { + // Authentication only. + access.permit(req, res); + } + } catch (e) { + debug(e); + if (e.status === 404 || e.status === 401) { + access.deny(res); + } else { + access.internalError(res, e, 'IDM'); + } + } +} + +/** + * Set headers with user information + * @param req - the incoming request + */ +function setHeaders(req) { + const user = req.user; + req.headers['X-Nick-Name'] = user.id ? user.id : ''; + req.headers['X-Display-Name'] = user.displayName ? user.displayName : ''; + req.headers['X-Roles'] = user.roles ? JSON.stringify(user.roles) : []; + req.headers['X-Organizations'] = user.organizations ? JSON.stringify(user.organizations) : []; + req.headers['X-Eidas-Profile'] = user.eidas_profile ? JSON.stringify(user.eidas_profile) : {}; + req.headers['X-App-Id'] = user.app_id; +} + +/** + * Extract the bearer token for the user, organization and the PEP itself + * + * @param req - the incoming request + */ +function getTokens(req) { + const tokenHeader = req.get('authorization'); + const pepToken = req.app.get('pepToken'); + const authOrgToken = config.organizations.header ? req.get(config.organizations.header) : undefined; + let authToken = tokenHeader ? tokenHeader.split('Bearer ')[1] : req.get('x-auth-token'); + + if (authToken === undefined && req.headers.authorization !== undefined) { + const headerAuth = req.headers.authorization.split(' ')[1]; + authToken = new Buffer(headerAuth, 'base64').toString(); + } + + return { authToken, authOrgToken, pepToken }; +} + +/** + * For most requests, check the headers and permit or deny access. + * + * @param req - the incoming request + * @param res - the response to return + */ +exports.restricted_access = function (req, res) { + config = config_service.get_config(); + const tokens = getTokens(req, res); + + if (tokens.authToken === undefined) { + debug('Auth-token not found in request header'); + res.set('WWW-Authenticate', 'IDM uri = ' + config.idm_host); + access.deny(res, 'Auth-token not found in request header'); + return; + } + if (config.magic_key && config.magic_key === tokens.authToken) { + access.permit(req, res); + return; + } + if (config.pep.token.secret) { + validateAccessJWT(req, res, tokens); + } else { + validateAccessIDM(req, res, tokens); + } +}; + +/** + * Allow access to whitelisted resources + */ +exports.open_access = access.permit; diff --git a/lib/HTTPClient.js b/lib/HTTPClient.js deleted file mode 100644 index d3ba23f..0000000 --- a/lib/HTTPClient.js +++ /dev/null @@ -1,117 +0,0 @@ -const XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest; - -const debug = require('debug')('pep-proxy:HTTP-Client'); - -exports.getClientIp = function(req, headers) { - const ipAddress = req.connection.remoteAddress; - - let forwardedIpsStr = req.header('x-forwarded-for'); - - if (forwardedIpsStr) { - // 'x-forwarded-for' header may return multiple IP addresses in - // the format: "client IP, proxy 1 IP, proxy 2 IP" so take the - // the first one - forwardedIpsStr += "," + ipAddress; - } else { - forwardedIpsStr = String(ipAddress); - } - - headers['x-forwarded-for'] = forwardedIpsStr; - - return headers; -}; - - -exports.sendData = function(protocol, options, data, res, callBackOK, callbackError) { - options.headers = options.headers || {}; - - callbackError = callbackError || function(status, resp) { - debug("Error: ", status, resp); - res.statusCode = status; - res.send(resp); - }; - callBackOK = callBackOK || function(status, resp, headers) { - res.statusCode = status; - for (const idx in headers) { - res.setHeader(idx, headers[idx]); - } - debug("Response: ", status); - debug(" Body: ", resp); - res.send(resp); - }; - - const url = protocol + "://" + options.host + ":" + options.port + options.path; - const xhr = new XMLHttpRequest(); - xhr.open(options.method, url, true); - if (options.headers["content-type"]) { - xhr.setRequestHeader("Content-Type", options.headers["content-type"]); - } - for (const headerIdx in options.headers) { - switch (headerIdx) { - // Unsafe headers - case "host": - break; - case "connection": - break; - case "referer": - break; -// case "accept-encoding": -// case "accept-charset": -// case "cookie": - case "content-type": - break; - case "origin": - break; - default: - xhr.setRequestHeader(headerIdx, options.headers[headerIdx]); - break; - } - } - - xhr.onerror = function() { - // DO NOTHING? - } - xhr.onreadystatechange = function () { - - // This resolves an error with Zombie.js - if (flag) { - return; - } - - if (xhr.readyState === 4) { - flag = true; - - if (xhr.status !== 0 && xhr.status < 400) { - const allHeaders = xhr.getAllResponseHeaders().split('\r\n'); - const headers = {}; - for (const h in allHeaders) { - headers[allHeaders[h].split(': ')[0]] = allHeaders[h].split(': ')[1]; - } - callBackOK(xhr.status, xhr.responseText, headers); - } else { - callbackError(xhr.status, xhr.responseText); - } - } - }; - - let flag = false; - debug("Sending ", options.method, " to: " + url); - debug(" Headers: ", options.headers); - //debug(" Body: ", data); - if (data !== undefined) { - try { - xhr.send(data); - } catch (e) { - - callbackError(e.message); - - } - } else { - try { - xhr.send(); - } catch (e) { - callbackError(e.message); - - } - } -} \ No newline at end of file diff --git a/lib/access_functions.js b/lib/access_functions.js new file mode 100644 index 0000000..d2a149e --- /dev/null +++ b/lib/access_functions.js @@ -0,0 +1,125 @@ +/* + * Copyright 2021 - Universidad Politécnica de Madrid. + * + * This file is part of PEP-Proxy + * + */ + +const config_service = require('./config_service'); +const config = config_service.get_config(); +const debug = require('debug')('pep-proxy:access'); +const got = require('got'); + +const PROXY_URL = (config.app.ssl ? 'https://' : 'http://') + config.app.host + ':' + config.app.port; + +/** + * Add the client IP of the proxy client to the list of X-forwarded-for headers. + * + * @param req - the incoming request + * @return a string representation of the X-forwarded-for header + */ +function getClientIp(req) { + let ip = req.ip; + if (ip.substr(0, 7) === '::ffff:') { + ip = ip.substr(7); + } + let forwardedIpsStr = req.header('x-forwarded-for'); + + if (forwardedIpsStr) { + // 'x-forwarded-for' header may return multiple IP addresses in + // the format: "client IP, proxy 1 IP, proxy 2 IP" so take the + // the first one + forwardedIpsStr += ',' + ip; + } else { + forwardedIpsStr = String(ip); + } + + return forwardedIpsStr; +} + +/** + * Based on the PDP decision, decide whether to forward the request + * or return "Access Denied" response + * + * @param req - the incoming request + * @param res - the response to return + * @param decision - the PDP decision permir/deny + */ +exports.adjudicate = function (req, res, decision) { + if (decision) { + permit(req, res); + } else { + deny(res); + } +}; + +/** + * Return an "Access Denied" response + * + * @param res - the response to return + * @param message - the error message to display + */ +function deny(res, message = 'User access-token not authorized') { + debug('Denied.'); + res.status(401).send(message); +} + +/** + * Return an "Internal Error" response. These should not occur + * during standard operation + * + * @param res - the response to return + * @param e - the error that occurred + * @param component - the component that caused the error + */ +function internalError(res, e, component) { + const message = e ? e.message : undefined; + debug(`Error in ${component} communication `, message ? message : e); + res.status(503).send(`Error in ${component} communication`); +} + +/** + * "Access Permitted" forwarding when using the PEP with NGINX + * + * @param req - the incoming request + * @param res - the response to return + */ +function nginxResponse(req, res) { + debug('Permitted. Response 204'); + res.sendStatus(204); +} + +/** + * "Access Permitted" forwarding. Forward the proxied request and + * return the response. + * + * @param req - the incoming request + * @param res - the response to return + */ +function pepResponse(req, res) { + const headers = req.headers; + headers['x-forwarded-for'] = getClientIp(req); + + got(PROXY_URL + req.url, { + method: req.method, + headers, + body: req.body, + allowGetBody: true, + throwHttpErrors: false, + retry: 0 + }) + .then((response) => { + debug(req.user ? 'Permitted.' : 'Public path.'); + res.statusCode = response.statusCode; + res.headers = response.headers; + return response.body ? res.send(response.body) : res.send(); + }) + .catch((error) => { + return internalError(res, error, 'Proxy'); + }); +} + +const permit = 'auth_for_nginx' in config && config.auth_for_nginx ? nginxResponse : pepResponse; +exports.permit = permit; +exports.deny = deny; +exports.internalError = internalError; diff --git a/lib/authorization_functions.js b/lib/authorization_functions.js new file mode 100644 index 0000000..d61347c --- /dev/null +++ b/lib/authorization_functions.js @@ -0,0 +1,107 @@ +/* + * Copyright 2021 - Universidad Politécnica de Madrid. + * + * This file is part of PEP-Proxy + * + */ + +const config_service = require('./config_service'); +const AZF = require('./pdp/azf'); +const IDM = require('./pdp/idm'); +const XACML = require('./pdp/xacml'); +const iShare = require('./pdp/ishare'); +const access = require('./access_functions'); +const debug = require('debug')('pep-proxy:authorize'); + +function getRoles(user) { + const roles = []; + for (const orgIdx in user.organizations) { + const org = user.organizations[orgIdx]; + for (const roleIdx in org.roles) { + const role = org.roles[roleIdx]; + if (roles.indexOf(role.id) === -1) { + roles.push(role.id); + } + } + } + + for (const roleIdx in user.roles) { + const role = user.roles[roleIdx]; + if (roles.indexOf(role) === -1) { + roles.push(role.id); + } + } + + return roles; +} + +function getData(req, res) { + const user = req.user; + const authorization = config_service.get_config().authorization; + return { + roles: getRoles(user), + appId: user.app_id, + azfDomain: user.app_azf_domain, + action: req.method, + resource: req.path, + payload_ids: res.locals.ids, + payload_attrs: res.locals.attrs, + payload_types: res.locals.types, + tenant_header: authorization.header ? req.get(authorization.header) : undefined + }; +} + +const authorize = { + idm: (req, res) => { + access.adjudicate(req, res, IDM.checkPolicies(req.user)); + }, + + xacml: (req, res) => { + // Check decision through XACML Endpoint + const authToken = req.app.get('pepToken'); + XACML.checkPolicies(authToken, getData(req, res)) + .then((decision) => { + access.adjudicate(req, res, decision); + }) + .catch((e) => { + access.internalError(res, e, 'XACML'); + }); + }, + + iShare: (req, res) => { + // Check decision through iShare Endpoint + const authToken = req.app.get('pepToken'); + iShare + .checkPolicies(authToken, getData(req, res)) + .then((decision) => { + access.adjudicate(req, res, decision); + }) + .catch((e) => { + access.internalError(res, e, 'iShare'); + }); + }, + authzforce: (req, res, authToken) => { + // Check decision through authzforce + AZF.checkPolicies(authToken, getData(req, res), req) + .then((decision) => { + access.adjudicate(req, res, decision); + }) + .catch((e) => { + if (e.status === 404) { + debug('Domain not found: ', e); + access.deny(res); + } else { + access.internalError(res, e, 'AZF'); + } + }); + } +}; + +exports.checkPayload = function () { + const authorization = config_service.get_config().authorization; + return authorization.enabled && (authorization.pdp === 'iShare' || authorization.pdp === 'xacml'); +}; + +exports.authorize = function (req, res, authToken) { + return authorize[config_service.get_config().authorization.pdp](req, res, authToken); +}; diff --git a/lib/cache.js b/lib/cache.js new file mode 100644 index 0000000..ca97dae --- /dev/null +++ b/lib/cache.js @@ -0,0 +1,89 @@ +/* + * Copyright 2021 - Universidad Politécnica de Madrid. + * + * This file is part of PEP-Proxy + * + */ + +const isHex = require('is-hex'); +const debug = require('debug')('pep-proxy:cache'); +const config_service = require('./config_service'); +const NodeCache = require('node-cache'); +const cache = new NodeCache({ + stdTTL: config_service.get_config().cache_time, + checkperiod: config_service.get_config().cache_time +}); + +/** + * Add a user into the cache + */ +function storeUser(token, user) { + cache.set(token, { date: new Date(), info: user }); +} + +/** + * Add an action+resource+token combo into the cache + */ +function storeAction(token, action, resource) { + const user = cache.get(token); + if (user) { + if (!user[action]) { + user[action] = []; + user[action].push(resource); + } else if (user[action] && user[action].indexOf(resource) === -1) { + user[action].push(resource); + } + + cache.set(token, user); + } +} + +/** + * Check if a user is found in the cache + * @return a user if a token for the given user is found and the token + * has not expired + */ +function checkTokenCache(token, jwtExpiration, action, resource) { + const user = cache.get(token); + const config = config_service.get_config(); + if (!user) { + return undefined; + } + debug('Token found, checking timestamp...'); + debug(token); + const currentTime = new Date().getTime(); + const tokenTime = token.length <= 40 && isHex(token) ? jwtExpiration * 1000 : user.date.getTime(); + + if (currentTime - tokenTime > config.cache_time * 1000) { + debug('Token in cache expired'); + cache.del(token); + return undefined; + } + + if (config.authorization.enabled && config.authorization.pdp === 'idm') { + if (tokenPermission(token, action, resource)) { + debug('Action-level permission in cache...'); + } else { + return undefined; + } + } + return user.info; +} + +/** + * Check if an action+resource is found in the cache + * @return true if a token for the given action and resource is found + */ +function tokenPermission(token, action, resource) { + const user = cache.get(token); + return user && user[action] && user[action].indexOf(resource) !== -1; +} + +exports.flush = function () { + cache.flushAll(); +}; + +exports.storeUser = storeUser; +exports.storeAction = storeAction; +exports.checkCache = checkTokenCache; +exports.tokenPermission = tokenPermission; diff --git a/lib/config_service.js b/lib/config_service.js index 184fc4c..00a212c 100644 --- a/lib/config_service.js +++ b/lib/config_service.js @@ -1,3 +1,10 @@ +/* + * Copyright 2021 - Universidad Politécnica de Madrid. + * + * This file is part of PEP-Proxy + * + */ + const debug = require('debug')('pep-proxy:Server'); const path = require('path'); const fs = require('fs'); @@ -62,6 +69,7 @@ function process_environment_variables(verbose) { 'PEP_TRUSTED_APPS', 'PEP_PROXY_AUTH_ENABLED', 'PEP_PROXY_PDP', + 'PEP_PROXY_TENANT_HEADER', 'PEP_PROXY_AZF_PROTOCOL', 'PEP_PROXY_AZF_HOST', 'PEP_PROXY_AZF_PORT', @@ -180,9 +188,13 @@ function process_environment_variables(verbose) { if (process.env.PEP_PROXY_AUTH_ENABLED) { config.authorization.enabled = to_boolean(process.env.PEP_PROXY_AUTH_ENABLED, false); } - if (process.env.PEP_PROXY_PDP) { + if (config.authorization.enabled && process.env.PEP_PROXY_PDP) { config.authorization.pdp = process.env.PEP_PROXY_PDP; } + if (config.authorization.enabled && process.env.PEP_PROXY_TENANT_HEADER) { + config.authorization.header = process.env.PEP_PROXY_TENANT_HEADER; + } + config.authorization.azf = config.authorization.azf || {}; if (process.env.PEP_PROXY_AZF_PROTOCOL) { config.authorization.azf.protocol = process.env.PEP_PROXY_AZF_PROTOCOL; diff --git a/lib/payload.js b/lib/payload.js new file mode 100644 index 0000000..62836bd --- /dev/null +++ b/lib/payload.js @@ -0,0 +1,106 @@ +/* + * Copyright 2021 - Universidad Politécnica de Madrid. + * + * This file is part of PEP-Proxy + * + */ + +const _ = require('underscore'); +const debug = require('debug')('pep-proxy:payload'); + +/** + * Check the payload body for attributes, types and ids + */ +exports.bodyAnalyse = function (req, res, next) { + debug('bodyAnalyse'); + let prefix = ''; + + const ids = []; + const attrs = []; + const types = []; + + function getAttrs(obj) { + if (Array.isArray(obj)) { + obj.forEach((element) => { + getAttrs(element); + }); + } else { + const keys = _.without(_.keys(obj), 'value', 'type', 'id', 'observedAt', 'metadata', 'unitCode'); + + keys.forEach((key) => { + if (Array.isArray(obj[key])) { + prefix = key + '.'; + getAttrs(obj[key]); + } else { + attrs.push(prefix + key); + } + }); + + if (obj.id) { + ids.push(obj.id); + } + if (obj.type) { + types.push(obj.type); + } + } + } + if (req.body) { + getAttrs(JSON.parse(req.body.toString())); + res.locals.ids = _.uniq(ids); + res.locals.attrs = _.uniq(attrs); + res.locals.types = _.uniq(types); + } + next(); +}; + +/** + * Check the URL path for attributes and ids + */ +exports.paramsAnalyse = function (req, res, next) { + debug('paramsAnalyse'); + if (req.params) { + if (req.params.id) { + res.locals.ids = res.locals.ids || []; + if (!res.locals.ids.includes(req.params.id)) { + res.locals.ids.push(req.params.id); + } + } + + if (req.params.attr) { + res.locals.attr = res.locals.attr || []; + if (!res.locals.attr.includes(req.params.attr)) { + res.locals.ids.push(req.params.attr); + } + } + } + next(); +}; + +/** + * Check the query string for attributes, types and ids + */ +exports.queryAnalyse = function (req, res, next) { + debug('queryAnalyse'); + if (req.query) { + if (req.query.ids) { + res.locals.ids = res.locals.ids || []; + const ids = req.query.ids.split(','); + ids.forEach((id) => { + if (!res.locals.ids.includes(id)) { + res.locals.ids.push(id); + } + }); + } + + if (req.query.type) { + res.locals.types = res.locals.types || []; + const types = req.query.type.split(','); + types.forEach((type) => { + if (!res.locals.types.includes(type)) { + res.locals.types.push(type); + } + }); + } + } + next(); +}; diff --git a/lib/pdp/azf.js b/lib/pdp/azf.js index 1ee3514..b57e4bc 100644 --- a/lib/pdp/azf.js +++ b/lib/pdp/azf.js @@ -1,304 +1,213 @@ -const config_service = require('./config_service.js'); -const config = config_service.get_config(); -const proxy = require('./HTTPClient.js'); +/* + * Copyright 2021 - Universidad Politécnica de Madrid. + * + * This file is part of PEP-Proxy + * + */ + +const config_service = require('../config_service'); const xml2json = require('xml2json'); const escapeXML = require('escape-html'); const xml2js = require('xml2js'); +const got = require('got'); const debug = require('debug')('pep-proxy:AZF-Client'); - -const AZF = (function() { - const checkConn = function(callback, callbackError) { - const options = { - host: config.azf.host, - port: config.azf.port, - path: '/', - method: 'GET', - }; - const protocol = config.azf.ssl ? 'https' : 'http'; - proxy.sendData( - protocol, - options, - undefined, - undefined, - callback, - callbackError - ); - }; - - const checkPermissions = function( - authToken, - userInfo, - req, - callback, - callbackError, - cache - ) { - const roles = getRoles(userInfo); - const appId = userInfo.app_id; - const azfDomain = userInfo.app_azf_domain; - +const cache = require('../cache'); + +function getUrl() { + const azf = config_service.get_config().authorization.azf; + return (azf.ssl ? 'https://' : 'http://') + azf.host + ':' + azf.port; +} + +/** + * Check that Authzforce is available and responding to requests + */ +exports.checkConnectivity = function () { + return got('', { prefixUrl: getUrl() }); +}; + +/** + * Make request to the Authzforce PDP and interpret the result + * + * @param authToken - the authToken for Authzforce + * @param data - A bag of data holding the action, resources, payload etc. + * this will be used to make the decision + * @param req - the incoming request for custom policies + * + * @return permit/deny + */ +exports.checkPolicies = function (authToken, data, req) { + const azf = config_service.get_config().authorization.azf; + + return new Promise((resolve, reject) => { let xml; - const action = req.method; - const resource = req.path; + const action = data.action; + const resource = data.resource; + const roles = data.roles; + const appId = data.appId; + + if (!data.azfDomain) { + return reject({ status: 404, message: 'AZF domain not created for application ' + appId }); + } + if (cache.tokenPermission(authToken, action, resource)) { + debug('Permission in cache...'); + return resolve(true); + } - if (config.authorization.azf.custom_policy) { + if (azf.custom_policy) { debug('Checking custom policy with AZF...'); - xml = require('./../policies/' + config.azf.custom_policy).getPolicy( - roles, - req, - appId - ); + xml = require('./../policies/' + azf.custom_policy).getPolicy(roles, req, appId); } else { - if ( - cache[authToken] && - cache[authToken][action] && - cache[authToken][action].indexOf(resource) !== -1 - ) { - debug('Permission in cache...'); - - callback(); - return; - } - debug('Checking auth with AZF...'); xml = getRESTPolicy(roles, action, resource, appId); } - - if (!azfDomain) { - callbackError(404, 'AZF domain not created for application ' + appId); - } else { - sendData( - xml, - authToken, - azfDomain, - function() { - // only caching basic authorization policies (verb + path) - if (!config.authorization.azf.custom_policy && cache[authToken]) { - if (!cache[authToken][action]) { - cache[authToken][action] = []; - cache[authToken][action].push(resource); - } else if ( - cache[authToken][action] && - cache[authToken][action].indexOf(resource) === -1 - ) { - cache[authToken][action].push(resource); - } - } - - callback(); + return got + .post('authzforce-ce/domains/' + data.azfDomain + '/pdp', { + prefixUrl: getUrl(), + headers: { + 'X-Auth-Token': authToken, + Accept: 'application/xml', + 'Content-Type': 'application/xml' }, - callbackError - ); - } - }; + body: xml + }) + .then((response) => { + debug('AZF response status: ', response.statusCode); + debug(response.body); - const getRoles = function(userInfo) { - const roles = []; - for (const orgIdx in userInfo.organizations) { - const org = userInfo.organizations[orgIdx]; - for (const roleIdx in org.roles) { - const role = org.roles[roleIdx]; - if (roles.indexOf(role.id) === -1) { - roles.push(role.id); + // xml2json keeps namespace prefixes in json keys, which is not right because prefixes are not supposed to be fixed; only the namespace URIs they refer to + // After parsing to JSON, we need to extract the Decision element in XACML namespace.. + // But there does not seem to be any good npm packge supporting namespace-aware XPath or equivalent evaluation on JSON. + // (xml2js-xpath will probably support namespaces in the next release: https://github.com/dsummersl/node-xml2js-xpath/issues/5 ) + // The easy way to go (but with inconvenients) is to get rid of prefixes.One way to refixes is to use npm package 'xml2js' with stripPrefix option. + xml2js.parseString(response.body, { tagNameProcessors: [xml2js.processors.stripPrefix] }, function (err, json) { + if (err) { + return reject(err); + } + // xml2js puts child nodes in array by default, except on the root node (option 'explicitArray') + const decision = json.Response.Result[0].Decision[0].includes('Permit'); + if (decision && !azf.custom_policy) { + cache.storeAction(authToken, action, resource); + } + return resolve(decision); + }); + }) + .catch((error) => { + if (error instanceof got.HTTPError) { + return resolve(false); } - } - } - - for (const roleIdx in userInfo.roles) { - const role = userInfo.roles[roleIdx]; - if (roles.indexOf(role) === -1) { - roles.push(role.id); - } - } - - return roles; - }; - - const getRESTPolicy = function(roles, action, resource, appId) { - debug( - 'Checking authorization to roles', - roles, - 'to do ', - action, - ' on ', - resource, - 'and app ', - appId - ); - - const XACMLPolicy = { - Request: { - xmlns: 'urn:oasis:names:tc:xacml:3.0:core:schema:wd-17', - CombinedDecision: 'false', - ReturnPolicyIdList: 'false', - Attributes: [ - { - Category: - 'urn:oasis:names:tc:xacml:1.0:subject-category:access-subject', - Attribute: [ - // ????? - // { - // "AttributeId":"urn:oasis:names:tc:xacml:1.0:subject:subject-id", - // "IncludeInResult": "false", - // "AttributeValue":{ - // "DataType":"http://www.w3.org/2001/XMLSchema#string", - // "$t":"joe" - // } - // }, - // Include the role Attribute if and only if the user has at least one role, since the XACML schema requires at least one AttributeValue in a element - //{ - // "AttributeId":"urn:oasis:names:tc:xacml:2.0:subject:role", - // "IncludeInResult": "false", - // "AttributeValue": [ - // One per role - // { - // "DataType":"http://www.w3.org/2001/XMLSchema#string", - // "$t":"Manager" - // } - // ] - //} - ], - }, - { - Category: - 'urn:oasis:names:tc:xacml:3.0:attribute-category:resource', - Attribute: [ - { - AttributeId: - 'urn:oasis:names:tc:xacml:1.0:resource:resource-id', - IncludeInResult: 'false', - AttributeValue: { - DataType: 'http://www.w3.org/2001/XMLSchema#string', - $t: appId, - }, - }, - { - AttributeId: 'urn:thales:xacml:2.0:resource:sub-resource-id', - IncludeInResult: 'false', - AttributeValue: { - DataType: 'http://www.w3.org/2001/XMLSchema#string', - $t: escapeXML(resource), - }, - }, - ], - }, - { - Category: 'urn:oasis:names:tc:xacml:3.0:attribute-category:action', - Attribute: { - AttributeId: 'urn:oasis:names:tc:xacml:1.0:action:action-id', + debug('Error in Authzforce communication ', error); + return reject(error); + }); + }); +}; + +/** + * Create a payload for making an XACML request to Authzforce + * based on the action,resource,tenant and attributes + * @return XML payload + */ +const getRESTPolicy = function (roles, action, resource, appId) { + debug('Checking authorization to roles', roles, 'to do ', action, ' on ', resource, 'and app ', appId); + + const XACMLPolicy = { + Request: { + xmlns: 'urn:oasis:names:tc:xacml:3.0:core:schema:wd-17', + CombinedDecision: 'false', + ReturnPolicyIdList: 'false', + Attributes: [ + { + Category: 'urn:oasis:names:tc:xacml:1.0:subject-category:access-subject', + Attribute: [ + // ????? + // { + // "AttributeId":"urn:oasis:names:tc:xacml:1.0:subject:subject-id", + // "IncludeInResult": "false", + // "AttributeValue":{ + // "DataType":"http://www.w3.org/2001/XMLSchema#string", + // "$t":"joe" + // } + // }, + // Include the role Attribute if and only if the user has at least one role, since the XACML schema requires at least one AttributeValue in a element + //{ + // "AttributeId":"urn:oasis:names:tc:xacml:2.0:subject:role", + // "IncludeInResult": "false", + // "AttributeValue": [ + // One per role + // { + // "DataType":"http://www.w3.org/2001/XMLSchema#string", + // "$t":"Manager" + // } + // ] + //} + ] + }, + { + Category: 'urn:oasis:names:tc:xacml:3.0:attribute-category:resource', + Attribute: [ + { + AttributeId: 'urn:oasis:names:tc:xacml:1.0:resource:resource-id', IncludeInResult: 'false', AttributeValue: { DataType: 'http://www.w3.org/2001/XMLSchema#string', - $t: action, - }, + $t: appId + } }, - }, - { - Category: - 'urn:oasis:names:tc:xacml:3.0:attribute-category:environment', - }, - ], - }, - }; - - // create Attribute only roles is not empty because XACML schema requires that an Attribute has at least one AttributeValue - if (roles.length > 0) { - XACMLPolicy.Request.Attributes[0].Attribute[0] = { - AttributeId: 'urn:oasis:names:tc:xacml:2.0:subject:role', - IncludeInResult: 'false', - AttributeValue: [ - // One per role - // { - // "DataType":"http://www.w3.org/2001/XMLSchema#string", - // "$t":"Manager" - // } - ], - }; - - for (const i in roles) { - XACMLPolicy.Request.Attributes[0].Attribute[0].AttributeValue[i] = { - //"AttributeId":"urn:oasis:names:tc:xacml:2.0:subject:role", - //"IncludeInResult": "false", - //"AttributeValue":{ - DataType: 'http://www.w3.org/2001/XMLSchema#string', - $t: roles[i], - //} - }; - } + { + AttributeId: 'urn:thales:xacml:2.0:resource:sub-resource-id', + IncludeInResult: 'false', + AttributeValue: { + DataType: 'http://www.w3.org/2001/XMLSchema#string', + $t: escapeXML(resource) + } + } + ] + }, + { + Category: 'urn:oasis:names:tc:xacml:3.0:attribute-category:action', + Attribute: { + AttributeId: 'urn:oasis:names:tc:xacml:1.0:action:action-id', + IncludeInResult: 'false', + AttributeValue: { + DataType: 'http://www.w3.org/2001/XMLSchema#string', + $t: action + } + } + }, + { + Category: 'urn:oasis:names:tc:xacml:3.0:attribute-category:environment' + } + ] } - - const xml = - '' + - xml2json.toXml(XACMLPolicy); - - debug('XML: ', xml); - return xml; }; - const sendData = function(xml, authToken, azfDomain, success, error) { - const path = '/authzforce-ce/domains/' + azfDomain + '/pdp'; - - const options = { - host: config.authorization.azf.host, - port: config.authorization.azf.port, - path, - method: 'POST', - headers: { - 'X-Auth-Token': authToken, - Accept: 'application/xml', - 'Content-Type': 'application/xml', - }, + // create Attribute only roles is not empty because XACML schema requires that an Attribute has at least one AttributeValue + if (roles.length > 0) { + XACMLPolicy.Request.Attributes[0].Attribute[0] = { + AttributeId: 'urn:oasis:names:tc:xacml:2.0:subject:role', + IncludeInResult: 'false', + AttributeValue: [ + // One per role + // { + // "DataType":"http://www.w3.org/2001/XMLSchema#string", + // "$t":"Manager" + // } + ] }; - const protocol = config.authorization.azf.ssl ? 'https' : 'http'; - - proxy.sendData( - protocol, - options, - xml, - undefined, - function(status, resp) { - debug('AZF response status: ', status); - debug('AZF response: ', resp); - let decision; - // xml2json keeps namespace prefixes in json keys, which is not right because prefixes are not supposed to be fixed; only the namespace URIs they refer to - // After parsing to JSON, we need to extract the Decision element in XACML namespace.. - // But there does not seem to be any good npm packge supporting namespace-aware XPath or equivalent evaluation on JSON. - // (xml2js-xpath will probably support namespaces in the next release: https://github.com/dsummersl/node-xml2js-xpath/issues/5 ) - // The easy way to go (but with inconvenients) is to get rid of prefixes.One way to refixes is to use npm package 'xml2js' with stripPrefix option. - xml2js.parseString( - resp, - { tagNameProcessors: [xml2js.processors.stripPrefix] }, - function(err, jsonRes) { - debug('AZF response parsing result (JSON): ', jsonRes); - debug( - "AZF response parsing error ('null' means no error): ", - err - ); - // xml2js puts child nodes in array by default, except on the root node (option 'explicitArray') - decision = jsonRes.Response.Result[0].Decision[0]; - } - ); + for (const i in roles) { + XACMLPolicy.Request.Attributes[0].Attribute[0].AttributeValue[i] = { + //"AttributeId":"urn:oasis:names:tc:xacml:2.0:subject:role", + //"IncludeInResult": "false", + //"AttributeValue":{ + DataType: 'http://www.w3.org/2001/XMLSchema#string', + $t: roles[i] + //} + }; + } + } - decision = String(decision); + const xml = '' + xml2json.toXml(XACMLPolicy); - debug('Decision: ', decision); - if (decision.includes('Permit')) { - success(); - } else { - error( - 401, - 'User not authorized in AZF for the given action and resource' - ); - } - }, - error - ); - }; - - return { - checkPermissions, - checkConn, - }; -})(); -exports.AZF = AZF; + debug(xml); + return xml; +}; diff --git a/lib/pdp/idm.js b/lib/pdp/idm.js index dd831d5..ab0c223 100644 --- a/lib/pdp/idm.js +++ b/lib/pdp/idm.js @@ -1,255 +1,171 @@ -const config_service = require('../lib/config_service.js'); -const config = config_service.get_config(); -const proxy = require('./HTTPClient.js'); -const isHex = require('is-hex'); +/* + * Copyright 2021 - Universidad Politécnica de Madrid. + * + * This file is part of PEP-Proxy + * + */ + +const config_service = require('../config_service'); const debug = require('debug')('pep-proxy:IDM-Client'); - -const IDM = (function() { - let myToken; - - const checkConn = function(callback, callbackError) { - const options = { - host: config.idm.host, - port: config.idm.port, - path: '/version', - method: 'GET', - }; - const protocol = config.idm.ssl ? 'https' : 'http'; - - proxy.sendData( - protocol, - options, - undefined, - undefined, - callback, - callbackError - ); - }; - - const authenticate = function(callback, callbackError) { - const options = { - host: config.idm.host, - port: config.idm.port, - path: '/v3/auth/tokens', - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - }; - const protocol = config.idm.ssl ? 'https' : 'http'; - const body = { - name: config.pep.username, - password: config.pep.password, - }; - proxy.sendData( - protocol, - options, - JSON.stringify(body), - undefined, - function(status, resp, headers) { - const response = JSON.parse(resp); - if (response.idm_authorization_config) { - debug('IDM authorization configuration:'); - debug( - ' + Authzforce enabled: ' + - response.idm_authorization_config.authzforce - ); - const rules = - response.idm_authorization_config.level === 'advanced' - ? 'HTTP Verb+Resource and Advanced' - : 'HTTP Verb+Resource'; - debug(' + Authorization rules allowed: ' + rules); - } - - myToken = headers['x-subject-token']; - callback(myToken); - }, - callbackError - ); - }; - - const checkToken = function( - token, - jwtExpiration, - action, - resource, - authzforce, - organizationToken, - callback, - callbackError, - cache - ) { - let path = '/user?access_token=' + encodeURIComponent(token); - - if (action && resource) { - path = path + '&action=' + action; - path = path + '&resource=' + resource; - path = path + '&app_id=' + config.pep.app_id; - } else if (authzforce) { - path = path + '&authzforce=' + authzforce; - } - else { - path = path + '&app_id=' + config.pep.app_id; +const cache = require('../cache'); +const got = require('got'); + +function getUrl() { + const idm = config_service.get_config().idm; + return (idm.ssl ? 'https://' : 'http://') + idm.host + ':' + idm.port; +} +/** + * The Permit/Deny decision will be found within the user data. + * + * @param user - the authorized User + * + * @return permit/deny + */ +exports.checkPolicies = function (user) { + return user.authorization_decision === 'Permit'; +}; + +/** + * When making a request to Keyrock ensure that the correct data is + * returned. When using Keyrock as a PDP, also include the action and + * resource. When using Authzforce as the PDP, ensure the domain is + * returned for further request. + * + * @param token - the PEP bearer token + * @param action - the action the user is requesting + * @param resource - the resource the user is requesting + * @param tenant - the tenant or service path the user is requesting + * + * @return a URL to make the request to Keyrock + */ +function getPath(token, action, resource, tenant) { + const authorization = config_service.get_config().authorization; + const policy_decision_point = authorization.enabled ? authorization.pdp : undefined; + let path = + 'user?access_token=' + encodeURIComponent(token.authToken) + '&app_id=' + config_service.get_config().pep.app_id; + + if (policy_decision_point === 'idm') { + // Using Keyrock as combined IDM and PDP - get a permit/deny adjudication directly + path = path + '&action=' + action; + path = path + '&resource=' + resource; + if (tenant) { + path = path + '&authorization_service_header=' + tenant; } - - const options = { - host: config.idm.host, - port: config.idm.port, - path, - method: 'GET', - headers: { 'X-Auth-Token': myToken, Accept: 'application/json' }, - }; - - const protocol = config.idm.ssl ? 'https' : 'http'; - - checkTokenCache(token, jwtExpiration, action, resource, cache, function( - cachedUserInfo - ) { - if (cachedUserInfo) { - callback(cachedUserInfo); - } else { - debug('Checking token with IDM...'); - proxy.sendData( - protocol, - options, - undefined, - undefined, - function(status, resp) { - const userInfo = JSON.parse(resp); - const organizations = userInfo.organizations - ? userInfo.organizations.map(elem => elem.id) - : []; - - - if (!checkApplication(userInfo.app_id, userInfo.trusted_apps)) { - debug('User not authorized in application', config.pep.app_id); - callbackError( - 401, - 'User not authorized in application', - config.pep.app_id - ); - } else if (!checkOrganizations(organizations, organizationToken)) { - debug('User not belongs to organization', organizationToken); - callbackError( - 401, - 'User not belongs to organization', - organizationToken - ); - } else { - storeToken(token, userInfo, action, resource, cache); - callback(userInfo); - } - }, - function(status, e) { - debug('Error in IDM communication ', e); - callbackError(status, e ? JSON.parse(e) : undefined); - } - ); + } + if (policy_decision_point === 'authzforce') { + // Using Authzforce as a PDP - get the location of the Authzforce PDP Domain + path = path + '&authzforce=true'; + } + + return path; +} + +/** + * Check that the appId aligns with the PEP or its trusted apps. + * @returns true if the application exists + */ +function checkApplication(appId, trusted_apps) { + const pep = config_service.get_config().pep; + return appId === pep.app_id || trusted_apps.indexOf(appId) !== -1 || pep.trusted_apps.indexOf(appId) !== -1; +} + +/** + * Check that a user has valid organizations + * @returns true if the organizations are valid + */ +function checkOrganizations(organizations, organizationToken) { + if (!config_service.get_config().organizations.enabled) { + return true; + } + debug('User belongs to: ', organizations); + debug('Token is in the scope of: ', organizationToken); + + return organizations.includes(organizationToken); +} + +/** + * Check that Keyrock is responding to requests + */ +exports.checkConnectivity = function () { + return got('version', { prefixUrl: getUrl() }); +}; + +/** + * Contact Keyrock and check the PEP Token is valid. + * @return the token and configuration for the PEP + */ +exports.authenticatePEP = function () { + const pep = config_service.get_config().pep; + return got + .post('v3/auth/tokens', { + prefixUrl: getUrl(), + json: { + name: pep.username, + password: pep.password } + }) + .then((response) => { + const body = JSON.parse(response.body) || {}; + return { config: body.idm_authorization_config, pepToken: response.headers['x-subject-token'] }; + }) + .catch((error) => { + throw error; }); - }; - - const checkTokenCache = function( - token, - jwtExpiration, - action, - resource, - cache, - callback - ) { - if (cache[token]) { - debug('Token in cache, checking timestamp...'); - debug(token); - const currentTime = new Date().getTime(); - const tokenTime = - token.length <= 40 && isHex(token) - ? jwtExpiration * 1000 - : cache[token].date.getTime(); - - if (currentTime - tokenTime < config.cache_time * 1000) { - if ( - config.authorization.enabled && - config.authorization.pdp === 'idm' - ) { - if ( - cache[token] && - cache[token][action] && - cache[token][action].indexOf(resource) !== -1 - ) { - debug('Permission in cache...'); - - callback(cache[token].userInfo); - } else { - callback(); - } - } else { - callback(cache[token].userInfo); - } - } else { - debug('Token in cache expired'); - delete cache[token]; - callback(); - } - } else { - callback(); +}; + +/** + * Connect with Keyrock and see if the token is valid for the application + * When using Keyrock as a PDP this also returns a permit/deny response. + * + * @param token - tokens to use in this request + * @param action - the action the user is requesting + * @param resource - the resource the user is requesting + * @param tenant - the tenant or service path the user is requesting + * + * @return the user if found + */ +exports.authenticateUser = function (token, action, resource, tenant) { + const authorization = config_service.get_config().authorization; + return new Promise((resolve, reject) => { + const authToken = token.authToken; + const authOrgToken = token.authOrgToken; + debug('Authenticating user'); + const cachedUser = cache.checkCache(authToken, token.jwtExpiry, action, resource); + if (cachedUser) { + return resolve(cachedUser); } - }; - const storeToken = function(token, userInfo, action, resource, cache) { - cache[token] = {}; - cache[token].date = new Date(); - cache[token].userInfo = userInfo; - - if (config.authorization.enabled) { - if ( - config.authorization.pdp === 'idm' && - userInfo.authorization_decision === 'Permit' - ) { - if (!cache[token][action]) { - cache[token][action] = []; - cache[token][action].push(resource); - } else if ( - cache[token][action] && - cache[token][action].indexOf(resource) === -1 - ) { - cache[token][action].push(resource); - } + return got(getPath(token, action, resource, tenant), { + prefixUrl: getUrl(), + headers: { + 'X-Auth-Token': token.pepToken, + Accept: 'application/json' } - } - }; - - const checkApplication = function(appId, trusted_apps) { - debug('Token created in application: ', appId); - debug('PEP Proxy application: ', config.pep.app_id); - debug('PEP Proxy trusted_apps: ', config.pep.trusted_apps); - - if ( - appId === config.pep.app_id || - trusted_apps.indexOf(appId) !==-1 || - config.pep.trusted_apps.indexOf(appId) !== -1 - ) { - return true; - } - return false; - }; - - const checkOrganizations = function(organizations, organizationToken) { - if (!config.organizations.enabled) { - return true; - } - debug('User belongs to: ', organizations); - debug('Token is in the scope of: ', organizationToken); - - if (organizations.includes(organizationToken)) { - return true; - } - return false; - }; - - return { - checkConn, - authenticate, - checkToken, - checkTokenCache, - storeToken, - }; -})(); -exports.IDM = IDM; + }) + .then((response) => { + const user = JSON.parse(response.body) || {}; + const organizations = user.organizations ? user.organizations.map((elem) => elem.id) : []; + if (!checkApplication(user.app_id, user.trusted_apps)) { + debug('User not authorized in application', config_service.get_config().pep.app_id); + return reject({ status: 401, message: 'User not authorized in application' }); + } else if (!checkOrganizations(organizations, authOrgToken)) { + debug('User does not belong to the organization', authOrgToken); + return reject({ status: 401, message: 'User does not belong to the organization' }); + } + // Keyrock is in use as an IDM - store the User + cache.storeUser(authToken, user); + if (authorization.enabled && authorization.pdp === 'idm') { + // Keyrock is also in use as a PDP - store the permissions. + cache.storeAction(authToken, action, resource); + } + return resolve(user); + }) + .catch((error) => { + if (error instanceof got.HTTPError) { + return reject({ status: 401, message: 'User not authorized in application' }); + } + debug('Error in IDM communication ', error); + return reject({ status: 503, message: error.message }); + }); + }); +}; diff --git a/lib/pdp/ishare.js b/lib/pdp/ishare.js new file mode 100644 index 0000000..443a543 --- /dev/null +++ b/lib/pdp/ishare.js @@ -0,0 +1,94 @@ +/* + * Copyright 2021 - Universidad Politécnica de Madrid. + * + * This file is part of PEP-Proxy + * + */ + +const config_service = require('../config_service'); +const config = config_service.get_config(); +const debug = require('debug')('pep-proxy:iShare-Client'); +const got = require('got'); +const KEYROCK_URL = (config.idm.ssl ? 'https://' : 'http://') + config.idm.host + ':' + config.idm.port; + +/** + * Make request to the iSHARE delegate endpoint and interpret the result + * + * @param req - the incoming request + * @param data - A bag of data holding the action, resources, payload etc. + * this will be used to make the decision + * + * @return permit/deny + */ +exports.checkPolicies = function (req, data) { + return new Promise((resolve, reject) => { + debug('Checking policy'); + return got + .post('delegate', { + prefixUrl: KEYROCK_URL, + headers: { + 'X-Auth-Token': req.app.get('pepToken') + }, + json: iShareBody(data) + }) + .json() + .then((result) => { + return resolve(result.Response[0].Decision === 'Permit'); + }) + .catch((error) => { + if (error instanceof got.HTTPError) { + return resolve(false); + } + debug('Error in iShare communication ', error); + return reject(error); + }); + }); +}; + +/** + * Create a payload for making an iSHARE request + * based on the action,resource,tenant and attributes + */ +function iShareBody(data) { + const action = data.action; + const resource = data.resource; + const roles = data.roles; + const appId = data.appId; + debug('Checking authorization to roles', roles, 'to do ', action, ' on ', resource, 'and app ', appId); + + + const json = { + delegationRequest: { + policyIssuer: 'EU.EORI.NL000000005', + target: { + accessSubject: 'EU.EORI.NL000000001' + }, + policySets: [ + { + policies: [ + { + target: { + resource: { + type: data.types, + identifiers: data.resource, + attributes: data.attrs + }, + actions: [action], + environment: { + serviceProviders: ['EU.EORI.NL000000003'] + } + }, + rules: [ + { + effect: 'Permit' + } + ] + } + ] + } + ] + } + }; + debug('JSON: ', JSON.stringify(json)); + return json; +} diff --git a/lib/pdp/xacml.js b/lib/pdp/xacml.js new file mode 100644 index 0000000..7937aa0 --- /dev/null +++ b/lib/pdp/xacml.js @@ -0,0 +1,102 @@ +/* + * Copyright 2021 - Universidad Politécnica de Madrid. + * + * This file is part of PEP-Proxy + * + */ + +const config_service = require('../config_service'); +const config = config_service.get_config(); +const debug = require('debug')('pep-proxy:XACML-Client'); +const got = require('got'); +const KEYROCK_URL = (config.idm.ssl ? 'https://' : 'http://') + config.idm.host + ':' + config.idm.port; + +/** + * Make request to the XACML JSON endpoint and interpret the result + * + * @param token - authorization token. + * @param data - A bag of data holding the action, resources, payload etc. + * this will be used to make the decision + * + * + * @return permit/deny + */ +exports.checkPolicies = function (token, data) { + return new Promise((resolve, reject) => { + return got + .post('xacml', { + prefixUrl: KEYROCK_URL, + headers: { + 'X-Auth-Token': token + }, + json: getPolicy(data) + }) + .json() + .then((result) => { + debug(JSON.stringify(result)); + return resolve(result.Response[0].Decision === 'Permit'); + }) + .catch((error) => { + if (error instanceof got.HTTPError) { + return resolve(false); + } + debug('Error in XACML communication ', error); + return reject(error); + }); + }); +}; + +/** + * Add an attribute to the XACML payload + * @return Object + */ +function attribute(id, value) { + return { + AttributeId: id, + Value: value + }; +} + +/** + * Create a payload for making an XACML JSON request + * based on the action,resource,tenant and attributes + * @return XACML payload + */ +function getPolicy(data) { + const action = data.action; + const resource = data.resource; + const roles = data.roles; + const appId = data.appId; + const tenant = data.tenant_header; + debug('Checking authorization to roles', roles, 'to do ', action, ' on ', resource, 'and app ', appId); + + const resourceInfo = [attribute('urn:oasis:names:tc:xacml:1.0:resource:resource-id', data.resource)]; + if (data.tenant_header) { + resourceInfo.push(attribute('urn:ngsi-ld:resource:tenant', data.tenant_header)); + } + if (data.payload_types) { + resourceInfo.push(attribute('urn:ngsi-ld:resource:types', data.payload_types)); + } + if (data.payload_attrs) { + resourceInfo.push(attribute('urn:ngsi-ld:resource:attrs', data.payload_attrs)); + } + if (data.payload_ids) { + resourceInfo.push(attribute('urn:ngsi-ld:resource:ids', data.payload_ids)); + } + const json = { + Request: { + AccessSubject: { + Attribute: [attribute('urn:oasis:names:tc:xacml:2.0:subject:role', data.roles)] + }, + Action: { + Attribute: [attribute('urn:oasis:names:tc:xacml:1.0:action:action-id', data.action)] + }, + Resource: { + Attribute: resourceInfo + } + } + }; + + debug('XACML request: ', JSON.stringify(json)); + return json; +} From 7721801e4ddba74201cc2536e43f8fca64c4e594 Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Mon, 9 Aug 2021 08:42:02 +0200 Subject: [PATCH 03/33] Add unit tests - Remove dependency on MySQL --- .github/workflows/ci.yml | 8 - sanity/test.js | 79 ---- test/config_test.js | 78 ---- test/mysql-data/backup.sql | 766 ------------------------------- test/unit/add-test.js | 36 -- test/unit/authentication-test.js | 208 +++++++++ test/unit/authzforce-pdp-test.js | 182 ++++++++ test/unit/connection-test.js | 132 ++++++ test/unit/keyrock-pdp-test.js | 194 ++++++++ test/unit/payload-pdp-test.js | 224 +++++++++ test/unit/sanity-test.js | 90 ---- test/utils.js | 19 - 12 files changed, 940 insertions(+), 1076 deletions(-) delete mode 100644 sanity/test.js delete mode 100644 test/config_test.js delete mode 100644 test/mysql-data/backup.sql delete mode 100644 test/unit/add-test.js create mode 100644 test/unit/authentication-test.js create mode 100644 test/unit/authzforce-pdp-test.js create mode 100644 test/unit/connection-test.js create mode 100644 test/unit/keyrock-pdp-test.js create mode 100644 test/unit/payload-pdp-test.js delete mode 100644 test/unit/sanity-test.js delete mode 100644 test/utils.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 31759da..279e003 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,10 +69,6 @@ jobs: node-version: "${{ matrix.node-version }}" - name: "Unit Tests with Node.js ${{ matrix.node-version }}" run: | - docker network create --driver=bridge my-network - docker run -d -h mysql --net=my-network -p 3306:3306 --name mysql -v $(pwd)/test/mysql-data:/docker-entrypoint-initdb.d/:ro -e MYSQL_ROOT_PASSWORD=test mysql:5.7 - docker run -d --net=my-network -p 3000:3000 --name keyrock -e IDM_DB_USER=root -e IDM_DB_PASS=test -e IDM_DB_HOST=mysql -e IDM_DB_PORT=3306 fiware/idm:8.0.0 - npm install npm test @@ -88,10 +84,6 @@ jobs: with: node-version: 12.x - run: | - docker network create --driver=bridge my-network - docker run -d -h mysql --net=my-network -p 3306:3306 --name mysql -v $(pwd)/test/mysql-data:/docker-entrypoint-initdb.d/:ro -e MYSQL_ROOT_PASSWORD=test mysql:5.7 - docker run -d --net=my-network -p 3000:3000 --name keyrock -e IDM_DB_USER=root -e IDM_DB_PASS=test -e IDM_DB_HOST=mysql -e IDM_DB_PORT=3306 fiware/idm:8.0.0 - npm install npm run test:coverage - name: Push to Coveralls diff --git a/sanity/test.js b/sanity/test.js deleted file mode 100644 index ac11b86..0000000 --- a/sanity/test.js +++ /dev/null @@ -1,79 +0,0 @@ -//const should = require('should'); -//const mocha = require('mocha'); - -const config = require('../test/config_test.js'); -const IDM = require('../lib/idm.js').IDM; -const AZF = require('../lib/azf.js').AZF; - -const debug = require('debug')('pep-proxy:test'); -process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; - -describe('Sanity Checks for Wilma PEP Proxy - Identity Manager Checks', function() { - describe('Testing Keystone configuration', function() { - it('should have PEP user configured', function(done) { - if (config.pep.username !== undefined && config.pep.username !== '') { - if (config.pep.password !== undefined && config.pep.password !== '') { - done(); - } - } - }); - }); - - describe('Testing connection with Keystone', function() { - it('should have connectivity with Keystone', function(done) { - IDM.checkConn( - function(status) { - if (status === 200) { - done(); - } - }, - function(status, e) { - debug('Error in keystone communication', e); - } - ); - }); - - it('should authenticate with Keystone', function(done) { - IDM.authenticate( - () => { - done(); - }, - () => {} - ); - }); - }); -}); - -describe('Sanity Checks for Wilma PEP Proxy - AuthZForce Checks', function() { - if ( - config.authorization.enabled && - config.authorization.pdp === 'authzforce' - ) { - describe('Testing configuration', function() { - it('should have AZF server configured', function(done) { - if (config.azf.host !== undefined && config.azf.host !== '') { - if (config.azf.port !== undefined && config.azf.port !== '') { - done(); - } - } - }); - }); - - describe('Testing connection with AZF', function() { - it('should have connectivity with AZF', function(done) { - AZF.checkConn( - function() {}, - function(status) { - if (status === 401) { - done(); - } - } - ); - }); - }); - } else { - it('AZF not enabled', function(done) { - done(); - }); - } -}); diff --git a/test/config_test.js b/test/config_test.js deleted file mode 100644 index 105487c..0000000 --- a/test/config_test.js +++ /dev/null @@ -1,78 +0,0 @@ -const config = {}; - -function toBoolean(env, defaultValue) { - return env !== undefined ? env.toLowerCase() === 'true' : defaultValue; -} - -function to_array(env, default_value) { - return env !== undefined ? env.split(',') : default_value; -} - -// Used only if https is disabled -config.pep_port = process.env.PEP_PROXY_PORT || 80; - -// Set this var to undefined if you don't want the server to listen on HTTPS -config.https = { - enabled: toBoolean(process.env.PEP_PROXY_HTTPS_ENABLED, false), - cert_file: 'cert/cert.crt', - key_file: 'cert/key.key', - port: process.env.PEP_PROXY_HTTPS_PORT || 443, -}; - -config.idm = { - host: process.env.PEP_PROXY_IDM_HOST || 'localhost', - port: process.env.PEP_PROXY_IDM_PORT || 4000, - ssl: toBoolean(process.env.PEP_PROXY_IDM_SSL_ENABLED, false), -}; - -config.app = { - host: process.env.PEP_PROXY_APP_HOST || 'www.fiware.org', - port: process.env.PEP_PROXY_APP_PORT || '80', - ssl: toBoolean(process.env.PEP_PROXY_APP_SSL_ENABLED, false), // Use true if the app server listens in https -}; - -config.organizations = { - enabled: toBoolean(process.env.PEP_PROXY_ORG_ENABLED, false), - header: process.env.PEP_PROXY_ORG_HEADER || 'fiware-service', -}; - -// Credentials obtained when registering PEP Proxy in app_id in Account Portal -config.pep = { - app_id: process.env.PEP_PROXY_APP_ID || '', - username: process.env.PEP_PROXY_USERNAME || '', - password: process.env.PEP_PASSWORD || '', - token: { - secret: process.env.PEP_TOKEN_SECRET || '', // Secret must be configured in order validate a jwt - }, - trusted_apps: [], -}; - -// in seconds -config.cache_time = 300; - -// if enabled PEP checks permissions in two ways: -// - With IdM: only allow basic authorization -// - With Authzforce: allow basic and advanced authorization. -// For advanced authorization, you can use custom policy checks by including programatic scripts -// in policies folder. An script template is included there -// -// This is only compatible with oauth2 tokens engine - -config.authorization = { - enabled: toBoolean(process.env.PEP_PROXY_AUTH_ENABLED, false), - pdp: process.env.PEP_PROXY_PDP || 'idm', // idm|authzforce - azf: { - protocol: process.env.PEP_PROXY_AZF_PROTOCOL || 'http', - host: process.env.PEP_PROXY_AZF_HOST || 'localhost', - port: process.env.PEP_PROXY_AZF_PORT || 8080, - custom_policy: process.env.PEP_PROXY_AZF_CUSTOM_POLICY || undefined, // use undefined to default policy checks (HTTP verb + path). - }, -}; - -// list of paths that will not check authentication/authorization -// example: ['/public/*', '/static/css/'] -config.public_paths = to_array(process.env.PEP_PROXY_PUBLIC_PATHS, []); - -config.magic_key = process.env.PEP_PROXY_MAGIC_KEY || undefined; - -module.exports = config; diff --git a/test/mysql-data/backup.sql b/test/mysql-data/backup.sql deleted file mode 100644 index dcb6b8f..0000000 --- a/test/mysql-data/backup.sql +++ /dev/null @@ -1,766 +0,0 @@ --- MySQL dump 10.13 Distrib 5.7.22, for Linux (x86_64) --- --- Host: localhost Database: idm --- ------------------------------------------------------ --- Server version 5.7.22 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `SequelizeMeta` --- - -CREATE DATABASE IF NOT EXISTS idm; -USE idm - -DROP TABLE IF EXISTS `SequelizeMeta`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `SequelizeMeta` ( - `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, - PRIMARY KEY (`name`), - UNIQUE KEY `name` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `SequelizeMeta` --- - -LOCK TABLES `SequelizeMeta` WRITE; -/*!40000 ALTER TABLE `SequelizeMeta` DISABLE KEYS */; -INSERT INTO `SequelizeMeta` VALUES ('201802190000-CreateUserTable.js'),('201802190003-CreateUserRegistrationProfileTable.js'),('201802190005-CreateOrganizationTable.js'),('201802190008-CreateOAuthClientTable.js'),('201802190009-CreateUserAuthorizedApplicationTable.js'),('201802190010-CreateRoleTable.js'),('201802190015-CreatePermissionTable.js'),('201802190020-CreateRoleAssignmentTable.js'),('201802190025-CreateRolePermissionTable.js'),('201802190030-CreateUserOrganizationTable.js'),('201802190035-CreateIotTable.js'),('201802190040-CreatePepProxyTable.js'),('201802190045-CreateAuthZForceTable.js'),('201802190050-CreateAuthTokenTable.js'),('201802190060-CreateOAuthAuthorizationCodeTable.js'),('201802190065-CreateOAuthAccessTokenTable.js'),('201802190070-CreateOAuthRefreshTokenTable.js'),('201802190075-CreateOAuthScopeTable.js'),('20180405125424-CreateUserTourAttribute.js'),('20180612134640-CreateEidasTable.js'); -/*!40000 ALTER TABLE `SequelizeMeta` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `auth_token` --- - -DROP TABLE IF EXISTS `auth_token`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `auth_token` ( - `access_token` varchar(255) NOT NULL, - `expires` datetime DEFAULT NULL, - `valid` tinyint(1) DEFAULT NULL, - `user_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - `pep_proxy_id` varchar(255) DEFAULT NULL, - PRIMARY KEY (`access_token`), - UNIQUE KEY `access_token` (`access_token`), - KEY `user_id` (`user_id`), - KEY `pep_proxy_id` (`pep_proxy_id`), - CONSTRAINT `auth_token_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE, - CONSTRAINT `auth_token_ibfk_2` FOREIGN KEY (`pep_proxy_id`) REFERENCES `pep_proxy` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `auth_token` --- - -LOCK TABLES `auth_token` WRITE; -/*!40000 ALTER TABLE `auth_token` DISABLE KEYS */; -INSERT INTO `auth_token` VALUES -('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa','2036-07-30 12:04:45',1,'aaaaaaaa-good-0000-0000-000000000000',NULL), -('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb','2036-07-30 12:38:13',1,'bbbbbbbb-good-0000-0000-000000000000',NULL), -('cccccccc-cccc-cccc-cccc-cccccccccccc','2036-07-31 09:36:13',1,'cccccccc-good-0000-0000-000000000000',NULL), -('51f2e380-c959-4dee-a0af-380f730137c3','2036-07-30 13:02:37',1,'admin',NULL); -/*!40000 ALTER TABLE `auth_token` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `authzforce` --- - -DROP TABLE IF EXISTS `authzforce`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `authzforce` ( - `az_domain` varchar(255) NOT NULL, - `policy` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - `version` int(11) DEFAULT NULL, - `oauth_client_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - PRIMARY KEY (`az_domain`), - KEY `oauth_client_id` (`oauth_client_id`), - CONSTRAINT `authzforce_ibfk_1` FOREIGN KEY (`oauth_client_id`) REFERENCES `oauth_client` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `authzforce` --- - -LOCK TABLES `authzforce` WRITE; -/*!40000 ALTER TABLE `authzforce` DISABLE KEYS */; -/*!40000 ALTER TABLE `authzforce` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `eidas_credentials` --- - -DROP TABLE IF EXISTS `eidas_credentials`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `eidas_credentials` ( - `id` char(36) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL, - `support_contact_person_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `support_contact_person_surname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `support_contact_person_email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `support_contact_person_telephone_number` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `support_contact_person_company` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `technical_contact_person_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `technical_contact_person_surname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `technical_contact_person_email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `technical_contact_person_telephone_number` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `technical_contact_person_company` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `organization_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `organization_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `oauth_client_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - `organization_nif` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `sp_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `attributes_list` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, - PRIMARY KEY (`id`), - UNIQUE KEY `oauth_client_id` (`oauth_client_id`), - CONSTRAINT `eidas_credentials_ibfk_1` FOREIGN KEY (`oauth_client_id`) REFERENCES `oauth_client` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `eidas_credentials` --- - -LOCK TABLES `eidas_credentials` WRITE; -/*!40000 ALTER TABLE `eidas_credentials` DISABLE KEYS */; -/*!40000 ALTER TABLE `eidas_credentials` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `iot` --- - -DROP TABLE IF EXISTS `iot`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `iot` ( - `id` varchar(255) NOT NULL, - `password` varchar(40) DEFAULT NULL, - `salt` varchar(40) DEFAULT NULL, - `oauth_client_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `oauth_client_id` (`oauth_client_id`), - CONSTRAINT `iot_ibfk_1` FOREIGN KEY (`oauth_client_id`) REFERENCES `oauth_client` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `iot` --- - -LOCK TABLES `iot` WRITE; -/*!40000 ALTER TABLE `iot` DISABLE KEYS */; -INSERT INTO `iot` VALUES ('iot_sensor_00000000-0000-0000-0000-000000000000','e9f7c64ec2895eec281f8fd36e588d1bc762bcca',NULL,'tutorial-dckr-site-0000-xpresswebapp'); -/*!40000 ALTER TABLE `iot` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `oauth_access_token` --- - -DROP TABLE IF EXISTS `oauth_access_token`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `oauth_access_token` ( - `access_token` varchar(255) NOT NULL, - `expires` datetime DEFAULT NULL, - `scope` varchar(255) DEFAULT NULL, - `refresh_token` varchar(255) DEFAULT NULL, - `valid` tinyint(1) DEFAULT NULL, - `extra` json DEFAULT NULL, - `oauth_client_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - `user_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - `iot_id` varchar(255) DEFAULT NULL, - `authorization_code` varchar(255) DEFAULT NULL, - PRIMARY KEY (`access_token`), - UNIQUE KEY `access_token` (`access_token`), - KEY `oauth_client_id` (`oauth_client_id`), - KEY `user_id` (`user_id`), - KEY `iot_id` (`iot_id`), - CONSTRAINT `oauth_access_token_ibfk_1` FOREIGN KEY (`oauth_client_id`) REFERENCES `oauth_client` (`id`) ON DELETE CASCADE, - CONSTRAINT `oauth_access_token_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE, - CONSTRAINT `oauth_access_token_ibfk_3` FOREIGN KEY (`iot_id`) REFERENCES `iot` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `oauth_access_token` --- - -LOCK TABLES `oauth_access_token` WRITE; -/*!40000 ALTER TABLE `oauth_access_token` DISABLE KEYS */; -INSERT INTO `oauth_access_token` VALUES -('15682667caa4bb5ac15056fee3836b2980288bf2','2016-07-30 12:14:21',NULL,NULL,NULL,NULL,'8ca60ce9-32f9-42d6-a013-a19b3af0c13d','admin',NULL,NULL), -('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa','2016-07-30 12:14:21',NULL,NULL,NULL,NULL,'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa','alice',NULL,NULL), -('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb','2016-07-30 12:14:21',NULL,NULL,NULL,NULL,'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb','bob',NULL,NULL), -('cccccccccccccccccccccccccccccccccccccccc','2016-07-30 12:14:21',NULL,NULL,NULL,NULL,'cccccccc-cccc-cccc-cccc-cccccccccccc','charlie',NULL,NULL), -('d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1','2016-07-30 12:14:21',NULL,NULL,NULL,NULL,'d1d1d1d1-dddd-dddd-dddd-d1d1d1d1d1d1','detective1',NULL,NULL), -('d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2','2016-07-30 12:14:21',NULL,NULL,NULL,NULL,'d2d2d2d2-dddd-dddd-dddd-d2d2d2d2d2d2','detective2',NULL,NULL), -('m1m1m1m1m1m1m1m1m1m1m1m1m1m1m1m1m1m1m1m1','2016-07-30 12:14:21',NULL,NULL,NULL,NULL,'m1m1m1m1-mmmm-mmmm-mmmm-m1m1m1m1m1m1','manager1',NULL,NULL), -('m2m2m2m2m2m2m2m2m2m2m2m2m2m2m2m2m2m2m2m2','2016-07-30 12:14:21',NULL,NULL,NULL,NULL,'m2m2m2m2-mmmm-mmmm-mmmm-m2m2m2m2m2m2','manager2',NULL,NULL); - -/*!40000 ALTER TABLE `oauth_access_token` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `oauth_authorization_code` --- - -DROP TABLE IF EXISTS `oauth_authorization_code`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `oauth_authorization_code` ( - `authorization_code` varchar(256) NOT NULL, - `expires` datetime DEFAULT NULL, - `redirect_uri` varchar(2000) DEFAULT NULL, - `scope` varchar(255) DEFAULT NULL, - `valid` tinyint(1) DEFAULT NULL, - `extra` json DEFAULT NULL, - `oauth_client_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - `user_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - PRIMARY KEY (`authorization_code`), - UNIQUE KEY `authorization_code` (`authorization_code`), - KEY `oauth_client_id` (`oauth_client_id`), - KEY `user_id` (`user_id`), - CONSTRAINT `oauth_authorization_code_ibfk_1` FOREIGN KEY (`oauth_client_id`) REFERENCES `oauth_client` (`id`) ON DELETE CASCADE, - CONSTRAINT `oauth_authorization_code_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `oauth_authorization_code` --- - -LOCK TABLES `oauth_authorization_code` WRITE; -/*!40000 ALTER TABLE `oauth_authorization_code` DISABLE KEYS */; -/*!40000 ALTER TABLE `oauth_authorization_code` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `oauth_client` --- - -DROP TABLE IF EXISTS `oauth_client`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `oauth_client` ( - `id` char(36) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL, - `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, - `secret` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - `url` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `redirect_uri` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `image` varchar(255) DEFAULT 'default', - `grant_type` varchar(255) DEFAULT NULL, - `response_type` varchar(255) DEFAULT NULL, - `client_type` varchar(15) DEFAULT NULL, - `scope` varchar(80) DEFAULT NULL, - `extra` json DEFAULT NULL, - `token_types` varchar(2000) DEFAULT 'bearer', - `jwt_secret` varchar(2000) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `oauth_client` --- - -LOCK TABLES `oauth_client` WRITE; -/*!40000 ALTER TABLE `oauth_client` DISABLE KEYS */; -INSERT INTO `oauth_client` VALUES -('tutorial-dckr-site-0000-xpresswebapp','FIWARE Tutorial', - 'FIWARE Application protected by OAuth2 and Keyrock','tutorial-dckr-site-0000-clientsecret', - 'http://localhost:3000','http://localhost:3000/login','default', - 'authorization_code,implicit,password,client_credentials,refresh_token','code',NULL,NULL,NULL,'bearer', NULL), -('tutorial-lcal-host-0000-xpresswebapp','localhost App', - 'Localhost Callback protected by OAuth2 and Keyrock','tutorial-lcal-host-0000-clientsecret', - 'http://localhost:3000','http://localhost:3000/login','default', - 'authorization_code,implicit,password,client_credentials,refresh_token','code',NULL,NULL,NULL,'bearer', NULL); - -/*!40000 ALTER TABLE `oauth_client` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `oauth_refresh_token` --- - -DROP TABLE IF EXISTS `oauth_refresh_token`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `oauth_refresh_token` ( - `refresh_token` varchar(256) NOT NULL, - `expires` datetime DEFAULT NULL, - `scope` varchar(255) DEFAULT NULL, - `oauth_client_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - `user_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - `iot_id` varchar(255) DEFAULT NULL, - `valid` tinyint(1) DEFAULT NULL, - `authorization_code` varchar(255) DEFAULT NULL, - PRIMARY KEY (`refresh_token`), - UNIQUE KEY `refresh_token` (`refresh_token`), - KEY `oauth_client_id` (`oauth_client_id`), - KEY `user_id` (`user_id`), - KEY `iot_id` (`iot_id`), - CONSTRAINT `oauth_refresh_token_ibfk_1` FOREIGN KEY (`oauth_client_id`) REFERENCES `oauth_client` (`id`) ON DELETE CASCADE, - CONSTRAINT `oauth_refresh_token_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE, - CONSTRAINT `oauth_refresh_token_ibfk_3` FOREIGN KEY (`iot_id`) REFERENCES `iot` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `oauth_refresh_token` --- - -LOCK TABLES `oauth_refresh_token` WRITE; -/*!40000 ALTER TABLE `oauth_refresh_token` DISABLE KEYS */; -INSERT INTO `oauth_refresh_token` VALUES ('4eb1f99f80f37c81a8ef85d92eae836919887e1e','2018-08-13 11:14:21',NULL,'8ca60ce9-32f9-42d6-a013-a19b3af0c13d','admin',NULL, NULL,NULL); -/*!40000 ALTER TABLE `oauth_refresh_token` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `oauth_scope` --- - -DROP TABLE IF EXISTS `oauth_scope`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `oauth_scope` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `scope` varchar(255) DEFAULT NULL, - `is_default` tinyint(1) DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `id` (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `oauth_scope` --- - -LOCK TABLES `oauth_scope` WRITE; -/*!40000 ALTER TABLE `oauth_scope` DISABLE KEYS */; -/*!40000 ALTER TABLE `oauth_scope` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `organization` --- - -DROP TABLE IF EXISTS `organization`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `organization` ( - `id` char(36) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL, - `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, - `website` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `image` varchar(255) DEFAULT 'default', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `organization` --- - -LOCK TABLES `organization` WRITE; -/*!40000 ALTER TABLE `organization` DISABLE KEYS */; -INSERT INTO `organization` VALUES -('security-team-0000-0000-000000000000','Security','Security Group for Store Detectives',NULL,'default'), -('managers-team-0000-0000-000000000000','Management','Management Group for Store Managers',NULL,'default'); -/*!40000 ALTER TABLE `organization` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `pep_proxy` --- - -DROP TABLE IF EXISTS `pep_proxy`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `pep_proxy` ( - `id` varchar(255) NOT NULL, - `password` varchar(40) DEFAULT NULL, - `salt` varchar(40) DEFAULT NULL, - `oauth_client_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `oauth_client_id` (`oauth_client_id`), - CONSTRAINT `pep_proxy_ibfk_1` FOREIGN KEY (`oauth_client_id`) REFERENCES `oauth_client` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `pep_proxy` --- - -LOCK TABLES `pep_proxy` WRITE; -/*!40000 ALTER TABLE `pep_proxy` DISABLE KEYS */; -INSERT INTO `pep_proxy` VALUES ('pep_proxy_00000000-0000-0000-0000-000000000000','e9f7c64ec2895eec281f8fd36e588d1bc762bcca',NULL,'tutorial-dckr-site-0000-xpresswebapp'); -/*!40000 ALTER TABLE `pep_proxy` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `permission` --- - -DROP TABLE IF EXISTS `permission`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `permission` ( - `id` char(36) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL, - `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, - `is_internal` tinyint(1) DEFAULT '0', - `action` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `resource` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `xml` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, - `oauth_client_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `oauth_client_id` (`oauth_client_id`), - CONSTRAINT `permission_ibfk_1` FOREIGN KEY (`oauth_client_id`) REFERENCES `oauth_client` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `permission` --- - -LOCK TABLES `permission` WRITE; -/*!40000 ALTER TABLE `permission` DISABLE KEYS */; -INSERT INTO `permission` VALUES -('1','Get and assign all internal application roles',NULL,1,NULL,NULL,NULL,'idm_admin_app'), -('2','Manage the application',NULL,1,NULL,NULL,NULL,'idm_admin_app'), -('3','Manage roles',NULL,1,NULL,NULL,NULL,'idm_admin_app'),('4','Manage authorizations',NULL,1,NULL,NULL,NULL,'idm_admin_app'), -('5','Get and assign all public application roles',NULL,1,NULL,NULL,NULL,'idm_admin_app'), -('6','Get and assign only public owned roles',NULL,1,NULL,NULL,NULL,'idm_admin_app'), -('increase-stck-0000-0000-000000000000','Order Stock','Increase Stock Count',0,'GET','/app/order-stock',NULL,'tutorial-dckr-site-0000-xpresswebapp'), -('entrance-open-0000-0000-000000000000','Unlock','Unlock main entrance',0,'POST','/door/unlock',NULL,'tutorial-dckr-site-0000-xpresswebapp'), -('alrmbell-ring-0000-0000-000000000000','Ring Alarm Bell',NULL,0,'POST','/bell/ring',NULL,'tutorial-dckr-site-0000-xpresswebapp'), -('pricechg-stck-0000-0000-000000000000','Access Price Changes',NULL,0,'GET','/app/price-change',NULL,'tutorial-dckr-site-0000-xpresswebapp'); -/*!40000 ALTER TABLE `permission` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `role` --- - -DROP TABLE IF EXISTS `role`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `role` ( - `id` char(36) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL, - `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `is_internal` tinyint(1) DEFAULT '0', - `oauth_client_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `oauth_client_id` (`oauth_client_id`), - CONSTRAINT `role_ibfk_1` FOREIGN KEY (`oauth_client_id`) REFERENCES `oauth_client` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `role` --- - -LOCK TABLES `role` WRITE; -/*!40000 ALTER TABLE `role` DISABLE KEYS */; -INSERT INTO `role` VALUES -('security-role-0000-0000-000000000000','Security Team',0,'tutorial-dckr-site-0000-xpresswebapp'), -('managers-role-0000-0000-000000000000','Management',0,'tutorial-dckr-site-0000-xpresswebapp'), -('provider','Provider',1,'idm_admin_app'),('purchaser','Purchaser',1,'idm_admin_app'); -/*!40000 ALTER TABLE `role` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `role_assignment` --- - -DROP TABLE IF EXISTS `role_assignment`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `role_assignment` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `role_organization` varchar(255) DEFAULT NULL, - `oauth_client_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - `role_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - `organization_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - `user_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `oauth_client_id` (`oauth_client_id`), - KEY `role_id` (`role_id`), - KEY `organization_id` (`organization_id`), - KEY `user_id` (`user_id`), - CONSTRAINT `role_assignment_ibfk_1` FOREIGN KEY (`oauth_client_id`) REFERENCES `oauth_client` (`id`) ON DELETE CASCADE, - CONSTRAINT `role_assignment_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`) ON DELETE CASCADE, - CONSTRAINT `role_assignment_ibfk_3` FOREIGN KEY (`organization_id`) REFERENCES `organization` (`id`) ON DELETE CASCADE, - CONSTRAINT `role_assignment_ibfk_4` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `role_assignment` --- - -LOCK TABLES `role_assignment` WRITE; -/*!40000 ALTER TABLE `role_assignment` DISABLE KEYS */; -INSERT INTO `role_assignment` VALUES -(1,NULL,'8ca60ce9-32f9-42d6-a013-a19b3af0c13d','provider',NULL,'96154659-cb3b-4d2d-afef-18d6aec0518e'), -(2,'member','8ca60ce9-32f9-42d6-a013-a19b3af0c13d','provider','74f5299e-3247-468c-affb-957cda03f0c4',NULL), -(3,NULL,'222eda27-958b-4f0c-a5cb-e4114fb170c3','provider',NULL,'admin'), -(4,NULL,'222eda27-958b-4f0c-a5cb-e4114fb170c3','provider',NULL,'96154659-cb3b-4d2d-afef-18d6aec0518e'), -(5,NULL,'tutorial-dckr-site-0000-xpresswebapp','provider',NULL,'aaaaaaaa-good-0000-0000-000000000000'), -(6,NULL,'tutorial-lcal-host-0000-xpresswebapp','provider',NULL,'aaaaaaaa-good-0000-0000-000000000000'), -(10,NULL,'tutorial-dckr-site-0000-xpresswebapp','security-role-0000-0000-000000000000',NULL,'cccccccc-good-0000-0000-000000000000'), -(11,'member','tutorial-dckr-site-0000-xpresswebapp','security-role-0000-0000-000000000000','security-team-0000-0000-000000000000',NULL), -(12,NULL,'tutorial-dckr-site-0000-xpresswebapp','managers-role-0000-0000-000000000000',NULL,'bbbbbbbb-good-0000-0000-000000000000'), -(13,'member','tutorial-dckr-site-0000-xpresswebapp','managers-role-0000-0000-000000000000','managers-team-0000-0000-000000000000',NULL); - -/*!40000 ALTER TABLE `role_assignment` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `role_permission` --- - -DROP TABLE IF EXISTS `role_permission`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `role_permission` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `role_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - `permission_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `role_id` (`role_id`), - KEY `permission_id` (`permission_id`), - CONSTRAINT `role_permission_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`) ON DELETE CASCADE, - CONSTRAINT `role_permission_ibfk_2` FOREIGN KEY (`permission_id`) REFERENCES `permission` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `role_permission` --- - -LOCK TABLES `role_permission` WRITE; -/*!40000 ALTER TABLE `role_permission` DISABLE KEYS */; -INSERT INTO `role_permission` VALUES -(1,'provider','1'),(2,'provider','2'),(3,'provider','3'),(4,'provider','4'),(5,'provider','5'),(6,'provider','6'), -(7,'purchaser','5'), -(8,'security-role-0000-0000-000000000000','alrmbell-ring-0000-0000-000000000000'), -(9,'security-role-0000-0000-000000000000','entrance-open-0000-0000-000000000000'), -(10,'managers-role-0000-0000-000000000000','alrmbell-ring-0000-0000-000000000000'), -(11,'managers-role-0000-0000-000000000000','increase-stck-0000-0000-000000000000'), -(12,'managers-role-0000-0000-000000000000','pricechg-stck-0000-0000-000000000000'); - - - - -/*!40000 ALTER TABLE `role_permission` ENABLE KEYS */; -UNLOCK TABLES; - - --- --- Table structure for table `trusted_application` --- - -DROP TABLE IF EXISTS `trusted_application`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `trusted_application` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `oauth_client_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - `trusted_oauth_client_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `oauth_client_id` (`oauth_client_id`), - KEY `trusted_oauth_client_id` (`trusted_oauth_client_id`), - CONSTRAINT `trusted_application_ibfk_1` FOREIGN KEY (`oauth_client_id`) REFERENCES `oauth_client` (`id`) ON DELETE CASCADE, - CONSTRAINT `trusted_application_ibfk_2` FOREIGN KEY (`trusted_oauth_client_id`) REFERENCES `oauth_client` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `trusted_application` --- - -LOCK TABLES `trusted_application` WRITE; -/*!40000 ALTER TABLE `trusted_application` DISABLE KEYS */; -/*!40000 ALTER TABLE `trusted_application` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `user` --- - -DROP TABLE IF EXISTS `user`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `user` ( - `id` char(36) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL, - `username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, - `website` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, - `image` varchar(255) DEFAULT 'default', - `gravatar` tinyint(1) DEFAULT '0', - `email` varchar(255) DEFAULT NULL, - `password` varchar(40) DEFAULT NULL, - `salt` varchar(40) DEFAULT NULL, - `date_password` datetime DEFAULT NULL, - `enabled` tinyint(1) DEFAULT '0', - `admin` tinyint(1) DEFAULT '0', - `extra` varchar(255) DEFAULT NULL, - `scope` varchar(80) DEFAULT NULL, - `starters_tour_ended` tinyint(1) DEFAULT '0', - `eidas_id` varchar(255) DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `email` (`email`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `user` --- - -LOCK TABLES `user` WRITE; -/*!40000 ALTER TABLE `user` DISABLE KEYS */; -INSERT INTO `user` VALUES -('aaaaaaaa-good-0000-0000-000000000000','alice', 'Alice is the admin',NULL,'default',0,'alice-the-admin@test.com','89e48c55e4e4b3b86141fb15f5e6abf70f8c32c0', 'fbba54b6750b16e8', '2018-07-30 11:41:14',1,1,NULL,NULL,0,NULL), -('bbbbbbbb-good-0000-0000-000000000000','bob','Bob is the regional manager',NULL,'default',0,'bob-the-manager@test.com','89e48c55e4e4b3b86141fb15f5e6abf70f8c32c0', 'fbba54b6750b16e8', '2018-07-30 11:41:14',1,0,NULL,NULL,0,NULL), -('cccccccc-good-0000-0000-000000000000','charlie','Charlie is head of security',NULL,'default',0,'charlie-security@test.com','89e48c55e4e4b3b86141fb15f5e6abf70f8c32c0', 'fbba54b6750b16e8', '2018-07-30 11:41:14',1,0,NULL,NULL,0,NULL), -('manager1-good-0000-0000-000000000000','manager1','Manager works for Bob',NULL,'default',0,'manager1@test.com','89e48c55e4e4b3b86141fb15f5e6abf70f8c32c0', 'fbba54b6750b16e8', '2018-07-30 11:41:14',1,0,NULL,NULL,0,NULL), -('manager2-good-0000-0000-000000000000','manager2','Manager works for Bob',NULL,'default',0,'manager2@test.com','89e48c55e4e4b3b86141fb15f5e6abf70f8c32c0', 'fbba54b6750b16e8', '2018-07-30 11:41:14',1,0,NULL,NULL,0,NULL), -('detective1-good-0000-0000-000000000000','detective1','Detective works for Charlie',NULL,'default',0,'detective1@test.com','89e48c55e4e4b3b86141fb15f5e6abf70f8c32c0', 'fbba54b6750b16e8', '2018-07-30 11:41:14',1,0,NULL,NULL,0,NULL), -('detective2-good-0000-0000-000000000000','detective2','Detective works for Charlie',NULL,'default',0,'detective2@test.com','89e48c55e4e4b3b86141fb15f5e6abf70f8c32c0', 'fbba54b6750b16e8', '2018-07-30 11:41:14',1,0,NULL,NULL,0,NULL), -('eve-evil-0000-0000-000000000000','eve','Eve the Eavesdropper',NULL,'default',0,'eve@example.com','89e48c55e4e4b3b86141fb15f5e6abf70f8c32c0', 'fbba54b6750b16e8', '2018-07-30 11:41:14',1,0,NULL,NULL,0,NULL), -('mallory-evil-0000-0000-000000000000','mallory','Mallory the malicious attacker',NULL,'default',0,'mallory@example.com','89e48c55e4e4b3b86141fb15f5e6abf70f8c32c0', 'fbba54b6750b16e8', '2018-07-30 11:41:14',1,0,NULL,NULL,0,NULL), -('rob-evil-0000-0000-000000000000','rob','Rob the Robber' ,NULL,'default',0,'rob@example.com','89e48c55e4e4b3b86141fb15f5e6abf70f8c32c0', 'fbba54b6750b16e8', '2018-07-30 11:41:14',1,0,NULL,NULL,0,NULL);/*!40000 ALTER TABLE `user` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `user_authorized_application` --- - -DROP TABLE IF EXISTS `user_authorized_application`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `user_authorized_application` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `user_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - `oauth_client_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `user_id` (`user_id`), - KEY `oauth_client_id` (`oauth_client_id`), - CONSTRAINT `user_authorized_application_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE, - CONSTRAINT `user_authorized_application_ibfk_2` FOREIGN KEY (`oauth_client_id`) REFERENCES `oauth_client` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `user_authorized_application` --- - -LOCK TABLES `user_authorized_application` WRITE; -/*!40000 ALTER TABLE `user_authorized_application` DISABLE KEYS */; -INSERT INTO `user_authorized_application` VALUES (1,'admin','8ca60ce9-32f9-42d6-a013-a19b3af0c13d'); -/*!40000 ALTER TABLE `user_authorized_application` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `user_organization` --- - -DROP TABLE IF EXISTS `user_organization`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `user_organization` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `role` varchar(10) DEFAULT NULL, - `user_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - `organization_id` char(36) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `user_id` (`user_id`), - KEY `organization_id` (`organization_id`), - CONSTRAINT `user_organization_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE, - CONSTRAINT `user_organization_ibfk_2` FOREIGN KEY (`organization_id`) REFERENCES `organization` (`id`) ON DELETE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `user_organization` --- - -LOCK TABLES `user_organization` WRITE; -/*!40000 ALTER TABLE `user_organization` DISABLE KEYS */; -INSERT INTO `user_organization` VALUES -(2,'owner', 'aaaaaaaa-good-0000-0000-000000000000','security-team-0000-0000-000000000000'), -(3,'owner', 'aaaaaaaa-good-0000-0000-000000000000','managers-team-0000-0000-000000000000'), -(4,'owner', 'bbbbbbbb-good-0000-0000-000000000000','managers-team-0000-0000-000000000000'), -(5,'member','manager1-good-0000-0000-000000000000','managers-team-0000-0000-000000000000'), -(6,'member','manager2-good-0000-0000-000000000000','managers-team-0000-0000-000000000000'), -(7,'owner', 'cccccccc-good-0000-0000-000000000000','security-team-0000-0000-000000000000'), -(8,'member','detective1-good-0000-0000-000000000000','security-team-0000-0000-000000000000'), -(9,'member','detective2-good-0000-0000-000000000000','security-team-0000-0000-000000000000'); -/*!40000 ALTER TABLE `user_organization` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `user_registration_profile` --- - -DROP TABLE IF EXISTS `user_registration_profile`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `user_registration_profile` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `activation_key` varchar(255) DEFAULT NULL, - `activation_expires` datetime DEFAULT NULL, - `reset_key` varchar(255) DEFAULT NULL, - `reset_expires` datetime DEFAULT NULL, - `verification_key` varchar(255) DEFAULT NULL, - `verification_expires` datetime DEFAULT NULL, - `user_email` varchar(255) DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `user_email` (`user_email`), - CONSTRAINT `user_registration_profile_ibfk_1` FOREIGN KEY (`user_email`) REFERENCES `user` (`email`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `user_registration_profile` --- - -LOCK TABLES `user_registration_profile` WRITE; -/*!40000 ALTER TABLE `user_registration_profile` DISABLE KEYS */; -INSERT INTO `user_registration_profile` VALUES (1,'b26roiin0r','2018-07-31 10:03:53',NULL,NULL,NULL,NULL,'eve@test.com'); -/*!40000 ALTER TABLE `user_registration_profile` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2018-08-10 9:03:58 diff --git a/test/unit/add-test.js b/test/unit/add-test.js deleted file mode 100644 index 3820b94..0000000 --- a/test/unit/add-test.js +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2019 - Universidad Politécnica de Madrid. - * - * This file is part of Keyrock - * - */ - -//var iotaOPCUA= require('../../../../index.js'), -/* -const config = require('./config-test.js'); -const nock = require('nock'); -const async = require('async'); -const request = require('request'); -*/ - -describe('Attribute alias', function() { - beforeEach(function(done) { - // Set up - done(); - }); - - afterEach(function(done) { - // Clean Up - done(); - }); - - describe('When a new multiple measure arrives with a timestamp in an attribute alias', function() { - beforeEach(function() { - // Set Up - }); - it('should send its value to the Context Broker', function(done) { - // Run test - done(); - }); - }); -}); diff --git a/test/unit/authentication-test.js b/test/unit/authentication-test.js new file mode 100644 index 0000000..008336d --- /dev/null +++ b/test/unit/authentication-test.js @@ -0,0 +1,208 @@ +/* + * Copyright 2021 - Universidad Politécnica de Madrid. + * + * This file is part of Keyrock + * + */ + +const got = require('got'); +const should = require('should'); +const nock = require('nock'); +const cache = require('../../lib/cache'); + +const pep_proxy_no_header = { + prefixUrl: 'http:/localhost:80', + throwHttpErrors: false +}; + +const pep_proxy_with_header = { + prefixUrl: 'http:/localhost:80', + throwHttpErrors: false, + headers: { 'x-auth-token': '111111111' } +}; + +const pep_proxy_with_magic_key = { + prefixUrl: 'http:/localhost:80', + throwHttpErrors: false, + headers: { 'x-auth-token': '999999999' } +}; + +const config = { + magic_key: '999999999', + pep_port: 80, + pep: { + app_id: 'application_id', + trusted_apps: [] + }, + idm: { + host: 'keyrock.com', + port: '3000', + ssl: false + }, + app: { + host: 'fiware.org', + port: '1026', + ssl: false // Use true if the app server listens in https + }, + organizations: { + enabled: false + }, + cache_time: 300, + public_paths: ['/public'], + authorization: { + enabled: false, + pdp: 'idm', // idm|iShare|xacml|authzforce + header: undefined, // NGSILD-Tenant|fiware-service + azf: { + protocol: 'http', + host: 'localhost', + port: 8080, + custom_policy: undefined // use undefined to default policy checks (HTTP verb + path). + } + } +}; + +const keyrock_user_response = { + app_id: 'application_id', + trusted_apps: [], + id : 'username', + displayName: 'Some User' +}; + +describe('Authentication: Keyrock IDM', function () { + let pep; + let contextBrokerMock; + let idmMock; + + beforeEach(function (done) { + const app = require('../../app'); + pep = app.start_server('12345', config); + cache.flush(); + done(); + }); + + afterEach(function (done) { + pep.close(config.pep_port); + done(); + }); + + describe('When a URL is requested and no token is present', function () { + beforeEach(function () { + // Set Up + }); + it('should deny access', function (done) { + got.get('restricted_path', pep_proxy_no_header).then((response) => { + should.equal(response.statusCode, 401); + done(); + }); + }); + }); + + describe('When a public path is requested', function () { + beforeEach(function () { + // Set Up + nock.cleanAll(); + contextBrokerMock = nock('http://fiware.org:1026').get('/public').reply(200, {}); + }); + it('should allow access', function (done) { + got.get('public', pep_proxy_no_header).then((response) => { + contextBrokerMock.done(); + should.equal(response.statusCode, 200); + done(); + }); + }); + }); + + describe('When a restricted path is requested and the token matches the magic key', function () { + beforeEach(function () { + nock.cleanAll(); + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); + }); + it('should allow access', function (done) { + got.get('restricted', pep_proxy_with_magic_key).then((response) => { + contextBrokerMock.done(); + should.equal(response.statusCode, 200); + done(); + }); + }); + }); + + describe('When a restricted path is requested for a legitimate user', function () { + beforeEach(function () { + // Set Up + nock.cleanAll(); + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=111111111&app_id=application_id') + .reply(200, keyrock_user_response); + }); + it('should authenticate the user and allow access', function (done) { + got.get('restricted', pep_proxy_with_header).then((response) => { + contextBrokerMock.done(); + idmMock.done(); + should.equal(response.statusCode, 200); + done(); + }); + }); + }); + + describe('When a restricted path is requested for a forbidden user', function () { + beforeEach(function () { + // Set Up + nock.cleanAll(); + idmMock = nock('http://keyrock.com:3000').get('/user?access_token=111111111&app_id=application_id').reply(401); + }); + it('should authenticate the user and deny access', function (done) { + got.get('restricted', pep_proxy_with_header).then((response) => { + contextBrokerMock.done(); + idmMock.done(); + should.equal(response.statusCode, 401); + done(); + }); + }); + }); + + describe('When a non-existant restricted path is requested', function () { + beforeEach(function () { + // Set Up + nock.cleanAll(); + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(404); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=111111111&app_id=application_id') + .reply(200, keyrock_user_response); + }); + it('should authenticate the user and proxy the error', function (done) { + got.get('restricted', pep_proxy_with_header).then((response) => { + contextBrokerMock.done(); + idmMock.done(); + should.equal(response.statusCode, 404); + done(); + }); + }); + }); + + describe('When the same restricted path is requested multiple times', function () { + beforeEach(function () { + // Set Up + nock.cleanAll(); + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').times(2).reply(200, {}); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=111111111&app_id=application_id') + .reply(200, keyrock_user_response); + }); + it('should access the user from cache', function (done) { + got + .get('restricted', pep_proxy_with_header) + .then((firstResponse) => { + should.equal(firstResponse.statusCode, 200); + return got.get('restricted', pep_proxy_with_header); + }) + .then((secondResponse) => { + contextBrokerMock.done(); + idmMock.done(); + should.equal(secondResponse.statusCode, 200); + done(); + }); + }); + }); +}); diff --git a/test/unit/authzforce-pdp-test.js b/test/unit/authzforce-pdp-test.js new file mode 100644 index 0000000..79e1c1e --- /dev/null +++ b/test/unit/authzforce-pdp-test.js @@ -0,0 +1,182 @@ +/* + * Copyright 2021 - Universidad Politécnica de Madrid. + * + * This file is part of PEP-Proxy + * + */ + +const got = require('got'); +const should = require('should'); +const nock = require('nock'); +const cache = require('../../lib/cache'); + +const pep_proxy_with_header = { + prefixUrl: 'http:/localhost:80', + throwHttpErrors: false, + headers: { 'x-auth-token': '111111111' } +}; + +const keyrock_user_with_azf = { + app_id: 'application_id', + trusted_apps: [], + app_azf_domain: 'authzforce', + roles: [ + { + id: 'managers-role-0000-0000-000000000000', + name: 'Management' + } + ], + organizations: [ + { + roles: [ + { + id: 'my-organization-0000-0000-000000000000', + name: 'Organization' + } + ] + } + ] +}; + +const authzforce_permit_response = ` + + + Permit + +`; + +const authzforce_deny_response = ` + + + Deny + +`; + +const config = { + pep_port: 80, + pep: { + app_id: 'application_id', + trusted_apps: [] + }, + idm: { + host: 'keyrock.com', + port: '3000', + ssl: false + }, + app: { + host: 'fiware.org', + port: '1026', + ssl: false // Use true if the app server listens in https + }, + organizations: { + enabled: false + }, + cache_time: 300, + public_paths: [], + authorization: { + enabled: true, + pdp: 'authzforce', // idm|iShare|xacml|authzforce + header: undefined, // NGSILD-Tenant|fiware-service + azf: { + protocol: 'http', + host: 'authzforce.com', + port: 8080, + custom_policy: undefined // use undefined to default policy checks (HTTP verb + path). + } + } +}; + +describe('Authorization: Authzforce PDP', function () { + let pep; + let contextBrokerMock; + let idmMock; + let authzforceMock; + + beforeEach(function (done) { + const app = require('../../app'); + pep = app.start_server('12345', config); + cache.flush(); + done(); + }); + + afterEach(function (done) { + pep.close(config.pep_port); + done(); + }); + + describe('When a restricted path is requested for a legitimate user', function () { + beforeEach(function () { + // Set Up + nock.cleanAll(); + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=111111111&app_id=application_id&authzforce=true') + .reply(200, keyrock_user_with_azf); + + authzforceMock = nock('http://authzforce.com:8080') + .post('/authzforce-ce/domains/authzforce/pdp') + .reply(200, authzforce_permit_response); + }); + + it('should allow access', function (done) { + got.get('restricted', pep_proxy_with_header).then((response) => { + contextBrokerMock.done(); + idmMock.done(); + authzforceMock.done(); + should.equal(response.statusCode, 200); + done(); + }); + }); + }); + + describe('When a restricted path is requested for a forbidden user', function () { + beforeEach(function () { + // Set Up + nock.cleanAll(); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=111111111&app_id=application_id&authzforce=true') + .reply(200, keyrock_user_with_azf); + + authzforceMock = nock('http://authzforce.com:8080') + .post('/authzforce-ce/domains/authzforce/pdp') + .reply(200, authzforce_deny_response); + }); + + it('should deny access when denied', function (done) { + got.get('restricted', pep_proxy_with_header).then((response) => { + idmMock.done(); + authzforceMock.done(); + should.equal(response.statusCode, 401); + done(); + }); + }); + }); + + describe('When no AZF domain is returned', function () { + beforeEach(function () { + // Set Up + nock.cleanAll(); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=111111111&app_id=application_id&authzforce=true') + .reply(200, { + app_id: 'application_id', + trusted_apps: [] + }); + }); + it('should deny access', function (done) { + got.get('restricted', pep_proxy_with_header).then((response) => { + idmMock.done(); + should.equal(response.statusCode, 401); + done(); + }); + }); + }); +}); diff --git a/test/unit/connection-test.js b/test/unit/connection-test.js new file mode 100644 index 0000000..536bd96 --- /dev/null +++ b/test/unit/connection-test.js @@ -0,0 +1,132 @@ +/* + * Copyright 2021 - Universidad Politécnica de Madrid. + * + * This file is part of Keyrock + * + */ + +const config_service = require('../../lib/config_service'); +const should = require('should'); +const nock = require('nock'); +const IDM = require('../../lib/pdp/idm'); +const Authzforce = require('../../lib/pdp/azf'); +const cache = require('../../lib/cache'); + +const config = { + pep_port: 80, + pep: { + app_id: 'application_id', + trusted_apps: [], + username: 'user', + password: 'password' + }, + idm: { + host: 'keyrock.com', + port: '3000', + ssl: false + }, + app: { + host: 'fiware.org', + port: '1026', + ssl: false // Use true if the app server listens in https + }, + organizations: { + enabled: false + }, + cache_time: 300, + public_paths: [], + authorization: { + enabled: true, + pdp: 'idm', // idm|iShare|xacml|authzforce + header: undefined, // NGSILD-Tenant|fiware-service + azf: { + protocol: 'http', + host: 'authzforce.com', + port: 8080, + custom_policy: undefined // use undefined to default policy checks (HTTP verb + path). + } + } +}; + +describe('Connection Tests', function () { + let idmMock; + let authzforceMock; + + beforeEach(function (done) { + config_service.set_config(config, true); + cache.flush(); + done(); + }); + + afterEach(function (done) { + done(); + }); + + describe('When connecting to Authzforce and it is present', function () { + beforeEach(function () { + // Set Up + authzforceMock = nock('http://authzforce.com:8080').get('/').reply(200, {}); + }); + it('should not error', function (done) { + Authzforce.checkConnectivity() + .then(() => { + authzforceMock.done(); + done(); + }) + .catch((err) => { + should.fail("error was thrown when it shouldn't have been", err); + }); + }); + }); + + describe('When connecting to Keyrock and it is present', function () { + beforeEach(function () { + // Set Up + idmMock = nock('http://keyrock.com:3000').get('/version').reply(200, {}); + }); + it('should not error', function (done) { + IDM.checkConnectivity() + .then(() => { + idmMock.done(); + done(); + }) + .catch((err) => { + should.fail("error was thrown when it shouldn't have been", err); + }); + }); + }); + + describe('When authenticating the PEP with Keyrock', function () { + beforeEach(function () { + // Set Up + idmMock = nock('http://keyrock.com:3000').post('/v3/auth/tokens').reply(200, {}); + }); + it('should not error', function (done) { + IDM.authenticatePEP() + .then(() => { + idmMock.done(); + done(); + }) + .catch((err) => { + should.fail("error was thrown when it shouldn't have been", err); + }); + }); + }); + + describe('When authenticating a misconfigured PEP with Keyrock', function () { + beforeEach(function () { + // Set Up + idmMock = nock('http://keyrock.com:3000').post('/v3/auth/tokens').reply(401); + }); + it('should error', function (done) { + IDM.authenticatePEP() + .then(() => { + should.fail('no error was thrown when it should have been'); + }) + .catch(() => { + idmMock.done(); + done(); + }); + }); + }); +}); diff --git a/test/unit/keyrock-pdp-test.js b/test/unit/keyrock-pdp-test.js new file mode 100644 index 0000000..9585efe --- /dev/null +++ b/test/unit/keyrock-pdp-test.js @@ -0,0 +1,194 @@ +/* + * Copyright 2021 - Universidad Politécnica de Madrid. + * + * This file is part of PEP-Proxy + * + */ + +const got = require('got'); +const should = require('should'); +const nock = require('nock'); +const cache = require('../../lib/cache'); + +const pep_proxy_with_header = { + prefixUrl: 'http:/localhost:80', + throwHttpErrors: false, + headers: { 'x-auth-token': '111111111' } +}; + +const pep_proxy_with_header_and_body = { + prefixUrl: 'http:/localhost:80', + throwHttpErrors: false, + headers: { 'x-auth-token': '111111111' }, + body: 'HELLO' +}; + +const keyrock_deny_response = { + app_id: 'application_id', + trusted_apps: [], + authorization_decision: 'Deny' +}; + +const keyrock_permit_response = { + app_id: 'application_id', + trusted_apps: [], + authorization_decision: 'Permit' +}; + +const config = { + pep_port: 80, + pep: { + app_id: 'application_id', + trusted_apps: [] + }, + idm: { + host: 'keyrock.com', + port: '3000', + ssl: false + }, + app: { + host: 'fiware.org', + port: '1026', + ssl: false // Use true if the app server listens in https + }, + organizations: { + enabled: false + }, + cache_time: 300, + public_paths: [], + authorization: { + enabled: true, + pdp: 'idm' // idm|iShare|xacml|authzforce + } +}; + +describe('Authorization: Keyrock PDP', function () { + let pep; + let contextBrokerMock; + let idmMock; + + beforeEach(function (done) { + const app = require('../../app'); + pep = app.start_server('12345', config); + cache.flush(); + done(); + }); + + afterEach(function (done) { + pep.close(config.pep_port); + done(); + }); + + describe('When a restricted path is requested for a legitimate user', function () { + beforeEach(function () { + // Set Up + nock.cleanAll(); + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') + .reply(200, keyrock_permit_response); + }); + it('should allow access', function (done) { + got.get('restricted', pep_proxy_with_header).then((response) => { + contextBrokerMock.done(); + idmMock.done(); + should.equal(response.statusCode, 200); + done(); + }); + }); + }); + + describe('When a restricted path is requested and the app-id is not found', function () { + beforeEach(function () { + // Set Up + nock.cleanAll(); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') + .reply(200, { + app_id: '', + trusted_apps: [], + authorization_decision: 'Permit' + }); + }); + it('should deny access', function (done) { + got.get('restricted', pep_proxy_with_header).then((response) => { + idmMock.done(); + should.equal(response.statusCode, 401); + done(); + }); + }); + }); + + describe('When a restricted path is requested for a forbidden user', function () { + beforeEach(function () { + // Set Up + nock.cleanAll(); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') + .reply(200, keyrock_deny_response); + }); + it('should deny access', function (done) { + got.get('restricted', pep_proxy_with_header).then((response) => { + idmMock.done(); + should.equal(response.statusCode, 401); + done(); + }); + }); + }); + + describe('When the same action on a restricted path multiple times', function () { + beforeEach(function () { + // Set Up + nock.cleanAll(); + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').times(2).reply(200, {}); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') + .reply(200, keyrock_permit_response); + }); + it('should access the user action from cache', function (done) { + got + .get('restricted', pep_proxy_with_header) + .then((firstResponse) => { + should.equal(firstResponse.statusCode, 200); + return got.get('restricted', pep_proxy_with_header); + }) + .then((secondResponse) => { + contextBrokerMock.done(); + idmMock.done(); + should.equal(secondResponse.statusCode, 200); + done(); + }); + }); + }); + + describe('When the same user request two different actions on a restricted path', function () { + beforeEach(function () { + // Set Up + nock.cleanAll(); + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); + + contextBrokerMock.post('/restricted').reply(204); + + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') + .reply(200, keyrock_permit_response); + idmMock + .get('/user?access_token=111111111&app_id=application_id&action=POST&resource=/restricted') + .reply(200, keyrock_permit_response); + }); + it('should not access the user from cache', function (done) { + got + .get('restricted', pep_proxy_with_header) + .then((firstResponse) => { + should.equal(firstResponse.statusCode, 200); + return got.post('restricted', pep_proxy_with_header_and_body); + }) + .then((secondResponse) => { + contextBrokerMock.done(); + idmMock.done(); + should.equal(secondResponse.statusCode, 204); + done(); + }); + }); + }); +}); diff --git a/test/unit/payload-pdp-test.js b/test/unit/payload-pdp-test.js new file mode 100644 index 0000000..0cf8ebb --- /dev/null +++ b/test/unit/payload-pdp-test.js @@ -0,0 +1,224 @@ +/* + * Copyright 2021 - Universidad Politécnica de Madrid. + * + * This file is part of PEP-Proxy + * + */ + +const got = require('got'); +const should = require('should'); +const nock = require('nock'); +const cache = require('../../lib/cache'); + +const ngsiPayload = [ + { + id: 'urn:ngsi-ld:TemperatureSensor:002', + type: 'TemperatureSensor', + category: { + type: 'Property', + value: 'sensor' + }, + temperature: { + type: 'Property', + value: 21, + unitCode: 'CEL' + } + }, + { + id: 'urn:ngsi-ld:TemperatureSensor:003', + type: 'TemperatureSensor', + category: { + type: 'Property', + value: 'sensor' + }, + temperature: { + type: 'Property', + value: 27, + unitCode: 'CEL' + } + } +]; +const keyrock_user_response = { + app_id: 'application_id', + trusted_apps: [], + roles: [ + { + id: 'managers-role-0000-0000-000000000000', + name: 'Management' + } + ] +}; + +const pep_proxy_with_headers = { + prefixUrl: 'http:/localhost:80', + throwHttpErrors: false, + headers: { 'x-auth-token': '111111111', 'fiware-service': 'smart-gondor' , 'x-forwarded-for': 'example.com'} +}; +const pep_proxy_with_headers_and_body = { + prefixUrl: 'http:/localhost:80', + throwHttpErrors: false, + headers: { 'x-auth-token': '111111111', 'fiware-service': 'smart-gondor' }, + json: ngsiPayload +}; + +const xacml_permit_response = { + Response: [ + { + Decision: 'Permit', + Status: { + StatusCode: { + Value: 'urn:oasis:names:tc:xacml:1.0:status:ok', + StatusCode: { Value: 'urn:oasis:names:tc:xacml:1.0:status:ok' } + } + } + } + ] +}; + +const xacml_deny_response = { + Response: [ + { + Decision: 'Deny', + Status: { + StatusCode: { + Value: 'urn:oasis:names:tc:xacml:1.0:status:ok', + StatusCode: { Value: 'urn:oasis:names:tc:xacml:1.0:status:ok' } + } + } + } + ] +}; + +const config = { + pep_port: 80, + pep: { + app_id: 'application_id', + trusted_apps: [] + }, + idm: { + host: 'keyrock.com', + port: '3000', + ssl: false + }, + app: { + host: 'fiware.org', + port: '1026', + ssl: false // Use true if the app server listens in https + }, + organizations: { + enabled: false + }, + cache_time: 300, + public_paths: [], + authorization: { + enabled: true, + pdp: 'xacml', // idm|iShare|xacml|authzforce + header: 'fiware-service' + } +}; + +describe('Authorization: Payload PDP', function () { + let pep; + let contextBrokerMock; + let idmMock; + + beforeEach(function (done) { + const app = require('../../app'); + pep = app.start_server('12345', config); + cache.flush(); + done(); + }); + + afterEach(function (done) { + pep.close(config.pep_port); + done(); + }); + + describe('When a restricted URL is requested by a legitimate user', function () { + beforeEach(function () { + // Set Up + nock.cleanAll(); + contextBrokerMock = nock('http://fiware.org:1026').get('/path/entities/urn:ngsi-ld:entity:1111').reply(200, {}); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=111111111&app_id=application_id') + .reply(200, keyrock_user_response); + + idmMock.post('/xacml').reply(200, xacml_permit_response); + }); + + it('should allow access', function (done) { + got.get('path/entities/urn:ngsi-ld:entity:1111', pep_proxy_with_headers).then((response) => { + contextBrokerMock.done(); + idmMock.done(); + should.equal(response.statusCode, 200); + done(); + }); + }); + }); + + describe('When a restricted URL is requested by a forbidden user', function () { + beforeEach(function () { + // Set Up + nock.cleanAll(); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=111111111&app_id=application_id') + .reply(200, keyrock_user_response); + + idmMock.post('/xacml').reply(200, xacml_deny_response); + }); + + it('should deny access', function (done) { + got.get('path/entities/urn:ngsi-ld:entity:1111', pep_proxy_with_headers).then((response) => { + idmMock.done(); + should.equal(response.statusCode, 401); + done(); + }); + }); + }); + + describe('When a restricted URL with a query string is requested', function () { + beforeEach(function () { + // Set Up + nock.cleanAll(); + contextBrokerMock = nock('http://fiware.org:1026') + .get('/path/entities/?ids=urn:ngsi-ld:entity:1111&type=entity') + .reply(200, {}); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=111111111&app_id=application_id') + .reply(200, keyrock_user_response); + + idmMock.post('/xacml').reply(200, xacml_permit_response); + }); + + it('should allow access based on entities', function (done) { + got.get('path/entities/?ids=urn:ngsi-ld:entity:1111&type=entity', pep_proxy_with_headers).then((response) => { + contextBrokerMock.done(); + idmMock.done(); + should.equal(response.statusCode, 200); + done(); + }); + }); + }); + + describe('When a restricted URL with a payload body is requested', function () { + beforeEach(function () { + // Set Up + nock.cleanAll(); + contextBrokerMock = nock('http://fiware.org:1026').patch('/path/entityOperations/upsert').reply(200, {}); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=111111111&app_id=application_id') + .reply(200, keyrock_user_response); + + idmMock.post('/xacml').reply(200, xacml_permit_response); + }); + + it('should allow access based on entities', function (done) { + got.patch('path/entityOperations/upsert', pep_proxy_with_headers_and_body).then((response) => { + contextBrokerMock.done(); + idmMock.done(); + should.equal(response.statusCode, 200); + done(); + }); + }); + }); +}); diff --git a/test/unit/sanity-test.js b/test/unit/sanity-test.js deleted file mode 100644 index ab87c22..0000000 --- a/test/unit/sanity-test.js +++ /dev/null @@ -1,90 +0,0 @@ -//const should = require('should'); -//const mocha = require('mocha'); - -process.env.PEP_PROXY_IDM_PORT = 3000; -process.env.PEP_PROXY_IDM_HOST = 'localhost'; -process.env.PEP_PROXY_IDM_SSL_ENABLED = false; -process.env.PEP_PROXY_USERNAME = - 'pep_proxy_00000000-0000-0000-0000-000000000000'; -process.env.PEP_PASSWORD = 'test'; - -const config_service = require('../../lib/config_service.js'); -config_service.set_config(require('../config_test.js')); -const config = config_service.get_config(); -const IDM = require('./../../lib/idm.js').IDM; -const AZF = require('./../../lib/azf.js').AZF; - -const debug = require('debug')('pep-proxy:sanity-test'); -process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; - -describe('Sanity Checks for Wilma PEP Proxy - Identity Manager Checks', function() { - describe('Testing Keyrock configuration', function() { - it('should have PEP user configured', function(done) { - if (config.pep.username !== undefined && config.pep.username !== '') { - if (config.pep.password !== undefined && config.pep.password !== '') { - done(); - } - } - }); - }); - - describe('Testing connection with Keyrock', function() { - it('should have connectivity with Keyrock', function(done) { - IDM.checkConn( - function(status) { - if (status === 200) { - done(); - } - }, - function(status, e) { - debug('Error in Keyrock communication', e); - } - ); - }); - - it('should authenticate with Keyrock', function(done) { - IDM.authenticate( - () => { - done(); - }, - function(status, e) { - debug('Error in Keyrock communication', e); - } - ); - }); - }); -}); - -describe('Sanity Checks for Wilma PEP Proxy - AuthZForce Checks', function() { - if ( - config.authorization.enabled && - config.authorization.pdp === 'authzforce' - ) { - describe('Testing configuration', function() { - it('should have AZF server configured', function(done) { - if (config.azf.host !== undefined && config.azf.host !== '') { - if (config.azf.port !== undefined && config.azf.port !== '') { - done(); - } - } - }); - }); - - describe('Testing connection with AZF', function() { - it('should have connectivity with AZF', function(done) { - AZF.checkConn( - function() {}, - function(status) { - if (status === 401) { - done(); - } - } - ); - }); - }); - } else { - it('AZF not enabled', function(done) { - done(); - }); - } -}); diff --git a/test/utils.js b/test/utils.js deleted file mode 100644 index 73ff319..0000000 --- a/test/utils.js +++ /dev/null @@ -1,19 +0,0 @@ -const fs = require('fs'); - -function readExampleFile(name, raw) { - const text = fs.readFileSync(name, 'UTF8'); - - if (raw) { - return text; - } - return JSON.parse(text); -} - -function delay(ms) { - return function(callback) { - setTimeout(callback, ms); - }; -} - -exports.readExampleFile = readExampleFile; -exports.delay = delay; From c5537165bc4bc67b2acd6e64916355e2ffd7a0a5 Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Mon, 9 Aug 2021 08:42:40 +0200 Subject: [PATCH 04/33] Update documentation --- Dockerfile | 1 + config.js | 23 ++++++++++++----------- config.js.template | 4 +++- doc/admin_guide.md | 10 ++++++++-- extras/docker/Dockerfile | 1 + extras/docker/README.md | 3 ++- policies/custom_policy.js.template | 1 + 7 files changed, 28 insertions(+), 15 deletions(-) diff --git a/Dockerfile b/Dockerfile index 122725d..24e0c18 100644 --- a/Dockerfile +++ b/Dockerfile @@ -84,6 +84,7 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=60s \ # PEP_TOKEN_SECRET # PEP_PROXY_AUTH_ENABLED # PEP_PROXY_PDP +# PEP_PROXY_TENANT_HEADER # PEP_PROXY_AZF_PROTOCOL # PEP_PROXY_AZF_HOST # PEP_PROXY_AZF_PORT diff --git a/config.js b/config.js index 5c6fc7e..3cb5e03 100644 --- a/config.js +++ b/config.js @@ -8,24 +8,24 @@ config.https = { enabled: false, cert_file: 'cert/cert.crt', key_file: 'cert/key.key', - port: 443, + port: 443 }; config.idm = { host: 'localhost', port: 3005, - ssl: false, + ssl: false }; config.app = { host: 'www.fiware.org', port: '80', - ssl: false, // Use true if the app server listens in https + ssl: false // Use true if the app server listens in https }; config.organizations = { enabled: false, - header: 'fiware-service', + header: 'fiware-service' }; // Credentials obtained when registering PEP Proxy in app_id in Account Portal @@ -34,9 +34,9 @@ config.pep = { username: '', password: '', token: { - secret: '', // Secret must be configured in order validate a jwt + secret: '' // Secret must be configured in order validate a jwt }, - trusted_apps: [], + trusted_apps: [] }; // in seconds @@ -52,18 +52,19 @@ config.cache_time = 300; config.authorization = { enabled: false, - pdp: 'idm', // idm|authzforce + pdp: 'idm', // idm|iShare|xacml|authzforce + header: undefined, // NGSILD-Tenant|fiware-service azf: { protocol: 'http', host: 'localhost', port: 8080, - custom_policy: undefined, // use undefined to default policy checks (HTTP verb + path). - }, + custom_policy: undefined // use undefined to default policy checks (HTTP verb + path). + } }; config.cors = { - origin: "*", - methods: "GET,HEAD,PUT,PATCH,POST,DELETE", + origin: '*', + methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', preflightContinue: false, optionsSuccessStatus: 204, credentials: true diff --git a/config.js.template b/config.js.template index 2f5b979..b9f711a 100644 --- a/config.js.template +++ b/config.js.template @@ -1,3 +1,4 @@ +#!/usr/bin/env node const config = {}; // Used only if https is disabled @@ -52,7 +53,8 @@ config.cache_time = 300; config.authorization = { enabled: false, - pdp: 'idm', // idm|authzforce + pdp: 'idm', // idm|iShare|xacml|authzforce + header: undefined, // NGSILD-Tenant|fiware-service azf: { protocol: 'http', host: 'localhost', diff --git a/doc/admin_guide.md b/doc/admin_guide.md index b0bf9a0..bb1b801 100644 --- a/doc/admin_guide.md +++ b/doc/admin_guide.md @@ -76,19 +76,24 @@ you have to first register an application. The steps can be found [here](https://fiware-idm.readthedocs.io/en/latest/user_and_programmers_guide/application_guide/index.html#register-pep-proxy-and-iot-agents). You can also configure Pep Proxy to validate authorization in your application -([levels 2 and 3 of authorization](user_guide.md#level-2-basic-authorization)). If enabled PEP checks permissions in two +([levels 2 and 3 of authorization](user_guide.md#level-2-basic-authorization)). If enabled PEP checks permissions in multiple ways: ways: - With [Keyrock Identity Manager](https://github.com/Fiware/catalogue/tree/master/security#keyrock): only allow basic authorization +- With [Keyrock Identity Manager](https://github.com/Fiware/catalogue/tree/master/security#keyrock): payload attribute level authorizationrequests in iShare format. +- With [Keyrock Identity Manager](https://github.com/Fiware/catalogue/tree/master/security#keyrock): payload attribute level authorization requests in XACML 3.0 JSON format. - With [Authzforce Authorization PDP](https://github.com/Fiware/catalogue/tree/master/security#authzforce): allow basic and advanced authorization. For advanced authorization, you can use custom policy checks by including programatic scripts in policies folder. An script template is included there. +The `config.authorization.header` can be passed to Keyrock IDM to reduce permissions to a single tenant and if used should correspond to the tenant header (`NGSILD-Tenant` or `fiware-service`) when authorizing a multi-tenant system such as FIWARE + ```javascript config.authorization = { enabled: false, - pdp: 'idm', // idm|authzforce + pdp: 'idm', // idm|iShare|xacml|authzforce + header: undefined, azf: { protocol: 'http', host: 'localhost', @@ -435,6 +440,7 @@ overrides. | PEP_TRUSTED_APPS | `pep.trusted_apps` | | PEP_PROXY_AUTH_ENABLED | `authorization.enabled` | | PEP_PROXY_PDP | `authorization.pdp` | +| PEP_PROXY_TENANT_HEADER | `authorization.header` | | PEP_PROXY_AZF_PROTOCOL | `authorization.azf.protocol` | | PEP_PROXY_AZF_HOST | `authorization.azf.host` | | PEP_PROXY_AZF_PORT | `authorization.azf.port` | diff --git a/extras/docker/Dockerfile b/extras/docker/Dockerfile index add07a1..429c7b8 100644 --- a/extras/docker/Dockerfile +++ b/extras/docker/Dockerfile @@ -178,6 +178,7 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=60s \ # PEP_TOKEN_SECRET # PEP_PROXY_AUTH_ENABLED # PEP_PROXY_PDP +# PEP_PROXY_TENANT_HEADER # PEP_PROXY_AZF_PROTOCOL # PEP_PROXY_AZF_HOST # PEP_PROXY_AZF_PORT diff --git a/extras/docker/README.md b/extras/docker/README.md index cf5d3c7..42424e9 100644 --- a/extras/docker/README.md +++ b/extras/docker/README.md @@ -93,7 +93,8 @@ sudo docker run -d --name pep-proxy-container -v [host_config_file]:/opt/fiware- - `PEP_PROXY_USERNAME` - default value is left blank and must be overridden - `PEP_PASSWORD` - default value is left blank and must be overridden - `PEP_PROXY_AUTH_ENABLED` - default value is `false` -- `PEP_PROXY_PDP` - default value is `idm` can be set tp `authzforce` +- `PEP_PROXY_PDP` - default value is `idm` can be set to `authzforce`, `iShare` or `xacml` +- `PEP_PROXY_TENANT_HEADER` - default value is left blank. Typically set to `NGSILD-Tenant` or `fiware-service`. - `PEP_PROXY_AZF_PROTOCOL` - default value is `http` - `PEP_PROXY_AZF_HOST` - default value is `localhost` - `PEP_PROXY_AZF_PORT` - default value is `8080` diff --git a/policies/custom_policy.js.template b/policies/custom_policy.js.template index a04c36e..6372779 100644 --- a/policies/custom_policy.js.template +++ b/policies/custom_policy.js.template @@ -1,3 +1,4 @@ +#!/usr/bin/env node /* Params roles: Array with the list of roles the user has inside app_id From e4313e043783cca60f78ed999c72be1764ebb4b8 Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Mon, 9 Aug 2021 08:43:23 +0200 Subject: [PATCH 05/33] Split app from www to enable injection of config for unit tests Update dependencies --- app.js | 112 ++++++++++++++++++++++++ package-lock.json | 212 +++++++++++++++++++++++++++++++++++++++++++--- package.json | 17 ++-- 3 files changed, 321 insertions(+), 20 deletions(-) create mode 100644 app.js diff --git a/app.js b/app.js new file mode 100644 index 0000000..2d26bd9 --- /dev/null +++ b/app.js @@ -0,0 +1,112 @@ +#!/usr/bin/env node +const cors = require('cors'); +const config_service = require('./lib/config_service'); + +const fs = require('fs'); +const https = require('https'); +const errorhandler = require('errorhandler'); + +const logger = require('morgan'); +const debug = require('debug')('pep-proxy:app'); +const express = require('express'); + +process.on('uncaughtException', function (err) { + debug('Caught exception: ' + err); +}); +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + +/** + * Start the express server to listen to all requests. Whitelisted public paths are + * proxied directly, all other requests are restricted access and must either: + * + * - hold a bearer token from an authenticated user + * - hold a bearer token and the user must be authorized to perform the action + * + * @param an auth token representing the PEP + * @param the configuration to use within the app + * + * @return a running express server + */ +exports.start_server = function (token, config) { + config_service.set_config(config, true); + const Root = require('./controllers/root'); + const Payload = require('./lib/payload'); + const Authorize = require('./lib/authorization_functions'); + const app = express(); + let server; + + // Set logs in development + if (config.debug) { + app.use(logger('dev')); + } + + app.use(function (req, res, next) { + const bodyChunks = []; + req.on('data', function (chunk) { + bodyChunks.push(chunk); + }); + + req.on('end', function () { + if (bodyChunks.length > 0) { + req.body = Buffer.concat(bodyChunks); + } + next(); + }); + }); + + app.disable('x-powered-by'); + app.use(errorhandler({ log: debug })); + app.use(cors(config.cors)); + + let port = config.pep_port || 80; + if (config.https.enabled) { + port = config.https.port || 443; + } + app.set('port', port); + app.set('pepToken', token); + app.set('trust proxy', '127.0.0.1'); + + // The auth mode (authorize or authenticate only) and PDP to adjudicate + // are set in the config. + debug( + 'Starting PEP proxy on port ' + + port + + (config.authorization.enabled + ? '. PDP authorization via ' + config.authorization.pdp + : '. User authentication via IDM') + ); + + for (const p in config.public_paths) { + debug('Public paths', config.public_paths[p]); + app.all(config.public_paths[p], Root.open_access); + } + + if (Authorize.checkPayload()) { + app.use(Payload.queryAnalyse); + // Oddity for NGSI-v2 + //payload.all('/*/op/*', Payload.queryAnalyse; + + app.use(Payload.bodyAnalyse); + app.all('/*/entities/:id', Payload.paramsAnalyse, Root.restricted_access); + app.all('/*/entities/:id/attrs', Payload.paramsAnalyse, Root.restricted_access); + app.all('/*/entities/:id/attrs/:attr', Payload.paramsAnalyse, Root.restricted_access); + } + + app.all('/*', Root.restricted_access); + + if (config.https.enabled === true) { + const options = { + key: fs.readFileSync(config.https.key_file), + cert: fs.readFileSync(config.https.cert_file) + }; + + server = https + .createServer(options, function (req, res) { + app.handle(req, res); + }) + .listen(app.get('port')); + } else { + server = app.listen(app.get('port')); + } + return server; +}; diff --git a/package-lock.json b/package-lock.json index 6c0cbd0..78e5b12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "fiware-pep-proxy", - "version": "7.9.2", + "version": "8.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -344,6 +344,11 @@ "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", "dev": true }, + "@sindresorhus/is": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", + "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==" + }, "@sinonjs/commons": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", @@ -389,6 +394,14 @@ "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", "dev": true }, + "@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "requires": { + "defer-to-connect": "^2.0.0" + } + }, "@textlint/ast-node-types": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-4.3.4.tgz", @@ -1039,12 +1052,49 @@ "integrity": "sha512-KmU+kGi7vG5toUhNdLHHPxyVN1mNGcjMVe1tNDEXT1wU/3oqA96bunElrROWHYw5iNt1QVRaIAtNeMVyzyAdVA==", "dev": true }, + "@types/cacheable-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", + "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" + }, + "@types/keyv": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.2.tgz", + "integrity": "sha512-/FvAK2p4jQOaJ6CGDHJTqZcUtbZe820qIeTg7o0Shg7drB4JHeL+V/dhSaly7NXx6u8eSee+r7coT+yuJEvDLg==", + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "16.4.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.4.13.tgz", + "integrity": "sha512-bLL69sKtd25w7p1nvg9pigE4gtKVpGTPojBFLMkGHXuUgap2sLqQt2qUnqmVCDfzGUL0DRNZP+1prIZJbMeAXg==" + }, "@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "requires": { + "@types/node": "*" + } + }, "@types/unist": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", @@ -1362,6 +1412,25 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, + "cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==" + }, + "cacheable-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + } + }, "caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -1651,6 +1720,19 @@ } } }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, "co": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/co/-/co-3.1.0.tgz", @@ -1842,6 +1924,21 @@ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + } + } + }, "dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -1886,6 +1983,11 @@ "strip-bom": "^4.0.0" } }, + "defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -1969,7 +2071,6 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, "requires": { "once": "^1.4.0" } @@ -2669,7 +2770,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, "requires": { "pump": "^3.0.0" } @@ -2721,6 +2821,24 @@ "type-fest": "^0.8.1" } }, + "got": { + "version": "11.8.2", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz", + "integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==", + "requires": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.1", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + } + }, "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", @@ -2818,6 +2936,11 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -2841,6 +2964,15 @@ "sshpk": "^1.7.0" } }, + "http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + } + }, "human-signals": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", @@ -3364,6 +3496,11 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -3494,6 +3631,14 @@ "safe-buffer": "^5.0.1" } }, + "keyv": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", + "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", + "requires": { + "json-buffer": "3.0.1" + } + }, "lcov-parse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz", @@ -3820,6 +3965,11 @@ "integrity": "sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==", "dev": true }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -3994,6 +4144,11 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -4262,6 +4417,14 @@ } } }, + "node-cache": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", + "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", + "requires": { + "clone": "2.x" + } + }, "node-expat": { "version": "2.3.18", "resolved": "https://registry.npmjs.org/node-expat/-/node-expat-2.3.18.tgz", @@ -4304,6 +4467,11 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + }, "npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -4537,7 +4705,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -4571,6 +4738,11 @@ "word-wrap": "^1.2.3" } }, + "p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==" + }, "p-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", @@ -4893,7 +5065,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -4909,6 +5080,11 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -5525,12 +5701,25 @@ "path-parse": "^1.0.6" } }, + "resolve-alpn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.0.tgz", + "integrity": "sha512-e4FNQs+9cINYMO5NMFc6kOUCdohjqFPSgMuwuZAOUWqrfWsen+Yjy5qZFkV5K7VO7tFSLKcUL97olkED7sCBHA==" + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "requires": { + "lowercase-keys": "^2.0.0" + } + }, "restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -6570,6 +6759,11 @@ "is-typedarray": "^1.0.0" } }, + "underscore": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", + "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==" + }, "unherit": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz", @@ -7120,8 +7314,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write": { "version": "1.0.3", @@ -7195,11 +7388,6 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" }, - "xmlhttprequest": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", - "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" - }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 31f728c..3006814 100644 --- a/package.json +++ b/package.json @@ -5,21 +5,23 @@ "description": "PEP oauth2 authentication proxy for FIWARE GE services", "author": "GING DIT UPM", "dependencies": { - "debug": "~4.1.1", "cors": "^2.8.5", + "debug": "~4.1.1", "errorhandler": "1.x", "escape-html": "1.0.3", "express": "4.x", + "got": "^11.8.2", "is-hex": "^1.1.3", "jsonwebtoken": "^8.5.1", "morgan": "^1.10.0", + "node-cache": "^5.1.2", + "underscore": "1.12.1", "xml2js": "0.4.23", - "xml2json": "0.12.0", - "xmlhttprequest": "1.8.0" + "xml2json": "0.12.0" }, "devDependencies": { - "coveralls": "^3.1.0", "chai": "4.2.0", + "coveralls": "^3.1.0", "eslint": "^7.7.0", "eslint-config-tamia": "^7.2.5", "eslint-plugin-prettier": "^3.1.4", @@ -33,7 +35,7 @@ "prettier": "^2.0.5", "remark-cli": "^8.0.1", "remark-preset-lint-recommended": "^4.0.1", - "should": "13.2.3", + "should": "^13.2.3", "sinon": "9.0.3", "sinon-chai": "3.5.0", "textlint": "^11.7.6", @@ -61,14 +63,13 @@ "start": "node ./bin/www", "debug": "DEBUG=pep-proxy:* node ./bin/www", "healthcheck": "node ./bin/healthcheck.js", - "pretest": "npm run lint", "lint": "eslint . --cache --fix", "lint:text": "textlint '*.md' 'doc/*.md' 'doc/**/*.md' 'extras/docker/*.md'", "lint:md": "remark -f 'README.md' 'roadmap.md' 'doc' 'extras/docker'", - "prettier": "prettier --config .prettierrc.json --write **/*.js *.js", + "prettier": "prettier --config .prettierrc.json --write **/**/*.js **/*.js *.js", "prettier:text": "prettier --parser markdown 'README.md' 'doc/**/*.md' 'extras/docker/*.md' --tab-width 4 --print-width 120 --write --prose-wrap always", "clean": "rm -rf package-lock.json && rm -rf node_modules && rm -rf coverage", - "test": "nyc --reporter=text mocha -- --recursive 'test/**/*.js' --reporter spec --timeout 3000 --ui bdd --exit --color true", + "test": "nyc --reporter=html mocha -- --recursive 'test/**/*.js' --reporter spec --timeout 3000 --ui bdd --exit --color true", "test:coverage": "nyc --reporter=lcov mocha -- --recursive 'test/**/*.js' --timeout 3000 --ui bdd --exit --color true", "test:coveralls": "npm run test:coverage && cat ./coverage/lcov.info | coveralls && rm -rf ./coverage" }, From 3469ef025b8f67ebdb257552ad9234bf1b712d14 Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Tue, 10 Aug 2021 09:23:13 +0200 Subject: [PATCH 06/33] Updating tests --- lib/pdp/xacml.js | 13 +- test/unit/authentication-test.js | 22 ++-- test/unit/authzforce-pdp-test.js | 8 +- test/unit/connection-test.js | 2 +- test/unit/jwt-authentication-test.js | 181 +++++++++++++++++++++++++++ test/unit/keyrock-pdp-test.js | 18 +-- test/unit/payload-pdp-test.js | 12 +- 7 files changed, 220 insertions(+), 36 deletions(-) create mode 100644 test/unit/jwt-authentication-test.js diff --git a/lib/pdp/xacml.js b/lib/pdp/xacml.js index 7937aa0..a6ee73d 100644 --- a/lib/pdp/xacml.js +++ b/lib/pdp/xacml.js @@ -70,9 +70,12 @@ function getPolicy(data) { const tenant = data.tenant_header; debug('Checking authorization to roles', roles, 'to do ', action, ' on ', resource, 'and app ', appId); - const resourceInfo = [attribute('urn:oasis:names:tc:xacml:1.0:resource:resource-id', data.resource)]; - if (data.tenant_header) { - resourceInfo.push(attribute('urn:ngsi-ld:resource:tenant', data.tenant_header)); + const resourceInfo = [ + attribute('urn:thales:xacml:2.0:resource:sub-resource-id', resource), + attribute('urn:oasis:names:tc:xacml:1.0:resource:resource-id', appId) + ]; + if (tenant) { + resourceInfo.push(attribute('urn:ngsi-ld:resource:tenant', tenant)); } if (data.payload_types) { resourceInfo.push(attribute('urn:ngsi-ld:resource:types', data.payload_types)); @@ -86,10 +89,10 @@ function getPolicy(data) { const json = { Request: { AccessSubject: { - Attribute: [attribute('urn:oasis:names:tc:xacml:2.0:subject:role', data.roles)] + Attribute: [attribute('urn:oasis:names:tc:xacml:2.0:subject:role', roles)] }, Action: { - Attribute: [attribute('urn:oasis:names:tc:xacml:1.0:action:action-id', data.action)] + Attribute: [attribute('urn:oasis:names:tc:xacml:1.0:action:action-id', action)] }, Resource: { Attribute: resourceInfo diff --git a/test/unit/authentication-test.js b/test/unit/authentication-test.js index 008336d..f3f4c78 100644 --- a/test/unit/authentication-test.js +++ b/test/unit/authentication-test.js @@ -10,18 +10,18 @@ const should = require('should'); const nock = require('nock'); const cache = require('../../lib/cache'); -const pep_proxy_no_header = { +const request_no_header = { prefixUrl: 'http:/localhost:80', throwHttpErrors: false }; -const pep_proxy_with_header = { +const request_with_header = { prefixUrl: 'http:/localhost:80', throwHttpErrors: false, headers: { 'x-auth-token': '111111111' } }; -const pep_proxy_with_magic_key = { +const request_with_magic_key = { prefixUrl: 'http:/localhost:80', throwHttpErrors: false, headers: { 'x-auth-token': '999999999' } @@ -91,7 +91,7 @@ describe('Authentication: Keyrock IDM', function () { // Set Up }); it('should deny access', function (done) { - got.get('restricted_path', pep_proxy_no_header).then((response) => { + got.get('restricted_path', request_no_header).then((response) => { should.equal(response.statusCode, 401); done(); }); @@ -105,7 +105,7 @@ describe('Authentication: Keyrock IDM', function () { contextBrokerMock = nock('http://fiware.org:1026').get('/public').reply(200, {}); }); it('should allow access', function (done) { - got.get('public', pep_proxy_no_header).then((response) => { + got.get('public', request_no_header).then((response) => { contextBrokerMock.done(); should.equal(response.statusCode, 200); done(); @@ -119,7 +119,7 @@ describe('Authentication: Keyrock IDM', function () { contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); }); it('should allow access', function (done) { - got.get('restricted', pep_proxy_with_magic_key).then((response) => { + got.get('restricted', request_with_magic_key).then((response) => { contextBrokerMock.done(); should.equal(response.statusCode, 200); done(); @@ -137,7 +137,7 @@ describe('Authentication: Keyrock IDM', function () { .reply(200, keyrock_user_response); }); it('should authenticate the user and allow access', function (done) { - got.get('restricted', pep_proxy_with_header).then((response) => { + got.get('restricted', request_with_header).then((response) => { contextBrokerMock.done(); idmMock.done(); should.equal(response.statusCode, 200); @@ -153,7 +153,7 @@ describe('Authentication: Keyrock IDM', function () { idmMock = nock('http://keyrock.com:3000').get('/user?access_token=111111111&app_id=application_id').reply(401); }); it('should authenticate the user and deny access', function (done) { - got.get('restricted', pep_proxy_with_header).then((response) => { + got.get('restricted', request_with_header).then((response) => { contextBrokerMock.done(); idmMock.done(); should.equal(response.statusCode, 401); @@ -172,7 +172,7 @@ describe('Authentication: Keyrock IDM', function () { .reply(200, keyrock_user_response); }); it('should authenticate the user and proxy the error', function (done) { - got.get('restricted', pep_proxy_with_header).then((response) => { + got.get('restricted', request_with_header).then((response) => { contextBrokerMock.done(); idmMock.done(); should.equal(response.statusCode, 404); @@ -192,10 +192,10 @@ describe('Authentication: Keyrock IDM', function () { }); it('should access the user from cache', function (done) { got - .get('restricted', pep_proxy_with_header) + .get('restricted', request_with_header) .then((firstResponse) => { should.equal(firstResponse.statusCode, 200); - return got.get('restricted', pep_proxy_with_header); + return got.get('restricted', request_with_header); }) .then((secondResponse) => { contextBrokerMock.done(); diff --git a/test/unit/authzforce-pdp-test.js b/test/unit/authzforce-pdp-test.js index 79e1c1e..d6e8519 100644 --- a/test/unit/authzforce-pdp-test.js +++ b/test/unit/authzforce-pdp-test.js @@ -10,7 +10,7 @@ const should = require('should'); const nock = require('nock'); const cache = require('../../lib/cache'); -const pep_proxy_with_header = { +const request_with_header = { prefixUrl: 'http:/localhost:80', throwHttpErrors: false, headers: { 'x-auth-token': '111111111' } @@ -127,7 +127,7 @@ describe('Authorization: Authzforce PDP', function () { }); it('should allow access', function (done) { - got.get('restricted', pep_proxy_with_header).then((response) => { + got.get('restricted', request_with_header).then((response) => { contextBrokerMock.done(); idmMock.done(); authzforceMock.done(); @@ -151,7 +151,7 @@ describe('Authorization: Authzforce PDP', function () { }); it('should deny access when denied', function (done) { - got.get('restricted', pep_proxy_with_header).then((response) => { + got.get('restricted', request_with_header).then((response) => { idmMock.done(); authzforceMock.done(); should.equal(response.statusCode, 401); @@ -172,7 +172,7 @@ describe('Authorization: Authzforce PDP', function () { }); }); it('should deny access', function (done) { - got.get('restricted', pep_proxy_with_header).then((response) => { + got.get('restricted', request_with_header).then((response) => { idmMock.done(); should.equal(response.statusCode, 401); done(); diff --git a/test/unit/connection-test.js b/test/unit/connection-test.js index 536bd96..90c61f0 100644 --- a/test/unit/connection-test.js +++ b/test/unit/connection-test.js @@ -1,7 +1,7 @@ /* * Copyright 2021 - Universidad Politécnica de Madrid. * - * This file is part of Keyrock + * This file is part of PEP-Proxy * */ diff --git a/test/unit/jwt-authentication-test.js b/test/unit/jwt-authentication-test.js new file mode 100644 index 0000000..96e67e7 --- /dev/null +++ b/test/unit/jwt-authentication-test.js @@ -0,0 +1,181 @@ +/* + * Copyright 2021 - Universidad Politécnica de Madrid. + * + * This file is part of Keyrock + * + */ + +const got = require('got'); +const should = require('should'); +const nock = require('nock'); +const cache = require('../../lib/cache'); +const jwt = require('jsonwebtoken'); +const token = jwt.sign({ + app_id: 'application_id', + trusted_apps: [], + id : 'username', + displayName: 'Some User' +}, 'shhhhh'); + +const invalid_token = jwt.sign({ + app_id: 'application_id', + trusted_apps: [], + id : 'username', + displayName: 'Some User' +}, 'wrong_secret'); + +const request_with_jwt = { + prefixUrl: 'http:/localhost:80', + throwHttpErrors: false, + headers: {'x-auth-token': token} +}; + +const request_with_invalid_jwt = { + prefixUrl: 'http:/localhost:80', + throwHttpErrors: false, + headers: {'x-auth-token': invalid_token}, + retry: 0 +}; + +const request_no_jwt = { + prefixUrl: 'http:/localhost:80', + throwHttpErrors: false +}; + + +const config = { + magic_key: '999999999', + pep_port: 80, + pep: { + app_id: 'application_id', + trusted_apps: [], + token: {secret: 'shhhhh'} + }, + idm: { + host: 'keyrock.com', + port: '3000', + ssl: false + }, + app: { + host: 'fiware.org', + port: '1026', + ssl: false // Use true if the app server listens in https + }, + organizations: { + enabled: false + }, + cache_time: 300, + public_paths: ['/public'], + authorization: { + enabled: false, + pdp: 'idm', // idm|iShare|xacml|authzforce + header: undefined, // NGSILD-Tenant|fiware-service + azf: { + protocol: 'http', + host: 'localhost', + port: 8080, + custom_policy: undefined // use undefined to default policy checks (HTTP verb + path). + } + } +}; + +const keyrock_user_response = { + app_id: 'application_id', + trusted_apps: [], + id : 'username', + displayName: 'Some User' +}; + +describe('Authentication: JWT Token', function () { + let pep; + let contextBrokerMock; + let idmMock; + + beforeEach(function (done) { + const app = require('../../app'); + pep = app.start_server('12345', config); + cache.flush(); + done(); + }); + + afterEach(function (done) { + pep.close(config.pep_port); + config.pep.secret = undefined; + done(); + }); + + describe('When a URL is requested and no JWT token is present', function () { + beforeEach(function () { + // Set Up + }); + it('should deny access', function (done) { + got.get('restricted_path', request_no_jwt).then((response) => { + should.equal(response.statusCode, 401); + done(); + }); + }); + }); + + describe('When a public path is requested', function () { + beforeEach(function () { + // Set Up + nock.cleanAll(); + contextBrokerMock = nock('http://fiware.org:1026').get('/public').reply(200, {}); + }); + it('should allow access', function (done) { + got.get('public', request_with_jwt).then((response) => { + contextBrokerMock.done(); + should.equal(response.statusCode, 200); + done(); + }); + }); + }); + + + + describe('When a restricted path is requested for a legitimate user', function () { + beforeEach(function () { + // Set Up + nock.cleanAll(); + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); + + }); + it('should authenticate the user and allow access', function (done) { + got.get('restricted', request_with_jwt).then((response) => { + contextBrokerMock.done(); + should.equal(response.statusCode, 200); + done(); + }); + }); + }); + + describe('When a restricted path is requested for an unrecognized JWT', function () { + beforeEach(function () { + // Set Up + nock.cleanAll(); + idmMock = nock('http://keyrock.com:3000').get('/user?access_token='+ invalid_token +'&app_id=application_id').reply(401); + }); + it('should fallback to Keyrock and deny access', function (done) { + got.get('restricted', request_with_invalid_jwt).then((response) => { + should.equal(response.statusCode, 401); + idmMock.done(); + done(); + }); + }); + }); + + describe('When a non-existant restricted path is requested', function () { + beforeEach(function () { + // Set Up + nock.cleanAll(); + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(404); + }); + it('should authenticate the user and proxy the error', function (done) { + got.get('restricted', request_with_jwt).then((response) => { + contextBrokerMock.done(); + should.equal(response.statusCode, 404); + done(); + }); + }); + }); +}); diff --git a/test/unit/keyrock-pdp-test.js b/test/unit/keyrock-pdp-test.js index 9585efe..469b480 100644 --- a/test/unit/keyrock-pdp-test.js +++ b/test/unit/keyrock-pdp-test.js @@ -10,13 +10,13 @@ const should = require('should'); const nock = require('nock'); const cache = require('../../lib/cache'); -const pep_proxy_with_header = { +const request_with_header = { prefixUrl: 'http:/localhost:80', throwHttpErrors: false, headers: { 'x-auth-token': '111111111' } }; -const pep_proxy_with_header_and_body = { +const request_with_header_and_body = { prefixUrl: 'http:/localhost:80', throwHttpErrors: false, headers: { 'x-auth-token': '111111111' }, @@ -89,7 +89,7 @@ describe('Authorization: Keyrock PDP', function () { .reply(200, keyrock_permit_response); }); it('should allow access', function (done) { - got.get('restricted', pep_proxy_with_header).then((response) => { + got.get('restricted', request_with_header).then((response) => { contextBrokerMock.done(); idmMock.done(); should.equal(response.statusCode, 200); @@ -111,7 +111,7 @@ describe('Authorization: Keyrock PDP', function () { }); }); it('should deny access', function (done) { - got.get('restricted', pep_proxy_with_header).then((response) => { + got.get('restricted', request_with_header).then((response) => { idmMock.done(); should.equal(response.statusCode, 401); done(); @@ -128,7 +128,7 @@ describe('Authorization: Keyrock PDP', function () { .reply(200, keyrock_deny_response); }); it('should deny access', function (done) { - got.get('restricted', pep_proxy_with_header).then((response) => { + got.get('restricted', request_with_header).then((response) => { idmMock.done(); should.equal(response.statusCode, 401); done(); @@ -147,10 +147,10 @@ describe('Authorization: Keyrock PDP', function () { }); it('should access the user action from cache', function (done) { got - .get('restricted', pep_proxy_with_header) + .get('restricted', request_with_header) .then((firstResponse) => { should.equal(firstResponse.statusCode, 200); - return got.get('restricted', pep_proxy_with_header); + return got.get('restricted', request_with_header); }) .then((secondResponse) => { contextBrokerMock.done(); @@ -178,10 +178,10 @@ describe('Authorization: Keyrock PDP', function () { }); it('should not access the user from cache', function (done) { got - .get('restricted', pep_proxy_with_header) + .get('restricted', request_with_header) .then((firstResponse) => { should.equal(firstResponse.statusCode, 200); - return got.post('restricted', pep_proxy_with_header_and_body); + return got.post('restricted', request_with_header_and_body); }) .then((secondResponse) => { contextBrokerMock.done(); diff --git a/test/unit/payload-pdp-test.js b/test/unit/payload-pdp-test.js index 0cf8ebb..93ebe86 100644 --- a/test/unit/payload-pdp-test.js +++ b/test/unit/payload-pdp-test.js @@ -49,12 +49,12 @@ const keyrock_user_response = { ] }; -const pep_proxy_with_headers = { +const request_with_headers = { prefixUrl: 'http:/localhost:80', throwHttpErrors: false, headers: { 'x-auth-token': '111111111', 'fiware-service': 'smart-gondor' , 'x-forwarded-for': 'example.com'} }; -const pep_proxy_with_headers_and_body = { +const request_with_headers_and_body = { prefixUrl: 'http:/localhost:80', throwHttpErrors: false, headers: { 'x-auth-token': '111111111', 'fiware-service': 'smart-gondor' }, @@ -147,7 +147,7 @@ describe('Authorization: Payload PDP', function () { }); it('should allow access', function (done) { - got.get('path/entities/urn:ngsi-ld:entity:1111', pep_proxy_with_headers).then((response) => { + got.get('path/entities/urn:ngsi-ld:entity:1111', request_with_headers).then((response) => { contextBrokerMock.done(); idmMock.done(); should.equal(response.statusCode, 200); @@ -168,7 +168,7 @@ describe('Authorization: Payload PDP', function () { }); it('should deny access', function (done) { - got.get('path/entities/urn:ngsi-ld:entity:1111', pep_proxy_with_headers).then((response) => { + got.get('path/entities/urn:ngsi-ld:entity:1111', request_with_headers).then((response) => { idmMock.done(); should.equal(response.statusCode, 401); done(); @@ -191,7 +191,7 @@ describe('Authorization: Payload PDP', function () { }); it('should allow access based on entities', function (done) { - got.get('path/entities/?ids=urn:ngsi-ld:entity:1111&type=entity', pep_proxy_with_headers).then((response) => { + got.get('path/entities/?ids=urn:ngsi-ld:entity:1111&type=entity', request_with_headers).then((response) => { contextBrokerMock.done(); idmMock.done(); should.equal(response.statusCode, 200); @@ -213,7 +213,7 @@ describe('Authorization: Payload PDP', function () { }); it('should allow access based on entities', function (done) { - got.patch('path/entityOperations/upsert', pep_proxy_with_headers_and_body).then((response) => { + got.patch('path/entityOperations/upsert', request_with_headers_and_body).then((response) => { contextBrokerMock.done(); idmMock.done(); should.equal(response.statusCode, 200); From 9116556c7fae984423c19b953fd4aff8d362c9e6 Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Tue, 10 Aug 2021 09:48:59 +0200 Subject: [PATCH 07/33] Add expired token test --- controllers/root.js | 24 +++++++-------- test/unit/jwt-authentication-test.js | 45 +++++++++++++++++++++++----- 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/controllers/root.js b/controllers/root.js index 6fc892b..b67e06d 100644 --- a/controllers/root.js +++ b/controllers/root.js @@ -22,35 +22,33 @@ const debug = require('debug')('pep-proxy:root'); * @param tokens - a collection of auth tokens to use for this verification */ function validateAccessJWT(req, res, tokens) { - jsonwebtoken.verify(tokens.authToken, config.pep.token.secret, function (err, userInfo) { + return jsonwebtoken.verify(tokens.authToken, config.pep.token.secret, function (err, userInfo) { if (err) { if (err.name === 'TokenExpiredError') { - access.deny(res, 'Invalid token: jwt token has expired'); + return access.deny(res, 'Invalid token: jwt token has expired'); } else { debug('Error in JWT ', err.message); debug('Or JWT secret misconfigured'); debug('Validate Token with Keyrock'); // Fallback to AuthToken access validation - validateAccessIDM(req, res, tokens); - return; + return validateAccessIDM(req, res, tokens); } } req.user = userInfo; if (!config.authorization.enabled) { // JWT Authentication Access granted setHeaders(req); - access.permit(req, res); - return; + return access.permit(req, res); } const policy_decision_point = config.authorization.pdp; if (policy_decision_point === 'authzforce') { // JWT Authorization by Authzforce - authorize(req, res, tokens.authToken); + return authorize(req, res, tokens.authToken); } else { // JWT Authorization by IDM, the user will already exist. tokens.jwtExpiry = userInfo.exp; - validateAccessIDM(req, res, tokens); + return validateAccessIDM(req, res, tokens); } }); } @@ -70,17 +68,17 @@ async function validateAccessIDM(req, res, tokens) { req.user = await IDM.authenticateUser(tokens, req.method, req.path, tenant_header); setHeaders(req); if (config.authorization.enabled) { - authorize(req, res, tokens.authToken); + return authorize(req, res, tokens.authToken); } else { // Authentication only. - access.permit(req, res); + return access.permit(req, res); } } catch (e) { debug(e); if (e.status === 404 || e.status === 401) { - access.deny(res); + return access.deny(res); } else { - access.internalError(res, e, 'IDM'); + return access.internalError(res, e, 'IDM'); } } } @@ -90,7 +88,7 @@ async function validateAccessIDM(req, res, tokens) { * @param req - the incoming request */ function setHeaders(req) { - const user = req.user; + const user = req.user || {}; req.headers['X-Nick-Name'] = user.id ? user.id : ''; req.headers['X-Display-Name'] = user.displayName ? user.displayName : ''; req.headers['X-Roles'] = user.roles ? JSON.stringify(user.roles) : []; diff --git a/test/unit/jwt-authentication-test.js b/test/unit/jwt-authentication-test.js index 96e67e7..ca469ce 100644 --- a/test/unit/jwt-authentication-test.js +++ b/test/unit/jwt-authentication-test.js @@ -10,6 +10,14 @@ const should = require('should'); const nock = require('nock'); const cache = require('../../lib/cache'); const jwt = require('jsonwebtoken'); + + +function sleep(ms) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +} + const token = jwt.sign({ app_id: 'application_id', trusted_apps: [], @@ -24,6 +32,13 @@ const invalid_token = jwt.sign({ displayName: 'Some User' }, 'wrong_secret'); +const expired_token = jwt.sign({ + app_id: 'application_id', + trusted_apps: [], + id : 'username', + displayName: 'Some User' +}, 'shhhhh', {expiresIn: '1ms'}); + const request_with_jwt = { prefixUrl: 'http:/localhost:80', throwHttpErrors: false, @@ -42,6 +57,12 @@ const request_no_jwt = { throwHttpErrors: false }; +const request_with_expired_jwt = { + prefixUrl: 'http:/localhost:80', + throwHttpErrors: false, + headers: {'x-auth-token': expired_token} +}; + const config = { magic_key: '999999999', @@ -79,13 +100,6 @@ const config = { } }; -const keyrock_user_response = { - app_id: 'application_id', - trusted_apps: [], - id : 'username', - displayName: 'Some User' -}; - describe('Authentication: JWT Token', function () { let pep; let contextBrokerMock; @@ -133,7 +147,7 @@ describe('Authentication: JWT Token', function () { - describe('When a restricted path is requested for a legitimate user', function () { + describe('When a restricted path is requested with a legitimate JWT', function () { beforeEach(function () { // Set Up nock.cleanAll(); @@ -149,6 +163,21 @@ describe('Authentication: JWT Token', function () { }); }); + describe('When a restricted path is requested with an expired JWT', function () { + beforeEach(async function () { + // Set Up + nock.cleanAll(); + await sleep(1000); + }); + it('should deny access', function (done) { + got.get('restricted', request_with_expired_jwt).then((response) => { + contextBrokerMock.done(); + should.equal(response.statusCode, 401); + done(); + }); + }); + }); + describe('When a restricted path is requested for an unrecognized JWT', function () { beforeEach(function () { // Set Up From 2e20c401a49d8e4215a55846391cc008b87cd374 Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Tue, 10 Aug 2021 13:36:18 +0200 Subject: [PATCH 08/33] Adding Open Policy Agent PDP. --- Dockerfile | 6 +- bin/www | 2 +- controllers/root.js | 13 +- doc/admin_guide.md | 7 +- lib/authorization_functions.js | 109 +++++----- lib/cache.js | 2 +- lib/config_service.js | 64 ++++-- lib/pdp/{azf.js => authzforce.js} | 4 +- lib/pdp/{ishare.js => iShare.js} | 1 - lib/pdp/{idm.js => keyrock.js} | 4 +- lib/pdp/openPolicyAgent.js | 77 +++++++ lib/pdp/xacml.js | 9 +- test/unit/authentication-test.js | 16 +- test/unit/authzforce-pdp-test.js | 19 +- test/unit/connection-test.js | 12 +- test/unit/jwt-authentication-test.js | 89 ++++----- test/unit/keyrock-pdp-test.js | 14 +- test/unit/opa-pdp-test.js | 189 ++++++++++++++++++ ...{payload-pdp-test.js => xacml-pdp-test.js} | 54 ++--- 19 files changed, 479 insertions(+), 212 deletions(-) rename lib/pdp/{azf.js => authzforce.js} (98%) rename lib/pdp/{ishare.js => iShare.js} (99%) rename lib/pdp/{idm.js => keyrock.js} (98%) create mode 100644 lib/pdp/openPolicyAgent.js create mode 100644 test/unit/opa-pdp-test.js rename test/unit/{payload-pdp-test.js => xacml-pdp-test.js} (79%) diff --git a/Dockerfile b/Dockerfile index 24e0c18..76dbf7b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -84,10 +84,10 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=60s \ # PEP_TOKEN_SECRET # PEP_PROXY_AUTH_ENABLED # PEP_PROXY_PDP +# PEP_PROXY_PDP_PROTOCOL +# PEP_PROXY_PDP_HOST +# PEP_PROXY_PDP_PORT # PEP_PROXY_TENANT_HEADER -# PEP_PROXY_AZF_PROTOCOL -# PEP_PROXY_AZF_HOST -# PEP_PROXY_AZF_PORT # PEP_PROXY_AZF_CUSTOM_POLICY # PEP_PROXY_PUBLIC_PATHS # PEP_PROXY_CORS_ORIGIN diff --git a/bin/www b/bin/www index 12c42be..35bf2f2 100644 --- a/bin/www +++ b/bin/www @@ -10,7 +10,7 @@ const config_service = require('../lib/config_service'); config_service.set_config(require('../config'), true); const config = config_service.get_config(); -const IDM = require('../lib/pdp/idm'); +const IDM = require('../lib/pdp/keyrock'); const app = require('../app'); const Authorize = require('../lib/authorization_functions'); const errorhandler = require('errorhandler'); diff --git a/controllers/root.js b/controllers/root.js index b67e06d..09db5e5 100644 --- a/controllers/root.js +++ b/controllers/root.js @@ -5,12 +5,12 @@ * */ -const config_service = require('../lib/config_service.js'); +const config_service = require('../lib/config_service'); let config; -const IDM = require('../lib/pdp/idm.js'); +const IDM = require('../lib/pdp/keyrock'); const jsonwebtoken = require('jsonwebtoken'); -const access = require('../lib/access_functions.js'); -const authorize = require('../lib/authorization_functions.js').authorize; +const access = require('../lib/access_functions'); +const authorize = require('../lib/authorization_functions').authorize; const debug = require('debug')('pep-proxy:root'); @@ -42,7 +42,7 @@ function validateAccessJWT(req, res, tokens) { } const policy_decision_point = config.authorization.pdp; - if (policy_decision_point === 'authzforce') { + if (policy_decision_point === 'azf') { // JWT Authorization by Authzforce return authorize(req, res, tokens.authToken); } else { @@ -61,8 +61,7 @@ function validateAccessJWT(req, res, tokens) { * @param tokens - a collection of auth tokens to use for this verification */ async function validateAccessIDM(req, res, tokens) { - const tenant_header = - config.authorization.enabled && config.authorization.header ? req.get(config.authorization.header) : undefined; + const tenant_header = config.authorization.header ? req.get(config.authorization.header) : undefined; try { req.user = await IDM.authenticateUser(tokens, req.method, req.path, tenant_header); diff --git a/doc/admin_guide.md b/doc/admin_guide.md index bb1b801..0b987cb 100644 --- a/doc/admin_guide.md +++ b/doc/admin_guide.md @@ -81,8 +81,9 @@ ways: - With [Keyrock Identity Manager](https://github.com/Fiware/catalogue/tree/master/security#keyrock): only allow basic authorization -- With [Keyrock Identity Manager](https://github.com/Fiware/catalogue/tree/master/security#keyrock): payload attribute level authorizationrequests in iShare format. -- With [Keyrock Identity Manager](https://github.com/Fiware/catalogue/tree/master/security#keyrock): payload attribute level authorization requests in XACML 3.0 JSON format. +- With [Keyrock Identity Manager](https://github.com/Fiware/catalogue/tree/master/security#keyrock): payload attribute level authorization requests in iShare format. +- With [Keyrock Identity Manager](https://github.com/Fiware/catalogue/tree/master/security#keyrock): payload attribute level authorization requests in [XACML 3.0 JSON](https://docs.oasis-open.org/xacml/xacml-json-http/v1.0/xacml-json-http-v1.0.html) format. +- With [Keyrock Identity Manager](https://github.com/Fiware/catalogue/tree/master/security#keyrock): payload attribute level authorization requests in [Open Policy Agent](https://www.openpolicyagent.org/) format. - With [Authzforce Authorization PDP](https://github.com/Fiware/catalogue/tree/master/security#authzforce): allow basic and advanced authorization. For advanced authorization, you can use custom policy checks by including programatic scripts in policies folder. An script template is included there. @@ -92,7 +93,7 @@ The `config.authorization.header` can be passed to Keyrock IDM to reduce permiss ```javascript config.authorization = { enabled: false, - pdp: 'idm', // idm|iShare|xacml|authzforce + pdp: 'idm', // idm|iShare|xacml|authzforce|opa header: undefined, azf: { protocol: 'http', diff --git a/lib/authorization_functions.js b/lib/authorization_functions.js index d61347c..92eaa1a 100644 --- a/lib/authorization_functions.js +++ b/lib/authorization_functions.js @@ -6,8 +6,9 @@ */ const config_service = require('./config_service'); -const AZF = require('./pdp/azf'); -const IDM = require('./pdp/idm'); +const AZF = require('./pdp/authzforce'); +const OPA = require('./pdp/openPolicyAgent'); +const IDM = require('./pdp/keyrock'); const XACML = require('./pdp/xacml'); const iShare = require('./pdp/ishare'); const access = require('./access_functions'); @@ -51,57 +52,75 @@ function getData(req, res) { }; } -const authorize = { - idm: (req, res) => { - access.adjudicate(req, res, IDM.checkPolicies(req.user)); - }, +function idmAuthorize(req, res) { + access.adjudicate(req, res, IDM.checkPolicies(req.user)); +} +function xacmlAuthorize(req, res) { + // Check decision through XACML Endpoint + const authToken = req.app.get('pepToken'); + XACML.checkPolicies(authToken, getData(req, res)) + .then((decision) => { + access.adjudicate(req, res, decision); + }) + .catch((e) => { + access.internalError(res, e, 'XACML'); + }); +} - xacml: (req, res) => { - // Check decision through XACML Endpoint - const authToken = req.app.get('pepToken'); - XACML.checkPolicies(authToken, getData(req, res)) - .then((decision) => { - access.adjudicate(req, res, decision); - }) - .catch((e) => { - access.internalError(res, e, 'XACML'); - }); - }, +function openPolicyAgentAuthorize(req,res){ + // Check decision through Open Policy Agent Endpoint + const authToken = req.app.get('pepToken'); + OPA.checkPolicies(authToken, getData(req, res)) + .then((decision) => { + access.adjudicate(req, res, decision); + }) + .catch((e) => { + access.internalError(res, e, 'OPA'); + }); +} - iShare: (req, res) => { - // Check decision through iShare Endpoint - const authToken = req.app.get('pepToken'); - iShare - .checkPolicies(authToken, getData(req, res)) - .then((decision) => { - access.adjudicate(req, res, decision); - }) - .catch((e) => { - access.internalError(res, e, 'iShare'); - }); - }, - authzforce: (req, res, authToken) => { - // Check decision through authzforce - AZF.checkPolicies(authToken, getData(req, res), req) - .then((decision) => { - access.adjudicate(req, res, decision); - }) - .catch((e) => { - if (e.status === 404) { - debug('Domain not found: ', e); - access.deny(res); - } else { - access.internalError(res, e, 'AZF'); - } - }); - } +function iShareAuthorize(req, res) { + // Check decision through iShare Endpoint + const authToken = req.app.get('pepToken'); + iShare + .checkPolicies(authToken, getData(req, res)) + .then((decision) => { + access.adjudicate(req, res, decision); + }) + .catch((e) => { + access.internalError(res, e, 'iShare'); + }); +} +function authzforceAuthorize(req, res, authToken) { + // Check decision through authzforce + AZF.checkPolicies(authToken, getData(req, res), req) + .then((decision) => { + access.adjudicate(req, res, decision); + }) + .catch((e) => { + if (e.status === 404) { + debug('Domain not found: ', e); + access.deny(res); + } else { + access.internalError(res, e, 'AZF'); + } + }); +} + +const authorize = { + idm: idmAuthorize, + xacml: xacmlAuthorize, + ishare: iShareAuthorize, + opa: openPolicyAgentAuthorize, + azf: authzforceAuthorize }; exports.checkPayload = function () { const authorization = config_service.get_config().authorization; - return authorization.enabled && (authorization.pdp === 'iShare' || authorization.pdp === 'xacml'); + return (authorization.pdp === 'ishare' || authorization.pdp === 'xacml' || authorization.pdp === 'opa'); }; exports.authorize = function (req, res, authToken) { + debug(config_service.get_config().authorization.pdp) return authorize[config_service.get_config().authorization.pdp](req, res, authToken); }; diff --git a/lib/cache.js b/lib/cache.js index ca97dae..a426af9 100644 --- a/lib/cache.js +++ b/lib/cache.js @@ -60,7 +60,7 @@ function checkTokenCache(token, jwtExpiration, action, resource) { return undefined; } - if (config.authorization.enabled && config.authorization.pdp === 'idm') { + if (config.authorization.pdp === 'idm') { if (tokenPermission(token, action, resource)) { debug('Action-level permission in cache...'); } else { diff --git a/lib/config_service.js b/lib/config_service.js index 00a212c..ca07e8b 100644 --- a/lib/config_service.js +++ b/lib/config_service.js @@ -69,6 +69,9 @@ function process_environment_variables(verbose) { 'PEP_TRUSTED_APPS', 'PEP_PROXY_AUTH_ENABLED', 'PEP_PROXY_PDP', + 'PEP_PROXY_PDP_PROTOCOL', + 'PEP_PROXY_PDP_HOST', + 'PEP_PROXY_PDP_PORT', 'PEP_PROXY_TENANT_HEADER', 'PEP_PROXY_AZF_PROTOCOL', 'PEP_PROXY_AZF_HOST', @@ -176,7 +179,7 @@ function process_environment_variables(verbose) { config.pep.trusted_apps = to_array(process.env.PEP_TRUSTED_APPS, []); } - // if enabled PEP checks permissions in two ways: + // if enabled PEP checks permissions in four ways: // - With IdM: only allow basic authorization // - With Authzforce: allow basic and advanced authorization. // For advanced authorization, you can use custom policy checks by including programatic scripts @@ -188,25 +191,50 @@ function process_environment_variables(verbose) { if (process.env.PEP_PROXY_AUTH_ENABLED) { config.authorization.enabled = to_boolean(process.env.PEP_PROXY_AUTH_ENABLED, false); } - if (config.authorization.enabled && process.env.PEP_PROXY_PDP) { - config.authorization.pdp = process.env.PEP_PROXY_PDP; - } - if (config.authorization.enabled && process.env.PEP_PROXY_TENANT_HEADER) { - config.authorization.header = process.env.PEP_PROXY_TENANT_HEADER; - } + if (config.authorization.enabled) { + if (process.env.PEP_PROXY_PDP) { + config.authorization.pdp = process.env.PEP_PROXY_PDP; + } + let pdp = config.authorization.pdp.toLowerCase(); + if (pdp === 'authzforce') { + pdp = 'azf'; + } + config.authorization.pdp = pdp; + config.authorization.azf = config.authorization.azf || {}; + config.authorization[pdp] = config.authorization[pdp] || {}; - config.authorization.azf = config.authorization.azf || {}; - if (process.env.PEP_PROXY_AZF_PROTOCOL) { - config.authorization.azf.protocol = process.env.PEP_PROXY_AZF_PROTOCOL; - } - if (process.env.PEP_PROXY_AZF_HOST) { - config.authorization.azf.host = process.env.PEP_PROXY_AZF_HOST; - } - if (process.env.PEP_PROXY_AZF_PORT) { - config.authorization.azf.port = process.env.PEP_PROXY_AZF_PORT; + if (process.env.PEP_PROXY_PDP_PROTOCOL) { + config.authorization[pdp].protocol = process.env.PEP_PROXY_PDP_PROTOCOL; + } + if (process.env.PEP_PROXY_PDP_HOST) { + config.authorization[pdp].host = process.env.PEP_PROXY_PDP_HOST; + } + if (process.env.PEP_PROXY_PDP_PORT) { + config.authorization[pdp].port = process.env.PEP_PROXY_PDP_PORT; + } + + // Deprecated - use common PDP attributes + if (process.env.PEP_PROXY_AZF_PROTOCOL) { + config.authorization.azf.protocol = process.env.PEP_PROXY_AZF_PROTOCOL; + } + // Deprecated - use common PDP attributes + if (process.env.PEP_PROXY_AZF_HOST) { + config.authorization.azf.host = process.env.PEP_PROXY_AZF_HOST; + } + // Deprecated - use common PDP attributes + if (process.env.PEP_PROXY_AZF_PORT) { + config.authorization.azf.port = process.env.PEP_PROXY_AZF_PORT; + } + if (process.env.PEP_PROXY_AZF_CUSTOM_POLICY) { + config.authorization.azf.custom_policy = process.env.PEP_PROXY_AZF_CUSTOM_POLICY; + } + } else { + // Authorization is disabled - remove auth config. + config.authorization = {}; } - if (process.env.PEP_PROXY_AZF_CUSTOM_POLICY) { - config.authorization.azf.custom_policy = process.env.PEP_PROXY_AZF_CUSTOM_POLICY; + + if (process.env.PEP_PROXY_TENANT_HEADER) { + config.authorization.header = process.env.PEP_PROXY_TENANT_HEADER; } if (process.env.PEP_PROXY_PUBLIC_PATHS) { diff --git a/lib/pdp/azf.js b/lib/pdp/authzforce.js similarity index 98% rename from lib/pdp/azf.js rename to lib/pdp/authzforce.js index b57e4bc..025bdf4 100644 --- a/lib/pdp/azf.js +++ b/lib/pdp/authzforce.js @@ -15,8 +15,8 @@ const debug = require('debug')('pep-proxy:AZF-Client'); const cache = require('../cache'); function getUrl() { - const azf = config_service.get_config().authorization.azf; - return (azf.ssl ? 'https://' : 'http://') + azf.host + ':' + azf.port; + const pdp = config_service.get_config().authorization.azf; + return (pdp.ssl ? 'https://' : 'http://') + pdp.host + ':' + pdp.port; } /** diff --git a/lib/pdp/ishare.js b/lib/pdp/iShare.js similarity index 99% rename from lib/pdp/ishare.js rename to lib/pdp/iShare.js index 443a543..da356b9 100644 --- a/lib/pdp/ishare.js +++ b/lib/pdp/iShare.js @@ -56,7 +56,6 @@ function iShareBody(data) { const appId = data.appId; debug('Checking authorization to roles', roles, 'to do ', action, ' on ', resource, 'and app ', appId); - const json = { delegationRequest: { policyIssuer: 'EU.EORI.NL000000005', diff --git a/lib/pdp/idm.js b/lib/pdp/keyrock.js similarity index 98% rename from lib/pdp/idm.js rename to lib/pdp/keyrock.js index ab0c223..536c91c 100644 --- a/lib/pdp/idm.js +++ b/lib/pdp/keyrock.js @@ -52,7 +52,7 @@ function getPath(token, action, resource, tenant) { path = path + '&authorization_service_header=' + tenant; } } - if (policy_decision_point === 'authzforce') { + if (policy_decision_point === 'azf') { // Using Authzforce as a PDP - get the location of the Authzforce PDP Domain path = path + '&authzforce=true'; } @@ -154,7 +154,7 @@ exports.authenticateUser = function (token, action, resource, tenant) { } // Keyrock is in use as an IDM - store the User cache.storeUser(authToken, user); - if (authorization.enabled && authorization.pdp === 'idm') { + if (authorization.pdp === 'idm') { // Keyrock is also in use as a PDP - store the permissions. cache.storeAction(authToken, action, resource); } diff --git a/lib/pdp/openPolicyAgent.js b/lib/pdp/openPolicyAgent.js new file mode 100644 index 0000000..e848880 --- /dev/null +++ b/lib/pdp/openPolicyAgent.js @@ -0,0 +1,77 @@ +/* + * Copyright 2021 - Universidad Politécnica de Madrid. + * + * This file is part of PEP-Proxy + * + */ + +const config_service = require('../config_service'); +const debug = require('debug')('pep-proxy:OPA-Client'); +const got = require('got'); + +function getUrl() { + const pdp = config_service.get_config().authorization.opa; + return (pdp.ssl ? 'https://' : 'http://') + pdp.host + ':' + pdp.port; +} + +/** + * Make request to the Open Policy Agent endpoint and interpret the result + * + * @param token - authorization token. + * @param data - A bag of data holding the action, resources, payload etc. + * this will be used to make the decision + * + * + * @return permit/deny + */ +exports.checkPolicies = function (token, data) { + return new Promise((resolve, reject) => { + return got + .post('query', { + prefixUrl: getUrl(), + headers: { + 'X-Auth-Token': token + }, + json: getPolicy(data) + }) + .json() + .then((result) => { + debug(result); + return resolve(result); + }) + .catch((error) => { + if (error instanceof got.HTTPError) { + return resolve(false); + } + debug('Error in OPA communication ', error); + return reject(error); + }); + }); +}; + + +/** + * Create a payload for making an Open Policy Agent request + * based on the action,resource,tenant and attributes + * @return OPA payload + */ +function getPolicy(data) { + const action = data.action; + const resource = data.resource; + const roles = data.roles; + const appId = data.appId; + debug('Checking authorization to roles', roles, 'to do ', action, ' on ', resource, 'and app ', appId); + const json = { + appId, + resource, + roles, + action, + tenant : data.tenant_header, + ids: data.payload_ids, + attrs: data.payload_attrs, + types: data.payload_types + }; + + debug('Open Policy Agent request: ', JSON.stringify(json)); + return json; +} diff --git a/lib/pdp/xacml.js b/lib/pdp/xacml.js index a6ee73d..ff82215 100644 --- a/lib/pdp/xacml.js +++ b/lib/pdp/xacml.js @@ -6,10 +6,13 @@ */ const config_service = require('../config_service'); -const config = config_service.get_config(); const debug = require('debug')('pep-proxy:XACML-Client'); const got = require('got'); -const KEYROCK_URL = (config.idm.ssl ? 'https://' : 'http://') + config.idm.host + ':' + config.idm.port; + +function getUrl() { + const pdp = config_service.get_config().authorization.xacml; + return (pdp.ssl ? 'https://' : 'http://') + pdp.host + ':' + pdp.port; +} /** * Make request to the XACML JSON endpoint and interpret the result @@ -25,7 +28,7 @@ exports.checkPolicies = function (token, data) { return new Promise((resolve, reject) => { return got .post('xacml', { - prefixUrl: KEYROCK_URL, + prefixUrl: getUrl(), headers: { 'X-Auth-Token': token }, diff --git a/test/unit/authentication-test.js b/test/unit/authentication-test.js index f3f4c78..eb3cec0 100644 --- a/test/unit/authentication-test.js +++ b/test/unit/authentication-test.js @@ -51,7 +51,7 @@ const config = { public_paths: ['/public'], authorization: { enabled: false, - pdp: 'idm', // idm|iShare|xacml|authzforce + pdp: 'idm', // idm|iShare|xacml|authzforce|opa|azf header: undefined, // NGSILD-Tenant|fiware-service azf: { protocol: 'http', @@ -65,7 +65,7 @@ const config = { const keyrock_user_response = { app_id: 'application_id', trusted_apps: [], - id : 'username', + id: 'username', displayName: 'Some User' }; @@ -78,6 +78,7 @@ describe('Authentication: Keyrock IDM', function () { const app = require('../../app'); pep = app.start_server('12345', config); cache.flush(); + nock.cleanAll(); done(); }); @@ -100,8 +101,6 @@ describe('Authentication: Keyrock IDM', function () { describe('When a public path is requested', function () { beforeEach(function () { - // Set Up - nock.cleanAll(); contextBrokerMock = nock('http://fiware.org:1026').get('/public').reply(200, {}); }); it('should allow access', function (done) { @@ -115,7 +114,6 @@ describe('Authentication: Keyrock IDM', function () { describe('When a restricted path is requested and the token matches the magic key', function () { beforeEach(function () { - nock.cleanAll(); contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); }); it('should allow access', function (done) { @@ -129,8 +127,6 @@ describe('Authentication: Keyrock IDM', function () { describe('When a restricted path is requested for a legitimate user', function () { beforeEach(function () { - // Set Up - nock.cleanAll(); contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id') @@ -148,8 +144,6 @@ describe('Authentication: Keyrock IDM', function () { describe('When a restricted path is requested for a forbidden user', function () { beforeEach(function () { - // Set Up - nock.cleanAll(); idmMock = nock('http://keyrock.com:3000').get('/user?access_token=111111111&app_id=application_id').reply(401); }); it('should authenticate the user and deny access', function (done) { @@ -164,8 +158,6 @@ describe('Authentication: Keyrock IDM', function () { describe('When a non-existant restricted path is requested', function () { beforeEach(function () { - // Set Up - nock.cleanAll(); contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(404); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id') @@ -183,8 +175,6 @@ describe('Authentication: Keyrock IDM', function () { describe('When the same restricted path is requested multiple times', function () { beforeEach(function () { - // Set Up - nock.cleanAll(); contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').times(2).reply(200, {}); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id') diff --git a/test/unit/authzforce-pdp-test.js b/test/unit/authzforce-pdp-test.js index d6e8519..cd95449 100644 --- a/test/unit/authzforce-pdp-test.js +++ b/test/unit/authzforce-pdp-test.js @@ -83,7 +83,7 @@ const config = { public_paths: [], authorization: { enabled: true, - pdp: 'authzforce', // idm|iShare|xacml|authzforce + pdp: 'authzforce', // idm|iShare|xacml|authzforce|opa|azf header: undefined, // NGSILD-Tenant|fiware-service azf: { protocol: 'http', @@ -104,6 +104,10 @@ describe('Authorization: Authzforce PDP', function () { const app = require('../../app'); pep = app.start_server('12345', config); cache.flush(); + nock.cleanAll(); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=111111111&app_id=application_id&authzforce=true') + .reply(200, keyrock_user_with_azf); done(); }); @@ -114,13 +118,7 @@ describe('Authorization: Authzforce PDP', function () { describe('When a restricted path is requested for a legitimate user', function () { beforeEach(function () { - // Set Up - nock.cleanAll(); contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); - idmMock = nock('http://keyrock.com:3000') - .get('/user?access_token=111111111&app_id=application_id&authzforce=true') - .reply(200, keyrock_user_with_azf); - authzforceMock = nock('http://authzforce.com:8080') .post('/authzforce-ce/domains/authzforce/pdp') .reply(200, authzforce_permit_response); @@ -139,12 +137,6 @@ describe('Authorization: Authzforce PDP', function () { describe('When a restricted path is requested for a forbidden user', function () { beforeEach(function () { - // Set Up - nock.cleanAll(); - idmMock = nock('http://keyrock.com:3000') - .get('/user?access_token=111111111&app_id=application_id&authzforce=true') - .reply(200, keyrock_user_with_azf); - authzforceMock = nock('http://authzforce.com:8080') .post('/authzforce-ce/domains/authzforce/pdp') .reply(200, authzforce_deny_response); @@ -162,7 +154,6 @@ describe('Authorization: Authzforce PDP', function () { describe('When no AZF domain is returned', function () { beforeEach(function () { - // Set Up nock.cleanAll(); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id&authzforce=true') diff --git a/test/unit/connection-test.js b/test/unit/connection-test.js index 90c61f0..0f5d715 100644 --- a/test/unit/connection-test.js +++ b/test/unit/connection-test.js @@ -8,8 +8,8 @@ const config_service = require('../../lib/config_service'); const should = require('should'); const nock = require('nock'); -const IDM = require('../../lib/pdp/idm'); -const Authzforce = require('../../lib/pdp/azf'); +const IDM = require('../../lib/pdp/keyrock'); +const Authzforce = require('../../lib/pdp/authzforce'); const cache = require('../../lib/cache'); const config = { @@ -37,8 +37,7 @@ const config = { public_paths: [], authorization: { enabled: true, - pdp: 'idm', // idm|iShare|xacml|authzforce - header: undefined, // NGSILD-Tenant|fiware-service + pdp: 'azf', azf: { protocol: 'http', host: 'authzforce.com', @@ -55,6 +54,7 @@ describe('Connection Tests', function () { beforeEach(function (done) { config_service.set_config(config, true); cache.flush(); + nock.cleanAll(); done(); }); @@ -64,7 +64,6 @@ describe('Connection Tests', function () { describe('When connecting to Authzforce and it is present', function () { beforeEach(function () { - // Set Up authzforceMock = nock('http://authzforce.com:8080').get('/').reply(200, {}); }); it('should not error', function (done) { @@ -81,7 +80,6 @@ describe('Connection Tests', function () { describe('When connecting to Keyrock and it is present', function () { beforeEach(function () { - // Set Up idmMock = nock('http://keyrock.com:3000').get('/version').reply(200, {}); }); it('should not error', function (done) { @@ -98,7 +96,6 @@ describe('Connection Tests', function () { describe('When authenticating the PEP with Keyrock', function () { beforeEach(function () { - // Set Up idmMock = nock('http://keyrock.com:3000').post('/v3/auth/tokens').reply(200, {}); }); it('should not error', function (done) { @@ -115,7 +112,6 @@ describe('Connection Tests', function () { describe('When authenticating a misconfigured PEP with Keyrock', function () { beforeEach(function () { - // Set Up idmMock = nock('http://keyrock.com:3000').post('/v3/auth/tokens').reply(401); }); it('should error', function (done) { diff --git a/test/unit/jwt-authentication-test.js b/test/unit/jwt-authentication-test.js index ca469ce..5ec7b38 100644 --- a/test/unit/jwt-authentication-test.js +++ b/test/unit/jwt-authentication-test.js @@ -11,44 +11,53 @@ const nock = require('nock'); const cache = require('../../lib/cache'); const jwt = require('jsonwebtoken'); - function sleep(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); -} - -const token = jwt.sign({ - app_id: 'application_id', - trusted_apps: [], - id : 'username', - displayName: 'Some User' -}, 'shhhhh'); - -const invalid_token = jwt.sign({ - app_id: 'application_id', - trusted_apps: [], - id : 'username', - displayName: 'Some User' -}, 'wrong_secret'); - -const expired_token = jwt.sign({ - app_id: 'application_id', - trusted_apps: [], - id : 'username', - displayName: 'Some User' -}, 'shhhhh', {expiresIn: '1ms'}); +} + +const token = jwt.sign( + { + app_id: 'application_id', + trusted_apps: [], + id: 'username', + displayName: 'Some User' + }, + 'shhhhh' +); + +const invalid_token = jwt.sign( + { + app_id: 'application_id', + trusted_apps: [], + id: 'username', + displayName: 'Some User' + }, + 'wrong_secret' +); + +const expired_token = jwt.sign( + { + app_id: 'application_id', + trusted_apps: [], + id: 'username', + displayName: 'Some User' + }, + 'shhhhh', + { expiresIn: '1ms' } +); const request_with_jwt = { prefixUrl: 'http:/localhost:80', throwHttpErrors: false, - headers: {'x-auth-token': token} + headers: { 'x-auth-token': token } }; const request_with_invalid_jwt = { prefixUrl: 'http:/localhost:80', throwHttpErrors: false, - headers: {'x-auth-token': invalid_token}, + headers: { 'x-auth-token': invalid_token }, retry: 0 }; @@ -60,17 +69,16 @@ const request_no_jwt = { const request_with_expired_jwt = { prefixUrl: 'http:/localhost:80', throwHttpErrors: false, - headers: {'x-auth-token': expired_token} + headers: { 'x-auth-token': expired_token } }; - const config = { magic_key: '999999999', pep_port: 80, pep: { app_id: 'application_id', trusted_apps: [], - token: {secret: 'shhhhh'} + token: { secret: 'shhhhh' } }, idm: { host: 'keyrock.com', @@ -89,7 +97,7 @@ const config = { public_paths: ['/public'], authorization: { enabled: false, - pdp: 'idm', // idm|iShare|xacml|authzforce + pdp: 'idm', // idm|iShare|xacml|authzforce|opa|azf header: undefined, // NGSILD-Tenant|fiware-service azf: { protocol: 'http', @@ -108,6 +116,7 @@ describe('Authentication: JWT Token', function () { beforeEach(function (done) { const app = require('../../app'); pep = app.start_server('12345', config); + nock.cleanAll(); cache.flush(); done(); }); @@ -119,9 +128,6 @@ describe('Authentication: JWT Token', function () { }); describe('When a URL is requested and no JWT token is present', function () { - beforeEach(function () { - // Set Up - }); it('should deny access', function (done) { got.get('restricted_path', request_no_jwt).then((response) => { should.equal(response.statusCode, 401); @@ -132,8 +138,6 @@ describe('Authentication: JWT Token', function () { describe('When a public path is requested', function () { beforeEach(function () { - // Set Up - nock.cleanAll(); contextBrokerMock = nock('http://fiware.org:1026').get('/public').reply(200, {}); }); it('should allow access', function (done) { @@ -145,14 +149,9 @@ describe('Authentication: JWT Token', function () { }); }); - - describe('When a restricted path is requested with a legitimate JWT', function () { beforeEach(function () { - // Set Up - nock.cleanAll(); contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); - }); it('should authenticate the user and allow access', function (done) { got.get('restricted', request_with_jwt).then((response) => { @@ -165,9 +164,7 @@ describe('Authentication: JWT Token', function () { describe('When a restricted path is requested with an expired JWT', function () { beforeEach(async function () { - // Set Up - nock.cleanAll(); - await sleep(1000); + await sleep(100); }); it('should deny access', function (done) { got.get('restricted', request_with_expired_jwt).then((response) => { @@ -180,9 +177,9 @@ describe('Authentication: JWT Token', function () { describe('When a restricted path is requested for an unrecognized JWT', function () { beforeEach(function () { - // Set Up - nock.cleanAll(); - idmMock = nock('http://keyrock.com:3000').get('/user?access_token='+ invalid_token +'&app_id=application_id').reply(401); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=' + invalid_token + '&app_id=application_id') + .reply(401); }); it('should fallback to Keyrock and deny access', function (done) { got.get('restricted', request_with_invalid_jwt).then((response) => { @@ -195,8 +192,6 @@ describe('Authentication: JWT Token', function () { describe('When a non-existant restricted path is requested', function () { beforeEach(function () { - // Set Up - nock.cleanAll(); contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(404); }); it('should authenticate the user and proxy the error', function (done) { diff --git a/test/unit/keyrock-pdp-test.js b/test/unit/keyrock-pdp-test.js index 469b480..149371e 100644 --- a/test/unit/keyrock-pdp-test.js +++ b/test/unit/keyrock-pdp-test.js @@ -58,7 +58,7 @@ const config = { public_paths: [], authorization: { enabled: true, - pdp: 'idm' // idm|iShare|xacml|authzforce + pdp: 'idm' // idm|iShare|xacml|authzforce|opa|azf } }; @@ -68,6 +68,7 @@ describe('Authorization: Keyrock PDP', function () { let idmMock; beforeEach(function (done) { + nock.cleanAll(); const app = require('../../app'); pep = app.start_server('12345', config); cache.flush(); @@ -81,8 +82,6 @@ describe('Authorization: Keyrock PDP', function () { describe('When a restricted path is requested for a legitimate user', function () { beforeEach(function () { - // Set Up - nock.cleanAll(); contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') @@ -100,8 +99,6 @@ describe('Authorization: Keyrock PDP', function () { describe('When a restricted path is requested and the app-id is not found', function () { beforeEach(function () { - // Set Up - nock.cleanAll(); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') .reply(200, { @@ -121,8 +118,6 @@ describe('Authorization: Keyrock PDP', function () { describe('When a restricted path is requested for a forbidden user', function () { beforeEach(function () { - // Set Up - nock.cleanAll(); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') .reply(200, keyrock_deny_response); @@ -138,8 +133,6 @@ describe('Authorization: Keyrock PDP', function () { describe('When the same action on a restricted path multiple times', function () { beforeEach(function () { - // Set Up - nock.cleanAll(); contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').times(2).reply(200, {}); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') @@ -163,10 +156,7 @@ describe('Authorization: Keyrock PDP', function () { describe('When the same user request two different actions on a restricted path', function () { beforeEach(function () { - // Set Up - nock.cleanAll(); contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); - contextBrokerMock.post('/restricted').reply(204); idmMock = nock('http://keyrock.com:3000') diff --git a/test/unit/opa-pdp-test.js b/test/unit/opa-pdp-test.js new file mode 100644 index 0000000..6c29f68 --- /dev/null +++ b/test/unit/opa-pdp-test.js @@ -0,0 +1,189 @@ +/* + * Copyright 2021 - Universidad Politécnica de Madrid. + * + * This file is part of PEP-Proxy + * + */ + +const got = require('got'); +const should = require('should'); +const nock = require('nock'); +const cache = require('../../lib/cache'); + +const ngsiPayload = [ + { + id: 'urn:ngsi-ld:TemperatureSensor:002', + type: 'TemperatureSensor', + category: { + type: 'Property', + value: 'sensor' + }, + temperature: { + type: 'Property', + value: 21, + unitCode: 'CEL' + } + }, + { + id: 'urn:ngsi-ld:TemperatureSensor:003', + type: 'TemperatureSensor', + category: { + type: 'Property', + value: 'sensor' + }, + temperature: { + type: 'Property', + value: 27, + unitCode: 'CEL' + } + } +]; +const keyrock_user_response = { + app_id: 'application_id', + trusted_apps: [], + roles: [ + { + id: 'managers-role-0000-0000-000000000000', + name: 'Management' + } + ] +}; + +const request_with_headers = { + prefixUrl: 'http:/localhost:80', + throwHttpErrors: false, + headers: { 'x-auth-token': '111111111', 'fiware-service': 'smart-gondor', 'x-forwarded-for': 'example.com' } +}; +const request_with_headers_and_body = { + prefixUrl: 'http:/localhost:80', + throwHttpErrors: false, + headers: { 'x-auth-token': '111111111', 'fiware-service': 'smart-gondor' }, + json: ngsiPayload +}; + +const open_policy_agent_permit_response = "true"; +const open_policy_agent_deny_response = "false"; + +const config = { + pep_port: 80, + pep: { + app_id: 'application_id', + trusted_apps: [] + }, + idm: { + host: 'keyrock.com', + port: '3000', + ssl: false + }, + app: { + host: 'fiware.org', + port: '1026', + ssl: false // Use true if the app server listens in https + }, + organizations: { + enabled: false + }, + cache_time: 300, + public_paths: [], + authorization: { + enabled: true, + pdp: 'opa', // idm|iShare|xacml|authzforce|opa|azf + header: 'fiware-service', + opa: { + protocol: 'http', + host: 'openpolicyagent.com', + port: 8080 + } + } +}; + +describe('Authorization: Open Policy Agent PDP', function () { + let pep; + let contextBrokerMock; + let idmMock; + let openPolicyAgentMock; + + beforeEach(function (done) { + const app = require('../../app'); + pep = app.start_server('12345', config); + cache.flush(); + nock.cleanAll(); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=111111111&app_id=application_id') + .reply(200, keyrock_user_response); + done(); + }); + + afterEach(function (done) { + pep.close(config.pep_port); + done(); + }); + + describe('When a restricted URL is requested by a legitimate user', function () { + beforeEach(function () { + contextBrokerMock = nock('http://fiware.org:1026').get('/path/entities/urn:ngsi-ld:entity:1111').reply(200, {}); + openPolicyAgentMock = nock('http://openpolicyagent.com:8080').post('/query').reply(200, open_policy_agent_permit_response); + }); + + it('should allow access', function (done) { + got.get('path/entities/urn:ngsi-ld:entity:1111', request_with_headers).then((response) => { + contextBrokerMock.done(); + idmMock.done(); + openPolicyAgentMock.done(); + should.equal(response.statusCode, 200); + done(); + }); + }); + }); + + describe('When a restricted URL is requested by a forbidden user', function () { + beforeEach(function () { + openPolicyAgentMock = nock('http://openpolicyagent.com:8080').post('/query').reply(200, open_policy_agent_deny_response); + }); + + it('should deny access', function (done) { + got.get('path/entities/urn:ngsi-ld:entity:1111', request_with_headers).then((response) => { + idmMock.done(); + openPolicyAgentMock.done(); + should.equal(response.statusCode, 401); + done(); + }); + }); + }); + + describe('When a restricted URL with a query string is requested', function () { + beforeEach(function () { + contextBrokerMock = nock('http://fiware.org:1026') + .get('/path/entities/?ids=urn:ngsi-ld:entity:1111&type=entity') + .reply(200, {}); + openPolicyAgentMock = nock('http://openpolicyagent.com:8080').post('/query').reply(200, open_policy_agent_permit_response); + }); + + it('should allow access based on entities', function (done) { + got.get('path/entities/?ids=urn:ngsi-ld:entity:1111&type=entity', request_with_headers).then((response) => { + contextBrokerMock.done(); + idmMock.done(); + openPolicyAgentMock.done(); + should.equal(response.statusCode, 200); + done(); + }); + }); + }); + + describe('When a restricted URL with a payload body is requested', function () { + beforeEach(function () { + openPolicyAgentMock = nock('http://openpolicyagent.com:8080').post('/query').reply(200, open_policy_agent_permit_response); + contextBrokerMock = nock('http://fiware.org:1026').patch('/path/entityOperations/upsert').reply(200, {}); + }); + + it('should allow access based on entities', function (done) { + got.patch('path/entityOperations/upsert', request_with_headers_and_body).then((response) => { + contextBrokerMock.done(); + idmMock.done(); + openPolicyAgentMock.done(); + should.equal(response.statusCode, 200); + done(); + }); + }); + }); +}); diff --git a/test/unit/payload-pdp-test.js b/test/unit/xacml-pdp-test.js similarity index 79% rename from test/unit/payload-pdp-test.js rename to test/unit/xacml-pdp-test.js index 93ebe86..698286a 100644 --- a/test/unit/payload-pdp-test.js +++ b/test/unit/xacml-pdp-test.js @@ -52,7 +52,7 @@ const keyrock_user_response = { const request_with_headers = { prefixUrl: 'http:/localhost:80', throwHttpErrors: false, - headers: { 'x-auth-token': '111111111', 'fiware-service': 'smart-gondor' , 'x-forwarded-for': 'example.com'} + headers: { 'x-auth-token': '111111111', 'fiware-service': 'smart-gondor', 'x-forwarded-for': 'example.com' } }; const request_with_headers_and_body = { prefixUrl: 'http:/localhost:80', @@ -112,20 +112,30 @@ const config = { public_paths: [], authorization: { enabled: true, - pdp: 'xacml', // idm|iShare|xacml|authzforce - header: 'fiware-service' + pdp: 'xacml', // idm|iShare|xacml|authzforce|opa|azf + header: 'fiware-service', + xacml: { + protocol: 'http', + host: 'xacml.com', + port: 8080 + } } }; -describe('Authorization: Payload PDP', function () { +describe('Authorization: XACML PDP', function () { let pep; let contextBrokerMock; let idmMock; + let xacmlMock; beforeEach(function (done) { const app = require('../../app'); pep = app.start_server('12345', config); cache.flush(); + nock.cleanAll(); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=111111111&app_id=application_id') + .reply(200, keyrock_user_response); done(); }); @@ -136,20 +146,15 @@ describe('Authorization: Payload PDP', function () { describe('When a restricted URL is requested by a legitimate user', function () { beforeEach(function () { - // Set Up - nock.cleanAll(); contextBrokerMock = nock('http://fiware.org:1026').get('/path/entities/urn:ngsi-ld:entity:1111').reply(200, {}); - idmMock = nock('http://keyrock.com:3000') - .get('/user?access_token=111111111&app_id=application_id') - .reply(200, keyrock_user_response); - - idmMock.post('/xacml').reply(200, xacml_permit_response); + xacmlMock = nock('http://xacml.com:8080').post('/xacml').reply(200, xacml_permit_response); }); it('should allow access', function (done) { got.get('path/entities/urn:ngsi-ld:entity:1111', request_with_headers).then((response) => { contextBrokerMock.done(); idmMock.done(); + xacmlMock.done(); should.equal(response.statusCode, 200); done(); }); @@ -158,18 +163,13 @@ describe('Authorization: Payload PDP', function () { describe('When a restricted URL is requested by a forbidden user', function () { beforeEach(function () { - // Set Up - nock.cleanAll(); - idmMock = nock('http://keyrock.com:3000') - .get('/user?access_token=111111111&app_id=application_id') - .reply(200, keyrock_user_response); - - idmMock.post('/xacml').reply(200, xacml_deny_response); + xacmlMock = nock('http://xacml.com:8080').post('/xacml').reply(200, xacml_deny_response); }); it('should deny access', function (done) { got.get('path/entities/urn:ngsi-ld:entity:1111', request_with_headers).then((response) => { idmMock.done(); + xacmlMock.done(); should.equal(response.statusCode, 401); done(); }); @@ -178,22 +178,17 @@ describe('Authorization: Payload PDP', function () { describe('When a restricted URL with a query string is requested', function () { beforeEach(function () { - // Set Up - nock.cleanAll(); contextBrokerMock = nock('http://fiware.org:1026') .get('/path/entities/?ids=urn:ngsi-ld:entity:1111&type=entity') .reply(200, {}); - idmMock = nock('http://keyrock.com:3000') - .get('/user?access_token=111111111&app_id=application_id') - .reply(200, keyrock_user_response); - - idmMock.post('/xacml').reply(200, xacml_permit_response); + xacmlMock = nock('http://xacml.com:8080').post('/xacml').reply(200, xacml_permit_response); }); it('should allow access based on entities', function (done) { got.get('path/entities/?ids=urn:ngsi-ld:entity:1111&type=entity', request_with_headers).then((response) => { contextBrokerMock.done(); idmMock.done(); + xacmlMock.done(); should.equal(response.statusCode, 200); done(); }); @@ -202,20 +197,15 @@ describe('Authorization: Payload PDP', function () { describe('When a restricted URL with a payload body is requested', function () { beforeEach(function () { - // Set Up - nock.cleanAll(); + xacmlMock = nock('http://xacml.com:8080').post('/xacml').reply(200, xacml_permit_response); contextBrokerMock = nock('http://fiware.org:1026').patch('/path/entityOperations/upsert').reply(200, {}); - idmMock = nock('http://keyrock.com:3000') - .get('/user?access_token=111111111&app_id=application_id') - .reply(200, keyrock_user_response); - - idmMock.post('/xacml').reply(200, xacml_permit_response); }); it('should allow access based on entities', function (done) { got.patch('path/entityOperations/upsert', request_with_headers_and_body).then((response) => { contextBrokerMock.done(); idmMock.done(); + xacmlMock.done(); should.equal(response.statusCode, 200); done(); }); From 94a0388de8f6175ecfcf49f525ceac5e58ae3fb9 Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Tue, 10 Aug 2021 14:00:07 +0200 Subject: [PATCH 09/33] Improve annotations --- lib/authorization_functions.js | 18 ++++++++++++++---- lib/pdp/authzforce.js | 8 ++++++++ lib/pdp/iShare.js | 5 +++++ lib/pdp/keyrock.js | 5 +++++ lib/pdp/openPolicyAgent.js | 5 +++++ lib/pdp/xacml.js | 5 +++++ 6 files changed, 42 insertions(+), 4 deletions(-) diff --git a/lib/authorization_functions.js b/lib/authorization_functions.js index 92eaa1a..12d40bc 100644 --- a/lib/authorization_functions.js +++ b/lib/authorization_functions.js @@ -10,7 +10,7 @@ const AZF = require('./pdp/authzforce'); const OPA = require('./pdp/openPolicyAgent'); const IDM = require('./pdp/keyrock'); const XACML = require('./pdp/xacml'); -const iShare = require('./pdp/ishare'); +const ISHARE = require('./pdp/iShare'); const access = require('./access_functions'); const debug = require('debug')('pep-proxy:authorize'); @@ -82,8 +82,7 @@ function openPolicyAgentAuthorize(req,res){ function iShareAuthorize(req, res) { // Check decision through iShare Endpoint const authToken = req.app.get('pepToken'); - iShare - .checkPolicies(authToken, getData(req, res)) + ISHARE.checkPolicies(authToken, getData(req, res)) .then((decision) => { access.adjudicate(req, res, decision); }) @@ -115,9 +114,20 @@ const authorize = { azf: authzforceAuthorize }; +const payload_enabled = { + azf: AZF.payload_enabled, + idm: IDM.payload_enabled, + xacml: XACML.payload_enabled, + ishare: ISHARE.payload_enabled, + opa: OPA.payload_enabled +}; + +/** + * Can the PDP check payloads? + */ exports.checkPayload = function () { const authorization = config_service.get_config().authorization; - return (authorization.pdp === 'ishare' || authorization.pdp === 'xacml' || authorization.pdp === 'opa'); + return payload_enabled[authorization.pdp]; }; exports.authorize = function (req, res, authToken) { diff --git a/lib/pdp/authzforce.js b/lib/pdp/authzforce.js index 025bdf4..97674ee 100644 --- a/lib/pdp/authzforce.js +++ b/lib/pdp/authzforce.js @@ -14,6 +14,14 @@ const got = require('got'); const debug = require('debug')('pep-proxy:AZF-Client'); const cache = require('../cache'); +/** + * Can the Authzforce PDP check payloads? + * + * Not yet, but the Authzforce policies could be reconfigured to check payloads at + * some point + */ +exports.payload_enabled = true; + function getUrl() { const pdp = config_service.get_config().authorization.azf; return (pdp.ssl ? 'https://' : 'http://') + pdp.host + ':' + pdp.port; diff --git a/lib/pdp/iShare.js b/lib/pdp/iShare.js index da356b9..47504b6 100644 --- a/lib/pdp/iShare.js +++ b/lib/pdp/iShare.js @@ -11,6 +11,11 @@ const debug = require('debug')('pep-proxy:iShare-Client'); const got = require('got'); const KEYROCK_URL = (config.idm.ssl ? 'https://' : 'http://') + config.idm.host + ':' + config.idm.port; +/** + * Can the iSHARE PDP check payloads? + */ +exports.payload_enabled = true; + /** * Make request to the iSHARE delegate endpoint and interpret the result * diff --git a/lib/pdp/keyrock.js b/lib/pdp/keyrock.js index 536c91c..ea6a4d1 100644 --- a/lib/pdp/keyrock.js +++ b/lib/pdp/keyrock.js @@ -9,6 +9,11 @@ const config_service = require('../config_service'); const debug = require('debug')('pep-proxy:IDM-Client'); const cache = require('../cache'); const got = require('got'); +/** + * Can the Keyrock PDP check payloads? + */ +exports.payload_enabled = false; + function getUrl() { const idm = config_service.get_config().idm; diff --git a/lib/pdp/openPolicyAgent.js b/lib/pdp/openPolicyAgent.js index e848880..24fb782 100644 --- a/lib/pdp/openPolicyAgent.js +++ b/lib/pdp/openPolicyAgent.js @@ -9,6 +9,11 @@ const config_service = require('../config_service'); const debug = require('debug')('pep-proxy:OPA-Client'); const got = require('got'); +/** + * Can the Open Policy PDP check payloads? + */ +exports.payload_enabled = true; + function getUrl() { const pdp = config_service.get_config().authorization.opa; return (pdp.ssl ? 'https://' : 'http://') + pdp.host + ':' + pdp.port; diff --git a/lib/pdp/xacml.js b/lib/pdp/xacml.js index ff82215..24376ce 100644 --- a/lib/pdp/xacml.js +++ b/lib/pdp/xacml.js @@ -9,6 +9,11 @@ const config_service = require('../config_service'); const debug = require('debug')('pep-proxy:XACML-Client'); const got = require('got'); +/** + * Can the XACML PDP check payloads? + */ +exports.payload_enabled = true; + function getUrl() { const pdp = config_service.get_config().authorization.xacml; return (pdp.ssl ? 'https://' : 'http://') + pdp.host + ':' + pdp.port; From 642f504193ca942c2d27c4d5cf4a745ecbaf3343 Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Tue, 10 Aug 2021 14:54:44 +0200 Subject: [PATCH 10/33] formatting --- lib/authorization_functions.js | 6 +- lib/config_service.js | 2 +- lib/pdp/authzforce.js | 6 +- lib/pdp/iShare.js | 2 +- lib/pdp/keyrock.js | 3 +- lib/pdp/openPolicyAgent.js | 19 ++-- lib/pdp/xacml.js | 2 +- test/unit/opa-pdp-test.js | 20 ++-- test/unit/start-up-test.js | 189 +++++++++++++++++++++++++++++++++ 9 files changed, 222 insertions(+), 27 deletions(-) create mode 100644 test/unit/start-up-test.js diff --git a/lib/authorization_functions.js b/lib/authorization_functions.js index 12d40bc..70a7d27 100644 --- a/lib/authorization_functions.js +++ b/lib/authorization_functions.js @@ -67,7 +67,7 @@ function xacmlAuthorize(req, res) { }); } -function openPolicyAgentAuthorize(req,res){ +function openPolicyAgentAuthorize(req, res) { // Check decision through Open Policy Agent Endpoint const authToken = req.app.get('pepToken'); OPA.checkPolicies(authToken, getData(req, res)) @@ -124,13 +124,13 @@ const payload_enabled = { /** * Can the PDP check payloads? - */ + */ exports.checkPayload = function () { const authorization = config_service.get_config().authorization; return payload_enabled[authorization.pdp]; }; exports.authorize = function (req, res, authToken) { - debug(config_service.get_config().authorization.pdp) + debug(config_service.get_config().authorization.pdp); return authorize[config_service.get_config().authorization.pdp](req, res, authToken); }; diff --git a/lib/config_service.js b/lib/config_service.js index ca07e8b..7fec49e 100644 --- a/lib/config_service.js +++ b/lib/config_service.js @@ -230,7 +230,7 @@ function process_environment_variables(verbose) { } } else { // Authorization is disabled - remove auth config. - config.authorization = {}; + config.authorization = {}; } if (process.env.PEP_PROXY_TENANT_HEADER) { diff --git a/lib/pdp/authzforce.js b/lib/pdp/authzforce.js index 97674ee..683c826 100644 --- a/lib/pdp/authzforce.js +++ b/lib/pdp/authzforce.js @@ -16,10 +16,10 @@ const cache = require('../cache'); /** * Can the Authzforce PDP check payloads? - * - * Not yet, but the Authzforce policies could be reconfigured to check payloads at + * + * Not yet, but the Authzforce policies could be reconfigured to check payloads at * some point - */ + */ exports.payload_enabled = true; function getUrl() { diff --git a/lib/pdp/iShare.js b/lib/pdp/iShare.js index 47504b6..46e32ca 100644 --- a/lib/pdp/iShare.js +++ b/lib/pdp/iShare.js @@ -13,7 +13,7 @@ const KEYROCK_URL = (config.idm.ssl ? 'https://' : 'http://') + config.idm.host /** * Can the iSHARE PDP check payloads? - */ + */ exports.payload_enabled = true; /** diff --git a/lib/pdp/keyrock.js b/lib/pdp/keyrock.js index ea6a4d1..b97a4dc 100644 --- a/lib/pdp/keyrock.js +++ b/lib/pdp/keyrock.js @@ -11,10 +11,9 @@ const cache = require('../cache'); const got = require('got'); /** * Can the Keyrock PDP check payloads? - */ + */ exports.payload_enabled = false; - function getUrl() { const idm = config_service.get_config().idm; return (idm.ssl ? 'https://' : 'http://') + idm.host + ':' + idm.port; diff --git a/lib/pdp/openPolicyAgent.js b/lib/pdp/openPolicyAgent.js index 24fb782..7d41375 100644 --- a/lib/pdp/openPolicyAgent.js +++ b/lib/pdp/openPolicyAgent.js @@ -11,7 +11,7 @@ const got = require('got'); /** * Can the Open Policy PDP check payloads? - */ + */ exports.payload_enabled = true; function getUrl() { @@ -54,7 +54,6 @@ exports.checkPolicies = function (token, data) { }); }; - /** * Create a payload for making an Open Policy Agent request * based on the action,resource,tenant and attributes @@ -67,14 +66,14 @@ function getPolicy(data) { const appId = data.appId; debug('Checking authorization to roles', roles, 'to do ', action, ' on ', resource, 'and app ', appId); const json = { - appId, - resource, - roles, - action, - tenant : data.tenant_header, - ids: data.payload_ids, - attrs: data.payload_attrs, - types: data.payload_types + appId, + resource, + roles, + action, + tenant: data.tenant_header, + ids: data.payload_ids, + attrs: data.payload_attrs, + types: data.payload_types }; debug('Open Policy Agent request: ', JSON.stringify(json)); diff --git a/lib/pdp/xacml.js b/lib/pdp/xacml.js index 24376ce..fcacd65 100644 --- a/lib/pdp/xacml.js +++ b/lib/pdp/xacml.js @@ -11,7 +11,7 @@ const got = require('got'); /** * Can the XACML PDP check payloads? - */ + */ exports.payload_enabled = true; function getUrl() { diff --git a/test/unit/opa-pdp-test.js b/test/unit/opa-pdp-test.js index 6c29f68..d525c1e 100644 --- a/test/unit/opa-pdp-test.js +++ b/test/unit/opa-pdp-test.js @@ -61,8 +61,8 @@ const request_with_headers_and_body = { json: ngsiPayload }; -const open_policy_agent_permit_response = "true"; -const open_policy_agent_deny_response = "false"; +const open_policy_agent_permit_response = 'true'; +const open_policy_agent_deny_response = 'false'; const config = { pep_port: 80, @@ -122,7 +122,9 @@ describe('Authorization: Open Policy Agent PDP', function () { describe('When a restricted URL is requested by a legitimate user', function () { beforeEach(function () { contextBrokerMock = nock('http://fiware.org:1026').get('/path/entities/urn:ngsi-ld:entity:1111').reply(200, {}); - openPolicyAgentMock = nock('http://openpolicyagent.com:8080').post('/query').reply(200, open_policy_agent_permit_response); + openPolicyAgentMock = nock('http://openpolicyagent.com:8080') + .post('/query') + .reply(200, open_policy_agent_permit_response); }); it('should allow access', function (done) { @@ -138,7 +140,9 @@ describe('Authorization: Open Policy Agent PDP', function () { describe('When a restricted URL is requested by a forbidden user', function () { beforeEach(function () { - openPolicyAgentMock = nock('http://openpolicyagent.com:8080').post('/query').reply(200, open_policy_agent_deny_response); + openPolicyAgentMock = nock('http://openpolicyagent.com:8080') + .post('/query') + .reply(200, open_policy_agent_deny_response); }); it('should deny access', function (done) { @@ -156,7 +160,9 @@ describe('Authorization: Open Policy Agent PDP', function () { contextBrokerMock = nock('http://fiware.org:1026') .get('/path/entities/?ids=urn:ngsi-ld:entity:1111&type=entity') .reply(200, {}); - openPolicyAgentMock = nock('http://openpolicyagent.com:8080').post('/query').reply(200, open_policy_agent_permit_response); + openPolicyAgentMock = nock('http://openpolicyagent.com:8080') + .post('/query') + .reply(200, open_policy_agent_permit_response); }); it('should allow access based on entities', function (done) { @@ -172,7 +178,9 @@ describe('Authorization: Open Policy Agent PDP', function () { describe('When a restricted URL with a payload body is requested', function () { beforeEach(function () { - openPolicyAgentMock = nock('http://openpolicyagent.com:8080').post('/query').reply(200, open_policy_agent_permit_response); + openPolicyAgentMock = nock('http://openpolicyagent.com:8080') + .post('/query') + .reply(200, open_policy_agent_permit_response); contextBrokerMock = nock('http://fiware.org:1026').patch('/path/entityOperations/upsert').reply(200, {}); }); diff --git a/test/unit/start-up-test.js b/test/unit/start-up-test.js new file mode 100644 index 0000000..04a28a5 --- /dev/null +++ b/test/unit/start-up-test.js @@ -0,0 +1,189 @@ +const config_service = require('../../lib/config_service'); + +const config = { + pep_port: 80, + pep: { + app_id: 'application_id', + trusted_apps: [] + }, + idm: { + host: 'keyrock.com', + port: '3000', + ssl: false + }, + app: { + host: 'fiware.org', + port: '1026', + ssl: false // Use true if the app server listens in https + }, + organizations: { + enabled: false + }, + cache_time: 300, + public_paths: [], + authorization: { + enabled: false, + pdp: 'authzforce', // idm|iShare|xacml|authzforce|opa|azf + } +}; + + + +describe('When the PEP Proxy is started with environment variables', function () { + beforeEach(function () { + process.env.PEP_PROXY_PORT = 8080 + process.env.PEP_PROXY_HTTPS_ENABLED = "true" + process.env.PEP_PROXY_HTTPS_PORT = 443; + process.env.PEP_PROXY_IDM_HOST = 'idm_host'; + process.env.PEP_PROXY_IDM_PORT = 3000; + process.env.PEP_PROXY_IDM_SSL_ENABLED = "true"; + process.env.PEP_PROXY_APP_HOST = 'app_host'; + process.env.PEP_PROXY_APP_PORT = '1026'; + process.env.PEP_PROXY_APP_SSL_ENABLED = "true"; + process.env.PEP_PROXY_ORG_ENABLED = "true"; + process.env.PEP_PROXY_ORG_HEADER = 'organization'; + process.env.PEP_PROXY_APP_ID = '9999999111'; + process.env.PEP_PROXY_USERNAME = 'user'; + process.env.PEP_PASSWORD = 'password'; + process.env.PEP_TOKEN_SECRET = 'secret-token'; + process.env.PEP_TRUSTED_APPS = ''; + + process.env.PEP_PROXY_PUBLIC_PATHS = 'a,b,c'; + process.env.PEP_PROXY_AUTH_FOR_NGINX; + process.env.PEP_PROXY_MAGIC_KEY; + process.env.PEP_PROXY_DEBUG; + }); + + afterEach(function () { + delete process.env.IOTA_CB_HOST; + delete process.env.PEP_PROXY_PORT; + delete process.env.PEP_PROXY_HTTPS_ENABLED; + delete process.env.PEP_PROXY_HTTPS_PORT; + delete process.env.PEP_PROXY_IDM_HOST; + delete process.env.PEP_PROXY_IDM_PORT; + delete process.env.PEP_PROXY_IDM_SSL_ENABLED; + delete process.env.PEP_PROXY_APP_HOST; + delete process.env.PEP_PROXY_APP_PORT; + delete process.env.PEP_PROXY_APP_SSL_ENABLED; + delete process.env.PEP_PROXY_ORG_ENABLED; + delete process.env.PEP_PROXY_ORG_HEADER; + delete process.env.PEP_PROXY_APP_ID; + delete process.env.PEP_PROXY_USERNAME; + delete process.env.PEP_PASSWORD; + delete process.env.PEP_TOKEN_SECRET; + delete process.env.PEP_TRUSTED_APPS; + + delete process.env.PEP_PROXY_PUBLIC_PATHS; + delete process.env.PEP_PROXY_AUTH_FOR_NGINX; + delete process.env.PEP_PROXY_MAGIC_KEY; + delete process.env.PEP_PROXY_DEBUG; + }); + + + + it('should amend the configuration', function (done) { + config_service.set_config(config, true); + const pep_config = config_service.get_config(); + + should.equal(pep_config.pep_port, "8080"); + should.equal(pep_config.pep.app_id, "9999999111"); + should.equal(pep_config.pep.username, "user"); + should.equal(pep_config.pep.password, "password"); + should.equal(pep_config.pep.token.secret, "secret-token"); + + should.equal(pep_config.idm.host, "idm_host"); + should.equal(pep_config.idm.port, "3000"); + should.equal(pep_config.idm.ssl, true); + + should.equal(pep_config.app.host, "app_host"); + should.equal(pep_config.app.port, "1026"); + should.equal(pep_config.app.ssl, true); + + should.equal(pep_config.organizations.enabled, true); + should.equal(pep_config.organizations.header, "organization"); + done(); + }); +}); + + + + +describe('When any PDP is configured with environment variables', function () { + beforeEach(function () { + process.env.PEP_PROXY_AUTH_ENABLED = "true"; + process.env.PEP_PROXY_PDP = 'opa'; + process.env.PEP_PROXY_PDP_PROTOCOL = 'https'; + process.env.PEP_PROXY_PDP_HOST = 'pdp-host'; + process.env.PEP_PROXY_PDP_PORT = 443; + }); + + afterEach(function () { + delete process.env.PEP_PROXY_PDP; + delete process.env.PEP_PROXY_PDP_PROTOCOL; + delete process.env.PEP_PROXY_PDP_HOST; + delete process.env.PEP_PROXY_PDP_PORT; + }); + + + + it('should amend the PDP configuration', function (done) { + config_service.set_config(config, true); + const authorization = config_service.get_config().authorization; + const pdp = config_service.get_config().authorization.opa; + + should.equal(authorization.enabled,true); + should.equal(authorization.pdp, "opa"); + should.equal(pdp.protocol, "https"); + should.equal(pdp.host, "pdp-host"); + should.equal(pdp.port,"443"); + + console.error(JSON.stringify(authorization)) + done(); + }); +}); + + +describe('When the Authzforce PDP is started with environment variables', function () { + beforeEach(function () { + process.env.PEP_PROXY_AZF_PROTOCOL = 'http'; + process.env.PEP_PROXY_AZF_HOST = 'authzforce.com'; + process.env.PEP_PROXY_AZF_PORT = 9090; + process.env.PEP_PROXY_AZF_CUSTOM_POLICY = 'policy'; + }); + + afterEach(function () { + delete process.env.PEP_PROXY_AZF_PROTOCOL; + delete process.env.PEP_PROXY_AZF_HOST; + delete process.env.PEP_PROXY_AZF_PORT; + delete process.env.PEP_PROXY_AZF_CUSTOM_POLICY; + }); + + + + it('should amend the PDP configuration', function (done) { + config_service.set_config(config, true); + const azf = config_service.get_config().authorization.azf; + + should.equal(azf.protocol, "http"); + should.equal(azf.host, "authzforce.com"); + should.equal(azf.port, "9090"); + should.equal(azf.custom_policy, "policy"); + done(); + }); +}); + +describe('When authorization is disabled with environment variables', function () { + beforeEach(function () { + process.env.PEP_PROXY_AUTH_ENABLED = "false"; + }); + + afterEach(function () { + delete process.env.PEP_PROXY_AUTH_ENABLED; + }); + it('should remove the authorization config', function (done) { + config_service.set_config(config, true); + const authorization = config_service.get_config().authorization; + authorization.should.be.empty() + done(); + }); +}); From 09cf330241b768b5f4c8c4a9bc80fa9e90bbe3c6 Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Tue, 10 Aug 2021 17:46:46 +0200 Subject: [PATCH 11/33] Add iSHARE prototype PDP. --- controllers/root.js | 11 +- lib/authorization_functions.js | 26 +++- lib/config_service.js | 2 +- lib/pdp/authzforce.js | 6 +- lib/pdp/iShare.js | 79 ++++++++++- lib/pdp/keyrock.js | 4 + lib/pdp/openPolicyAgent.js | 4 + lib/pdp/xacml.js | 4 + test/unit/ishare-test.js | 249 +++++++++++++++++++++++++++++++++ test/unit/start-up-test.js | 163 ++++++++++----------- 10 files changed, 440 insertions(+), 108 deletions(-) create mode 100644 test/unit/ishare-test.js diff --git a/controllers/root.js b/controllers/root.js index 09db5e5..4560043 100644 --- a/controllers/root.js +++ b/controllers/root.js @@ -10,7 +10,7 @@ let config; const IDM = require('../lib/pdp/keyrock'); const jsonwebtoken = require('jsonwebtoken'); const access = require('../lib/access_functions'); -const authorize = require('../lib/authorization_functions').authorize; +const PDP = require('../lib/authorization_functions'); const debug = require('debug')('pep-proxy:root'); @@ -41,10 +41,9 @@ function validateAccessJWT(req, res, tokens) { return access.permit(req, res); } - const policy_decision_point = config.authorization.pdp; - if (policy_decision_point === 'azf') { - // JWT Authorization by Authzforce - return authorize(req, res, tokens.authToken); + if (PDP.validateJWT()) { + // JWT Authorization by PDP + return PDP.authorize(req, res, tokens.authToken); } else { // JWT Authorization by IDM, the user will already exist. tokens.jwtExpiry = userInfo.exp; @@ -67,7 +66,7 @@ async function validateAccessIDM(req, res, tokens) { req.user = await IDM.authenticateUser(tokens, req.method, req.path, tenant_header); setHeaders(req); if (config.authorization.enabled) { - return authorize(req, res, tokens.authToken); + return PDP.authorize(req, res, tokens.authToken); } else { // Authentication only. return access.permit(req, res); diff --git a/lib/authorization_functions.js b/lib/authorization_functions.js index 70a7d27..0d3ac28 100644 --- a/lib/authorization_functions.js +++ b/lib/authorization_functions.js @@ -75,19 +75,20 @@ function openPolicyAgentAuthorize(req, res) { access.adjudicate(req, res, decision); }) .catch((e) => { - access.internalError(res, e, 'OPA'); + access.internalError(res, e, 'Open Policy Agent'); }); } function iShareAuthorize(req, res) { // Check decision through iShare Endpoint + const user = req.user || {}; const authToken = req.app.get('pepToken'); - ISHARE.checkPolicies(authToken, getData(req, res)) + return ISHARE.checkPolicies(authToken, getData(req, res), user.delegationEvidence) .then((decision) => { access.adjudicate(req, res, decision); }) .catch((e) => { - access.internalError(res, e, 'iShare'); + access.internalError(res, e, 'iSHARE'); }); } function authzforceAuthorize(req, res, authToken) { @@ -101,7 +102,7 @@ function authzforceAuthorize(req, res, authToken) { debug('Domain not found: ', e); access.deny(res); } else { - access.internalError(res, e, 'AZF'); + access.internalError(res, e, 'Authzforce'); } }); } @@ -122,6 +123,14 @@ const payload_enabled = { opa: OPA.payload_enabled }; +const jwt_enabled = { + azf: AZF.jwt_enabled, + idm: IDM.jwt_enabled, + xacml: XACML.jwt_enabled, + ishare: ISHARE.jwt_enabled, + opa: OPA.jwt_enabled +}; + /** * Can the PDP check payloads? */ @@ -130,7 +139,14 @@ exports.checkPayload = function () { return payload_enabled[authorization.pdp]; }; +/** + * Can the PDP validate from a JWT? + */ +exports.validateJWT = function () { + const authorization = config_service.get_config().authorization; + return jwt_enabled[authorization.pdp]; +}; + exports.authorize = function (req, res, authToken) { - debug(config_service.get_config().authorization.pdp); return authorize[config_service.get_config().authorization.pdp](req, res, authToken); }; diff --git a/lib/config_service.js b/lib/config_service.js index 7fec49e..c8fdd15 100644 --- a/lib/config_service.js +++ b/lib/config_service.js @@ -5,7 +5,7 @@ * */ -const debug = require('debug')('pep-proxy:Server'); +const debug = require('debug')('pep-proxy:config'); const path = require('path'); const fs = require('fs'); diff --git a/lib/pdp/authzforce.js b/lib/pdp/authzforce.js index 683c826..e4edbc7 100644 --- a/lib/pdp/authzforce.js +++ b/lib/pdp/authzforce.js @@ -20,7 +20,11 @@ const cache = require('../cache'); * Not yet, but the Authzforce policies could be reconfigured to check payloads at * some point */ -exports.payload_enabled = true; +exports.payload_enabled = false; +/** + * Can the Authzforce PDP authorize via a JWT? + */ +exports.jwt_enabled = true; function getUrl() { const pdp = config_service.get_config().authorization.azf; diff --git a/lib/pdp/iShare.js b/lib/pdp/iShare.js index 46e32ca..f9b0844 100644 --- a/lib/pdp/iShare.js +++ b/lib/pdp/iShare.js @@ -6,15 +6,22 @@ */ const config_service = require('../config_service'); -const config = config_service.get_config(); const debug = require('debug')('pep-proxy:iShare-Client'); -const got = require('got'); -const KEYROCK_URL = (config.idm.ssl ? 'https://' : 'http://') + config.idm.host + ':' + config.idm.port; +//const got = require('got'); + +function getUrl() { + const pdp = config_service.get_config().authorization.ishare; + return (pdp.ssl ? 'https://' : 'http://') + pdp.host + ':' + pdp.port; +} /** * Can the iSHARE PDP check payloads? */ exports.payload_enabled = true; +/** + * Can the iSHARE PDP authorize via JWT? + */ +exports.jwt_enabled = true; /** * Make request to the iSHARE delegate endpoint and interpret the result @@ -25,14 +32,34 @@ exports.payload_enabled = true; * * @return permit/deny */ -exports.checkPolicies = function (req, data) { +exports.checkPolicies = function (token, data, ishare_policy) { return new Promise((resolve, reject) => { debug('Checking policy'); + + const unix_timestamp = Math.floor(new Date().getTime() / 1000); + if (!ishare_policy || !ishare_policy.policySets) { + debug('No iSHARE Policy found'); + return resolve(false); + } else if (ishare_policy.notBefore > unix_timestamp) { + debug('Attached iSHARE Policy not yet valid'); + return resolve(false); + } else if (ishare_policy.notOnOrAfter <= unix_timestamp) { + debug('Attached iSHARE Policy expired'); + return resolve(false); + } else if (!valid_payload(data, ishare_policy.policySets)) { + debug('Attached iSHARE Policy disallows the request'); + return resolve(false); + } + + return resolve(true); + + /* + return got .post('delegate', { - prefixUrl: KEYROCK_URL, + prefixUrl: getUrl(), headers: { - 'X-Auth-Token': req.app.get('pepToken') + 'X-Auth-Token': token }, json: iShareBody(data) }) @@ -46,10 +73,48 @@ exports.checkPolicies = function (req, data) { } debug('Error in iShare communication ', error); return reject(error); - }); + });*/ }); }; +function valid_payload(data, policy_sets) { + const result = policy_sets.some((policy_set) => { + return policy_set.policies.some((policy) => { + const ruleEffect = policy.rules[0].effect === 'Permit'; + let fireRule = true; + + if (policy.target.resource.type && data.payload_types) { + fireRule = data.payload_types.length === 1 && data.payload_types[0] === policy.target.resource.type; + } + if (fireRule && policy.target.actions) { + fireRule = policy.target.actions.some((action) => { + return action.toUpperCase() === data.action; + }); + } + + if (fireRule && policy.target.resource.identifiers && data.payload_entity_ids) { + data.payload_entity_ids.forEach((id) => { + fireRule = policy.target.resource.identifiers.some((identifier) => { + const regex = new RegExp(identifier, 'i'); + return regex.test(id); + }); + }); + } + + if (fireRule && policy.target.resource.attributes && data.payload_entity_attrs) { + data.payload_entity_attrs.forEach((attr) => { + fireRule = policy.target.resource.attributes.some((attribute) => { + const regex = new RegExp(attribute, 'i'); + return regex.test(attr); + }); + }); + } + return fireRule ? ruleEffect : !ruleEffect; + }); + }); + return result; +} + /** * Create a payload for making an iSHARE request * based on the action,resource,tenant and attributes diff --git a/lib/pdp/keyrock.js b/lib/pdp/keyrock.js index b97a4dc..8426241 100644 --- a/lib/pdp/keyrock.js +++ b/lib/pdp/keyrock.js @@ -13,6 +13,10 @@ const got = require('got'); * Can the Keyrock PDP check payloads? */ exports.payload_enabled = false; +/** + * Can the Keyrock PDP check JWT? + */ +exports.jwt_enabled = false; function getUrl() { const idm = config_service.get_config().idm; diff --git a/lib/pdp/openPolicyAgent.js b/lib/pdp/openPolicyAgent.js index 7d41375..f0fe909 100644 --- a/lib/pdp/openPolicyAgent.js +++ b/lib/pdp/openPolicyAgent.js @@ -13,6 +13,10 @@ const got = require('got'); * Can the Open Policy PDP check payloads? */ exports.payload_enabled = true; +/** + * Can the Open Policy PDP check JWT? + */ +exports.jwt_enabled = false; function getUrl() { const pdp = config_service.get_config().authorization.opa; diff --git a/lib/pdp/xacml.js b/lib/pdp/xacml.js index fcacd65..8bb73e7 100644 --- a/lib/pdp/xacml.js +++ b/lib/pdp/xacml.js @@ -13,6 +13,10 @@ const got = require('got'); * Can the XACML PDP check payloads? */ exports.payload_enabled = true; +/** + * Can the XACML PDP check JWT? + */ +exports.jwt_enabled = false; function getUrl() { const pdp = config_service.get_config().authorization.xacml; diff --git a/test/unit/ishare-test.js b/test/unit/ishare-test.js new file mode 100644 index 0000000..ce7e7f6 --- /dev/null +++ b/test/unit/ishare-test.js @@ -0,0 +1,249 @@ +/* + * Copyright 2021 - Universidad Politécnica de Madrid. + * + * This file is part of Keyrock + * + */ + +const got = require('got'); +const should = require('should'); +const nock = require('nock'); +const cache = require('../../lib/cache'); +const jwt = require('jsonwebtoken'); + +const ngsiPayload = [ + { + id: 'urn:ngsi-ld:TemperatureSensor:002', + type: 'TemperatureSensor', + category: { + type: 'Property', + value: 'sensor' + }, + temperature: { + type: 'Property', + value: 21, + unitCode: 'CEL' + } + }, + { + id: 'urn:ngsi-ld:TemperatureSensor:003', + type: 'TemperatureSensor', + category: { + type: 'Property', + value: 'sensor' + }, + temperature: { + type: 'Property', + value: 27, + unitCode: 'CEL' + } + } +]; + +const token = jwt.sign( + { + app_id: 'application_id', + trusted_apps: [], + id: 'username', + displayName: 'Some User', + delegationEvidence: { + notBefore: Math.floor(new Date().getTime() / 1000) - 2000, + notOnOrAfter: Math.floor(new Date().getTime() / 1000) + 2000, + policyIssuer: 'EU.EORI.NLPACKETDEL', + target: { + accessSubject: 'EU.EORI.NLNOCHEAPER' + }, + policySets: [ + { + maxDelegationDepth: 1, + target: { + environment: { + licenses: ['ISHARE.0001'] + } + }, + policies: [ + { + target: { + resource: { + type: 'TemperatureSensor', + identifiers: ['*'], + attributes: ['*'] + }, + actions: ['GET', 'PATCH'] + }, + rules: [ + { + effect: 'Permit' + } + ] + }, + { + target: { + resource: { + type: 'SoilSensor', + identifiers: ['*'], + attributes: ['*'] + }, + actions: ['GET'] + }, + rules: [ + { + effect: 'Permit' + } + ] + } + ] + } + ] + } + }, + 'shhhhh' +); + +const request_with_jwt = { + prefixUrl: 'http:/localhost:80', + throwHttpErrors: false, + headers: { 'x-auth-token': token }, + retry: 0 +}; + +const request_with_jwt_and_body = { + prefixUrl: 'http:/localhost:80', + throwHttpErrors: false, + headers: { 'x-auth-token': token }, + json: ngsiPayload, + retry: 0 +}; + +const config = { + magic_key: '999999999', + pep_port: 80, + pep: { + app_id: 'application_id', + trusted_apps: [], + token: { secret: 'shhhhh' } + }, + idm: { + host: 'keyrock.com', + port: '3000', + ssl: false + }, + app: { + host: 'fiware.org', + port: '1026', + ssl: false // Use true if the app server listens in https + }, + organizations: { + enabled: false + }, + cache_time: 300, + public_paths: ['/public'], + authorization: { + enabled: true, + pdp: 'ishare', // idm|iShare|xacml|authzforce|opa|azf + header: 'fiware-service', + ishare: { + protocol: 'http', + host: 'ishare.org', + port: 8080 + } + } +}; + +const ishare_policy_recognized = 'HELLO'; +const ishare_policy_not_recognized = 'WORLD'; + +describe('Authorization: iSHARE PDP', function () { + let pep; + let contextBrokerMock; + let iShareMock; + + beforeEach(function (done) { + const app = require('../../app'); + pep = app.start_server('12345', config); + nock.cleanAll(); + cache.flush(); + done(); + }); + + afterEach(function (done) { + pep.close(config.pep_port); + done(); + }); + + describe('When a restricted URL matches the JWT policy and is legitimate', function () { + beforeEach(function () { + contextBrokerMock = nock('http://fiware.org:1026') + .get('/path/entities/urn:ngsi-ld:SoilSensor:1111') + .reply(200, {}); + iShareMock = nock('http://ishare.com:8080').post('/delegate').reply(200, ishare_policy_recognized); + }); + + it('should allow access', function (done) { + got.get('path/entities/urn:ngsi-ld:SoilSensor:1111', request_with_jwt).then((response) => { + contextBrokerMock.done(); + //iShareMock.done(); + should.equal(response.statusCode, 200); + done(); + }); + }); + }); + + describe('When a restricted URL does not match the attached JWT policy', function () { + it('should deny access', function (done) { + got.get('path/entities/urn:ngsi-ld:Tractor:1111?type=Tractor', request_with_jwt).then((response) => { + //iShareMock.done(); + should.equal(response.statusCode, 401); + done(); + }); + }); + }); + + xdescribe('When a JWT policy is not recognized by the iSHARE delegate', function () { + beforeEach(function () { + iShareMock = nock('http://ishare.com:8080').post('/delegate').reply(200, ishare_policy_not_recognized); + }); + + it('should deny access', function (done) { + got.get('path/entities/urn:ngsi-ld:SoilSensor:1111', request_with_jwt).then((response) => { + //iShareMock.done(); + should.equal(response.statusCode, 401); + done(); + }); + }); + }); + + describe('When a restricted URL with a string is requested', function () { + beforeEach(function () { + contextBrokerMock = nock('http://fiware.org:1026') + .get('/path/entities/?ids=urn:ngsi-ld:SoilSensor:1111&type=SoilSensor') + .reply(200, {}); + iShareMock = nock('http://ishare.com:8080').post('/delegate').reply(200, ishare_policy_recognized); + }); + + it('should allow access based on the JWT policy and entities', function (done) { + got.get('path/entities/?ids=urn:ngsi-ld:SoilSensor:1111&type=SoilSensor', request_with_jwt).then((response) => { + contextBrokerMock.done(); + //iShareMock.done(); + should.equal(response.statusCode, 200); + done(); + }); + }); + }); + + describe('When a restricted URL with a payload body is requested', function () { + beforeEach(function () { + iShareMock = nock('http://ishare.com:8080').post('/delegate').reply(200, ishare_policy_recognized); + contextBrokerMock = nock('http://fiware.org:1026').patch('/path/entityOperations/upsert').reply(200, {}); + }); + + it('should allow access based on the JWT policy and entities', function (done) { + got.patch('path/entityOperations/upsert', request_with_jwt_and_body).then((response) => { + contextBrokerMock.done(); + //iShareMock.done(); + should.equal(response.statusCode, 200); + done(); + }); + }); + }); +}); diff --git a/test/unit/start-up-test.js b/test/unit/start-up-test.js index 04a28a5..2ab74f1 100644 --- a/test/unit/start-up-test.js +++ b/test/unit/start-up-test.js @@ -1,4 +1,5 @@ const config_service = require('../../lib/config_service'); +const should = require('should'); const config = { pep_port: 80, @@ -23,35 +24,33 @@ const config = { public_paths: [], authorization: { enabled: false, - pdp: 'authzforce', // idm|iShare|xacml|authzforce|opa|azf + pdp: 'authzforce' // idm|iShare|xacml|authzforce|opa|azf } }; - - describe('When the PEP Proxy is started with environment variables', function () { beforeEach(function () { - process.env.PEP_PROXY_PORT = 8080 - process.env.PEP_PROXY_HTTPS_ENABLED = "true" - process.env.PEP_PROXY_HTTPS_PORT = 443; - process.env.PEP_PROXY_IDM_HOST = 'idm_host'; - process.env.PEP_PROXY_IDM_PORT = 3000; - process.env.PEP_PROXY_IDM_SSL_ENABLED = "true"; - process.env.PEP_PROXY_APP_HOST = 'app_host'; - process.env.PEP_PROXY_APP_PORT = '1026'; - process.env.PEP_PROXY_APP_SSL_ENABLED = "true"; - process.env.PEP_PROXY_ORG_ENABLED = "true"; - process.env.PEP_PROXY_ORG_HEADER = 'organization'; - process.env.PEP_PROXY_APP_ID = '9999999111'; - process.env.PEP_PROXY_USERNAME = 'user'; - process.env.PEP_PASSWORD = 'password'; - process.env.PEP_TOKEN_SECRET = 'secret-token'; - process.env.PEP_TRUSTED_APPS = ''; - - process.env.PEP_PROXY_PUBLIC_PATHS = 'a,b,c'; - process.env.PEP_PROXY_AUTH_FOR_NGINX; - process.env.PEP_PROXY_MAGIC_KEY; - process.env.PEP_PROXY_DEBUG; + process.env.PEP_PROXY_PORT = 8080; + process.env.PEP_PROXY_HTTPS_ENABLED = 'true'; + process.env.PEP_PROXY_HTTPS_PORT = 443; + process.env.PEP_PROXY_IDM_HOST = 'idm_host'; + process.env.PEP_PROXY_IDM_PORT = 3000; + process.env.PEP_PROXY_IDM_SSL_ENABLED = 'true'; + process.env.PEP_PROXY_APP_HOST = 'app_host'; + process.env.PEP_PROXY_APP_PORT = '1026'; + process.env.PEP_PROXY_APP_SSL_ENABLED = 'true'; + process.env.PEP_PROXY_ORG_ENABLED = 'true'; + process.env.PEP_PROXY_ORG_HEADER = 'organization'; + process.env.PEP_PROXY_APP_ID = '9999999111'; + process.env.PEP_PROXY_USERNAME = 'user'; + process.env.PEP_PASSWORD = 'password'; + process.env.PEP_TOKEN_SECRET = 'secret-token'; + process.env.PEP_TRUSTED_APPS = ''; + + process.env.PEP_PROXY_PUBLIC_PATHS = 'a,b,c'; + process.env.PEP_PROXY_AUTH_FOR_NGINX = 'false'; + process.env.PEP_PROXY_MAGIC_KEY = '54321'; + process.env.PEP_PROXY_DEBUG = 'PEP-Proxy:*'; }); afterEach(function () { @@ -79,42 +78,37 @@ describe('When the PEP Proxy is started with environment variables', function () delete process.env.PEP_PROXY_DEBUG; }); - - it('should amend the configuration', function (done) { - config_service.set_config(config, true); - const pep_config = config_service.get_config(); - - should.equal(pep_config.pep_port, "8080"); - should.equal(pep_config.pep.app_id, "9999999111"); - should.equal(pep_config.pep.username, "user"); - should.equal(pep_config.pep.password, "password"); - should.equal(pep_config.pep.token.secret, "secret-token"); - - should.equal(pep_config.idm.host, "idm_host"); - should.equal(pep_config.idm.port, "3000"); - should.equal(pep_config.idm.ssl, true); - - should.equal(pep_config.app.host, "app_host"); - should.equal(pep_config.app.port, "1026"); - should.equal(pep_config.app.ssl, true); - - should.equal(pep_config.organizations.enabled, true); - should.equal(pep_config.organizations.header, "organization"); - done(); + config_service.set_config(config, true); + const pep_config = config_service.get_config(); + + should.equal(pep_config.pep_port, '8080'); + should.equal(pep_config.pep.app_id, '9999999111'); + should.equal(pep_config.pep.username, 'user'); + should.equal(pep_config.pep.password, 'password'); + should.equal(pep_config.pep.token.secret, 'secret-token'); + + should.equal(pep_config.idm.host, 'idm_host'); + should.equal(pep_config.idm.port, '3000'); + should.equal(pep_config.idm.ssl, true); + + should.equal(pep_config.app.host, 'app_host'); + should.equal(pep_config.app.port, '1026'); + should.equal(pep_config.app.ssl, true); + + should.equal(pep_config.organizations.enabled, true); + should.equal(pep_config.organizations.header, 'organization'); + done(); }); }); - - - describe('When any PDP is configured with environment variables', function () { beforeEach(function () { - process.env.PEP_PROXY_AUTH_ENABLED = "true"; - process.env.PEP_PROXY_PDP = 'opa'; - process.env.PEP_PROXY_PDP_PROTOCOL = 'https'; - process.env.PEP_PROXY_PDP_HOST = 'pdp-host'; - process.env.PEP_PROXY_PDP_PORT = 443; + process.env.PEP_PROXY_AUTH_ENABLED = 'true'; + process.env.PEP_PROXY_PDP = 'opa'; + process.env.PEP_PROXY_PDP_PROTOCOL = 'https'; + process.env.PEP_PROXY_PDP_HOST = 'pdp-host'; + process.env.PEP_PROXY_PDP_PORT = 443; }); afterEach(function () { @@ -124,31 +118,26 @@ describe('When any PDP is configured with environment variables', function () { delete process.env.PEP_PROXY_PDP_PORT; }); - - it('should amend the PDP configuration', function (done) { - config_service.set_config(config, true); - const authorization = config_service.get_config().authorization; - const pdp = config_service.get_config().authorization.opa; - - should.equal(authorization.enabled,true); - should.equal(authorization.pdp, "opa"); - should.equal(pdp.protocol, "https"); - should.equal(pdp.host, "pdp-host"); - should.equal(pdp.port,"443"); - - console.error(JSON.stringify(authorization)) - done(); + config_service.set_config(config, true); + const authorization = config_service.get_config().authorization; + const pdp = config_service.get_config().authorization.opa; + + should.equal(authorization.enabled, true); + should.equal(authorization.pdp, 'opa'); + should.equal(pdp.protocol, 'https'); + should.equal(pdp.host, 'pdp-host'); + should.equal(pdp.port, '443'); + done(); }); }); - describe('When the Authzforce PDP is started with environment variables', function () { beforeEach(function () { - process.env.PEP_PROXY_AZF_PROTOCOL = 'http'; - process.env.PEP_PROXY_AZF_HOST = 'authzforce.com'; - process.env.PEP_PROXY_AZF_PORT = 9090; - process.env.PEP_PROXY_AZF_CUSTOM_POLICY = 'policy'; + process.env.PEP_PROXY_AZF_PROTOCOL = 'http'; + process.env.PEP_PROXY_AZF_HOST = 'authzforce.com'; + process.env.PEP_PROXY_AZF_PORT = 9090; + process.env.PEP_PROXY_AZF_CUSTOM_POLICY = 'policy'; }); afterEach(function () { @@ -158,32 +147,30 @@ describe('When the Authzforce PDP is started with environment variables', functi delete process.env.PEP_PROXY_AZF_CUSTOM_POLICY; }); - - it('should amend the PDP configuration', function (done) { - config_service.set_config(config, true); - const azf = config_service.get_config().authorization.azf; - - should.equal(azf.protocol, "http"); - should.equal(azf.host, "authzforce.com"); - should.equal(azf.port, "9090"); - should.equal(azf.custom_policy, "policy"); - done(); + config_service.set_config(config, true); + const azf = config_service.get_config().authorization.azf; + + should.equal(azf.protocol, 'http'); + should.equal(azf.host, 'authzforce.com'); + should.equal(azf.port, '9090'); + should.equal(azf.custom_policy, 'policy'); + done(); }); }); describe('When authorization is disabled with environment variables', function () { beforeEach(function () { - process.env.PEP_PROXY_AUTH_ENABLED = "false"; + process.env.PEP_PROXY_AUTH_ENABLED = 'false'; }); afterEach(function () { delete process.env.PEP_PROXY_AUTH_ENABLED; }); it('should remove the authorization config', function (done) { - config_service.set_config(config, true); - const authorization = config_service.get_config().authorization; - authorization.should.be.empty() - done(); + config_service.set_config(config, true); + const authorization = config_service.get_config().authorization; + authorization.should.be.empty(); + done(); }); }); From f021ecbfdfa94546b2e9f4e8dd9e1161766d3a24 Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Wed, 11 Aug 2021 09:59:26 +0200 Subject: [PATCH 12/33] Amend type check. --- lib/pdp/iShare.js | 76 ++++++++++++++++++++++++---------------- test/unit/ishare-test.js | 4 +-- 2 files changed, 48 insertions(+), 32 deletions(-) diff --git a/lib/pdp/iShare.js b/lib/pdp/iShare.js index f9b0844..e3d3506 100644 --- a/lib/pdp/iShare.js +++ b/lib/pdp/iShare.js @@ -78,40 +78,56 @@ exports.checkPolicies = function (token, data, ishare_policy) { }; function valid_payload(data, policy_sets) { - const result = policy_sets.some((policy_set) => { - return policy_set.policies.some((policy) => { - const ruleEffect = policy.rules[0].effect === 'Permit'; - let fireRule = true; - - if (policy.target.resource.type && data.payload_types) { - fireRule = data.payload_types.length === 1 && data.payload_types[0] === policy.target.resource.type; - } - if (fireRule && policy.target.actions) { - fireRule = policy.target.actions.some((action) => { - return action.toUpperCase() === data.action; - }); - } - - if (fireRule && policy.target.resource.identifiers && data.payload_entity_ids) { - data.payload_entity_ids.forEach((id) => { - fireRule = policy.target.resource.identifiers.some((identifier) => { - const regex = new RegExp(identifier, 'i'); - return regex.test(id); + // If no type found in the payload, set it to check for null + data.payload_types = data.payload_types || [null]; + + const result = data.payload_types.every((type) => { + return policy_sets.some((policy_set) => { + // Policies are permissive, at least one policy + // from the collection of policy sets must fire. + return policy_set.policies.some((policy) => { + const ruleEffect = policy.rules[0].effect === 'Permit'; + let fireRule = true; + + // The action of the request must be found in the + // array of actions found in the policy + if (policy.target.actions) { + fireRule = policy.target.actions.some((action) => { + return action.toUpperCase() === data.action; }); - }); - } - - if (fireRule && policy.target.resource.attributes && data.payload_entity_attrs) { - data.payload_entity_attrs.forEach((attr) => { - fireRule = policy.target.resource.attributes.some((attribute) => { - const regex = new RegExp(attribute, 'i'); - return regex.test(attr); + } + + // If a type is defined, the payload must have a type + // and that type must match directly + if (fireRule && policy.target.resource.type) { + fireRule = !!type && type === policy.target.resource.type; + } + + // If Ids are found in the policy, they must match a regex + if (fireRule && policy.target.resource.identifiers && data.payload_entity_ids) { + data.payload_entity_ids.every((id) => { + fireRule = policy.target.resource.identifiers.some((identifier) => { + const regex = new RegExp(identifier, 'i'); + return regex.test(id); + }); }); - }); - } - return fireRule ? ruleEffect : !ruleEffect; + } + + // If attributes are found in the policy, they must match a regex + if (fireRule && policy.target.resource.attributes && data.payload_entity_attrs) { + data.payload_entity_attrs.every((attr) => { + fireRule = policy.target.resource.attributes.some((attribute) => { + const regex = new RegExp(attribute, 'i'); + return regex.test(attr); + }); + }); + } + + return fireRule ? ruleEffect : !ruleEffect; + }); }); }); + console.error(result); return result; } diff --git a/test/unit/ishare-test.js b/test/unit/ishare-test.js index ce7e7f6..9546d0c 100644 --- a/test/unit/ishare-test.js +++ b/test/unit/ishare-test.js @@ -174,13 +174,13 @@ describe('Authorization: iSHARE PDP', function () { describe('When a restricted URL matches the JWT policy and is legitimate', function () { beforeEach(function () { contextBrokerMock = nock('http://fiware.org:1026') - .get('/path/entities/urn:ngsi-ld:SoilSensor:1111') + .get('/path/entities/urn:ngsi-ld:SoilSensor:1111?type=SoilSensor') .reply(200, {}); iShareMock = nock('http://ishare.com:8080').post('/delegate').reply(200, ishare_policy_recognized); }); it('should allow access', function (done) { - got.get('path/entities/urn:ngsi-ld:SoilSensor:1111', request_with_jwt).then((response) => { + got.get('path/entities/urn:ngsi-ld:SoilSensor:1111?type=SoilSensor', request_with_jwt).then((response) => { contextBrokerMock.done(); //iShareMock.done(); should.equal(response.statusCode, 200); From 33190c871f6d7f697eb4cac1bb947167993bd280 Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Thu, 12 Aug 2021 10:27:30 +0200 Subject: [PATCH 13/33] Tidying code base. - Fix signatures - Update Buffer Call - Expand test coverage - Add docs --- Dockerfile | 1 + config.js | 7 ++- controllers/root.js | 2 +- doc/admin_guide.md | 88 ++++++++++++++++------------ extras/docker/README.md | 10 +++- lib/config_service.js | 5 +- lib/pdp/authzforce.js | 2 +- lib/pdp/iShare.js | 1 - lib/pdp/openPolicyAgent.js | 7 +-- lib/pdp/xacml.js | 5 +- test/unit/authentication-test.js | 71 ++++++++++++++-------- test/unit/authzforce-pdp-test.js | 24 ++++---- test/unit/connection-test.js | 30 +++++----- test/unit/ishare-test.js | 34 +++++------ test/unit/jwt-authentication-test.js | 40 ++++++------- test/unit/keyrock-pdp-test.js | 36 ++++++------ test/unit/opa-pdp-test.js | 41 +++++++------ test/unit/start-up-test.js | 32 +++++----- test/unit/xacml-pdp-test.js | 33 ++++++----- 19 files changed, 258 insertions(+), 211 deletions(-) diff --git a/Dockerfile b/Dockerfile index 76dbf7b..babdce5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -87,6 +87,7 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=60s \ # PEP_PROXY_PDP_PROTOCOL # PEP_PROXY_PDP_HOST # PEP_PROXY_PDP_PORT +# PEP_PROXY_PDP_PATH # PEP_PROXY_TENANT_HEADER # PEP_PROXY_AZF_CUSTOM_POLICY # PEP_PROXY_PUBLIC_PATHS diff --git a/config.js b/config.js index 3cb5e03..11806e3 100644 --- a/config.js +++ b/config.js @@ -52,12 +52,15 @@ config.cache_time = 300; config.authorization = { enabled: false, - pdp: 'idm', // idm|iShare|xacml|authzforce + pdp: 'idm', // idm|iShare|xacml|authzforce|opa|azf header: undefined, // NGSILD-Tenant|fiware-service - azf: { + pdp: { protocol: 'http', host: 'localhost', port: 8080, + path: '' + } + azf: { custom_policy: undefined // use undefined to default policy checks (HTTP verb + path). } }; diff --git a/controllers/root.js b/controllers/root.js index 4560043..4cf31b2 100644 --- a/controllers/root.js +++ b/controllers/root.js @@ -108,7 +108,7 @@ function getTokens(req) { if (authToken === undefined && req.headers.authorization !== undefined) { const headerAuth = req.headers.authorization.split(' ')[1]; - authToken = new Buffer(headerAuth, 'base64').toString(); + authToken = Buffer.from(headerAuth, 'base64').toString(); } return { authToken, authOrgToken, pepToken }; diff --git a/doc/admin_guide.md b/doc/admin_guide.md index 0b987cb..aa54ce1 100644 --- a/doc/admin_guide.md +++ b/doc/admin_guide.md @@ -76,19 +76,25 @@ you have to first register an application. The steps can be found [here](https://fiware-idm.readthedocs.io/en/latest/user_and_programmers_guide/application_guide/index.html#register-pep-proxy-and-iot-agents). You can also configure Pep Proxy to validate authorization in your application -([levels 2 and 3 of authorization](user_guide.md#level-2-basic-authorization)). If enabled PEP checks permissions in multiple ways: -ways: +([levels 2 and 3 of authorization](user_guide.md#level-2-basic-authorization)). If enabled PEP checks permissions in +multiple ways: ways: - With [Keyrock Identity Manager](https://github.com/Fiware/catalogue/tree/master/security#keyrock): only allow basic authorization -- With [Keyrock Identity Manager](https://github.com/Fiware/catalogue/tree/master/security#keyrock): payload attribute level authorization requests in iShare format. -- With [Keyrock Identity Manager](https://github.com/Fiware/catalogue/tree/master/security#keyrock): payload attribute level authorization requests in [XACML 3.0 JSON](https://docs.oasis-open.org/xacml/xacml-json-http/v1.0/xacml-json-http-v1.0.html) format. -- With [Keyrock Identity Manager](https://github.com/Fiware/catalogue/tree/master/security#keyrock): payload attribute level authorization requests in [Open Policy Agent](https://www.openpolicyagent.org/) format. +- With [Keyrock Identity Manager](https://github.com/Fiware/catalogue/tree/master/security#keyrock): payload attribute + level authorization requests in iShare format. +- With [Keyrock Identity Manager](https://github.com/Fiware/catalogue/tree/master/security#keyrock): payload attribute + level authorization requests in + [XACML 3.0 JSON](https://docs.oasis-open.org/xacml/xacml-json-http/v1.0/xacml-json-http-v1.0.html) format. +- With [Keyrock Identity Manager](https://github.com/Fiware/catalogue/tree/master/security#keyrock): payload attribute + level authorization requests in [Open Policy Agent](https://www.openpolicyagent.org/) format. - With [Authzforce Authorization PDP](https://github.com/Fiware/catalogue/tree/master/security#authzforce): allow basic and advanced authorization. For advanced authorization, you can use custom policy checks by including programatic scripts in policies folder. An script template is included there. -The `config.authorization.header` can be passed to Keyrock IDM to reduce permissions to a single tenant and if used should correspond to the tenant header (`NGSILD-Tenant` or `fiware-service`) when authorizing a multi-tenant system such as FIWARE +The `config.authorization.header` can be passed to Keyrock IDM to reduce permissions to a single tenant and if used +should correspond to the tenant header (`NGSILD-Tenant` or `fiware-service`) when authorizing a multi-tenant system such +as FIWARE ```javascript config.authorization = { @@ -421,39 +427,43 @@ with container-based technologies, like Docker, Heroku, etc... The following table shows the accepted environment variables, as well as the configuration parameter the variable overrides. -| Environment variable | Configuration attribute | -| :------------------------------------ | :-------------------------------- | -| PEP_PROXY_PORT | `pep_port` | -| PEP_PROXY_HTTPS_ENABLED | `https` | -| PEP_PROXY_HTTPS_PORT | `https.port` | -| PEP_PROXY_IDM_HOST | `idm.host` | -| PEP_PROXY_IDM_PORT | `idm.port` | -| PEP_PROXY_IDM_SSL_ENABLED | `idm.ssl` | -| PEP_PROXY_APP_HOST | `app.host` | -| PEP_PROXY_APP_PORT | `app.port` | -| PEP_PROXY_APP_SSL_ENABLED | `app.ssl` | -| PEP_PROXY_ORG_ENABLED | `organizations.enabled` | -| PEP_PROXY_ORG_HEADER | `organizations.header` | -| PEP_PROXY_APP_ID | `pep.app_id` | -| PEP_PROXY_USERNAME | `pep.username` | -| PEP_PASSWORD | `pep.password` | -| PEP_TOKEN_SECRET | `pep.token` | -| PEP_TRUSTED_APPS | `pep.trusted_apps` | -| PEP_PROXY_AUTH_ENABLED | `authorization.enabled` | -| PEP_PROXY_PDP | `authorization.pdp` | -| PEP_PROXY_TENANT_HEADER | `authorization.header` | -| PEP_PROXY_AZF_PROTOCOL | `authorization.azf.protocol` | -| PEP_PROXY_AZF_HOST | `authorization.azf.host` | -| PEP_PROXY_AZF_PORT | `authorization.azf.port` | -| PEP_PROXY_AZF_CUSTOM_POLICY | `authorization.azf.custom_policy` | -| PEP_PROXY_PUBLIC_PATHS | `public_path` | -| PEP_PROXY_CORS_ORIGIN | `cors.origin` | -| PEP_PROXY_CORS_METHODS | `cors.methods` | -| PEP_PROXY_CORS_OPTIONS_SUCCESS_STATUS | `cors.optionsSuccessStatus` | -| PEP_PROXY_CORS_ALLOWED_HEADERS | `cors.allowedHeaders` | -| PEP_PROXY_CORS_CREDENTIALS | `cors.credentials` | -| PEP_PROXY_CORS_MAX_AGE | `cors.maxAge` | -| PEP_PROXY_AUTH_FOR_NGINX | `config.auth_for_nginx` | +| Environment variable | Configuration attribute | Notes | | +| :------------------------------------ | :-------------------------------- | ------------------------------------------- | --- | +| PEP_PROXY_PORT | `pep_port` | | +| PEP_PROXY_HTTPS_ENABLED | `https` | | +| PEP_PROXY_HTTPS_PORT | `https.port` | | +| PEP_PROXY_IDM_HOST | `idm.host` | | +| PEP_PROXY_IDM_PORT | `idm.port` | | +| PEP_PROXY_IDM_SSL_ENABLED | `idm.ssl` | | +| PEP_PROXY_APP_HOST | `app.host` | | +| PEP_PROXY_APP_PORT | `app.port` | | +| PEP_PROXY_APP_SSL_ENABLED | `app.ssl` | | +| PEP_PROXY_ORG_ENABLED | `organizations.enabled` | | +| PEP_PROXY_ORG_HEADER | `organizations.header` | | +| PEP_PROXY_APP_ID | `pep.app_id` | | +| PEP_PROXY_USERNAME | `pep.username` | | +| PEP_PASSWORD | `pep.password` | | +| PEP_TOKEN_SECRET | `pep.token` | | +| PEP_TRUSTED_APPS | `pep.trusted_apps` | | +| PEP_PROXY_AUTH_ENABLED | `authorization.enabled` | | +| PEP_PROXY_PDP | `authorization.pdp` | | +| PEP_PROXY_PDP_PROTOCOL | `authorization.pdp.protocol` | | +| PEP_PROXY_PDP_HOST | `authorization.pdp.host` | | +| PEP_PROXY_PDP_PORT | `authorization.pdp.port` | | +| PEP_PROXY_PDP_PATH | `authorization.pdp.path` | | +| PEP_PROXY_TENANT_HEADER | `authorization.header` | | +| PEP_PROXY_AZF_PROTOCOL | `authorization.azf.protocol` | **deprecated** use `PEP_PROXY_PDP_PROTOCOL` | +| PEP_PROXY_AZF_HOST | `authorization.azf.host` | **deprecated** use `PEP_PROXY_PDP_HOST` | +| PEP_PROXY_AZF_PORT | `authorization.azf.port` | **deprecated** use `PEP_PROXY_PDP_PORT` | +| PEP_PROXY_AZF_CUSTOM_POLICY | `authorization.azf.custom_policy` | | +| PEP_PROXY_PUBLIC_PATHS | `public_path` | | +| PEP_PROXY_CORS_ORIGIN | `cors.origin` | | +| PEP_PROXY_CORS_METHODS | `cors.methods` | | +| PEP_PROXY_CORS_OPTIONS_SUCCESS_STATUS | `cors.optionsSuccessStatus` | | +| PEP_PROXY_CORS_ALLOWED_HEADERS | `cors.allowedHeaders` | | +| PEP_PROXY_CORS_CREDENTIALS | `cors.credentials` | | +| PEP_PROXY_CORS_MAX_AGE | `cors.maxAge` | | +| PEP_PROXY_AUTH_FOR_NGINX | `config.auth_for_nginx` | | | PEP_PROXY_MAGIC_KEY | `config.magic_key` | Note: diff --git a/extras/docker/README.md b/extras/docker/README.md index 42424e9..4db1ec4 100644 --- a/extras/docker/README.md +++ b/extras/docker/README.md @@ -94,10 +94,14 @@ sudo docker run -d --name pep-proxy-container -v [host_config_file]:/opt/fiware- - `PEP_PASSWORD` - default value is left blank and must be overridden - `PEP_PROXY_AUTH_ENABLED` - default value is `false` - `PEP_PROXY_PDP` - default value is `idm` can be set to `authzforce`, `iShare` or `xacml` +- `PEP_PROXY_PDP_PROTOCOL` - default value is `http` +- `PEP_PROXY_PDP_HOST` - default value is `localhost` +- `PEP_PROXY_PDP_PORT` - default value is `8080` +- `PEP_PROXY_PDP_PATH` - default value is blank - `PEP_PROXY_TENANT_HEADER` - default value is left blank. Typically set to `NGSILD-Tenant` or `fiware-service`. -- `PEP_PROXY_AZF_PROTOCOL` - default value is `http` -- `PEP_PROXY_AZF_HOST` - default value is `localhost` -- `PEP_PROXY_AZF_PORT` - default value is `8080` +- `PEP_PROXY_AZF_PROTOCOL` - _deprecated_ use `PEP_PROXY_PDP_PROTOCOL` +- `PEP_PROXY_AZF_HOST` - _deprecated_ use `PEP_PROXY_PDP_HOST` +- `PEP_PROXY_AZF_PORT` - _deprecated_ use `PEP_PROXY_PDP_PORT` - `PEP_PROXY_AZF_CUSTOM_POLICY` - default value is `undefined` which impliesthe usage of default policy checks (HTTP verb + path). - `PEP_PROXY_PUBLIC_PATHS` - default value is `[]` - Use `,` to split paths - example: diff --git a/lib/config_service.js b/lib/config_service.js index c8fdd15..82c9d8b 100644 --- a/lib/config_service.js +++ b/lib/config_service.js @@ -16,7 +16,7 @@ const secrets = {}; if (fs.existsSync(SECRETS_DIR)) { const files = fs.readdirSync(SECRETS_DIR); // eslint-disable-next-line no-unused-vars - files.forEach(function (file, index) { + files.forEach((file, index) => { const fullPath = path.join(SECRETS_DIR, file); const key = file; try { @@ -212,6 +212,9 @@ function process_environment_variables(verbose) { if (process.env.PEP_PROXY_PDP_PORT) { config.authorization[pdp].port = process.env.PEP_PROXY_PDP_PORT; } + if (process.env.PEP_PROXY_PDP_PATH) { + config.authorization[pdp].path = process.env.PEP_PROXY_PDP_PATH; + } // Deprecated - use common PDP attributes if (process.env.PEP_PROXY_AZF_PROTOCOL) { diff --git a/lib/pdp/authzforce.js b/lib/pdp/authzforce.js index e4edbc7..cc36a88 100644 --- a/lib/pdp/authzforce.js +++ b/lib/pdp/authzforce.js @@ -91,7 +91,7 @@ exports.checkPolicies = function (authToken, data, req) { // But there does not seem to be any good npm packge supporting namespace-aware XPath or equivalent evaluation on JSON. // (xml2js-xpath will probably support namespaces in the next release: https://github.com/dsummersl/node-xml2js-xpath/issues/5 ) // The easy way to go (but with inconvenients) is to get rid of prefixes.One way to refixes is to use npm package 'xml2js' with stripPrefix option. - xml2js.parseString(response.body, { tagNameProcessors: [xml2js.processors.stripPrefix] }, function (err, json) { + xml2js.parseString(response.body, { tagNameProcessors: [xml2js.processors.stripPrefix] }, (err, json) => { if (err) { return reject(err); } diff --git a/lib/pdp/iShare.js b/lib/pdp/iShare.js index e3d3506..d231954 100644 --- a/lib/pdp/iShare.js +++ b/lib/pdp/iShare.js @@ -127,7 +127,6 @@ function valid_payload(data, policy_sets) { }); }); }); - console.error(result); return result; } diff --git a/lib/pdp/openPolicyAgent.js b/lib/pdp/openPolicyAgent.js index f0fe909..63faf99 100644 --- a/lib/pdp/openPolicyAgent.js +++ b/lib/pdp/openPolicyAgent.js @@ -20,7 +20,7 @@ exports.jwt_enabled = false; function getUrl() { const pdp = config_service.get_config().authorization.opa; - return (pdp.ssl ? 'https://' : 'http://') + pdp.host + ':' + pdp.port; + return (pdp.ssl ? 'https://' : 'http://') + pdp.host + ':' + pdp.port + '/' + pdp.path; } /** @@ -36,8 +36,7 @@ function getUrl() { exports.checkPolicies = function (token, data) { return new Promise((resolve, reject) => { return got - .post('query', { - prefixUrl: getUrl(), + .post(getUrl(), { headers: { 'X-Auth-Token': token }, @@ -46,7 +45,7 @@ exports.checkPolicies = function (token, data) { .json() .then((result) => { debug(result); - return resolve(result); + return resolve(result.allow); }) .catch((error) => { if (error instanceof got.HTTPError) { diff --git a/lib/pdp/xacml.js b/lib/pdp/xacml.js index 8bb73e7..b7888b8 100644 --- a/lib/pdp/xacml.js +++ b/lib/pdp/xacml.js @@ -20,7 +20,7 @@ exports.jwt_enabled = false; function getUrl() { const pdp = config_service.get_config().authorization.xacml; - return (pdp.ssl ? 'https://' : 'http://') + pdp.host + ':' + pdp.port; + return (pdp.ssl ? 'https://' : 'http://') + pdp.host + ':' + pdp.port + '/' + pdp.path } /** @@ -36,8 +36,7 @@ function getUrl() { exports.checkPolicies = function (token, data) { return new Promise((resolve, reject) => { return got - .post('xacml', { - prefixUrl: getUrl(), + .post(getUrl(), { headers: { 'X-Auth-Token': token }, diff --git a/test/unit/authentication-test.js b/test/unit/authentication-test.js index eb3cec0..1007e8c 100644 --- a/test/unit/authentication-test.js +++ b/test/unit/authentication-test.js @@ -21,6 +21,12 @@ const request_with_header = { headers: { 'x-auth-token': '111111111' } }; +const request_with_auth_header = { + prefixUrl: 'http:/localhost:80', + throwHttpErrors: false, + headers: { authorization: 'Bearer: '+ Buffer.from('111111111' , 'utf-8').toString('base64')} +}; + const request_with_magic_key = { prefixUrl: 'http:/localhost:80', throwHttpErrors: false, @@ -69,12 +75,12 @@ const keyrock_user_response = { displayName: 'Some User' }; -describe('Authentication: Keyrock IDM', function () { +describe('Authentication: Keyrock IDM', () => { let pep; let contextBrokerMock; let idmMock; - beforeEach(function (done) { + beforeEach((done) => { const app = require('../../app'); pep = app.start_server('12345', config); cache.flush(); @@ -82,16 +88,16 @@ describe('Authentication: Keyrock IDM', function () { done(); }); - afterEach(function (done) { + afterEach((done) => { pep.close(config.pep_port); done(); }); - describe('When a URL is requested and no token is present', function () { - beforeEach(function () { + describe('When a URL is requested and no token is present', () => { + beforeEach(() => { // Set Up }); - it('should deny access', function (done) { + it('should deny access', (done) => { got.get('restricted_path', request_no_header).then((response) => { should.equal(response.statusCode, 401); done(); @@ -99,11 +105,11 @@ describe('Authentication: Keyrock IDM', function () { }); }); - describe('When a public path is requested', function () { - beforeEach(function () { + describe('When a public path is requested', () => { + beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026').get('/public').reply(200, {}); }); - it('should allow access', function (done) { + it('should allow access', (done) => { got.get('public', request_no_header).then((response) => { contextBrokerMock.done(); should.equal(response.statusCode, 200); @@ -112,11 +118,11 @@ describe('Authentication: Keyrock IDM', function () { }); }); - describe('When a restricted path is requested and the token matches the magic key', function () { - beforeEach(function () { + describe('When a restricted path is requested and the token matches the magic key', () => { + beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); }); - it('should allow access', function (done) { + it('should allow access', (done) => { got.get('restricted', request_with_magic_key).then((response) => { contextBrokerMock.done(); should.equal(response.statusCode, 200); @@ -125,14 +131,14 @@ describe('Authentication: Keyrock IDM', function () { }); }); - describe('When a restricted path is requested for a legitimate user', function () { - beforeEach(function () { + describe('When a restricted path is requested for a legitimate user with an x-auth-token', () => { + beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id') .reply(200, keyrock_user_response); }); - it('should authenticate the user and allow access', function (done) { + it('should authenticate the user and allow access', (done) => { got.get('restricted', request_with_header).then((response) => { contextBrokerMock.done(); idmMock.done(); @@ -142,11 +148,28 @@ describe('Authentication: Keyrock IDM', function () { }); }); - describe('When a restricted path is requested for a forbidden user', function () { - beforeEach(function () { + describe('When a restricted path is requested for a legitimate user with a bearer token', () => { + beforeEach(() => { + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=111111111&app_id=application_id') + .reply(200, keyrock_user_response); + }); + it('should authenticate the user and allow access', (done) => { + got.get('restricted', request_with_auth_header).then((response) => { + contextBrokerMock.done(); + idmMock.done(); + should.equal(response.statusCode, 200); + done(); + }); + }); + }); + + describe('When a restricted path is requested for a forbidden user', () => { + beforeEach(() => { idmMock = nock('http://keyrock.com:3000').get('/user?access_token=111111111&app_id=application_id').reply(401); }); - it('should authenticate the user and deny access', function (done) { + it('should authenticate the user and deny access', (done) => { got.get('restricted', request_with_header).then((response) => { contextBrokerMock.done(); idmMock.done(); @@ -156,14 +179,14 @@ describe('Authentication: Keyrock IDM', function () { }); }); - describe('When a non-existant restricted path is requested', function () { - beforeEach(function () { + describe('When a non-existant restricted path is requested', () => { + beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(404); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id') .reply(200, keyrock_user_response); }); - it('should authenticate the user and proxy the error', function (done) { + it('should authenticate the user and proxy the error', (done) => { got.get('restricted', request_with_header).then((response) => { contextBrokerMock.done(); idmMock.done(); @@ -173,14 +196,14 @@ describe('Authentication: Keyrock IDM', function () { }); }); - describe('When the same restricted path is requested multiple times', function () { - beforeEach(function () { + describe('When the same restricted path is requested multiple times', () => { + beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').times(2).reply(200, {}); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id') .reply(200, keyrock_user_response); }); - it('should access the user from cache', function (done) { + it('should access the user from cache', (done) => { got .get('restricted', request_with_header) .then((firstResponse) => { diff --git a/test/unit/authzforce-pdp-test.js b/test/unit/authzforce-pdp-test.js index cd95449..111f818 100644 --- a/test/unit/authzforce-pdp-test.js +++ b/test/unit/authzforce-pdp-test.js @@ -94,13 +94,13 @@ const config = { } }; -describe('Authorization: Authzforce PDP', function () { +describe('Authorization: Authzforce PDP', () => { let pep; let contextBrokerMock; let idmMock; let authzforceMock; - beforeEach(function (done) { + beforeEach((done) => { const app = require('../../app'); pep = app.start_server('12345', config); cache.flush(); @@ -111,20 +111,20 @@ describe('Authorization: Authzforce PDP', function () { done(); }); - afterEach(function (done) { + afterEach((done) => { pep.close(config.pep_port); done(); }); - describe('When a restricted path is requested for a legitimate user', function () { - beforeEach(function () { + describe('When a restricted path is requested for a legitimate user', () => { + beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); authzforceMock = nock('http://authzforce.com:8080') .post('/authzforce-ce/domains/authzforce/pdp') .reply(200, authzforce_permit_response); }); - it('should allow access', function (done) { + it('should allow access', (done) => { got.get('restricted', request_with_header).then((response) => { contextBrokerMock.done(); idmMock.done(); @@ -135,14 +135,14 @@ describe('Authorization: Authzforce PDP', function () { }); }); - describe('When a restricted path is requested for a forbidden user', function () { - beforeEach(function () { + describe('When a restricted path is requested for a forbidden user', () => { + beforeEach(() => { authzforceMock = nock('http://authzforce.com:8080') .post('/authzforce-ce/domains/authzforce/pdp') .reply(200, authzforce_deny_response); }); - it('should deny access when denied', function (done) { + it('should deny access when denied', (done) => { got.get('restricted', request_with_header).then((response) => { idmMock.done(); authzforceMock.done(); @@ -152,8 +152,8 @@ describe('Authorization: Authzforce PDP', function () { }); }); - describe('When no AZF domain is returned', function () { - beforeEach(function () { + describe('When no AZF domain is returned', () => { + beforeEach(() => { nock.cleanAll(); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id&authzforce=true') @@ -162,7 +162,7 @@ describe('Authorization: Authzforce PDP', function () { trusted_apps: [] }); }); - it('should deny access', function (done) { + it('should deny access', (done) => { got.get('restricted', request_with_header).then((response) => { idmMock.done(); should.equal(response.statusCode, 401); diff --git a/test/unit/connection-test.js b/test/unit/connection-test.js index 0f5d715..82340a1 100644 --- a/test/unit/connection-test.js +++ b/test/unit/connection-test.js @@ -47,26 +47,26 @@ const config = { } }; -describe('Connection Tests', function () { +describe('Connection Tests', () => { let idmMock; let authzforceMock; - beforeEach(function (done) { + beforeEach((done) => { config_service.set_config(config, true); cache.flush(); nock.cleanAll(); done(); }); - afterEach(function (done) { + afterEach((done) => { done(); }); - describe('When connecting to Authzforce and it is present', function () { - beforeEach(function () { + describe('When connecting to Authzforce and it is present', () => { + beforeEach(() => { authzforceMock = nock('http://authzforce.com:8080').get('/').reply(200, {}); }); - it('should not error', function (done) { + it('should not error', (done) => { Authzforce.checkConnectivity() .then(() => { authzforceMock.done(); @@ -78,11 +78,11 @@ describe('Connection Tests', function () { }); }); - describe('When connecting to Keyrock and it is present', function () { - beforeEach(function () { + describe('When connecting to Keyrock and it is present', () => { + beforeEach(() => { idmMock = nock('http://keyrock.com:3000').get('/version').reply(200, {}); }); - it('should not error', function (done) { + it('should not error', (done) => { IDM.checkConnectivity() .then(() => { idmMock.done(); @@ -94,11 +94,11 @@ describe('Connection Tests', function () { }); }); - describe('When authenticating the PEP with Keyrock', function () { - beforeEach(function () { + describe('When authenticating the PEP with Keyrock', () => { + beforeEach(() => { idmMock = nock('http://keyrock.com:3000').post('/v3/auth/tokens').reply(200, {}); }); - it('should not error', function (done) { + it('should not error', (done) => { IDM.authenticatePEP() .then(() => { idmMock.done(); @@ -110,11 +110,11 @@ describe('Connection Tests', function () { }); }); - describe('When authenticating a misconfigured PEP with Keyrock', function () { - beforeEach(function () { + describe('When authenticating a misconfigured PEP with Keyrock', () => { + beforeEach(() => { idmMock = nock('http://keyrock.com:3000').post('/v3/auth/tokens').reply(401); }); - it('should error', function (done) { + it('should error', (done) => { IDM.authenticatePEP() .then(() => { should.fail('no error was thrown when it should have been'); diff --git a/test/unit/ishare-test.js b/test/unit/ishare-test.js index 9546d0c..6bd26e1 100644 --- a/test/unit/ishare-test.js +++ b/test/unit/ishare-test.js @@ -153,12 +153,12 @@ const config = { const ishare_policy_recognized = 'HELLO'; const ishare_policy_not_recognized = 'WORLD'; -describe('Authorization: iSHARE PDP', function () { +describe('Authorization: iSHARE PDP', () => { let pep; let contextBrokerMock; let iShareMock; - beforeEach(function (done) { + beforeEach((done) => { const app = require('../../app'); pep = app.start_server('12345', config); nock.cleanAll(); @@ -166,20 +166,20 @@ describe('Authorization: iSHARE PDP', function () { done(); }); - afterEach(function (done) { + afterEach((done) => { pep.close(config.pep_port); done(); }); - describe('When a restricted URL matches the JWT policy and is legitimate', function () { - beforeEach(function () { + describe('When a restricted URL matches the JWT policy and is legitimate', () => { + beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026') .get('/path/entities/urn:ngsi-ld:SoilSensor:1111?type=SoilSensor') .reply(200, {}); iShareMock = nock('http://ishare.com:8080').post('/delegate').reply(200, ishare_policy_recognized); }); - it('should allow access', function (done) { + it('should allow access', (done) => { got.get('path/entities/urn:ngsi-ld:SoilSensor:1111?type=SoilSensor', request_with_jwt).then((response) => { contextBrokerMock.done(); //iShareMock.done(); @@ -189,8 +189,8 @@ describe('Authorization: iSHARE PDP', function () { }); }); - describe('When a restricted URL does not match the attached JWT policy', function () { - it('should deny access', function (done) { + describe('When a restricted URL does not match the attached JWT policy', () => { + it('should deny access', (done) => { got.get('path/entities/urn:ngsi-ld:Tractor:1111?type=Tractor', request_with_jwt).then((response) => { //iShareMock.done(); should.equal(response.statusCode, 401); @@ -199,12 +199,12 @@ describe('Authorization: iSHARE PDP', function () { }); }); - xdescribe('When a JWT policy is not recognized by the iSHARE delegate', function () { - beforeEach(function () { + xdescribe('When a JWT policy is not recognized by the iSHARE delegate', () => { + beforeEach(() => { iShareMock = nock('http://ishare.com:8080').post('/delegate').reply(200, ishare_policy_not_recognized); }); - it('should deny access', function (done) { + it('should deny access', (done) => { got.get('path/entities/urn:ngsi-ld:SoilSensor:1111', request_with_jwt).then((response) => { //iShareMock.done(); should.equal(response.statusCode, 401); @@ -213,15 +213,15 @@ describe('Authorization: iSHARE PDP', function () { }); }); - describe('When a restricted URL with a string is requested', function () { - beforeEach(function () { + describe('When a restricted URL with a string is requested', () => { + beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026') .get('/path/entities/?ids=urn:ngsi-ld:SoilSensor:1111&type=SoilSensor') .reply(200, {}); iShareMock = nock('http://ishare.com:8080').post('/delegate').reply(200, ishare_policy_recognized); }); - it('should allow access based on the JWT policy and entities', function (done) { + it('should allow access based on the JWT policy and entities', (done) => { got.get('path/entities/?ids=urn:ngsi-ld:SoilSensor:1111&type=SoilSensor', request_with_jwt).then((response) => { contextBrokerMock.done(); //iShareMock.done(); @@ -231,13 +231,13 @@ describe('Authorization: iSHARE PDP', function () { }); }); - describe('When a restricted URL with a payload body is requested', function () { - beforeEach(function () { + describe('When a restricted URL with a payload body is requested', () => { + beforeEach(() => { iShareMock = nock('http://ishare.com:8080').post('/delegate').reply(200, ishare_policy_recognized); contextBrokerMock = nock('http://fiware.org:1026').patch('/path/entityOperations/upsert').reply(200, {}); }); - it('should allow access based on the JWT policy and entities', function (done) { + it('should allow access based on the JWT policy and entities', (done) => { got.patch('path/entityOperations/upsert', request_with_jwt_and_body).then((response) => { contextBrokerMock.done(); //iShareMock.done(); diff --git a/test/unit/jwt-authentication-test.js b/test/unit/jwt-authentication-test.js index 5ec7b38..3482cbd 100644 --- a/test/unit/jwt-authentication-test.js +++ b/test/unit/jwt-authentication-test.js @@ -108,12 +108,12 @@ const config = { } }; -describe('Authentication: JWT Token', function () { +describe('Authentication: JWT Token', () => { let pep; let contextBrokerMock; let idmMock; - beforeEach(function (done) { + beforeEach((done) => { const app = require('../../app'); pep = app.start_server('12345', config); nock.cleanAll(); @@ -121,14 +121,14 @@ describe('Authentication: JWT Token', function () { done(); }); - afterEach(function (done) { + afterEach((done) => { pep.close(config.pep_port); config.pep.secret = undefined; done(); }); - describe('When a URL is requested and no JWT token is present', function () { - it('should deny access', function (done) { + describe('When a URL is requested and no JWT token is present', () => { + it('should deny access', (done) => { got.get('restricted_path', request_no_jwt).then((response) => { should.equal(response.statusCode, 401); done(); @@ -136,11 +136,11 @@ describe('Authentication: JWT Token', function () { }); }); - describe('When a public path is requested', function () { - beforeEach(function () { + describe('When a public path is requested', () => { + beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026').get('/public').reply(200, {}); }); - it('should allow access', function (done) { + it('should allow access', (done) => { got.get('public', request_with_jwt).then((response) => { contextBrokerMock.done(); should.equal(response.statusCode, 200); @@ -149,11 +149,11 @@ describe('Authentication: JWT Token', function () { }); }); - describe('When a restricted path is requested with a legitimate JWT', function () { - beforeEach(function () { + describe('When a restricted path is requested with a legitimate JWT', () => { + beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); }); - it('should authenticate the user and allow access', function (done) { + it('should authenticate the user and allow access', (done) => { got.get('restricted', request_with_jwt).then((response) => { contextBrokerMock.done(); should.equal(response.statusCode, 200); @@ -162,11 +162,11 @@ describe('Authentication: JWT Token', function () { }); }); - describe('When a restricted path is requested with an expired JWT', function () { - beforeEach(async function () { + describe('When a restricted path is requested with an expired JWT', () => { + beforeEach(async () => { await sleep(100); }); - it('should deny access', function (done) { + it('should deny access', (done) => { got.get('restricted', request_with_expired_jwt).then((response) => { contextBrokerMock.done(); should.equal(response.statusCode, 401); @@ -175,13 +175,13 @@ describe('Authentication: JWT Token', function () { }); }); - describe('When a restricted path is requested for an unrecognized JWT', function () { - beforeEach(function () { + describe('When a restricted path is requested for an unrecognized JWT', () => { + beforeEach(() => { idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=' + invalid_token + '&app_id=application_id') .reply(401); }); - it('should fallback to Keyrock and deny access', function (done) { + it('should fallback to Keyrock and deny access', (done) => { got.get('restricted', request_with_invalid_jwt).then((response) => { should.equal(response.statusCode, 401); idmMock.done(); @@ -190,11 +190,11 @@ describe('Authentication: JWT Token', function () { }); }); - describe('When a non-existant restricted path is requested', function () { - beforeEach(function () { + describe('When a non-existant restricted path is requested', () => { + beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(404); }); - it('should authenticate the user and proxy the error', function (done) { + it('should authenticate the user and proxy the error', (done) => { got.get('restricted', request_with_jwt).then((response) => { contextBrokerMock.done(); should.equal(response.statusCode, 404); diff --git a/test/unit/keyrock-pdp-test.js b/test/unit/keyrock-pdp-test.js index 149371e..cfd6f07 100644 --- a/test/unit/keyrock-pdp-test.js +++ b/test/unit/keyrock-pdp-test.js @@ -62,12 +62,12 @@ const config = { } }; -describe('Authorization: Keyrock PDP', function () { +describe('Authorization: Keyrock PDP', () => { let pep; let contextBrokerMock; let idmMock; - beforeEach(function (done) { + beforeEach((done) => { nock.cleanAll(); const app = require('../../app'); pep = app.start_server('12345', config); @@ -75,19 +75,19 @@ describe('Authorization: Keyrock PDP', function () { done(); }); - afterEach(function (done) { + afterEach((done) => { pep.close(config.pep_port); done(); }); - describe('When a restricted path is requested for a legitimate user', function () { - beforeEach(function () { + describe('When a restricted path is requested for a legitimate user', () => { + beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') .reply(200, keyrock_permit_response); }); - it('should allow access', function (done) { + it('should allow access', (done) => { got.get('restricted', request_with_header).then((response) => { contextBrokerMock.done(); idmMock.done(); @@ -97,8 +97,8 @@ describe('Authorization: Keyrock PDP', function () { }); }); - describe('When a restricted path is requested and the app-id is not found', function () { - beforeEach(function () { + describe('When a restricted path is requested and the app-id is not found', () => { + beforeEach(() => { idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') .reply(200, { @@ -107,7 +107,7 @@ describe('Authorization: Keyrock PDP', function () { authorization_decision: 'Permit' }); }); - it('should deny access', function (done) { + it('should deny access', (done) => { got.get('restricted', request_with_header).then((response) => { idmMock.done(); should.equal(response.statusCode, 401); @@ -116,13 +116,13 @@ describe('Authorization: Keyrock PDP', function () { }); }); - describe('When a restricted path is requested for a forbidden user', function () { - beforeEach(function () { + describe('When a restricted path is requested for a forbidden user', () => { + beforeEach(() => { idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') .reply(200, keyrock_deny_response); }); - it('should deny access', function (done) { + it('should deny access', (done) => { got.get('restricted', request_with_header).then((response) => { idmMock.done(); should.equal(response.statusCode, 401); @@ -131,14 +131,14 @@ describe('Authorization: Keyrock PDP', function () { }); }); - describe('When the same action on a restricted path multiple times', function () { - beforeEach(function () { + describe('When the same action on a restricted path multiple times', () => { + beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').times(2).reply(200, {}); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') .reply(200, keyrock_permit_response); }); - it('should access the user action from cache', function (done) { + it('should access the user action from cache', (done) => { got .get('restricted', request_with_header) .then((firstResponse) => { @@ -154,8 +154,8 @@ describe('Authorization: Keyrock PDP', function () { }); }); - describe('When the same user request two different actions on a restricted path', function () { - beforeEach(function () { + describe('When the same user request two different actions on a restricted path', () => { + beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); contextBrokerMock.post('/restricted').reply(204); @@ -166,7 +166,7 @@ describe('Authorization: Keyrock PDP', function () { .get('/user?access_token=111111111&app_id=application_id&action=POST&resource=/restricted') .reply(200, keyrock_permit_response); }); - it('should not access the user from cache', function (done) { + it('should not access the user from cache', (done) => { got .get('restricted', request_with_header) .then((firstResponse) => { diff --git a/test/unit/opa-pdp-test.js b/test/unit/opa-pdp-test.js index d525c1e..77a4b3a 100644 --- a/test/unit/opa-pdp-test.js +++ b/test/unit/opa-pdp-test.js @@ -61,8 +61,12 @@ const request_with_headers_and_body = { json: ngsiPayload }; -const open_policy_agent_permit_response = 'true'; -const open_policy_agent_deny_response = 'false'; +const open_policy_agent_permit_response = { + allow: true +}; +const open_policy_agent_deny_response = { + allow: false +}; const config = { pep_port: 80, @@ -92,18 +96,19 @@ const config = { opa: { protocol: 'http', host: 'openpolicyagent.com', - port: 8080 + port: 8080, + path: 'query' } } }; -describe('Authorization: Open Policy Agent PDP', function () { +describe('Authorization: Open Policy Agent PDP', () => { let pep; let contextBrokerMock; let idmMock; let openPolicyAgentMock; - beforeEach(function (done) { + beforeEach((done) => { const app = require('../../app'); pep = app.start_server('12345', config); cache.flush(); @@ -114,20 +119,20 @@ describe('Authorization: Open Policy Agent PDP', function () { done(); }); - afterEach(function (done) { + afterEach((done) => { pep.close(config.pep_port); done(); }); - describe('When a restricted URL is requested by a legitimate user', function () { - beforeEach(function () { + describe('When a restricted URL is requested by a legitimate user', () => { + beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026').get('/path/entities/urn:ngsi-ld:entity:1111').reply(200, {}); openPolicyAgentMock = nock('http://openpolicyagent.com:8080') .post('/query') .reply(200, open_policy_agent_permit_response); }); - it('should allow access', function (done) { + it('should allow access', (done) => { got.get('path/entities/urn:ngsi-ld:entity:1111', request_with_headers).then((response) => { contextBrokerMock.done(); idmMock.done(); @@ -138,14 +143,14 @@ describe('Authorization: Open Policy Agent PDP', function () { }); }); - describe('When a restricted URL is requested by a forbidden user', function () { - beforeEach(function () { + describe('When a restricted URL is requested by a forbidden user', () => { + beforeEach(() => { openPolicyAgentMock = nock('http://openpolicyagent.com:8080') .post('/query') .reply(200, open_policy_agent_deny_response); }); - it('should deny access', function (done) { + it('should deny access', (done) => { got.get('path/entities/urn:ngsi-ld:entity:1111', request_with_headers).then((response) => { idmMock.done(); openPolicyAgentMock.done(); @@ -155,8 +160,8 @@ describe('Authorization: Open Policy Agent PDP', function () { }); }); - describe('When a restricted URL with a query string is requested', function () { - beforeEach(function () { + describe('When a restricted URL with a query string is requested', () => { + beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026') .get('/path/entities/?ids=urn:ngsi-ld:entity:1111&type=entity') .reply(200, {}); @@ -165,7 +170,7 @@ describe('Authorization: Open Policy Agent PDP', function () { .reply(200, open_policy_agent_permit_response); }); - it('should allow access based on entities', function (done) { + it('should allow access based on entities', (done) => { got.get('path/entities/?ids=urn:ngsi-ld:entity:1111&type=entity', request_with_headers).then((response) => { contextBrokerMock.done(); idmMock.done(); @@ -176,15 +181,15 @@ describe('Authorization: Open Policy Agent PDP', function () { }); }); - describe('When a restricted URL with a payload body is requested', function () { - beforeEach(function () { + describe('When a restricted URL with a payload body is requested', () => { + beforeEach(() => { openPolicyAgentMock = nock('http://openpolicyagent.com:8080') .post('/query') .reply(200, open_policy_agent_permit_response); contextBrokerMock = nock('http://fiware.org:1026').patch('/path/entityOperations/upsert').reply(200, {}); }); - it('should allow access based on entities', function (done) { + it('should allow access based on entities', (done) => { got.patch('path/entityOperations/upsert', request_with_headers_and_body).then((response) => { contextBrokerMock.done(); idmMock.done(); diff --git a/test/unit/start-up-test.js b/test/unit/start-up-test.js index 2ab74f1..a8f40ad 100644 --- a/test/unit/start-up-test.js +++ b/test/unit/start-up-test.js @@ -28,8 +28,8 @@ const config = { } }; -describe('When the PEP Proxy is started with environment variables', function () { - beforeEach(function () { +describe('When the PEP Proxy is started with environment variables', () => { + beforeEach(() => { process.env.PEP_PROXY_PORT = 8080; process.env.PEP_PROXY_HTTPS_ENABLED = 'true'; process.env.PEP_PROXY_HTTPS_PORT = 443; @@ -53,7 +53,7 @@ describe('When the PEP Proxy is started with environment variables', function () process.env.PEP_PROXY_DEBUG = 'PEP-Proxy:*'; }); - afterEach(function () { + afterEach(() => { delete process.env.IOTA_CB_HOST; delete process.env.PEP_PROXY_PORT; delete process.env.PEP_PROXY_HTTPS_ENABLED; @@ -78,7 +78,7 @@ describe('When the PEP Proxy is started with environment variables', function () delete process.env.PEP_PROXY_DEBUG; }); - it('should amend the configuration', function (done) { + it('should amend the configuration', (done) => { config_service.set_config(config, true); const pep_config = config_service.get_config(); @@ -102,8 +102,8 @@ describe('When the PEP Proxy is started with environment variables', function () }); }); -describe('When any PDP is configured with environment variables', function () { - beforeEach(function () { +describe('When any PDP is configured with environment variables', () => { + beforeEach(() => { process.env.PEP_PROXY_AUTH_ENABLED = 'true'; process.env.PEP_PROXY_PDP = 'opa'; process.env.PEP_PROXY_PDP_PROTOCOL = 'https'; @@ -111,14 +111,14 @@ describe('When any PDP is configured with environment variables', function () { process.env.PEP_PROXY_PDP_PORT = 443; }); - afterEach(function () { + afterEach(() => { delete process.env.PEP_PROXY_PDP; delete process.env.PEP_PROXY_PDP_PROTOCOL; delete process.env.PEP_PROXY_PDP_HOST; delete process.env.PEP_PROXY_PDP_PORT; }); - it('should amend the PDP configuration', function (done) { + it('should amend the PDP configuration', (done) => { config_service.set_config(config, true); const authorization = config_service.get_config().authorization; const pdp = config_service.get_config().authorization.opa; @@ -132,22 +132,22 @@ describe('When any PDP is configured with environment variables', function () { }); }); -describe('When the Authzforce PDP is started with environment variables', function () { - beforeEach(function () { +describe('When the Authzforce PDP is started with environment variables', () => { + beforeEach(() => { process.env.PEP_PROXY_AZF_PROTOCOL = 'http'; process.env.PEP_PROXY_AZF_HOST = 'authzforce.com'; process.env.PEP_PROXY_AZF_PORT = 9090; process.env.PEP_PROXY_AZF_CUSTOM_POLICY = 'policy'; }); - afterEach(function () { + afterEach(() => { delete process.env.PEP_PROXY_AZF_PROTOCOL; delete process.env.PEP_PROXY_AZF_HOST; delete process.env.PEP_PROXY_AZF_PORT; delete process.env.PEP_PROXY_AZF_CUSTOM_POLICY; }); - it('should amend the PDP configuration', function (done) { + it('should amend the PDP configuration', (done) => { config_service.set_config(config, true); const azf = config_service.get_config().authorization.azf; @@ -159,15 +159,15 @@ describe('When the Authzforce PDP is started with environment variables', functi }); }); -describe('When authorization is disabled with environment variables', function () { - beforeEach(function () { +describe('When authorization is disabled with environment variables', () => { + beforeEach(() => { process.env.PEP_PROXY_AUTH_ENABLED = 'false'; }); - afterEach(function () { + afterEach(() => { delete process.env.PEP_PROXY_AUTH_ENABLED; }); - it('should remove the authorization config', function (done) { + it('should remove the authorization config', (done) => { config_service.set_config(config, true); const authorization = config_service.get_config().authorization; authorization.should.be.empty(); diff --git a/test/unit/xacml-pdp-test.js b/test/unit/xacml-pdp-test.js index 698286a..c815f1b 100644 --- a/test/unit/xacml-pdp-test.js +++ b/test/unit/xacml-pdp-test.js @@ -117,18 +117,19 @@ const config = { xacml: { protocol: 'http', host: 'xacml.com', - port: 8080 + port: 8080, + path: 'xacml' } } }; -describe('Authorization: XACML PDP', function () { +describe('Authorization: XACML PDP', () => { let pep; let contextBrokerMock; let idmMock; let xacmlMock; - beforeEach(function (done) { + beforeEach((done) => { const app = require('../../app'); pep = app.start_server('12345', config); cache.flush(); @@ -139,18 +140,18 @@ describe('Authorization: XACML PDP', function () { done(); }); - afterEach(function (done) { + afterEach((done) => { pep.close(config.pep_port); done(); }); - describe('When a restricted URL is requested by a legitimate user', function () { - beforeEach(function () { + describe('When a restricted URL is requested by a legitimate user', () => { + beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026').get('/path/entities/urn:ngsi-ld:entity:1111').reply(200, {}); xacmlMock = nock('http://xacml.com:8080').post('/xacml').reply(200, xacml_permit_response); }); - it('should allow access', function (done) { + it('should allow access', (done) => { got.get('path/entities/urn:ngsi-ld:entity:1111', request_with_headers).then((response) => { contextBrokerMock.done(); idmMock.done(); @@ -161,12 +162,12 @@ describe('Authorization: XACML PDP', function () { }); }); - describe('When a restricted URL is requested by a forbidden user', function () { - beforeEach(function () { + describe('When a restricted URL is requested by a forbidden user', () => { + beforeEach(() => { xacmlMock = nock('http://xacml.com:8080').post('/xacml').reply(200, xacml_deny_response); }); - it('should deny access', function (done) { + it('should deny access', (done) => { got.get('path/entities/urn:ngsi-ld:entity:1111', request_with_headers).then((response) => { idmMock.done(); xacmlMock.done(); @@ -176,15 +177,15 @@ describe('Authorization: XACML PDP', function () { }); }); - describe('When a restricted URL with a query string is requested', function () { - beforeEach(function () { + describe('When a restricted URL with a query string is requested', () => { + beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026') .get('/path/entities/?ids=urn:ngsi-ld:entity:1111&type=entity') .reply(200, {}); xacmlMock = nock('http://xacml.com:8080').post('/xacml').reply(200, xacml_permit_response); }); - it('should allow access based on entities', function (done) { + it('should allow access based on entities', (done) => { got.get('path/entities/?ids=urn:ngsi-ld:entity:1111&type=entity', request_with_headers).then((response) => { contextBrokerMock.done(); idmMock.done(); @@ -195,13 +196,13 @@ describe('Authorization: XACML PDP', function () { }); }); - describe('When a restricted URL with a payload body is requested', function () { - beforeEach(function () { + describe('When a restricted URL with a payload body is requested', () => { + beforeEach(() => { xacmlMock = nock('http://xacml.com:8080').post('/xacml').reply(200, xacml_permit_response); contextBrokerMock = nock('http://fiware.org:1026').patch('/path/entityOperations/upsert').reply(200, {}); }); - it('should allow access based on entities', function (done) { + it('should allow access based on entities', (done) => { got.patch('path/entityOperations/upsert', request_with_headers_and_body).then((response) => { contextBrokerMock.done(); idmMock.done(); From d9bdade0e0aa89a43794b9a426691d27486e2b89 Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Thu, 12 Aug 2021 15:45:52 +0200 Subject: [PATCH 14/33] Add path --- config.js | 4 ++-- config.js.template | 5 ++++- lib/config_service.js | 1 + lib/pdp/openPolicyAgent.js | 2 +- lib/pdp/xacml.js | 2 +- test/unit/authentication-test.js | 2 +- test/unit/opa-pdp-test.js | 2 +- test/unit/xacml-pdp-test.js | 2 +- 8 files changed, 12 insertions(+), 8 deletions(-) diff --git a/config.js b/config.js index 11806e3..f6ee2b2 100644 --- a/config.js +++ b/config.js @@ -58,8 +58,8 @@ config.authorization = { protocol: 'http', host: 'localhost', port: 8080, - path: '' - } + path: '' + }, azf: { custom_policy: undefined // use undefined to default policy checks (HTTP verb + path). } diff --git a/config.js.template b/config.js.template index b9f711a..5684fda 100644 --- a/config.js.template +++ b/config.js.template @@ -55,10 +55,13 @@ config.authorization = { enabled: false, pdp: 'idm', // idm|iShare|xacml|authzforce header: undefined, // NGSILD-Tenant|fiware-service - azf: { + pdp: { protocol: 'http', host: 'localhost', port: 8080, + path: '' + }, + azf: { custom_policy: undefined, // use undefined to default policy checks (HTTP verb + path). }, }; diff --git a/lib/config_service.js b/lib/config_service.js index 82c9d8b..3eeb54a 100644 --- a/lib/config_service.js +++ b/lib/config_service.js @@ -72,6 +72,7 @@ function process_environment_variables(verbose) { 'PEP_PROXY_PDP_PROTOCOL', 'PEP_PROXY_PDP_HOST', 'PEP_PROXY_PDP_PORT', + 'PEP_PROXY_PDP_PATH', 'PEP_PROXY_TENANT_HEADER', 'PEP_PROXY_AZF_PROTOCOL', 'PEP_PROXY_AZF_HOST', diff --git a/lib/pdp/openPolicyAgent.js b/lib/pdp/openPolicyAgent.js index 63faf99..5cf113c 100644 --- a/lib/pdp/openPolicyAgent.js +++ b/lib/pdp/openPolicyAgent.js @@ -20,7 +20,7 @@ exports.jwt_enabled = false; function getUrl() { const pdp = config_service.get_config().authorization.opa; - return (pdp.ssl ? 'https://' : 'http://') + pdp.host + ':' + pdp.port + '/' + pdp.path; + return (pdp.ssl ? 'https://' : 'http://') + pdp.host + ':' + pdp.port + pdp.path; } /** diff --git a/lib/pdp/xacml.js b/lib/pdp/xacml.js index b7888b8..926770f 100644 --- a/lib/pdp/xacml.js +++ b/lib/pdp/xacml.js @@ -20,7 +20,7 @@ exports.jwt_enabled = false; function getUrl() { const pdp = config_service.get_config().authorization.xacml; - return (pdp.ssl ? 'https://' : 'http://') + pdp.host + ':' + pdp.port + '/' + pdp.path + return (pdp.ssl ? 'https://' : 'http://') + pdp.host + ':' + pdp.port + pdp.path; } /** diff --git a/test/unit/authentication-test.js b/test/unit/authentication-test.js index 1007e8c..b0b140c 100644 --- a/test/unit/authentication-test.js +++ b/test/unit/authentication-test.js @@ -24,7 +24,7 @@ const request_with_header = { const request_with_auth_header = { prefixUrl: 'http:/localhost:80', throwHttpErrors: false, - headers: { authorization: 'Bearer: '+ Buffer.from('111111111' , 'utf-8').toString('base64')} + headers: { authorization: 'Bearer: ' + Buffer.from('111111111', 'utf-8').toString('base64') } }; const request_with_magic_key = { diff --git a/test/unit/opa-pdp-test.js b/test/unit/opa-pdp-test.js index 77a4b3a..ed3ae8e 100644 --- a/test/unit/opa-pdp-test.js +++ b/test/unit/opa-pdp-test.js @@ -97,7 +97,7 @@ const config = { protocol: 'http', host: 'openpolicyagent.com', port: 8080, - path: 'query' + path: '/query' } } }; diff --git a/test/unit/xacml-pdp-test.js b/test/unit/xacml-pdp-test.js index c815f1b..c3859f0 100644 --- a/test/unit/xacml-pdp-test.js +++ b/test/unit/xacml-pdp-test.js @@ -118,7 +118,7 @@ const config = { protocol: 'http', host: 'xacml.com', port: 8080, - path: 'xacml' + path: '/xacml' } } }; From d4b99b2d2a4065dd6acf7adfd55b09dd1af64fbf Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Thu, 12 Aug 2021 17:29:11 +0200 Subject: [PATCH 15/33] Remove authorizationRegistry stub. --- config.js | 7 +- lib/authorization_functions.js | 17 ++--- lib/pdp/iShare.js | 126 +++++++-------------------------- test/unit/ishare-test.js | 14 +--- 4 files changed, 38 insertions(+), 126 deletions(-) diff --git a/config.js b/config.js index f6ee2b2..1e34ec1 100644 --- a/config.js +++ b/config.js @@ -54,13 +54,12 @@ config.authorization = { enabled: false, pdp: 'idm', // idm|iShare|xacml|authzforce|opa|azf header: undefined, // NGSILD-Tenant|fiware-service - pdp: { + azf: { + // iShare|xacml|authzforce|opa|azf protocol: 'http', host: 'localhost', port: 8080, - path: '' - }, - azf: { + path: '', custom_policy: undefined // use undefined to default policy checks (HTTP verb + path). } }; diff --git a/lib/authorization_functions.js b/lib/authorization_functions.js index 0d3ac28..82b3f25 100644 --- a/lib/authorization_functions.js +++ b/lib/authorization_functions.js @@ -53,7 +53,8 @@ function getData(req, res) { } function idmAuthorize(req, res) { - access.adjudicate(req, res, IDM.checkPolicies(req.user)); + const decision = IDM.checkPolicies(req.user); + access.adjudicate(req, res, decision); } function xacmlAuthorize(req, res) { // Check decision through XACML Endpoint @@ -83,13 +84,13 @@ function iShareAuthorize(req, res) { // Check decision through iShare Endpoint const user = req.user || {}; const authToken = req.app.get('pepToken'); - return ISHARE.checkPolicies(authToken, getData(req, res), user.delegationEvidence) - .then((decision) => { - access.adjudicate(req, res, decision); - }) - .catch((e) => { - access.internalError(res, e, 'iSHARE'); - }); + const decision = ISHARE.checkPolicies( + authToken, + getData(req, res), + user.delegationEvidence, + user.authorzationRegistry + ); + access.adjudicate(req, res, decision); } function authzforceAuthorize(req, res, authToken) { // Check decision through authzforce diff --git a/lib/pdp/iShare.js b/lib/pdp/iShare.js index d231954..1ffe4c7 100644 --- a/lib/pdp/iShare.js +++ b/lib/pdp/iShare.js @@ -5,14 +5,7 @@ * */ -const config_service = require('../config_service'); const debug = require('debug')('pep-proxy:iShare-Client'); -//const got = require('got'); - -function getUrl() { - const pdp = config_service.get_config().authorization.ishare; - return (pdp.ssl ? 'https://' : 'http://') + pdp.host + ':' + pdp.port; -} /** * Can the iSHARE PDP check payloads? @@ -24,7 +17,7 @@ exports.payload_enabled = true; exports.jwt_enabled = true; /** - * Make request to the iSHARE delegate endpoint and interpret the result + * Apply the attached policy to the payload & interpret the result * * @param req - the incoming request * @param data - A bag of data holding the action, resources, payload etc. @@ -32,49 +25,27 @@ exports.jwt_enabled = true; * * @return permit/deny */ -exports.checkPolicies = function (token, data, ishare_policy) { - return new Promise((resolve, reject) => { - debug('Checking policy'); - - const unix_timestamp = Math.floor(new Date().getTime() / 1000); - if (!ishare_policy || !ishare_policy.policySets) { - debug('No iSHARE Policy found'); - return resolve(false); - } else if (ishare_policy.notBefore > unix_timestamp) { - debug('Attached iSHARE Policy not yet valid'); - return resolve(false); - } else if (ishare_policy.notOnOrAfter <= unix_timestamp) { - debug('Attached iSHARE Policy expired'); - return resolve(false); - } else if (!valid_payload(data, ishare_policy.policySets)) { - debug('Attached iSHARE Policy disallows the request'); - return resolve(false); - } - - return resolve(true); - /* - - return got - .post('delegate', { - prefixUrl: getUrl(), - headers: { - 'X-Auth-Token': token - }, - json: iShareBody(data) - }) - .json() - .then((result) => { - return resolve(result.Response[0].Decision === 'Permit'); - }) - .catch((error) => { - if (error instanceof got.HTTPError) { - return resolve(false); - } - debug('Error in iShare communication ', error); - return reject(error); - });*/ - }); +/* eslint-disable-next-line no-unused-vars */ +exports.checkPolicies = function (token, data, ishare_policy, ishare_authregistry) { + debug('Checking policy'); + + const unix_timestamp = Math.floor(new Date().getTime() / 1000); + if (!ishare_policy || !ishare_policy.policySets) { + debug('No iSHARE Policy found'); + // TODO use the auth registry to retrieve a policy. + return false; + } else if (ishare_policy.notBefore > unix_timestamp) { + debug('Attached iSHARE Policy not yet valid'); + return false; + } else if (ishare_policy.notOnOrAfter <= unix_timestamp) { + debug('Attached iSHARE Policy expired'); + return false; + } else if (!valid_payload(data, ishare_policy.policySets)) { + debug('Attached iSHARE Policy disallows the request'); + return false; + } + return true; }; function valid_payload(data, policy_sets) { @@ -105,8 +76,8 @@ function valid_payload(data, policy_sets) { // If Ids are found in the policy, they must match a regex if (fireRule && policy.target.resource.identifiers && data.payload_entity_ids) { - data.payload_entity_ids.every((id) => { - fireRule = policy.target.resource.identifiers.some((identifier) => { + fireRule = data.payload_entity_ids.every((id) => { + return policy.target.resource.identifiers.some((identifier) => { const regex = new RegExp(identifier, 'i'); return regex.test(id); }); @@ -115,8 +86,8 @@ function valid_payload(data, policy_sets) { // If attributes are found in the policy, they must match a regex if (fireRule && policy.target.resource.attributes && data.payload_entity_attrs) { - data.payload_entity_attrs.every((attr) => { - fireRule = policy.target.resource.attributes.some((attribute) => { + fireRule = data.payload_entity_attrs.every((attr) => { + return policy.target.resource.attributes.some((attribute) => { const regex = new RegExp(attribute, 'i'); return regex.test(attr); }); @@ -129,50 +100,3 @@ function valid_payload(data, policy_sets) { }); return result; } - -/** - * Create a payload for making an iSHARE request - * based on the action,resource,tenant and attributes - */ -function iShareBody(data) { - const action = data.action; - const resource = data.resource; - const roles = data.roles; - const appId = data.appId; - debug('Checking authorization to roles', roles, 'to do ', action, ' on ', resource, 'and app ', appId); - - const json = { - delegationRequest: { - policyIssuer: 'EU.EORI.NL000000005', - target: { - accessSubject: 'EU.EORI.NL000000001' - }, - policySets: [ - { - policies: [ - { - target: { - resource: { - type: data.types, - identifiers: data.resource, - attributes: data.attrs - }, - actions: [action], - environment: { - serviceProviders: ['EU.EORI.NL000000003'] - } - }, - rules: [ - { - effect: 'Permit' - } - ] - } - ] - } - ] - } - }; - debug('JSON: ', JSON.stringify(json)); - return json; -} diff --git a/test/unit/ishare-test.js b/test/unit/ishare-test.js index 6bd26e1..f3c0c08 100644 --- a/test/unit/ishare-test.js +++ b/test/unit/ishare-test.js @@ -150,13 +150,9 @@ const config = { } }; -const ishare_policy_recognized = 'HELLO'; -const ishare_policy_not_recognized = 'WORLD'; - describe('Authorization: iSHARE PDP', () => { let pep; let contextBrokerMock; - let iShareMock; beforeEach((done) => { const app = require('../../app'); @@ -176,13 +172,11 @@ describe('Authorization: iSHARE PDP', () => { contextBrokerMock = nock('http://fiware.org:1026') .get('/path/entities/urn:ngsi-ld:SoilSensor:1111?type=SoilSensor') .reply(200, {}); - iShareMock = nock('http://ishare.com:8080').post('/delegate').reply(200, ishare_policy_recognized); }); it('should allow access', (done) => { got.get('path/entities/urn:ngsi-ld:SoilSensor:1111?type=SoilSensor', request_with_jwt).then((response) => { contextBrokerMock.done(); - //iShareMock.done(); should.equal(response.statusCode, 200); done(); }); @@ -192,7 +186,6 @@ describe('Authorization: iSHARE PDP', () => { describe('When a restricted URL does not match the attached JWT policy', () => { it('should deny access', (done) => { got.get('path/entities/urn:ngsi-ld:Tractor:1111?type=Tractor', request_with_jwt).then((response) => { - //iShareMock.done(); should.equal(response.statusCode, 401); done(); }); @@ -201,12 +194,11 @@ describe('Authorization: iSHARE PDP', () => { xdescribe('When a JWT policy is not recognized by the iSHARE delegate', () => { beforeEach(() => { - iShareMock = nock('http://ishare.com:8080').post('/delegate').reply(200, ishare_policy_not_recognized); + //iShareMock = nock('http://ishare.com:8080').post('/delegate').reply(200, ishare_policy_not_recognized); }); it('should deny access', (done) => { got.get('path/entities/urn:ngsi-ld:SoilSensor:1111', request_with_jwt).then((response) => { - //iShareMock.done(); should.equal(response.statusCode, 401); done(); }); @@ -218,13 +210,11 @@ describe('Authorization: iSHARE PDP', () => { contextBrokerMock = nock('http://fiware.org:1026') .get('/path/entities/?ids=urn:ngsi-ld:SoilSensor:1111&type=SoilSensor') .reply(200, {}); - iShareMock = nock('http://ishare.com:8080').post('/delegate').reply(200, ishare_policy_recognized); }); it('should allow access based on the JWT policy and entities', (done) => { got.get('path/entities/?ids=urn:ngsi-ld:SoilSensor:1111&type=SoilSensor', request_with_jwt).then((response) => { contextBrokerMock.done(); - //iShareMock.done(); should.equal(response.statusCode, 200); done(); }); @@ -233,14 +223,12 @@ describe('Authorization: iSHARE PDP', () => { describe('When a restricted URL with a payload body is requested', () => { beforeEach(() => { - iShareMock = nock('http://ishare.com:8080').post('/delegate').reply(200, ishare_policy_recognized); contextBrokerMock = nock('http://fiware.org:1026').patch('/path/entityOperations/upsert').reply(200, {}); }); it('should allow access based on the JWT policy and entities', (done) => { got.patch('path/entityOperations/upsert', request_with_jwt_and_body).then((response) => { contextBrokerMock.done(); - //iShareMock.done(); should.equal(response.statusCode, 200); done(); }); From 03e6c09d49ff3d2590ea8778a643a881e3f593b6 Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Tue, 17 Aug 2021 10:21:02 +0200 Subject: [PATCH 16/33] Fix text duplication --- doc/admin_guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/admin_guide.md b/doc/admin_guide.md index aa54ce1..4230990 100644 --- a/doc/admin_guide.md +++ b/doc/admin_guide.md @@ -77,7 +77,7 @@ you have to first register an application. The steps can be found You can also configure Pep Proxy to validate authorization in your application ([levels 2 and 3 of authorization](user_guide.md#level-2-basic-authorization)). If enabled PEP checks permissions in -multiple ways: ways: +multiple ways: - With [Keyrock Identity Manager](https://github.com/Fiware/catalogue/tree/master/security#keyrock): only allow basic authorization From 0046c0930ad234f4419105a8c0c9ecabc743a754 Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Tue, 17 Aug 2021 10:24:16 +0200 Subject: [PATCH 17/33] Update to minimum node 12. --- .github/workflows/ci.yml | 1 - extras/docker/Dockerfile | 2 +- package.json | 3 +++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 279e003..bc966fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,7 +57,6 @@ jobs: strategy: matrix: node-version: - - 10.x - 12.x - 14.x steps: diff --git a/extras/docker/Dockerfile b/extras/docker/Dockerfile index 429c7b8..c2f25f8 100644 --- a/extras/docker/Dockerfile +++ b/extras/docker/Dockerfile @@ -1,4 +1,4 @@ -ARG NODE_VERSION=10 +ARG NODE_VERSION=12 ARG GITHUB_ACCOUNT=ging ARG GITHUB_REPOSITORY=fiware-pep-proxy ARG DOWNLOAD=latest diff --git a/package.json b/package.json index 3006814..f3828f9 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,9 @@ "textlint-rule-terminology": "^2.1.4", "textlint-rule-write-good": "^1.6.2" }, + "engines": { + "node": ">=12" + }, "repository": { "type": "git", "url": "https://github.com/ging/fiware-pep-proxy" From bf1e839fbfa5dfce1c45ff41b21bd2ae3ea9ea84 Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Tue, 17 Aug 2021 10:31:53 +0200 Subject: [PATCH 18/33] Remove package lock --- package-lock.json | 7552 --------------------------------------------- 1 file changed, 7552 deletions(-) delete mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 78e5b12..0000000 --- a/package-lock.json +++ /dev/null @@ -1,7552 +0,0 @@ -{ - "name": "fiware-pep-proxy", - "version": "8.1.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@azu/format-text": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@azu/format-text/-/format-text-1.0.1.tgz", - "integrity": "sha1-aWc1CpRkD2sChVFpvYl85U1s6+I=", - "dev": true - }, - "@azu/style-format": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@azu/style-format/-/style-format-1.0.0.tgz", - "integrity": "sha1-5wGH+Khi4ZGxvObAJo8TrNOlayA=", - "dev": true, - "requires": { - "@azu/format-text": "^1.0.1" - } - }, - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/core": { - "version": "7.11.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.6.tgz", - "integrity": "sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.6", - "@babel/helper-module-transforms": "^7.11.0", - "@babel/helpers": "^7.10.4", - "@babel/parser": "^7.11.5", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.11.5", - "@babel/types": "^7.11.5", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.19", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.11.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz", - "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==", - "dev": true, - "requires": { - "@babel/types": "^7.11.5", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz", - "integrity": "sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==", - "dev": true, - "requires": { - "@babel/types": "^7.11.0" - } - }, - "@babel/helper-module-imports": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", - "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-module-transforms": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz", - "integrity": "sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-simple-access": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/template": "^7.10.4", - "@babel/types": "^7.11.0", - "lodash": "^4.17.19" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", - "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-replace-supers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", - "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-simple-access": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", - "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", - "dev": true, - "requires": { - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", - "dev": true, - "requires": { - "@babel/types": "^7.11.0" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true - }, - "@babel/helpers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz", - "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==", - "dev": true, - "requires": { - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - } - } - }, - "@babel/parser": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", - "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==", - "dev": true - }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/traverse": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz", - "integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.5", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.11.5", - "@babel/types": "^7.11.5", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" - }, - "dependencies": { - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", - "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "@eslint/eslintrc": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz", - "integrity": "sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "lodash": "^4.17.19", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", - "dev": true - }, - "@sindresorhus/is": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", - "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==" - }, - "@sinonjs/commons": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", - "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", - "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "@sinonjs/formatio": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-5.0.1.tgz", - "integrity": "sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^5.0.2" - } - }, - "@sinonjs/samsam": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.1.0.tgz", - "integrity": "sha512-42nyaQOVunX5Pm6GRJobmzbS7iLI+fhERITnETXzzwDZh+TtDr/Au3yAvXVjFmZ4wEUaE4Y3NFZfKv0bV0cbtg==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.6.0", - "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" - } - }, - "@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", - "dev": true - }, - "@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", - "requires": { - "defer-to-connect": "^2.0.0" - } - }, - "@textlint/ast-node-types": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-4.3.4.tgz", - "integrity": "sha512-Grq+vJuNH7HCa278eFeiqJvowrD+onMCoG2ctLyoN+fXYIQGIr1/8fo8AcIg+VM16Kga+N6Y1UWNOWPd8j1nFg==", - "dev": true - }, - "@textlint/ast-tester": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@textlint/ast-tester/-/ast-tester-2.2.4.tgz", - "integrity": "sha512-676xpY3/+Xa+tPaiUPaD4sl//+p3xsnSPYLrQjSmHWXX78F3MwAWd/Lek+SCn4wwvf1tCIx0SPtjfOCa6ru8qw==", - "dev": true, - "requires": { - "@textlint/ast-node-types": "^4.3.4" - } - }, - "@textlint/ast-traverse": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@textlint/ast-traverse/-/ast-traverse-2.2.5.tgz", - "integrity": "sha512-YduGVn7iaUYOfo7TwHO4b0K/qQpj61Ol/M884ck3vetNd0zBxpHO3GpQKW87SZGGtlsBa9v5Suz/yypnlPo3Og==", - "dev": true, - "requires": { - "@textlint/ast-node-types": "^4.3.4" - } - }, - "@textlint/feature-flag": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@textlint/feature-flag/-/feature-flag-3.2.4.tgz", - "integrity": "sha512-ABhbZ5rfkwa/kTBFxVmeMzE1flcnUjLJ5LTZvOaxH/pElfLLN1J4FEmAZTRCvXGAB498II6nkM2CqcikbKzh6A==", - "dev": true, - "requires": { - "map-like": "^2.0.0" - } - }, - "@textlint/fixer-formatter": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/@textlint/fixer-formatter/-/fixer-formatter-3.2.5.tgz", - "integrity": "sha512-fh6XiLbX9WF8+79g20qb1I85k/Yc9+h7LRccmaLzTBjVQDNYxX5BtfvGsY0Vf5tBZKT2xFZH4eSLH/EWoL3weg==", - "dev": true, - "requires": { - "@textlint/module-interop": "^1.1.4", - "@textlint/types": "^1.4.5", - "chalk": "^1.1.3", - "debug": "^4.1.1", - "diff": "^4.0.1", - "is-file": "^1.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^6.0.0", - "text-table": "^0.2.0", - "try-resolve": "^1.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "@textlint/kernel": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/@textlint/kernel/-/kernel-3.3.6.tgz", - "integrity": "sha512-M2ciQDAo5W6rpRADzGnMXyxhvJ+lnqYG9iHrqmfDQ2MA0VcolWuA37H67/UstqTs3NYaGC7RZkq9FAV//pl30w==", - "dev": true, - "requires": { - "@textlint/ast-node-types": "^4.3.4", - "@textlint/ast-tester": "^2.2.4", - "@textlint/ast-traverse": "^2.2.5", - "@textlint/feature-flag": "^3.2.4", - "@textlint/types": "^1.4.5", - "@textlint/utils": "^1.1.4", - "debug": "^4.1.1", - "deep-equal": "^1.1.1", - "map-like": "^2.0.0", - "structured-source": "^3.0.2" - }, - "dependencies": { - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@textlint/linter-formatter": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-3.2.5.tgz", - "integrity": "sha512-oy5RcBWrC2d7r0Mjw/FBH8cvQuOaCB5PeOPG0Pp44Yr5JbIGLXfh84umHQOTCmxfRxw3ccnUfA9wjbxuL8rWOQ==", - "dev": true, - "requires": { - "@azu/format-text": "^1.0.1", - "@azu/style-format": "^1.0.0", - "@textlint/module-interop": "^1.1.4", - "@textlint/types": "^1.4.5", - "chalk": "^1.0.0", - "concat-stream": "^1.5.1", - "debug": "^4.1.1", - "is-file": "^1.0.0", - "js-yaml": "^3.2.4", - "optionator": "^0.8.1", - "pluralize": "^2.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^6.0.0", - "table": "^3.7.8", - "text-table": "^0.2.0", - "try-resolve": "^1.0.1", - "xml-escape": "^1.0.0" - }, - "dependencies": { - "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "dev": true, - "requires": { - "co": "^4.6.0", - "json-stable-stringify": "^1.0.1" - } - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "pluralize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-2.0.0.tgz", - "integrity": "sha1-crcmqm+sHt7uQiVsfY3CVrM1Z38=", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", - "dev": true - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - }, - "table": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", - "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", - "dev": true, - "requires": { - "ajv": "^4.7.0", - "ajv-keywords": "^1.0.0", - "chalk": "^1.1.1", - "lodash": "^4.0.0", - "slice-ansi": "0.0.4", - "string-width": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - } - } - }, - "@textlint/markdown-to-ast": { - "version": "6.2.5", - "resolved": "https://registry.npmjs.org/@textlint/markdown-to-ast/-/markdown-to-ast-6.2.5.tgz", - "integrity": "sha512-9vlQbylGjnnRGev3yt9ntNy6I9FQH3p+MkbijybKnwobK/msoAX9sThDHOMbGM24PsUHxcDjktDlj2vHN/pwDA==", - "dev": true, - "requires": { - "@textlint/ast-node-types": "^4.3.4", - "debug": "^4.1.1", - "remark-frontmatter": "^1.2.0", - "remark-parse": "^5.0.0", - "structured-source": "^3.0.2", - "traverse": "^0.6.6", - "unified": "^6.1.6" - }, - "dependencies": { - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "parse-entities": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.2.tgz", - "integrity": "sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg==", - "dev": true, - "requires": { - "character-entities": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "character-reference-invalid": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-hexadecimal": "^1.0.0" - } - }, - "remark-parse": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-5.0.0.tgz", - "integrity": "sha512-b3iXszZLH1TLoyUzrATcTQUZrwNl1rE70rVdSruJFlDaJ9z5aMkhrG43Pp68OgfHndL/ADz6V69Zow8cTQu+JA==", - "dev": true, - "requires": { - "collapse-white-space": "^1.0.2", - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-whitespace-character": "^1.0.0", - "is-word-character": "^1.0.0", - "markdown-escapes": "^1.0.0", - "parse-entities": "^1.1.0", - "repeat-string": "^1.5.4", - "state-toggle": "^1.0.0", - "trim": "0.0.1", - "trim-trailing-lines": "^1.0.0", - "unherit": "^1.0.4", - "unist-util-remove-position": "^1.0.0", - "vfile-location": "^2.0.0", - "xtend": "^4.0.1" - } - }, - "unified": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/unified/-/unified-6.2.0.tgz", - "integrity": "sha512-1k+KPhlVtqmG99RaTbAv/usu85fcSRu3wY8X+vnsEhIxNP5VbVIDiXnLqyKIG+UMdyTg0ZX9EI6k2AfjJkHPtA==", - "dev": true, - "requires": { - "bail": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^1.1.0", - "trough": "^1.0.0", - "vfile": "^2.0.0", - "x-is-string": "^0.1.0" - } - }, - "unist-util-is": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", - "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==", - "dev": true - }, - "unist-util-remove-position": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.4.tgz", - "integrity": "sha512-tLqd653ArxJIPnKII6LMZwH+mb5q+n/GtXQZo6S6csPRs5zB0u79Yw8ouR3wTw8wxvdJFhpP6Y7jorWdCgLO0A==", - "dev": true, - "requires": { - "unist-util-visit": "^1.1.0" - } - }, - "unist-util-stringify-position": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz", - "integrity": "sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ==", - "dev": true - }, - "unist-util-visit": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", - "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==", - "dev": true, - "requires": { - "unist-util-visit-parents": "^2.0.0" - } - }, - "unist-util-visit-parents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz", - "integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==", - "dev": true, - "requires": { - "unist-util-is": "^3.0.0" - } - }, - "vfile": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-2.3.0.tgz", - "integrity": "sha512-ASt4mBUHcTpMKD/l5Q+WJXNtshlWxOogYyGYYrg4lt/vuRjC1EFQtlAofL5VmtVNIZJzWYFJjzGWZ0Gw8pzW1w==", - "dev": true, - "requires": { - "is-buffer": "^1.1.4", - "replace-ext": "1.0.0", - "unist-util-stringify-position": "^1.0.0", - "vfile-message": "^1.0.0" - } - }, - "vfile-location": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.6.tgz", - "integrity": "sha512-sSFdyCP3G6Ka0CEmN83A2YCMKIieHx0EDaj5IDP4g1pa5ZJ4FJDvpO0WODLxo4LUX4oe52gmSCK7Jw4SBghqxA==", - "dev": true - }, - "vfile-message": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-1.1.1.tgz", - "integrity": "sha512-1WmsopSGhWt5laNir+633LszXvZ+Z/lxveBf6yhGsqnQIhlhzooZae7zV6YVM1Sdkw68dtAW3ow0pOdPANugvA==", - "dev": true, - "requires": { - "unist-util-stringify-position": "^1.1.1" - } - } - } - }, - "@textlint/module-interop": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-1.1.4.tgz", - "integrity": "sha512-9M3kYG5nBoD2lhp05sqi6fieNU6rBcf+A8Trp+4d8o5uJ4RRsWeRtAQMWM7Tv15onqIITRq7fm3la7xovVB9KA==", - "dev": true - }, - "@textlint/text-to-ast": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@textlint/text-to-ast/-/text-to-ast-3.2.4.tgz", - "integrity": "sha512-uIiNg52OdQ3Fn8aOYaV7BLW2QakNsmf4doypIwrW4q+gHYQ3jxdPHHkq6RxuYoO112vO40M3zmAoEZmM1qmPhw==", - "dev": true, - "requires": { - "@textlint/ast-node-types": "^4.3.4" - } - }, - "@textlint/textlint-plugin-markdown": { - "version": "5.2.6", - "resolved": "https://registry.npmjs.org/@textlint/textlint-plugin-markdown/-/textlint-plugin-markdown-5.2.6.tgz", - "integrity": "sha512-S65wy2npaBLT7pwPPlrN9Pw40hOJsxiW+T6peMJOFEMLRem5qlCIlT+02Wlf0Rrtr6/gKDckpphTUiZT1+xRnQ==", - "dev": true, - "requires": { - "@textlint/markdown-to-ast": "^6.2.5" - } - }, - "@textlint/textlint-plugin-text": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/@textlint/textlint-plugin-text/-/textlint-plugin-text-4.2.6.tgz", - "integrity": "sha512-KCgb5GVjoEDIi37UpQN6kFciiouyATNYrj/JufCeLNJEcVcxSm12EoFRKjpXpXmTOVqZUyGnIDU797z1usAZDw==", - "dev": true, - "requires": { - "@textlint/text-to-ast": "^3.2.4" - } - }, - "@textlint/types": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@textlint/types/-/types-1.4.5.tgz", - "integrity": "sha512-7pA1rdiw1jsDNGwxupMC6fPlRNAHY6fKZ3s+jAY53o6RroOSR+5qO0sAjJ26lsSOhveH8imZzyyD08dk58IVJQ==", - "dev": true, - "requires": { - "@textlint/ast-node-types": "^4.3.4" - } - }, - "@textlint/utils": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@textlint/utils/-/utils-1.1.4.tgz", - "integrity": "sha512-KmU+kGi7vG5toUhNdLHHPxyVN1mNGcjMVe1tNDEXT1wU/3oqA96bunElrROWHYw5iNt1QVRaIAtNeMVyzyAdVA==", - "dev": true - }, - "@types/cacheable-request": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", - "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", - "requires": { - "@types/http-cache-semantics": "*", - "@types/keyv": "*", - "@types/node": "*", - "@types/responselike": "*" - } - }, - "@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" - }, - "@types/keyv": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.2.tgz", - "integrity": "sha512-/FvAK2p4jQOaJ6CGDHJTqZcUtbZe820qIeTg7o0Shg7drB4JHeL+V/dhSaly7NXx6u8eSee+r7coT+yuJEvDLg==", - "requires": { - "@types/node": "*" - } - }, - "@types/node": { - "version": "16.4.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.4.13.tgz", - "integrity": "sha512-bLL69sKtd25w7p1nvg9pigE4gtKVpGTPojBFLMkGHXuUgap2sLqQt2qUnqmVCDfzGUL0DRNZP+1prIZJbMeAXg==" - }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", - "requires": { - "@types/node": "*" - } - }, - "@types/unist": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", - "integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==", - "dev": true - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true - }, - "adverb-where": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/adverb-where/-/adverb-where-0.0.9.tgz", - "integrity": "sha1-CcXN3Y1QO5/l924LjcXHCo8ZPjQ=", - "dev": true - }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ajv": { - "version": "6.12.5", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz", - "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", - "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", - "dev": true - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", - "dev": true, - "requires": { - "type-fest": "^0.11.0" - }, - "dependencies": { - "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", - "dev": true - } - } - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "dev": true, - "requires": { - "default-require-extensions": "^3.0.0" - } - }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "array.prototype.map": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz", - "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.4" - } - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true - }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz", - "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==", - "dev": true - }, - "bail": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", - "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", - "dev": true - }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "requires": { - "file-uri-to-path": "1.0.0" - } - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "boundary": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/boundary/-/boundary-1.0.1.tgz", - "integrity": "sha1-TWfcJgLAzBbdm85+v4fpSCkPWBI=", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, - "cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==" - }, - "cacheable-request": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", - "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" - } - }, - "caching-transform": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", - "dev": true, - "requires": { - "hasha": "^5.0.0", - "make-dir": "^3.0.0", - "package-hash": "^4.0.0", - "write-file-atomic": "^3.0.0" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "ccount": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.5.tgz", - "integrity": "sha512-MOli1W+nfbPLlKEhInaxhRdp7KVLFxLN5ykwzHgLsLI3H3gs5jjFAK4Eoj3OzzcxCtumDaI8onoVDeQyWaNTkw==", - "dev": true - }, - "chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", - "dev": true, - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.0", - "type-detect": "^4.0.5" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "character-entities": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", - "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", - "dev": true - }, - "character-entities-html4": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.4.tgz", - "integrity": "sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g==", - "dev": true - }, - "character-entities-legacy": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", - "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", - "dev": true - }, - "character-reference-invalid": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", - "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", - "dev": true - }, - "charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=", - "dev": true - }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true - }, - "chokidar": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", - "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.4.0" - } - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "requires": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - } - } - }, - "clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" - }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "requires": { - "mimic-response": "^1.0.0" - } - }, - "co": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/co/-/co-3.1.0.tgz", - "integrity": "sha1-TqVOpaCJOBUxheFSEMaNkJK8G3g=", - "dev": true - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "collapse-white-space": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz", - "integrity": "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==", - "dev": true - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.1.0.tgz", - "integrity": "sha512-wl7PNrYWd2y5mp1OK/LhTlv8Ff4kQJQRXXAvF+uU/TPNiVJUxZLRYGj/B0y/lPGAVcSbJqH2Za/cvHmrPMC8mA==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "compare-versions": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", - "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "requires": { - "object-assign": "^4", - "vary": "^1" - } - }, - "cosmiconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", - "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", - "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - } - }, - "coveralls": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.1.0.tgz", - "integrity": "sha512-sHxOu2ELzW8/NC1UP5XVLbZDzO4S3VxfFye3XYCznopHy02YjNkHcj5bKaVw2O7hVaBdBjEdQGpie4II1mWhuQ==", - "dev": true, - "requires": { - "js-yaml": "^3.13.1", - "lcov-parse": "^1.0.0", - "log-driver": "^1.2.7", - "minimist": "^1.2.5", - "request": "^2.88.2" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=", - "dev": true - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "requires": { - "mimic-response": "^3.1.0" - }, - "dependencies": { - "mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" - } - } - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", - "dev": true - }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, - "deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", - "dev": true, - "requires": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" - } - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "default-require-extensions": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", - "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", - "dev": true, - "requires": { - "strip-bom": "^4.0.0" - } - }, - "defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "requires": { - "object-keys": "^1.0.12" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "e-prime": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/e-prime/-/e-prime-0.10.4.tgz", - "integrity": "sha512-tzBmM2mFSnAq5BuxPSyin6qXb3yMe1wufJN7L7ZPcEWS5S+jI2dhKQEoqHVEcSMMXo/j5lcWpX5jzA6wLSmX6w==", - "dev": true - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "errorhandler": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz", - "integrity": "sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A==", - "requires": { - "accepts": "~1.3.7", - "escape-html": "~1.0.3" - } - }, - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - }, - "object.assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", - "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.0", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } - } - } - } - }, - "es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", - "dev": true - }, - "es-get-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", - "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", - "dev": true, - "requires": { - "es-abstract": "^1.17.4", - "has-symbols": "^1.0.1", - "is-arguments": "^1.0.4", - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-string": "^1.0.5", - "isarray": "^2.0.5" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.10.0.tgz", - "integrity": "sha512-BDVffmqWl7JJXqCjAK6lWtcQThZB/aP1HXSH1JKwGwv0LQEdvpR7qzNrUT487RM39B5goWuboFad5ovMBmD8yA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.1.3", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^1.3.0", - "espree": "^7.3.0", - "esquery": "^1.2.0", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash": "^4.17.19", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^5.2.3", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true - } - } - }, - "eslint-config-tamia": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/eslint-config-tamia/-/eslint-config-tamia-7.2.5.tgz", - "integrity": "sha512-cAHAQE+bwfC5kJKDMln6ypL1g7EK7yZ3BxvS9JsOmSrem3UvoSLm353EOOBp1sBl4i/Z4AjaQPLasBG37b9oKA==", - "dev": true - }, - "eslint-plugin-prettier": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz", - "integrity": "sha512-jZDa8z76klRqo+TdGDTFJSavwbnWK2ZpqGKNZ+VvweMW516pDUMmQ2koXvxEE4JhzNvTv+radye/bWGBmA6jmg==", - "dev": true, - "requires": { - "prettier-linter-helpers": "^1.0.0" - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - }, - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - }, - "espree": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz", - "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==", - "dev": true, - "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.3.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", - "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "execa": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", - "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fault": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", - "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", - "dev": true, - "requires": { - "format": "^0.2.0" - } - }, - "figgy-pudding": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", - "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", - "dev": true - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", - "dev": true, - "requires": { - "flat-cache": "^2.0.1" - } - }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "find-cache-dir": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", - "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "find-versions": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz", - "integrity": "sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==", - "dev": true, - "requires": { - "semver-regex": "^2.0.0" - } - }, - "flat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", - "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", - "dev": true, - "requires": { - "is-buffer": "~2.0.3" - } - }, - "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", - "dev": true, - "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" - } - }, - "flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", - "dev": true - }, - "foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "format": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", - "integrity": "sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=", - "dev": true - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "fromentries": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.2.1.tgz", - "integrity": "sha512-Xu2Qh8yqYuDhQGOhD5iJGninErSfI9A3FrriD3tjUgV5VbJFeH8vfgZ9HnC6jWN80QDVNQK5vmxRAmEAp7Mevw==", - "dev": true - }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "fs-monkey": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.1.tgz", - "integrity": "sha512-fcSa+wyTqZa46iWweI7/ZiUfegOZl0SG8+dltIwFXo7+zYU9J9kpS3NB6pZcSlJdhvIwp81Adx2XhZorncxiaA==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true - }, - "get-own-enumerable-property-symbols": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", - "dev": true - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-stdin": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", - "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=", - "dev": true - }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "requires": { - "pump": "^3.0.0" - } - }, - "get-url-origin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-url-origin/-/get-url-origin-1.0.1.tgz", - "integrity": "sha512-MMSKo16gB2+6CjWy55jNdIAqUEaKgw3LzZCb8wVVtFrhoQ78EXyuYXxDdn3COI3A4Xr4ZfM3fZa9RTjO6DOTxw==", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - }, - "got": { - "version": "11.8.2", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz", - "integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==", - "requires": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.1", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "dev": true, - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - } - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" - }, - "hasha": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.1.tgz", - "integrity": "sha512-x15jnRSHTi3VmH+oHtVb9kgU/HuKOK8mjK8iCL3dPQXh4YJlUb9YSI8ZLiiqLAIvY2wuDIlZYZppy8vB2XISkQ==", - "dev": true, - "requires": { - "is-stream": "^2.0.0", - "type-fest": "^0.8.0" - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "hoek": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", - "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==" - }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "requires": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - } - }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true - }, - "husky": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/husky/-/husky-4.3.0.tgz", - "integrity": "sha512-tTMeLCLqSBqnflBZnlVDhpaIMucSGaYyX6855jM4AguGeWCeSzNdb1mfyWduTZ3pe3SJVvVWGL0jO1iKZVPfTA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "ci-info": "^2.0.0", - "compare-versions": "^3.6.0", - "cosmiconfig": "^7.0.0", - "find-versions": "^3.2.0", - "opencollective-postinstall": "^2.0.2", - "pkg-dir": "^4.2.0", - "please-upgrade-node": "^3.2.0", - "slash": "^3.0.0", - "which-pm-runs": "^1.0.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "import-fresh": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ini": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", - "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==", - "dev": true - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "is-alphabetical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", - "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", - "dev": true - }, - "is-alphanumeric": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz", - "integrity": "sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ=", - "dev": true - }, - "is-alphanumerical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", - "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", - "dev": true, - "requires": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" - } - }, - "is-arguments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", - "dev": true - }, - "is-callable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" - }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" - }, - "is-decimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", - "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", - "dev": true - }, - "is-empty": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-empty/-/is-empty-1.2.0.tgz", - "integrity": "sha1-3pu1snhzigWgsJpX4ftNSjQan2s=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-file/-/is-file-1.0.0.tgz", - "integrity": "sha1-KKRM+9nT2xkwRfIrZfzo7fliBZY=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-hex": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-hex/-/is-hex-1.1.3.tgz", - "integrity": "sha1-RAJZwHloHgNy5SFWqmeiM8OXXe4=" - }, - "is-hexadecimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", - "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", - "dev": true - }, - "is-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", - "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", - "dev": true - }, - "is-negative-zero": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", - "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=" - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", - "dev": true - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true - }, - "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", - "dev": true - }, - "is-set": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", - "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", - "dev": true - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true - }, - "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", - "dev": true - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, - "is-whitespace-character": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz", - "integrity": "sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-word-character": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz", - "integrity": "sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==", - "dev": true - }, - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "isemail": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", - "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", - "requires": { - "punycode": "2.x.x" - } - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", - "dev": true - }, - "istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "dev": true, - "requires": { - "append-transform": "^2.0.0" - } - }, - "istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "requires": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "istanbul-lib-processinfo": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", - "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", - "dev": true, - "requires": { - "archy": "^1.0.0", - "cross-spawn": "^7.0.0", - "istanbul-lib-coverage": "^3.0.0-alpha.1", - "make-dir": "^3.0.0", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "uuid": "^3.3.3" - }, - "dependencies": { - "p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "dependencies": { - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "iterate-iterator": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz", - "integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==", - "dev": true - }, - "iterate-value": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", - "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", - "dev": true, - "requires": { - "es-get-iterator": "^1.0.2", - "iterate-iterator": "^1.0.1" - } - }, - "joi": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-13.7.0.tgz", - "integrity": "sha512-xuY5VkHfeOYK3Hdi91ulocfuFopwgbSORmIwzcwHKESQhC7w1kD5jaVSPnqDxS2I8t3RZ9omCKAxNwXN5zG1/Q==", - "requires": { - "hoek": "5.x.x", - "isemail": "3.x.x", - "topo": "3.x.x" - }, - "dependencies": { - "hoek": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.4.tgz", - "integrity": "sha512-Alr4ZQgoMlnere5FZJsIyfIjORBqZll5POhDsF4q64dPuJR6rNxXdDxtHSQq8OXRurhmx+PWYEE8bXRROY8h0w==" - } - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "dev": true, - "requires": { - "jsonify": "~0.0.0" - } - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true - }, - "jsonwebtoken": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", - "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", - "requires": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^5.6.0" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "just-extend": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.1.tgz", - "integrity": "sha512-aWgeGFW67BP3e5181Ep1Fv2v8z//iBJfrvyTnq8wG86vEESwmonn1zPBJ0VfmT9CJq2FIT0VsETtrNFm2a+SHA==", - "dev": true - }, - "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "keyv": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", - "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", - "requires": { - "json-buffer": "3.0.1" - } - }, - "lcov-parse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz", - "integrity": "sha1-6w1GtUER68VhrLTECO+TY73I9+A=", - "dev": true - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "libnpmconfig": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/libnpmconfig/-/libnpmconfig-1.2.1.tgz", - "integrity": "sha512-9esX8rTQAHqarx6qeZqmGQKBNZR5OIbl/Ayr0qQDy3oXja2iFVQQI81R6GZ2a02bSNZ9p3YOGX1O6HHCb1X7kA==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1", - "find-up": "^3.0.0", - "ini": "^1.3.5" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } - } - }, - "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true - }, - "lint-staged": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.4.0.tgz", - "integrity": "sha512-uaiX4U5yERUSiIEQc329vhCTDDwUcSvKdRLsNomkYLRzijk3v8V9GWm2Nz0RMVB87VcuzLvtgy6OsjoH++QHIg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "cli-truncate": "^2.1.0", - "commander": "^6.0.0", - "cosmiconfig": "^7.0.0", - "debug": "^4.1.1", - "dedent": "^0.7.0", - "enquirer": "^2.3.6", - "execa": "^4.0.3", - "listr2": "^2.6.0", - "log-symbols": "^4.0.0", - "micromatch": "^4.0.2", - "normalize-path": "^3.0.0", - "please-upgrade-node": "^3.2.0", - "string-argv": "0.3.1", - "stringify-object": "^3.3.0" - }, - "dependencies": { - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "listr2": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-2.6.2.tgz", - "integrity": "sha512-6x6pKEMs8DSIpA/tixiYY2m/GcbgMplMVmhQAaLFxEtNSKLeWTGjtmU57xvv6QCm2XcqzyNXL/cTSVf4IChCRA==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "cli-truncate": "^2.1.0", - "figures": "^3.2.0", - "indent-string": "^4.0.0", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rxjs": "^6.6.2", - "through": "^2.3.8" - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - }, - "dependencies": { - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "^0.2.0" - } - } - } - }, - "load-plugin": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/load-plugin/-/load-plugin-3.0.0.tgz", - "integrity": "sha512-od7eKCCZ62ITvFf8nHHrIiYmgOHb4xVNDRDqxBWSaao5FZyyZVX8OmRCbwjDGPrSrgIulwPNyBsWCGnhiDC0oQ==", - "dev": true, - "requires": { - "libnpmconfig": "^1.0.0", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", - "dev": true - }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, - "lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" - }, - "lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" - }, - "lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" - }, - "lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" - }, - "lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" - }, - "lodash.set": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", - "dev": true - }, - "log-driver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", - "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", - "dev": true - }, - "log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", - "dev": true, - "requires": { - "chalk": "^4.0.0" - } - }, - "log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "dev": true, - "requires": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - } - } - }, - "longest-streak": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.4.tgz", - "integrity": "sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==", - "dev": true - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "requires": { - "p-defer": "^1.0.0" - } - }, - "map-like": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/map-like/-/map-like-2.0.0.tgz", - "integrity": "sha1-lEltSa0zPA3DI0snrbvR6FNZU7Q=", - "dev": true - }, - "markdown-escapes": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz", - "integrity": "sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==", - "dev": true - }, - "markdown-extensions": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-1.1.1.tgz", - "integrity": "sha512-WWC0ZuMzCyDHYCasEGs4IPvLyTGftYwh6wIEOULOF0HXcqZlhwRzrK0w2VUlxWA98xnvb/jszw4ZSkJ6ADpM6Q==", - "dev": true - }, - "markdown-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", - "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", - "dev": true, - "requires": { - "repeat-string": "^1.0.0" - } - }, - "md5": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", - "dev": true, - "requires": { - "charenc": "0.0.2", - "crypt": "0.0.2", - "is-buffer": "~1.1.6" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - } - } - }, - "mdast-comment-marker": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/mdast-comment-marker/-/mdast-comment-marker-1.1.2.tgz", - "integrity": "sha512-vTFXtmbbF3rgnTh3Zl3irso4LtvwUq/jaDvT2D1JqTGAwaipcS7RpTxzi6KjoRqI9n2yuAhzLDAC8xVTF3XYVQ==", - "dev": true - }, - "mdast-util-compact": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-2.0.1.tgz", - "integrity": "sha512-7GlnT24gEwDrdAwEHrU4Vv5lLWrEer4KOkAiKT9nYstsTad7Oc1TwqT2zIMKRdZF7cTuaf+GA1E4Kv7jJh8mPA==", - "dev": true, - "requires": { - "unist-util-visit": "^2.0.0" - } - }, - "mdast-util-heading-style": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/mdast-util-heading-style/-/mdast-util-heading-style-1.0.6.tgz", - "integrity": "sha512-8ZuuegRqS0KESgjAGW8zTx4tJ3VNIiIaGFNEzFpRSAQBavVc7AvOo9I4g3crcZBfYisHs4seYh0rAVimO6HyOw==", - "dev": true - }, - "mdast-util-to-string": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz", - "integrity": "sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A==", - "dev": true - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, - "memfs": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.2.0.tgz", - "integrity": "sha512-f/xxz2TpdKv6uDn6GtHee8ivFyxwxmPuXatBb1FBwxYNuVpbM3k/Y1Z+vC0mH/dIXXrukYfe3qe5J32Dfjg93A==", - "dev": true, - "requires": { - "fs-monkey": "1.0.1" - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" - }, - "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "requires": { - "mime-db": "1.44.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "misspellings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/misspellings/-/misspellings-1.1.0.tgz", - "integrity": "sha1-U9UAJmy9Cc2p2UxM85LmBYm1syQ=", - "dev": true - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "mocha": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.3.tgz", - "integrity": "sha512-ZbaYib4hT4PpF4bdSO2DohooKXIn4lDeiYqB+vTmCdr6l2woW0b6H3pf5x4sM5nwQMru9RvjjHYWVGltR50ZBw==", - "dev": true, - "requires": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.4.2", - "debug": "4.1.1", - "diff": "4.0.2", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.1.6", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.14.0", - "log-symbols": "4.0.0", - "minimatch": "3.0.4", - "ms": "2.1.2", - "object.assign": "4.1.0", - "promise.allsettled": "1.0.2", - "serialize-javascript": "4.0.0", - "strip-json-comments": "3.0.1", - "supports-color": "7.1.0", - "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.0.0", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.1" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "p-limit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", - "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "strip-json-comments": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", - "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "mocha-lcov-reporter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/mocha-lcov-reporter/-/mocha-lcov-reporter-1.3.0.tgz", - "integrity": "sha1-Rpve9PivyaEWBW8HnfYYLQr7A4Q=", - "dev": true - }, - "morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", - "requires": { - "basic-auth": "~2.0.1", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "nan": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", - "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==" - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - }, - "nise": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/nise/-/nise-4.0.4.tgz", - "integrity": "sha512-bTTRUNlemx6deJa+ZyoCUTRvH3liK5+N6VQZ4NIw90AgDXY6iPnsqplNFf6STcj+ePk0H/xqxnP75Lr0J0Fq3A==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0", - "@sinonjs/fake-timers": "^6.0.0", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "path-to-regexp": "^1.7.0" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dev": true, - "requires": { - "isarray": "0.0.1" - } - } - } - }, - "no-cliches": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/no-cliches/-/no-cliches-0.1.1.tgz", - "integrity": "sha512-mYihjs47X5+N71CN3P+QBrEIBuclIfMMpgWEpkmLqFPvrOXdzokvDlhbLfjdBNZOqYgniaeZC6J1ZCgxFdyvXw==", - "dev": true - }, - "nock": { - "version": "13.0.4", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.0.4.tgz", - "integrity": "sha512-alqTV8Qt7TUbc74x1pKRLSENzfjp4nywovcJgi/1aXDiUxXdt7TkruSTF5MDWPP7UoPVgea4F9ghVdmX0xxnSA==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "json-stringify-safe": "^5.0.1", - "lodash.set": "^4.3.2", - "propagate": "^2.0.0" - }, - "dependencies": { - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "node-cache": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", - "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", - "requires": { - "clone": "2.x" - } - }, - "node-expat": { - "version": "2.3.18", - "resolved": "https://registry.npmjs.org/node-expat/-/node-expat-2.3.18.tgz", - "integrity": "sha512-9dIrDxXePa9HSn+hhlAg1wXkvqOjxefEbMclGxk2cEnq/Y3U7Qo5HNNqeo3fQ4bVmLhcdt3YN1TZy7WMZy4MHw==", - "requires": { - "bindings": "^1.5.0", - "nan": "^2.13.2" - } - }, - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "dev": true - }, - "node-preload": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", - "dev": true, - "requires": { - "process-on-spawn": "^1.0.0" - } - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "nyc": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", - "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", - "dev": true, - "requires": { - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "caching-transform": "^4.0.0", - "convert-source-map": "^1.7.0", - "decamelize": "^1.2.0", - "find-cache-dir": "^3.2.0", - "find-up": "^4.1.0", - "foreground-child": "^2.0.0", - "get-package-type": "^0.1.0", - "glob": "^7.1.6", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-processinfo": "^2.0.2", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "make-dir": "^3.0.0", - "node-preload": "^0.2.1", - "p-map": "^3.0.0", - "process-on-spawn": "^1.0.0", - "resolve-from": "^5.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "spawn-wrap": "^2.0.0", - "test-exclude": "^6.0.0", - "yargs": "^15.0.2" - }, - "dependencies": { - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" - }, - "object-is": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.3.tgz", - "integrity": "sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - }, - "object.assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", - "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.0", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - } - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "opencollective-postinstall": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", - "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", - "dev": true - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==" - }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", - "dev": true - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "p-memoize": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-memoize/-/p-memoize-3.1.0.tgz", - "integrity": "sha512-e5tIvrsr7ydUUnxb534iQWtXxWgk/86IsH+H+nV4FHouIggBt4coXboKBt26o4lTu7JbEnGSeXdEsYR8BhAHFA==", - "dev": true, - "requires": { - "mem": "^4.3.0", - "mimic-fn": "^2.1.0" - } - }, - "p-queue": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.1.tgz", - "integrity": "sha512-miQiSxLYPYBxGkrldecZC18OTLjdUqnlRebGzPRiVxB8mco7usCmm7hFuxiTvp93K18JnLtE4KMMycjAu/cQQg==", - "dev": true, - "requires": { - "eventemitter3": "^4.0.4", - "p-timeout": "^3.1.0" - } - }, - "p-timeout": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", - "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", - "dev": true, - "requires": { - "p-finally": "^1.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "package-hash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-entities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", - "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", - "dev": true, - "requires": { - "character-entities": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "character-reference-invalid": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-hexadecimal": "^1.0.0" - } - }, - "parse-json": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", - "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "passive-voice": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/passive-voice/-/passive-voice-0.1.0.tgz", - "integrity": "sha1-Fv+RrkC6DpLEPmcXY/3IQqcCcLE=", - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "path-to-glob-pattern": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-to-glob-pattern/-/path-to-glob-pattern-1.0.2.tgz", - "integrity": "sha1-Rz5qOikqnRP7rj7czuctO6uoxhk=", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", - "dev": true - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "please-upgrade-node": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", - "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", - "dev": true, - "requires": { - "semver-compare": "^1.0.0" - } - }, - "pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "dev": true - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "prettier": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.2.tgz", - "integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==", - "dev": true - }, - "prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "requires": { - "fast-diff": "^1.1.2" - } - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "process-on-spawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", - "dev": true, - "requires": { - "fromentries": "^1.2.0" - } - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "promise.allsettled": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz", - "integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==", - "dev": true, - "requires": { - "array.prototype.map": "^1.0.1", - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "iterate-value": "^1.0.0" - } - }, - "propagate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", - "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", - "dev": true - }, - "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.1" - } - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "rc-config-loader": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-3.0.0.tgz", - "integrity": "sha512-bwfUSB37TWkHfP+PPjb/x8BUjChFmmBK44JMfVnU7paisWqZl/o5k7ttCH+EQLnrbn2Aq8Fo1LAsyUiz+WF4CQ==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "js-yaml": "^3.12.0", - "json5": "^2.1.1", - "require-from-string": "^2.0.2" - }, - "dependencies": { - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - }, - "dependencies": { - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - } - } - }, - "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdirp": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", - "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "regexp.prototype.flags": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", - "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, - "regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", - "dev": true - }, - "release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", - "dev": true, - "requires": { - "es6-error": "^4.0.1" - } - }, - "remark": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/remark/-/remark-12.0.1.tgz", - "integrity": "sha512-gS7HDonkdIaHmmP/+shCPejCEEW+liMp/t/QwmF0Xt47Rpuhl32lLtDV1uKWvGoq+kxr5jSgg5oAIpGuyULjUw==", - "dev": true, - "requires": { - "remark-parse": "^8.0.0", - "remark-stringify": "^8.0.0", - "unified": "^9.0.0" - } - }, - "remark-cli": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/remark-cli/-/remark-cli-8.0.1.tgz", - "integrity": "sha512-UaYeFI5qUAzkthUd8/MLBQD5OKM6jLN8GRvF6v+KF7xO/i1jQ+X2VqUSQAxWFYxZ8R25gM56GVjeoKOZ0EIr8A==", - "dev": true, - "requires": { - "markdown-extensions": "^1.1.0", - "remark": "^12.0.0", - "unified-args": "^8.0.0" - } - }, - "remark-frontmatter": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-1.3.3.tgz", - "integrity": "sha512-fM5eZPBvu2pVNoq3ZPW22q+5Ativ1oLozq2qYt9I2oNyxiUd/tDl0iLLntEVAegpZIslPWg1brhcP1VsaSVUag==", - "dev": true, - "requires": { - "fault": "^1.0.1", - "xtend": "^4.0.1" - } - }, - "remark-lint": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/remark-lint/-/remark-lint-7.0.1.tgz", - "integrity": "sha512-caZXo3qhuBxzvq9JSJFVQ/ERDq/6TJVgWn0KDwKOIJCGOuLXfQhby5XttUq+Rn7kLbNMtvwfWHJlte14LpaeXQ==", - "dev": true, - "requires": { - "remark-message-control": "^6.0.0" - } - }, - "remark-lint-final-newline": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/remark-lint-final-newline/-/remark-lint-final-newline-1.0.5.tgz", - "integrity": "sha512-rfLlW8+Fz2dqnaEgU4JwLA55CQF1T4mfSs/GwkkeUCGPenvEYwSkCN2KO2Gr1dy8qPoOdTFE1rSufLjmeTW5HA==", - "dev": true, - "requires": { - "unified-lint-rule": "^1.0.0" - } - }, - "remark-lint-hard-break-spaces": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/remark-lint-hard-break-spaces/-/remark-lint-hard-break-spaces-2.0.1.tgz", - "integrity": "sha512-Qfn/BMQFamHhtbfLrL8Co/dbYJFLRL4PGVXZ5wumkUO5f9FkZC2RsV+MD9lisvGTkJK0ZEJrVVeaPbUIFM0OAw==", - "dev": true, - "requires": { - "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^1.1.0", - "unist-util-position": "^3.0.0", - "unist-util-visit": "^2.0.0" - } - }, - "remark-lint-list-item-bullet-indent": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/remark-lint-list-item-bullet-indent/-/remark-lint-list-item-bullet-indent-2.0.1.tgz", - "integrity": "sha512-tozDt9LChG1CvYJnBQH/oh45vNcHYBvg79ogvV0f8MtE/K0CXsM8EpfQ6pImFUdHpBV1op6aF6zPMrB0AkRhcQ==", - "dev": true, - "requires": { - "pluralize": "^8.0.0", - "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^1.1.0", - "unist-util-position": "^3.0.0", - "unist-util-visit": "^2.0.0" - } - }, - "remark-lint-list-item-indent": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/remark-lint-list-item-indent/-/remark-lint-list-item-indent-2.0.1.tgz", - "integrity": "sha512-4IKbA9GA14Q9PzKSQI6KEHU/UGO36CSQEjaDIhmb9UOhyhuzz4vWhnSIsxyI73n9nl9GGRAMNUSGzr4pQUFwTA==", - "dev": true, - "requires": { - "pluralize": "^8.0.0", - "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^1.1.0", - "unist-util-position": "^3.0.0", - "unist-util-visit": "^2.0.0" - } - }, - "remark-lint-no-auto-link-without-protocol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/remark-lint-no-auto-link-without-protocol/-/remark-lint-no-auto-link-without-protocol-2.0.1.tgz", - "integrity": "sha512-TFcXxzucsfBb/5uMqGF1rQA+WJJqm1ZlYQXyvJEXigEZ8EAxsxZGPb/gOQARHl/y0vymAuYxMTaChavPKaBqpQ==", - "dev": true, - "requires": { - "mdast-util-to-string": "^1.0.2", - "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^1.1.0", - "unist-util-position": "^3.0.0", - "unist-util-visit": "^2.0.0" - } - }, - "remark-lint-no-blockquote-without-marker": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/remark-lint-no-blockquote-without-marker/-/remark-lint-no-blockquote-without-marker-3.0.1.tgz", - "integrity": "sha512-sM953+u0zN90SGd2V5hWcFbacbpaROUslS5Q5F7/aa66/2rAwh6zVnrXc4pf7fFOpj7I9Xa8Aw+uB+3RJWwdrQ==", - "dev": true, - "requires": { - "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^1.1.0", - "unist-util-position": "^3.0.0", - "unist-util-visit": "^2.0.0", - "vfile-location": "^3.0.0" - } - }, - "remark-lint-no-duplicate-definitions": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/remark-lint-no-duplicate-definitions/-/remark-lint-no-duplicate-definitions-2.0.1.tgz", - "integrity": "sha512-XL22benJZB01m+aOse91nsu1IMFqeWJWme9QvoJuxIcBROO1BG1VoqLOkwNcawE/M/0CkvTo5rfx0eMlcnXOIw==", - "dev": true, - "requires": { - "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^1.1.0", - "unist-util-position": "^3.0.0", - "unist-util-stringify-position": "^2.0.0", - "unist-util-visit": "^2.0.0" - } - }, - "remark-lint-no-heading-content-indent": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/remark-lint-no-heading-content-indent/-/remark-lint-no-heading-content-indent-2.0.1.tgz", - "integrity": "sha512-Jp0zCykGwg13z7XU4VuoFK7DN8bVZ1u3Oqu3hqECsH6LMASb0tW4zcTIc985kcVo3OQTRyb6KLQXL2ltOvppKA==", - "dev": true, - "requires": { - "mdast-util-heading-style": "^1.0.2", - "pluralize": "^8.0.0", - "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^1.1.0", - "unist-util-position": "^3.0.0", - "unist-util-visit": "^2.0.0" - } - }, - "remark-lint-no-inline-padding": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/remark-lint-no-inline-padding/-/remark-lint-no-inline-padding-2.0.1.tgz", - "integrity": "sha512-a36UlPvRrLCgxjjG3YZA9VCDvLBcoBtGNyM04VeCPz+d9hHe+5Fs1C/jL+DRLCH7nff90jJ5C/9b8/LTwhjaWA==", - "dev": true, - "requires": { - "mdast-util-to-string": "^1.0.2", - "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^1.1.0", - "unist-util-visit": "^2.0.0" - } - }, - "remark-lint-no-literal-urls": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/remark-lint-no-literal-urls/-/remark-lint-no-literal-urls-2.0.1.tgz", - "integrity": "sha512-IDdKtWOMuKVQIlb1CnsgBoyoTcXU3LppelDFAIZePbRPySVHklTtuK57kacgU5grc7gPM04bZV96eliGrRU7Iw==", - "dev": true, - "requires": { - "mdast-util-to-string": "^1.0.2", - "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^1.1.0", - "unist-util-position": "^3.0.0", - "unist-util-visit": "^2.0.0" - } - }, - "remark-lint-no-shortcut-reference-image": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/remark-lint-no-shortcut-reference-image/-/remark-lint-no-shortcut-reference-image-2.0.1.tgz", - "integrity": "sha512-2jcZBdnN6ecP7u87gkOVFrvICLXIU5OsdWbo160FvS/2v3qqqwF2e/n/e7D9Jd+KTq1mR1gEVVuTqkWWuh3cig==", - "dev": true, - "requires": { - "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^1.1.0", - "unist-util-visit": "^2.0.0" - } - }, - "remark-lint-no-shortcut-reference-link": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/remark-lint-no-shortcut-reference-link/-/remark-lint-no-shortcut-reference-link-2.0.1.tgz", - "integrity": "sha512-pTZbslG412rrwwGQkIboA8wpBvcjmGFmvugIA+UQR+GfFysKtJ5OZMPGJ98/9CYWjw9Z5m0/EktplZ5TjFjqwA==", - "dev": true, - "requires": { - "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^1.1.0", - "unist-util-visit": "^2.0.0" - } - }, - "remark-lint-no-undefined-references": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/remark-lint-no-undefined-references/-/remark-lint-no-undefined-references-2.0.1.tgz", - "integrity": "sha512-tXM2ctFnduC3QcskrIePUajcjtNtBmo2dvlj4aoQJtQy09Soav/rYngb8u/SgERc6Irdmm5s55UAwR9CcSrzVg==", - "dev": true, - "requires": { - "collapse-white-space": "^1.0.4", - "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^1.1.0", - "unist-util-visit": "^2.0.0" - } - }, - "remark-lint-no-unused-definitions": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/remark-lint-no-unused-definitions/-/remark-lint-no-unused-definitions-2.0.1.tgz", - "integrity": "sha512-+BMc0BOjc364SvKYLkspmxDch8OaKPbnUGgQBvK0Bmlwy42baR4C9zhwAWBxm0SBy5Z4AyM4G4jKpLXPH40Oxg==", - "dev": true, - "requires": { - "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^1.1.0", - "unist-util-visit": "^2.0.0" - } - }, - "remark-lint-ordered-list-marker-style": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/remark-lint-ordered-list-marker-style/-/remark-lint-ordered-list-marker-style-2.0.1.tgz", - "integrity": "sha512-Cnpw1Dn9CHn+wBjlyf4qhPciiJroFOEGmyfX008sQ8uGoPZsoBVIJx76usnHklojSONbpjEDcJCjnOvfAcWW1A==", - "dev": true, - "requires": { - "unified-lint-rule": "^1.0.0", - "unist-util-generated": "^1.1.0", - "unist-util-position": "^3.0.0", - "unist-util-visit": "^2.0.0" - } - }, - "remark-message-control": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/remark-message-control/-/remark-message-control-6.0.0.tgz", - "integrity": "sha512-k9bt7BYc3G7YBdmeAhvd3VavrPa/XlKWR3CyHjr4sLO9xJyly8WHHT3Sp+8HPR8lEUv+/sZaffL7IjMLV0f6BA==", - "dev": true, - "requires": { - "mdast-comment-marker": "^1.0.0", - "unified-message-control": "^3.0.0" - } - }, - "remark-parse": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz", - "integrity": "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==", - "dev": true, - "requires": { - "ccount": "^1.0.0", - "collapse-white-space": "^1.0.2", - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-whitespace-character": "^1.0.0", - "is-word-character": "^1.0.0", - "markdown-escapes": "^1.0.0", - "parse-entities": "^2.0.0", - "repeat-string": "^1.5.4", - "state-toggle": "^1.0.0", - "trim": "0.0.1", - "trim-trailing-lines": "^1.0.0", - "unherit": "^1.0.4", - "unist-util-remove-position": "^2.0.0", - "vfile-location": "^3.0.0", - "xtend": "^4.0.1" - } - }, - "remark-preset-lint-recommended": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-preset-lint-recommended/-/remark-preset-lint-recommended-4.0.1.tgz", - "integrity": "sha512-zn+ImQbOVcAQVWLL0R0rFQ2Wy8JyWnuU3mJ8Zh0EVOckglcxByssvTbKqPih3Lh8ogpE38EfnC3a/vshj4Jx6A==", - "dev": true, - "requires": { - "remark-lint": "^7.0.0", - "remark-lint-final-newline": "^1.0.0", - "remark-lint-hard-break-spaces": "^2.0.0", - "remark-lint-list-item-bullet-indent": "^2.0.0", - "remark-lint-list-item-indent": "^2.0.0", - "remark-lint-no-auto-link-without-protocol": "^2.0.0", - "remark-lint-no-blockquote-without-marker": "^3.0.0", - "remark-lint-no-duplicate-definitions": "^2.0.0", - "remark-lint-no-heading-content-indent": "^2.0.0", - "remark-lint-no-inline-padding": "^2.0.0", - "remark-lint-no-literal-urls": "^2.0.0", - "remark-lint-no-shortcut-reference-image": "^2.0.0", - "remark-lint-no-shortcut-reference-link": "^2.0.0", - "remark-lint-no-undefined-references": "^2.0.0", - "remark-lint-no-unused-definitions": "^2.0.0", - "remark-lint-ordered-list-marker-style": "^2.0.0" - } - }, - "remark-stringify": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-8.1.1.tgz", - "integrity": "sha512-q4EyPZT3PcA3Eq7vPpT6bIdokXzFGp9i85igjmhRyXWmPs0Y6/d2FYwUNotKAWyLch7g0ASZJn/KHHcHZQ163A==", - "dev": true, - "requires": { - "ccount": "^1.0.0", - "is-alphanumeric": "^1.0.0", - "is-decimal": "^1.0.0", - "is-whitespace-character": "^1.0.0", - "longest-streak": "^2.0.1", - "markdown-escapes": "^1.0.0", - "markdown-table": "^2.0.0", - "mdast-util-compact": "^2.0.0", - "parse-entities": "^2.0.0", - "repeat-string": "^1.5.4", - "state-toggle": "^1.0.0", - "stringify-entities": "^3.0.0", - "unherit": "^1.0.4", - "xtend": "^4.0.1" - } - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "replace-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", - "dev": true - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - } - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "resolve-alpn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.0.tgz", - "integrity": "sha512-e4FNQs+9cINYMO5NMFc6kOUCdohjqFPSgMuwuZAOUWqrfWsen+Yjy5qZFkV5K7VO7tFSLKcUL97olkED7sCBHA==" - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "responselike": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", - "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", - "requires": { - "lowercase-keys": "^2.0.0" - } - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "rxjs": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", - "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", - "dev": true - }, - "semver-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz", - "integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==", - "dev": true - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } - } - }, - "serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "should": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", - "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", - "dev": true, - "requires": { - "should-equal": "^2.0.0", - "should-format": "^3.0.3", - "should-type": "^1.4.0", - "should-type-adaptors": "^1.0.1", - "should-util": "^1.0.0" - } - }, - "should-equal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", - "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", - "dev": true, - "requires": { - "should-type": "^1.4.0" - } - }, - "should-format": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", - "integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=", - "dev": true, - "requires": { - "should-type": "^1.3.0", - "should-type-adaptors": "^1.0.1" - } - }, - "should-type": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", - "integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=", - "dev": true - }, - "should-type-adaptors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", - "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", - "dev": true, - "requires": { - "should-type": "^1.3.0", - "should-util": "^1.0.0" - } - }, - "should-util": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", - "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==", - "dev": true - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "sinon": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.0.3.tgz", - "integrity": "sha512-IKo9MIM111+smz9JGwLmw5U1075n1YXeAq8YeSFlndCLhAL5KGn6bLgu7b/4AYHTV/LcEMcRm2wU2YiL55/6Pg==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.2", - "@sinonjs/fake-timers": "^6.0.1", - "@sinonjs/formatio": "^5.0.1", - "@sinonjs/samsam": "^5.1.0", - "diff": "^4.0.2", - "nise": "^4.0.4", - "supports-color": "^7.1.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "sinon-chai": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.5.0.tgz", - "integrity": "sha512-IifbusYiQBpUxxFJkR3wTU68xzBN0+bxCScEaKMjBvAQERg6FnTTc1F17rseLb1tjmkJ23730AXpFI0c47FgAg==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - } - }, - "sliced": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", - "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "spawn-wrap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", - "dev": true, - "requires": { - "foreground-child": "^2.0.0", - "is-windows": "^1.0.2", - "make-dir": "^3.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "which": "^2.0.1" - }, - "dependencies": { - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz", - "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "state-toggle": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz", - "integrity": "sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==", - "dev": true - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, - "stringify-entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-3.0.1.tgz", - "integrity": "sha512-Lsk3ISA2++eJYqBMPKcr/8eby1I6L0gP0NlxF8Zja6c05yr/yCYyb2c9PwXjd08Ib3If1vn1rbs1H5ZtVuOfvQ==", - "dev": true, - "requires": { - "character-entities-html4": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.2", - "is-hexadecimal": "^1.0.0" - } - }, - "stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "dev": true, - "requires": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "structured-source": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/structured-source/-/structured-source-3.0.2.tgz", - "integrity": "sha1-3YAkJeD1PcSm56yjdSkBoczaevU=", - "dev": true, - "requires": { - "boundary": "^1.0.1" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", - "dev": true, - "requires": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "textlint": { - "version": "11.7.6", - "resolved": "https://registry.npmjs.org/textlint/-/textlint-11.7.6.tgz", - "integrity": "sha512-o9nhbylWjOErba1gq2bMoJzughp9JK2VbENR+NCiMsNNEiaJ1P8jbnrL3ES86D6e0QMxziR79w5l7VmmdmLjCw==", - "dev": true, - "requires": { - "@textlint/ast-node-types": "^4.3.4", - "@textlint/ast-traverse": "^2.2.5", - "@textlint/feature-flag": "^3.2.4", - "@textlint/fixer-formatter": "^3.2.5", - "@textlint/kernel": "^3.3.6", - "@textlint/linter-formatter": "^3.2.5", - "@textlint/module-interop": "^1.1.4", - "@textlint/textlint-plugin-markdown": "^5.2.6", - "@textlint/textlint-plugin-text": "^4.2.6", - "@textlint/types": "^1.4.5", - "@textlint/utils": "^1.1.4", - "debug": "^4.1.1", - "deep-equal": "^1.1.0", - "file-entry-cache": "^5.0.1", - "get-stdin": "^5.0.1", - "glob": "^7.1.3", - "is-file": "^1.0.0", - "log-symbols": "^1.0.2", - "map-like": "^2.0.0", - "md5": "^2.2.1", - "mkdirp": "^0.5.0", - "optionator": "^0.8.0", - "path-to-glob-pattern": "^1.0.2", - "rc-config-loader": "^3.0.0", - "read-pkg": "^1.1.0", - "read-pkg-up": "^3.0.0", - "structured-source": "^3.0.2", - "try-resolve": "^1.0.1", - "unique-concat": "^0.2.2" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", - "dev": true, - "requires": { - "chalk": "^1.0.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - } - } - }, - "textlint-filter-rule-comments": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/textlint-filter-rule-comments/-/textlint-filter-rule-comments-1.2.2.tgz", - "integrity": "sha1-OnLElJlOBo4OSqrQ8k6nz+M4UDo=", - "dev": true - }, - "textlint-rule-common-misspellings": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/textlint-rule-common-misspellings/-/textlint-rule-common-misspellings-1.0.1.tgz", - "integrity": "sha1-jEEzzzu1mqFZGZ0sm87RJBM2V3Q=", - "dev": true, - "requires": { - "misspellings": "^1.0.1", - "textlint-rule-helper": "^1.1.5" - } - }, - "textlint-rule-helper": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/textlint-rule-helper/-/textlint-rule-helper-1.2.0.tgz", - "integrity": "sha1-vmjUelFGsW3RFieMmut701YxzNo=", - "dev": true, - "requires": { - "unist-util-visit": "^1.1.0" - }, - "dependencies": { - "unist-util-is": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", - "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==", - "dev": true - }, - "unist-util-visit": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", - "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==", - "dev": true, - "requires": { - "unist-util-visit-parents": "^2.0.0" - } - }, - "unist-util-visit-parents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz", - "integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==", - "dev": true, - "requires": { - "unist-util-is": "^3.0.0" - } - } - } - }, - "textlint-rule-no-dead-link": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/textlint-rule-no-dead-link/-/textlint-rule-no-dead-link-4.7.0.tgz", - "integrity": "sha512-enqn5JHR7Xa5vtkrYBAwa1POuKoi4j2hY7RKrPX+mWgQGIFapPrxfy7Bw+2pEHfV2nLqXrLQuPo4H6/D9FvzSw==", - "dev": true, - "requires": { - "fs-extra": "^8.1.0", - "get-url-origin": "^1.0.1", - "minimatch": "^3.0.4", - "node-fetch": "^2.6.0", - "p-memoize": "^3.1.0", - "p-queue": "^6.2.0", - "textlint-rule-helper": "^2.1.1" - }, - "dependencies": { - "textlint-rule-helper": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/textlint-rule-helper/-/textlint-rule-helper-2.1.1.tgz", - "integrity": "sha512-6fxgHzoJVkjl3LaC1b2Egi+5wbhG4i0pU0knJmQujVhxIJ3D3AcQQZPs457xKAi5xKz1WayYeTeJ5jrD/hnO7g==", - "dev": true, - "requires": { - "@textlint/ast-node-types": "^4.2.1", - "@textlint/types": "^1.1.2", - "structured-source": "^3.0.2", - "unist-util-visit": "^1.1.0" - } - }, - "unist-util-is": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", - "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==", - "dev": true - }, - "unist-util-visit": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", - "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==", - "dev": true, - "requires": { - "unist-util-visit-parents": "^2.0.0" - } - }, - "unist-util-visit-parents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz", - "integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==", - "dev": true, - "requires": { - "unist-util-is": "^3.0.0" - } - } - } - }, - "textlint-rule-terminology": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/textlint-rule-terminology/-/textlint-rule-terminology-2.1.4.tgz", - "integrity": "sha512-kLw4qL8RwY2lCNqgKveHc5sjCDlS5Tdw2TXWOrHvSvQxqaVOwsv3+51oMIQLGfJzQrhFSMlSlw5MvfaOerBvPQ==", - "dev": true, - "requires": { - "lodash": "^4.17.15", - "strip-json-comments": "^3.0.1", - "textlint-rule-helper": "^2.1.1" - }, - "dependencies": { - "textlint-rule-helper": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/textlint-rule-helper/-/textlint-rule-helper-2.1.1.tgz", - "integrity": "sha512-6fxgHzoJVkjl3LaC1b2Egi+5wbhG4i0pU0knJmQujVhxIJ3D3AcQQZPs457xKAi5xKz1WayYeTeJ5jrD/hnO7g==", - "dev": true, - "requires": { - "@textlint/ast-node-types": "^4.2.1", - "@textlint/types": "^1.1.2", - "structured-source": "^3.0.2", - "unist-util-visit": "^1.1.0" - } - }, - "unist-util-is": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", - "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==", - "dev": true - }, - "unist-util-visit": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", - "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==", - "dev": true, - "requires": { - "unist-util-visit-parents": "^2.0.0" - } - }, - "unist-util-visit-parents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz", - "integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==", - "dev": true, - "requires": { - "unist-util-is": "^3.0.0" - } - } - } - }, - "textlint-rule-write-good": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/textlint-rule-write-good/-/textlint-rule-write-good-1.6.2.tgz", - "integrity": "sha1-PHmwQJExnU6L5ftELFlr9QDoST4=", - "dev": true, - "requires": { - "textlint-rule-helper": "^2.0.0", - "write-good": "^0.11.0" - }, - "dependencies": { - "textlint-rule-helper": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/textlint-rule-helper/-/textlint-rule-helper-2.1.1.tgz", - "integrity": "sha512-6fxgHzoJVkjl3LaC1b2Egi+5wbhG4i0pU0knJmQujVhxIJ3D3AcQQZPs457xKAi5xKz1WayYeTeJ5jrD/hnO7g==", - "dev": true, - "requires": { - "@textlint/ast-node-types": "^4.2.1", - "@textlint/types": "^1.1.2", - "structured-source": "^3.0.2", - "unist-util-visit": "^1.1.0" - } - }, - "unist-util-is": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", - "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==", - "dev": true - }, - "unist-util-visit": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", - "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==", - "dev": true, - "requires": { - "unist-util-visit-parents": "^2.0.0" - } - }, - "unist-util-visit-parents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz", - "integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==", - "dev": true, - "requires": { - "unist-util-is": "^3.0.0" - } - } - } - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "to-vfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/to-vfile/-/to-vfile-6.1.0.tgz", - "integrity": "sha512-BxX8EkCxOAZe+D/ToHdDsJcVI4HqQfmw0tCkp31zf3dNP/XWIAjU4CmeuSwsSoOzOTqHPOL0KUzyZqJplkD0Qw==", - "dev": true, - "requires": { - "is-buffer": "^2.0.0", - "vfile": "^4.0.0" - } - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "too-wordy": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/too-wordy/-/too-wordy-0.1.6.tgz", - "integrity": "sha512-MV5F74YF9+UYsvwXGXTh+5YP3EqH/ivwWfyFE2/YHWQQxm9jDPmkIC23nkN133Ye4nO3HTXmiMcfGqJ5xRPfOA==", - "dev": true - }, - "topo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.3.tgz", - "integrity": "sha512-IgpPtvD4kjrJ7CRA3ov2FhWQADwv+Tdqbsf1ZnPUSAtCJ9e1Z44MmoSGDXGk4IppoZA7jd/QRkNddlLJWlUZsQ==", - "requires": { - "hoek": "6.x.x" - }, - "dependencies": { - "hoek": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.3.tgz", - "integrity": "sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==" - } - } - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "traverse": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", - "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=", - "dev": true - }, - "trim": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", - "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=", - "dev": true - }, - "trim-trailing-lines": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.3.tgz", - "integrity": "sha512-4ku0mmjXifQcTVfYDfR5lpgV7zVqPg6zV9rdZmwOPqq0+Zq19xDqEgagqVbc4pOOShbncuAOIs59R3+3gcF3ZA==", - "dev": true - }, - "trough": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", - "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", - "dev": true - }, - "try-resolve": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/try-resolve/-/try-resolve-1.0.1.tgz", - "integrity": "sha1-z95vq9ctY+V5fPqrhzq76OcA6RI=", - "dev": true - }, - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-detect": { - "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==", - "dev": true - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "underscore": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", - "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==" - }, - "unherit": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz", - "integrity": "sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==", - "dev": true, - "requires": { - "inherits": "^2.0.0", - "xtend": "^4.0.0" - } - }, - "unified": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz", - "integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==", - "dev": true, - "requires": { - "bail": "^1.0.0", - "extend": "^3.0.0", - "is-buffer": "^2.0.0", - "is-plain-obj": "^2.0.0", - "trough": "^1.0.0", - "vfile": "^4.0.0" - }, - "dependencies": { - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - } - } - }, - "unified-args": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/unified-args/-/unified-args-8.1.0.tgz", - "integrity": "sha512-t1HPS1cQPsVvt/6EtyWIbQGurza5684WGRigNghZRvzIdHm3LPgMdXPyGx0npORKzdiy5+urkF0rF5SXM8lBuQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "chalk": "^3.0.0", - "chokidar": "^3.0.0", - "fault": "^1.0.2", - "json5": "^2.0.0", - "minimist": "^1.2.0", - "text-table": "^0.2.0", - "unified-engine": "^8.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "unified-engine": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/unified-engine/-/unified-engine-8.0.0.tgz", - "integrity": "sha512-vLUezxCnjzz+ya4pYouRQVMT8k82Rk4fIj406UidRnSFJdGXFaQyQklAnalsQHJrLqAlaYPkXPUa1upfVSHGCA==", - "dev": true, - "requires": { - "concat-stream": "^2.0.0", - "debug": "^4.0.0", - "fault": "^1.0.0", - "figures": "^3.0.0", - "glob": "^7.0.3", - "ignore": "^5.0.0", - "is-buffer": "^2.0.0", - "is-empty": "^1.0.0", - "is-plain-obj": "^2.0.0", - "js-yaml": "^3.6.1", - "load-plugin": "^3.0.0", - "parse-json": "^5.0.0", - "to-vfile": "^6.0.0", - "trough": "^1.0.0", - "unist-util-inspect": "^5.0.0", - "vfile-reporter": "^6.0.0", - "vfile-statistics": "^1.1.0" - }, - "dependencies": { - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "unified-lint-rule": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/unified-lint-rule/-/unified-lint-rule-1.0.6.tgz", - "integrity": "sha512-YPK15YBFwnsVorDFG/u0cVVQN5G2a3V8zv5/N6KN3TCG+ajKtaALcy7u14DCSrJI+gZeyYquFL9cioJXOGXSvg==", - "dev": true, - "requires": { - "wrapped": "^1.0.1" - } - }, - "unified-message-control": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/unified-message-control/-/unified-message-control-3.0.1.tgz", - "integrity": "sha512-K2Kvvp1DBzeuxYLLsumZh/gDWUTl4e2z/P3VReFirC78cfHKtQifbhnfRrSBtKtd1Uc6cvYTW0/SZIUaMAEcTg==", - "dev": true, - "requires": { - "unist-util-visit": "^2.0.0", - "vfile-location": "^3.0.0" - } - }, - "unique-concat": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/unique-concat/-/unique-concat-0.2.2.tgz", - "integrity": "sha1-khD5vcqsxeHjkpSQ18AZ35bxhxI=", - "dev": true - }, - "unist-util-generated": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.5.tgz", - "integrity": "sha512-1TC+NxQa4N9pNdayCYA1EGUOCAO0Le3fVp7Jzns6lnua/mYgwHo0tz5WUAfrdpNch1RZLHc61VZ1SDgrtNXLSw==", - "dev": true - }, - "unist-util-inspect": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/unist-util-inspect/-/unist-util-inspect-5.0.1.tgz", - "integrity": "sha512-fPNWewS593JSmg49HbnE86BJKuBi1/nMWhDSccBvbARfxezEuJV85EaARR9/VplveiwCoLm2kWq+DhP8TBaDpw==", - "dev": true, - "requires": { - "is-empty": "^1.0.0" - } - }, - "unist-util-is": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.2.tgz", - "integrity": "sha512-Ofx8uf6haexJwI1gxWMGg6I/dLnF2yE+KibhD3/diOqY2TinLcqHXCV6OI5gFVn3xQqDH+u0M625pfKwIwgBKQ==", - "dev": true - }, - "unist-util-position": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz", - "integrity": "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==", - "dev": true - }, - "unist-util-remove-position": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz", - "integrity": "sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA==", - "dev": true, - "requires": { - "unist-util-visit": "^2.0.0" - } - }, - "unist-util-stringify-position": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", - "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", - "dev": true, - "requires": { - "@types/unist": "^2.0.2" - } - }, - "unist-util-visit": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", - "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", - "dev": true, - "requires": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0", - "unist-util-visit-parents": "^3.0.0" - } - }, - "unist-util-visit-parents": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.0.tgz", - "integrity": "sha512-0g4wbluTF93npyPrp/ymd3tCDTMnP0yo2akFD2FIBAYXq/Sga3lwaU1D8OYKbtpioaI6CkDcQ6fsMnmtzt7htw==", - "dev": true, - "requires": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0" - } - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "uri-js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", - "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - }, - "v8-compile-cache": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", - "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "vfile": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.0.tgz", - "integrity": "sha512-a/alcwCvtuc8OX92rqqo7PflxiCgXRFjdyoGVuYV+qbgCb0GgZJRvIgCD4+U/Kl1yhaRsaTwksF88xbPyGsgpw==", - "dev": true, - "requires": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "replace-ext": "1.0.0", - "unist-util-stringify-position": "^2.0.0", - "vfile-message": "^2.0.0" - } - }, - "vfile-location": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.1.0.tgz", - "integrity": "sha512-FCZ4AN9xMcjFIG1oGmZKo61PjwJHRVA+0/tPUP2ul4uIwjGGndIxavEMRpWn5p4xwm/ZsdXp9YNygf1ZyE4x8g==", - "dev": true - }, - "vfile-message": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", - "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", - "dev": true, - "requires": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" - } - }, - "vfile-reporter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/vfile-reporter/-/vfile-reporter-6.0.1.tgz", - "integrity": "sha512-0OppK9mo8G2XUpv+hIKLVSDsoxJrXnOy73+vIm0jQUOUFYRduqpFHX+QqAQfvRHyX9B0UFiRuNJnBOjQCIsw1g==", - "dev": true, - "requires": { - "repeat-string": "^1.5.0", - "string-width": "^4.0.0", - "supports-color": "^6.0.0", - "unist-util-stringify-position": "^2.0.0", - "vfile-sort": "^2.1.2", - "vfile-statistics": "^1.1.0" - }, - "dependencies": { - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "vfile-sort": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/vfile-sort/-/vfile-sort-2.2.2.tgz", - "integrity": "sha512-tAyUqD2R1l/7Rn7ixdGkhXLD3zsg+XLAeUDUhXearjfIcpL1Hcsj5hHpCoy/gvfK/Ws61+e972fm0F7up7hfYA==", - "dev": true - }, - "vfile-statistics": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/vfile-statistics/-/vfile-statistics-1.1.4.tgz", - "integrity": "sha512-lXhElVO0Rq3frgPvFBwahmed3X03vjPF8OcjKMy8+F1xU/3Q3QU3tKEDp743SFtb74PdF0UWpxPvtOP0GCLheA==", - "dev": true - }, - "weasel-words": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/weasel-words/-/weasel-words-0.1.1.tgz", - "integrity": "sha1-cTeUZYXHP+RIggE4U70ADF1oek4=", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "which-pm-runs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", - "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", - "dev": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "workerpool": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz", - "integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==", - "dev": true - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "wrapped": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wrapped/-/wrapped-1.0.1.tgz", - "integrity": "sha1-x4PZ2Aeyc+mwHoUWgKk4yHyQckI=", - "dev": true, - "requires": { - "co": "3.1.0", - "sliced": "^1.0.1" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "write-good": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/write-good/-/write-good-0.11.3.tgz", - "integrity": "sha512-fDKIHO5wCzTLCOGNJl1rzzJrZlTIzfZl8msOoJQZzRhYo0X/tFTm4+2B1zTibFYK01Nnd1kLZBjj4xjcFLePNQ==", - "dev": true, - "requires": { - "adverb-where": "0.0.9", - "e-prime": "^0.10.2", - "no-cliches": "^0.1.0", - "object.assign": "^4.0.4", - "passive-voice": "^0.1.0", - "too-wordy": "^0.1.4", - "weasel-words": "^0.1.1" - } - }, - "x-is-string": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", - "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=", - "dev": true - }, - "xml-escape": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/xml-escape/-/xml-escape-1.1.0.tgz", - "integrity": "sha1-OQTBQ/qOs6ADDsZG0pAqLxtwbEQ=", - "dev": true - }, - "xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - } - }, - "xml2json": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/xml2json/-/xml2json-0.12.0.tgz", - "integrity": "sha512-EPJHRWJnJUYbJlzR4pBhZODwWdi2IaYGtDdteJi0JpZ4OD31IplWALuit8r73dJuM4iHZdDVKY1tLqY2UICejg==", - "requires": { - "hoek": "^4.2.1", - "joi": "^13.1.2", - "node-expat": "^2.3.18" - } - }, - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true - }, - "y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", - "dev": true - }, - "yaml": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", - "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", - "dev": true - }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "yargs-unparser": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.1.tgz", - "integrity": "sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "decamelize": "^1.2.0", - "flat": "^4.1.0", - "is-plain-obj": "^1.1.0", - "yargs": "^14.2.3" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "yargs": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", - "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^15.0.1" - } - }, - "yargs-parser": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz", - "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - } - } -} From c37f94b02936939ec81d20e2bed898a61fee66bd Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Tue, 17 Aug 2021 10:44:39 +0200 Subject: [PATCH 19/33] Switch PEP proxy port for tests. --- test/unit/authentication-test.js | 10 +++++----- test/unit/authzforce-pdp-test.js | 4 ++-- test/unit/connection-test.js | 2 +- test/unit/ishare-test.js | 6 +++--- test/unit/jwt-authentication-test.js | 10 +++++----- test/unit/keyrock-pdp-test.js | 6 +++--- test/unit/opa-pdp-test.js | 6 +++--- test/unit/start-up-test.js | 2 +- test/unit/xacml-pdp-test.js | 6 +++--- 9 files changed, 26 insertions(+), 26 deletions(-) diff --git a/test/unit/authentication-test.js b/test/unit/authentication-test.js index b0b140c..56547f2 100644 --- a/test/unit/authentication-test.js +++ b/test/unit/authentication-test.js @@ -11,31 +11,31 @@ const nock = require('nock'); const cache = require('../../lib/cache'); const request_no_header = { - prefixUrl: 'http:/localhost:80', + prefixUrl: 'http:/localhost:1026', throwHttpErrors: false }; const request_with_header = { - prefixUrl: 'http:/localhost:80', + prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, headers: { 'x-auth-token': '111111111' } }; const request_with_auth_header = { - prefixUrl: 'http:/localhost:80', + prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, headers: { authorization: 'Bearer: ' + Buffer.from('111111111', 'utf-8').toString('base64') } }; const request_with_magic_key = { - prefixUrl: 'http:/localhost:80', + prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, headers: { 'x-auth-token': '999999999' } }; const config = { magic_key: '999999999', - pep_port: 80, + pep_port: 1026, pep: { app_id: 'application_id', trusted_apps: [] diff --git a/test/unit/authzforce-pdp-test.js b/test/unit/authzforce-pdp-test.js index 111f818..dc2e895 100644 --- a/test/unit/authzforce-pdp-test.js +++ b/test/unit/authzforce-pdp-test.js @@ -11,7 +11,7 @@ const nock = require('nock'); const cache = require('../../lib/cache'); const request_with_header = { - prefixUrl: 'http:/localhost:80', + prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, headers: { 'x-auth-token': '111111111' } }; @@ -61,7 +61,7 @@ const authzforce_deny_response = ``; const config = { - pep_port: 80, + pep_port: 1026, pep: { app_id: 'application_id', trusted_apps: [] diff --git a/test/unit/connection-test.js b/test/unit/connection-test.js index 82340a1..378646f 100644 --- a/test/unit/connection-test.js +++ b/test/unit/connection-test.js @@ -13,7 +13,7 @@ const Authzforce = require('../../lib/pdp/authzforce'); const cache = require('../../lib/cache'); const config = { - pep_port: 80, + pep_port: 1026, pep: { app_id: 'application_id', trusted_apps: [], diff --git a/test/unit/ishare-test.js b/test/unit/ishare-test.js index f3c0c08..63b83f4 100644 --- a/test/unit/ishare-test.js +++ b/test/unit/ishare-test.js @@ -101,14 +101,14 @@ const token = jwt.sign( ); const request_with_jwt = { - prefixUrl: 'http:/localhost:80', + prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, headers: { 'x-auth-token': token }, retry: 0 }; const request_with_jwt_and_body = { - prefixUrl: 'http:/localhost:80', + prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, headers: { 'x-auth-token': token }, json: ngsiPayload, @@ -117,7 +117,7 @@ const request_with_jwt_and_body = { const config = { magic_key: '999999999', - pep_port: 80, + pep_port: 1026, pep: { app_id: 'application_id', trusted_apps: [], diff --git a/test/unit/jwt-authentication-test.js b/test/unit/jwt-authentication-test.js index 3482cbd..058b3c8 100644 --- a/test/unit/jwt-authentication-test.js +++ b/test/unit/jwt-authentication-test.js @@ -49,32 +49,32 @@ const expired_token = jwt.sign( ); const request_with_jwt = { - prefixUrl: 'http:/localhost:80', + prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, headers: { 'x-auth-token': token } }; const request_with_invalid_jwt = { - prefixUrl: 'http:/localhost:80', + prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, headers: { 'x-auth-token': invalid_token }, retry: 0 }; const request_no_jwt = { - prefixUrl: 'http:/localhost:80', + prefixUrl: 'http:/localhost:1026', throwHttpErrors: false }; const request_with_expired_jwt = { - prefixUrl: 'http:/localhost:80', + prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, headers: { 'x-auth-token': expired_token } }; const config = { magic_key: '999999999', - pep_port: 80, + pep_port: 1026, pep: { app_id: 'application_id', trusted_apps: [], diff --git a/test/unit/keyrock-pdp-test.js b/test/unit/keyrock-pdp-test.js index cfd6f07..94276e9 100644 --- a/test/unit/keyrock-pdp-test.js +++ b/test/unit/keyrock-pdp-test.js @@ -11,13 +11,13 @@ const nock = require('nock'); const cache = require('../../lib/cache'); const request_with_header = { - prefixUrl: 'http:/localhost:80', + prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, headers: { 'x-auth-token': '111111111' } }; const request_with_header_and_body = { - prefixUrl: 'http:/localhost:80', + prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, headers: { 'x-auth-token': '111111111' }, body: 'HELLO' @@ -36,7 +36,7 @@ const keyrock_permit_response = { }; const config = { - pep_port: 80, + pep_port: 1026, pep: { app_id: 'application_id', trusted_apps: [] diff --git a/test/unit/opa-pdp-test.js b/test/unit/opa-pdp-test.js index ed3ae8e..b15911d 100644 --- a/test/unit/opa-pdp-test.js +++ b/test/unit/opa-pdp-test.js @@ -50,12 +50,12 @@ const keyrock_user_response = { }; const request_with_headers = { - prefixUrl: 'http:/localhost:80', + prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, headers: { 'x-auth-token': '111111111', 'fiware-service': 'smart-gondor', 'x-forwarded-for': 'example.com' } }; const request_with_headers_and_body = { - prefixUrl: 'http:/localhost:80', + prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, headers: { 'x-auth-token': '111111111', 'fiware-service': 'smart-gondor' }, json: ngsiPayload @@ -69,7 +69,7 @@ const open_policy_agent_deny_response = { }; const config = { - pep_port: 80, + pep_port: 1026, pep: { app_id: 'application_id', trusted_apps: [] diff --git a/test/unit/start-up-test.js b/test/unit/start-up-test.js index a8f40ad..f14f653 100644 --- a/test/unit/start-up-test.js +++ b/test/unit/start-up-test.js @@ -2,7 +2,7 @@ const config_service = require('../../lib/config_service'); const should = require('should'); const config = { - pep_port: 80, + pep_port: 1026, pep: { app_id: 'application_id', trusted_apps: [] diff --git a/test/unit/xacml-pdp-test.js b/test/unit/xacml-pdp-test.js index c3859f0..1f17cb7 100644 --- a/test/unit/xacml-pdp-test.js +++ b/test/unit/xacml-pdp-test.js @@ -50,12 +50,12 @@ const keyrock_user_response = { }; const request_with_headers = { - prefixUrl: 'http:/localhost:80', + prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, headers: { 'x-auth-token': '111111111', 'fiware-service': 'smart-gondor', 'x-forwarded-for': 'example.com' } }; const request_with_headers_and_body = { - prefixUrl: 'http:/localhost:80', + prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, headers: { 'x-auth-token': '111111111', 'fiware-service': 'smart-gondor' }, json: ngsiPayload @@ -90,7 +90,7 @@ const xacml_deny_response = { }; const config = { - pep_port: 80, + pep_port: 1026, pep: { app_id: 'application_id', trusted_apps: [] From 5469e2b74c3cb7fb684e4f28cbf9683daf5143d4 Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Tue, 17 Aug 2021 17:10:20 +0200 Subject: [PATCH 20/33] Add bugs and homepage --- package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/package.json b/package.json index f3828f9..1c26730 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,10 @@ "type": "git", "url": "https://github.com/ging/fiware-pep-proxy" }, + "homepage": "https://fiware-pep-proxy.readthedocs.io/en/latest/", + "bugs": { + "url": "https://github.com/ging/fiware-pep-proxy/issues" + }, "contributors": [ { "name": "Alvaro Alonso", From e3d2943af438f0e1b6437982d6a45dfc364d06df Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Wed, 18 Aug 2021 10:09:47 +0200 Subject: [PATCH 21/33] Follow IUDX standard for error messaging. --- controllers/root.js | 9 +- lib/access_functions.js | 22 +- lib/authorization_functions.js | 2 +- lib/pdp/keyrock.js | 10 +- package-lock.json | 7557 ++++++++++++++++++++++++++ package.json | 1 + test/unit/authentication-test.js | 37 +- test/unit/authzforce-pdp-test.js | 21 +- test/unit/connection-test.js | 9 +- test/unit/ishare-test.js | 19 +- test/unit/jwt-authentication-test.js | 17 +- test/unit/keyrock-pdp-test.js | 31 +- test/unit/opa-pdp-test.js | 25 +- test/unit/xacml-pdp-test.js | 25 +- 14 files changed, 7682 insertions(+), 103 deletions(-) create mode 100644 package-lock.json diff --git a/controllers/root.js b/controllers/root.js index 4cf31b2..94c7be1 100644 --- a/controllers/root.js +++ b/controllers/root.js @@ -11,6 +11,7 @@ const IDM = require('../lib/pdp/keyrock'); const jsonwebtoken = require('jsonwebtoken'); const access = require('../lib/access_functions'); const PDP = require('../lib/authorization_functions'); +const StatusCodes = require('http-status-codes').StatusCodes; const debug = require('debug')('pep-proxy:root'); @@ -25,7 +26,7 @@ function validateAccessJWT(req, res, tokens) { return jsonwebtoken.verify(tokens.authToken, config.pep.token.secret, function (err, userInfo) { if (err) { if (err.name === 'TokenExpiredError') { - return access.deny(res, 'Invalid token: jwt token has expired'); + return access.deny(res, 'Invalid token: jwt token has expired', 'urn:dx:as:ExpiredAuthenticationToken'); } else { debug('Error in JWT ', err.message); debug('Or JWT secret misconfigured'); @@ -73,8 +74,8 @@ async function validateAccessIDM(req, res, tokens) { } } catch (e) { debug(e); - if (e.status === 404 || e.status === 401) { - return access.deny(res); + if (e.type) { + return access.deny(res, e.message, e.type); } else { return access.internalError(res, e, 'IDM'); } @@ -127,7 +128,7 @@ exports.restricted_access = function (req, res) { if (tokens.authToken === undefined) { debug('Auth-token not found in request header'); res.set('WWW-Authenticate', 'IDM uri = ' + config.idm_host); - access.deny(res, 'Auth-token not found in request header'); + access.deny(res, 'Auth-token not found in request header', 'urn:dx:as:MissingAuthenticationToken'); return; } if (config.magic_key && config.magic_key === tokens.authToken) { diff --git a/lib/access_functions.js b/lib/access_functions.js index d2a149e..f64ebe7 100644 --- a/lib/access_functions.js +++ b/lib/access_functions.js @@ -9,6 +9,8 @@ const config_service = require('./config_service'); const config = config_service.get_config(); const debug = require('debug')('pep-proxy:access'); const got = require('got'); +const StatusCodes = require('http-status-codes').StatusCodes; +const getReasonPhrase = require('http-status-codes').getReasonPhrase const PROXY_URL = (config.app.ssl ? 'https://' : 'http://') + config.app.host + ':' + config.app.port; @@ -49,7 +51,7 @@ exports.adjudicate = function (req, res, decision) { if (decision) { permit(req, res); } else { - deny(res); + deny(res, 'User access-token not authorized', 'urn:dx:as:InvalidRole'); } }; @@ -58,10 +60,15 @@ exports.adjudicate = function (req, res, decision) { * * @param res - the response to return * @param message - the error message to display + * @param type - the error type */ -function deny(res, message = 'User access-token not authorized') { - debug('Denied.'); - res.status(401).send(message); +function deny(res, message, type) { + debug('Denied. ' + type); + res.status(StatusCodes.UNAUTHORIZED).send({ + type, + title: getReasonPhrase(StatusCodes.UNAUTHORIZED), + detail: message + }); } /** @@ -75,7 +82,10 @@ function deny(res, message = 'User access-token not authorized') { function internalError(res, e, component) { const message = e ? e.message : undefined; debug(`Error in ${component} communication `, message ? message : e); - res.status(503).send(`Error in ${component} communication`); + res.status(503).send({ + title: getReasonPhrase(StatusCodes.INTERNAL_SERVER_ERROR), + detail: message + }); } /** @@ -86,7 +96,7 @@ function internalError(res, e, component) { */ function nginxResponse(req, res) { debug('Permitted. Response 204'); - res.sendStatus(204); + res.sendStatus(StatusCodes.NO_CONTENT); } /** diff --git a/lib/authorization_functions.js b/lib/authorization_functions.js index 82b3f25..3e7eac6 100644 --- a/lib/authorization_functions.js +++ b/lib/authorization_functions.js @@ -101,7 +101,7 @@ function authzforceAuthorize(req, res, authToken) { .catch((e) => { if (e.status === 404) { debug('Domain not found: ', e); - access.deny(res); + access.deny(res, 'Domain not Found', 'urn:dx:as:UnauthorizedEndpoint'); } else { access.internalError(res, e, 'Authzforce'); } diff --git a/lib/pdp/keyrock.js b/lib/pdp/keyrock.js index 8426241..5386b40 100644 --- a/lib/pdp/keyrock.js +++ b/lib/pdp/keyrock.js @@ -9,6 +9,8 @@ const config_service = require('../config_service'); const debug = require('debug')('pep-proxy:IDM-Client'); const cache = require('../cache'); const got = require('got'); +const StatusCodes = require('http-status-codes').StatusCodes; + /** * Can the Keyrock PDP check payloads? */ @@ -155,10 +157,10 @@ exports.authenticateUser = function (token, action, resource, tenant) { const organizations = user.organizations ? user.organizations.map((elem) => elem.id) : []; if (!checkApplication(user.app_id, user.trusted_apps)) { debug('User not authorized in application', config_service.get_config().pep.app_id); - return reject({ status: 401, message: 'User not authorized in application' }); + return reject({ type: 'urn:dx:as:InvalidRole', message: 'User not have the required role in the application' }); } else if (!checkOrganizations(organizations, authOrgToken)) { debug('User does not belong to the organization', authOrgToken); - return reject({ status: 401, message: 'User does not belong to the organization' }); + return reject({ type: 'urn:dx:as:InvalidRole', message: 'User does not belong to the organization' }); } // Keyrock is in use as an IDM - store the User cache.storeUser(authToken, user); @@ -170,10 +172,10 @@ exports.authenticateUser = function (token, action, resource, tenant) { }) .catch((error) => { if (error instanceof got.HTTPError) { - return reject({ status: 401, message: 'User not authorized in application' }); + return reject({ type: 'urn:dx:as:InvalidAuthenticationToken', message: 'User not authorized in the application' }); } debug('Error in IDM communication ', error); - return reject({ status: 503, message: error.message }); + return reject({message: error.message }); }); }); }; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..ca7caf3 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,7557 @@ +{ + "name": "fiware-pep-proxy", + "version": "8.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@azu/format-text": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azu/format-text/-/format-text-1.0.1.tgz", + "integrity": "sha1-aWc1CpRkD2sChVFpvYl85U1s6+I=", + "dev": true + }, + "@azu/style-format": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@azu/style-format/-/style-format-1.0.0.tgz", + "integrity": "sha1-5wGH+Khi4ZGxvObAJo8TrNOlayA=", + "dev": true, + "requires": { + "@azu/format-text": "^1.0.1" + } + }, + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/core": { + "version": "7.11.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.6.tgz", + "integrity": "sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.11.6", + "@babel/helper-module-transforms": "^7.11.0", + "@babel/helpers": "^7.10.4", + "@babel/parser": "^7.11.5", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.11.5", + "@babel/types": "^7.11.5", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.11.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz", + "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==", + "dev": true, + "requires": { + "@babel/types": "^7.11.5", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz", + "integrity": "sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==", + "dev": true, + "requires": { + "@babel/types": "^7.11.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", + "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-module-transforms": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz", + "integrity": "sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-simple-access": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/template": "^7.10.4", + "@babel/types": "^7.11.0", + "lodash": "^4.17.19" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", + "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-replace-supers": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", + "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.10.4", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-simple-access": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", + "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", + "dev": true, + "requires": { + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", + "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", + "dev": true, + "requires": { + "@babel/types": "^7.11.0" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "@babel/helpers": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz", + "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==", + "dev": true, + "requires": { + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } + } + }, + "@babel/parser": { + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", + "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==", + "dev": true + }, + "@babel/template": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", + "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/traverse": { + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz", + "integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.11.5", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.11.5", + "@babel/types": "^7.11.5", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "@eslint/eslintrc": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz", + "integrity": "sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true + }, + "@sindresorhus/is": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", + "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==" + }, + "@sinonjs/commons": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", + "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@sinonjs/formatio": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-5.0.1.tgz", + "integrity": "sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1", + "@sinonjs/samsam": "^5.0.2" + } + }, + "@sinonjs/samsam": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.1.0.tgz", + "integrity": "sha512-42nyaQOVunX5Pm6GRJobmzbS7iLI+fhERITnETXzzwDZh+TtDr/Au3yAvXVjFmZ4wEUaE4Y3NFZfKv0bV0cbtg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "@sinonjs/text-encoding": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", + "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "@textlint/ast-node-types": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-4.3.4.tgz", + "integrity": "sha512-Grq+vJuNH7HCa278eFeiqJvowrD+onMCoG2ctLyoN+fXYIQGIr1/8fo8AcIg+VM16Kga+N6Y1UWNOWPd8j1nFg==", + "dev": true + }, + "@textlint/ast-tester": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@textlint/ast-tester/-/ast-tester-2.2.4.tgz", + "integrity": "sha512-676xpY3/+Xa+tPaiUPaD4sl//+p3xsnSPYLrQjSmHWXX78F3MwAWd/Lek+SCn4wwvf1tCIx0SPtjfOCa6ru8qw==", + "dev": true, + "requires": { + "@textlint/ast-node-types": "^4.3.4" + } + }, + "@textlint/ast-traverse": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@textlint/ast-traverse/-/ast-traverse-2.2.5.tgz", + "integrity": "sha512-YduGVn7iaUYOfo7TwHO4b0K/qQpj61Ol/M884ck3vetNd0zBxpHO3GpQKW87SZGGtlsBa9v5Suz/yypnlPo3Og==", + "dev": true, + "requires": { + "@textlint/ast-node-types": "^4.3.4" + } + }, + "@textlint/feature-flag": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@textlint/feature-flag/-/feature-flag-3.2.4.tgz", + "integrity": "sha512-ABhbZ5rfkwa/kTBFxVmeMzE1flcnUjLJ5LTZvOaxH/pElfLLN1J4FEmAZTRCvXGAB498II6nkM2CqcikbKzh6A==", + "dev": true, + "requires": { + "map-like": "^2.0.0" + } + }, + "@textlint/fixer-formatter": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@textlint/fixer-formatter/-/fixer-formatter-3.2.5.tgz", + "integrity": "sha512-fh6XiLbX9WF8+79g20qb1I85k/Yc9+h7LRccmaLzTBjVQDNYxX5BtfvGsY0Vf5tBZKT2xFZH4eSLH/EWoL3weg==", + "dev": true, + "requires": { + "@textlint/module-interop": "^1.1.4", + "@textlint/types": "^1.4.5", + "chalk": "^1.1.3", + "debug": "^4.1.1", + "diff": "^4.0.1", + "is-file": "^1.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^6.0.0", + "text-table": "^0.2.0", + "try-resolve": "^1.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "@textlint/kernel": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@textlint/kernel/-/kernel-3.3.6.tgz", + "integrity": "sha512-M2ciQDAo5W6rpRADzGnMXyxhvJ+lnqYG9iHrqmfDQ2MA0VcolWuA37H67/UstqTs3NYaGC7RZkq9FAV//pl30w==", + "dev": true, + "requires": { + "@textlint/ast-node-types": "^4.3.4", + "@textlint/ast-tester": "^2.2.4", + "@textlint/ast-traverse": "^2.2.5", + "@textlint/feature-flag": "^3.2.4", + "@textlint/types": "^1.4.5", + "@textlint/utils": "^1.1.4", + "debug": "^4.1.1", + "deep-equal": "^1.1.1", + "map-like": "^2.0.0", + "structured-source": "^3.0.2" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@textlint/linter-formatter": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-3.2.5.tgz", + "integrity": "sha512-oy5RcBWrC2d7r0Mjw/FBH8cvQuOaCB5PeOPG0Pp44Yr5JbIGLXfh84umHQOTCmxfRxw3ccnUfA9wjbxuL8rWOQ==", + "dev": true, + "requires": { + "@azu/format-text": "^1.0.1", + "@azu/style-format": "^1.0.0", + "@textlint/module-interop": "^1.1.4", + "@textlint/types": "^1.4.5", + "chalk": "^1.0.0", + "concat-stream": "^1.5.1", + "debug": "^4.1.1", + "is-file": "^1.0.0", + "js-yaml": "^3.2.4", + "optionator": "^0.8.1", + "pluralize": "^2.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^6.0.0", + "table": "^3.7.8", + "text-table": "^0.2.0", + "try-resolve": "^1.0.1", + "xml-escape": "^1.0.0" + }, + "dependencies": { + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true, + "requires": { + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "pluralize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-2.0.0.tgz", + "integrity": "sha1-crcmqm+sHt7uQiVsfY3CVrM1Z38=", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "slice-ansi": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", + "dev": true + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "table": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", + "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", + "dev": true, + "requires": { + "ajv": "^4.7.0", + "ajv-keywords": "^1.0.0", + "chalk": "^1.1.1", + "lodash": "^4.0.0", + "slice-ansi": "0.0.4", + "string-width": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + } + } + }, + "@textlint/markdown-to-ast": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@textlint/markdown-to-ast/-/markdown-to-ast-6.2.5.tgz", + "integrity": "sha512-9vlQbylGjnnRGev3yt9ntNy6I9FQH3p+MkbijybKnwobK/msoAX9sThDHOMbGM24PsUHxcDjktDlj2vHN/pwDA==", + "dev": true, + "requires": { + "@textlint/ast-node-types": "^4.3.4", + "debug": "^4.1.1", + "remark-frontmatter": "^1.2.0", + "remark-parse": "^5.0.0", + "structured-source": "^3.0.2", + "traverse": "^0.6.6", + "unified": "^6.1.6" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "parse-entities": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.2.tgz", + "integrity": "sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg==", + "dev": true, + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, + "remark-parse": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-5.0.0.tgz", + "integrity": "sha512-b3iXszZLH1TLoyUzrATcTQUZrwNl1rE70rVdSruJFlDaJ9z5aMkhrG43Pp68OgfHndL/ADz6V69Zow8cTQu+JA==", + "dev": true, + "requires": { + "collapse-white-space": "^1.0.2", + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "is-word-character": "^1.0.0", + "markdown-escapes": "^1.0.0", + "parse-entities": "^1.1.0", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "trim": "0.0.1", + "trim-trailing-lines": "^1.0.0", + "unherit": "^1.0.4", + "unist-util-remove-position": "^1.0.0", + "vfile-location": "^2.0.0", + "xtend": "^4.0.1" + } + }, + "unified": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-6.2.0.tgz", + "integrity": "sha512-1k+KPhlVtqmG99RaTbAv/usu85fcSRu3wY8X+vnsEhIxNP5VbVIDiXnLqyKIG+UMdyTg0ZX9EI6k2AfjJkHPtA==", + "dev": true, + "requires": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^1.1.0", + "trough": "^1.0.0", + "vfile": "^2.0.0", + "x-is-string": "^0.1.0" + } + }, + "unist-util-is": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", + "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==", + "dev": true + }, + "unist-util-remove-position": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.4.tgz", + "integrity": "sha512-tLqd653ArxJIPnKII6LMZwH+mb5q+n/GtXQZo6S6csPRs5zB0u79Yw8ouR3wTw8wxvdJFhpP6Y7jorWdCgLO0A==", + "dev": true, + "requires": { + "unist-util-visit": "^1.1.0" + } + }, + "unist-util-stringify-position": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz", + "integrity": "sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ==", + "dev": true + }, + "unist-util-visit": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", + "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==", + "dev": true, + "requires": { + "unist-util-visit-parents": "^2.0.0" + } + }, + "unist-util-visit-parents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz", + "integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==", + "dev": true, + "requires": { + "unist-util-is": "^3.0.0" + } + }, + "vfile": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-2.3.0.tgz", + "integrity": "sha512-ASt4mBUHcTpMKD/l5Q+WJXNtshlWxOogYyGYYrg4lt/vuRjC1EFQtlAofL5VmtVNIZJzWYFJjzGWZ0Gw8pzW1w==", + "dev": true, + "requires": { + "is-buffer": "^1.1.4", + "replace-ext": "1.0.0", + "unist-util-stringify-position": "^1.0.0", + "vfile-message": "^1.0.0" + } + }, + "vfile-location": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.6.tgz", + "integrity": "sha512-sSFdyCP3G6Ka0CEmN83A2YCMKIieHx0EDaj5IDP4g1pa5ZJ4FJDvpO0WODLxo4LUX4oe52gmSCK7Jw4SBghqxA==", + "dev": true + }, + "vfile-message": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-1.1.1.tgz", + "integrity": "sha512-1WmsopSGhWt5laNir+633LszXvZ+Z/lxveBf6yhGsqnQIhlhzooZae7zV6YVM1Sdkw68dtAW3ow0pOdPANugvA==", + "dev": true, + "requires": { + "unist-util-stringify-position": "^1.1.1" + } + } + } + }, + "@textlint/module-interop": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-1.1.4.tgz", + "integrity": "sha512-9M3kYG5nBoD2lhp05sqi6fieNU6rBcf+A8Trp+4d8o5uJ4RRsWeRtAQMWM7Tv15onqIITRq7fm3la7xovVB9KA==", + "dev": true + }, + "@textlint/text-to-ast": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@textlint/text-to-ast/-/text-to-ast-3.2.4.tgz", + "integrity": "sha512-uIiNg52OdQ3Fn8aOYaV7BLW2QakNsmf4doypIwrW4q+gHYQ3jxdPHHkq6RxuYoO112vO40M3zmAoEZmM1qmPhw==", + "dev": true, + "requires": { + "@textlint/ast-node-types": "^4.3.4" + } + }, + "@textlint/textlint-plugin-markdown": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/@textlint/textlint-plugin-markdown/-/textlint-plugin-markdown-5.2.6.tgz", + "integrity": "sha512-S65wy2npaBLT7pwPPlrN9Pw40hOJsxiW+T6peMJOFEMLRem5qlCIlT+02Wlf0Rrtr6/gKDckpphTUiZT1+xRnQ==", + "dev": true, + "requires": { + "@textlint/markdown-to-ast": "^6.2.5" + } + }, + "@textlint/textlint-plugin-text": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@textlint/textlint-plugin-text/-/textlint-plugin-text-4.2.6.tgz", + "integrity": "sha512-KCgb5GVjoEDIi37UpQN6kFciiouyATNYrj/JufCeLNJEcVcxSm12EoFRKjpXpXmTOVqZUyGnIDU797z1usAZDw==", + "dev": true, + "requires": { + "@textlint/text-to-ast": "^3.2.4" + } + }, + "@textlint/types": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@textlint/types/-/types-1.4.5.tgz", + "integrity": "sha512-7pA1rdiw1jsDNGwxupMC6fPlRNAHY6fKZ3s+jAY53o6RroOSR+5qO0sAjJ26lsSOhveH8imZzyyD08dk58IVJQ==", + "dev": true, + "requires": { + "@textlint/ast-node-types": "^4.3.4" + } + }, + "@textlint/utils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@textlint/utils/-/utils-1.1.4.tgz", + "integrity": "sha512-KmU+kGi7vG5toUhNdLHHPxyVN1mNGcjMVe1tNDEXT1wU/3oqA96bunElrROWHYw5iNt1QVRaIAtNeMVyzyAdVA==", + "dev": true + }, + "@types/cacheable-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz", + "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==", + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" + }, + "@types/keyv": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.2.tgz", + "integrity": "sha512-/FvAK2p4jQOaJ6CGDHJTqZcUtbZe820qIeTg7o0Shg7drB4JHeL+V/dhSaly7NXx6u8eSee+r7coT+yuJEvDLg==", + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "16.4.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.4.13.tgz", + "integrity": "sha512-bLL69sKtd25w7p1nvg9pigE4gtKVpGTPojBFLMkGHXuUgap2sLqQt2qUnqmVCDfzGUL0DRNZP+1prIZJbMeAXg==" + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "requires": { + "@types/node": "*" + } + }, + "@types/unist": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", + "integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==", + "dev": true + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "dev": true + }, + "adverb-where": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/adverb-where/-/adverb-where-0.0.9.tgz", + "integrity": "sha1-CcXN3Y1QO5/l924LjcXHCo8ZPjQ=", + "dev": true + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.5", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz", + "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", + "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", + "dev": true + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "requires": { + "default-require-extensions": "^3.0.0" + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "array.prototype.map": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz", + "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.4" + } + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz", + "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==", + "dev": true + }, + "bail": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", + "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "binary-extensions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", + "dev": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "boundary": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/boundary/-/boundary-1.0.1.tgz", + "integrity": "sha1-TWfcJgLAzBbdm85+v4fpSCkPWBI=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==" + }, + "cacheable-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", + "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + } + }, + "caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "requires": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "ccount": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.5.tgz", + "integrity": "sha512-MOli1W+nfbPLlKEhInaxhRdp7KVLFxLN5ykwzHgLsLI3H3gs5jjFAK4Eoj3OzzcxCtumDaI8onoVDeQyWaNTkw==", + "dev": true + }, + "chai": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.0", + "type-detect": "^4.0.5" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "dev": true + }, + "character-entities-html4": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.4.tgz", + "integrity": "sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g==", + "dev": true + }, + "character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "dev": true + }, + "character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "dev": true + }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=", + "dev": true + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, + "chokidar": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", + "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.4.0" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + } + } + }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "co": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/co/-/co-3.1.0.tgz", + "integrity": "sha1-TqVOpaCJOBUxheFSEMaNkJK8G3g=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collapse-white-space": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz", + "integrity": "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==", + "dev": true + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.1.0.tgz", + "integrity": "sha512-wl7PNrYWd2y5mp1OK/LhTlv8Ff4kQJQRXXAvF+uU/TPNiVJUxZLRYGj/B0y/lPGAVcSbJqH2Za/cvHmrPMC8mA==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "compare-versions": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", + "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "cosmiconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", + "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "coveralls": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.1.0.tgz", + "integrity": "sha512-sHxOu2ELzW8/NC1UP5XVLbZDzO4S3VxfFye3XYCznopHy02YjNkHcj5bKaVw2O7hVaBdBjEdQGpie4II1mWhuQ==", + "dev": true, + "requires": { + "js-yaml": "^3.13.1", + "lcov-parse": "^1.0.0", + "log-driver": "^1.2.7", + "minimist": "^1.2.5", + "request": "^2.88.2" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + } + } + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "default-require-extensions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", + "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", + "dev": true, + "requires": { + "strip-bom": "^4.0.0" + } + }, + "defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "e-prime": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/e-prime/-/e-prime-0.10.4.tgz", + "integrity": "sha512-tzBmM2mFSnAq5BuxPSyin6qXb3yMe1wufJN7L7ZPcEWS5S+jI2dhKQEoqHVEcSMMXo/j5lcWpX5jzA6wLSmX6w==", + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "errorhandler": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz", + "integrity": "sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A==", + "requires": { + "accepts": "~1.3.7", + "escape-html": "~1.0.3" + } + }, + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "object.assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", + "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + } + } + }, + "es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true + }, + "es-get-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", + "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", + "dev": true, + "requires": { + "es-abstract": "^1.17.4", + "has-symbols": "^1.0.1", + "is-arguments": "^1.0.4", + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-string": "^1.0.5", + "isarray": "^2.0.5" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.10.0.tgz", + "integrity": "sha512-BDVffmqWl7JJXqCjAK6lWtcQThZB/aP1HXSH1JKwGwv0LQEdvpR7qzNrUT487RM39B5goWuboFad5ovMBmD8yA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@eslint/eslintrc": "^0.1.3", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^1.3.0", + "espree": "^7.3.0", + "esquery": "^1.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + } + } + }, + "eslint-config-tamia": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/eslint-config-tamia/-/eslint-config-tamia-7.2.5.tgz", + "integrity": "sha512-cAHAQE+bwfC5kJKDMln6ypL1g7EK7yZ3BxvS9JsOmSrem3UvoSLm353EOOBp1sBl4i/Z4AjaQPLasBG37b9oKA==", + "dev": true + }, + "eslint-plugin-prettier": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz", + "integrity": "sha512-jZDa8z76klRqo+TdGDTFJSavwbnWK2ZpqGKNZ+VvweMW516pDUMmQ2koXvxEE4JhzNvTv+radye/bWGBmA6jmg==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + }, + "espree": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz", + "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.3.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "execa": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", + "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "dev": true, + "requires": { + "format": "^0.2.0" + } + }, + "figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", + "dev": true + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "find-versions": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz", + "integrity": "sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==", + "dev": true, + "requires": { + "semver-regex": "^2.0.0" + } + }, + "flat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", + "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "dev": true, + "requires": { + "is-buffer": "~2.0.3" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=", + "dev": true + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fromentries": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.2.1.tgz", + "integrity": "sha512-Xu2Qh8yqYuDhQGOhD5iJGninErSfI9A3FrriD3tjUgV5VbJFeH8vfgZ9HnC6jWN80QDVNQK5vmxRAmEAp7Mevw==", + "dev": true + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-monkey": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.1.tgz", + "integrity": "sha512-fcSa+wyTqZa46iWweI7/ZiUfegOZl0SG8+dltIwFXo7+zYU9J9kpS3NB6pZcSlJdhvIwp81Adx2XhZorncxiaA==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, + "get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-stdin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", + "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=", + "dev": true + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, + "get-url-origin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-url-origin/-/get-url-origin-1.0.1.tgz", + "integrity": "sha512-MMSKo16gB2+6CjWy55jNdIAqUEaKgw3LzZCb8wVVtFrhoQ78EXyuYXxDdn3COI3A4Xr4ZfM3fZa9RTjO6DOTxw==", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "got": { + "version": "11.8.2", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz", + "integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==", + "requires": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.1", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "dev": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + } + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "hasha": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.1.tgz", + "integrity": "sha512-x15jnRSHTi3VmH+oHtVb9kgU/HuKOK8mjK8iCL3dPQXh4YJlUb9YSI8ZLiiqLAIvY2wuDIlZYZppy8vB2XISkQ==", + "dev": true, + "requires": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "hoek": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", + "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==" + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "http-status-codes": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.1.4.tgz", + "integrity": "sha512-MZVIsLKGVOVE1KEnldppe6Ij+vmemMuApDfjhVSLzyYP+td0bREEYyAoIw9yFePoBXManCuBqmiNP5FqJS5Xkg==" + }, + "http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + } + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, + "husky": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/husky/-/husky-4.3.0.tgz", + "integrity": "sha512-tTMeLCLqSBqnflBZnlVDhpaIMucSGaYyX6855jM4AguGeWCeSzNdb1mfyWduTZ3pe3SJVvVWGL0jO1iKZVPfTA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "ci-info": "^2.0.0", + "compare-versions": "^3.6.0", + "cosmiconfig": "^7.0.0", + "find-versions": "^3.2.0", + "opencollective-postinstall": "^2.0.2", + "pkg-dir": "^4.2.0", + "please-upgrade-node": "^3.2.0", + "slash": "^3.0.0", + "which-pm-runs": "^1.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", + "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==", + "dev": true + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "dev": true + }, + "is-alphanumeric": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz", + "integrity": "sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ=", + "dev": true + }, + "is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "dev": true, + "requires": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + } + }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-buffer": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", + "dev": true + }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" + }, + "is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "dev": true + }, + "is-empty": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-empty/-/is-empty-1.2.0.tgz", + "integrity": "sha1-3pu1snhzigWgsJpX4ftNSjQan2s=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-file/-/is-file-1.0.0.tgz", + "integrity": "sha1-KKRM+9nT2xkwRfIrZfzo7fliBZY=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-hex": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-hex/-/is-hex-1.1.3.tgz", + "integrity": "sha1-RAJZwHloHgNy5SFWqmeiM8OXXe4=" + }, + "is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "dev": true + }, + "is-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", + "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", + "dev": true + }, + "is-negative-zero": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=" + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", + "dev": true + }, + "is-set": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", + "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", + "dev": true + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-whitespace-character": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz", + "integrity": "sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-word-character": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz", + "integrity": "sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==", + "dev": true + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "isemail": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", + "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", + "requires": { + "punycode": "2.x.x" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "requires": { + "append-transform": "^2.0.0" + } + }, + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-processinfo": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", + "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", + "dev": true, + "requires": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.0", + "istanbul-lib-coverage": "^3.0.0-alpha.1", + "make-dir": "^3.0.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^3.3.3" + }, + "dependencies": { + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "iterate-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz", + "integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==", + "dev": true + }, + "iterate-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", + "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", + "dev": true, + "requires": { + "es-get-iterator": "^1.0.2", + "iterate-iterator": "^1.0.1" + } + }, + "joi": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-13.7.0.tgz", + "integrity": "sha512-xuY5VkHfeOYK3Hdi91ulocfuFopwgbSORmIwzcwHKESQhC7w1kD5jaVSPnqDxS2I8t3RZ9omCKAxNwXN5zG1/Q==", + "requires": { + "hoek": "5.x.x", + "isemail": "3.x.x", + "topo": "3.x.x" + }, + "dependencies": { + "hoek": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.4.tgz", + "integrity": "sha512-Alr4ZQgoMlnere5FZJsIyfIjORBqZll5POhDsF4q64dPuJR6rNxXdDxtHSQq8OXRurhmx+PWYEE8bXRROY8h0w==" + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "~0.0.0" + } + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "just-extend": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.1.tgz", + "integrity": "sha512-aWgeGFW67BP3e5181Ep1Fv2v8z//iBJfrvyTnq8wG86vEESwmonn1zPBJ0VfmT9CJq2FIT0VsETtrNFm2a+SHA==", + "dev": true + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "keyv": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", + "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", + "requires": { + "json-buffer": "3.0.1" + } + }, + "lcov-parse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz", + "integrity": "sha1-6w1GtUER68VhrLTECO+TY73I9+A=", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "libnpmconfig": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/libnpmconfig/-/libnpmconfig-1.2.1.tgz", + "integrity": "sha512-9esX8rTQAHqarx6qeZqmGQKBNZR5OIbl/Ayr0qQDy3oXja2iFVQQI81R6GZ2a02bSNZ9p3YOGX1O6HHCb1X7kA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "find-up": "^3.0.0", + "ini": "^1.3.5" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } + } + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "lint-staged": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.4.0.tgz", + "integrity": "sha512-uaiX4U5yERUSiIEQc329vhCTDDwUcSvKdRLsNomkYLRzijk3v8V9GWm2Nz0RMVB87VcuzLvtgy6OsjoH++QHIg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "cli-truncate": "^2.1.0", + "commander": "^6.0.0", + "cosmiconfig": "^7.0.0", + "debug": "^4.1.1", + "dedent": "^0.7.0", + "enquirer": "^2.3.6", + "execa": "^4.0.3", + "listr2": "^2.6.0", + "log-symbols": "^4.0.0", + "micromatch": "^4.0.2", + "normalize-path": "^3.0.0", + "please-upgrade-node": "^3.2.0", + "string-argv": "0.3.1", + "stringify-object": "^3.3.0" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "listr2": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-2.6.2.tgz", + "integrity": "sha512-6x6pKEMs8DSIpA/tixiYY2m/GcbgMplMVmhQAaLFxEtNSKLeWTGjtmU57xvv6QCm2XcqzyNXL/cTSVf4IChCRA==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "cli-truncate": "^2.1.0", + "figures": "^3.2.0", + "indent-string": "^4.0.0", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rxjs": "^6.6.2", + "through": "^2.3.8" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + } + } + }, + "load-plugin": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/load-plugin/-/load-plugin-3.0.0.tgz", + "integrity": "sha512-od7eKCCZ62ITvFf8nHHrIiYmgOHb4xVNDRDqxBWSaao5FZyyZVX8OmRCbwjDGPrSrgIulwPNyBsWCGnhiDC0oQ==", + "dev": true, + "requires": { + "libnpmconfig": "^1.0.0", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "dev": true + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, + "lodash.set": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", + "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", + "dev": true + }, + "log-driver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", + "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", + "dev": true + }, + "log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "requires": { + "chalk": "^4.0.0" + } + }, + "log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + } + } + }, + "longest-streak": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.4.tgz", + "integrity": "sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==", + "dev": true + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-like": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-like/-/map-like-2.0.0.tgz", + "integrity": "sha1-lEltSa0zPA3DI0snrbvR6FNZU7Q=", + "dev": true + }, + "markdown-escapes": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz", + "integrity": "sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==", + "dev": true + }, + "markdown-extensions": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-1.1.1.tgz", + "integrity": "sha512-WWC0ZuMzCyDHYCasEGs4IPvLyTGftYwh6wIEOULOF0HXcqZlhwRzrK0w2VUlxWA98xnvb/jszw4ZSkJ6ADpM6Q==", + "dev": true + }, + "markdown-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", + "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", + "dev": true, + "requires": { + "repeat-string": "^1.0.0" + } + }, + "md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dev": true, + "requires": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + }, + "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + } + } + }, + "mdast-comment-marker": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/mdast-comment-marker/-/mdast-comment-marker-1.1.2.tgz", + "integrity": "sha512-vTFXtmbbF3rgnTh3Zl3irso4LtvwUq/jaDvT2D1JqTGAwaipcS7RpTxzi6KjoRqI9n2yuAhzLDAC8xVTF3XYVQ==", + "dev": true + }, + "mdast-util-compact": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-2.0.1.tgz", + "integrity": "sha512-7GlnT24gEwDrdAwEHrU4Vv5lLWrEer4KOkAiKT9nYstsTad7Oc1TwqT2zIMKRdZF7cTuaf+GA1E4Kv7jJh8mPA==", + "dev": true, + "requires": { + "unist-util-visit": "^2.0.0" + } + }, + "mdast-util-heading-style": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/mdast-util-heading-style/-/mdast-util-heading-style-1.0.6.tgz", + "integrity": "sha512-8ZuuegRqS0KESgjAGW8zTx4tJ3VNIiIaGFNEzFpRSAQBavVc7AvOo9I4g3crcZBfYisHs4seYh0rAVimO6HyOw==", + "dev": true + }, + "mdast-util-to-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz", + "integrity": "sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A==", + "dev": true + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + } + }, + "memfs": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.2.0.tgz", + "integrity": "sha512-f/xxz2TpdKv6uDn6GtHee8ivFyxwxmPuXatBb1FBwxYNuVpbM3k/Y1Z+vC0mH/dIXXrukYfe3qe5J32Dfjg93A==", + "dev": true, + "requires": { + "fs-monkey": "1.0.1" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "misspellings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/misspellings/-/misspellings-1.1.0.tgz", + "integrity": "sha1-U9UAJmy9Cc2p2UxM85LmBYm1syQ=", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "mocha": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.3.tgz", + "integrity": "sha512-ZbaYib4hT4PpF4bdSO2DohooKXIn4lDeiYqB+vTmCdr6l2woW0b6H3pf5x4sM5nwQMru9RvjjHYWVGltR50ZBw==", + "dev": true, + "requires": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.4.2", + "debug": "4.1.1", + "diff": "4.0.2", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.14.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.2", + "object.assign": "4.1.0", + "promise.allsettled": "1.0.2", + "serialize-javascript": "4.0.0", + "strip-json-comments": "3.0.1", + "supports-color": "7.1.0", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.0.0", + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "1.6.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "p-limit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", + "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "strip-json-comments": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "mocha-lcov-reporter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/mocha-lcov-reporter/-/mocha-lcov-reporter-1.3.0.tgz", + "integrity": "sha1-Rpve9PivyaEWBW8HnfYYLQr7A4Q=", + "dev": true + }, + "morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "requires": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "nan": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==" + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "nise": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/nise/-/nise-4.0.4.tgz", + "integrity": "sha512-bTTRUNlemx6deJa+ZyoCUTRvH3liK5+N6VQZ4NIw90AgDXY6iPnsqplNFf6STcj+ePk0H/xqxnP75Lr0J0Fq3A==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0", + "@sinonjs/fake-timers": "^6.0.0", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "requires": { + "isarray": "0.0.1" + } + } + } + }, + "no-cliches": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/no-cliches/-/no-cliches-0.1.1.tgz", + "integrity": "sha512-mYihjs47X5+N71CN3P+QBrEIBuclIfMMpgWEpkmLqFPvrOXdzokvDlhbLfjdBNZOqYgniaeZC6J1ZCgxFdyvXw==", + "dev": true + }, + "nock": { + "version": "13.0.4", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.0.4.tgz", + "integrity": "sha512-alqTV8Qt7TUbc74x1pKRLSENzfjp4nywovcJgi/1aXDiUxXdt7TkruSTF5MDWPP7UoPVgea4F9ghVdmX0xxnSA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash.set": "^4.3.2", + "propagate": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "node-cache": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", + "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", + "requires": { + "clone": "2.x" + } + }, + "node-expat": { + "version": "2.3.18", + "resolved": "https://registry.npmjs.org/node-expat/-/node-expat-2.3.18.tgz", + "integrity": "sha512-9dIrDxXePa9HSn+hhlAg1wXkvqOjxefEbMclGxk2cEnq/Y3U7Qo5HNNqeo3fQ4bVmLhcdt3YN1TZy7WMZy4MHw==", + "requires": { + "bindings": "^1.5.0", + "nan": "^2.13.2" + } + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "dev": true + }, + "node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "requires": { + "process-on-spawn": "^1.0.0" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "requires": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "dependencies": { + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" + }, + "object-is": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.3.tgz", + "integrity": "sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "object.assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", + "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "dev": true + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==" + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-memoize": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-memoize/-/p-memoize-3.1.0.tgz", + "integrity": "sha512-e5tIvrsr7ydUUnxb534iQWtXxWgk/86IsH+H+nV4FHouIggBt4coXboKBt26o4lTu7JbEnGSeXdEsYR8BhAHFA==", + "dev": true, + "requires": { + "mem": "^4.3.0", + "mimic-fn": "^2.1.0" + } + }, + "p-queue": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.1.tgz", + "integrity": "sha512-miQiSxLYPYBxGkrldecZC18OTLjdUqnlRebGzPRiVxB8mco7usCmm7hFuxiTvp93K18JnLtE4KMMycjAu/cQQg==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.1.0" + } + }, + "p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dev": true, + "requires": { + "p-finally": "^1.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "dev": true, + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, + "parse-json": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "passive-voice": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/passive-voice/-/passive-voice-0.1.0.tgz", + "integrity": "sha1-Fv+RrkC6DpLEPmcXY/3IQqcCcLE=", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-to-glob-pattern": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-to-glob-pattern/-/path-to-glob-pattern-1.0.2.tgz", + "integrity": "sha1-Rz5qOikqnRP7rj7czuctO6uoxhk=", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "requires": { + "semver-compare": "^1.0.0" + } + }, + "pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.2.tgz", + "integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "requires": { + "fromentries": "^1.2.0" + } + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "promise.allsettled": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz", + "integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==", + "dev": true, + "requires": { + "array.prototype.map": "^1.0.1", + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "iterate-value": "^1.0.0" + } + }, + "propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "rc-config-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-3.0.0.tgz", + "integrity": "sha512-bwfUSB37TWkHfP+PPjb/x8BUjChFmmBK44JMfVnU7paisWqZl/o5k7ttCH+EQLnrbn2Aq8Fo1LAsyUiz+WF4CQ==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "js-yaml": "^3.12.0", + "json5": "^2.1.1", + "require-from-string": "^2.0.2" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "dependencies": { + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + } + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", + "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true + }, + "release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "dev": true, + "requires": { + "es6-error": "^4.0.1" + } + }, + "remark": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/remark/-/remark-12.0.1.tgz", + "integrity": "sha512-gS7HDonkdIaHmmP/+shCPejCEEW+liMp/t/QwmF0Xt47Rpuhl32lLtDV1uKWvGoq+kxr5jSgg5oAIpGuyULjUw==", + "dev": true, + "requires": { + "remark-parse": "^8.0.0", + "remark-stringify": "^8.0.0", + "unified": "^9.0.0" + } + }, + "remark-cli": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/remark-cli/-/remark-cli-8.0.1.tgz", + "integrity": "sha512-UaYeFI5qUAzkthUd8/MLBQD5OKM6jLN8GRvF6v+KF7xO/i1jQ+X2VqUSQAxWFYxZ8R25gM56GVjeoKOZ0EIr8A==", + "dev": true, + "requires": { + "markdown-extensions": "^1.1.0", + "remark": "^12.0.0", + "unified-args": "^8.0.0" + } + }, + "remark-frontmatter": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-1.3.3.tgz", + "integrity": "sha512-fM5eZPBvu2pVNoq3ZPW22q+5Ativ1oLozq2qYt9I2oNyxiUd/tDl0iLLntEVAegpZIslPWg1brhcP1VsaSVUag==", + "dev": true, + "requires": { + "fault": "^1.0.1", + "xtend": "^4.0.1" + } + }, + "remark-lint": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/remark-lint/-/remark-lint-7.0.1.tgz", + "integrity": "sha512-caZXo3qhuBxzvq9JSJFVQ/ERDq/6TJVgWn0KDwKOIJCGOuLXfQhby5XttUq+Rn7kLbNMtvwfWHJlte14LpaeXQ==", + "dev": true, + "requires": { + "remark-message-control": "^6.0.0" + } + }, + "remark-lint-final-newline": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/remark-lint-final-newline/-/remark-lint-final-newline-1.0.5.tgz", + "integrity": "sha512-rfLlW8+Fz2dqnaEgU4JwLA55CQF1T4mfSs/GwkkeUCGPenvEYwSkCN2KO2Gr1dy8qPoOdTFE1rSufLjmeTW5HA==", + "dev": true, + "requires": { + "unified-lint-rule": "^1.0.0" + } + }, + "remark-lint-hard-break-spaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/remark-lint-hard-break-spaces/-/remark-lint-hard-break-spaces-2.0.1.tgz", + "integrity": "sha512-Qfn/BMQFamHhtbfLrL8Co/dbYJFLRL4PGVXZ5wumkUO5f9FkZC2RsV+MD9lisvGTkJK0ZEJrVVeaPbUIFM0OAw==", + "dev": true, + "requires": { + "unified-lint-rule": "^1.0.0", + "unist-util-generated": "^1.1.0", + "unist-util-position": "^3.0.0", + "unist-util-visit": "^2.0.0" + } + }, + "remark-lint-list-item-bullet-indent": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/remark-lint-list-item-bullet-indent/-/remark-lint-list-item-bullet-indent-2.0.1.tgz", + "integrity": "sha512-tozDt9LChG1CvYJnBQH/oh45vNcHYBvg79ogvV0f8MtE/K0CXsM8EpfQ6pImFUdHpBV1op6aF6zPMrB0AkRhcQ==", + "dev": true, + "requires": { + "pluralize": "^8.0.0", + "unified-lint-rule": "^1.0.0", + "unist-util-generated": "^1.1.0", + "unist-util-position": "^3.0.0", + "unist-util-visit": "^2.0.0" + } + }, + "remark-lint-list-item-indent": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/remark-lint-list-item-indent/-/remark-lint-list-item-indent-2.0.1.tgz", + "integrity": "sha512-4IKbA9GA14Q9PzKSQI6KEHU/UGO36CSQEjaDIhmb9UOhyhuzz4vWhnSIsxyI73n9nl9GGRAMNUSGzr4pQUFwTA==", + "dev": true, + "requires": { + "pluralize": "^8.0.0", + "unified-lint-rule": "^1.0.0", + "unist-util-generated": "^1.1.0", + "unist-util-position": "^3.0.0", + "unist-util-visit": "^2.0.0" + } + }, + "remark-lint-no-auto-link-without-protocol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-auto-link-without-protocol/-/remark-lint-no-auto-link-without-protocol-2.0.1.tgz", + "integrity": "sha512-TFcXxzucsfBb/5uMqGF1rQA+WJJqm1ZlYQXyvJEXigEZ8EAxsxZGPb/gOQARHl/y0vymAuYxMTaChavPKaBqpQ==", + "dev": true, + "requires": { + "mdast-util-to-string": "^1.0.2", + "unified-lint-rule": "^1.0.0", + "unist-util-generated": "^1.1.0", + "unist-util-position": "^3.0.0", + "unist-util-visit": "^2.0.0" + } + }, + "remark-lint-no-blockquote-without-marker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-blockquote-without-marker/-/remark-lint-no-blockquote-without-marker-3.0.1.tgz", + "integrity": "sha512-sM953+u0zN90SGd2V5hWcFbacbpaROUslS5Q5F7/aa66/2rAwh6zVnrXc4pf7fFOpj7I9Xa8Aw+uB+3RJWwdrQ==", + "dev": true, + "requires": { + "unified-lint-rule": "^1.0.0", + "unist-util-generated": "^1.1.0", + "unist-util-position": "^3.0.0", + "unist-util-visit": "^2.0.0", + "vfile-location": "^3.0.0" + } + }, + "remark-lint-no-duplicate-definitions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-duplicate-definitions/-/remark-lint-no-duplicate-definitions-2.0.1.tgz", + "integrity": "sha512-XL22benJZB01m+aOse91nsu1IMFqeWJWme9QvoJuxIcBROO1BG1VoqLOkwNcawE/M/0CkvTo5rfx0eMlcnXOIw==", + "dev": true, + "requires": { + "unified-lint-rule": "^1.0.0", + "unist-util-generated": "^1.1.0", + "unist-util-position": "^3.0.0", + "unist-util-stringify-position": "^2.0.0", + "unist-util-visit": "^2.0.0" + } + }, + "remark-lint-no-heading-content-indent": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-heading-content-indent/-/remark-lint-no-heading-content-indent-2.0.1.tgz", + "integrity": "sha512-Jp0zCykGwg13z7XU4VuoFK7DN8bVZ1u3Oqu3hqECsH6LMASb0tW4zcTIc985kcVo3OQTRyb6KLQXL2ltOvppKA==", + "dev": true, + "requires": { + "mdast-util-heading-style": "^1.0.2", + "pluralize": "^8.0.0", + "unified-lint-rule": "^1.0.0", + "unist-util-generated": "^1.1.0", + "unist-util-position": "^3.0.0", + "unist-util-visit": "^2.0.0" + } + }, + "remark-lint-no-inline-padding": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-inline-padding/-/remark-lint-no-inline-padding-2.0.1.tgz", + "integrity": "sha512-a36UlPvRrLCgxjjG3YZA9VCDvLBcoBtGNyM04VeCPz+d9hHe+5Fs1C/jL+DRLCH7nff90jJ5C/9b8/LTwhjaWA==", + "dev": true, + "requires": { + "mdast-util-to-string": "^1.0.2", + "unified-lint-rule": "^1.0.0", + "unist-util-generated": "^1.1.0", + "unist-util-visit": "^2.0.0" + } + }, + "remark-lint-no-literal-urls": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-literal-urls/-/remark-lint-no-literal-urls-2.0.1.tgz", + "integrity": "sha512-IDdKtWOMuKVQIlb1CnsgBoyoTcXU3LppelDFAIZePbRPySVHklTtuK57kacgU5grc7gPM04bZV96eliGrRU7Iw==", + "dev": true, + "requires": { + "mdast-util-to-string": "^1.0.2", + "unified-lint-rule": "^1.0.0", + "unist-util-generated": "^1.1.0", + "unist-util-position": "^3.0.0", + "unist-util-visit": "^2.0.0" + } + }, + "remark-lint-no-shortcut-reference-image": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-shortcut-reference-image/-/remark-lint-no-shortcut-reference-image-2.0.1.tgz", + "integrity": "sha512-2jcZBdnN6ecP7u87gkOVFrvICLXIU5OsdWbo160FvS/2v3qqqwF2e/n/e7D9Jd+KTq1mR1gEVVuTqkWWuh3cig==", + "dev": true, + "requires": { + "unified-lint-rule": "^1.0.0", + "unist-util-generated": "^1.1.0", + "unist-util-visit": "^2.0.0" + } + }, + "remark-lint-no-shortcut-reference-link": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-shortcut-reference-link/-/remark-lint-no-shortcut-reference-link-2.0.1.tgz", + "integrity": "sha512-pTZbslG412rrwwGQkIboA8wpBvcjmGFmvugIA+UQR+GfFysKtJ5OZMPGJ98/9CYWjw9Z5m0/EktplZ5TjFjqwA==", + "dev": true, + "requires": { + "unified-lint-rule": "^1.0.0", + "unist-util-generated": "^1.1.0", + "unist-util-visit": "^2.0.0" + } + }, + "remark-lint-no-undefined-references": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-undefined-references/-/remark-lint-no-undefined-references-2.0.1.tgz", + "integrity": "sha512-tXM2ctFnduC3QcskrIePUajcjtNtBmo2dvlj4aoQJtQy09Soav/rYngb8u/SgERc6Irdmm5s55UAwR9CcSrzVg==", + "dev": true, + "requires": { + "collapse-white-space": "^1.0.4", + "unified-lint-rule": "^1.0.0", + "unist-util-generated": "^1.1.0", + "unist-util-visit": "^2.0.0" + } + }, + "remark-lint-no-unused-definitions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/remark-lint-no-unused-definitions/-/remark-lint-no-unused-definitions-2.0.1.tgz", + "integrity": "sha512-+BMc0BOjc364SvKYLkspmxDch8OaKPbnUGgQBvK0Bmlwy42baR4C9zhwAWBxm0SBy5Z4AyM4G4jKpLXPH40Oxg==", + "dev": true, + "requires": { + "unified-lint-rule": "^1.0.0", + "unist-util-generated": "^1.1.0", + "unist-util-visit": "^2.0.0" + } + }, + "remark-lint-ordered-list-marker-style": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/remark-lint-ordered-list-marker-style/-/remark-lint-ordered-list-marker-style-2.0.1.tgz", + "integrity": "sha512-Cnpw1Dn9CHn+wBjlyf4qhPciiJroFOEGmyfX008sQ8uGoPZsoBVIJx76usnHklojSONbpjEDcJCjnOvfAcWW1A==", + "dev": true, + "requires": { + "unified-lint-rule": "^1.0.0", + "unist-util-generated": "^1.1.0", + "unist-util-position": "^3.0.0", + "unist-util-visit": "^2.0.0" + } + }, + "remark-message-control": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/remark-message-control/-/remark-message-control-6.0.0.tgz", + "integrity": "sha512-k9bt7BYc3G7YBdmeAhvd3VavrPa/XlKWR3CyHjr4sLO9xJyly8WHHT3Sp+8HPR8lEUv+/sZaffL7IjMLV0f6BA==", + "dev": true, + "requires": { + "mdast-comment-marker": "^1.0.0", + "unified-message-control": "^3.0.0" + } + }, + "remark-parse": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz", + "integrity": "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==", + "dev": true, + "requires": { + "ccount": "^1.0.0", + "collapse-white-space": "^1.0.2", + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "is-word-character": "^1.0.0", + "markdown-escapes": "^1.0.0", + "parse-entities": "^2.0.0", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "trim": "0.0.1", + "trim-trailing-lines": "^1.0.0", + "unherit": "^1.0.4", + "unist-util-remove-position": "^2.0.0", + "vfile-location": "^3.0.0", + "xtend": "^4.0.1" + } + }, + "remark-preset-lint-recommended": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-preset-lint-recommended/-/remark-preset-lint-recommended-4.0.1.tgz", + "integrity": "sha512-zn+ImQbOVcAQVWLL0R0rFQ2Wy8JyWnuU3mJ8Zh0EVOckglcxByssvTbKqPih3Lh8ogpE38EfnC3a/vshj4Jx6A==", + "dev": true, + "requires": { + "remark-lint": "^7.0.0", + "remark-lint-final-newline": "^1.0.0", + "remark-lint-hard-break-spaces": "^2.0.0", + "remark-lint-list-item-bullet-indent": "^2.0.0", + "remark-lint-list-item-indent": "^2.0.0", + "remark-lint-no-auto-link-without-protocol": "^2.0.0", + "remark-lint-no-blockquote-without-marker": "^3.0.0", + "remark-lint-no-duplicate-definitions": "^2.0.0", + "remark-lint-no-heading-content-indent": "^2.0.0", + "remark-lint-no-inline-padding": "^2.0.0", + "remark-lint-no-literal-urls": "^2.0.0", + "remark-lint-no-shortcut-reference-image": "^2.0.0", + "remark-lint-no-shortcut-reference-link": "^2.0.0", + "remark-lint-no-undefined-references": "^2.0.0", + "remark-lint-no-unused-definitions": "^2.0.0", + "remark-lint-ordered-list-marker-style": "^2.0.0" + } + }, + "remark-stringify": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-8.1.1.tgz", + "integrity": "sha512-q4EyPZT3PcA3Eq7vPpT6bIdokXzFGp9i85igjmhRyXWmPs0Y6/d2FYwUNotKAWyLch7g0ASZJn/KHHcHZQ163A==", + "dev": true, + "requires": { + "ccount": "^1.0.0", + "is-alphanumeric": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "longest-streak": "^2.0.1", + "markdown-escapes": "^1.0.0", + "markdown-table": "^2.0.0", + "mdast-util-compact": "^2.0.0", + "parse-entities": "^2.0.0", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "stringify-entities": "^3.0.0", + "unherit": "^1.0.4", + "xtend": "^4.0.1" + } + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-alpn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.0.tgz", + "integrity": "sha512-e4FNQs+9cINYMO5NMFc6kOUCdohjqFPSgMuwuZAOUWqrfWsen+Yjy5qZFkV5K7VO7tFSLKcUL97olkED7sCBHA==" + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "requires": { + "lowercase-keys": "^2.0.0" + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "rxjs": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", + "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true + }, + "semver-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz", + "integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==", + "dev": true + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "should": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", + "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", + "dev": true, + "requires": { + "should-equal": "^2.0.0", + "should-format": "^3.0.3", + "should-type": "^1.4.0", + "should-type-adaptors": "^1.0.1", + "should-util": "^1.0.0" + } + }, + "should-equal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", + "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", + "dev": true, + "requires": { + "should-type": "^1.4.0" + } + }, + "should-format": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", + "integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=", + "dev": true, + "requires": { + "should-type": "^1.3.0", + "should-type-adaptors": "^1.0.1" + } + }, + "should-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", + "integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=", + "dev": true + }, + "should-type-adaptors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", + "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", + "dev": true, + "requires": { + "should-type": "^1.3.0", + "should-util": "^1.0.0" + } + }, + "should-util": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", + "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "sinon": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.0.3.tgz", + "integrity": "sha512-IKo9MIM111+smz9JGwLmw5U1075n1YXeAq8YeSFlndCLhAL5KGn6bLgu7b/4AYHTV/LcEMcRm2wU2YiL55/6Pg==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.2", + "@sinonjs/fake-timers": "^6.0.1", + "@sinonjs/formatio": "^5.0.1", + "@sinonjs/samsam": "^5.1.0", + "diff": "^4.0.2", + "nise": "^4.0.4", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "sinon-chai": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.5.0.tgz", + "integrity": "sha512-IifbusYiQBpUxxFJkR3wTU68xzBN0+bxCScEaKMjBvAQERg6FnTTc1F17rseLb1tjmkJ23730AXpFI0c47FgAg==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, + "sliced": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", + "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "requires": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, + "dependencies": { + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz", + "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "state-toggle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz", + "integrity": "sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==", + "dev": true + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "stringify-entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-3.0.1.tgz", + "integrity": "sha512-Lsk3ISA2++eJYqBMPKcr/8eby1I6L0gP0NlxF8Zja6c05yr/yCYyb2c9PwXjd08Ib3If1vn1rbs1H5ZtVuOfvQ==", + "dev": true, + "requires": { + "character-entities-html4": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.2", + "is-hexadecimal": "^1.0.0" + } + }, + "stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "requires": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "structured-source": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/structured-source/-/structured-source-3.0.2.tgz", + "integrity": "sha1-3YAkJeD1PcSm56yjdSkBoczaevU=", + "dev": true, + "requires": { + "boundary": "^1.0.1" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "textlint": { + "version": "11.7.6", + "resolved": "https://registry.npmjs.org/textlint/-/textlint-11.7.6.tgz", + "integrity": "sha512-o9nhbylWjOErba1gq2bMoJzughp9JK2VbENR+NCiMsNNEiaJ1P8jbnrL3ES86D6e0QMxziR79w5l7VmmdmLjCw==", + "dev": true, + "requires": { + "@textlint/ast-node-types": "^4.3.4", + "@textlint/ast-traverse": "^2.2.5", + "@textlint/feature-flag": "^3.2.4", + "@textlint/fixer-formatter": "^3.2.5", + "@textlint/kernel": "^3.3.6", + "@textlint/linter-formatter": "^3.2.5", + "@textlint/module-interop": "^1.1.4", + "@textlint/textlint-plugin-markdown": "^5.2.6", + "@textlint/textlint-plugin-text": "^4.2.6", + "@textlint/types": "^1.4.5", + "@textlint/utils": "^1.1.4", + "debug": "^4.1.1", + "deep-equal": "^1.1.0", + "file-entry-cache": "^5.0.1", + "get-stdin": "^5.0.1", + "glob": "^7.1.3", + "is-file": "^1.0.0", + "log-symbols": "^1.0.2", + "map-like": "^2.0.0", + "md5": "^2.2.1", + "mkdirp": "^0.5.0", + "optionator": "^0.8.0", + "path-to-glob-pattern": "^1.0.2", + "rc-config-loader": "^3.0.0", + "read-pkg": "^1.1.0", + "read-pkg-up": "^3.0.0", + "structured-source": "^3.0.2", + "try-resolve": "^1.0.1", + "unique-concat": "^0.2.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "log-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", + "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", + "dev": true, + "requires": { + "chalk": "^1.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + } + } + }, + "textlint-filter-rule-comments": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/textlint-filter-rule-comments/-/textlint-filter-rule-comments-1.2.2.tgz", + "integrity": "sha1-OnLElJlOBo4OSqrQ8k6nz+M4UDo=", + "dev": true + }, + "textlint-rule-common-misspellings": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/textlint-rule-common-misspellings/-/textlint-rule-common-misspellings-1.0.1.tgz", + "integrity": "sha1-jEEzzzu1mqFZGZ0sm87RJBM2V3Q=", + "dev": true, + "requires": { + "misspellings": "^1.0.1", + "textlint-rule-helper": "^1.1.5" + } + }, + "textlint-rule-helper": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/textlint-rule-helper/-/textlint-rule-helper-1.2.0.tgz", + "integrity": "sha1-vmjUelFGsW3RFieMmut701YxzNo=", + "dev": true, + "requires": { + "unist-util-visit": "^1.1.0" + }, + "dependencies": { + "unist-util-is": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", + "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==", + "dev": true + }, + "unist-util-visit": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", + "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==", + "dev": true, + "requires": { + "unist-util-visit-parents": "^2.0.0" + } + }, + "unist-util-visit-parents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz", + "integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==", + "dev": true, + "requires": { + "unist-util-is": "^3.0.0" + } + } + } + }, + "textlint-rule-no-dead-link": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/textlint-rule-no-dead-link/-/textlint-rule-no-dead-link-4.7.0.tgz", + "integrity": "sha512-enqn5JHR7Xa5vtkrYBAwa1POuKoi4j2hY7RKrPX+mWgQGIFapPrxfy7Bw+2pEHfV2nLqXrLQuPo4H6/D9FvzSw==", + "dev": true, + "requires": { + "fs-extra": "^8.1.0", + "get-url-origin": "^1.0.1", + "minimatch": "^3.0.4", + "node-fetch": "^2.6.0", + "p-memoize": "^3.1.0", + "p-queue": "^6.2.0", + "textlint-rule-helper": "^2.1.1" + }, + "dependencies": { + "textlint-rule-helper": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/textlint-rule-helper/-/textlint-rule-helper-2.1.1.tgz", + "integrity": "sha512-6fxgHzoJVkjl3LaC1b2Egi+5wbhG4i0pU0knJmQujVhxIJ3D3AcQQZPs457xKAi5xKz1WayYeTeJ5jrD/hnO7g==", + "dev": true, + "requires": { + "@textlint/ast-node-types": "^4.2.1", + "@textlint/types": "^1.1.2", + "structured-source": "^3.0.2", + "unist-util-visit": "^1.1.0" + } + }, + "unist-util-is": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", + "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==", + "dev": true + }, + "unist-util-visit": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", + "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==", + "dev": true, + "requires": { + "unist-util-visit-parents": "^2.0.0" + } + }, + "unist-util-visit-parents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz", + "integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==", + "dev": true, + "requires": { + "unist-util-is": "^3.0.0" + } + } + } + }, + "textlint-rule-terminology": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/textlint-rule-terminology/-/textlint-rule-terminology-2.1.4.tgz", + "integrity": "sha512-kLw4qL8RwY2lCNqgKveHc5sjCDlS5Tdw2TXWOrHvSvQxqaVOwsv3+51oMIQLGfJzQrhFSMlSlw5MvfaOerBvPQ==", + "dev": true, + "requires": { + "lodash": "^4.17.15", + "strip-json-comments": "^3.0.1", + "textlint-rule-helper": "^2.1.1" + }, + "dependencies": { + "textlint-rule-helper": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/textlint-rule-helper/-/textlint-rule-helper-2.1.1.tgz", + "integrity": "sha512-6fxgHzoJVkjl3LaC1b2Egi+5wbhG4i0pU0knJmQujVhxIJ3D3AcQQZPs457xKAi5xKz1WayYeTeJ5jrD/hnO7g==", + "dev": true, + "requires": { + "@textlint/ast-node-types": "^4.2.1", + "@textlint/types": "^1.1.2", + "structured-source": "^3.0.2", + "unist-util-visit": "^1.1.0" + } + }, + "unist-util-is": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", + "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==", + "dev": true + }, + "unist-util-visit": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", + "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==", + "dev": true, + "requires": { + "unist-util-visit-parents": "^2.0.0" + } + }, + "unist-util-visit-parents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz", + "integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==", + "dev": true, + "requires": { + "unist-util-is": "^3.0.0" + } + } + } + }, + "textlint-rule-write-good": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/textlint-rule-write-good/-/textlint-rule-write-good-1.6.2.tgz", + "integrity": "sha1-PHmwQJExnU6L5ftELFlr9QDoST4=", + "dev": true, + "requires": { + "textlint-rule-helper": "^2.0.0", + "write-good": "^0.11.0" + }, + "dependencies": { + "textlint-rule-helper": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/textlint-rule-helper/-/textlint-rule-helper-2.1.1.tgz", + "integrity": "sha512-6fxgHzoJVkjl3LaC1b2Egi+5wbhG4i0pU0knJmQujVhxIJ3D3AcQQZPs457xKAi5xKz1WayYeTeJ5jrD/hnO7g==", + "dev": true, + "requires": { + "@textlint/ast-node-types": "^4.2.1", + "@textlint/types": "^1.1.2", + "structured-source": "^3.0.2", + "unist-util-visit": "^1.1.0" + } + }, + "unist-util-is": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", + "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==", + "dev": true + }, + "unist-util-visit": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", + "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==", + "dev": true, + "requires": { + "unist-util-visit-parents": "^2.0.0" + } + }, + "unist-util-visit-parents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz", + "integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==", + "dev": true, + "requires": { + "unist-util-is": "^3.0.0" + } + } + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "to-vfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/to-vfile/-/to-vfile-6.1.0.tgz", + "integrity": "sha512-BxX8EkCxOAZe+D/ToHdDsJcVI4HqQfmw0tCkp31zf3dNP/XWIAjU4CmeuSwsSoOzOTqHPOL0KUzyZqJplkD0Qw==", + "dev": true, + "requires": { + "is-buffer": "^2.0.0", + "vfile": "^4.0.0" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "too-wordy": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/too-wordy/-/too-wordy-0.1.6.tgz", + "integrity": "sha512-MV5F74YF9+UYsvwXGXTh+5YP3EqH/ivwWfyFE2/YHWQQxm9jDPmkIC23nkN133Ye4nO3HTXmiMcfGqJ5xRPfOA==", + "dev": true + }, + "topo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.3.tgz", + "integrity": "sha512-IgpPtvD4kjrJ7CRA3ov2FhWQADwv+Tdqbsf1ZnPUSAtCJ9e1Z44MmoSGDXGk4IppoZA7jd/QRkNddlLJWlUZsQ==", + "requires": { + "hoek": "6.x.x" + }, + "dependencies": { + "hoek": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.3.tgz", + "integrity": "sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==" + } + } + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "traverse": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", + "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=", + "dev": true + }, + "trim": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", + "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=", + "dev": true + }, + "trim-trailing-lines": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.3.tgz", + "integrity": "sha512-4ku0mmjXifQcTVfYDfR5lpgV7zVqPg6zV9rdZmwOPqq0+Zq19xDqEgagqVbc4pOOShbncuAOIs59R3+3gcF3ZA==", + "dev": true + }, + "trough": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", + "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", + "dev": true + }, + "try-resolve": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/try-resolve/-/try-resolve-1.0.1.tgz", + "integrity": "sha1-z95vq9ctY+V5fPqrhzq76OcA6RI=", + "dev": true + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "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==", + "dev": true + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "underscore": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", + "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==" + }, + "unherit": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz", + "integrity": "sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==", + "dev": true, + "requires": { + "inherits": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "unified": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz", + "integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==", + "dev": true, + "requires": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + }, + "dependencies": { + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + } + } + }, + "unified-args": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/unified-args/-/unified-args-8.1.0.tgz", + "integrity": "sha512-t1HPS1cQPsVvt/6EtyWIbQGurza5684WGRigNghZRvzIdHm3LPgMdXPyGx0npORKzdiy5+urkF0rF5SXM8lBuQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "chalk": "^3.0.0", + "chokidar": "^3.0.0", + "fault": "^1.0.2", + "json5": "^2.0.0", + "minimist": "^1.2.0", + "text-table": "^0.2.0", + "unified-engine": "^8.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "unified-engine": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/unified-engine/-/unified-engine-8.0.0.tgz", + "integrity": "sha512-vLUezxCnjzz+ya4pYouRQVMT8k82Rk4fIj406UidRnSFJdGXFaQyQklAnalsQHJrLqAlaYPkXPUa1upfVSHGCA==", + "dev": true, + "requires": { + "concat-stream": "^2.0.0", + "debug": "^4.0.0", + "fault": "^1.0.0", + "figures": "^3.0.0", + "glob": "^7.0.3", + "ignore": "^5.0.0", + "is-buffer": "^2.0.0", + "is-empty": "^1.0.0", + "is-plain-obj": "^2.0.0", + "js-yaml": "^3.6.1", + "load-plugin": "^3.0.0", + "parse-json": "^5.0.0", + "to-vfile": "^6.0.0", + "trough": "^1.0.0", + "unist-util-inspect": "^5.0.0", + "vfile-reporter": "^6.0.0", + "vfile-statistics": "^1.1.0" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "unified-lint-rule": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/unified-lint-rule/-/unified-lint-rule-1.0.6.tgz", + "integrity": "sha512-YPK15YBFwnsVorDFG/u0cVVQN5G2a3V8zv5/N6KN3TCG+ajKtaALcy7u14DCSrJI+gZeyYquFL9cioJXOGXSvg==", + "dev": true, + "requires": { + "wrapped": "^1.0.1" + } + }, + "unified-message-control": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/unified-message-control/-/unified-message-control-3.0.1.tgz", + "integrity": "sha512-K2Kvvp1DBzeuxYLLsumZh/gDWUTl4e2z/P3VReFirC78cfHKtQifbhnfRrSBtKtd1Uc6cvYTW0/SZIUaMAEcTg==", + "dev": true, + "requires": { + "unist-util-visit": "^2.0.0", + "vfile-location": "^3.0.0" + } + }, + "unique-concat": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/unique-concat/-/unique-concat-0.2.2.tgz", + "integrity": "sha1-khD5vcqsxeHjkpSQ18AZ35bxhxI=", + "dev": true + }, + "unist-util-generated": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.5.tgz", + "integrity": "sha512-1TC+NxQa4N9pNdayCYA1EGUOCAO0Le3fVp7Jzns6lnua/mYgwHo0tz5WUAfrdpNch1RZLHc61VZ1SDgrtNXLSw==", + "dev": true + }, + "unist-util-inspect": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/unist-util-inspect/-/unist-util-inspect-5.0.1.tgz", + "integrity": "sha512-fPNWewS593JSmg49HbnE86BJKuBi1/nMWhDSccBvbARfxezEuJV85EaARR9/VplveiwCoLm2kWq+DhP8TBaDpw==", + "dev": true, + "requires": { + "is-empty": "^1.0.0" + } + }, + "unist-util-is": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.2.tgz", + "integrity": "sha512-Ofx8uf6haexJwI1gxWMGg6I/dLnF2yE+KibhD3/diOqY2TinLcqHXCV6OI5gFVn3xQqDH+u0M625pfKwIwgBKQ==", + "dev": true + }, + "unist-util-position": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz", + "integrity": "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==", + "dev": true + }, + "unist-util-remove-position": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz", + "integrity": "sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA==", + "dev": true, + "requires": { + "unist-util-visit": "^2.0.0" + } + }, + "unist-util-stringify-position": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", + "dev": true, + "requires": { + "@types/unist": "^2.0.2" + } + }, + "unist-util-visit": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + } + }, + "unist-util-visit-parents": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.0.tgz", + "integrity": "sha512-0g4wbluTF93npyPrp/ymd3tCDTMnP0yo2akFD2FIBAYXq/Sga3lwaU1D8OYKbtpioaI6CkDcQ6fsMnmtzt7htw==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "uri-js": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + }, + "v8-compile-cache": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", + "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vfile": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.0.tgz", + "integrity": "sha512-a/alcwCvtuc8OX92rqqo7PflxiCgXRFjdyoGVuYV+qbgCb0GgZJRvIgCD4+U/Kl1yhaRsaTwksF88xbPyGsgpw==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "replace-ext": "1.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-message": "^2.0.0" + } + }, + "vfile-location": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.1.0.tgz", + "integrity": "sha512-FCZ4AN9xMcjFIG1oGmZKo61PjwJHRVA+0/tPUP2ul4uIwjGGndIxavEMRpWn5p4xwm/ZsdXp9YNygf1ZyE4x8g==", + "dev": true + }, + "vfile-message": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", + "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + } + }, + "vfile-reporter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/vfile-reporter/-/vfile-reporter-6.0.1.tgz", + "integrity": "sha512-0OppK9mo8G2XUpv+hIKLVSDsoxJrXnOy73+vIm0jQUOUFYRduqpFHX+QqAQfvRHyX9B0UFiRuNJnBOjQCIsw1g==", + "dev": true, + "requires": { + "repeat-string": "^1.5.0", + "string-width": "^4.0.0", + "supports-color": "^6.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-sort": "^2.1.2", + "vfile-statistics": "^1.1.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "vfile-sort": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/vfile-sort/-/vfile-sort-2.2.2.tgz", + "integrity": "sha512-tAyUqD2R1l/7Rn7ixdGkhXLD3zsg+XLAeUDUhXearjfIcpL1Hcsj5hHpCoy/gvfK/Ws61+e972fm0F7up7hfYA==", + "dev": true + }, + "vfile-statistics": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/vfile-statistics/-/vfile-statistics-1.1.4.tgz", + "integrity": "sha512-lXhElVO0Rq3frgPvFBwahmed3X03vjPF8OcjKMy8+F1xU/3Q3QU3tKEDp743SFtb74PdF0UWpxPvtOP0GCLheA==", + "dev": true + }, + "weasel-words": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/weasel-words/-/weasel-words-0.1.1.tgz", + "integrity": "sha1-cTeUZYXHP+RIggE4U70ADF1oek4=", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", + "dev": true + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "workerpool": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz", + "integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==", + "dev": true + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "wrapped": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wrapped/-/wrapped-1.0.1.tgz", + "integrity": "sha1-x4PZ2Aeyc+mwHoUWgKk4yHyQckI=", + "dev": true, + "requires": { + "co": "3.1.0", + "sliced": "^1.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "write-good": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/write-good/-/write-good-0.11.3.tgz", + "integrity": "sha512-fDKIHO5wCzTLCOGNJl1rzzJrZlTIzfZl8msOoJQZzRhYo0X/tFTm4+2B1zTibFYK01Nnd1kLZBjj4xjcFLePNQ==", + "dev": true, + "requires": { + "adverb-where": "0.0.9", + "e-prime": "^0.10.2", + "no-cliches": "^0.1.0", + "object.assign": "^4.0.4", + "passive-voice": "^0.1.0", + "too-wordy": "^0.1.4", + "weasel-words": "^0.1.1" + } + }, + "x-is-string": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", + "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=", + "dev": true + }, + "xml-escape": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xml-escape/-/xml-escape-1.1.0.tgz", + "integrity": "sha1-OQTBQ/qOs6ADDsZG0pAqLxtwbEQ=", + "dev": true + }, + "xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xml2json": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/xml2json/-/xml2json-0.12.0.tgz", + "integrity": "sha512-EPJHRWJnJUYbJlzR4pBhZODwWdi2IaYGtDdteJi0JpZ4OD31IplWALuit8r73dJuM4iHZdDVKY1tLqY2UICejg==", + "requires": { + "hoek": "^4.2.1", + "joi": "^13.1.2", + "node-expat": "^2.3.18" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "dev": true + }, + "yaml": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", + "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", + "dev": true + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yargs-unparser": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.1.tgz", + "integrity": "sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "decamelize": "^1.2.0", + "flat": "^4.1.0", + "is-plain-obj": "^1.1.0", + "yargs": "^14.2.3" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "yargs": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", + "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^15.0.1" + } + }, + "yargs-parser": { + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz", + "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + } + } +} diff --git a/package.json b/package.json index 1c26730..aac56dc 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "escape-html": "1.0.3", "express": "4.x", "got": "^11.8.2", + "http-status-codes": "^2.1.4", "is-hex": "^1.1.3", "jsonwebtoken": "^8.5.1", "morgan": "^1.10.0", diff --git a/test/unit/authentication-test.js b/test/unit/authentication-test.js index 56547f2..f083529 100644 --- a/test/unit/authentication-test.js +++ b/test/unit/authentication-test.js @@ -9,6 +9,7 @@ const got = require('got'); const should = require('should'); const nock = require('nock'); const cache = require('../../lib/cache'); +const StatusCodes = require('http-status-codes').StatusCodes; const request_no_header = { prefixUrl: 'http:/localhost:1026', @@ -99,7 +100,7 @@ describe('Authentication: Keyrock IDM', () => { }); it('should deny access', (done) => { got.get('restricted_path', request_no_header).then((response) => { - should.equal(response.statusCode, 401); + should.equal(response.statusCode, StatusCodes.UNAUTHORIZED); done(); }); }); @@ -107,12 +108,12 @@ describe('Authentication: Keyrock IDM', () => { describe('When a public path is requested', () => { beforeEach(() => { - contextBrokerMock = nock('http://fiware.org:1026').get('/public').reply(200, {}); + contextBrokerMock = nock('http://fiware.org:1026').get('/public').reply(StatusCodes.OK, {}); }); it('should allow access', (done) => { got.get('public', request_no_header).then((response) => { contextBrokerMock.done(); - should.equal(response.statusCode, 200); + should.equal(response.statusCode, StatusCodes.OK); done(); }); }); @@ -120,12 +121,12 @@ describe('Authentication: Keyrock IDM', () => { describe('When a restricted path is requested and the token matches the magic key', () => { beforeEach(() => { - contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(StatusCodes.OK, {}); }); it('should allow access', (done) => { got.get('restricted', request_with_magic_key).then((response) => { contextBrokerMock.done(); - should.equal(response.statusCode, 200); + should.equal(response.statusCode, StatusCodes.OK); done(); }); }); @@ -133,16 +134,16 @@ describe('Authentication: Keyrock IDM', () => { describe('When a restricted path is requested for a legitimate user with an x-auth-token', () => { beforeEach(() => { - contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(StatusCodes.OK, {}); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id') - .reply(200, keyrock_user_response); + .reply(StatusCodes.OK, keyrock_user_response); }); it('should authenticate the user and allow access', (done) => { got.get('restricted', request_with_header).then((response) => { contextBrokerMock.done(); idmMock.done(); - should.equal(response.statusCode, 200); + should.equal(response.statusCode, StatusCodes.OK); done(); }); }); @@ -150,16 +151,16 @@ describe('Authentication: Keyrock IDM', () => { describe('When a restricted path is requested for a legitimate user with a bearer token', () => { beforeEach(() => { - contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(StatusCodes.OK, {}); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id') - .reply(200, keyrock_user_response); + .reply(StatusCodes.OK, keyrock_user_response); }); it('should authenticate the user and allow access', (done) => { got.get('restricted', request_with_auth_header).then((response) => { contextBrokerMock.done(); idmMock.done(); - should.equal(response.statusCode, 200); + should.equal(response.statusCode, StatusCodes.OK); done(); }); }); @@ -167,13 +168,13 @@ describe('Authentication: Keyrock IDM', () => { describe('When a restricted path is requested for a forbidden user', () => { beforeEach(() => { - idmMock = nock('http://keyrock.com:3000').get('/user?access_token=111111111&app_id=application_id').reply(401); + idmMock = nock('http://keyrock.com:3000').get('/user?access_token=111111111&app_id=application_id').reply(StatusCodes.UNAUTHORIZED); }); it('should authenticate the user and deny access', (done) => { got.get('restricted', request_with_header).then((response) => { contextBrokerMock.done(); idmMock.done(); - should.equal(response.statusCode, 401); + should.equal(response.statusCode, StatusCodes.UNAUTHORIZED); done(); }); }); @@ -184,7 +185,7 @@ describe('Authentication: Keyrock IDM', () => { contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(404); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id') - .reply(200, keyrock_user_response); + .reply(StatusCodes.OK, keyrock_user_response); }); it('should authenticate the user and proxy the error', (done) => { got.get('restricted', request_with_header).then((response) => { @@ -198,22 +199,22 @@ describe('Authentication: Keyrock IDM', () => { describe('When the same restricted path is requested multiple times', () => { beforeEach(() => { - contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').times(2).reply(200, {}); + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').times(2).reply(StatusCodes.OK, {}); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id') - .reply(200, keyrock_user_response); + .reply(StatusCodes.OK, keyrock_user_response); }); it('should access the user from cache', (done) => { got .get('restricted', request_with_header) .then((firstResponse) => { - should.equal(firstResponse.statusCode, 200); + should.equal(firstResponse.statusCode, StatusCodes.OK); return got.get('restricted', request_with_header); }) .then((secondResponse) => { contextBrokerMock.done(); idmMock.done(); - should.equal(secondResponse.statusCode, 200); + should.equal(secondResponse.statusCode, StatusCodes.OK); done(); }); }); diff --git a/test/unit/authzforce-pdp-test.js b/test/unit/authzforce-pdp-test.js index dc2e895..67e4706 100644 --- a/test/unit/authzforce-pdp-test.js +++ b/test/unit/authzforce-pdp-test.js @@ -9,6 +9,7 @@ const got = require('got'); const should = require('should'); const nock = require('nock'); const cache = require('../../lib/cache'); +const StatusCodes = require('http-status-codes').StatusCodes; const request_with_header = { prefixUrl: 'http:/localhost:1026', @@ -40,7 +41,7 @@ const keyrock_user_with_azf = { const authzforce_permit_response = ` @@ -51,7 +52,7 @@ const authzforce_permit_response = ` @@ -107,7 +108,7 @@ describe('Authorization: Authzforce PDP', () => { nock.cleanAll(); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id&authzforce=true') - .reply(200, keyrock_user_with_azf); + .reply(StatusCodes.OK, keyrock_user_with_azf); done(); }); @@ -118,10 +119,10 @@ describe('Authorization: Authzforce PDP', () => { describe('When a restricted path is requested for a legitimate user', () => { beforeEach(() => { - contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(StatusCodes.OK, {}); authzforceMock = nock('http://authzforce.com:8080') .post('/authzforce-ce/domains/authzforce/pdp') - .reply(200, authzforce_permit_response); + .reply(StatusCodes.OK, authzforce_permit_response); }); it('should allow access', (done) => { @@ -129,7 +130,7 @@ describe('Authorization: Authzforce PDP', () => { contextBrokerMock.done(); idmMock.done(); authzforceMock.done(); - should.equal(response.statusCode, 200); + should.equal(response.statusCode, StatusCodes.OK); done(); }); }); @@ -139,14 +140,14 @@ describe('Authorization: Authzforce PDP', () => { beforeEach(() => { authzforceMock = nock('http://authzforce.com:8080') .post('/authzforce-ce/domains/authzforce/pdp') - .reply(200, authzforce_deny_response); + .reply(StatusCodes.OK, authzforce_deny_response); }); it('should deny access when denied', (done) => { got.get('restricted', request_with_header).then((response) => { idmMock.done(); authzforceMock.done(); - should.equal(response.statusCode, 401); + should.equal(response.statusCode, StatusCodes.UNAUTHORIZED); done(); }); }); @@ -157,7 +158,7 @@ describe('Authorization: Authzforce PDP', () => { nock.cleanAll(); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id&authzforce=true') - .reply(200, { + .reply(StatusCodes.OK, { app_id: 'application_id', trusted_apps: [] }); @@ -165,7 +166,7 @@ describe('Authorization: Authzforce PDP', () => { it('should deny access', (done) => { got.get('restricted', request_with_header).then((response) => { idmMock.done(); - should.equal(response.statusCode, 401); + should.equal(response.statusCode, StatusCodes.UNAUTHORIZED); done(); }); }); diff --git a/test/unit/connection-test.js b/test/unit/connection-test.js index 378646f..57ff76d 100644 --- a/test/unit/connection-test.js +++ b/test/unit/connection-test.js @@ -11,6 +11,7 @@ const nock = require('nock'); const IDM = require('../../lib/pdp/keyrock'); const Authzforce = require('../../lib/pdp/authzforce'); const cache = require('../../lib/cache'); +const StatusCodes = require('http-status-codes').StatusCodes; const config = { pep_port: 1026, @@ -64,7 +65,7 @@ describe('Connection Tests', () => { describe('When connecting to Authzforce and it is present', () => { beforeEach(() => { - authzforceMock = nock('http://authzforce.com:8080').get('/').reply(200, {}); + authzforceMock = nock('http://authzforce.com:8080').get('/').reply(StatusCodes.OK, {}); }); it('should not error', (done) => { Authzforce.checkConnectivity() @@ -80,7 +81,7 @@ describe('Connection Tests', () => { describe('When connecting to Keyrock and it is present', () => { beforeEach(() => { - idmMock = nock('http://keyrock.com:3000').get('/version').reply(200, {}); + idmMock = nock('http://keyrock.com:3000').get('/version').reply(StatusCodes.OK, {}); }); it('should not error', (done) => { IDM.checkConnectivity() @@ -96,7 +97,7 @@ describe('Connection Tests', () => { describe('When authenticating the PEP with Keyrock', () => { beforeEach(() => { - idmMock = nock('http://keyrock.com:3000').post('/v3/auth/tokens').reply(200, {}); + idmMock = nock('http://keyrock.com:3000').post('/v3/auth/tokens').reply(StatusCodes.OK, {}); }); it('should not error', (done) => { IDM.authenticatePEP() @@ -112,7 +113,7 @@ describe('Connection Tests', () => { describe('When authenticating a misconfigured PEP with Keyrock', () => { beforeEach(() => { - idmMock = nock('http://keyrock.com:3000').post('/v3/auth/tokens').reply(401); + idmMock = nock('http://keyrock.com:3000').post('/v3/auth/tokens').reply(StatusCodes.UNAUTHORIZED); }); it('should error', (done) => { IDM.authenticatePEP() diff --git a/test/unit/ishare-test.js b/test/unit/ishare-test.js index 63b83f4..c32b62a 100644 --- a/test/unit/ishare-test.js +++ b/test/unit/ishare-test.js @@ -10,6 +10,7 @@ const should = require('should'); const nock = require('nock'); const cache = require('../../lib/cache'); const jwt = require('jsonwebtoken'); +const StatusCodes = require('http-status-codes').StatusCodes; const ngsiPayload = [ { @@ -171,13 +172,13 @@ describe('Authorization: iSHARE PDP', () => { beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026') .get('/path/entities/urn:ngsi-ld:SoilSensor:1111?type=SoilSensor') - .reply(200, {}); + .reply(StatusCodes.OK, {}); }); it('should allow access', (done) => { got.get('path/entities/urn:ngsi-ld:SoilSensor:1111?type=SoilSensor', request_with_jwt).then((response) => { contextBrokerMock.done(); - should.equal(response.statusCode, 200); + should.equal(response.statusCode, StatusCodes.OK); done(); }); }); @@ -186,7 +187,7 @@ describe('Authorization: iSHARE PDP', () => { describe('When a restricted URL does not match the attached JWT policy', () => { it('should deny access', (done) => { got.get('path/entities/urn:ngsi-ld:Tractor:1111?type=Tractor', request_with_jwt).then((response) => { - should.equal(response.statusCode, 401); + should.equal(response.statusCode, StatusCodes.UNAUTHORIZED); done(); }); }); @@ -194,12 +195,12 @@ describe('Authorization: iSHARE PDP', () => { xdescribe('When a JWT policy is not recognized by the iSHARE delegate', () => { beforeEach(() => { - //iShareMock = nock('http://ishare.com:8080').post('/delegate').reply(200, ishare_policy_not_recognized); + //iShareMock = nock('http://ishare.com:8080').post('/delegate').reply(StatusCodes.OK, ishare_policy_not_recognized); }); it('should deny access', (done) => { got.get('path/entities/urn:ngsi-ld:SoilSensor:1111', request_with_jwt).then((response) => { - should.equal(response.statusCode, 401); + should.equal(response.statusCode, StatusCodes.UNAUTHORIZED); done(); }); }); @@ -209,13 +210,13 @@ describe('Authorization: iSHARE PDP', () => { beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026') .get('/path/entities/?ids=urn:ngsi-ld:SoilSensor:1111&type=SoilSensor') - .reply(200, {}); + .reply(StatusCodes.OK, {}); }); it('should allow access based on the JWT policy and entities', (done) => { got.get('path/entities/?ids=urn:ngsi-ld:SoilSensor:1111&type=SoilSensor', request_with_jwt).then((response) => { contextBrokerMock.done(); - should.equal(response.statusCode, 200); + should.equal(response.statusCode, StatusCodes.OK); done(); }); }); @@ -223,13 +224,13 @@ describe('Authorization: iSHARE PDP', () => { describe('When a restricted URL with a payload body is requested', () => { beforeEach(() => { - contextBrokerMock = nock('http://fiware.org:1026').patch('/path/entityOperations/upsert').reply(200, {}); + contextBrokerMock = nock('http://fiware.org:1026').patch('/path/entityOperations/upsert').reply(StatusCodes.OK, {}); }); it('should allow access based on the JWT policy and entities', (done) => { got.patch('path/entityOperations/upsert', request_with_jwt_and_body).then((response) => { contextBrokerMock.done(); - should.equal(response.statusCode, 200); + should.equal(response.statusCode, StatusCodes.OK); done(); }); }); diff --git a/test/unit/jwt-authentication-test.js b/test/unit/jwt-authentication-test.js index 058b3c8..a43b1ef 100644 --- a/test/unit/jwt-authentication-test.js +++ b/test/unit/jwt-authentication-test.js @@ -10,6 +10,7 @@ const should = require('should'); const nock = require('nock'); const cache = require('../../lib/cache'); const jwt = require('jsonwebtoken'); +const StatusCodes = require('http-status-codes').StatusCodes; function sleep(ms) { return new Promise((resolve) => { @@ -130,7 +131,7 @@ describe('Authentication: JWT Token', () => { describe('When a URL is requested and no JWT token is present', () => { it('should deny access', (done) => { got.get('restricted_path', request_no_jwt).then((response) => { - should.equal(response.statusCode, 401); + should.equal(response.statusCode, StatusCodes.UNAUTHORIZED); done(); }); }); @@ -138,12 +139,12 @@ describe('Authentication: JWT Token', () => { describe('When a public path is requested', () => { beforeEach(() => { - contextBrokerMock = nock('http://fiware.org:1026').get('/public').reply(200, {}); + contextBrokerMock = nock('http://fiware.org:1026').get('/public').reply(StatusCodes.OK, {}); }); it('should allow access', (done) => { got.get('public', request_with_jwt).then((response) => { contextBrokerMock.done(); - should.equal(response.statusCode, 200); + should.equal(response.statusCode, StatusCodes.OK); done(); }); }); @@ -151,12 +152,12 @@ describe('Authentication: JWT Token', () => { describe('When a restricted path is requested with a legitimate JWT', () => { beforeEach(() => { - contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(StatusCodes.OK, {}); }); it('should authenticate the user and allow access', (done) => { got.get('restricted', request_with_jwt).then((response) => { contextBrokerMock.done(); - should.equal(response.statusCode, 200); + should.equal(response.statusCode, StatusCodes.OK); done(); }); }); @@ -169,7 +170,7 @@ describe('Authentication: JWT Token', () => { it('should deny access', (done) => { got.get('restricted', request_with_expired_jwt).then((response) => { contextBrokerMock.done(); - should.equal(response.statusCode, 401); + should.equal(response.statusCode, StatusCodes.UNAUTHORIZED); done(); }); }); @@ -179,11 +180,11 @@ describe('Authentication: JWT Token', () => { beforeEach(() => { idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=' + invalid_token + '&app_id=application_id') - .reply(401); + .reply(StatusCodes.UNAUTHORIZED); }); it('should fallback to Keyrock and deny access', (done) => { got.get('restricted', request_with_invalid_jwt).then((response) => { - should.equal(response.statusCode, 401); + should.equal(response.statusCode, StatusCodes.UNAUTHORIZED); idmMock.done(); done(); }); diff --git a/test/unit/keyrock-pdp-test.js b/test/unit/keyrock-pdp-test.js index 94276e9..e4bdfd9 100644 --- a/test/unit/keyrock-pdp-test.js +++ b/test/unit/keyrock-pdp-test.js @@ -9,6 +9,7 @@ const got = require('got'); const should = require('should'); const nock = require('nock'); const cache = require('../../lib/cache'); +const StatusCodes = require('http-status-codes').StatusCodes; const request_with_header = { prefixUrl: 'http:/localhost:1026', @@ -82,16 +83,16 @@ describe('Authorization: Keyrock PDP', () => { describe('When a restricted path is requested for a legitimate user', () => { beforeEach(() => { - contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(StatusCodes.OK, {}); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') - .reply(200, keyrock_permit_response); + .reply(StatusCodes.OK, keyrock_permit_response); }); it('should allow access', (done) => { got.get('restricted', request_with_header).then((response) => { contextBrokerMock.done(); idmMock.done(); - should.equal(response.statusCode, 200); + should.equal(response.statusCode, StatusCodes.OK); done(); }); }); @@ -101,7 +102,7 @@ describe('Authorization: Keyrock PDP', () => { beforeEach(() => { idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') - .reply(200, { + .reply(StatusCodes.OK, { app_id: '', trusted_apps: [], authorization_decision: 'Permit' @@ -110,7 +111,7 @@ describe('Authorization: Keyrock PDP', () => { it('should deny access', (done) => { got.get('restricted', request_with_header).then((response) => { idmMock.done(); - should.equal(response.statusCode, 401); + should.equal(response.statusCode, StatusCodes.UNAUTHORIZED); done(); }); }); @@ -120,12 +121,12 @@ describe('Authorization: Keyrock PDP', () => { beforeEach(() => { idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') - .reply(200, keyrock_deny_response); + .reply(StatusCodes.OK, keyrock_deny_response); }); it('should deny access', (done) => { got.get('restricted', request_with_header).then((response) => { idmMock.done(); - should.equal(response.statusCode, 401); + should.equal(response.statusCode, StatusCodes.UNAUTHORIZED); done(); }); }); @@ -133,22 +134,22 @@ describe('Authorization: Keyrock PDP', () => { describe('When the same action on a restricted path multiple times', () => { beforeEach(() => { - contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').times(2).reply(200, {}); + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').times(2).reply(StatusCodes.OK, {}); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') - .reply(200, keyrock_permit_response); + .reply(StatusCodes.OK, keyrock_permit_response); }); it('should access the user action from cache', (done) => { got .get('restricted', request_with_header) .then((firstResponse) => { - should.equal(firstResponse.statusCode, 200); + should.equal(firstResponse.statusCode, StatusCodes.OK); return got.get('restricted', request_with_header); }) .then((secondResponse) => { contextBrokerMock.done(); idmMock.done(); - should.equal(secondResponse.statusCode, 200); + should.equal(secondResponse.statusCode, StatusCodes.OK); done(); }); }); @@ -156,21 +157,21 @@ describe('Authorization: Keyrock PDP', () => { describe('When the same user request two different actions on a restricted path', () => { beforeEach(() => { - contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(200, {}); + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(StatusCodes.OK, {}); contextBrokerMock.post('/restricted').reply(204); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') - .reply(200, keyrock_permit_response); + .reply(StatusCodes.OK, keyrock_permit_response); idmMock .get('/user?access_token=111111111&app_id=application_id&action=POST&resource=/restricted') - .reply(200, keyrock_permit_response); + .reply(StatusCodes.OK, keyrock_permit_response); }); it('should not access the user from cache', (done) => { got .get('restricted', request_with_header) .then((firstResponse) => { - should.equal(firstResponse.statusCode, 200); + should.equal(firstResponse.statusCode, StatusCodes.OK); return got.post('restricted', request_with_header_and_body); }) .then((secondResponse) => { diff --git a/test/unit/opa-pdp-test.js b/test/unit/opa-pdp-test.js index b15911d..4a93dfb 100644 --- a/test/unit/opa-pdp-test.js +++ b/test/unit/opa-pdp-test.js @@ -9,6 +9,7 @@ const got = require('got'); const should = require('should'); const nock = require('nock'); const cache = require('../../lib/cache'); +const StatusCodes = require('http-status-codes').StatusCodes; const ngsiPayload = [ { @@ -115,7 +116,7 @@ describe('Authorization: Open Policy Agent PDP', () => { nock.cleanAll(); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id') - .reply(200, keyrock_user_response); + .reply(StatusCodes.OK, keyrock_user_response); done(); }); @@ -126,10 +127,10 @@ describe('Authorization: Open Policy Agent PDP', () => { describe('When a restricted URL is requested by a legitimate user', () => { beforeEach(() => { - contextBrokerMock = nock('http://fiware.org:1026').get('/path/entities/urn:ngsi-ld:entity:1111').reply(200, {}); + contextBrokerMock = nock('http://fiware.org:1026').get('/path/entities/urn:ngsi-ld:entity:1111').reply(StatusCodes.OK, {}); openPolicyAgentMock = nock('http://openpolicyagent.com:8080') .post('/query') - .reply(200, open_policy_agent_permit_response); + .reply(StatusCodes.OK, open_policy_agent_permit_response); }); it('should allow access', (done) => { @@ -137,7 +138,7 @@ describe('Authorization: Open Policy Agent PDP', () => { contextBrokerMock.done(); idmMock.done(); openPolicyAgentMock.done(); - should.equal(response.statusCode, 200); + should.equal(response.statusCode, StatusCodes.OK); done(); }); }); @@ -147,14 +148,14 @@ describe('Authorization: Open Policy Agent PDP', () => { beforeEach(() => { openPolicyAgentMock = nock('http://openpolicyagent.com:8080') .post('/query') - .reply(200, open_policy_agent_deny_response); + .reply(StatusCodes.OK, open_policy_agent_deny_response); }); it('should deny access', (done) => { got.get('path/entities/urn:ngsi-ld:entity:1111', request_with_headers).then((response) => { idmMock.done(); openPolicyAgentMock.done(); - should.equal(response.statusCode, 401); + should.equal(response.statusCode, StatusCodes.UNAUTHORIZED); done(); }); }); @@ -164,10 +165,10 @@ describe('Authorization: Open Policy Agent PDP', () => { beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026') .get('/path/entities/?ids=urn:ngsi-ld:entity:1111&type=entity') - .reply(200, {}); + .reply(StatusCodes.OK, {}); openPolicyAgentMock = nock('http://openpolicyagent.com:8080') .post('/query') - .reply(200, open_policy_agent_permit_response); + .reply(StatusCodes.OK, open_policy_agent_permit_response); }); it('should allow access based on entities', (done) => { @@ -175,7 +176,7 @@ describe('Authorization: Open Policy Agent PDP', () => { contextBrokerMock.done(); idmMock.done(); openPolicyAgentMock.done(); - should.equal(response.statusCode, 200); + should.equal(response.statusCode, StatusCodes.OK); done(); }); }); @@ -185,8 +186,8 @@ describe('Authorization: Open Policy Agent PDP', () => { beforeEach(() => { openPolicyAgentMock = nock('http://openpolicyagent.com:8080') .post('/query') - .reply(200, open_policy_agent_permit_response); - contextBrokerMock = nock('http://fiware.org:1026').patch('/path/entityOperations/upsert').reply(200, {}); + .reply(StatusCodes.OK, open_policy_agent_permit_response); + contextBrokerMock = nock('http://fiware.org:1026').patch('/path/entityOperations/upsert').reply(StatusCodes.OK, {}); }); it('should allow access based on entities', (done) => { @@ -194,7 +195,7 @@ describe('Authorization: Open Policy Agent PDP', () => { contextBrokerMock.done(); idmMock.done(); openPolicyAgentMock.done(); - should.equal(response.statusCode, 200); + should.equal(response.statusCode, StatusCodes.OK); done(); }); }); diff --git a/test/unit/xacml-pdp-test.js b/test/unit/xacml-pdp-test.js index 1f17cb7..7758feb 100644 --- a/test/unit/xacml-pdp-test.js +++ b/test/unit/xacml-pdp-test.js @@ -9,6 +9,7 @@ const got = require('got'); const should = require('should'); const nock = require('nock'); const cache = require('../../lib/cache'); +const StatusCodes = require('http-status-codes').StatusCodes; const ngsiPayload = [ { @@ -136,7 +137,7 @@ describe('Authorization: XACML PDP', () => { nock.cleanAll(); idmMock = nock('http://keyrock.com:3000') .get('/user?access_token=111111111&app_id=application_id') - .reply(200, keyrock_user_response); + .reply(StatusCodes.OK, keyrock_user_response); done(); }); @@ -147,8 +148,8 @@ describe('Authorization: XACML PDP', () => { describe('When a restricted URL is requested by a legitimate user', () => { beforeEach(() => { - contextBrokerMock = nock('http://fiware.org:1026').get('/path/entities/urn:ngsi-ld:entity:1111').reply(200, {}); - xacmlMock = nock('http://xacml.com:8080').post('/xacml').reply(200, xacml_permit_response); + contextBrokerMock = nock('http://fiware.org:1026').get('/path/entities/urn:ngsi-ld:entity:1111').reply(StatusCodes.OK, {}); + xacmlMock = nock('http://xacml.com:8080').post('/xacml').reply(StatusCodes.OK, xacml_permit_response); }); it('should allow access', (done) => { @@ -156,7 +157,7 @@ describe('Authorization: XACML PDP', () => { contextBrokerMock.done(); idmMock.done(); xacmlMock.done(); - should.equal(response.statusCode, 200); + should.equal(response.statusCode, StatusCodes.OK); done(); }); }); @@ -164,14 +165,14 @@ describe('Authorization: XACML PDP', () => { describe('When a restricted URL is requested by a forbidden user', () => { beforeEach(() => { - xacmlMock = nock('http://xacml.com:8080').post('/xacml').reply(200, xacml_deny_response); + xacmlMock = nock('http://xacml.com:8080').post('/xacml').reply(StatusCodes.OK, xacml_deny_response); }); it('should deny access', (done) => { got.get('path/entities/urn:ngsi-ld:entity:1111', request_with_headers).then((response) => { idmMock.done(); xacmlMock.done(); - should.equal(response.statusCode, 401); + should.equal(response.statusCode, StatusCodes.UNAUTHORIZED); done(); }); }); @@ -181,8 +182,8 @@ describe('Authorization: XACML PDP', () => { beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026') .get('/path/entities/?ids=urn:ngsi-ld:entity:1111&type=entity') - .reply(200, {}); - xacmlMock = nock('http://xacml.com:8080').post('/xacml').reply(200, xacml_permit_response); + .reply(StatusCodes.OK, {}); + xacmlMock = nock('http://xacml.com:8080').post('/xacml').reply(StatusCodes.OK, xacml_permit_response); }); it('should allow access based on entities', (done) => { @@ -190,7 +191,7 @@ describe('Authorization: XACML PDP', () => { contextBrokerMock.done(); idmMock.done(); xacmlMock.done(); - should.equal(response.statusCode, 200); + should.equal(response.statusCode, StatusCodes.OK); done(); }); }); @@ -198,8 +199,8 @@ describe('Authorization: XACML PDP', () => { describe('When a restricted URL with a payload body is requested', () => { beforeEach(() => { - xacmlMock = nock('http://xacml.com:8080').post('/xacml').reply(200, xacml_permit_response); - contextBrokerMock = nock('http://fiware.org:1026').patch('/path/entityOperations/upsert').reply(200, {}); + xacmlMock = nock('http://xacml.com:8080').post('/xacml').reply(StatusCodes.OK, xacml_permit_response); + contextBrokerMock = nock('http://fiware.org:1026').patch('/path/entityOperations/upsert').reply(StatusCodes.OK, {}); }); it('should allow access based on entities', (done) => { @@ -207,7 +208,7 @@ describe('Authorization: XACML PDP', () => { contextBrokerMock.done(); idmMock.done(); xacmlMock.done(); - should.equal(response.statusCode, 200); + should.equal(response.statusCode, StatusCodes.OK); done(); }); }); From 038dbe0adea153a521748ec3f8c462e0bce7d2c5 Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Wed, 18 Aug 2021 10:23:07 +0200 Subject: [PATCH 22/33] Remove imports --- controllers/root.js | 2 -- lib/pdp/keyrock.js | 1 - 2 files changed, 3 deletions(-) diff --git a/controllers/root.js b/controllers/root.js index 94c7be1..81f7369 100644 --- a/controllers/root.js +++ b/controllers/root.js @@ -11,8 +11,6 @@ const IDM = require('../lib/pdp/keyrock'); const jsonwebtoken = require('jsonwebtoken'); const access = require('../lib/access_functions'); const PDP = require('../lib/authorization_functions'); -const StatusCodes = require('http-status-codes').StatusCodes; - const debug = require('debug')('pep-proxy:root'); /** diff --git a/lib/pdp/keyrock.js b/lib/pdp/keyrock.js index 5386b40..81fd76c 100644 --- a/lib/pdp/keyrock.js +++ b/lib/pdp/keyrock.js @@ -9,7 +9,6 @@ const config_service = require('../config_service'); const debug = require('debug')('pep-proxy:IDM-Client'); const cache = require('../cache'); const got = require('got'); -const StatusCodes = require('http-status-codes').StatusCodes; /** * Can the Keyrock PDP check payloads? From b7855d96a89f5fc2c5a2505f638180ed89c2d58b Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Wed, 18 Aug 2021 10:33:48 +0200 Subject: [PATCH 23/33] Use StatusCode constant. --- lib/access_functions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/access_functions.js b/lib/access_functions.js index f64ebe7..295d186 100644 --- a/lib/access_functions.js +++ b/lib/access_functions.js @@ -82,7 +82,7 @@ function deny(res, message, type) { function internalError(res, e, component) { const message = e ? e.message : undefined; debug(`Error in ${component} communication `, message ? message : e); - res.status(503).send({ + res.status(StatusCodes.INTERNAL_SERVER_ERROR).send({ title: getReasonPhrase(StatusCodes.INTERNAL_SERVER_ERROR), detail: message }); From a568626a6ffeb289ba9db90e00915568a2a75be8 Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Wed, 18 Aug 2021 12:10:10 +0200 Subject: [PATCH 24/33] Use Handlebars to configure the Error message payload. --- Dockerfile | 2 ++ config.js | 7 +++++++ config.js.template | 7 +++++++ doc/admin_guide.md | 10 ++++++++-- extras/docker/README.md | 2 ++ lib/access_functions.js | 24 ++++++++++++++++++------ lib/config_service.js | 10 +++++++++- package-lock.json | 38 ++++++++++++++++++++++++++++++++++++-- package.json | 1 + test/unit/start-up-test.js | 4 ++++ 10 files changed, 94 insertions(+), 11 deletions(-) diff --git a/Dockerfile b/Dockerfile index babdce5..7dfcf1b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -99,3 +99,5 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=60s \ # PEP_PROXY_CORS_MAX_AGE # PEP_PROXY_AUTH_FOR_NGINX # PEP_PROXY_MAGIC_KEY +# PEP_PROXY_ERROR_TEMPLATE +# PEP_PROXY_ERROR_CONTENT_TYPE diff --git a/config.js b/config.js index 1e34ec1..d4c5b75 100644 --- a/config.js +++ b/config.js @@ -79,4 +79,11 @@ config.public_paths = []; config.magic_key = undefined; config.auth_for_nginx = false; +config.error_template = `{ + "type": "{{type}}", + "title": "{{title}}", + "detail": "{{message}}" + }`; +config.error_content_type = "application/json"; + module.exports = config; diff --git a/config.js.template b/config.js.template index 5684fda..c17887e 100644 --- a/config.js.template +++ b/config.js.template @@ -81,4 +81,11 @@ config.public_paths = []; config.magic_key = undefined; config.auth_for_nginx = false; +config.error_template = `{ + "type": "{{type}}", + "title": "{{title}}", + "detail": "{{message}}" + }`; +config.error_content_type = "application/json"; + module.exports = config; diff --git a/doc/admin_guide.md b/doc/admin_guide.md index 4230990..5881260 100644 --- a/doc/admin_guide.md +++ b/doc/admin_guide.md @@ -295,13 +295,17 @@ These are the parameters that can be configured in the global section: - **pep_port**: Port to use if HTTPS is disabled - **https**: HTTPS configuration. Disable or leave undefined if you are testing without an HTTPS certificate +- **error_template**: A [Handlebars](https://handlebarsjs.com/) template defining the format of an error message payload +- **error_content_type**: The content-type header of the error message ```json { "enabled": false, "cert_file": "cert/cert.crt", "key_file": "cert/key.key", - "port": 443 + "port": 443, + "error_template" : "{\"type\": \"{{type}}\", \"title\": \"{{title}}\", \"detail\": \"{{message}}\"}", + "error_content_type" "application/json" } ``` @@ -464,7 +468,9 @@ overrides. | PEP_PROXY_CORS_CREDENTIALS | `cors.credentials` | | | PEP_PROXY_CORS_MAX_AGE | `cors.maxAge` | | | PEP_PROXY_AUTH_FOR_NGINX | `config.auth_for_nginx` | | -| PEP_PROXY_MAGIC_KEY | `config.magic_key` | +| PEP_PROXY_MAGIC_KEY | `config.magic_key` | +| PEP_PROXY_ERROR_TEMPLATE | `config.error_template` | + Note: diff --git a/extras/docker/README.md b/extras/docker/README.md index 4db1ec4..99da412 100644 --- a/extras/docker/README.md +++ b/extras/docker/README.md @@ -116,3 +116,5 @@ sudo docker run -d --name pep-proxy-container -v [host_config_file]:/opt/fiware- - `PEP_PROXY_CORS_MAX_AGE` - The `Access-Control-Max-Age` header is not sent by default. set to `true` to enable it. - `PEP_PROXY_MAGIC_KEY` - default value is `undefined` - should be overridden - `PEP_PROXY_AUTH_FOR_NGINX` - default value is `false` +- `PEP_PROXY_ERROR_TEMPLATE` - default value is an NGSI error payload. +- `PEP_PROXY_ERROR_CONTENT_TYPE` - default value is `application/json` diff --git a/lib/access_functions.js b/lib/access_functions.js index 295d186..804fd63 100644 --- a/lib/access_functions.js +++ b/lib/access_functions.js @@ -13,6 +13,14 @@ const StatusCodes = require('http-status-codes').StatusCodes; const getReasonPhrase = require('http-status-codes').getReasonPhrase const PROXY_URL = (config.app.ssl ? 'https://' : 'http://') + config.app.host + ':' + config.app.port; +const template = require('handlebars').compile(config.error_template || `{ + "type": "{{type}}", + "title": "{{title}}", + "detail": "{{message}}" + }`); + +const error_content_type = config.error_content_type || 'application/json'; + /** * Add the client IP of the proxy client to the list of X-forwarded-for headers. @@ -64,11 +72,13 @@ exports.adjudicate = function (req, res, decision) { */ function deny(res, message, type) { debug('Denied. ' + type); - res.status(StatusCodes.UNAUTHORIZED).send({ + res.setHeader('Content-Type', error_content_type); + res.status(StatusCodes.UNAUTHORIZED).send( + template({ type, title: getReasonPhrase(StatusCodes.UNAUTHORIZED), - detail: message - }); + message + })); } /** @@ -82,10 +92,12 @@ function deny(res, message, type) { function internalError(res, e, component) { const message = e ? e.message : undefined; debug(`Error in ${component} communication `, message ? message : e); - res.status(StatusCodes.INTERNAL_SERVER_ERROR).send({ + res.setHeader('Content-Type', error_content_type); + res.status(StatusCodes.INTERNAL_SERVER_ERROR).send(template({ + type: 'urn:dx:as:InternalServerError', title: getReasonPhrase(StatusCodes.INTERNAL_SERVER_ERROR), - detail: message - }); + message + })); } /** diff --git a/lib/config_service.js b/lib/config_service.js index 3eeb54a..725e6d7 100644 --- a/lib/config_service.js +++ b/lib/config_service.js @@ -87,7 +87,9 @@ function process_environment_variables(verbose) { 'PEP_PROXY_CORS_MAX_AGE', 'PEP_PROXY_AUTH_FOR_NGINX', 'PEP_PROXY_MAGIC_KEY', - 'PEP_PROXY_DEBUG' + 'PEP_PROXY_DEBUG', + 'PEP_PROXY_ERROR_TEMPLATE', + 'PEP_PROXY_ERROR_CONTENT_TYPE' ]; const protected_variables = ['PEP_PROXY_USERNAME', 'PEP_PASSWORD', 'PEP_TOKEN_SECRET', 'PEP_TRUSTED_APPS']; @@ -283,6 +285,12 @@ function process_environment_variables(verbose) { if (process.env.PEP_PROXY_DEBUG) { config.debug = to_boolean(process.env.PEP_PROXY_DEBUG, true); } + if (process.env.PEP_PROXY_ERROR_TEMPLATE) { + config.error_template = process.env.PEP_PROXY_ERROR_TEMPLATE; + } + if (process.env.PEP_PROXY_ERROR_CONTENT_TYPE) { + config.error_content_type = process.env.PEP_PROXY_ERROR_CONTENT_TYPE; + } } function set_config(new_config, verbose = false) { diff --git a/package-lock.json b/package-lock.json index ca7caf3..9438aea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2851,6 +2851,25 @@ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, + "handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -4166,8 +4185,7 @@ "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "misspellings": { "version": "1.1.0", @@ -4357,6 +4375,11 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, "nise": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/nise/-/nise-4.0.4.tgz", @@ -6764,6 +6787,12 @@ "is-typedarray": "^1.0.0" } }, + "uglify-js": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.1.tgz", + "integrity": "sha512-JhS3hmcVaXlp/xSo3PKY5R0JqKs5M3IV+exdLHW99qKvKivPO4Z8qbej6mte17SOPqAOVMjt/XGgWacnFSzM3g==", + "optional": true + }, "underscore": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", @@ -7240,6 +7269,11 @@ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + }, "workerpool": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz", diff --git a/package.json b/package.json index aac56dc..3265ab7 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "escape-html": "1.0.3", "express": "4.x", "got": "^11.8.2", + "handlebars": "^4.7.7", "http-status-codes": "^2.1.4", "is-hex": "^1.1.3", "jsonwebtoken": "^8.5.1", diff --git a/test/unit/start-up-test.js b/test/unit/start-up-test.js index f14f653..5e45b47 100644 --- a/test/unit/start-up-test.js +++ b/test/unit/start-up-test.js @@ -51,6 +51,8 @@ describe('When the PEP Proxy is started with environment variables', () => { process.env.PEP_PROXY_AUTH_FOR_NGINX = 'false'; process.env.PEP_PROXY_MAGIC_KEY = '54321'; process.env.PEP_PROXY_DEBUG = 'PEP-Proxy:*'; + process.env.PEP_PROXY_ERROR_TEMPLATE ='{{message}}'; + process.env.PEP_PROXY_ERROR_CONTENT_TYPE="text/html"; }); afterEach(() => { @@ -76,6 +78,8 @@ describe('When the PEP Proxy is started with environment variables', () => { delete process.env.PEP_PROXY_AUTH_FOR_NGINX; delete process.env.PEP_PROXY_MAGIC_KEY; delete process.env.PEP_PROXY_DEBUG; + delete process.env.PEP_PROXY_ERROR_TEMPLATE; + delete process.env.PEP_PROXY_ERROR_CONTENT_TYPE; }); it('should amend the configuration', (done) => { From 5a3132963d216e2a4fa40ad2cb8bdb1a3c456526 Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Wed, 18 Aug 2021 15:28:03 +0200 Subject: [PATCH 25/33] Formatting --- app.js | 16 +++--- config.js | 2 +- lib/access_functions.js | 33 +++++++----- lib/authorization_functions.js | 7 +-- lib/{payload.js => payload_analyse.js} | 74 +++++++++++++++++++++++--- lib/pdp/iShare.js | 21 ++++---- lib/pdp/keyrock.js | 12 +++-- lib/pdp/openPolicyAgent.js | 7 +-- lib/pdp/xacml.js | 15 +++--- test/unit/authentication-test.js | 6 ++- test/unit/authzforce-pdp-test.js | 2 +- test/unit/connection-test.js | 2 +- test/unit/ishare-test.js | 60 ++++++++++++++++++--- test/unit/jwt-authentication-test.js | 2 +- test/unit/keyrock-pdp-test.js | 2 +- test/unit/opa-pdp-test.js | 59 ++++++++++++++++++-- test/unit/start-up-test.js | 4 +- test/unit/xacml-pdp-test.js | 10 ++-- 18 files changed, 261 insertions(+), 73 deletions(-) rename lib/{payload.js => payload_analyse.js} (55%) diff --git a/app.js b/app.js index 2d26bd9..751ffb4 100644 --- a/app.js +++ b/app.js @@ -30,7 +30,7 @@ process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; exports.start_server = function (token, config) { config_service.set_config(config, true); const Root = require('./controllers/root'); - const Payload = require('./lib/payload'); + const Payload = require('./lib/payload_analyse'); const Authorize = require('./lib/authorization_functions'); const app = express(); let server; @@ -82,14 +82,16 @@ exports.start_server = function (token, config) { } if (Authorize.checkPayload()) { - app.use(Payload.queryAnalyse); + app.post('/*/subscriptions/', Payload.subscription, Root.restricted_access); + app.patch('/*/subscriptions/*', Payload.subscription, Root.restricted_access); + app.use(Payload.query); // Oddity for NGSI-v2 - //payload.all('/*/op/*', Payload.queryAnalyse; + //payload.all('/*/op/*', Payload.query; - app.use(Payload.bodyAnalyse); - app.all('/*/entities/:id', Payload.paramsAnalyse, Root.restricted_access); - app.all('/*/entities/:id/attrs', Payload.paramsAnalyse, Root.restricted_access); - app.all('/*/entities/:id/attrs/:attr', Payload.paramsAnalyse, Root.restricted_access); + app.use(Payload.body); + app.all('/*/entities/:id', Payload.params, Root.restricted_access); + app.all('/*/entities/:id/attrs', Payload.params, Root.restricted_access); + app.all('/*/entities/:id/attrs/:attr', Payload.params, Root.restricted_access); } app.all('/*', Root.restricted_access); diff --git a/config.js b/config.js index d4c5b75..7428c89 100644 --- a/config.js +++ b/config.js @@ -84,6 +84,6 @@ config.error_template = `{ "title": "{{title}}", "detail": "{{message}}" }`; -config.error_content_type = "application/json"; +config.error_content_type = 'application/json'; module.exports = config; diff --git a/lib/access_functions.js b/lib/access_functions.js index 804fd63..132d899 100644 --- a/lib/access_functions.js +++ b/lib/access_functions.js @@ -9,19 +9,21 @@ const config_service = require('./config_service'); const config = config_service.get_config(); const debug = require('debug')('pep-proxy:access'); const got = require('got'); -const StatusCodes = require('http-status-codes').StatusCodes; -const getReasonPhrase = require('http-status-codes').getReasonPhrase +const StatusCodes = require('http-status-codes').StatusCodes; +const getReasonPhrase = require('http-status-codes').getReasonPhrase; const PROXY_URL = (config.app.ssl ? 'https://' : 'http://') + config.app.host + ':' + config.app.port; -const template = require('handlebars').compile(config.error_template || `{ +const template = require('handlebars').compile( + config.error_template || + `{ "type": "{{type}}", "title": "{{title}}", "detail": "{{message}}" - }`); + }` +); const error_content_type = config.error_content_type || 'application/json'; - /** * Add the client IP of the proxy client to the list of X-forwarded-for headers. * @@ -75,10 +77,11 @@ function deny(res, message, type) { res.setHeader('Content-Type', error_content_type); res.status(StatusCodes.UNAUTHORIZED).send( template({ - type, - title: getReasonPhrase(StatusCodes.UNAUTHORIZED), - message - })); + type, + title: getReasonPhrase(StatusCodes.UNAUTHORIZED), + message + }) + ); } /** @@ -93,11 +96,13 @@ function internalError(res, e, component) { const message = e ? e.message : undefined; debug(`Error in ${component} communication `, message ? message : e); res.setHeader('Content-Type', error_content_type); - res.status(StatusCodes.INTERNAL_SERVER_ERROR).send(template({ - type: 'urn:dx:as:InternalServerError', - title: getReasonPhrase(StatusCodes.INTERNAL_SERVER_ERROR), - message - })); + res.status(StatusCodes.INTERNAL_SERVER_ERROR).send( + template({ + type: 'urn:dx:as:InternalServerError', + title: getReasonPhrase(StatusCodes.INTERNAL_SERVER_ERROR), + message + }) + ); } /** diff --git a/lib/authorization_functions.js b/lib/authorization_functions.js index 3e7eac6..35cfcef 100644 --- a/lib/authorization_functions.js +++ b/lib/authorization_functions.js @@ -45,9 +45,10 @@ function getData(req, res) { azfDomain: user.app_azf_domain, action: req.method, resource: req.path, - payload_ids: res.locals.ids, - payload_attrs: res.locals.attrs, - payload_types: res.locals.types, + payloadEntityIds: res.locals.ids, + payloadIdPatterns: res.locals.IdPatterns, + payloadAttrs: res.locals.attrs, + payloadTypes: res.locals.types, tenant_header: authorization.header ? req.get(authorization.header) : undefined }; } diff --git a/lib/payload.js b/lib/payload_analyse.js similarity index 55% rename from lib/payload.js rename to lib/payload_analyse.js index 62836bd..3a1849c 100644 --- a/lib/payload.js +++ b/lib/payload_analyse.js @@ -11,8 +11,70 @@ const debug = require('debug')('pep-proxy:payload'); /** * Check the payload body for attributes, types and ids */ -exports.bodyAnalyse = function (req, res, next) { - debug('bodyAnalyse'); +exports.subscription = function (req, res, next) { + debug('subscription'); + + function getSubIdPatterns(entityInfo) { + const IdPatterns = []; + + entityInfo.forEach((entity) => { + if (entity.idPattern) { + IdPatterns.push(entity.idPattern); + } + }); + return _.isEmpty(IdPatterns) ? undefined : IdPatterns; + } + + function getSubIds(entityInfo) { + const ids = []; + entityInfo.forEach((entity) => { + if (entity.id) { + ids.push(entity.id); + } + }); + return _.isEmpty(ids) ? undefined : ids; + } + + function getSubAttrs(notificationAttrs, watchedAttrs) { + let attrs = []; + if (notificationAttrs) { + attrs = _.union(attrs, notificationAttrs); + } + if (watchedAttrs) { + attrs = _.union(attrs, watchedAttrs); + } + return _.isEmpty(attrs) ? undefined : attrs; + } + + function getSubTypes(entityInfo) { + const types = []; + entityInfo.forEach((entity) => { + if (entity.type) { + types.push(entity.type); + } + }); + + return _.isEmpty(types) ? undefined : types; + } + + if (req.body) { + const body = JSON.parse(req.body.toString()); + const entityInfo = body.entities || []; + const notification = body.notification || {}; + + res.locals.IdPatterns = getSubIdPatterns(entityInfo); + res.locals.ids = getSubIds(entityInfo); + res.locals.attrs = getSubAttrs(notification.attributes, body.watchedAttributes); + res.locals.types = getSubTypes(entityInfo); + } + next(); +}; + +/** + * Check the payload body for attributes, types and ids + */ +exports.body = function (req, res, next) { + debug('body'); let prefix = ''; const ids = []; @@ -56,8 +118,8 @@ exports.bodyAnalyse = function (req, res, next) { /** * Check the URL path for attributes and ids */ -exports.paramsAnalyse = function (req, res, next) { - debug('paramsAnalyse'); +exports.params = function (req, res, next) { + debug('params'); if (req.params) { if (req.params.id) { res.locals.ids = res.locals.ids || []; @@ -79,8 +141,8 @@ exports.paramsAnalyse = function (req, res, next) { /** * Check the query string for attributes, types and ids */ -exports.queryAnalyse = function (req, res, next) { - debug('queryAnalyse'); +exports.query = function (req, res, next) { + debug('query'); if (req.query) { if (req.query.ids) { res.locals.ids = res.locals.ids || []; diff --git a/lib/pdp/iShare.js b/lib/pdp/iShare.js index 1ffe4c7..47a4fae 100644 --- a/lib/pdp/iShare.js +++ b/lib/pdp/iShare.js @@ -41,18 +41,17 @@ exports.checkPolicies = function (token, data, ishare_policy, ishare_authregistr } else if (ishare_policy.notOnOrAfter <= unix_timestamp) { debug('Attached iSHARE Policy expired'); return false; - } else if (!valid_payload(data, ishare_policy.policySets)) { + } else if (!validPayload(data, ishare_policy.policySets)) { debug('Attached iSHARE Policy disallows the request'); return false; } return true; }; -function valid_payload(data, policy_sets) { +function validPayload(data, policy_sets) { // If no type found in the payload, set it to check for null - data.payload_types = data.payload_types || [null]; - - const result = data.payload_types.every((type) => { + data.payloadTypes = data.payloadTypes || [null]; + const result = data.payloadTypes.every((type) => { return policy_sets.some((policy_set) => { // Policies are permissive, at least one policy // from the collection of policy sets must fire. @@ -75,8 +74,8 @@ function valid_payload(data, policy_sets) { } // If Ids are found in the policy, they must match a regex - if (fireRule && policy.target.resource.identifiers && data.payload_entity_ids) { - fireRule = data.payload_entity_ids.every((id) => { + if (fireRule && policy.target.resource.identifiers && data.payloadEntityIds) { + fireRule = data.payloadEntityIds.every((id) => { return policy.target.resource.identifiers.some((identifier) => { const regex = new RegExp(identifier, 'i'); return regex.test(id); @@ -84,9 +83,13 @@ function valid_payload(data, policy_sets) { }); } + if (fireRule && policy.target.resource.identifiers && data.payloadIdPatterns) { + fireRule = data.payloadIdPatterns === policy.target.resource.identifiers; + } + // If attributes are found in the policy, they must match a regex - if (fireRule && policy.target.resource.attributes && data.payload_entity_attrs) { - fireRule = data.payload_entity_attrs.every((attr) => { + if (fireRule && policy.target.resource.attributes && data.payloadEntityAttrs) { + fireRule = data.payloadEntityAttrs.every((attr) => { return policy.target.resource.attributes.some((attribute) => { const regex = new RegExp(attribute, 'i'); return regex.test(attr); diff --git a/lib/pdp/keyrock.js b/lib/pdp/keyrock.js index 81fd76c..07da7b3 100644 --- a/lib/pdp/keyrock.js +++ b/lib/pdp/keyrock.js @@ -156,7 +156,10 @@ exports.authenticateUser = function (token, action, resource, tenant) { const organizations = user.organizations ? user.organizations.map((elem) => elem.id) : []; if (!checkApplication(user.app_id, user.trusted_apps)) { debug('User not authorized in application', config_service.get_config().pep.app_id); - return reject({ type: 'urn:dx:as:InvalidRole', message: 'User not have the required role in the application' }); + return reject({ + type: 'urn:dx:as:InvalidRole', + message: 'User not have the required role in the application' + }); } else if (!checkOrganizations(organizations, authOrgToken)) { debug('User does not belong to the organization', authOrgToken); return reject({ type: 'urn:dx:as:InvalidRole', message: 'User does not belong to the organization' }); @@ -171,10 +174,13 @@ exports.authenticateUser = function (token, action, resource, tenant) { }) .catch((error) => { if (error instanceof got.HTTPError) { - return reject({ type: 'urn:dx:as:InvalidAuthenticationToken', message: 'User not authorized in the application' }); + return reject({ + type: 'urn:dx:as:InvalidAuthenticationToken', + message: 'User not authorized in the application' + }); } debug('Error in IDM communication ', error); - return reject({message: error.message }); + return reject({ message: error.message }); }); }); }; diff --git a/lib/pdp/openPolicyAgent.js b/lib/pdp/openPolicyAgent.js index 5cf113c..d843292 100644 --- a/lib/pdp/openPolicyAgent.js +++ b/lib/pdp/openPolicyAgent.js @@ -74,9 +74,10 @@ function getPolicy(data) { roles, action, tenant: data.tenant_header, - ids: data.payload_ids, - attrs: data.payload_attrs, - types: data.payload_types + ids: data.payloadEntityIds, + idPatterns: data.payloadIdPatterns, + attrs: data.payloadAttrs, + types: data.payloadTypes }; debug('Open Policy Agent request: ', JSON.stringify(json)); diff --git a/lib/pdp/xacml.js b/lib/pdp/xacml.js index 926770f..df4c03c 100644 --- a/lib/pdp/xacml.js +++ b/lib/pdp/xacml.js @@ -88,14 +88,17 @@ function getPolicy(data) { if (tenant) { resourceInfo.push(attribute('urn:ngsi-ld:resource:tenant', tenant)); } - if (data.payload_types) { - resourceInfo.push(attribute('urn:ngsi-ld:resource:types', data.payload_types)); + if (data.payloadTypes) { + resourceInfo.push(attribute('urn:ngsi-ld:resource:types', data.payloadTypes)); } - if (data.payload_attrs) { - resourceInfo.push(attribute('urn:ngsi-ld:resource:attrs', data.payload_attrs)); + if (data.payloadAttrs) { + resourceInfo.push(attribute('urn:ngsi-ld:resource:attrs', data.payloadAttrs)); } - if (data.payload_ids) { - resourceInfo.push(attribute('urn:ngsi-ld:resource:ids', data.payload_ids)); + if (data.payloadEntityIds) { + resourceInfo.push(attribute('urn:ngsi-ld:resource:ids', data.payloadEntityIds)); + } + if (data.payloadIdPatterns) { + resourceInfo.push(attribute('urn:ngsi-ld:resource:id-patterns', data.payloadIdPatterns)); } const json = { Request: { diff --git a/test/unit/authentication-test.js b/test/unit/authentication-test.js index f083529..1e6a7d1 100644 --- a/test/unit/authentication-test.js +++ b/test/unit/authentication-test.js @@ -9,7 +9,7 @@ const got = require('got'); const should = require('should'); const nock = require('nock'); const cache = require('../../lib/cache'); -const StatusCodes = require('http-status-codes').StatusCodes; +const StatusCodes = require('http-status-codes').StatusCodes; const request_no_header = { prefixUrl: 'http:/localhost:1026', @@ -168,7 +168,9 @@ describe('Authentication: Keyrock IDM', () => { describe('When a restricted path is requested for a forbidden user', () => { beforeEach(() => { - idmMock = nock('http://keyrock.com:3000').get('/user?access_token=111111111&app_id=application_id').reply(StatusCodes.UNAUTHORIZED); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=111111111&app_id=application_id') + .reply(StatusCodes.UNAUTHORIZED); }); it('should authenticate the user and deny access', (done) => { got.get('restricted', request_with_header).then((response) => { diff --git a/test/unit/authzforce-pdp-test.js b/test/unit/authzforce-pdp-test.js index 67e4706..7e9fc0b 100644 --- a/test/unit/authzforce-pdp-test.js +++ b/test/unit/authzforce-pdp-test.js @@ -9,7 +9,7 @@ const got = require('got'); const should = require('should'); const nock = require('nock'); const cache = require('../../lib/cache'); -const StatusCodes = require('http-status-codes').StatusCodes; +const StatusCodes = require('http-status-codes').StatusCodes; const request_with_header = { prefixUrl: 'http:/localhost:1026', diff --git a/test/unit/connection-test.js b/test/unit/connection-test.js index 57ff76d..f8c725f 100644 --- a/test/unit/connection-test.js +++ b/test/unit/connection-test.js @@ -11,7 +11,7 @@ const nock = require('nock'); const IDM = require('../../lib/pdp/keyrock'); const Authzforce = require('../../lib/pdp/authzforce'); const cache = require('../../lib/cache'); -const StatusCodes = require('http-status-codes').StatusCodes; +const StatusCodes = require('http-status-codes').StatusCodes; const config = { pep_port: 1026, diff --git a/test/unit/ishare-test.js b/test/unit/ishare-test.js index c32b62a..6c6b7c9 100644 --- a/test/unit/ishare-test.js +++ b/test/unit/ishare-test.js @@ -10,7 +10,7 @@ const should = require('should'); const nock = require('nock'); const cache = require('../../lib/cache'); const jwt = require('jsonwebtoken'); -const StatusCodes = require('http-status-codes').StatusCodes; +const StatusCodes = require('http-status-codes').StatusCodes; const ngsiPayload = [ { @@ -41,6 +41,26 @@ const ngsiPayload = [ } ]; +const ngsi_subscription = { + description: 'Notify me of low feedstock on Farm:001', + type: 'Subscription', + entities: [ + { type: 'TemperatureSensor' }, + { id: 'urn:ngsi-ld:TemperatureSensor001' } + ], + watchedAttributes: ['temperature'], + q: 'temperature>0.6;temperature<0.8;controlledAsset==urn:ngsi-ld:Building:farm001', + notification: { + attributes: ['temperature', 'controlledAsset'], + format: 'keyValues', + endpoint: { + uri: 'http://tutorial:3000/subscription/low-stock-farm001', + accept: 'application/json' + } + }, + '@context': 'http://context/ngsi-context.jsonld' +}; + const token = jwt.sign( { app_id: 'application_id', @@ -67,10 +87,10 @@ const token = jwt.sign( target: { resource: { type: 'TemperatureSensor', - identifiers: ['*'], - attributes: ['*'] + identifiers: ['.*'], + attributes: ['.*'] }, - actions: ['GET', 'PATCH'] + actions: ['GET', 'PATCH', 'POST'] }, rules: [ { @@ -82,8 +102,8 @@ const token = jwt.sign( target: { resource: { type: 'SoilSensor', - identifiers: ['*'], - attributes: ['*'] + identifiers: ['.*'], + attributes: ['.*'] }, actions: ['GET'] }, @@ -116,6 +136,14 @@ const request_with_jwt_and_body = { retry: 0 }; +const request_with_jwt_and_subscription_body = { + prefixUrl: 'http:/localhost:1026', + throwHttpErrors: false, + headers: { 'x-auth-token': token }, + json: ngsi_subscription, + retry: 0 +}; + const config = { magic_key: '999999999', pep_port: 1026, @@ -224,7 +252,9 @@ describe('Authorization: iSHARE PDP', () => { describe('When a restricted URL with a payload body is requested', () => { beforeEach(() => { - contextBrokerMock = nock('http://fiware.org:1026').patch('/path/entityOperations/upsert').reply(StatusCodes.OK, {}); + contextBrokerMock = nock('http://fiware.org:1026') + .patch('/path/entityOperations/upsert') + .reply(StatusCodes.OK, {}); }); it('should allow access based on the JWT policy and entities', (done) => { @@ -235,4 +265,20 @@ describe('Authorization: iSHARE PDP', () => { }); }); }); + + describe('When a restricted subscription URL with a payload body is requested', () => { + beforeEach(() => { + contextBrokerMock = nock('http://fiware.org:1026') + .post('/path/ngsi-ld/v1/subscriptions') + .reply(StatusCodes.OK, {}); + }); + + it('should allow access based on entities', (done) => { + got.post('path/ngsi-ld/v1/subscriptions', request_with_jwt_and_subscription_body).then((response) => { + contextBrokerMock.done(); + should.equal(response.statusCode, StatusCodes.OK); + done(); + }); + }); + }); }); diff --git a/test/unit/jwt-authentication-test.js b/test/unit/jwt-authentication-test.js index a43b1ef..1bdf038 100644 --- a/test/unit/jwt-authentication-test.js +++ b/test/unit/jwt-authentication-test.js @@ -10,7 +10,7 @@ const should = require('should'); const nock = require('nock'); const cache = require('../../lib/cache'); const jwt = require('jsonwebtoken'); -const StatusCodes = require('http-status-codes').StatusCodes; +const StatusCodes = require('http-status-codes').StatusCodes; function sleep(ms) { return new Promise((resolve) => { diff --git a/test/unit/keyrock-pdp-test.js b/test/unit/keyrock-pdp-test.js index e4bdfd9..036b5cf 100644 --- a/test/unit/keyrock-pdp-test.js +++ b/test/unit/keyrock-pdp-test.js @@ -9,7 +9,7 @@ const got = require('got'); const should = require('should'); const nock = require('nock'); const cache = require('../../lib/cache'); -const StatusCodes = require('http-status-codes').StatusCodes; +const StatusCodes = require('http-status-codes').StatusCodes; const request_with_header = { prefixUrl: 'http:/localhost:1026', diff --git a/test/unit/opa-pdp-test.js b/test/unit/opa-pdp-test.js index 4a93dfb..9d72e89 100644 --- a/test/unit/opa-pdp-test.js +++ b/test/unit/opa-pdp-test.js @@ -9,7 +9,7 @@ const got = require('got'); const should = require('should'); const nock = require('nock'); const cache = require('../../lib/cache'); -const StatusCodes = require('http-status-codes').StatusCodes; +const StatusCodes = require('http-status-codes').StatusCodes; const ngsiPayload = [ { @@ -39,6 +39,28 @@ const ngsiPayload = [ } } ]; + +const ngsi_subscription = { + description: 'Notify me of low feedstock on Farm:001', + type: 'Subscription', + entities: [ + { type: 'FillingLevelSensor' }, + { id: 'urn:ngsi-ld:FillingLevelSensor001' }, + { idPattern: 'urn:ngsi-ld:*' } + ], + watchedAttributes: ['filling'], + q: 'filling>0.6;filling<0.8;controlledAsset==urn:ngsi-ld:Building:farm001', + notification: { + attributes: ['filling', 'controlledAsset'], + format: 'keyValues', + endpoint: { + uri: 'http://tutorial:3000/subscription/low-stock-farm001', + accept: 'application/json' + } + }, + '@context': 'http://context/ngsi-context.jsonld' +}; + const keyrock_user_response = { app_id: 'application_id', trusted_apps: [], @@ -61,6 +83,12 @@ const request_with_headers_and_body = { headers: { 'x-auth-token': '111111111', 'fiware-service': 'smart-gondor' }, json: ngsiPayload }; +const request_with_headers_and_subscription_body = { + prefixUrl: 'http:/localhost:1026', + throwHttpErrors: false, + headers: { 'x-auth-token': '111111111', 'fiware-service': 'smart-gondor' }, + json: ngsi_subscription +}; const open_policy_agent_permit_response = { allow: true @@ -127,7 +155,9 @@ describe('Authorization: Open Policy Agent PDP', () => { describe('When a restricted URL is requested by a legitimate user', () => { beforeEach(() => { - contextBrokerMock = nock('http://fiware.org:1026').get('/path/entities/urn:ngsi-ld:entity:1111').reply(StatusCodes.OK, {}); + contextBrokerMock = nock('http://fiware.org:1026') + .get('/path/entities/urn:ngsi-ld:entity:1111') + .reply(StatusCodes.OK, {}); openPolicyAgentMock = nock('http://openpolicyagent.com:8080') .post('/query') .reply(StatusCodes.OK, open_policy_agent_permit_response); @@ -187,7 +217,9 @@ describe('Authorization: Open Policy Agent PDP', () => { openPolicyAgentMock = nock('http://openpolicyagent.com:8080') .post('/query') .reply(StatusCodes.OK, open_policy_agent_permit_response); - contextBrokerMock = nock('http://fiware.org:1026').patch('/path/entityOperations/upsert').reply(StatusCodes.OK, {}); + contextBrokerMock = nock('http://fiware.org:1026') + .patch('/path/entityOperations/upsert') + .reply(StatusCodes.OK, {}); }); it('should allow access based on entities', (done) => { @@ -200,4 +232,25 @@ describe('Authorization: Open Policy Agent PDP', () => { }); }); }); + + describe('When a restricted subscription URL with a payload body is requested', () => { + beforeEach(() => { + openPolicyAgentMock = nock('http://openpolicyagent.com:8080') + .post('/query') + .reply(StatusCodes.OK, open_policy_agent_permit_response); + contextBrokerMock = nock('http://fiware.org:1026') + .post('/path/ngsi-ld/v1/subscriptions') + .reply(StatusCodes.OK, {}); + }); + + it('should allow access based on entities', (done) => { + got.post('path/ngsi-ld/v1/subscriptions', request_with_headers_and_subscription_body).then((response) => { + contextBrokerMock.done(); + idmMock.done(); + openPolicyAgentMock.done(); + should.equal(response.statusCode, StatusCodes.OK); + done(); + }); + }); + }); }); diff --git a/test/unit/start-up-test.js b/test/unit/start-up-test.js index 5e45b47..b7af3a7 100644 --- a/test/unit/start-up-test.js +++ b/test/unit/start-up-test.js @@ -51,8 +51,8 @@ describe('When the PEP Proxy is started with environment variables', () => { process.env.PEP_PROXY_AUTH_FOR_NGINX = 'false'; process.env.PEP_PROXY_MAGIC_KEY = '54321'; process.env.PEP_PROXY_DEBUG = 'PEP-Proxy:*'; - process.env.PEP_PROXY_ERROR_TEMPLATE ='{{message}}'; - process.env.PEP_PROXY_ERROR_CONTENT_TYPE="text/html"; + process.env.PEP_PROXY_ERROR_TEMPLATE = '{{message}}'; + process.env.PEP_PROXY_ERROR_CONTENT_TYPE = 'text/html'; }); afterEach(() => { diff --git a/test/unit/xacml-pdp-test.js b/test/unit/xacml-pdp-test.js index 7758feb..d24ea29 100644 --- a/test/unit/xacml-pdp-test.js +++ b/test/unit/xacml-pdp-test.js @@ -9,7 +9,7 @@ const got = require('got'); const should = require('should'); const nock = require('nock'); const cache = require('../../lib/cache'); -const StatusCodes = require('http-status-codes').StatusCodes; +const StatusCodes = require('http-status-codes').StatusCodes; const ngsiPayload = [ { @@ -148,7 +148,9 @@ describe('Authorization: XACML PDP', () => { describe('When a restricted URL is requested by a legitimate user', () => { beforeEach(() => { - contextBrokerMock = nock('http://fiware.org:1026').get('/path/entities/urn:ngsi-ld:entity:1111').reply(StatusCodes.OK, {}); + contextBrokerMock = nock('http://fiware.org:1026') + .get('/path/entities/urn:ngsi-ld:entity:1111') + .reply(StatusCodes.OK, {}); xacmlMock = nock('http://xacml.com:8080').post('/xacml').reply(StatusCodes.OK, xacml_permit_response); }); @@ -200,7 +202,9 @@ describe('Authorization: XACML PDP', () => { describe('When a restricted URL with a payload body is requested', () => { beforeEach(() => { xacmlMock = nock('http://xacml.com:8080').post('/xacml').reply(StatusCodes.OK, xacml_permit_response); - contextBrokerMock = nock('http://fiware.org:1026').patch('/path/entityOperations/upsert').reply(StatusCodes.OK, {}); + contextBrokerMock = nock('http://fiware.org:1026') + .patch('/path/entityOperations/upsert') + .reply(StatusCodes.OK, {}); }); it('should allow access based on entities', (done) => { From 2e904817014e3b82b9d1b8adbef06d01557d08cb Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Wed, 18 Aug 2021 15:38:03 +0200 Subject: [PATCH 26/33] Add idPattern test. --- lib/pdp/iShare.js | 6 +++++- test/unit/ishare-test.js | 5 +++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/pdp/iShare.js b/lib/pdp/iShare.js index 47a4fae..3374609 100644 --- a/lib/pdp/iShare.js +++ b/lib/pdp/iShare.js @@ -84,7 +84,11 @@ function validPayload(data, policy_sets) { } if (fireRule && policy.target.resource.identifiers && data.payloadIdPatterns) { - fireRule = data.payloadIdPatterns === policy.target.resource.identifiers; + fireRule = data.payloadIdPatterns.every((id) => { + return policy.target.resource.identifiers.some((identifier) => { + return identifier === id; + }); + }); } // If attributes are found in the policy, they must match a regex diff --git a/test/unit/ishare-test.js b/test/unit/ishare-test.js index 6c6b7c9..37aa87e 100644 --- a/test/unit/ishare-test.js +++ b/test/unit/ishare-test.js @@ -46,7 +46,8 @@ const ngsi_subscription = { type: 'Subscription', entities: [ { type: 'TemperatureSensor' }, - { id: 'urn:ngsi-ld:TemperatureSensor001' } + { id: 'urn:ngsi-ld:TemperatureSensor001' }, + { idPattern: 'urn:ngsi-ld:.*' } ], watchedAttributes: ['temperature'], q: 'temperature>0.6;temperature<0.8;controlledAsset==urn:ngsi-ld:Building:farm001', @@ -87,7 +88,7 @@ const token = jwt.sign( target: { resource: { type: 'TemperatureSensor', - identifiers: ['.*'], + identifiers: ['urn:ngsi-ld:.*'], attributes: ['.*'] }, actions: ['GET', 'PATCH', 'POST'] From af1fb1659cd5fe7d690b12a723cd680b4e43c3ad Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Wed, 18 Aug 2021 16:51:15 +0200 Subject: [PATCH 27/33] Improve annotations. --- lib/pdp/iShare.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/pdp/iShare.js b/lib/pdp/iShare.js index 3374609..ce2dbd2 100644 --- a/lib/pdp/iShare.js +++ b/lib/pdp/iShare.js @@ -48,6 +48,8 @@ exports.checkPolicies = function (token, data, ishare_policy, ishare_authregistr return true; }; + +// TODO: This currently does not deal with exception rules function validPayload(data, policy_sets) { // If no type found in the payload, set it to check for null data.payloadTypes = data.payloadTypes || [null]; @@ -67,13 +69,14 @@ function validPayload(data, policy_sets) { }); } - // If a type is defined, the payload must have a type - // and that type must match directly + // If a type is defined in a policy, the payload must have a type + // and that type must equal the policy type directly if (fireRule && policy.target.resource.type) { fireRule = !!type && type === policy.target.resource.type; } - // If Ids are found in the policy, they must match a regex + // If ids are defined in the policy, and ids are found in the payload + // the payload ids must match a regex if (fireRule && policy.target.resource.identifiers && data.payloadEntityIds) { fireRule = data.payloadEntityIds.every((id) => { return policy.target.resource.identifiers.some((identifier) => { @@ -83,6 +86,8 @@ function validPayload(data, policy_sets) { }); } + // If ids are defined in the policy, and idPatterns are found in the payload + // the payload idPatterns must equal the policy id directly if (fireRule && policy.target.resource.identifiers && data.payloadIdPatterns) { fireRule = data.payloadIdPatterns.every((id) => { return policy.target.resource.identifiers.some((identifier) => { @@ -91,7 +96,8 @@ function validPayload(data, policy_sets) { }); } - // If attributes are found in the policy, they must match a regex + // If attributes are defined in the policy, and attributes are found in the payload + // the payload attributes must match a regex if (fireRule && policy.target.resource.attributes && data.payloadEntityAttrs) { fireRule = data.payloadEntityAttrs.every((attr) => { return policy.target.resource.attributes.some((attribute) => { From d75597f08ee380e8c1ccc2f5b1e1afc483770afe Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Wed, 18 Aug 2021 17:26:48 +0200 Subject: [PATCH 28/33] Add payload body checking for NGSI-v2 batch operations. --- app.js | 8 ++--- lib/payload_analyse.js | 75 ++++++++++++++++++++++++--------------- lib/pdp/iShare.js | 1 - test/unit/opa-pdp-test.js | 43 ++++++++++++++++++++++ 4 files changed, 94 insertions(+), 33 deletions(-) diff --git a/app.js b/app.js index 751ffb4..2032848 100644 --- a/app.js +++ b/app.js @@ -82,12 +82,12 @@ exports.start_server = function (token, config) { } if (Authorize.checkPayload()) { - app.post('/*/subscriptions/', Payload.subscription, Root.restricted_access); + // Oddity for Subscriptions + app.post('/*/subscriptions', Payload.subscription, Root.restricted_access); app.patch('/*/subscriptions/*', Payload.subscription, Root.restricted_access); - app.use(Payload.query); // Oddity for NGSI-v2 - //payload.all('/*/op/*', Payload.query; - + app.all('/*/op/*', Payload.v2batch, Root.restricted_access); + app.use(Payload.query); app.use(Payload.body); app.all('/*/entities/:id', Payload.params, Root.restricted_access); app.all('/*/entities/:id/attrs', Payload.params, Root.restricted_access); diff --git a/lib/payload_analyse.js b/lib/payload_analyse.js index 3a1849c..3e40666 100644 --- a/lib/payload_analyse.js +++ b/lib/payload_analyse.js @@ -8,6 +8,52 @@ const _ = require('underscore'); const debug = require('debug')('pep-proxy:payload'); +function getAttrs(obj, ids, types, attrs) { + if (Array.isArray(obj)) { + obj.forEach((element) => { + getAttrs(element, ids, types, attrs); + }); + } else { + const keys = _.without(_.keys(obj), 'value', 'type', 'id', 'observedAt', 'metadata', 'unitCode'); + + keys.forEach((key) => { + if (Array.isArray(obj[key])) { + getAttrs(obj[key], ids, types, attrs); + } else { + attrs.push(key); + } + }); + + if (obj.id) { + ids.push(obj.id); + } + if (obj.type) { + types.push(obj.type); + } + } +} + +/** + * Check the payload body for attributes, types and ids + */ +exports.v2batch = function (req, res, next) { + debug('v2batch'); + + const ids = []; + const attrs = []; + const types = []; + + if (req.body) { + const body = JSON.parse(req.body.toString()); + const entities = body.entities || []; + getAttrs(entities, ids, types, attrs); + res.locals.ids = _.uniq(ids); + res.locals.attrs = _.uniq(attrs); + res.locals.types = _.uniq(types); + } + next(); +}; + /** * Check the payload body for attributes, types and ids */ @@ -75,39 +121,12 @@ exports.subscription = function (req, res, next) { */ exports.body = function (req, res, next) { debug('body'); - let prefix = ''; - const ids = []; const attrs = []; const types = []; - function getAttrs(obj) { - if (Array.isArray(obj)) { - obj.forEach((element) => { - getAttrs(element); - }); - } else { - const keys = _.without(_.keys(obj), 'value', 'type', 'id', 'observedAt', 'metadata', 'unitCode'); - - keys.forEach((key) => { - if (Array.isArray(obj[key])) { - prefix = key + '.'; - getAttrs(obj[key]); - } else { - attrs.push(prefix + key); - } - }); - - if (obj.id) { - ids.push(obj.id); - } - if (obj.type) { - types.push(obj.type); - } - } - } if (req.body) { - getAttrs(JSON.parse(req.body.toString())); + getAttrs(JSON.parse(req.body.toString()), ids, types, attrs); res.locals.ids = _.uniq(ids); res.locals.attrs = _.uniq(attrs); res.locals.types = _.uniq(types); diff --git a/lib/pdp/iShare.js b/lib/pdp/iShare.js index ce2dbd2..1799ce0 100644 --- a/lib/pdp/iShare.js +++ b/lib/pdp/iShare.js @@ -48,7 +48,6 @@ exports.checkPolicies = function (token, data, ishare_policy, ishare_authregistr return true; }; - // TODO: This currently does not deal with exception rules function validPayload(data, policy_sets) { // If no type found in the payload, set it to check for null diff --git a/test/unit/opa-pdp-test.js b/test/unit/opa-pdp-test.js index 9d72e89..9b61d8b 100644 --- a/test/unit/opa-pdp-test.js +++ b/test/unit/opa-pdp-test.js @@ -61,6 +61,24 @@ const ngsi_subscription = { '@context': 'http://context/ngsi-context.jsonld' }; +const ngsi_v2_batch_operation = { + actionType: 'append_strict', + entities: [ + { + id: 'urn:ngsi-ld:TemperatureSensor:004', + type: 'TemperatureSensor', + category: { type: 'Text', value: 'sensor' }, + temperature: { type: 'Integer', value: 25, metadata: { unitCode: { type: 'Text', value: 'CEL' } } } + }, + { + id: 'urn:ngsi-ld:TemperatureSensor:005', + type: 'TemperatureSensor', + category: { type: 'Text', value: 'sensor' }, + temperature: { type: 'Integer', value: 30, metadata: { unitCode: { type: 'Text', value: 'CEL' } } } + } + ] +}; + const keyrock_user_response = { app_id: 'application_id', trusted_apps: [], @@ -89,6 +107,12 @@ const request_with_headers_and_subscription_body = { headers: { 'x-auth-token': '111111111', 'fiware-service': 'smart-gondor' }, json: ngsi_subscription }; +const request_with_headers_and_v2_batch_body = { + prefixUrl: 'http:/localhost:1026', + throwHttpErrors: false, + headers: { 'x-auth-token': '111111111', 'fiware-service': 'smart-gondor' }, + json: ngsi_v2_batch_operation +}; const open_policy_agent_permit_response = { allow: true @@ -253,4 +277,23 @@ describe('Authorization: Open Policy Agent PDP', () => { }); }); }); + + describe('When a restricted NGSI-v2 batch operation with a payload body is requested', () => { + beforeEach(() => { + openPolicyAgentMock = nock('http://openpolicyagent.com:8080') + .post('/query') + .reply(StatusCodes.OK, open_policy_agent_permit_response); + contextBrokerMock = nock('http://fiware.org:1026').post('/path/v2/op/update').reply(StatusCodes.OK, {}); + }); + + it('should allow access based on entities', (done) => { + got.post('path/v2/op/update', request_with_headers_and_v2_batch_body).then((response) => { + contextBrokerMock.done(); + idmMock.done(); + openPolicyAgentMock.done(); + should.equal(response.statusCode, StatusCodes.OK); + done(); + }); + }); + }); }); From 76618b092f9f5403d1f370557dc958dd4898019f Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Mon, 4 Oct 2021 10:03:12 +0200 Subject: [PATCH 29/33] Add bearer cache test --- test/unit/authentication-test.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/unit/authentication-test.js b/test/unit/authentication-test.js index 1e6a7d1..6cd12fd 100644 --- a/test/unit/authentication-test.js +++ b/test/unit/authentication-test.js @@ -221,4 +221,27 @@ describe('Authentication: Keyrock IDM', () => { }); }); }); + + describe('When the same restricted path is requested multiple times with a bearer token', () => { + beforeEach(() => { + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').times(2).reply(StatusCodes.OK, {}); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=111111111&app_id=application_id') + .reply(StatusCodes.OK, keyrock_user_response); + }); + it('should access the user from cache', (done) => { + got + .get('restricted', request_with_auth_header) + .then((firstResponse) => { + should.equal(firstResponse.statusCode, StatusCodes.OK); + return got.get('restricted', request_with_auth_header); + }) + .then((secondResponse) => { + contextBrokerMock.done(); + idmMock.done(); + should.equal(secondResponse.statusCode, StatusCodes.OK); + done(); + }); + }); + }); }); From 00e19cbb3bfdef72ec68f7475e1f764541ecbccc Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Mon, 4 Oct 2021 13:21:34 +0200 Subject: [PATCH 30/33] Add expired cache tests --- test/unit/authentication-test.js | 74 +++++++++++++++++++----------- test/unit/keyrock-pdp-test.js | 79 ++++++++++++++++++++++++-------- 2 files changed, 108 insertions(+), 45 deletions(-) diff --git a/test/unit/authentication-test.js b/test/unit/authentication-test.js index 6cd12fd..62d9e69 100644 --- a/test/unit/authentication-test.js +++ b/test/unit/authentication-test.js @@ -10,25 +10,33 @@ const should = require('should'); const nock = require('nock'); const cache = require('../../lib/cache'); const StatusCodes = require('http-status-codes').StatusCodes; +const shortToken = '111111111'; +const longToken = '11111111111111111111111111111111111111111111111111111111111111'; -const request_no_header = { +const no_token = { prefixUrl: 'http:/localhost:1026', throwHttpErrors: false }; -const request_with_header = { +const auth_token = { prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, - headers: { 'x-auth-token': '111111111' } + headers: { 'x-auth-token': shortToken } }; -const request_with_auth_header = { +const bearer_token = { prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, - headers: { authorization: 'Bearer: ' + Buffer.from('111111111', 'utf-8').toString('base64') } + headers: { authorization: 'Bearer: ' + Buffer.from(shortToken, 'utf-8').toString('base64') } }; -const request_with_magic_key = { +const bearer_token_long = { + prefixUrl: 'http:/localhost:1026', + throwHttpErrors: false, + headers: { authorization: 'Bearer: ' + Buffer.from(longToken, 'utf-8').toString('base64') } +}; + +const magic_key = { prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, headers: { 'x-auth-token': '999999999' } @@ -54,7 +62,7 @@ const config = { organizations: { enabled: false }, - cache_time: 300, + cache_time: 1, public_paths: ['/public'], authorization: { enabled: false, @@ -76,6 +84,12 @@ const keyrock_user_response = { displayName: 'Some User' }; +function sleep(ms) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +} + describe('Authentication: Keyrock IDM', () => { let pep; let contextBrokerMock; @@ -99,7 +113,7 @@ describe('Authentication: Keyrock IDM', () => { // Set Up }); it('should deny access', (done) => { - got.get('restricted_path', request_no_header).then((response) => { + got.get('restricted_path', no_token).then((response) => { should.equal(response.statusCode, StatusCodes.UNAUTHORIZED); done(); }); @@ -111,7 +125,7 @@ describe('Authentication: Keyrock IDM', () => { contextBrokerMock = nock('http://fiware.org:1026').get('/public').reply(StatusCodes.OK, {}); }); it('should allow access', (done) => { - got.get('public', request_no_header).then((response) => { + got.get('public', no_token).then((response) => { contextBrokerMock.done(); should.equal(response.statusCode, StatusCodes.OK); done(); @@ -124,7 +138,7 @@ describe('Authentication: Keyrock IDM', () => { contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(StatusCodes.OK, {}); }); it('should allow access', (done) => { - got.get('restricted', request_with_magic_key).then((response) => { + got.get('restricted', magic_key).then((response) => { contextBrokerMock.done(); should.equal(response.statusCode, StatusCodes.OK); done(); @@ -136,11 +150,11 @@ describe('Authentication: Keyrock IDM', () => { beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(StatusCodes.OK, {}); idmMock = nock('http://keyrock.com:3000') - .get('/user?access_token=111111111&app_id=application_id') + .get('/user?access_token=' + shortToken + '&app_id=application_id') .reply(StatusCodes.OK, keyrock_user_response); }); it('should authenticate the user and allow access', (done) => { - got.get('restricted', request_with_header).then((response) => { + got.get('restricted', auth_token).then((response) => { contextBrokerMock.done(); idmMock.done(); should.equal(response.statusCode, StatusCodes.OK); @@ -153,11 +167,11 @@ describe('Authentication: Keyrock IDM', () => { beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(StatusCodes.OK, {}); idmMock = nock('http://keyrock.com:3000') - .get('/user?access_token=111111111&app_id=application_id') + .get('/user?access_token=' + shortToken + '&app_id=application_id') .reply(StatusCodes.OK, keyrock_user_response); }); it('should authenticate the user and allow access', (done) => { - got.get('restricted', request_with_auth_header).then((response) => { + got.get('restricted', bearer_token).then((response) => { contextBrokerMock.done(); idmMock.done(); should.equal(response.statusCode, StatusCodes.OK); @@ -169,11 +183,11 @@ describe('Authentication: Keyrock IDM', () => { describe('When a restricted path is requested for a forbidden user', () => { beforeEach(() => { idmMock = nock('http://keyrock.com:3000') - .get('/user?access_token=111111111&app_id=application_id') + .get('/user?access_token=' + shortToken + '&app_id=application_id') .reply(StatusCodes.UNAUTHORIZED); }); it('should authenticate the user and deny access', (done) => { - got.get('restricted', request_with_header).then((response) => { + got.get('restricted', auth_token).then((response) => { contextBrokerMock.done(); idmMock.done(); should.equal(response.statusCode, StatusCodes.UNAUTHORIZED); @@ -186,11 +200,11 @@ describe('Authentication: Keyrock IDM', () => { beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(404); idmMock = nock('http://keyrock.com:3000') - .get('/user?access_token=111111111&app_id=application_id') + .get('/user?access_token=' + shortToken + '&app_id=application_id') .reply(StatusCodes.OK, keyrock_user_response); }); it('should authenticate the user and proxy the error', (done) => { - got.get('restricted', request_with_header).then((response) => { + got.get('restricted', auth_token).then((response) => { contextBrokerMock.done(); idmMock.done(); should.equal(response.statusCode, 404); @@ -203,15 +217,15 @@ describe('Authentication: Keyrock IDM', () => { beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').times(2).reply(StatusCodes.OK, {}); idmMock = nock('http://keyrock.com:3000') - .get('/user?access_token=111111111&app_id=application_id') + .get('/user?access_token=' + shortToken + '&app_id=application_id') .reply(StatusCodes.OK, keyrock_user_response); }); it('should access the user from cache', (done) => { got - .get('restricted', request_with_header) + .get('restricted', auth_token) .then((firstResponse) => { should.equal(firstResponse.statusCode, StatusCodes.OK); - return got.get('restricted', request_with_header); + return got.get('restricted', auth_token); }) .then((secondResponse) => { contextBrokerMock.done(); @@ -224,22 +238,28 @@ describe('Authentication: Keyrock IDM', () => { describe('When the same restricted path is requested multiple times with a bearer token', () => { beforeEach(() => { - contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').times(2).reply(StatusCodes.OK, {}); + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').times(3).reply(StatusCodes.OK, {}); idmMock = nock('http://keyrock.com:3000') - .get('/user?access_token=111111111&app_id=application_id') + .get('/user?access_token=' + longToken + '&app_id=application_id') + .times(2) .reply(StatusCodes.OK, keyrock_user_response); }); it('should access the user from cache', (done) => { got - .get('restricted', request_with_auth_header) + .get('restricted', bearer_token_long) .then((firstResponse) => { should.equal(firstResponse.statusCode, StatusCodes.OK); - return got.get('restricted', request_with_auth_header); + return got.get('restricted', bearer_token_long); }) - .then((secondResponse) => { + .then(async function (secondResponse) { + should.equal(secondResponse.statusCode, StatusCodes.OK); + await sleep(2000); + return got.get('restricted', bearer_token_long); + }) + .then((thirdResponse) => { contextBrokerMock.done(); idmMock.done(); - should.equal(secondResponse.statusCode, StatusCodes.OK); + should.equal(thirdResponse.statusCode, StatusCodes.OK); done(); }); }); diff --git a/test/unit/keyrock-pdp-test.js b/test/unit/keyrock-pdp-test.js index 036b5cf..0dcdfbd 100644 --- a/test/unit/keyrock-pdp-test.js +++ b/test/unit/keyrock-pdp-test.js @@ -10,20 +10,34 @@ const should = require('should'); const nock = require('nock'); const cache = require('../../lib/cache'); const StatusCodes = require('http-status-codes').StatusCodes; +const shortToken = '111111111'; +const longToken = '11111111111111111111111111111111111111111111111111111111111111'; -const request_with_header = { +function sleep(ms) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +} + +const auth_token = { prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, - headers: { 'x-auth-token': '111111111' } + headers: { 'x-auth-token': shortToken } }; -const request_with_header_and_body = { +const auth_token_and_body = { prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, - headers: { 'x-auth-token': '111111111' }, + headers: { 'x-auth-token': shortToken }, body: 'HELLO' }; +const bearer_token_long = { + prefixUrl: 'http:/localhost:1026', + throwHttpErrors: false, + headers: { authorization: 'Bearer: ' + Buffer.from(longToken, 'utf-8').toString('base64') } +}; + const keyrock_deny_response = { app_id: 'application_id', trusted_apps: [], @@ -55,7 +69,7 @@ const config = { organizations: { enabled: false }, - cache_time: 300, + cache_time: 1, public_paths: [], authorization: { enabled: true, @@ -85,11 +99,11 @@ describe('Authorization: Keyrock PDP', () => { beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(StatusCodes.OK, {}); idmMock = nock('http://keyrock.com:3000') - .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') + .get('/user?access_token=' + shortToken + '&app_id=application_id&action=GET&resource=/restricted') .reply(StatusCodes.OK, keyrock_permit_response); }); it('should allow access', (done) => { - got.get('restricted', request_with_header).then((response) => { + got.get('restricted', auth_token).then((response) => { contextBrokerMock.done(); idmMock.done(); should.equal(response.statusCode, StatusCodes.OK); @@ -101,7 +115,7 @@ describe('Authorization: Keyrock PDP', () => { describe('When a restricted path is requested and the app-id is not found', () => { beforeEach(() => { idmMock = nock('http://keyrock.com:3000') - .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') + .get('/user?access_token=' + shortToken + '&app_id=application_id&action=GET&resource=/restricted') .reply(StatusCodes.OK, { app_id: '', trusted_apps: [], @@ -109,7 +123,7 @@ describe('Authorization: Keyrock PDP', () => { }); }); it('should deny access', (done) => { - got.get('restricted', request_with_header).then((response) => { + got.get('restricted', auth_token).then((response) => { idmMock.done(); should.equal(response.statusCode, StatusCodes.UNAUTHORIZED); done(); @@ -120,11 +134,11 @@ describe('Authorization: Keyrock PDP', () => { describe('When a restricted path is requested for a forbidden user', () => { beforeEach(() => { idmMock = nock('http://keyrock.com:3000') - .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') + .get('/user?access_token=' + shortToken + '&app_id=application_id&action=GET&resource=/restricted') .reply(StatusCodes.OK, keyrock_deny_response); }); it('should deny access', (done) => { - got.get('restricted', request_with_header).then((response) => { + got.get('restricted', auth_token).then((response) => { idmMock.done(); should.equal(response.statusCode, StatusCodes.UNAUTHORIZED); done(); @@ -136,15 +150,15 @@ describe('Authorization: Keyrock PDP', () => { beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').times(2).reply(StatusCodes.OK, {}); idmMock = nock('http://keyrock.com:3000') - .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') + .get('/user?access_token=' + shortToken + '&app_id=application_id&action=GET&resource=/restricted') .reply(StatusCodes.OK, keyrock_permit_response); }); it('should access the user action from cache', (done) => { got - .get('restricted', request_with_header) + .get('restricted', auth_token) .then((firstResponse) => { should.equal(firstResponse.statusCode, StatusCodes.OK); - return got.get('restricted', request_with_header); + return got.get('restricted', auth_token); }) .then((secondResponse) => { contextBrokerMock.done(); @@ -155,24 +169,53 @@ describe('Authorization: Keyrock PDP', () => { }); }); + describe('When the same action on a restricted path multiple times with a bearer token', () => { + beforeEach(() => { + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').times(3).reply(StatusCodes.OK, {}); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=' + longToken + '&app_id=application_id&action=GET&resource=/restricted') + .times(2) + .reply(StatusCodes.OK, keyrock_permit_response); + }); + it('should access the user action from cache', (done) => { + got + .get('restricted', bearer_token_long) + .then((firstResponse) => { + should.equal(firstResponse.statusCode, StatusCodes.OK); + return got.get('restricted', bearer_token_long); + }) + .then(async function (secondResponse) { + should.equal(secondResponse.statusCode, StatusCodes.OK); + await sleep(2000); + return got.get('restricted', bearer_token_long); + }) + .then((thitdResponse) => { + contextBrokerMock.done(); + idmMock.done(); + should.equal(thitdResponse.statusCode, StatusCodes.OK); + done(); + }); + }); + }); + describe('When the same user request two different actions on a restricted path', () => { beforeEach(() => { contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(StatusCodes.OK, {}); contextBrokerMock.post('/restricted').reply(204); idmMock = nock('http://keyrock.com:3000') - .get('/user?access_token=111111111&app_id=application_id&action=GET&resource=/restricted') + .get('/user?access_token=' + shortToken + '&app_id=application_id&action=GET&resource=/restricted') .reply(StatusCodes.OK, keyrock_permit_response); idmMock - .get('/user?access_token=111111111&app_id=application_id&action=POST&resource=/restricted') + .get('/user?access_token=' + shortToken + '&app_id=application_id&action=POST&resource=/restricted') .reply(StatusCodes.OK, keyrock_permit_response); }); it('should not access the user from cache', (done) => { got - .get('restricted', request_with_header) + .get('restricted', auth_token) .then((firstResponse) => { should.equal(firstResponse.statusCode, StatusCodes.OK); - return got.post('restricted', request_with_header_and_body); + return got.post('restricted', auth_token_and_body); }) .then((secondResponse) => { contextBrokerMock.done(); From 6ecf09f181d1cd35a4fdb961196295a5bfc19f7e Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Mon, 4 Oct 2021 13:56:00 +0200 Subject: [PATCH 31/33] Add expired action test --- test/unit/authentication-test.js | 10 ++---- test/unit/jwt-authentication-test.js | 50 ++++++++-------------------- test/unit/keyrock-pdp-test.js | 49 ++++++++++++++++++++++----- test/unit/utils.js | 23 +++++++++++++ 4 files changed, 80 insertions(+), 52 deletions(-) create mode 100644 test/unit/utils.js diff --git a/test/unit/authentication-test.js b/test/unit/authentication-test.js index 62d9e69..e4e66e1 100644 --- a/test/unit/authentication-test.js +++ b/test/unit/authentication-test.js @@ -10,6 +10,8 @@ const should = require('should'); const nock = require('nock'); const cache = require('../../lib/cache'); const StatusCodes = require('http-status-codes').StatusCodes; +const utils = require('./utils'); + const shortToken = '111111111'; const longToken = '11111111111111111111111111111111111111111111111111111111111111'; @@ -84,12 +86,6 @@ const keyrock_user_response = { displayName: 'Some User' }; -function sleep(ms) { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }); -} - describe('Authentication: Keyrock IDM', () => { let pep; let contextBrokerMock; @@ -253,7 +249,7 @@ describe('Authentication: Keyrock IDM', () => { }) .then(async function (secondResponse) { should.equal(secondResponse.statusCode, StatusCodes.OK); - await sleep(2000); + await utils.sleep(2000); return got.get('restricted', bearer_token_long); }) .then((thirdResponse) => { diff --git a/test/unit/jwt-authentication-test.js b/test/unit/jwt-authentication-test.js index 1bdf038..6393ebc 100644 --- a/test/unit/jwt-authentication-test.js +++ b/test/unit/jwt-authentication-test.js @@ -9,60 +9,38 @@ const got = require('got'); const should = require('should'); const nock = require('nock'); const cache = require('../../lib/cache'); -const jwt = require('jsonwebtoken'); const StatusCodes = require('http-status-codes').StatusCodes; +const utils = require('./utils'); -function sleep(ms) { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }); -} - -const token = jwt.sign( - { - app_id: 'application_id', - trusted_apps: [], - id: 'username', - displayName: 'Some User' - }, - 'shhhhh' -); +const token = utils.createJWT(); -const invalid_token = jwt.sign( - { - app_id: 'application_id', - trusted_apps: [], - id: 'username', - displayName: 'Some User' - }, - 'wrong_secret' -); +const invalid_token = utils.createJWT('wrong_secret'); -const expired_token = jwt.sign( +const expired_token = utils.createJWT( + 'shhhhh', { app_id: 'application_id', trusted_apps: [], id: 'username', displayName: 'Some User' }, - 'shhhhh', { expiresIn: '1ms' } ); -const request_with_jwt = { +const jwt = { prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, headers: { 'x-auth-token': token } }; -const request_with_invalid_jwt = { +const invalid_jwt = { prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, headers: { 'x-auth-token': invalid_token }, retry: 0 }; -const request_no_jwt = { +const missing_jwt = { prefixUrl: 'http:/localhost:1026', throwHttpErrors: false }; @@ -130,7 +108,7 @@ describe('Authentication: JWT Token', () => { describe('When a URL is requested and no JWT token is present', () => { it('should deny access', (done) => { - got.get('restricted_path', request_no_jwt).then((response) => { + got.get('restricted_path', missing_jwt).then((response) => { should.equal(response.statusCode, StatusCodes.UNAUTHORIZED); done(); }); @@ -142,7 +120,7 @@ describe('Authentication: JWT Token', () => { contextBrokerMock = nock('http://fiware.org:1026').get('/public').reply(StatusCodes.OK, {}); }); it('should allow access', (done) => { - got.get('public', request_with_jwt).then((response) => { + got.get('public', jwt).then((response) => { contextBrokerMock.done(); should.equal(response.statusCode, StatusCodes.OK); done(); @@ -155,7 +133,7 @@ describe('Authentication: JWT Token', () => { contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(StatusCodes.OK, {}); }); it('should authenticate the user and allow access', (done) => { - got.get('restricted', request_with_jwt).then((response) => { + got.get('restricted', jwt).then((response) => { contextBrokerMock.done(); should.equal(response.statusCode, StatusCodes.OK); done(); @@ -165,7 +143,7 @@ describe('Authentication: JWT Token', () => { describe('When a restricted path is requested with an expired JWT', () => { beforeEach(async () => { - await sleep(100); + await utils.sleep(100); }); it('should deny access', (done) => { got.get('restricted', request_with_expired_jwt).then((response) => { @@ -183,7 +161,7 @@ describe('Authentication: JWT Token', () => { .reply(StatusCodes.UNAUTHORIZED); }); it('should fallback to Keyrock and deny access', (done) => { - got.get('restricted', request_with_invalid_jwt).then((response) => { + got.get('restricted', invalid_jwt).then((response) => { should.equal(response.statusCode, StatusCodes.UNAUTHORIZED); idmMock.done(); done(); @@ -196,7 +174,7 @@ describe('Authentication: JWT Token', () => { contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(404); }); it('should authenticate the user and proxy the error', (done) => { - got.get('restricted', request_with_jwt).then((response) => { + got.get('restricted', jwt).then((response) => { contextBrokerMock.done(); should.equal(response.statusCode, 404); done(); diff --git a/test/unit/keyrock-pdp-test.js b/test/unit/keyrock-pdp-test.js index 0dcdfbd..a8dc247 100644 --- a/test/unit/keyrock-pdp-test.js +++ b/test/unit/keyrock-pdp-test.js @@ -12,12 +12,7 @@ const cache = require('../../lib/cache'); const StatusCodes = require('http-status-codes').StatusCodes; const shortToken = '111111111'; const longToken = '11111111111111111111111111111111111111111111111111111111111111'; - -function sleep(ms) { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }); -} +const utils = require('./utils'); const auth_token = { prefixUrl: 'http:/localhost:1026', @@ -38,6 +33,13 @@ const bearer_token_long = { headers: { authorization: 'Bearer: ' + Buffer.from(longToken, 'utf-8').toString('base64') } }; +const jwt = utils.createJWT(); +const bearer_jwt_token = { + prefixUrl: 'http:/localhost:1026', + throwHttpErrors: false, + headers: { authorization: 'Bearer: ' + Buffer.from(jwt, 'utf-8').toString('base64') } +}; + const keyrock_deny_response = { app_id: 'application_id', trusted_apps: [], @@ -186,13 +188,42 @@ describe('Authorization: Keyrock PDP', () => { }) .then(async function (secondResponse) { should.equal(secondResponse.statusCode, StatusCodes.OK); - await sleep(2000); + await utils.sleep(2000); return got.get('restricted', bearer_token_long); }) - .then((thitdResponse) => { + .then((thirdResponse) => { + contextBrokerMock.done(); + idmMock.done(); + should.equal(thirdResponse.statusCode, StatusCodes.OK); + done(); + }); + }); + }); + + describe('When the same action on a restricted path multiple times with a bearer jwt', () => { + beforeEach(() => { + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').times(3).reply(StatusCodes.OK, {}); + idmMock = nock('http://keyrock.com:3000') + .get('/user?access_token=' + jwt + '&app_id=application_id&action=GET&resource=/restricted') + .times(2) + .reply(StatusCodes.OK, keyrock_permit_response); + }); + it('should access the user action from cache', (done) => { + got + .get('restricted', bearer_jwt_token) + .then((firstResponse) => { + should.equal(firstResponse.statusCode, StatusCodes.OK); + return got.get('restricted', bearer_jwt_token); + }) + .then(async function (secondResponse) { + should.equal(secondResponse.statusCode, StatusCodes.OK); + await utils.sleep(2000); + return got.get('restricted', bearer_jwt_token); + }) + .then((thirdResponse) => { contextBrokerMock.done(); idmMock.done(); - should.equal(thitdResponse.statusCode, StatusCodes.OK); + should.equal(thirdResponse.statusCode, StatusCodes.OK); done(); }); }); diff --git a/test/unit/utils.js b/test/unit/utils.js new file mode 100644 index 0000000..53f0f65 --- /dev/null +++ b/test/unit/utils.js @@ -0,0 +1,23 @@ +const jwt = require('jsonwebtoken'); + +function sleep(ms) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +} + +function createJWT( + key = 'shhhhh', + data = { + app_id: 'application_id', + trusted_apps: [], + id: 'username', + displayName: 'Some User' + }, + expiry = undefined +) { + return jwt.sign(data, key, expiry); +} + +exports.sleep = sleep; +exports.createJWT = createJWT; From 14a9bbefe56ff08b35375584c8a4d654f0a4d235 Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Mon, 4 Oct 2021 14:37:50 +0200 Subject: [PATCH 32/33] Add Keyrock JWT test --- test/unit/keyrock-pdp-test.js | 87 ++++++++++++++++++++++++----------- 1 file changed, 59 insertions(+), 28 deletions(-) diff --git a/test/unit/keyrock-pdp-test.js b/test/unit/keyrock-pdp-test.js index a8dc247..5ebc33f 100644 --- a/test/unit/keyrock-pdp-test.js +++ b/test/unit/keyrock-pdp-test.js @@ -33,7 +33,17 @@ const bearer_token_long = { headers: { authorization: 'Bearer: ' + Buffer.from(longToken, 'utf-8').toString('base64') } }; -const jwt = utils.createJWT(); +const jwt = utils.createJWT( + 'shhhhh', + { + app_id: 'application_id', + trusted_apps: [], + id: 'username', + displayName: 'Some User' + }, + { expiresIn: '20000ms' } +); + const bearer_jwt_token = { prefixUrl: 'http:/localhost:1026', throwHttpErrors: false, @@ -200,58 +210,79 @@ describe('Authorization: Keyrock PDP', () => { }); }); - describe('When the same action on a restricted path multiple times with a bearer jwt', () => { + describe('When the same user request two different actions on a restricted path', () => { beforeEach(() => { - contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').times(3).reply(StatusCodes.OK, {}); + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(StatusCodes.OK, {}); + contextBrokerMock.post('/restricted').reply(204); + idmMock = nock('http://keyrock.com:3000') - .get('/user?access_token=' + jwt + '&app_id=application_id&action=GET&resource=/restricted') - .times(2) + .get('/user?access_token=' + shortToken + '&app_id=application_id&action=GET&resource=/restricted') + .reply(StatusCodes.OK, keyrock_permit_response); + idmMock + .get('/user?access_token=' + shortToken + '&app_id=application_id&action=POST&resource=/restricted') .reply(StatusCodes.OK, keyrock_permit_response); }); - it('should access the user action from cache', (done) => { + it('should not access the user from cache', (done) => { got - .get('restricted', bearer_jwt_token) + .get('restricted', auth_token) .then((firstResponse) => { should.equal(firstResponse.statusCode, StatusCodes.OK); - return got.get('restricted', bearer_jwt_token); - }) - .then(async function (secondResponse) { - should.equal(secondResponse.statusCode, StatusCodes.OK); - await utils.sleep(2000); - return got.get('restricted', bearer_jwt_token); + return got.post('restricted', auth_token_and_body); }) - .then((thirdResponse) => { + .then((secondResponse) => { contextBrokerMock.done(); idmMock.done(); - should.equal(thirdResponse.statusCode, StatusCodes.OK); + should.equal(secondResponse.statusCode, 204); done(); }); }); }); +}); - describe('When the same user request two different actions on a restricted path', () => { - beforeEach(() => { - contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').reply(StatusCodes.OK, {}); - contextBrokerMock.post('/restricted').reply(204); +describe('Authorization: Keyrock PDP', () => { + let pep; + let contextBrokerMock; + let idmMock; + + beforeEach((done) => { + nock.cleanAll(); + const app = require('../../app'); + config.pep.token.secret = 'shhhhh'; + pep = app.start_server('12345', config); + cache.flush(); + done(); + }); + afterEach((done) => { + delete config.pep.token.secret; + pep.close(config.pep_port); + done(); + }); + + describe('When the same action on a restricted path multiple times with a bearer jwt', () => { + beforeEach(() => { + contextBrokerMock = nock('http://fiware.org:1026').get('/restricted').times(3).reply(StatusCodes.OK, {}); idmMock = nock('http://keyrock.com:3000') - .get('/user?access_token=' + shortToken + '&app_id=application_id&action=GET&resource=/restricted') - .reply(StatusCodes.OK, keyrock_permit_response); - idmMock - .get('/user?access_token=' + shortToken + '&app_id=application_id&action=POST&resource=/restricted') + .get('/user?access_token=' + jwt + '&app_id=application_id&action=GET&resource=/restricted') + .times(2) .reply(StatusCodes.OK, keyrock_permit_response); }); - it('should not access the user from cache', (done) => { + it('should access the user action from cache', (done) => { got - .get('restricted', auth_token) + .get('restricted', bearer_jwt_token) .then((firstResponse) => { should.equal(firstResponse.statusCode, StatusCodes.OK); - return got.post('restricted', auth_token_and_body); + return got.get('restricted', bearer_jwt_token); }) - .then((secondResponse) => { + .then(async function (secondResponse) { + should.equal(secondResponse.statusCode, StatusCodes.OK); + await utils.sleep(2000); + return got.get('restricted', bearer_jwt_token); + }) + .then((thirdResponse) => { contextBrokerMock.done(); idmMock.done(); - should.equal(secondResponse.statusCode, 204); + should.equal(thirdResponse.statusCode, StatusCodes.OK); done(); }); }); From 459323a1e5cee14b070631a2a2e46df95b5bfce6 Mon Sep 17 00:00:00 2001 From: Jason Fox Date: Mon, 7 Feb 2022 09:50:32 +0100 Subject: [PATCH 33/33] Fix README --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 04a8108..bdfbe1e 100644 --- a/README.md +++ b/README.md @@ -71,8 +71,11 @@ sudo npm start > **ATTENTION!!!** ->There is an existing security exploit in all versions older than 2.15 of Log4J. Although not using this software currently, the older 7.x.x versions of PEP-Proxy used to use Log4j for logging. Prior to the release 8.0.0, older versions of this software were affected by this exploit as well. -Logging was updated to use Debug and Morgan in March 2021. We released a new version 8.0.0 on dockerhub. Also latest is updated already. If still using 7.x.x please update as soon as possible. +> There is an existing security exploit in all versions older than 2.15 of Log4J. Although not using this software +> currently, the older 7.x.x versions of PEP-Proxy used to use Log4j for logging. Prior to the release 8.0.0, older +> versions of this software were affected by this exploit as well. Logging was updated to use Debug and Morgan in +> March 2021. We released a new version 8.0.0 on dockerhub. Also latest is updated already. If still using 7.x.x please +> update as soon as possible. ### Docker