0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-03-11 02:12:21 -05:00

Switch to @trghost/validator, remove validator

- Part of the effort to split Ghost down into smaller, decoupled pieces
- Moved out our internal validator tooling to a separate library
- Replaced all usage of our own tooling and validatorjs directly with @tryghost/validator
- Removed the validatorjs dependency and removed the renovate pin
- This gives us a consistant, smaller, clearer public API for validations
- It will eventually be used on Ghost Admin too
- This way we can start getting up to date with validator whilst not increasing build size
This commit is contained in:
Hannah Wolfe 2021-06-15 19:46:27 +01:00
parent 97c0c93959
commit 526993965a
No known key found for this signature in database
GPG key ID: 9F8C7532D0A6BA55
29 changed files with 40 additions and 211 deletions

View file

@ -1,5 +1,5 @@
const _ = require('lodash');
const validator = require('validator');
const validator = require('@tryghost/validator');
const BaseMapGenerator = require('./base-generator');
class UserMapGenerator extends BaseMapGenerator {

View file

@ -1,6 +1,6 @@
const Promise = require('bluebird');
const _ = require('lodash');
const validator = require('validator');
const validator = require('@tryghost/validator');
const models = require('../../models');
const frontendRouting = require('../../../frontend/services/routing');
const frontendSettings = require('../../../frontend/services/settings');

View file

@ -1,5 +1,5 @@
const Promise = require('bluebird');
const validator = require('validator');
const validator = require('@tryghost/validator');
const debug = require('@tryghost/debug')('api:canary:utils:validators:input:invitation');
const i18n = require('../../../../../../shared/i18n');
const errors = require('@tryghost/errors');

View file

@ -1,5 +1,5 @@
const Promise = require('bluebird');
const validator = require('validator');
const validator = require('@tryghost/validator');
const debug = require('@tryghost/debug')('api:canary:utils:validators:input:passwordreset');
const i18n = require('../../../../../../shared/i18n');
const errors = require('@tryghost/errors');

View file

@ -3,7 +3,7 @@ const _ = require('lodash');
const Promise = require('bluebird');
const i18n = require('../../../../../shared/i18n');
const {BadRequestError, ValidationError} = require('@tryghost/errors');
const validator = require('../../../../data/validator');
const validator = require('@tryghost/validator');
const GLOBAL_VALIDATORS = {
id: {matches: /^[a-f\d]{24}$|^1$|me/i},

View file

@ -1,5 +1,5 @@
const Promise = require('bluebird');
const validator = require('validator');
const validator = require('@tryghost/validator');
const debug = require('@tryghost/debug')('api:v2:utils:validators:input:invitation');
const i18n = require('../../../../../../shared/i18n');
const errors = require('@tryghost/errors');

View file

@ -1,5 +1,5 @@
const Promise = require('bluebird');
const validator = require('validator');
const validator = require('@tryghost/validator');
const debug = require('@tryghost/debug')('api:v2:utils:validators:input:passwordreset');
const i18n = require('../../../../../../shared/i18n');
const errors = require('@tryghost/errors');

View file

@ -1,6 +1,6 @@
const Promise = require('bluebird');
const _ = require('lodash');
const validator = require('validator');
const validator = require('@tryghost/validator');
const models = require('../../models');
const frontendRouting = require('../../../frontend/services/routing');
const frontendSettings = require('../../../frontend/services/settings');

View file

@ -1,5 +1,5 @@
const Promise = require('bluebird');
const validator = require('validator');
const validator = require('@tryghost/validator');
const debug = require('@tryghost/debug')('api:v3:utils:validators:input:invitation');
const i18n = require('../../../../../../shared/i18n');
const errors = require('@tryghost/errors');

View file

@ -1,5 +1,5 @@
const Promise = require('bluebird');
const validator = require('validator');
const validator = require('@tryghost/validator');
const debug = require('@tryghost/debug')('api:v3:utils:validators:input:passwordreset');
const i18n = require('../../../../../../shared/i18n');
const errors = require('@tryghost/errors');

View file

@ -3,7 +3,7 @@ const _ = require('lodash');
const uuid = require('uuid');
const BaseImporter = require('./base');
const mobiledocLib = require('../../../../lib/mobiledoc');
const validator = require('../../../validator');
const validator = require('@tryghost/validator');
const postsMetaSchema = require('../../../schema').tables.posts_meta;
const metaAttrs = _.keys(_.omit(postsMetaSchema, ['id']));

View file

@ -3,7 +3,7 @@ const Promise = require('bluebird');
const tpl = require('@tryghost/tpl');
const errors = require('@tryghost/errors');
const validator = require('../validator');
const validator = require('@tryghost/validator');
const schema = require('./schema');

View file

@ -1,2 +0,0 @@
module.exports = require('./validator');
module.exports.validate = require('./validate');

View file

@ -1,80 +0,0 @@
const _ = require('lodash');
const validator = require('./validator');
const tpl = require('@tryghost/tpl');
const errors = require('@tryghost/errors');
const messages = {
validationFailed: 'Validation ({validationName}) failed for {key}',
validationFailedTypes: {
isLength: 'Value in [{tableName}.{key}] exceeds maximum length of {max} characters.'
}
};
/**
* Validate keys using the validator module.
* Each validation's key is a method name and its value is an array of options
* eg:
* validations: { isURL: true, isLength: [20, 40] }
* will validate that a values's length is a URL between 20 and 40 chars.
*
* If you pass a boolean as the value, it will specify the "good" result. By default
* the "good" result is assumed to be true.
* eg:
* validations: { isNull: false } // means the "good" result would
* // fail the `isNull` check, so
* // not null.
*
* available validators: https://github.com/chriso/validator.js#validators
* @param {String} value the value to validate.
* @param {String} key the db column key of the value to validate.
* @param {Object} validations the validations object as described above.
* @param {String} [tableName] (optional) the db table of the value to validate, used for error message.
* @return {Array} returns an Array including the found validation errors (empty if none found);
*/
function validate(value, key, validations, tableName) {
const validationErrors = [];
let message;
value = _.toString(value);
_.each(validations, (validationOptions, validationName) => {
let goodResult = true;
if (_.isBoolean(validationOptions)) {
goodResult = validationOptions;
validationOptions = [];
} else if (!_.isArray(validationOptions)) {
validationOptions = [validationOptions];
}
validationOptions.unshift(value);
// equivalent of validator.isSomething(option1, option2)
if (validator[validationName].apply(validator, validationOptions) !== goodResult) {
// CASE: You can define specific messages for validators e.g. isLength
if (_.has(messages.validationFailedTypes, validationName)) {
message = tpl(messages.validationFailedTypes[validationName], _.merge({
validationName: validationName,
key: key,
tableName: tableName
}, validationOptions[1]));
} else {
message = tpl(messages.validationFailed, {
validationName: validationName,
key: key
});
}
validationErrors.push(new errors.ValidationError({
message: message,
context: `${tableName}.${key}`
}));
}
validationOptions.shift();
});
return validationErrors;
}
module.exports = validate;

View file

@ -1,48 +0,0 @@
const _ = require('lodash');
const baseValidator = require('validator');
const moment = require('moment-timezone');
const assert = require('assert');
const allowedValidators = [
'isLength',
'isEmpty',
'isURL',
'isEmail',
'isIn',
'isUUID',
'isBoolean',
'isInt',
'isLowercase',
'equals',
'matches'
];
function assertString(input) {
assert(typeof input === 'string', 'Validator validates strings only');
}
const validators = {};
allowedValidators.forEach((name) => {
if (_.has(baseValidator, name)) {
validators[name] = baseValidator[name];
}
});
validators.isTimezone = function isTimezone(str) {
assertString(str);
return moment.tz.zone(str) ? true : false;
};
validators.isEmptyOrURL = function isEmptyOrURL(str) {
assertString(str);
return (validators.isEmpty(str) || validators.isURL(str, {require_protocol: false}));
};
validators.isSlug = function isSlug(str) {
assertString(str);
return validators.matches(str, /^[a-z0-9\-_]+$/);
};
module.exports = validators;

View file

@ -2,7 +2,7 @@ const request = require('../request');
const urlUtils = require('../../../shared/url-utils');
const storage = require('../../adapters/storage');
const storageUtils = require('../../adapters/storage/utils');
const validator = require('../../data/validator');
const validator = require('@tryghost/validator');
const config = require('../../../shared/config');
const logging = require('@tryghost/logging');
const i18n = require('../../../shared/i18n');

View file

@ -2,7 +2,7 @@ const got = require('got');
const dnsPromises = require('dns').promises;
const errors = require('@tryghost/errors');
const config = require('../../shared/config');
const validator = require('../data/validator');
const validator = require('@tryghost/validator');
function isPrivateIp(addr) {
return /^(::f{4}:)?10\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(addr) ||

View file

@ -1,6 +1,6 @@
const got = require('got');
const _ = require('lodash');
const validator = require('../data/validator');
const validator = require('@tryghost/validator');
const errors = require('@tryghost/errors');
const ghostVersion = require('./ghost-version');

View file

@ -1,6 +1,6 @@
const _ = require('lodash');
const validator = require('../data/validator');
const validator = require('@tryghost/validator');
const tpl = require('@tryghost/tpl');
const settingsCache = require('../services/settings/cache');

View file

@ -7,7 +7,7 @@ const ObjectID = require('bson-objectid');
const ghostBookshelf = require('./base');
const i18n = require('../../shared/i18n');
const errors = require('@tryghost/errors');
const validator = require('../data/validator');
const validator = require('@tryghost/validator');
const urlUtils = require('../../shared/url-utils');
const {WRITABLE_KEYS_ALLOWLIST} = require('../services/labs');

View file

@ -1,6 +1,6 @@
const _ = require('lodash');
const Promise = require('bluebird');
const validator = require('validator');
const validator = require('@tryghost/validator');
const ObjectId = require('bson-objectid');
const ghostBookshelf = require('./base');
const baseUtils = require('./base/utils');

View file

@ -2,7 +2,7 @@
// Handles sending email for Ghost
const _ = require('lodash');
const Promise = require('bluebird');
const validator = require('validator');
const validator = require('@tryghost/validator');
const config = require('../../../shared/config');
const errors = require('@tryghost/errors');
const i18n = require('../../../shared/i18n');

View file

@ -76,6 +76,7 @@
"@tryghost/tpl": "0.1.1",
"@tryghost/update-check-service": "0.1.0",
"@tryghost/url-utils": "1.1.4",
"@tryghost/validator": "0.1.0",
"@tryghost/vhost-middleware": "1.0.15",
"@tryghost/zip": "1.1.14",
"amperize": "0.6.1",
@ -145,7 +146,6 @@
"stoppable": "1.1.0",
"tough-cookie": "4.0.0",
"uuid": "8.3.2",
"validator": "7.2.0",
"xml": "1.0.1"
},
"optionalDependencies": {

View file

@ -13,7 +13,6 @@
"moment",
"moment-timezone",
"nodemailer",
"validator",
"simple-dom"
],
"ignorePaths": ["test"],

View file

@ -6,7 +6,7 @@ const moment = require('moment-timezone');
const ObjectId = require('bson-objectid');
const assert = require('assert');
const _ = require('lodash');
const validator = require('validator');
const validator = require('@tryghost/validator');
// Stuff we are testing
const db = require('../../../core/server/data/db');

View file

@ -1,16 +0,0 @@
const should = require('should');
const validator = require('../../../../core/server/data/validator');
// Validate our customizations
describe('Validate', function () {
it('should export our required functions', function () {
should.exist(validator);
validator.should.have.properties(
['validate']
);
validator.validate.should.be.a.Function();
});
});

View file

@ -1,42 +0,0 @@
const should = require('should');
const validator = require('../../../../core/server/data/validator');
const validators = ['isLength',
'isEmpty',
'isURL',
'isEmail',
'isIn',
'isUUID',
'isBoolean',
'isInt',
'isLowercase',
'equals',
'matches'
];
const custom = ['isTimezone', 'isEmptyOrURL', 'isSlug'];
describe('Validator', function () {
it('should export our required functions', function () {
should.exist(validator);
const allMethods = validators.concat(custom).concat('validate');
Object.keys(validator).should.eql(allMethods);
});
describe('Custom Validators', function () {
it('isEmptyOrUrl filters javascript urls', function () {
validator.isEmptyOrURL('javascript:alert(0)').should.be.false();
validator.isEmptyOrURL('http://example.com/lol/<script>lalala</script>/').should.be.false();
validator.isEmptyOrURL('http://example.com/lol?somequery=<script>lalala</script>').should.be.false();
validator.isEmptyOrURL('').should.be.true();
validator.isEmptyOrURL('http://localhost:2368').should.be.true();
validator.isEmptyOrURL('http://example.com/test/').should.be.true();
validator.isEmptyOrURL('http://www.example.com/test/').should.be.true();
validator.isEmptyOrURL('http://example.com/foo?somequery=bar').should.be.true();
validator.isEmptyOrURL('example.com/test/').should.be.true();
});
});
});

View file

@ -1,6 +1,6 @@
const should = require('should');
const sinon = require('sinon');
const validator = require('validator');
const validator = require('@tryghost/validator');
const requestId = require('../../../../../core/server/web/parent/middleware/request-id');

View file

@ -1015,6 +1015,13 @@
c8 "^7.7.2"
lodash.template "^4.5.0"
"@tryghost/tpl@^0.1.2":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@tryghost/tpl/-/tpl-0.1.2.tgz#4b6acc97a6ac517baf40585216c8adf888d620f1"
integrity sha512-VZyD4v8rNJrJEWUjrAT7YCpEmr+8rXWhQbfmWE199F29pbEpvMPK0MKwF1hTc3b0JkvGOo3lgULJ7KB6TrxvkA==
dependencies:
lodash.template "^4.5.0"
"@tryghost/update-check-service@0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@tryghost/update-check-service/-/update-check-service-0.1.0.tgz#ca1277989989968f2b2603a7d3b86e1124025be5"
@ -1037,6 +1044,17 @@
remark-footnotes "^1.0.0"
unist-util-visit "^2.0.0"
"@tryghost/validator@0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@tryghost/validator/-/validator-0.1.0.tgz#f089fed04998a4ceb4957350117f8d9bb626daf0"
integrity sha512-Np9EN3RayTOd81GjVna6Zv12LsTz98JlqeXbOnUVzrRphARRJHvNwcKi7Kh4EDqA2MWpYQDiU0+QwP7zyA0Gkg==
dependencies:
"@tryghost/errors" "^0.2.12"
"@tryghost/tpl" "^0.1.2"
lodash "^4.17.21"
moment-timezone "0.5.23"
validator "7.2.0"
"@tryghost/vhost-middleware@1.0.15":
version "1.0.15"
resolved "https://registry.yarnpkg.com/@tryghost/vhost-middleware/-/vhost-middleware-1.0.15.tgz#6f5e7e018fe6d9b09b0f2b7f5294f8133398cbd1"