зеркало из https://github.com/mozilla/fxa.git
Merge pull request #13046 from mozilla/joi_fix
chore(deps): Upgrade hapi/joi dependency - fix import
This commit is contained in:
Коммит
843f098973
|
@ -71,7 +71,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
|
||||
const Pool = require('./pool');
|
||||
const error = require('./error');
|
||||
|
@ -135,24 +135,23 @@ module.exports = function createBackendServiceAPI(
|
|||
// to the client.
|
||||
|
||||
function validate(location, value, schema, options) {
|
||||
const { value: result, error: err } = schema.validate(value, options);
|
||||
return new Promise((resolve, reject) => {
|
||||
Joi.validate(value, schema, options, (err, value) => {
|
||||
if (!err) {
|
||||
return resolve(value);
|
||||
}
|
||||
log.error(fullMethodName, {
|
||||
error: `${location} schema validation failed`,
|
||||
message: err.message,
|
||||
value,
|
||||
});
|
||||
reject(
|
||||
error.internalValidationError(
|
||||
fullMethodName,
|
||||
{ location, value },
|
||||
err
|
||||
)
|
||||
);
|
||||
if (!err) {
|
||||
return resolve(result);
|
||||
}
|
||||
log.error(fullMethodName, {
|
||||
error: `${location} schema validation failed`,
|
||||
message: err.message,
|
||||
value,
|
||||
});
|
||||
reject(
|
||||
error.internalValidationError(
|
||||
fullMethodName,
|
||||
{ location, value },
|
||||
err
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
const createBackendServiceAPI = require('./backendService');
|
||||
const config = require('../config');
|
||||
const localizeTimestamp = require('fxa-shared').l10n.localizeTimestamp({
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const isA = require('@hapi/joi');
|
||||
const isA = require('joi');
|
||||
const DESCRIPTION = require('../docs/swagger/shared/descriptions').default;
|
||||
const validators = require('./routes/validators');
|
||||
const error = require('./error');
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
'use strict';
|
||||
|
||||
const inherits = require('util').inherits;
|
||||
const messages = require('@hapi/joi/lib/language').errors;
|
||||
const OauthError = require('./oauth/error');
|
||||
const verror = require('verror');
|
||||
|
||||
|
@ -270,7 +269,7 @@ AppError.translate = function (request, response) {
|
|||
} else if (payload.validation) {
|
||||
if (
|
||||
payload.message &&
|
||||
payload.message.indexOf(messages.any.required) >= 0
|
||||
payload.message.includes('is required')
|
||||
) {
|
||||
error = AppError.missingRequestParameter(payload.validation.keys[0]);
|
||||
} else {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
'use strict';
|
||||
|
||||
const crypto = require('crypto');
|
||||
const isA = require('@hapi/joi');
|
||||
const isA = require('joi');
|
||||
|
||||
const SCHEMA = isA.array().items(isA.string()).optional();
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
const bufferEqualConstantTime = require('buffer-equal-constant-time');
|
||||
const crypto = require('crypto');
|
||||
const HEX_STRING = require('../routes/validators').HEX_STRING;
|
||||
const isA = require('@hapi/joi');
|
||||
const isA = require('joi');
|
||||
|
||||
const FLOW_ID_LENGTH = 64;
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
const validators = require('./validators');
|
||||
|
||||
const OauthError = require('./error');
|
||||
|
@ -122,7 +122,7 @@ module.exports = async function verifyAssertion(assertion) {
|
|||
}
|
||||
}
|
||||
try {
|
||||
return await CLAIMS_SCHEMA.validate(claims);
|
||||
return await CLAIMS_SCHEMA.validateAsync(claims);
|
||||
} catch (err) {
|
||||
return error(assertion, err, claims);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
const crypto = require('crypto');
|
||||
const buf = require('buf').hex;
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
|
||||
const OauthError = require('./error');
|
||||
const validators = require('./validators');
|
||||
|
|
|
@ -6,28 +6,28 @@ const assert = require('assert');
|
|||
const config = require('../../config');
|
||||
const { jwk2pem, pem2jwk } = require('pem-jwk');
|
||||
const crypto = require('crypto');
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
|
||||
const BASE64URL = /^[A-Za-z0-9-_]+$/;
|
||||
|
||||
const PUBLIC_KEY_SCHEMA = (exports.PUBLIC_KEY_SCHEMA = Joi.object({
|
||||
kty: Joi.string().only('RSA').required(),
|
||||
kty: Joi.string().valid('RSA').required(),
|
||||
kid: Joi.string().required(),
|
||||
n: Joi.string().regex(BASE64URL).required(),
|
||||
e: Joi.string().regex(BASE64URL).required(),
|
||||
alg: Joi.string().only('RS256').optional(),
|
||||
use: Joi.string().only('sig').optional(),
|
||||
alg: Joi.string().valid('RS256').optional(),
|
||||
use: Joi.string().valid('sig').optional(),
|
||||
'fxa-createdAt': Joi.number().integer().min(0).optional(),
|
||||
}));
|
||||
|
||||
const PRIVATE_KEY_SCHEMA = (exports.PRIVATE_KEY_SCHEMA = Joi.object({
|
||||
kty: Joi.string().only('RSA').required(),
|
||||
kty: Joi.string().valid('RSA').required(),
|
||||
kid: Joi.string().required(),
|
||||
n: Joi.string().regex(BASE64URL).required(),
|
||||
e: Joi.string().regex(BASE64URL).required(),
|
||||
d: Joi.string().regex(BASE64URL).required(),
|
||||
alg: Joi.string().only('RS256').optional(),
|
||||
use: Joi.string().only('sig').optional(),
|
||||
alg: Joi.string().valid('RS256').optional(),
|
||||
use: Joi.string().valid('sig').optional(),
|
||||
p: Joi.string().regex(BASE64URL).required(),
|
||||
q: Joi.string().regex(BASE64URL).required(),
|
||||
dp: Joi.string().regex(BASE64URL).required(),
|
||||
|
@ -46,7 +46,7 @@ const currentPrivJWK = config.get('oauthServer.openid.key');
|
|||
if (currentPrivJWK) {
|
||||
assert.strictEqual(
|
||||
PRIVATE_KEY_SCHEMA.validate(currentPrivJWK).error,
|
||||
null,
|
||||
undefined,
|
||||
'openid.key must be a valid private key'
|
||||
);
|
||||
PRIVATE_JWKS_MAP.set(currentPrivJWK.kid, currentPrivJWK);
|
||||
|
@ -62,7 +62,7 @@ const newPrivJWK = config.get('oauthServer.openid.newKey');
|
|||
if (newPrivJWK) {
|
||||
assert.strictEqual(
|
||||
PRIVATE_KEY_SCHEMA.validate(newPrivJWK).error,
|
||||
null,
|
||||
undefined,
|
||||
'openid.newKey must be a valid private key'
|
||||
);
|
||||
assert.notEqual(
|
||||
|
@ -79,7 +79,7 @@ const oldPubJWK = config.get('oauthServer.openid.oldKey');
|
|||
if (oldPubJWK) {
|
||||
assert.strictEqual(
|
||||
PUBLIC_KEY_SCHEMA.validate(oldPubJWK).error,
|
||||
null,
|
||||
undefined,
|
||||
'openid.oldKey must be a valid public key'
|
||||
);
|
||||
assert.notEqual(
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
const ScopeSet = require('fxa-shared').oauth.scopes;
|
||||
const authServerValidators = require('../routes/validators');
|
||||
|
||||
|
@ -37,20 +37,22 @@ exports.sessionToken = authServerValidators.sessionToken;
|
|||
|
||||
const scopeString = Joi.string().max(256);
|
||||
exports.scope = Joi.extend({
|
||||
name: 'scope',
|
||||
type: 'scope',
|
||||
base: Joi.any(), // We're not returning a string, so don't base this on Joi.string().
|
||||
language: {
|
||||
messages: {
|
||||
base: 'needs to be a valid scope string',
|
||||
},
|
||||
pre(value, state, options) {
|
||||
prepare(value, state, options) {
|
||||
const err = scopeString.validate(value).err;
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
try {
|
||||
return ScopeSet.fromString(value || '');
|
||||
return { value: ScopeSet.fromString(value || '') };
|
||||
} catch (err) {
|
||||
return this.createError('scope.base', { v: value }, state, options);
|
||||
return {
|
||||
errors: this.$_createError('scope.base', { v: value }, state, options),
|
||||
};
|
||||
}
|
||||
},
|
||||
}).scope();
|
||||
|
@ -77,7 +79,7 @@ exports.jwt = Joi.string()
|
|||
// JWT format: 'header.payload.signature'
|
||||
.regex(/^([a-zA-Z0-9\-_]+)\.([a-zA-Z0-9\-_]+)\.([a-zA-Z0-9\-_]+)$/);
|
||||
|
||||
exports.accessToken = Joi.alternatives().try([exports.token, exports.jwt]);
|
||||
exports.accessToken = Joi.alternatives().try(exports.token, exports.jwt);
|
||||
|
||||
exports.ppidSeed = authServerValidators.ppidSeed.default(0);
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const isA = require('@hapi/joi');
|
||||
const isA = require('joi');
|
||||
const createBackendServiceAPI = require('../backendService');
|
||||
|
||||
const PATH_PREFIX = '/v1';
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const isA = require('@hapi/joi');
|
||||
const isA = require('joi');
|
||||
const error = require('./error');
|
||||
const createBackendServiceAPI = require('./backendService');
|
||||
const validators = require('./routes/validators');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
import isA from '@hapi/joi';
|
||||
import isA from 'joi';
|
||||
import {
|
||||
deleteAllPayPalBAs,
|
||||
getAllPayPalBAByUid,
|
||||
|
@ -1590,7 +1590,7 @@ export const accountRoutes = (
|
|||
.optional()
|
||||
.description(DESCRIPTION.resume),
|
||||
metricsContext: METRICS_CONTEXT_SCHEMA,
|
||||
style: isA.string().allow(['trailhead']).optional(),
|
||||
style: isA.string().allow('trailhead').optional(),
|
||||
verificationMethod: validators.verificationMethod.optional(),
|
||||
// preVerified is not available in production mode.
|
||||
...(!(config as any).isProduction && {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const isA = require('@hapi/joi');
|
||||
const isA = require('joi');
|
||||
const validators = require('./validators');
|
||||
const authorizedClients = require('../oauth/authorized_clients');
|
||||
const error = require('../error');
|
||||
|
@ -137,7 +137,7 @@ module.exports = (log, db, devices, clientUtils) => {
|
|||
.with('refreshTokenId', ['clientId']),
|
||||
},
|
||||
response: {
|
||||
schema: {},
|
||||
schema: isA.object({}),
|
||||
},
|
||||
},
|
||||
handler: async function (request) {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
'use strict';
|
||||
|
||||
const AppError = require('../../error');
|
||||
const joi = require('@hapi/joi');
|
||||
const joi = require('joi');
|
||||
const hex = require('buf').to.hex;
|
||||
const validators = require('../validators');
|
||||
const { BEARER_AUTH_REGEX } = validators;
|
||||
|
|
|
@ -10,7 +10,7 @@ const ajv = new Ajv();
|
|||
const hex = require('buf').to.hex;
|
||||
const error = require('../error');
|
||||
const fs = require('fs');
|
||||
const isA = require('@hapi/joi');
|
||||
const isA = require('joi');
|
||||
const path = require('path');
|
||||
const validators = require('./validators');
|
||||
|
||||
|
@ -440,7 +440,7 @@ module.exports = (
|
|||
),
|
||||
},
|
||||
response: {
|
||||
schema: {},
|
||||
schema: isA.object({}),
|
||||
},
|
||||
},
|
||||
handler: async function (request) {
|
||||
|
@ -760,7 +760,7 @@ module.exports = (
|
|||
}),
|
||||
},
|
||||
response: {
|
||||
schema: {},
|
||||
schema: isA.object({}),
|
||||
},
|
||||
},
|
||||
handler: async function (request) {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
const butil = require('../crypto/butil');
|
||||
const emailUtils = require('./utils/email');
|
||||
const error = require('../error');
|
||||
const isA = require('@hapi/joi');
|
||||
const isA = require('joi');
|
||||
const random = require('../crypto/random');
|
||||
const Sentry = require('@sentry/node');
|
||||
const validators = require('./validators');
|
||||
|
@ -213,7 +213,7 @@ module.exports = (
|
|||
.string()
|
||||
.max(32)
|
||||
.alphanum()
|
||||
.allow(['upgradeSession'])
|
||||
.allow('upgradeSession')
|
||||
.optional(),
|
||||
}),
|
||||
payload: isA.object({
|
||||
|
@ -227,12 +227,12 @@ module.exports = (
|
|||
.max(2048)
|
||||
.optional()
|
||||
.description(DESCRIPTION.resume),
|
||||
style: isA.string().allow(['trailhead']).optional(),
|
||||
style: isA.string().allow('trailhead').optional(),
|
||||
type: isA
|
||||
.string()
|
||||
.max(32)
|
||||
.alphanum()
|
||||
.allow(['upgradeSession'])
|
||||
.allow('upgradeSession')
|
||||
.optional(),
|
||||
}),
|
||||
},
|
||||
|
@ -385,7 +385,7 @@ module.exports = (
|
|||
.alphanum()
|
||||
.optional()
|
||||
.description(DESCRIPTION.type),
|
||||
style: isA.string().allow(['trailhead']).optional(),
|
||||
style: isA.string().allow('trailhead').optional(),
|
||||
// The `marketingOptIn` is safe to remove after train-167+
|
||||
marketingOptIn: isA.boolean().optional(),
|
||||
newsletters: validators.newsletters,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const hex = require('buf').to.hex;
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
|
||||
const OauthError = require('../../oauth/error');
|
||||
const AuthError = require('../../error');
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Joi = require('@hapi/joi');
|
||||
|
||||
const Joi = require('joi');
|
||||
const validators = require('../../../oauth/validators');
|
||||
const verifyAssertion = require('../../../oauth/assertion');
|
||||
const authorizedClients = require('../../../oauth/authorized_clients');
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const hex = require('buf').to.hex;
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
|
||||
const AppError = require('../../../oauth/error');
|
||||
const validators = require('../../../oauth/validators');
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const crypto = require('crypto');
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
const hex = require('buf').to.hex;
|
||||
|
||||
const OauthError = require('../../oauth/error');
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
const JWTIdToken = require('../../oauth/jwt_id_token');
|
||||
const MISC_DOCS = require('../../../docs/swagger/misc-api').default;
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/*jshint camelcase: false*/
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
const validators = require('../../oauth/validators');
|
||||
const hex = require('buf').to.hex;
|
||||
const AppError = require('../../oauth/error');
|
||||
|
@ -12,7 +12,7 @@ const MISC_DOCS = require('../../../docs/swagger/misc-api').default;
|
|||
|
||||
const PAYLOAD_SCHEMA = Joi.object({
|
||||
token: Joi.string().required(),
|
||||
token_type_hint: Joi.string().equal(['access_token', 'refresh_token']),
|
||||
token_type_hint: Joi.string().equal('access_token', 'refresh_token'),
|
||||
});
|
||||
|
||||
// The "token introspection" endpoint, per https://tools.ietf.org/html/rfc7662
|
||||
|
@ -32,7 +32,7 @@ module.exports = ({ oauthDB }) => ({
|
|||
active: Joi.boolean().required(),
|
||||
scope: validators.scope.optional(),
|
||||
client_id: validators.clientId.optional(),
|
||||
token_type: Joi.string().equal(['access_token', 'refresh_token']),
|
||||
token_type: Joi.string().equal('access_token', 'refresh_token'),
|
||||
exp: Joi.number().optional(),
|
||||
iat: Joi.number().optional(),
|
||||
sub: Joi.string().optional(),
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Joi = require('@hapi/joi');
|
||||
|
||||
const Joi = require('joi');
|
||||
const OauthError = require('../../oauth/error');
|
||||
const AuthError = require('../../error');
|
||||
const config = require('../../../config').getProperties();
|
||||
|
|
|
@ -30,7 +30,7 @@ const OauthError = require('../../oauth/error');
|
|||
const AuthError = require('../../error');
|
||||
const buf = require('buf').hex;
|
||||
const hex = require('buf').to.hex;
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
|
||||
const {
|
||||
OAUTH_SCOPE_OLD_SYNC,
|
||||
|
@ -110,11 +110,11 @@ const PAYLOAD_SCHEMA = Joi.object({
|
|||
ttl: Joi.number().positive().default(MAX_TTL_S).optional(),
|
||||
|
||||
scope: Joi.alternatives()
|
||||
.when('grant_type', {
|
||||
.conditional('grant_type', {
|
||||
is: GRANT_REFRESH_TOKEN,
|
||||
then: validators.scope.optional(),
|
||||
})
|
||||
.when('grant_type', {
|
||||
.conditional('grant_type', {
|
||||
is: GRANT_FXA_ASSERTION,
|
||||
then: validators.scope.required(),
|
||||
otherwise: Joi.forbidden(),
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Joi = require('@hapi/joi');
|
||||
|
||||
const Joi = require('joi');
|
||||
const token = require('../../oauth/token');
|
||||
const validators = require('../../oauth/validators');
|
||||
const MISC_DOCS = require('../../../docs/swagger/misc-api').default;
|
||||
|
|
|
@ -9,7 +9,7 @@ const HEX_STRING = validators.HEX_STRING;
|
|||
|
||||
const butil = require('../crypto/butil');
|
||||
const error = require('../error');
|
||||
const isA = require('@hapi/joi');
|
||||
const isA = require('joi');
|
||||
const random = require('../crypto/random');
|
||||
const requestHelper = require('../routes/utils/request_helper');
|
||||
const { emailsMatch } = require('fxa-shared').email.helpers;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
'use strict';
|
||||
|
||||
const errors = require('../error');
|
||||
const isA = require('@hapi/joi');
|
||||
const isA = require('joi');
|
||||
const validators = require('./validators');
|
||||
const RECOVERY_CODES_DOCS =
|
||||
require('../../docs/swagger/recovery-codes-api').default;
|
||||
|
|
|
@ -10,7 +10,7 @@ const DESCRIPTION = require('../../docs/swagger/shared/descriptions').default;
|
|||
|
||||
const errors = require('../error');
|
||||
const validators = require('./validators');
|
||||
const isA = require('@hapi/joi');
|
||||
const isA = require('joi');
|
||||
|
||||
module.exports = (log, db, Password, verifierVersion, customs, mailer) => {
|
||||
return [
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
'use strict';
|
||||
|
||||
const error = require('../error');
|
||||
const isA = require('@hapi/joi');
|
||||
const isA = require('joi');
|
||||
const requestHelper = require('../routes/utils/request_helper');
|
||||
const METRICS_CONTEXT_SCHEMA = require('../metrics/context').schema;
|
||||
const validators = require('./validators');
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
'use strict';
|
||||
|
||||
const error = require('../error');
|
||||
const isA = require('@hapi/joi');
|
||||
const isA = require('joi');
|
||||
const validators = require('./validators');
|
||||
const SIGN_DOCS = require('../../docs/swagger/sign-api').default;
|
||||
const DESCRIPTION = require('../../docs/swagger/shared/descriptions').default;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
import { ServerRoute } from '@hapi/hapi';
|
||||
import isA from '@hapi/joi';
|
||||
import isA from 'joi';
|
||||
import { OAUTH_SCOPE_SUBSCRIPTIONS_IAP } from 'fxa-shared/oauth/constants';
|
||||
import { Container } from 'typedi';
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
import { ServerRoute } from '@hapi/hapi';
|
||||
import isA from '@hapi/joi';
|
||||
import isA from 'joi';
|
||||
import {
|
||||
createPayPalBA,
|
||||
getAccountCustomerByUid,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
import { ServerRoute } from '@hapi/hapi';
|
||||
import isA from '@hapi/joi';
|
||||
import isA from 'joi';
|
||||
import { Container } from 'typedi';
|
||||
|
||||
import SUBSCRIPTIONS_DOCS from '../../../docs/swagger/subscriptions-api';
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
import { ServerRoute } from '@hapi/hapi';
|
||||
import isA from '@hapi/joi';
|
||||
import isA from 'joi';
|
||||
import * as Sentry from '@sentry/node';
|
||||
import { Account } from 'fxa-shared/db/models/auth';
|
||||
import { ACTIVE_SUBSCRIPTION_STATUSES } from 'fxa-shared/subscriptions/stripe';
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
import { ServerRoute } from '@hapi/hapi';
|
||||
import isA from '@hapi/joi';
|
||||
import isA from 'joi';
|
||||
import * as Sentry from '@sentry/node';
|
||||
import { getAccountCustomerByUid } from 'fxa-shared/db/models/auth';
|
||||
import { AbbrevPlan } from 'fxa-shared/dist/subscriptions/types';
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
import { ServerRoute } from '@hapi/hapi';
|
||||
import isA from '@hapi/joi';
|
||||
import isA from 'joi';
|
||||
import zendesk from 'node-zendesk';
|
||||
import pRetry from 'p-retry';
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
import { ServerRoute } from '@hapi/hapi';
|
||||
import isA from '@hapi/joi';
|
||||
import isA from 'joi';
|
||||
import { MozillaSubscriptionTypes } from 'fxa-shared/subscriptions/types';
|
||||
import { Container } from 'typedi';
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
const errors = require('../error');
|
||||
const validators = require('./validators');
|
||||
const isA = require('@hapi/joi');
|
||||
const isA = require('joi');
|
||||
const otplib = require('otplib');
|
||||
const qrcode = require('qrcode');
|
||||
const { promisify } = require('util');
|
||||
|
|
|
@ -8,7 +8,7 @@ const UNBLOCK_CODES_DOCS =
|
|||
require('../../docs/swagger/unblock-codes-api').default;
|
||||
const DESCRIPTION = require('../../docs/swagger/shared/descriptions').default;
|
||||
|
||||
const isA = require('@hapi/joi');
|
||||
const isA = require('joi');
|
||||
const METRICS_CONTEXT_SCHEMA = require('../metrics/context').schema;
|
||||
const validators = require('./validators');
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const isA = require('@hapi/joi');
|
||||
const isA = require('joi');
|
||||
const random = require('../crypto/random');
|
||||
const validators = require('./validators');
|
||||
const UTIL_DOCS = require('../../docs/swagger/util-api').default;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
'use strict';
|
||||
|
||||
const emailUtils = require('./email');
|
||||
const isA = require('@hapi/joi');
|
||||
const isA = require('joi');
|
||||
const validators = require('../validators');
|
||||
const butil = require('../../crypto/butil');
|
||||
const error = require('../../error');
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
const { URL } = require('url');
|
||||
const punycode = require('punycode.js');
|
||||
const isA = require('@hapi/joi');
|
||||
const isA = require('joi');
|
||||
const { MozillaSubscriptionTypes } = require('fxa-shared/subscriptions/types');
|
||||
const {
|
||||
minimalConfigSchema,
|
||||
|
@ -81,25 +81,20 @@ module.exports.BEARER_AUTH_REGEX = BEARER_AUTH_REGEX;
|
|||
// This is different to Joi's builtin email validator, and
|
||||
// requires a custom validation function.
|
||||
|
||||
// The custom validators below need to either return the value
|
||||
// or create an error object using `createError`.
|
||||
// see examples here: https://github.com/hapijs/joi/blob/master/lib/string.js
|
||||
|
||||
module.exports.email = function () {
|
||||
const email = isA.string().max(255).regex(DISPLAY_SAFE_UNICODE);
|
||||
// Imma add a custom test to this Joi object using internal
|
||||
// properties because I can't find a nice API to do that.
|
||||
email._tests.push({
|
||||
func: function (value, state, options) {
|
||||
if (value !== undefined && value !== null) {
|
||||
if (module.exports.isValidEmailAddress(value)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
const email = isA
|
||||
.string()
|
||||
.max(255)
|
||||
.regex(DISPLAY_SAFE_UNICODE)
|
||||
.custom((value) => {
|
||||
// Do custom validation
|
||||
const isValid = module.exports.isValidEmailAddress(value);
|
||||
|
||||
return email.createError('string.base', { value }, state, options);
|
||||
},
|
||||
});
|
||||
if (!isValid) {
|
||||
throw new Error('Not a valid email address');
|
||||
}
|
||||
return value;
|
||||
});
|
||||
|
||||
return email;
|
||||
};
|
||||
|
@ -154,7 +149,7 @@ module.exports.jwt = isA
|
|||
|
||||
module.exports.accessToken = isA
|
||||
.alternatives()
|
||||
.try([module.exports.hexString.length(64), module.exports.jwt]);
|
||||
.try(module.exports.hexString.length(64), module.exports.jwt);
|
||||
|
||||
// Function to validate an email address.
|
||||
//
|
||||
|
@ -192,38 +187,36 @@ module.exports.isValidEmailAddress = function (value) {
|
|||
};
|
||||
|
||||
module.exports.redirectTo = function redirectTo(base) {
|
||||
const validator = isA.string().max(512);
|
||||
let hostnameRegex = null;
|
||||
if (base) {
|
||||
hostnameRegex = new RegExp(`(?:\\.|^)${base.replace('.', '\\.')}$`);
|
||||
}
|
||||
validator._tests.push({
|
||||
func: (value, state, options) => {
|
||||
if (value !== undefined && value !== null) {
|
||||
if (isValidUrl(value, hostnameRegex)) {
|
||||
return value;
|
||||
}
|
||||
const validator = isA
|
||||
.string()
|
||||
.max(512)
|
||||
.custom((value) => {
|
||||
let hostnameRegex = '';
|
||||
if (base) {
|
||||
hostnameRegex = new RegExp(`(?:\\.|^)${base.replace('.', '\\.')}$`);
|
||||
}
|
||||
// Do your validation
|
||||
const isValid = isValidUrl(value, hostnameRegex);
|
||||
|
||||
return validator.createError('string.base', { value }, state, options);
|
||||
},
|
||||
});
|
||||
if (!isValid) {
|
||||
throw new Error('Not a valid URL');
|
||||
}
|
||||
return value;
|
||||
});
|
||||
return validator;
|
||||
};
|
||||
|
||||
module.exports.url = function url(options) {
|
||||
const validator = isA.string().uri(options);
|
||||
validator._tests.push({
|
||||
func: (value, state, options) => {
|
||||
if (value !== undefined && value !== null) {
|
||||
if (isValidUrl(value)) {
|
||||
return value;
|
||||
}
|
||||
const validator = isA
|
||||
.string()
|
||||
.uri(options)
|
||||
.custom((value) => {
|
||||
const isValid = isValidUrl(value);
|
||||
if (!isValid) {
|
||||
throw new Error('Not a valid URL');
|
||||
}
|
||||
|
||||
return validator.createError('string.base', { value }, state, options);
|
||||
},
|
||||
});
|
||||
return value;
|
||||
});
|
||||
return validator;
|
||||
};
|
||||
|
||||
|
@ -232,25 +225,22 @@ module.exports.url = function url(options) {
|
|||
module.exports.resourceUrl = module.exports.url().regex(/#/, { invert: true });
|
||||
|
||||
module.exports.pushCallbackUrl = function pushUrl(options) {
|
||||
const validator = isA.string().uri(options);
|
||||
validator._tests.push({
|
||||
func: (value, state, options) => {
|
||||
if (value !== undefined && value !== null) {
|
||||
let normalizedValue = value;
|
||||
// Fx Desktop registers https push urls with a :443 which causes `isValidUrl`
|
||||
// to fail because the :443 is expected to have been normalized away.
|
||||
if (/^https:\/\/[a-zA-Z0-9._-]+(:443)($|\/)/.test(value)) {
|
||||
normalizedValue = value.replace(':443', '');
|
||||
}
|
||||
|
||||
if (isValidUrl(normalizedValue)) {
|
||||
return value;
|
||||
}
|
||||
const validator = isA
|
||||
.string()
|
||||
.uri(options)
|
||||
.custom((value) => {
|
||||
let normalizedValue = value;
|
||||
// Fx Desktop registers https push urls with a :443 which causes `isValidUrl`
|
||||
// to fail because the :443 is expected to have been normalized away.
|
||||
if (/^https:\/\/[a-zA-Z0-9._-]+(:443)($|\/)/.test(value)) {
|
||||
normalizedValue = value.replace(':443', '');
|
||||
}
|
||||
|
||||
return validator.createError('string.base', { value }, state, options);
|
||||
},
|
||||
});
|
||||
const isValid = isValidUrl(normalizedValue);
|
||||
if (!isValid) {
|
||||
throw new Error('Not a valid URL');
|
||||
}
|
||||
return value;
|
||||
});
|
||||
return validator;
|
||||
};
|
||||
|
||||
|
@ -279,13 +269,13 @@ function isValidUrl(url, hostnameRegex) {
|
|||
return parsed.href;
|
||||
}
|
||||
|
||||
module.exports.verificationMethod = isA.string().valid([
|
||||
module.exports.verificationMethod = isA.string().valid(
|
||||
'email', // Verification by email link
|
||||
'email-otp', // Verification by email otp code using account long code (`emailCode`) as secret
|
||||
'email-2fa', // Verification by email code using randomly generated code (used in login flow)
|
||||
'email-captcha', // Verification by email code using randomly generated code (used in unblock flow)
|
||||
'totp-2fa', // Verification by TOTP authenticator device code, secret is randomly generated
|
||||
]);
|
||||
'totp-2fa' // Verification by TOTP authenticator device code, secret is randomly generated
|
||||
);
|
||||
|
||||
module.exports.authPW = isA.string().length(64).regex(HEX_STRING).required();
|
||||
module.exports.wrapKb = isA.string().length(64).regex(HEX_STRING);
|
||||
|
@ -486,12 +476,20 @@ module.exports.subscriptionProductMetadataValidator = {
|
|||
error: 'Capability missing from metadata',
|
||||
};
|
||||
}
|
||||
return module.exports.subscriptionProductMetadataBaseValidator.validate(
|
||||
metadata,
|
||||
{
|
||||
abortEarly: false,
|
||||
}
|
||||
);
|
||||
|
||||
const { value: result, error } =
|
||||
module.exports.subscriptionProductMetadataBaseValidator.validate(
|
||||
metadata,
|
||||
{
|
||||
abortEarly: false,
|
||||
}
|
||||
);
|
||||
|
||||
if (error) {
|
||||
return { error };
|
||||
}
|
||||
|
||||
return { result };
|
||||
},
|
||||
async validateAsync(metadata) {
|
||||
const hasCapability = Object.keys(metadata).some((k) =>
|
||||
|
@ -505,13 +503,11 @@ module.exports.subscriptionProductMetadataValidator = {
|
|||
}
|
||||
|
||||
try {
|
||||
const value = await isA.validate(
|
||||
metadata,
|
||||
module.exports.subscriptionProductMetadataBaseValidator,
|
||||
{
|
||||
abortEarly: false,
|
||||
}
|
||||
);
|
||||
const validationSchema =
|
||||
module.exports.subscriptionProductMetadataBaseValidator;
|
||||
const value = await validationSchema.validateAsync(metadata, {
|
||||
abortEarly: false,
|
||||
});
|
||||
return { value };
|
||||
} catch (error) {
|
||||
return { error };
|
||||
|
@ -567,7 +563,7 @@ module.exports.subscriptionsStripeIntentValidator = isA
|
|||
.alternatives(isA.string(), isA.object({}).unknown(true))
|
||||
.optional()
|
||||
.allow(null),
|
||||
source: isA.alternatives().when('payment_method', {
|
||||
source: isA.alternatives().conditional('payment_method', {
|
||||
// cannot be that strict here since this validator is used in two routes
|
||||
is: null,
|
||||
then: isA.string().optional(),
|
||||
|
@ -719,7 +715,7 @@ module.exports.newsletters = isA
|
|||
module.exports.thirdPartyProvider = isA
|
||||
.string()
|
||||
.max(256)
|
||||
.allow(['google', 'apple'])
|
||||
.allow('google', 'apple')
|
||||
.required();
|
||||
|
||||
module.exports.thirdPartyIdToken = module.exports.jwt.optional();
|
||||
|
|
|
@ -6,10 +6,9 @@
|
|||
|
||||
const fs = require('fs');
|
||||
const Hapi = require('@hapi/hapi');
|
||||
const joi = require('@hapi/joi');
|
||||
const HapiSwagger = require('hapi-swagger');
|
||||
const Inert = require('inert');
|
||||
const Vision = require('vision');
|
||||
const Inert = require('@hapi/inert');
|
||||
const Vision = require('@hapi/vision');
|
||||
const path = require('path');
|
||||
const url = require('url');
|
||||
const userAgent = require('./userAgent');
|
||||
|
@ -171,7 +170,7 @@ async function create(log, error, config, routes, db, statsd) {
|
|||
}
|
||||
|
||||
const server = new Hapi.Server(serverOptions);
|
||||
server.validator(require('@hapi/joi'));
|
||||
server.validator(require('joi'));
|
||||
|
||||
server.ext('onRequest', (request, h) => {
|
||||
log.begin('server.onRequest', request);
|
||||
|
@ -187,9 +186,7 @@ async function create(log, error, config, routes, db, statsd) {
|
|||
return xff
|
||||
.filter(Boolean)
|
||||
.map((address) => address.trim())
|
||||
.filter(
|
||||
(address) => !joi.validate(address, IP_ADDRESS.required()).error
|
||||
);
|
||||
.filter((address) => !IP_ADDRESS.required().validate(address).error);
|
||||
});
|
||||
|
||||
defineLazyGetter(request.app, 'clientAddress', () => {
|
||||
|
|
|
@ -61,7 +61,8 @@
|
|||
"@hapi/hapi": "^20.2.1",
|
||||
"@hapi/hawk": "^8.0.0",
|
||||
"@hapi/hoek": "^10.0.0",
|
||||
"@hapi/joi": "^15.1.1",
|
||||
"@hapi/inert": "^6.0.5",
|
||||
"@hapi/vision": "^6.1.0",
|
||||
"@sentry/integrations": "^6.19.1",
|
||||
"@sentry/node": "^6.19.1",
|
||||
"@type-cacheable/core": "^10.1.0",
|
||||
|
@ -91,12 +92,12 @@
|
|||
"googleapis": "^100.0.0",
|
||||
"hapi-auth-jwt2": "^10.2.0",
|
||||
"hapi-error": "^2.3.0",
|
||||
"hapi-swagger": "10.3.0",
|
||||
"hapi-swagger": "^14.4.0",
|
||||
"hkdf": "0.0.2",
|
||||
"hot-shots": "^9.0.0",
|
||||
"i18n-iso-countries": "^7.4.0",
|
||||
"inert": "^5.1.3",
|
||||
"ioredis": "^4.28.2",
|
||||
"joi": "17.4.0",
|
||||
"jose": "^4.8.1",
|
||||
"jsdom": "^19.0.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
|
@ -129,7 +130,6 @@
|
|||
"typedi": "^0.8.0",
|
||||
"uuid": "^8.3.2",
|
||||
"verror": "^1.10.1",
|
||||
"vision": "^5.4.4",
|
||||
"web-push": "3.4.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -144,8 +144,6 @@
|
|||
"@types/chai-as-promised": "^7",
|
||||
"@types/dedent": "^0",
|
||||
"@types/hapi__hapi": "^20.0.10",
|
||||
"@types/hapi__joi": "^15.0.4",
|
||||
"@types/inert": "^5",
|
||||
"@types/ioredis": "^4.26.4",
|
||||
"@types/jsdom": "^16",
|
||||
"@types/jsonwebtoken": "^8.5.1",
|
||||
|
@ -163,7 +161,6 @@
|
|||
"@types/sass": "^1",
|
||||
"@types/uuid": "^8.3.0",
|
||||
"@types/verror": "^1.10.4",
|
||||
"@types/vision": "^5",
|
||||
"@types/webpack": "5.28.0",
|
||||
"acorn": "^8.0.1",
|
||||
"async-retry": "^1.3.3",
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
'use strict';
|
||||
|
||||
const nock = require('nock');
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
const { assert } = require('chai');
|
||||
const { mockLog } = require('../mocks');
|
||||
const sinon = require('sinon');
|
||||
|
|
|
@ -6,16 +6,10 @@
|
|||
|
||||
const { assert } = require('chai');
|
||||
const verror = require('verror');
|
||||
const messages = require('@hapi/joi/lib/language');
|
||||
const AppError = require('../../lib/error');
|
||||
const OauthError = require('../../lib/oauth/error');
|
||||
|
||||
describe('AppErrors', () => {
|
||||
it('tightly-coupled joi message hack is okay', () => {
|
||||
assert.equal(typeof messages.errors.any.required, 'string');
|
||||
assert.notEqual(messages.errors.any.required, '');
|
||||
});
|
||||
|
||||
it('exported functions exist', () => {
|
||||
assert.equal(typeof AppError, 'function');
|
||||
assert.equal(AppError.length, 4);
|
||||
|
@ -53,7 +47,7 @@ describe('AppErrors', () => {
|
|||
const result = AppError.translate(null, {
|
||||
output: {
|
||||
payload: {
|
||||
message: `foo${messages.errors.any.required}`,
|
||||
message: `foo${'is required'}`,
|
||||
validation: {
|
||||
keys: ['bar', 'baz'],
|
||||
},
|
||||
|
|
|
@ -288,10 +288,7 @@ describe('PaymentConfigManager', () => {
|
|||
await paymentConfigManager.storePlanConfig(newPlan, randomUUID());
|
||||
assert.fail('should have thrown');
|
||||
} catch (err) {
|
||||
assert.equal(
|
||||
err.jse_cause.message,
|
||||
'child "active" fails because ["active" is required]'
|
||||
);
|
||||
assert.equal(err.jse_cause.message, '"active" is required');
|
||||
assert.equal(err.errno, errors.ERRNO.INTERNAL_VALIDATION_ERROR);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -7,10 +7,9 @@
|
|||
const sinon = require('sinon');
|
||||
const assert = { ...sinon.assert, ...require('chai').assert };
|
||||
const crypto = require('crypto');
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
const error = require('../../../lib/error');
|
||||
const getRoute = require('../../routes_helpers').getRoute;
|
||||
const isA = require('@hapi/joi');
|
||||
const mocks = require('../../mocks');
|
||||
const moment = require('moment'); // Ensure consistency with production code
|
||||
const proxyquire = require('proxyquire');
|
||||
|
@ -77,7 +76,8 @@ async function runTest(route, request, onSuccess, onError) {
|
|||
try {
|
||||
const response = await route.handler(request);
|
||||
if (route.options.response.schema) {
|
||||
await Joi.validate(response, route.options.response.schema);
|
||||
const validationSchema = route.options.response.schema;
|
||||
await validationSchema.validateAsync(response);
|
||||
}
|
||||
if (onSuccess) {
|
||||
onSuccess(response);
|
||||
|
@ -808,10 +808,8 @@ describe('/account/device/commands', () => {
|
|||
'/account/device/commands'
|
||||
);
|
||||
|
||||
mockRequest.query = isA.validate(
|
||||
mockRequest.query,
|
||||
route.options.validate.query
|
||||
).value;
|
||||
const validationSchema = route.options.validate.query;
|
||||
mockRequest.query = validationSchema.validate(mockRequest.query).value;
|
||||
assert.ok(mockRequest.query);
|
||||
return runTest(route, mockRequest).then((response) => {
|
||||
assert.equal(mockPushbox.retrieve.callCount, 1, 'pushbox was called');
|
||||
|
@ -911,10 +909,8 @@ describe('/account/device/commands', () => {
|
|||
'/account/device/commands'
|
||||
);
|
||||
|
||||
mockRequest.query = isA.validate(
|
||||
mockRequest.query,
|
||||
route.options.validate.query
|
||||
).value;
|
||||
const validationSchema = route.options.validate.query;
|
||||
mockRequest.query = validationSchema.validate(mockRequest.query).value;
|
||||
assert.ok(mockRequest.query);
|
||||
return runTest(route, mockRequest).then((response) => {
|
||||
assert.callCount(mockLog.info, 2);
|
||||
|
@ -1628,12 +1624,12 @@ describe('/account/devices', () => {
|
|||
pushEndpointExpired: false,
|
||||
},
|
||||
];
|
||||
isA.assert(res, route.options.response.schema);
|
||||
Joi.assert(res, route.options.response.schema);
|
||||
});
|
||||
|
||||
it('should allow returning approximateLastAccessTime', () => {
|
||||
const route = getRoute(makeRoutes({}), '/account/devices');
|
||||
isA.assert(
|
||||
Joi.assert(
|
||||
[
|
||||
{
|
||||
id: crypto.randomBytes(16).toString('hex'),
|
||||
|
@ -1653,7 +1649,7 @@ describe('/account/devices', () => {
|
|||
it('should not allow returning approximateLastAccessTime < EARLIEST_SANE_TIMESTAMP', () => {
|
||||
const route = getRoute(makeRoutes({}), '/account/devices');
|
||||
assert.throws(() =>
|
||||
isA.assert(
|
||||
Joi.assert(
|
||||
[
|
||||
{
|
||||
id: crypto.randomBytes(16).toString('hex'),
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
const ROOT_DIR = '../../..';
|
||||
|
||||
const sinon = require('sinon');
|
||||
const Joi = require('@hapi/joi');
|
||||
const assert = { ...sinon.assert, ...require('chai').assert };
|
||||
const getRoute = require('../../routes_helpers').getRoute;
|
||||
const mocks = require('../../mocks');
|
||||
|
@ -37,10 +36,10 @@ describe('/oauth/ routes', () => {
|
|||
);
|
||||
const route = await getRoute(routes, path);
|
||||
if (route.config.validate.payload) {
|
||||
const validationSchema = route.config.validate.payload;
|
||||
// eslint-disable-next-line require-atomic-updates
|
||||
request.payload = await Joi.validate(
|
||||
request.payload = await validationSchema.validateAsync(
|
||||
request.payload,
|
||||
route.config.validate.payload,
|
||||
{
|
||||
context: {
|
||||
headers: request.headers || {},
|
||||
|
|
|
@ -889,7 +889,9 @@ describe('/v1', function () {
|
|||
assert.equal(res.statusCode, 200);
|
||||
assertSecurityHeaders(res);
|
||||
assert(res.result.access_token);
|
||||
assert.isNull(validators.jwt.validate(res.result.access_token).error);
|
||||
assert.isUndefined(
|
||||
validators.jwt.validate(res.result.access_token).error
|
||||
);
|
||||
const jwt = decodeJWT(res.result.access_token);
|
||||
assert.strictEqual(jwt.claims.sub, USERID);
|
||||
assert.deepEqual(jwt.claims.aud, [
|
||||
|
@ -2133,6 +2135,7 @@ describe('/v1', function () {
|
|||
payload: {
|
||||
client_id: clientId,
|
||||
grant_type: 'fxa-credentials',
|
||||
scope: 'profile testme',
|
||||
},
|
||||
});
|
||||
assertInvalidRequestParam(res.result, 'assertion');
|
||||
|
@ -2186,6 +2189,7 @@ describe('/v1', function () {
|
|||
grant_type: 'fxa-credentials',
|
||||
ttl: 42,
|
||||
assertion: AN_ASSERTION,
|
||||
scope: 'profile testme',
|
||||
},
|
||||
});
|
||||
assertSecurityHeaders(res);
|
||||
|
@ -2202,6 +2206,7 @@ describe('/v1', function () {
|
|||
grant_type: 'fxa-credentials',
|
||||
ttl: MAX_TTL_S * 100,
|
||||
assertion: AN_ASSERTION,
|
||||
scope: 'profile testme',
|
||||
},
|
||||
});
|
||||
assertSecurityHeaders(res);
|
||||
|
@ -2234,6 +2239,7 @@ describe('/v1', function () {
|
|||
client_id: clientId,
|
||||
grant_type: 'fxa-credentials',
|
||||
assertion: AN_ASSERTION,
|
||||
scope: 'profile testme',
|
||||
},
|
||||
});
|
||||
assertSecurityHeaders(res);
|
||||
|
@ -3721,7 +3727,7 @@ describe('/v1', function () {
|
|||
assert.equal(tokenResult.statusCode, 200);
|
||||
assertSecurityHeaders(tokenResult);
|
||||
assert.ok(tokenResult.result.access_token);
|
||||
assert.isNull(
|
||||
assert.isUndefined(
|
||||
validators.jwt.validate(tokenResult.result.access_token).error
|
||||
);
|
||||
assert.strictEqual(tokenResult.result.token_type, 'bearer');
|
||||
|
@ -3808,7 +3814,7 @@ describe('/v1', function () {
|
|||
assert.equal(refreshTokenResult.statusCode, 200);
|
||||
assertSecurityHeaders(refreshTokenResult);
|
||||
assert.ok(refreshTokenResult.result.access_token);
|
||||
assert.isNull(
|
||||
assert.isUndefined(
|
||||
validators.jwt.validate(refreshTokenResult.result.access_token).error
|
||||
);
|
||||
assert.strictEqual(refreshTokenResult.result.token_type, 'bearer');
|
||||
|
@ -3878,7 +3884,7 @@ describe('/v1', function () {
|
|||
);
|
||||
|
||||
assert.equal(ppidTokenResult.statusCode, 200);
|
||||
assert.isNull(
|
||||
assert.isUndefined(
|
||||
validators.jwt.validate(ppidTokenResult.result.access_token).error
|
||||
);
|
||||
|
||||
|
@ -3916,7 +3922,7 @@ describe('/v1', function () {
|
|||
});
|
||||
|
||||
assert.equal(refreshTokenResult.statusCode, 200);
|
||||
assert.isNull(
|
||||
assert.isUndefined(
|
||||
validators.jwt.validate(refreshTokenResult.result.access_token).error
|
||||
);
|
||||
|
||||
|
@ -3957,7 +3963,7 @@ describe('/v1', function () {
|
|||
});
|
||||
|
||||
assert.equal(refreshTokenResult.statusCode, 200);
|
||||
assert.isNull(
|
||||
assert.isUndefined(
|
||||
validators.jwt.validate(refreshTokenResult.result.access_token).error
|
||||
);
|
||||
|
||||
|
@ -3988,7 +3994,7 @@ describe('/v1', function () {
|
|||
}
|
||||
);
|
||||
assert.equal(tokenResult.statusCode, 200);
|
||||
assert.isNull(
|
||||
assert.isUndefined(
|
||||
validators.jwt.validate(tokenResult.result.access_token).error
|
||||
);
|
||||
const tokenJWT = decodeJWT(tokenResult.result.access_token);
|
||||
|
@ -4009,7 +4015,7 @@ describe('/v1', function () {
|
|||
});
|
||||
|
||||
assert.equal(serverRotatedResult.statusCode, 200);
|
||||
assert.isNull(
|
||||
assert.isUndefined(
|
||||
validators.jwt.validate(serverRotatedResult.result.access_token).error
|
||||
);
|
||||
|
||||
|
@ -4034,7 +4040,7 @@ describe('/v1', function () {
|
|||
}
|
||||
);
|
||||
assert.equal(accessTokenResult.statusCode, 200);
|
||||
assert.isNull(
|
||||
assert.isUndefined(
|
||||
validators.jwt.validate(accessTokenResult.result.access_token).error
|
||||
);
|
||||
const accessTokenJWT = decodeJWT(accessTokenResult.result.access_token);
|
||||
|
@ -4051,7 +4057,7 @@ describe('/v1', function () {
|
|||
},
|
||||
});
|
||||
assert.equal(refreshTokenResult.statusCode, 200);
|
||||
assert.isNull(
|
||||
assert.isUndefined(
|
||||
validators.jwt.validate(refreshTokenResult.result.access_token).error
|
||||
);
|
||||
const refreshTokenJWT = decodeJWT(refreshTokenResult.result.access_token);
|
||||
|
@ -4074,7 +4080,7 @@ describe('/v1', function () {
|
|||
}
|
||||
);
|
||||
assert.equal(tokenResult.statusCode, 200);
|
||||
assert.isNull(
|
||||
assert.isUndefined(
|
||||
validators.jwt.validate(tokenResult.result.access_token).error
|
||||
);
|
||||
const tokenJWT = decodeJWT(tokenResult.result.access_token);
|
||||
|
@ -4090,7 +4096,7 @@ describe('/v1', function () {
|
|||
},
|
||||
});
|
||||
assert.equal(clientRotatedResult.statusCode, 200);
|
||||
assert.isNull(
|
||||
assert.isUndefined(
|
||||
validators.jwt.validate(clientRotatedResult.result.access_token).error
|
||||
);
|
||||
const clientRotatedJWT = decodeJWT(
|
||||
|
|
|
@ -252,14 +252,14 @@ describe('lib/keys', () => {
|
|||
|
||||
it('can generate new private keys', () => {
|
||||
const key = keys.generatePrivateKey();
|
||||
assert.strictEqual(keys.PRIVATE_KEY_SCHEMA.validate(key).error, null);
|
||||
assert.strictEqual(keys.PRIVATE_KEY_SCHEMA.validate(key).error, undefined);
|
||||
assert.ok(key['fxa-createdAt'] <= Date.now() / 1000);
|
||||
assert.ok(key['fxa-createdAt'] >= Date.now() / 1000 - 3600);
|
||||
});
|
||||
|
||||
it('can extract public keys', () => {
|
||||
const key = keys.extractPublicKey(keys.generatePrivateKey());
|
||||
assert.strictEqual(keys.PUBLIC_KEY_SCHEMA.validate(key).error, null);
|
||||
assert.notEqual(keys.PRIVATE_KEY_SCHEMA.validate(key).error, null);
|
||||
assert.strictEqual(keys.PUBLIC_KEY_SCHEMA.validate(key).error, undefined);
|
||||
assert.notEqual(keys.PRIVATE_KEY_SCHEMA.validate(key).error, undefined);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
'use strict';
|
||||
|
||||
const { assert } = require('chai');
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
|
||||
const CLIENT_ID = '98e6508e88680e1b';
|
||||
// jscs:disable
|
||||
|
@ -38,7 +38,6 @@ describe('/authorization POST', function () {
|
|||
Joi.assert(req, validation);
|
||||
} catch (err) {
|
||||
fail = true;
|
||||
assert.ok(err.isJoi);
|
||||
assert.ok(err.name, 'ValidationError');
|
||||
assert.equal(err.details[0].message, `"${param}" ${messagePostfix}`);
|
||||
}
|
||||
|
@ -140,7 +139,7 @@ describe('/authorization POST', function () {
|
|||
code_challenge_method: 'bad_method',
|
||||
},
|
||||
'code_challenge_method',
|
||||
'must be one of [S256]'
|
||||
'must be [S256]'
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const { assert } = require('chai');
|
||||
const Joi = require('@hapi/joi');
|
||||
|
||||
const CLIENT_SECRET =
|
||||
'b93ef8a8f3e553a430d7e5b904c6132b2722633af9f03128029201d24a97f2a8';
|
||||
|
@ -30,14 +29,14 @@ const route = require('../../../lib/routes/oauth/token')({
|
|||
})[0];
|
||||
|
||||
function joiRequired(err, param) {
|
||||
assert.ok(err.isJoi);
|
||||
assert.ok(err.name, 'ValidationError');
|
||||
assert.isTrue(err.isJoi);
|
||||
assert.equal(err.name, 'ValidationError');
|
||||
assert.equal(err.details[0].message, `"${param}" is required`);
|
||||
}
|
||||
|
||||
function joiNotAllowed(err, param) {
|
||||
assert.ok(err.isJoi);
|
||||
assert.ok(err.name, 'ValidationError');
|
||||
assert.isTrue(err.isJoi);
|
||||
assert.equal(err.name, 'ValidationError');
|
||||
assert.equal(err.details[0].message, `"${param}" is not allowed`);
|
||||
}
|
||||
|
||||
|
@ -49,51 +48,39 @@ describe('/token POST', function () {
|
|||
cb = ctx;
|
||||
ctx = undefined;
|
||||
}
|
||||
Joi.validate(req, route.config.validate.payload, { context: ctx }, cb);
|
||||
const validationSchema = route.config.validate.payload;
|
||||
return validationSchema.validate(req, { context: ctx }, cb);
|
||||
}
|
||||
|
||||
it('fails with no client_id', (done) => {
|
||||
v(
|
||||
{
|
||||
client_secret: CLIENT_SECRET,
|
||||
code: CODE,
|
||||
},
|
||||
(err) => {
|
||||
joiRequired(err, 'client_id');
|
||||
done();
|
||||
}
|
||||
);
|
||||
it('fails with no client_id', () => {
|
||||
const res = v({
|
||||
client_secret: CLIENT_SECRET,
|
||||
code: CODE,
|
||||
});
|
||||
joiRequired(res.error, 'client_id');
|
||||
});
|
||||
|
||||
it('valid client_secret scheme', (done) => {
|
||||
v(
|
||||
{
|
||||
client_id: CLIENT_ID,
|
||||
client_secret: CLIENT_SECRET,
|
||||
code: CODE,
|
||||
},
|
||||
(err) => {
|
||||
assert.equal(err, null);
|
||||
done();
|
||||
}
|
||||
);
|
||||
it('valid client_secret scheme', () => {
|
||||
const res = v({
|
||||
client_id: CLIENT_ID,
|
||||
client_secret: CLIENT_SECRET,
|
||||
code: CODE,
|
||||
});
|
||||
|
||||
assert.equal(res.error, undefined);
|
||||
});
|
||||
|
||||
it('requires client_secret', (done) => {
|
||||
v(
|
||||
{
|
||||
client_id: CLIENT_ID,
|
||||
code: CODE,
|
||||
},
|
||||
(err) => {
|
||||
joiRequired(err, 'client_secret');
|
||||
done();
|
||||
}
|
||||
);
|
||||
it('requires client_secret', () => {
|
||||
const res = v({
|
||||
client_id: CLIENT_ID,
|
||||
code: CODE,
|
||||
});
|
||||
|
||||
joiRequired(res.error, 'client_secret');
|
||||
});
|
||||
|
||||
it('forbids client_id when authz header provided', (done) => {
|
||||
v(
|
||||
it('forbids client_id when authz header provided', () => {
|
||||
const res = v(
|
||||
{
|
||||
client_id: CLIENT_ID,
|
||||
},
|
||||
|
@ -101,16 +88,14 @@ describe('/token POST', function () {
|
|||
headers: {
|
||||
authorization: 'Basic ABCDEF',
|
||||
},
|
||||
},
|
||||
(err) => {
|
||||
joiNotAllowed(err, 'client_id');
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
joiNotAllowed(res.error, 'client_id');
|
||||
});
|
||||
|
||||
it('forbids client_secret when authz header provided', (done) => {
|
||||
v(
|
||||
it('forbids client_secret when authz header provided', () => {
|
||||
const res = v(
|
||||
{
|
||||
client_secret: CLIENT_SECRET,
|
||||
code: CODE, // If we don't send `code`, then the missing `code` will fail validation first.
|
||||
|
@ -119,92 +104,75 @@ describe('/token POST', function () {
|
|||
headers: {
|
||||
authorization: 'Basic ABCDEF',
|
||||
},
|
||||
},
|
||||
(err) => {
|
||||
joiNotAllowed(err, 'client_secret');
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
joiNotAllowed(res.error, 'client_secret');
|
||||
});
|
||||
|
||||
describe('pkce', () => {
|
||||
it('accepts pkce code_verifier instead of client_secret', (done) => {
|
||||
v(
|
||||
{
|
||||
client_id: CLIENT_ID,
|
||||
code_verifier: PKCE_CODE_VERIFIER,
|
||||
code: CODE,
|
||||
},
|
||||
(err) => {
|
||||
assert.equal(err, null);
|
||||
done();
|
||||
}
|
||||
);
|
||||
it('accepts pkce code_verifier instead of client_secret', () => {
|
||||
const res = v({
|
||||
client_id: CLIENT_ID,
|
||||
code_verifier: PKCE_CODE_VERIFIER,
|
||||
code: CODE,
|
||||
});
|
||||
|
||||
assert.equal(res.error, undefined);
|
||||
});
|
||||
|
||||
it('rejects pkce code_verifier that is too small', (done) => {
|
||||
it('rejects pkce code_verifier that is too small', () => {
|
||||
const bad_code_verifier = PKCE_CODE_VERIFIER.substring(0, 32);
|
||||
v(
|
||||
{
|
||||
client_id: CLIENT_ID,
|
||||
code_verifier: bad_code_verifier,
|
||||
code: CODE,
|
||||
},
|
||||
(err) => {
|
||||
assert.ok(err.isJoi);
|
||||
assert.ok(err.name, 'ValidationError');
|
||||
assert.equal(
|
||||
err.details[0].message,
|
||||
// eslint-disable-next-line quotes
|
||||
`"code_verifier" length must be at least 43 characters long`
|
||||
); // eslint-disable-line quotes
|
||||
done();
|
||||
}
|
||||
);
|
||||
const res = v({
|
||||
client_id: CLIENT_ID,
|
||||
code_verifier: bad_code_verifier,
|
||||
code: CODE,
|
||||
});
|
||||
|
||||
assert.isTrue(res.error.isJoi);
|
||||
assert.equal(res.error.name, 'ValidationError');
|
||||
assert.equal(
|
||||
res.error.details[0].message,
|
||||
// eslint-disable-next-line quotes
|
||||
`"code_verifier" length must be at least 43 characters long`
|
||||
); // eslint-disable-line quotes
|
||||
});
|
||||
|
||||
it('rejects pkce code_verifier that is too big', (done) => {
|
||||
it('rejects pkce code_verifier that is too big', () => {
|
||||
const bad_code_verifier =
|
||||
PKCE_CODE_VERIFIER +
|
||||
PKCE_CODE_VERIFIER +
|
||||
PKCE_CODE_VERIFIER +
|
||||
PKCE_CODE_VERIFIER;
|
||||
v(
|
||||
{
|
||||
client_id: CLIENT_ID,
|
||||
code_verifier: bad_code_verifier,
|
||||
code: CODE,
|
||||
},
|
||||
(err) => {
|
||||
assert.ok(err.isJoi);
|
||||
assert.ok(err.name, 'ValidationError');
|
||||
assert.equal(
|
||||
err.details[0].message,
|
||||
// eslint-disable-next-line quotes
|
||||
`"code_verifier" length must be less than or equal to 128 characters long`
|
||||
); // eslint-disable-line quotes
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
const res = v({
|
||||
client_id: CLIENT_ID,
|
||||
code_verifier: bad_code_verifier,
|
||||
code: CODE,
|
||||
});
|
||||
|
||||
assert.isTrue(res.error.isJoi);
|
||||
assert.equal(res.error.name, 'ValidationError');
|
||||
assert.equal(
|
||||
res.error.details[0].message,
|
||||
// eslint-disable-next-line quotes
|
||||
`"code_verifier" length must be less than or equal to 128 characters long`
|
||||
); // eslint-disable-line quotes
|
||||
});
|
||||
|
||||
it('rejects pkce code_verifier that contains invalid characters', (done) => {
|
||||
it('rejects pkce code_verifier that contains invalid characters', () => {
|
||||
const bad_code_verifier = PKCE_CODE_VERIFIER + ' :.';
|
||||
v(
|
||||
{
|
||||
client_id: CLIENT_ID,
|
||||
code_verifier: bad_code_verifier,
|
||||
code: CODE,
|
||||
},
|
||||
(err) => {
|
||||
assert.ok(err.isJoi);
|
||||
assert.ok(err.name, 'ValidationError');
|
||||
assert.equal(
|
||||
err.details[0].message,
|
||||
`"code_verifier" with value "${bad_code_verifier}" fails to match the required pattern: /^[A-Za-z0-9-_]+$/`
|
||||
);
|
||||
done();
|
||||
}
|
||||
const res = v({
|
||||
client_id: CLIENT_ID,
|
||||
code_verifier: bad_code_verifier,
|
||||
code: CODE,
|
||||
});
|
||||
|
||||
assert.isTrue(res.error.isJoi);
|
||||
assert.equal(res.error.name, 'ValidationError');
|
||||
assert.equal(
|
||||
res.error.details[0].message,
|
||||
`"code_verifier" with value "${bad_code_verifier}" fails to match the required pattern: /^[A-Za-z0-9-_]+$/`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const { assert } = require('chai');
|
||||
const Joi = require('@hapi/joi');
|
||||
const proxyquire = require('proxyquire');
|
||||
const sinon = require('sinon');
|
||||
const ScopeSet = require('fxa-shared').oauth.scopes;
|
||||
|
@ -12,8 +11,8 @@ const TOKEN =
|
|||
'df6dcfe7bf6b54a65db5742cbcdce5c0a84a5da81a0bb6bdf5fc793eef041fc6';
|
||||
|
||||
function joiRequired(err, param) {
|
||||
assert.ok(err.isJoi);
|
||||
assert.ok(err.name, 'ValidationError');
|
||||
assert.isTrue(err.isJoi);
|
||||
assert.equal(err.name, 'ValidationError');
|
||||
assert.strictEqual(err.details[0].message, `"${param}" is required`);
|
||||
}
|
||||
|
||||
|
@ -59,7 +58,8 @@ describe('/verify POST', () => {
|
|||
|
||||
describe('validation', () => {
|
||||
function validate(req, context = {}) {
|
||||
const result = Joi.validate(req, route.config.validate.payload, {
|
||||
const validationSchema = route.config.validate.payload;
|
||||
const result = validationSchema.validate(req, {
|
||||
context,
|
||||
});
|
||||
return result.error;
|
||||
|
@ -76,7 +76,7 @@ describe('/verify POST', () => {
|
|||
const err = validate({
|
||||
token: TOKEN,
|
||||
});
|
||||
assert.strictEqual(err, null);
|
||||
assert.strictEqual(err, undefined);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -532,6 +532,7 @@ describe('StripeProductsAndPlansConverter', () => {
|
|||
Container.reset();
|
||||
sandbox.reset();
|
||||
});
|
||||
|
||||
it('processes new products and plans', async () => {
|
||||
await converter.convert(args);
|
||||
products = await paymentConfigManager.allProducts();
|
||||
|
@ -567,6 +568,7 @@ describe('StripeProductsAndPlansConverter', () => {
|
|||
productConfigId: products[1].id,
|
||||
});
|
||||
});
|
||||
|
||||
it('updates existing products and plans', async () => {
|
||||
// Put some configs into Firestore
|
||||
const productConfigDocId1 = await paymentConfigManager.storeProductConfig(
|
||||
|
@ -638,6 +640,7 @@ describe('StripeProductsAndPlansConverter', () => {
|
|||
productConfigId: products[0].id,
|
||||
});
|
||||
});
|
||||
|
||||
it('processes only the product with productId when passed', async () => {
|
||||
await converter.convert({ ...args, productId: product1.id });
|
||||
sinon.assert.calledOnceWithExactly(
|
||||
|
@ -645,6 +648,7 @@ describe('StripeProductsAndPlansConverter', () => {
|
|||
{ ids: [product1.id] }
|
||||
);
|
||||
});
|
||||
|
||||
it('does not update Firestore if dryRun = true', async () => {
|
||||
paymentConfigManager.storeProductConfig = sandbox.stub();
|
||||
paymentConfigManager.storePlanConfig = sandbox.stub();
|
||||
|
@ -653,6 +657,7 @@ describe('StripeProductsAndPlansConverter', () => {
|
|||
sinon.assert.notCalled(paymentConfigManager.storeProductConfig);
|
||||
sinon.assert.notCalled(paymentConfigManager.storePlanConfig);
|
||||
});
|
||||
|
||||
it('moves localized data from plans into the productConfig', async () => {
|
||||
const productWithRequiredKeys = {
|
||||
...deepCopy(product1),
|
||||
|
@ -711,6 +716,7 @@ describe('StripeProductsAndPlansConverter', () => {
|
|||
};
|
||||
assert.deepEqual(products[0].locales, expected);
|
||||
});
|
||||
|
||||
it('logs an error and keeps processing if a product fails', async () => {
|
||||
const productConfigId = 'test-product-id';
|
||||
const planConfigId = 'test-plan-id';
|
||||
|
@ -760,6 +766,7 @@ describe('StripeProductsAndPlansConverter', () => {
|
|||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('logs an error and keeps processing if a plan fails', async () => {
|
||||
const productConfigId = 'test-product-id';
|
||||
const planConfigId = 'test-plan-id';
|
||||
|
|
|
@ -98,7 +98,7 @@
|
|||
"hot-shots": "^9.0.0",
|
||||
"http-proxy-middleware": "^2.0.0",
|
||||
"i18n-abide": "0.0.26",
|
||||
"joi": "^14.3.1",
|
||||
"joi": "17.4.0",
|
||||
"jquery": "3.6.0",
|
||||
"jquery-modal": "https://github.com/mozilla-fxa/jquery-modal.git#0576775d1b4590314b114386019f4c7421c77503",
|
||||
"jquery-simulate": "1.0.2",
|
||||
|
|
|
@ -29,6 +29,7 @@ const PATTERNS = {
|
|||
SERVICE: /^([a-zA-Z0-9\-]{1,16})$/,
|
||||
SYNC_ENGINE: /^[a-z]+$/,
|
||||
UNIQUE_USER_ID: /^[0-9a-z-]{36}$/,
|
||||
UTM: /^[\w\/.%-]+$/,
|
||||
};
|
||||
|
||||
const TYPES = {
|
||||
|
@ -37,7 +38,7 @@ const TYPES = {
|
|||
BOOLEAN: joi.boolean(),
|
||||
DIMENSION: joi.number().integer().min(0),
|
||||
DOMAIN: joi.string().max(32).regex(PATTERNS.DOMAIN),
|
||||
EXPERIMENT: joi.string().valid(EXPERIMENT_NAMES),
|
||||
EXPERIMENT: joi.string().valid(...EXPERIMENT_NAMES),
|
||||
FLOW_ID: joi.string().hex().length(64),
|
||||
HEX32: joi.string().regex(/^[0-9a-f]{32}$/),
|
||||
INTEGER: joi.number().integer(),
|
||||
|
@ -66,7 +67,7 @@ const TYPES = {
|
|||
.string()
|
||||
.max(128)
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
.regex(/^[\w\/.%-]+$/), // values here can be 'firefox/sync'
|
||||
.regex(PATTERNS.UTM), // values here can be 'firefox/sync'
|
||||
};
|
||||
|
||||
// the crazy long allow comes from the firstrun page.
|
||||
|
|
|
@ -564,6 +564,9 @@ async function testInvalidFlowQueryParam(paramName, paramValue) {
|
|||
assert.fail('request should have failed');
|
||||
} catch (err) {
|
||||
assert.equal(err.response.statusCode, 400);
|
||||
assert.include(JSON.parse(err.response.body).validation.keys, paramName);
|
||||
assert.include(
|
||||
JSON.parse(err.response.body).validation.query.keys,
|
||||
paramName
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,16 +11,16 @@ const validation = require('../../server/lib/validation');
|
|||
|
||||
const METRICS_DOCS_URL =
|
||||
'https://raw.githubusercontent.com/mozilla/ecosystem-platform/master/docs/relying-parties/reference/metrics-for-relying-parties.md';
|
||||
const UTM_REGEX = validation.TYPES.UTM._tests[1].arg.pattern;
|
||||
|
||||
const REGEXES = new Map([
|
||||
['entrypoint', validation.PATTERNS.ENTRYPOINT],
|
||||
['entrypoint_experiment', validation.PATTERNS.ENTRYPOINT],
|
||||
['entrypoint_variation', validation.PATTERNS.ENTRYPOINT],
|
||||
['utm_campaign', UTM_REGEX],
|
||||
['utm_content', UTM_REGEX],
|
||||
['utm_medium', UTM_REGEX],
|
||||
['utm_source', UTM_REGEX],
|
||||
['utm_term', UTM_REGEX],
|
||||
['utm_campaign', validation.PATTERNS.UTM],
|
||||
['utm_content', validation.PATTERNS.UTM],
|
||||
['utm_medium', validation.PATTERNS.UTM],
|
||||
['utm_source', validation.PATTERNS.UTM],
|
||||
['utm_term', validation.PATTERNS.UTM],
|
||||
]);
|
||||
|
||||
let docs;
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
"google-auth-library": "^8.0.2",
|
||||
"graphql": "^15.6.1",
|
||||
"hot-shots": "^9.0.0",
|
||||
"joi": "17.4.0",
|
||||
"jwks-rsa": "^2.1.1",
|
||||
"mozlog": "^3.0.2",
|
||||
"passport": "^0.5.2",
|
||||
|
|
|
@ -124,7 +124,7 @@
|
|||
"@stripe/react-stripe-js": "^1.7.1",
|
||||
"@stripe/stripe-js": "^1.29.0",
|
||||
"accept-language-parser": "^1.5.0",
|
||||
"celebrate": "^10.1.0",
|
||||
"celebrate": "^15.0.1",
|
||||
"classnames": "^2.3.1",
|
||||
"convict": "^6.2.2",
|
||||
"convict-format-with-moment": "^6.2.0",
|
||||
|
@ -140,7 +140,7 @@
|
|||
"helmet": "^5.0.0",
|
||||
"hot-shots": "^9.0.0",
|
||||
"intl-pluralrules": "^1.3.1",
|
||||
"joi": "^14.3.1",
|
||||
"joi": "17.4.0",
|
||||
"jquery-modal": "https://github.com/mozilla-fxa/jquery-modal.git#0576775d1b4590314b114386019f4c7421c77503",
|
||||
"morgan": "^1.10.0",
|
||||
"mozlog": "^3.0.2",
|
||||
|
|
|
@ -81,7 +81,7 @@ module.exports = {
|
|||
method: 'get',
|
||||
path: '/legal-docs',
|
||||
validate: {
|
||||
query: { url: joi.string().uri().required() },
|
||||
query: joi.object({ url: joi.string().uri().required() }),
|
||||
},
|
||||
async process(req, res) {
|
||||
const docUrl = new URL(req.query.url);
|
||||
|
|
|
@ -35,11 +35,11 @@ describe('navigation-timing route', () => {
|
|||
describe('request body validation', () => {
|
||||
test('should pass when given a validate request body', () => {
|
||||
const result = route.validate.body.validate(validRequestBody);
|
||||
expect(result.error).toBeNull();
|
||||
expect(result.error).toBeUndefined();
|
||||
});
|
||||
test('should fail when given an invalidate request body', () => {
|
||||
const result = route.validate.body.validate(invalidRequestBody);
|
||||
expect(result.error).not.toBeNull();
|
||||
expect(result.error).not.toBeUndefined();
|
||||
});
|
||||
});
|
||||
describe('handler', () => {
|
||||
|
|
|
@ -18,10 +18,9 @@ const UTM = joi
|
|||
// eslint-disable-next-line no-useless-escape
|
||||
.regex(/^[\w\/.%-]+$/); // values here can be 'firefox/sync'
|
||||
const UTM_CAMPAIGN = UTM.allow('page+referral+-+not+part+of+a+campaign');
|
||||
const BODY_SCHEMA = {
|
||||
const BODY_SCHEMA = joi.object({
|
||||
data: joi
|
||||
.object()
|
||||
.keys({
|
||||
.object({
|
||||
flowBeginTime: OFFSET_TYPE.optional(),
|
||||
flowId: STRING_TYPE.hex().length(64).optional(),
|
||||
utm_campaign: UTM_CAMPAIGN.optional(),
|
||||
|
@ -41,7 +40,7 @@ const BODY_SCHEMA = {
|
|||
})
|
||||
)
|
||||
.required(),
|
||||
};
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
method: 'post',
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
|
||||
const AppError = require('../error');
|
||||
const config = require('../config');
|
||||
|
@ -29,7 +29,7 @@ module.exports = {
|
|||
],
|
||||
},
|
||||
response: {
|
||||
schema: {
|
||||
schema: Joi.object({
|
||||
email: Joi.string().optional(),
|
||||
locale: Joi.string().optional(),
|
||||
amrValues: Joi.array().items(Joi.string().required()).optional(),
|
||||
|
@ -38,7 +38,7 @@ module.exports = {
|
|||
subscriptionsByClientId: Joi.object().unknown(true).optional(),
|
||||
profileChangedAt: Joi.number().optional(),
|
||||
metricsEnabled: Joi.boolean().optional(),
|
||||
},
|
||||
}),
|
||||
},
|
||||
handler: async function _core_profile(req) {
|
||||
function makeReq() {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const hex = require('buf').to.hex;
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
const P = require('../../promise');
|
||||
|
||||
const AppError = require('../../error');
|
||||
|
@ -24,9 +24,9 @@ module.exports = {
|
|||
scope: ['profile:avatar:write'],
|
||||
},
|
||||
validate: {
|
||||
params: {
|
||||
params: Joi.object({
|
||||
id: Joi.string().length(32).regex(validate.hex).optional(),
|
||||
},
|
||||
}),
|
||||
},
|
||||
handler: async function deleteAvatar(req) {
|
||||
if (req.params.id === DEFAULT_AVATAR_ID) {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
|
||||
const db = require('../../db');
|
||||
const hex = require('buf').to.hex;
|
||||
|
@ -28,11 +28,11 @@ module.exports = {
|
|||
scope: ['profile:avatar'],
|
||||
},
|
||||
response: {
|
||||
schema: {
|
||||
schema: Joi.object({
|
||||
id: Joi.string().regex(validate.hex).length(32),
|
||||
avatarDefault: Joi.boolean(),
|
||||
avatar: Joi.string().max(256),
|
||||
},
|
||||
}),
|
||||
},
|
||||
handler: async function avatar(req, h) {
|
||||
var uid = req.auth.credentials.user;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
const assert = require('assert');
|
||||
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
|
||||
const config = require('../../config');
|
||||
const db = require('../../db');
|
||||
|
@ -41,10 +41,10 @@ module.exports = {
|
|||
maxBytes: config.get('img.uploads.maxSize'),
|
||||
},
|
||||
response: {
|
||||
schema: {
|
||||
schema: Joi.object({
|
||||
id: Joi.string().regex(validate.hex).length(32),
|
||||
url: Joi.string().required(),
|
||||
},
|
||||
}),
|
||||
},
|
||||
handler: async function upload(req, h) {
|
||||
const uid = req.auth.credentials.user;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
const checksum = require('checksum');
|
||||
|
||||
const db = require('../../db');
|
||||
|
@ -13,9 +13,9 @@ module.exports = {
|
|||
scope: ['profile:display_name'],
|
||||
},
|
||||
response: {
|
||||
schema: {
|
||||
schema: Joi.object({
|
||||
displayName: Joi.string().max(256),
|
||||
},
|
||||
}),
|
||||
},
|
||||
handler: async function displayNameGet(req, h) {
|
||||
return db.getDisplayName(req.auth.credentials.user).then(function (result) {
|
||||
|
|
|
@ -2,55 +2,55 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Joi = require('@hapi/joi');
|
||||
const db = require('../../db');
|
||||
const notifyProfileUpdated = require('../../updates-queue');
|
||||
const Joi = require('joi');
|
||||
const db = require('../../db');
|
||||
const notifyProfileUpdated = require('../../updates-queue');
|
||||
|
||||
const EMPTY = Object.create(null);
|
||||
const EMPTY = Object.create(null);
|
||||
|
||||
// We're pretty liberal with what's allowed in a display-name,
|
||||
// but we exclude the following classes of characters:
|
||||
//
|
||||
// \u0000-\u001F - C0 (ascii) control characters
|
||||
// \u007F - ascii DEL character
|
||||
// \u0080-\u009F - C1 (ansi escape) control characters
|
||||
// \u2028-\u2029 - unicode line/paragraph separator
|
||||
// \uE000-\uF8FF - BMP private use area
|
||||
// \uFFF9-\uFFFC - unicode specials prior to the replacement character
|
||||
// \uFFFE-\uFFFF - unicode this-is-not-a-character specials
|
||||
//
|
||||
// Note that the unicode replacement character \uFFFD is explicitly allowed,
|
||||
// and clients may use it to replace other disallowed characters.
|
||||
//
|
||||
// We might tweak this list in future.
|
||||
// We're pretty liberal with what's allowed in a display-name,
|
||||
// but we exclude the following classes of characters:
|
||||
//
|
||||
// \u0000-\u001F - C0 (ascii) control characters
|
||||
// \u007F - ascii DEL character
|
||||
// \u0080-\u009F - C1 (ansi escape) control characters
|
||||
// \u2028-\u2029 - unicode line/paragraph separator
|
||||
// \uE000-\uF8FF - BMP private use area
|
||||
// \uFFF9-\uFFFC - unicode specials prior to the replacement character
|
||||
// \uFFFE-\uFFFF - unicode this-is-not-a-character specials
|
||||
//
|
||||
// Note that the unicode replacement character \uFFFD is explicitly allowed,
|
||||
// and clients may use it to replace other disallowed characters.
|
||||
//
|
||||
// We might tweak this list in future.
|
||||
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const ALLOWED_DISPLAY_NAME_CHARS = /^(?:[^\u0000-\u001F\u007F\u0080-\u009F\u2028-\u2029\uE000-\uF8FF\uFFF9-\uFFFC\uFFFE-\uFFFF])*$/;
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const ALLOWED_DISPLAY_NAME_CHARS = /^(?:[^\u0000-\u001F\u007F\u0080-\u009F\u2028-\u2029\uE000-\uF8FF\uFFF9-\uFFFC\uFFFE-\uFFFF])*$/;
|
||||
|
||||
module.exports = {
|
||||
auth: {
|
||||
strategy: 'secretBearerToken',
|
||||
},
|
||||
validate: {
|
||||
payload: {
|
||||
name: Joi.string()
|
||||
.max(256)
|
||||
.required()
|
||||
.allow('')
|
||||
.regex(ALLOWED_DISPLAY_NAME_CHARS),
|
||||
},
|
||||
params: {
|
||||
uid: Joi.string(),
|
||||
}
|
||||
},
|
||||
handler: async function displayNamePost(req) {
|
||||
const uid = req.params.uid;
|
||||
return req.server.methods.profileCache.drop(uid).then(() => {
|
||||
const payload = req.payload;
|
||||
return db.setDisplayName(uid, payload.name).then(() => {
|
||||
notifyProfileUpdated(uid); // Don't wait on promise
|
||||
return EMPTY;
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
module.exports = {
|
||||
auth: {
|
||||
strategy: 'secretBearerToken',
|
||||
},
|
||||
validate: {
|
||||
payload: {
|
||||
name: Joi.string()
|
||||
.max(256)
|
||||
.required()
|
||||
.allow('')
|
||||
.regex(ALLOWED_DISPLAY_NAME_CHARS),
|
||||
},
|
||||
params: {
|
||||
uid: Joi.string(),
|
||||
}
|
||||
},
|
||||
handler: async function displayNamePost(req) {
|
||||
const uid = req.params.uid;
|
||||
return req.server.methods.profileCache.drop(uid).then(() => {
|
||||
const payload = req.payload;
|
||||
return db.setDisplayName(uid, payload.name).then(() => {
|
||||
notifyProfileUpdated(uid); // Don't wait on promise
|
||||
return EMPTY;
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
const db = require('../../db');
|
||||
const notifyProfileUpdated = require('../../updates-queue');
|
||||
|
||||
|
@ -33,13 +33,13 @@ module.exports = {
|
|||
scope: ['profile:display_name:write'],
|
||||
},
|
||||
validate: {
|
||||
payload: {
|
||||
payload: Joi.object({
|
||||
displayName: Joi.string()
|
||||
.max(256)
|
||||
.required()
|
||||
.allow('')
|
||||
.regex(ALLOWED_DISPLAY_NAME_CHARS),
|
||||
},
|
||||
}),
|
||||
},
|
||||
handler: async function displayNamePost(req) {
|
||||
const uid = req.auth.credentials.user;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
|
||||
const AppError = require('../error');
|
||||
const logger = require('../logging')('routes.email');
|
||||
|
@ -13,9 +13,9 @@ module.exports = {
|
|||
scope: ['profile:email', /* openid-connect scope */ 'email'],
|
||||
},
|
||||
response: {
|
||||
schema: {
|
||||
schema: Joi.object({
|
||||
email: Joi.string().required(),
|
||||
},
|
||||
}),
|
||||
},
|
||||
handler: async function email(req) {
|
||||
return req.server
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
const crypto = require('crypto');
|
||||
const checksum = require('checksum');
|
||||
const {
|
||||
|
@ -60,7 +60,7 @@ module.exports = {
|
|||
strategy: 'oauth',
|
||||
},
|
||||
response: {
|
||||
schema: {
|
||||
schema: Joi.object({
|
||||
email: Joi.string().allow(null),
|
||||
uid: Joi.string().allow(null),
|
||||
avatar: Joi.string().allow(null),
|
||||
|
@ -74,7 +74,7 @@ module.exports = {
|
|||
|
||||
//openid-connect
|
||||
sub: Joi.string().allow(null),
|
||||
},
|
||||
}),
|
||||
},
|
||||
handler: async function profile(req, h) {
|
||||
const server = req.server;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
const exec = require('child_process').exec;
|
||||
const path = require('path');
|
||||
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
|
||||
const version = require('../../package.json').version;
|
||||
let commitHash, source;
|
||||
|
@ -21,11 +21,11 @@ try {
|
|||
|
||||
module.exports = {
|
||||
response: {
|
||||
schema: {
|
||||
schema: Joi.object({
|
||||
version: Joi.string().required(),
|
||||
commit: Joi.string().required(),
|
||||
source: Joi.string().required(),
|
||||
},
|
||||
}),
|
||||
},
|
||||
handler: async function index(req, h) {
|
||||
function sendReply() {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
const {
|
||||
determineClientVisibleSubscriptionCapabilities,
|
||||
} = require('../subscriptions');
|
||||
|
@ -13,9 +13,9 @@ module.exports = {
|
|||
scope: ['profile:subscriptions'],
|
||||
},
|
||||
response: {
|
||||
schema: {
|
||||
schema: Joi.object({
|
||||
subscriptions: Joi.array().items(Joi.string()).required(),
|
||||
},
|
||||
}),
|
||||
},
|
||||
handler: async function subscriptions(req) {
|
||||
const res = await req.server.inject({
|
||||
|
|
|
@ -6,6 +6,7 @@ const Hapi = require('@hapi/hapi');
|
|||
const Boom = require('@hapi/boom');
|
||||
const path = require('path');
|
||||
const Inert = require('@hapi/inert');
|
||||
const Joi = require('joi');
|
||||
|
||||
const config = require('../config').getProperties();
|
||||
const logger = require('../logging')('server.static');
|
||||
|
@ -37,7 +38,7 @@ exports.create = async function () {
|
|||
},
|
||||
},
|
||||
});
|
||||
server.validator(require('@hapi/joi'));
|
||||
server.validator(Joi);
|
||||
|
||||
await server.register(Inert);
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ exports.create = async function createServer() {
|
|||
},
|
||||
},
|
||||
});
|
||||
server.validator(require('@hapi/joi'));
|
||||
server.validator(require('joi'));
|
||||
|
||||
// configure Sentry
|
||||
if (config.sentry && config.sentry.dsn) {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Hapi = require('@hapi/hapi');
|
||||
const Joi = require('@hapi/joi');
|
||||
const Joi = require('joi');
|
||||
const P = require('../promise');
|
||||
|
||||
const AppError = require('../error');
|
||||
|
@ -29,7 +29,7 @@ exports.create = async function () {
|
|||
},
|
||||
},
|
||||
});
|
||||
server.validator(require('@hapi/joi'));
|
||||
server.validator(Joi);
|
||||
|
||||
server.route({
|
||||
method: 'GET',
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
"@hapi/catbox-redis": "~6.0.2",
|
||||
"@hapi/hapi": "^20.2.1",
|
||||
"@hapi/inert": "6.0.5",
|
||||
"@hapi/joi": "^17.1.1",
|
||||
"@sentry/node": "^6.19.1",
|
||||
"aws-sdk": "^2.1135.0",
|
||||
"bluebird": "^3.7.2",
|
||||
|
@ -31,6 +30,7 @@
|
|||
"convict-format-with-moment": "^6.2.0",
|
||||
"convict-format-with-validator": "^6.2.0",
|
||||
"fxa-shared": "workspace:*",
|
||||
"joi": "17.4.0",
|
||||
"lodash": "^4.17.21",
|
||||
"mozlog": "^3.0.2",
|
||||
"mysql": "^2.18.1",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import joi from 'typesafe-joi';
|
||||
import joi from 'joi';
|
||||
|
||||
export interface CouponDetails {
|
||||
promotionCode: string;
|
||||
|
@ -20,4 +20,12 @@ export const couponDetailsSchema = joi.object({
|
|||
maximallyRedeemed: joi.boolean().required(),
|
||||
});
|
||||
|
||||
export type couponDetailsSchema = joi.Literal<typeof couponDetailsSchema>;
|
||||
export type couponDetailsSchema = {
|
||||
promotionCode: string;
|
||||
type: string;
|
||||
durationInMonths: number | null;
|
||||
valid: boolean;
|
||||
discountAmount?: number;
|
||||
expired: boolean;
|
||||
maximallyRedeemed: boolean;
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
import Stripe from 'stripe';
|
||||
import joi from 'typesafe-joi';
|
||||
import joi from 'joi';
|
||||
|
||||
import {
|
||||
MozillaSubscriptionTypes,
|
||||
|
@ -21,9 +21,11 @@ export const iapExtraStripeInfoSchema = joi.object({
|
|||
product_name: joi.string().required(),
|
||||
});
|
||||
|
||||
export type iapExtraStripeInfoSchema = joi.Literal<
|
||||
typeof iapExtraStripeInfoSchema
|
||||
>;
|
||||
export type iapExtraStripeInfoSchema = {
|
||||
price_id: string;
|
||||
product_id: string;
|
||||
product_name: string;
|
||||
};
|
||||
|
||||
export type PlayStoreSubscription = {
|
||||
auto_renewing: boolean;
|
||||
|
@ -45,10 +47,6 @@ export const playStoreSubscriptionSchema = joi
|
|||
})
|
||||
.concat(iapExtraStripeInfoSchema);
|
||||
|
||||
export type playStoreSubscriptionSchema = joi.Literal<
|
||||
typeof playStoreSubscriptionSchema
|
||||
>;
|
||||
|
||||
export type AppStoreSubscription = {
|
||||
app_store_product_id: string;
|
||||
auto_renewing: boolean;
|
||||
|
@ -68,7 +66,3 @@ export const appStoreSubscriptionSchema = joi
|
|||
_subscription_type: MozillaSubscriptionTypes.IAP_APPLE,
|
||||
})
|
||||
.concat(iapExtraStripeInfoSchema);
|
||||
|
||||
export type appStoreSubscriptionSchema = joi.Literal<
|
||||
typeof appStoreSubscriptionSchema
|
||||
>;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
import joi from 'typesafe-joi';
|
||||
import joi from 'joi';
|
||||
|
||||
export interface InvoiceLineItem {
|
||||
amount: number;
|
||||
|
@ -64,9 +64,29 @@ export const firstInvoicePreviewSchema = joi.object({
|
|||
}),
|
||||
});
|
||||
|
||||
export type firstInvoicePreviewSchema = joi.Literal<
|
||||
typeof firstInvoicePreviewSchema
|
||||
>;
|
||||
type line_item = {
|
||||
amount: number;
|
||||
currency: string;
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type firstInvoicePreviewSchema = {
|
||||
line_items: Array<line_item>;
|
||||
subtotal: number;
|
||||
total: number;
|
||||
tax?: {
|
||||
amount: number;
|
||||
inclusive: boolean;
|
||||
name: string;
|
||||
percentage: number;
|
||||
};
|
||||
discount?: {
|
||||
amount: number;
|
||||
amount_off: number | null;
|
||||
percent_off: number | null;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines an interface for the subsequent invoice preview response
|
||||
|
@ -86,6 +106,10 @@ export const subsequentInvoicePreviewsSchema = joi.array().items(
|
|||
})
|
||||
);
|
||||
|
||||
export type subsequentInvoicePreviewsSchema = joi.Literal<
|
||||
typeof subsequentInvoicePreviewsSchema
|
||||
>;
|
||||
type subsequentInvoicePreview = {
|
||||
subscriptionId: string;
|
||||
period_start: number;
|
||||
total: number;
|
||||
};
|
||||
|
||||
export type subsequentInvoicePreviewsSchema = Array<subsequentInvoicePreview>;
|
||||
|
|
|
@ -16,7 +16,7 @@ module.exports = (clientIpAddressDepth) => (request) => {
|
|||
.map((address) => address.trim());
|
||||
ipAddresses.push(request.ip || request.connection.remoteAddress);
|
||||
ipAddresses = ipAddresses.filter(
|
||||
(ipAddress) => !joi.validate(ipAddress, IP_ADDRESS).error
|
||||
(ipAddress) => !IP_ADDRESS.validate(ipAddress).error
|
||||
);
|
||||
|
||||
let clientAddressIndex = ipAddresses.length - clientIpAddressDepth;
|
||||
|
|
|
@ -6,7 +6,7 @@ module.exports = (app, logger) => {
|
|||
const cors = require('./cors');
|
||||
const {
|
||||
celebrate,
|
||||
isCelebrate: isValidationError,
|
||||
isCelebrateError: isValidationError,
|
||||
errors: validationErrorHandlerFactory,
|
||||
} = require('celebrate');
|
||||
const validationErrorHandler = validationErrorHandlerFactory();
|
||||
|
|
|
@ -47,7 +47,6 @@
|
|||
"@types/ioredis": "^4.26.4",
|
||||
"@types/ip": "^1",
|
||||
"@types/jest": "^26.0.23",
|
||||
"@types/joi": "^14.3.4",
|
||||
"@types/lodash.omitby": "^4",
|
||||
"@types/lodash.pick": "^4",
|
||||
"@types/mocha": "^8.2.2",
|
||||
|
@ -98,7 +97,7 @@
|
|||
"app-store-server-api": "^0.3.0",
|
||||
"aws-sdk": "^2.1135.0",
|
||||
"buf": "^0.1.1",
|
||||
"celebrate": "^10.0.1",
|
||||
"celebrate": "^15.0.1",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.13.2",
|
||||
"cldr-localenames-full": "41.0.0",
|
||||
|
@ -110,7 +109,7 @@
|
|||
"hot-shots": "^9.0.0",
|
||||
"ioredis": "^4.28.2",
|
||||
"ip": "^1.1.5",
|
||||
"joi": "^14.3.1",
|
||||
"joi": "17.4.0",
|
||||
"js-md5": "^0.7.3",
|
||||
"knex": "^2.0.0",
|
||||
"lodash.omitby": "^4.6.0",
|
||||
|
@ -124,7 +123,6 @@
|
|||
"rxjs": "^7.2.0",
|
||||
"stripe": "^8.218.0",
|
||||
"superagent": "^7.1.2",
|
||||
"typesafe-joi": "^2.1.0",
|
||||
"typesafe-node-firestore": "^1.4.0"
|
||||
},
|
||||
"jest": {
|
||||
|
|
|
@ -30,12 +30,8 @@ export const planConfigJoiKeys = {
|
|||
.optional(),
|
||||
};
|
||||
|
||||
export const planConfigSchema = baseConfigSchema
|
||||
.keys(planConfigJoiKeys)
|
||||
.requiredKeys('active');
|
||||
|
||||
const buildPlanConfigSchema = (baseSchema: joi.ObjectSchema) =>
|
||||
baseSchema.keys(planConfigJoiKeys).requiredKeys('active');
|
||||
const buildPlanConfigSchema = (schema: joi.ObjectSchema) =>
|
||||
schema.fork(['active'], (schema) => schema.required());
|
||||
|
||||
export class PlanConfig implements BaseConfig {
|
||||
// Firestore document id
|
||||
|
@ -70,14 +66,14 @@ export class PlanConfig implements BaseConfig {
|
|||
schemaValidation: ProductConfigSchemaValidation
|
||||
) {
|
||||
const extendedBaseSchema = extendBaseConfigSchema(
|
||||
baseConfigSchema,
|
||||
baseConfigSchema.keys(planConfigJoiKeys),
|
||||
schemaValidation.cdnUrlRegex
|
||||
);
|
||||
|
||||
const planConfigSchema = buildPlanConfigSchema(extendedBaseSchema);
|
||||
|
||||
try {
|
||||
const value = await joi.validate(planConfig, planConfigSchema, {
|
||||
const value = await planConfigSchema.validateAsync(planConfig, {
|
||||
abortEarly: false,
|
||||
});
|
||||
return { value };
|
||||
|
|
|
@ -22,9 +22,8 @@ export const productConfigJoiKeys = {
|
|||
};
|
||||
|
||||
const buildProductConfigSchema = (baseSchema: joi.ObjectSchema) =>
|
||||
baseSchema
|
||||
.keys(productConfigJoiKeys)
|
||||
.requiredKeys(
|
||||
baseSchema.fork(
|
||||
[
|
||||
'capabilities',
|
||||
'locales',
|
||||
'styles',
|
||||
|
@ -35,8 +34,10 @@ const buildProductConfigSchema = (baseSchema: joi.ObjectSchema) =>
|
|||
'urls.termsOfService',
|
||||
'urls.termsOfServiceDownload',
|
||||
'urls.webIcon',
|
||||
'urls'
|
||||
);
|
||||
'urls',
|
||||
],
|
||||
(schema) => schema.required()
|
||||
);
|
||||
|
||||
// This type defines the required fields of urls, set by function buildProductConfigSchema.
|
||||
// Any change to required fields in urls, should be updated here as well.
|
||||
|
@ -83,14 +84,14 @@ export class ProductConfig implements BaseConfig {
|
|||
schemaValidation: ProductConfigSchemaValidation
|
||||
) {
|
||||
const extendedBaseSchema = extendBaseConfigSchema(
|
||||
baseConfigSchema,
|
||||
baseConfigSchema.keys(productConfigJoiKeys),
|
||||
schemaValidation.cdnUrlRegex
|
||||
);
|
||||
|
||||
const productConfigSchema = buildProductConfigSchema(extendedBaseSchema);
|
||||
|
||||
try {
|
||||
const value = await joi.validate(productConfig, productConfigSchema, {
|
||||
const value = await productConfigSchema.validateAsync(productConfig, {
|
||||
abortEarly: false,
|
||||
});
|
||||
return { value };
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import Joi from '@hapi/joi';
|
||||
import Joi from 'joi';
|
||||
|
||||
export const capabilitiesClientIdPattern = /^capabilities/;
|
||||
|
||||
|
|
|
@ -14,19 +14,19 @@ describe('express/routing:', () => {
|
|||
let celebrateMock;
|
||||
let corsHandler;
|
||||
let errorHandler;
|
||||
let isCelebrate;
|
||||
let isCelebrateError;
|
||||
let loggerMock;
|
||||
let routing;
|
||||
let routingFactory;
|
||||
|
||||
beforeEach(() => {
|
||||
celebrateHandler = () => {};
|
||||
isCelebrate = false;
|
||||
isCelebrateError = false;
|
||||
errorHandler = sinon.spy();
|
||||
|
||||
celebrateMock = {
|
||||
celebrate: () => celebrateHandler,
|
||||
isCelebrate: () => isCelebrate,
|
||||
isCelebrateError: () => isCelebrateError,
|
||||
errors: () => errorHandler,
|
||||
};
|
||||
|
||||
|
@ -202,7 +202,7 @@ describe('express/routing:', () => {
|
|||
it('logs and delegates validation errors to celebrate', () => {
|
||||
const error = new Error('uh oh');
|
||||
const next = sinon.spy();
|
||||
isCelebrate = true;
|
||||
isCelebrateError = true;
|
||||
|
||||
routing.validationErrorHandler(error, {}, {}, next);
|
||||
|
||||
|
@ -213,7 +213,7 @@ describe('express/routing:', () => {
|
|||
it('passes on other errors to the next error handler', () => {
|
||||
const error = new Error('uh oh');
|
||||
const next = sinon.spy();
|
||||
isCelebrate = false;
|
||||
isCelebrateError = false;
|
||||
|
||||
routing.validationErrorHandler(error, {}, {}, next);
|
||||
|
||||
|
|
|
@ -56,8 +56,7 @@
|
|||
"rxjs": "^7.2.0",
|
||||
"semver": "^7.3.5",
|
||||
"superagent": "^7.1.2",
|
||||
"tslib": "^2.3.1",
|
||||
"typesafe-joi": "^2.1.0"
|
||||
"tslib": "^2.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^8.2.5",
|
||||
|
|
320
yarn.lock
320
yarn.lock
|
@ -3841,15 +3841,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@hapi/address@npm:^4.0.1":
|
||||
version: 4.0.1
|
||||
resolution: "@hapi/address@npm:4.0.1"
|
||||
dependencies:
|
||||
"@hapi/hoek": ^9.0.0
|
||||
checksum: 365547dbed1dcc8404a2cf5212fbe4c279f069e2ed817cb2a1b53bcc85a5e8ea7ce0e5377dc111cfff9d31e46275dbf83b316b0e4aba5abd05416c2a38914ea5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@hapi/ammo@npm:5.x.x, @hapi/ammo@npm:^5.0.1":
|
||||
version: 5.0.1
|
||||
resolution: "@hapi/ammo@npm:5.0.1"
|
||||
|
@ -3868,7 +3859,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@hapi/boom@npm:9.x.x, @hapi/boom@npm:^9.0.0, @hapi/boom@npm:^9.1.0":
|
||||
"@hapi/boom@npm:9.x.x, @hapi/boom@npm:^9.0.0, @hapi/boom@npm:^9.1.0, @hapi/boom@npm:^9.1.4":
|
||||
version: 9.1.4
|
||||
resolution: "@hapi/boom@npm:9.1.4"
|
||||
dependencies:
|
||||
|
@ -3877,15 +3868,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@hapi/boom@npm:^7.1.1":
|
||||
version: 7.4.11
|
||||
resolution: "@hapi/boom@npm:7.4.11"
|
||||
dependencies:
|
||||
"@hapi/hoek": 8.x.x
|
||||
checksum: ef166e1a764a9574b084d258a315d83fd61070b3ba0f5c16a2a5d716fe3f3fc068ce3c600bcb8bf62c0bad0ab01ba505b81ff93e806091d17e17571bcb76c6d3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@hapi/boom@npm:~10.0.0":
|
||||
version: 10.0.0
|
||||
resolution: "@hapi/boom@npm:10.0.0"
|
||||
|
@ -3987,13 +3969,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@hapi/formula@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "@hapi/formula@npm:2.0.0"
|
||||
checksum: 902da057c53027d9356de15e53a8af9ea4795d3fb78c066668b36cfca54abd2d707860469618448e7de02eb8364ead86e53b839dbd363e1aeb65ee9a195e5fad
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@hapi/hapi@npm:^20.2.1":
|
||||
version: 20.2.1
|
||||
resolution: "@hapi/hapi@npm:20.2.1"
|
||||
|
@ -4065,14 +4040,14 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@hapi/hoek@npm:^6.1.2":
|
||||
version: 6.2.4
|
||||
resolution: "@hapi/hoek@npm:6.2.4"
|
||||
checksum: 17e6e687509c20d3730dfb14b05536024d50253e96fcd0c7a4df8247e3a30568a8028b59208173f3e63dbab3eb6b71f07eff06b234dd5c2bd773d254af769e37
|
||||
"@hapi/hoek@npm:^9.3.0":
|
||||
version: 9.3.0
|
||||
resolution: "@hapi/hoek@npm:9.3.0"
|
||||
checksum: 4771c7a776242c3c022b168046af4e324d116a9d2e1d60631ee64f474c6e38d1bb07092d898bf95c7bc5d334c5582798a1456321b2e53ca817d4e7c88bc25b43
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@hapi/inert@npm:6.0.5":
|
||||
"@hapi/inert@npm:6.0.5, @hapi/inert@npm:^6.0.5":
|
||||
version: 6.0.5
|
||||
resolution: "@hapi/inert@npm:6.0.5"
|
||||
dependencies:
|
||||
|
@ -4099,7 +4074,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@hapi/joi@npm:15.x.x, @hapi/joi@npm:^15.0.1, @hapi/joi@npm:^15.1.0, @hapi/joi@npm:^15.1.1":
|
||||
"@hapi/joi@npm:^15.1.0, @hapi/joi@npm:^15.1.1":
|
||||
version: 15.1.1
|
||||
resolution: "@hapi/joi@npm:15.1.1"
|
||||
dependencies:
|
||||
|
@ -4111,19 +4086,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@hapi/joi@npm:^17.1.1":
|
||||
version: 17.1.1
|
||||
resolution: "@hapi/joi@npm:17.1.1"
|
||||
dependencies:
|
||||
"@hapi/address": ^4.0.1
|
||||
"@hapi/formula": ^2.0.0
|
||||
"@hapi/hoek": ^9.0.0
|
||||
"@hapi/pinpoint": ^2.0.0
|
||||
"@hapi/topo": ^5.0.0
|
||||
checksum: 803d77e19e26802860de22b8d1ae3435679844202703d20bfd6246a8c6aa00143ce48d0e8beecf55812334e8e89ac04b79301264d4a3f87c980eb0c0646b33ab
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@hapi/mimos@npm:^6.0.0":
|
||||
version: 6.0.0
|
||||
resolution: "@hapi/mimos@npm:6.0.0"
|
||||
|
@ -4157,13 +4119,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@hapi/pinpoint@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "@hapi/pinpoint@npm:2.0.0"
|
||||
checksum: ed011c0af4eeab75f7ea2b4657f16a93e31d449a43b40a4f398e2f1a0752357af2badd7b3f3e2da9554cb9d13adb52bde123b250acef1709260954d0301eed16
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@hapi/podium@npm:4.x.x, @hapi/podium@npm:^4.1.1":
|
||||
version: 4.1.1
|
||||
resolution: "@hapi/podium@npm:4.1.1"
|
||||
|
@ -4299,6 +4254,18 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@hapi/vision@npm:^6.1.0":
|
||||
version: 6.1.0
|
||||
resolution: "@hapi/vision@npm:6.1.0"
|
||||
dependencies:
|
||||
"@hapi/boom": 9.x.x
|
||||
"@hapi/bounce": 2.x.x
|
||||
"@hapi/hoek": 9.x.x
|
||||
"@hapi/validate": 1.x.x
|
||||
checksum: 3e38ea9a0b29849c98ce9ba92b200c6912f60d47768140cb371f05593b4bf34ad4544d3e48e38b57831b22e47f8778cf5877f8a5e7d895566b1d2b8cc82a1535
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@hapi/wreck@npm:17.x.x":
|
||||
version: 17.0.0
|
||||
resolution: "@hapi/wreck@npm:17.0.0"
|
||||
|
@ -9254,13 +9221,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/boom@npm:*":
|
||||
version: 7.3.1
|
||||
resolution: "@types/boom@npm:7.3.1"
|
||||
checksum: fc483602fb667d8858cef0a7685b9f25ce805d2e15ba033abf6e33da4ca1f103d69ec77e2d28d116d88a17a18f57c909358d99f9fbe3a9dd00c781190e877b7f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/braces@npm:*":
|
||||
version: 3.0.0
|
||||
resolution: "@types/braces@npm:3.0.0"
|
||||
|
@ -9296,13 +9256,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/catbox@npm:*":
|
||||
version: 10.0.7
|
||||
resolution: "@types/catbox@npm:10.0.7"
|
||||
checksum: d44d7e529757d46be3a3c755659b1ab68104749ada8537ce72959588560ae87db8d9c546bd2b179f91e5d5edae569dc6b58ea91f7edcffb6130509f98059b613
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/chai-as-promised@npm:^7":
|
||||
version: 7.1.4
|
||||
resolution: "@types/chai-as-promised@npm:7.1.4"
|
||||
|
@ -9613,22 +9566,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/hapi@npm:*":
|
||||
version: 18.0.7
|
||||
resolution: "@types/hapi@npm:18.0.7"
|
||||
dependencies:
|
||||
"@types/boom": "*"
|
||||
"@types/catbox": "*"
|
||||
"@types/iron": "*"
|
||||
"@types/mimos": "*"
|
||||
"@types/node": "*"
|
||||
"@types/podium": "*"
|
||||
"@types/shot": "*"
|
||||
joi: ^17.3.0
|
||||
checksum: a10ed6407ad54382bd9e05a67ce5eff2ec70f74e1f40f0a13bf5687c4458245e96d93b1704d51e870ca2d12f6e4f957281d711c80884f69e8702ff3c08533720
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/hapi__catbox@npm:*":
|
||||
version: 10.2.2
|
||||
resolution: "@types/hapi__catbox@npm:10.2.2"
|
||||
|
@ -9659,7 +9596,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/hapi__joi@npm:^15.0.1, @types/hapi__joi@npm:^15.0.4":
|
||||
"@types/hapi__joi@npm:^15.0.1":
|
||||
version: 15.0.4
|
||||
resolution: "@types/hapi__joi@npm:15.0.4"
|
||||
dependencies:
|
||||
|
@ -9762,15 +9699,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/inert@npm:^5":
|
||||
version: 5.1.3
|
||||
resolution: "@types/inert@npm:5.1.3"
|
||||
dependencies:
|
||||
"@types/hapi": "*"
|
||||
checksum: 223811d00ba88f043e0ec9ff7e3e2bdbc9c61552bcfadd65bbe5ddaa36da9f501bf342a5c74d981a8c2fc748bce8ea41f017ba86853c1f27275908ef21a7f84d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/ioredis@npm:^4.26.4":
|
||||
version: 4.26.4
|
||||
resolution: "@types/ioredis@npm:4.26.4"
|
||||
|
@ -9789,15 +9717,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/iron@npm:*":
|
||||
version: 5.0.2
|
||||
resolution: "@types/iron@npm:5.0.2"
|
||||
dependencies:
|
||||
"@types/node": "*"
|
||||
checksum: a3e9f914b3e880632ca3f423c08249c66a98277b1a4cb16bca42e910c216eaf8bd333d24b550b9deb685b6851f36784a25066806078ccf4bdcacc5997eb44e10
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/is-function@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "@types/is-function@npm:1.0.0"
|
||||
|
@ -9861,13 +9780,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/joi@npm:^14.3.4":
|
||||
version: 14.3.4
|
||||
resolution: "@types/joi@npm:14.3.4"
|
||||
checksum: 084c0fe211b43a84d25317d772bb8f6426382f1975cb4065fbfa1da7a64c600e9068a9b13ec2a32ddbe71d14f25c90da14a196651acb27e35d02856dbf6d4798
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/jquery@npm:*":
|
||||
version: 3.3.38
|
||||
resolution: "@types/jquery@npm:3.3.38"
|
||||
|
@ -10082,15 +9994,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/mimos@npm:*":
|
||||
version: 3.0.3
|
||||
resolution: "@types/mimos@npm:3.0.3"
|
||||
dependencies:
|
||||
"@types/mime-db": "*"
|
||||
checksum: 84d9ff1fdb9e67366aade363e898ad00a843c10dfab9790865268848a2665aa2e87cd2a9374a1e3def4cd7d7ea0c2441885e67df9c8b8b2b5a567f7c2618def4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/minimatch@npm:*, @types/minimatch@npm:^3.0.3":
|
||||
version: 3.0.3
|
||||
resolution: "@types/minimatch@npm:3.0.3"
|
||||
|
@ -10280,13 +10183,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/podium@npm:*":
|
||||
version: 1.0.1
|
||||
resolution: "@types/podium@npm:1.0.1"
|
||||
checksum: 7e9a54922f0c534fbc48db886a6a8c1fc65fa9361139a60e86d4d18600f0b9d60908bfdcee084492c5a19d7d2dfd012203a796622c2b52e1e37fc778d8701c35
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/postcss-import@npm:^12":
|
||||
version: 12.0.1
|
||||
resolution: "@types/postcss-import@npm:12.0.1"
|
||||
|
@ -10598,15 +10494,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/shot@npm:*":
|
||||
version: 4.0.1
|
||||
resolution: "@types/shot@npm:4.0.1"
|
||||
dependencies:
|
||||
"@types/node": "*"
|
||||
checksum: 31eae160a82a1bfdf53f10f8cd18bc7add5a8a860d9da1b183fa4e681eb12318d8ee4099d1f3d29623684291caed59d235a14eec089114d81738e138b245d658
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/sinon-chai@npm:3.2.5":
|
||||
version: 3.2.5
|
||||
resolution: "@types/sinon-chai@npm:3.2.5"
|
||||
|
@ -10800,16 +10687,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/vision@npm:^5":
|
||||
version: 5.3.8
|
||||
resolution: "@types/vision@npm:5.3.8"
|
||||
dependencies:
|
||||
"@types/hapi": "*"
|
||||
handlebars: ^4.1.0
|
||||
checksum: bafe17c0e881304a1e0dc5d4ce819938bd53968609c3ec6fecf9fa64eff0ce1575e905bca87511ffc78448b3a907444cdab9883e88148cedf1599330a3be6ee9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/webpack-env@npm:^1.16.0":
|
||||
version: 1.16.0
|
||||
resolution: "@types/webpack-env@npm:1.16.0"
|
||||
|
@ -12007,15 +11884,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ammo@npm:3.x.x":
|
||||
version: 3.0.3
|
||||
resolution: "ammo@npm:3.0.3"
|
||||
dependencies:
|
||||
hoek: 6.x.x
|
||||
checksum: 6f8c7c5864317645f7397607edbd09213cc54a7868b36523842ef23b72e1e0e0f93946795725a353a4c67ad3a0a71bfbd0f23ddb0434ac119008a0234c4a4401
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"amp-message@npm:~0.1.1":
|
||||
version: 0.1.2
|
||||
resolution: "amp-message@npm:0.1.2"
|
||||
|
@ -14747,25 +14615,6 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"boom@npm:7.x.x":
|
||||
version: 7.3.0
|
||||
resolution: "boom@npm:7.3.0"
|
||||
dependencies:
|
||||
hoek: 6.x.x
|
||||
checksum: 86d22bef321d3907a5a8f9188e3004efd1bf9e4aff21e358e3a2a8061a8ff5e442843cf5c43041ae5324496fc881562070c65040a59b7265ee84130fb1a8eae3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bounce@npm:1.x.x":
|
||||
version: 1.2.3
|
||||
resolution: "bounce@npm:1.2.3"
|
||||
dependencies:
|
||||
boom: 7.x.x
|
||||
hoek: 6.x.x
|
||||
checksum: 4f6c177433e4e2f58f357589a18492f81db1d3085835d36b233db50e978d493d4e1b9b3e26a89f03ac6e0e019f688f30e860f4aa11e7a486858d7ba249ba0fe5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"boxen@npm:^4.2.0":
|
||||
version: 4.2.0
|
||||
resolution: "boxen@npm:4.2.0"
|
||||
|
@ -15753,14 +15602,14 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"celebrate@npm:^10.0.1, celebrate@npm:^10.1.0":
|
||||
version: 10.1.0
|
||||
resolution: "celebrate@npm:10.1.0"
|
||||
"celebrate@npm:^15.0.1":
|
||||
version: 15.0.1
|
||||
resolution: "celebrate@npm:15.0.1"
|
||||
dependencies:
|
||||
"@hapi/joi": 15.x.x
|
||||
escape-html: 1.0.3
|
||||
lodash.get: 4.4.x
|
||||
checksum: b229c9fccca47152bf9ea70f98dfe7a3679d6cc565d2ff5d2d7c808f37ef3889898c85ea04714714cc06158cea16a38c945acba7073d1ba749f26b6e6a75ddf2
|
||||
joi: 17.x.x
|
||||
lodash: 4.17.x
|
||||
checksum: 8f20e47f1285fbb914f80f1f6ebb60eb224805f737a7652f1423cbcd2307f5b6e4e9681859a84e59154f7625214abe086c24a516c5483fe798db54d3066c790a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -22370,7 +22219,8 @@ fsevents@~2.1.1:
|
|||
"@hapi/hapi": ^20.2.1
|
||||
"@hapi/hawk": ^8.0.0
|
||||
"@hapi/hoek": ^10.0.0
|
||||
"@hapi/joi": ^15.1.1
|
||||
"@hapi/inert": ^6.0.5
|
||||
"@hapi/vision": ^6.1.0
|
||||
"@sentry/integrations": ^6.19.1
|
||||
"@sentry/node": ^6.19.1
|
||||
"@storybook/addon-controls": ^6.4.19
|
||||
|
@ -22386,8 +22236,6 @@ fsevents@~2.1.1:
|
|||
"@types/dedent": ^0
|
||||
"@types/ejs": ^3.0.6
|
||||
"@types/hapi__hapi": ^20.0.10
|
||||
"@types/hapi__joi": ^15.0.4
|
||||
"@types/inert": ^5
|
||||
"@types/ioredis": ^4.26.4
|
||||
"@types/jsdom": ^16
|
||||
"@types/jsonwebtoken": ^8.5.1
|
||||
|
@ -22406,7 +22254,6 @@ fsevents@~2.1.1:
|
|||
"@types/sass": ^1
|
||||
"@types/uuid": ^8.3.0
|
||||
"@types/verror": ^1.10.4
|
||||
"@types/vision": ^5
|
||||
"@types/webpack": 5.28.0
|
||||
acorn: ^8.0.1
|
||||
ajv: ^6.12.2
|
||||
|
@ -22450,12 +22297,12 @@ fsevents@~2.1.1:
|
|||
grunt-newer: 1.3.0
|
||||
hapi-auth-jwt2: ^10.2.0
|
||||
hapi-error: ^2.3.0
|
||||
hapi-swagger: 10.3.0
|
||||
hapi-swagger: ^14.4.0
|
||||
hkdf: 0.0.2
|
||||
hot-shots: ^9.0.0
|
||||
i18n-iso-countries: ^7.4.0
|
||||
inert: ^5.1.3
|
||||
ioredis: ^4.28.2
|
||||
joi: 17.4.0
|
||||
jose: ^4.8.1
|
||||
jsdom: ^19.0.0
|
||||
jsonwebtoken: ^8.5.1
|
||||
|
@ -22517,7 +22364,6 @@ fsevents@~2.1.1:
|
|||
typescript: ^4.5.2
|
||||
uuid: ^8.3.2
|
||||
verror: ^1.10.1
|
||||
vision: ^5.4.4
|
||||
web-push: 3.4.4
|
||||
webpack: ^4.43.0
|
||||
webpack-watch-files-plugin: ^1.2.1
|
||||
|
@ -22632,7 +22478,7 @@ fsevents@~2.1.1:
|
|||
i18n-abide: 0.0.26
|
||||
install: 0.13.0
|
||||
intern: ^4.10.1
|
||||
joi: ^14.3.1
|
||||
joi: 17.4.0
|
||||
jquery: 3.6.0
|
||||
jquery-modal: "https://github.com/mozilla-fxa/jquery-modal.git#0576775d1b4590314b114386019f4c7421c77503"
|
||||
jquery-simulate: 1.0.2
|
||||
|
@ -22811,6 +22657,7 @@ fsevents@~2.1.1:
|
|||
graphql: ^15.6.1
|
||||
hot-shots: ^9.0.0
|
||||
jest: 27.5.1
|
||||
joi: 17.4.0
|
||||
jwks-rsa: ^2.1.1
|
||||
mozlog: ^3.0.2
|
||||
nock: ^13.2.2
|
||||
|
@ -23017,7 +22864,7 @@ fsevents@~2.1.1:
|
|||
babel-loader: ^8.2.2
|
||||
browserslist: ^4.7.2
|
||||
caniuse-lite: ^1.0.30001294
|
||||
celebrate: ^10.1.0
|
||||
celebrate: ^15.0.1
|
||||
classnames: ^2.3.1
|
||||
convict: ^6.2.2
|
||||
convict-format-with-moment: ^6.2.0
|
||||
|
@ -23044,7 +22891,7 @@ fsevents@~2.1.1:
|
|||
intl: 1.2.5
|
||||
intl-pluralrules: ^1.3.1
|
||||
jest: 27.5.1
|
||||
joi: ^14.3.1
|
||||
joi: 17.4.0
|
||||
jquery-modal: "https://github.com/mozilla-fxa/jquery-modal.git#0576775d1b4590314b114386019f4c7421c77503"
|
||||
morgan: ^1.10.0
|
||||
mozlog: ^3.0.2
|
||||
|
@ -23099,7 +22946,6 @@ fsevents@~2.1.1:
|
|||
"@hapi/catbox-redis": ~6.0.2
|
||||
"@hapi/hapi": ^20.2.1
|
||||
"@hapi/inert": 6.0.5
|
||||
"@hapi/joi": ^17.1.1
|
||||
"@sentry/node": ^6.19.1
|
||||
"@types/sharp": ^0
|
||||
audit-filter: ^0.5.0
|
||||
|
@ -23116,6 +22962,7 @@ fsevents@~2.1.1:
|
|||
eslint-plugin-fxa: ^2.0.2
|
||||
fxa-shared: "workspace:*"
|
||||
insist: 1.0.1
|
||||
joi: 17.4.0
|
||||
lodash: ^4.17.21
|
||||
mkdirp: 0.5.1
|
||||
mocha: ^9.1.2
|
||||
|
@ -23305,7 +23152,6 @@ fsevents@~2.1.1:
|
|||
"@types/ioredis": ^4.26.4
|
||||
"@types/ip": ^1
|
||||
"@types/jest": ^26.0.23
|
||||
"@types/joi": ^14.3.4
|
||||
"@types/js-md5": ^0.4.2
|
||||
"@types/lodash.omitby": ^4
|
||||
"@types/lodash.pick": ^4
|
||||
|
@ -23323,7 +23169,7 @@ fsevents@~2.1.1:
|
|||
audit-filter: ^0.5.0
|
||||
aws-sdk: ^2.1135.0
|
||||
buf: ^0.1.1
|
||||
celebrate: ^10.0.1
|
||||
celebrate: ^15.0.1
|
||||
chai: ^4.3.6
|
||||
chance: ^1.1.8
|
||||
class-transformer: ^0.5.1
|
||||
|
@ -23342,7 +23188,7 @@ fsevents@~2.1.1:
|
|||
ioredis: ^4.28.2
|
||||
ip: ^1.1.5
|
||||
jest: 27.5.1
|
||||
joi: ^14.3.1
|
||||
joi: 17.4.0
|
||||
js-md5: ^0.7.3
|
||||
jsdom: 19.0.0
|
||||
jsdom-global: 3.0.2
|
||||
|
@ -23367,7 +23213,6 @@ fsevents@~2.1.1:
|
|||
ts-jest: ^27.1.4
|
||||
ts-loader: ^8.3.0
|
||||
tsconfig-paths: ^4.0.0
|
||||
typesafe-joi: ^2.1.0
|
||||
typesafe-node-firestore: ^1.4.0
|
||||
typescript: ^4.5.2
|
||||
underscore: ^1.13.1
|
||||
|
@ -23420,7 +23265,6 @@ fsevents@~2.1.1:
|
|||
supertest: ^6.2.3
|
||||
ts-jest: ^27.1.4
|
||||
tslib: ^2.3.1
|
||||
typesafe-joi: ^2.1.0
|
||||
typescript: ^4.5.2
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
@ -24857,7 +24701,7 @@ fsevents@~2.1.1:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"handlebars@npm:4.7.7, handlebars@npm:^4.1.0, handlebars@npm:^4.3.3, handlebars@npm:^4.7.7":
|
||||
"handlebars@npm:4.7.7, handlebars@npm:^4.1.0, handlebars@npm:^4.7.7":
|
||||
version: 4.7.7
|
||||
resolution: "handlebars@npm:4.7.7"
|
||||
dependencies:
|
||||
|
@ -24895,21 +24739,21 @@ fsevents@~2.1.1:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"hapi-swagger@npm:10.3.0":
|
||||
version: 10.3.0
|
||||
resolution: "hapi-swagger@npm:10.3.0"
|
||||
"hapi-swagger@npm:^14.4.0":
|
||||
version: 14.5.1
|
||||
resolution: "hapi-swagger@npm:14.5.1"
|
||||
dependencies:
|
||||
"@hapi/boom": ^7.1.1
|
||||
"@hapi/hoek": ^6.1.2
|
||||
"@hapi/joi": ^15.0.1
|
||||
handlebars: ^4.3.3
|
||||
http-status: ^1.0.1
|
||||
"@hapi/boom": ^9.1.4
|
||||
"@hapi/hoek": ^9.3.0
|
||||
handlebars: ^4.7.7
|
||||
http-status: ^1.5.2
|
||||
json-schema-ref-parser: ^6.1.0
|
||||
swagger-parser: 4.0.2
|
||||
swagger-ui-dist: ^3.22.1
|
||||
swagger-ui-dist: ^4.11.1
|
||||
peerDependencies:
|
||||
"@hapi/hapi": ^18.0.0
|
||||
checksum: b49d22c3640ccb4e86d2397d889212776a68cf4dc6e21c5eefee09e87fd50d47cd31827ba6e7adfe793f59546b97f6dce78918261f764d1e0aafec646e8c0216
|
||||
"@hapi/hapi": ^20.2.2
|
||||
joi: 17.x
|
||||
checksum: 5a9968885d64f50cff138c5fdb718680a330f730e397a9919a01f489d36c86ed443bffb0d025bafd3b6a8af262dd094ad57384e07b3e134e633ea041a0b56f0e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -25785,10 +25629,10 @@ fsevents@~2.1.1:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"http-status@npm:^1.0.1":
|
||||
version: 1.5.0
|
||||
resolution: "http-status@npm:1.5.0"
|
||||
checksum: d311aacdfb5156fb2c073a00e8e69d5b7adf9995e5f420bc2fb2b57639ea1796343956584607718ba6b1975d38cb6aee829f32d12e26980c36dd7da6046c1aa8
|
||||
"http-status@npm:^1.5.2":
|
||||
version: 1.5.2
|
||||
resolution: "http-status@npm:1.5.2"
|
||||
checksum: f975c98184d232afb438af86c77e4011a5d161fd490f048992ad19dfa0133ddce169f54d18e226d55b80f39def7008822d993cfbb7f318bab5a49836ec5097e1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -26184,20 +26028,6 @@ fsevents@~2.1.1:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"inert@npm:^5.1.3":
|
||||
version: 5.1.3
|
||||
resolution: "inert@npm:5.1.3"
|
||||
dependencies:
|
||||
ammo: 3.x.x
|
||||
boom: 7.x.x
|
||||
bounce: 1.x.x
|
||||
hoek: 6.x.x
|
||||
joi: 14.x.x
|
||||
lru-cache: 4.1.x
|
||||
checksum: 8859c41b3f30b1cc17ab3215810819910dac405508e6202c6d9c436e6c14d3fb4aa1c0b1c0309494b172ba6b9c10140910dbeef28ba1ba09dbbecead9e1a3fc4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"infer-owner@npm:^1.0.3, infer-owner@npm:^1.0.4":
|
||||
version: 1.0.4
|
||||
resolution: "infer-owner@npm:1.0.4"
|
||||
|
@ -27576,13 +27406,6 @@ fsevents@~2.1.1:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"items@npm:2.x.x":
|
||||
version: 2.1.2
|
||||
resolution: "items@npm:2.1.2"
|
||||
checksum: 29730f0085ec4585a51f25655ce9ecff0d444d3fe75bb7963fc6cbe741176d47cb6a4eb18d67019030e67ac8c9b0267579e8cebd5ed4ba50cf5d95b01650d7a0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"iterall@npm:1.3.0, iterall@npm:^1.1.3, iterall@npm:^1.2.1":
|
||||
version: 1.3.0
|
||||
resolution: "iterall@npm:1.3.0"
|
||||
|
@ -28809,18 +28632,7 @@ fsevents@~2.1.1:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"joi@npm:14.x.x, joi@npm:^14.3.1":
|
||||
version: 14.3.1
|
||||
resolution: "joi@npm:14.3.1"
|
||||
dependencies:
|
||||
hoek: 6.x.x
|
||||
isemail: 3.x.x
|
||||
topo: 3.x.x
|
||||
checksum: 2ae5b54c4f425ed26e1ac937940fb21e8d71a4c3cbaf99d895319d3f99b119fddabc663a4b32926c3911a5c017c1dc5953adc7b43db4d0410d522d717bd80c8d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"joi@npm:17.x.x":
|
||||
"joi@npm:17.4.0, joi@npm:17.x.x":
|
||||
version: 17.4.0
|
||||
resolution: "joi@npm:17.4.0"
|
||||
dependencies:
|
||||
|
@ -30252,7 +30064,7 @@ fsevents@~2.1.1:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash.get@npm:4.4.x, lodash.get@npm:^4.0.0, lodash.get@npm:^4.4.2":
|
||||
"lodash.get@npm:^4.0.0, lodash.get@npm:^4.4.2":
|
||||
version: 4.4.2
|
||||
resolution: "lodash.get@npm:4.4.2"
|
||||
checksum: e403047ddb03181c9d0e92df9556570e2b67e0f0a930fcbbbd779370972368f5568e914f913e93f3b08f6d492abc71e14d4e9b7a18916c31fa04bd2306efe545
|
||||
|
@ -30604,7 +30416,7 @@ fsevents@~2.1.1:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lru-cache@npm:4.1.x, lru-cache@npm:^4.0.0, lru-cache@npm:^4.1.5":
|
||||
"lru-cache@npm:^4.0.0, lru-cache@npm:^4.1.5":
|
||||
version: 4.1.5
|
||||
resolution: "lru-cache@npm:4.1.5"
|
||||
dependencies:
|
||||
|
@ -41284,10 +41096,10 @@ resolve@^2.0.0-next.3:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"swagger-ui-dist@npm:^3.22.1":
|
||||
version: 3.52.5
|
||||
resolution: "swagger-ui-dist@npm:3.52.5"
|
||||
checksum: a8bd28bf475c67ad8445a2bb864c4130415d001caeb5db54f0a561785ac9f7df77230a7634520e9c10f25b568035a8f924ecc059945d4caed00a3f2a39ca243c
|
||||
"swagger-ui-dist@npm:^4.11.1":
|
||||
version: 4.11.1
|
||||
resolution: "swagger-ui-dist@npm:4.11.1"
|
||||
checksum: 678f677f2620f58d868d3f97832d9a9a9402e88f3d513da5749273d78c527cd8825db5d275f6046badf981ddb977b6c8737a384c575bdc8de2a67605ff11822b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -43732,18 +43544,6 @@ resolve@^2.0.0-next.3:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"vision@npm:^5.4.4":
|
||||
version: 5.4.4
|
||||
resolution: "vision@npm:5.4.4"
|
||||
dependencies:
|
||||
boom: 7.x.x
|
||||
hoek: 6.x.x
|
||||
items: 2.x.x
|
||||
joi: 14.x.x
|
||||
checksum: a48c6a75b2426d66e3f3b68d4586a9291fb24f28640033209780a4929144eee4abfb3e40acaaa3cc35760337bca22896d640cda699ceda5d31fc8f7fe30915e0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"vizion@npm:~2.2.1":
|
||||
version: 2.2.1
|
||||
resolution: "vizion@npm:2.2.1"
|
||||
|
|
Загрузка…
Ссылка в новой задаче