不要怂,就是干,撸起袖子干!

Commit e22ce18d by Michael Kaufman Committed by Jan Aagaard Meier

6935 remove order string syntax (#7220)

* Refactored support for ORDER BY clause so SQL injection is not possible.

* Adds sublime text files to gitignore.
Modified changelog to accurately represent changes to the order property.
Fixed lint issues.

* Added rejection with error where Order must be type of Array or instance of sequelize literal.

* Removed support for options.order accepting strings.
@TODO Look into plain objects with property raw
@TODO Update documentation and tests

* Removed all direct lodash and replaced them with Utils.
ie. `_.isPlainObject` is now `Utils._.isPlainObject`
find: `(?s)(?<!Utils\.)_\.`
replace : `Utils._.`

* Updates documentation for findAll order.

* Added removeTicks to addTicks in quoteIdentifier for each dialect.

* Added documentation for order by only an array.

* Added breaking change to changelog and renamed error to be more general.

@TODO: TESTS!

* Fixes bad code when checking if the order is valid.

* Updated unit tests.

* Should incorporate all integration tests.

* Update changelog.md capitalize Sequelize

* Changes everything in query generator to use lodash outside of utils.

* Instead of checking if the order object is an instance of a Utils member we just see if `_.isSequelizeMethod` is truthy.

* Removing escapeId because it is no longer used.

* Instead of removing ticks, we should escape them.

* Instead of removing ticks, we should escape them. (reverted from commit e4dade0a18be85dfaf8f91f1210bc0b5bedcb31a)

* Removes support for `._isSequelizeMethod` because it can be spoofed.  Instead, use instanceof.
1 parent c12b36b8
...@@ -479,7 +479,7 @@ const QueryGenerator = { ...@@ -479,7 +479,7 @@ const QueryGenerator = {
const fieldsSql = options.fields.map(field => { const fieldsSql = options.fields.map(field => {
if (typeof field === 'string') { if (typeof field === 'string') {
return this.quoteIdentifier(field); return this.quoteIdentifier(field);
} else if (field._isSequelizeMethod) { } else if (field instanceof Utils.SequelizeMethod) {
return this.handleSequelizeMethod(field); return this.handleSequelizeMethod(field);
} else { } else {
let result = ''; let result = '';
...@@ -635,7 +635,7 @@ const QueryGenerator = { ...@@ -635,7 +635,7 @@ const QueryGenerator = {
for (i = 0; i < len - 1; i++) { for (i = 0; i < len - 1; i++) {
item = obj[i]; item = obj[i];
if (item._modelAttribute || _.isString(item) || item._isSequelizeMethod || 'raw' in item) { if (item._modelAttribute || _.isString(item) || item instanceof Utils.SequelizeMethod || 'raw' in item) {
break; break;
} }
...@@ -668,7 +668,7 @@ const QueryGenerator = { ...@@ -668,7 +668,7 @@ const QueryGenerator = {
// add 1st string as quoted, 2nd as unquoted raw // add 1st string as quoted, 2nd as unquoted raw
let sql = (i > 0 ? this.quoteIdentifier(tableNames.join(connector)) + '.' : (_.isString(obj[0]) && parent ? this.quoteIdentifier(parent.name) + '.' : '')) + this.quote(obj[i], parent, force); let sql = (i > 0 ? this.quoteIdentifier(tableNames.join(connector)) + '.' : (_.isString(obj[0]) && parent ? this.quoteIdentifier(parent.name) + '.' : '')) + this.quote(obj[i], parent, force);
if (i < len - 1) { if (i < len - 1) {
if (obj[i + 1]._isSequelizeMethod) { if (obj[i + 1] instanceof Utils.SequelizeMethod) {
sql += this.handleSequelizeMethod(obj[i + 1]); sql += this.handleSequelizeMethod(obj[i + 1]);
} else { } else {
sql += ' ' + obj[i + 1]; sql += ' ' + obj[i + 1];
...@@ -677,7 +677,7 @@ const QueryGenerator = { ...@@ -677,7 +677,7 @@ const QueryGenerator = {
return sql; return sql;
} else if (obj._modelAttribute) { } else if (obj._modelAttribute) {
return this.quoteTable(obj.Model.name) + '.' + obj.fieldName; return this.quoteTable(obj.Model.name) + '.' + obj.fieldName;
} else if (obj._isSequelizeMethod) { } else if (obj instanceof Utils.SequelizeMethod) {
return this.handleSequelizeMethod(obj); return this.handleSequelizeMethod(obj);
} else if (_.isObject(obj) && 'raw' in obj) { } else if (_.isObject(obj) && 'raw' in obj) {
return obj.raw; return obj.raw;
...@@ -707,7 +707,7 @@ const QueryGenerator = { ...@@ -707,7 +707,7 @@ const QueryGenerator = {
options = options || {}; options = options || {};
if (value !== null && value !== undefined) { if (value !== null && value !== undefined) {
if (value._isSequelizeMethod) { if (value instanceof Utils.SequelizeMethod) {
return this.handleSequelizeMethod(value); return this.handleSequelizeMethod(value);
} else { } else {
if (field && field.type) { if (field && field.type) {
...@@ -1029,7 +1029,7 @@ const QueryGenerator = { ...@@ -1029,7 +1029,7 @@ const QueryGenerator = {
return attributes && attributes.map(attr => { return attributes && attributes.map(attr => {
let addTable = true; let addTable = true;
if (attr._isSequelizeMethod) { if (attr instanceof Utils.SequelizeMethod) {
return this.handleSequelizeMethod(attr); return this.handleSequelizeMethod(attr);
} }
if (Array.isArray(attr)) { if (Array.isArray(attr)) {
...@@ -1038,7 +1038,7 @@ const QueryGenerator = { ...@@ -1038,7 +1038,7 @@ const QueryGenerator = {
} }
attr = attr.slice(); attr = attr.slice();
if (attr[0]._isSequelizeMethod) { if (attr[0] instanceof Utils.SequelizeMethod) {
attr[0] = this.handleSequelizeMethod(attr[0]); attr[0] = this.handleSequelizeMethod(attr[0]);
addTable = false; addTable = false;
} else if (attr[0].indexOf('(') === -1 && attr[0].indexOf(')') === -1) { } else if (attr[0].indexOf('(') === -1 && attr[0].indexOf(')') === -1) {
...@@ -1089,16 +1089,15 @@ const QueryGenerator = { ...@@ -1089,16 +1089,15 @@ const QueryGenerator = {
let verbatim = false; let verbatim = false;
if (Array.isArray(attr) && attr.length === 2) { if (Array.isArray(attr) && attr.length === 2) {
if (attr[0]._isSequelizeMethod) { if (attr[0] instanceof Utils.SequelizeMethod && (
if (attr[0] instanceof Utils.Literal || attr[0] instanceof Utils.Literal ||
attr[0] instanceof Utils.Cast || attr[0] instanceof Utils.Cast ||
attr[0] instanceof Utils.Fn attr[0] instanceof Utils.Fn
) { )) {
verbatim = true; verbatim = true;
}
} }
attr = attr.map(attr => attr._isSequelizeMethod ? this.handleSequelizeMethod(attr) : attr); attr = attr.map(attr => attr instanceof Utils.SequelizeMethod ? this.handleSequelizeMethod(attr) : attr);
attrAs = attr[1]; attrAs = attr[1];
attr = attr[0]; attr = attr[0];
...@@ -1496,7 +1495,7 @@ const QueryGenerator = { ...@@ -1496,7 +1495,7 @@ const QueryGenerator = {
]; ];
const validateOrder = order => { const validateOrder = order => {
if (order._isSequelizeMethod) { if (order instanceof Utils.SequelizeMethod) {
return; return;
} }
...@@ -1523,7 +1522,7 @@ const QueryGenerator = { ...@@ -1523,7 +1522,7 @@ const QueryGenerator = {
mainQueryOrder.push(this.quote(order, model, false, '->')); mainQueryOrder.push(this.quote(order, model, false, '->'));
} }
} else if (options.order._isSequelizeMethod){ } else if (options.order instanceof Utils.SequelizeMethod){
const sql = this.quote(options.order, model, false, '->'); const sql = this.quote(options.order, model, false, '->');
if (subQuery) { if (subQuery) {
subQueryOrder.push(sql); subQueryOrder.push(sql);
...@@ -1682,13 +1681,13 @@ const QueryGenerator = { ...@@ -1682,13 +1681,13 @@ const QueryGenerator = {
let value = smth.logic; let value = smth.logic;
let key; let key;
if (smth.attribute._isSequelizeMethod) { if (smth.attribute instanceof Utils.SequelizeMethod) {
key = this.getWhereConditions(smth.attribute, tableName, factory, options, prepend); key = this.getWhereConditions(smth.attribute, tableName, factory, options, prepend);
} else { } else {
key = this.quoteTable(smth.attribute.Model.name) + '.' + this.quoteIdentifier(smth.attribute.field || smth.attribute.fieldName); key = this.quoteTable(smth.attribute.Model.name) + '.' + this.quoteIdentifier(smth.attribute.field || smth.attribute.fieldName);
} }
if (value && value._isSequelizeMethod) { if (value && value instanceof Utils.SequelizeMethod) {
value = this.getWhereConditions(value, tableName, factory, options, prepend); value = this.getWhereConditions(value, tableName, factory, options, prepend);
result = (value === 'NULL') ? key + ' IS NULL' : [key, value].join(smth.comparator); result = (value === 'NULL') ? key + ' IS NULL' : [key, value].join(smth.comparator);
...@@ -1708,7 +1707,7 @@ const QueryGenerator = { ...@@ -1708,7 +1707,7 @@ const QueryGenerator = {
} else if (smth instanceof Utils.Literal) { } else if (smth instanceof Utils.Literal) {
result = smth.val; result = smth.val;
} else if (smth instanceof Utils.Cast) { } else if (smth instanceof Utils.Cast) {
if (smth.val._isSequelizeMethod) { if (smth.val instanceof Utils.SequelizeMethod) {
result = this.handleSequelizeMethod(smth.val, tableName, factory, options, prepend); result = this.handleSequelizeMethod(smth.val, tableName, factory, options, prepend);
} else if (_.isPlainObject(smth.val)) { } else if (_.isPlainObject(smth.val)) {
result = this.whereItemsQuery(smth.val); result = this.whereItemsQuery(smth.val);
...@@ -1719,7 +1718,7 @@ const QueryGenerator = { ...@@ -1719,7 +1718,7 @@ const QueryGenerator = {
result = 'CAST(' + result + ' AS ' + smth.type.toUpperCase() + ')'; result = 'CAST(' + result + ' AS ' + smth.type.toUpperCase() + ')';
} else if (smth instanceof Utils.Fn) { } else if (smth instanceof Utils.Fn) {
result = smth.fn + '(' + smth.args.map(arg => { result = smth.fn + '(' + smth.args.map(arg => {
if (arg._isSequelizeMethod) { if (arg instanceof Utils.SequelizeMethod) {
return this.handleSequelizeMethod(arg, tableName, factory, options, prepend); return this.handleSequelizeMethod(arg, tableName, factory, options, prepend);
} else if (_.isPlainObject(arg)) { } else if (_.isPlainObject(arg)) {
return this.whereItemsQuery(arg); return this.whereItemsQuery(arg);
...@@ -1875,7 +1874,7 @@ const QueryGenerator = { ...@@ -1875,7 +1874,7 @@ const QueryGenerator = {
} }
} }
if (value && value._isSequelizeMethod && !(key !== undefined && value instanceof Utils.Fn)) { if (value && value instanceof Utils.SequelizeMethod && !(key !== undefined && value instanceof Utils.Fn)) {
return this.handleSequelizeMethod(value); return this.handleSequelizeMethod(value);
} }
...@@ -2134,7 +2133,7 @@ const QueryGenerator = { ...@@ -2134,7 +2133,7 @@ const QueryGenerator = {
if (key) { if (key) {
let prefix = true; let prefix = true;
if (key._isSequelizeMethod) { if (key instanceof Utils.SequelizeMethod) {
key = this.handleSequelizeMethod(key); key = this.handleSequelizeMethod(key);
} else if (Utils.isColString(key)) { } else if (Utils.isColString(key)) {
key = key.substr(1, key.length - 2).split('.'); key = key.substr(1, key.length - 2).split('.');
...@@ -2186,7 +2185,7 @@ const QueryGenerator = { ...@@ -2186,7 +2185,7 @@ const QueryGenerator = {
prepend = true; prepend = true;
} }
if (smth && smth._isSequelizeMethod === true) { // Checking a property is cheaper than a lot of instanceof calls if (smth && smth instanceof Utils.SequelizeMethod) { // Checking a property is cheaper than a lot of instanceof calls
result = this.handleSequelizeMethod(smth, tableName, factory, options, prepend); result = this.handleSequelizeMethod(smth, tableName, factory, options, prepend);
} else if (_.isPlainObject(smth)) { } else if (_.isPlainObject(smth)) {
return this.whereItemsQuery(smth, { return this.whereItemsQuery(smth, {
......
...@@ -319,7 +319,7 @@ class InstanceValidator { ...@@ -319,7 +319,7 @@ class InstanceValidator {
} }
if (rawAttribute.type === DataTypes.STRING || rawAttribute.type instanceof DataTypes.STRING || rawAttribute.type === DataTypes.TEXT || rawAttribute.type instanceof DataTypes.TEXT) { if (rawAttribute.type === DataTypes.STRING || rawAttribute.type instanceof DataTypes.STRING || rawAttribute.type === DataTypes.TEXT || rawAttribute.type instanceof DataTypes.TEXT) {
if (Array.isArray(value) || (_.isObject(value) && !value._isSequelizeMethod) && !Buffer.isBuffer(value)) { if (Array.isArray(value) || (_.isObject(value) && !(value instanceof Utils.SequelizeMethod)) && !Buffer.isBuffer(value)) {
error = new sequelizeError.ValidationErrorItem(`${field} cannot be an array or an object`, 'string violation', field, value); error = new sequelizeError.ValidationErrorItem(`${field} cannot be an array or an object`, 'string violation', field, value);
this.errors.push(error); this.errors.push(error);
} }
......
...@@ -2403,7 +2403,7 @@ class Model { ...@@ -2403,7 +2403,7 @@ class Model {
throw new Error('Missing where or truncate attribute in the options parameter of model.destroy.'); throw new Error('Missing where or truncate attribute in the options parameter of model.destroy.');
} }
if (!options.truncate && !_.isPlainObject(options.where) && !_.isArray(options.where) && options.where._isSequelizeMethod !== true) { if (!options.truncate && !_.isPlainObject(options.where) && !_.isArray(options.where) && !(options.where instanceof Utils.SequelizeMethod)) {
throw new Error('Expected plain object, array or sequelize method in the options.where parameter of model.destroy.'); throw new Error('Expected plain object, array or sequelize method in the options.where parameter of model.destroy.');
} }
...@@ -2560,7 +2560,7 @@ class Model { ...@@ -2560,7 +2560,7 @@ class Model {
throw new Error('Missing where attribute in the options parameter passed to update.'); throw new Error('Missing where attribute in the options parameter passed to update.');
} }
if (!_.isPlainObject(options.where) && !_.isArray(options.where) && options.where._isSequelizeMethod !== true) { if (!_.isPlainObject(options.where) && !_.isArray(options.where) && !(options.where instanceof Utils.SequelizeMethod)) {
throw new Error('Expected plain object, array or sequelize method in the options.where parameter of model.update.'); throw new Error('Expected plain object, array or sequelize method in the options.where parameter of model.update.');
} }
...@@ -2885,7 +2885,7 @@ class Model { ...@@ -2885,7 +2885,7 @@ class Model {
if (this.constructor._hasDefaultValues) { if (this.constructor._hasDefaultValues) {
defaults = _.mapValues(this.constructor._defaultValues, valueFn => { defaults = _.mapValues(this.constructor._defaultValues, valueFn => {
const value = valueFn(); const value = valueFn();
return value && value._isSequelizeMethod ? value : _.cloneDeep(value); return value && value instanceof Utils.SequelizeMethod ? value : _.cloneDeep(value);
}); });
} }
...@@ -3159,7 +3159,7 @@ class Model { ...@@ -3159,7 +3159,7 @@ class Model {
} }
// Convert date fields to real date objects // Convert date fields to real date objects
if (this.constructor._hasDateAttributes && this.constructor._isDateAttribute(key) && !!value && !value._isSequelizeMethod) { if (this.constructor._hasDateAttributes && this.constructor._isDateAttribute(key) && !!value && !(value instanceof Utils.SequelizeMethod)) {
// Dont parse DATEONLY to new Date, keep them as string // Dont parse DATEONLY to new Date, keep them as string
if (this.rawAttributes[key].type instanceof DataTypes.DATEONLY) { if (this.rawAttributes[key].type instanceof DataTypes.DATEONLY) {
if (originalValue && originalValue === value) { if (originalValue && originalValue === value) {
...@@ -3184,7 +3184,7 @@ class Model { ...@@ -3184,7 +3184,7 @@ class Model {
} }
// Convert boolean-ish values to booleans // Convert boolean-ish values to booleans
if (this.constructor._hasBooleanAttributes && this.constructor._isBooleanAttribute(key) && value !== null && value !== undefined && !value._isSequelizeMethod) { if (this.constructor._hasBooleanAttributes && this.constructor._isBooleanAttribute(key) && value !== null && value !== undefined && !(value instanceof Utils.SequelizeMethod)) {
if (Buffer.isBuffer(value) && value.length === 1) { if (Buffer.isBuffer(value) && value.length === 1) {
// Bit fields are returned as buffers // Bit fields are returned as buffers
value = value[0]; value = value[0];
......
...@@ -455,8 +455,12 @@ exports.removeTicks = removeTicks; ...@@ -455,8 +455,12 @@ exports.removeTicks = removeTicks;
* Please do not use these functions directly, use Sequelize.fn and Sequelize.col instead. * Please do not use these functions directly, use Sequelize.fn and Sequelize.col instead.
* @private * @private
*/ */
class Fn { class SequelizeMethod {}
exports.SequelizeMethod = SequelizeMethod;
class Fn extends SequelizeMethod {
constructor(fn, args) { constructor(fn, args) {
super();
this.fn = fn; this.fn = fn;
this.args = args; this.args = args;
} }
...@@ -466,8 +470,9 @@ class Fn { ...@@ -466,8 +470,9 @@ class Fn {
} }
exports.Fn = Fn; exports.Fn = Fn;
class Col { class Col extends SequelizeMethod {
constructor(col) { constructor(col) {
super();
if (arguments.length > 1) { if (arguments.length > 1) {
col = this.sliceArgs(arguments); col = this.sliceArgs(arguments);
} }
...@@ -476,23 +481,26 @@ class Col { ...@@ -476,23 +481,26 @@ class Col {
} }
exports.Col = Col; exports.Col = Col;
class Cast { class Cast extends SequelizeMethod {
constructor(val, type) { constructor(val, type) {
super();
this.val = val; this.val = val;
this.type = (type || '').trim(); this.type = (type || '').trim();
} }
} }
exports.Cast = Cast; exports.Cast = Cast;
class Literal { class Literal extends SequelizeMethod {
constructor(val) { constructor(val) {
super();
this.val = val; this.val = val;
} }
} }
exports.Literal = Literal; exports.Literal = Literal;
class Json { class Json extends SequelizeMethod {
constructor(conditionsOrPath, value) { constructor(conditionsOrPath, value) {
super();
if (_.isObject(conditionsOrPath)) { if (_.isObject(conditionsOrPath)) {
this.conditions = conditionsOrPath; this.conditions = conditionsOrPath;
} else { } else {
...@@ -505,8 +513,9 @@ class Json { ...@@ -505,8 +513,9 @@ class Json {
} }
exports.Json = Json; exports.Json = Json;
class Where { class Where extends SequelizeMethod {
constructor(attribute, comparator, logic) { constructor(attribute, comparator, logic) {
super();
if (logic === undefined) { if (logic === undefined) {
logic = comparator; logic = comparator;
comparator = '='; comparator = '=';
...@@ -519,11 +528,4 @@ class Where { ...@@ -519,11 +528,4 @@ class Where {
} }
exports.Where = Where; exports.Where = Where;
Where.prototype._isSequelizeMethod =
Literal.prototype._isSequelizeMethod =
Cast.prototype._isSequelizeMethod =
Fn.prototype._isSequelizeMethod =
Col.prototype._isSequelizeMethod =
Json.prototype._isSequelizeMethod = true;
exports.validateParameter = parameterValidator; exports.validateParameter = parameterValidator;
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!