-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
fix(query-generator): ignore undefined keys in query #9548
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
1446107 to
1166017
Compare
|
I just want to say #8583 (comment) is a good argument, especially for find / destroy operations |
|
Ok, another option that is better than current behavior is to UPD: we can add a global parameter that would switch between |
|
Why don't we push I think original idea was similar to this, push
I don't like switching behaviours with such flags, it makes it hard to debug why two similar where options are failing on one machine but working on other machine. #8583 (comment) argument is good, but should we follow it? if I am correct we already ignore I guess I would like to protect those unsuspecting users which might end with I think we can ignore |
|
Ok, so I'm going to ignore |
|
@sushantdhiman looks like we are not ignoring const Sequelize = require('Sequelize')
const sequelize = new Sequelize(
'db', 'root', 'root', {
host: 'localhost',
dialect: 'mysql',
pool: {max: 100, min: 0, idle: 10000},
dialectOptions: {
charset: 'utf8_general_ci',
},
}
)
;(async () => {
await sequelize.authenticate()
const User = sequelize.define('User', {
id: {type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true},
name: {type: Sequelize.STRING, allowNull: true}
})
await sequelize.sync({force: true})
await User.update({name: 'ololo'}, {where: {name: undefined}})
await sequelize.close()
})()will throw and I can assume that after #9512 Of course, if I'll add the code I'm having now there would be no query generated for How would do you think it should be resolved? |
|
@Alexsey I meant for user.update({
name: undefined // will be ignored
})
How so? I agree as we start ignoring undefined for update / select, we wont be generating queries for
Ignore Fail fast for |
d38631a to
925f94b
Compare
|
@sushantdhiman it's ready |
| if (key && typeof key === 'string' && key.indexOf('.') !== -1 && options.model) { | ||
| if (value === undefined) { | ||
| if ([QueryTypes.BULKDELETE, QueryTypes.DELETE].includes(options.type)) { | ||
| throw Error(`WHERE parameter "${key}" of DELETE query has value of undefined`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just for consistency sake
throw new Error(`....`)
| if ([QueryTypes.BULKDELETE, QueryTypes.DELETE].includes(options.type)) { | ||
| throw Error(`WHERE parameter "${key}" of DELETE query has value of undefined`); | ||
| } | ||
| return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A comment on return
for other query types, ignore all where parameters with undefined value
test/unit/sql/delete.test.js
Outdated
| User | ||
| ); | ||
| return expectsql(sqlOrError, { | ||
| default: Error('WHERE parameter "name" of DELETE query has value of undefined'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
alone default should be enough
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And use new Error
test/unit/sql/where.test.js
Outdated
| default: 'WHERE [id] = 1' | ||
| }); | ||
| testsql({id: 1, user: undefined}, {type: QueryTypes.DELETE}, { | ||
| default: Error('WHERE parameter "user" of DELETE query has value of undefined') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new
test/unit/sql/where.test.js
Outdated
| default: Error('WHERE parameter "user" of DELETE query has value of undefined') | ||
| }); | ||
| testsql({id: 1, user: undefined}, {type: QueryTypes.BULKDELETE}, { | ||
| default: Error('WHERE parameter "user" of DELETE query has value of undefined') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new
I disagree on having different behavior for DELETE. UPDATE can be just as destructive as DELETE, so having different behavior for DELETE in the name of safety doesn't seem worth it. |
|
I don't have a strong opinion on this - just want to fix current behaviour that is broken from any perspective. We can make |
Incorrect or missing data may be, but at-least records wont be deleted. I just want to avoid gotcha moments which involves loss of data
:) then half of the queries will be running in different mode than other, but actually make sense to throw for Would also like comments from @sequelize/orm |
|
Including #7056 in discussion, I propose
|
|
I think we should be concerned only about operations that affect multiple records on write. That are |
|
as for #7056 I think we can include it in the scope of this PR, but it's quite different and nothing is still done for it at this moment |
|
Because @jony89 told that he would like to finish his PR for update payload #7056 (comment) I would not try to include those changes in this PR At this point, I have nothing more to add to code unless someone else has some comments |
|
BTW I think this should have a big announcement in the migration to v5 guide, because this may silently break security: const authorizationToken = AuthorizationToken.findOne({where: {token: cookie.someSessionToken})
assert(authorizationToken, Error('Unauthorized'))so this PR can break authorization check by returning arbitrary result instead of no result. Possibly some other simmilar valnaribilities |
Is there an option to change this behavior? That would make migrating a lot safer and easier for such cases. |
|
This change is definitely a bit dangerous, i don't think errors like this should fail silently. |
|
Looking at my codebase I can tell that it's the desired behavior in at least 90% of cases, so generally, I'm for having it this way. Reverting this to the way it was is is not an option because the SQL that was generating before is close to invalid and 100% unexpected And I don't think there is any significant risk for a new code. Only for migration |
|
@Alexsey Having invalid SQL sounds more proper to me, since that's atleast not silently ignoring an error like this change does. While i do agree that the SQL generated was unexpected, i think the new SQL can produce even more unexpected behaviour. When something is an error i think it's better to return less results than more results, with security and ACL in mind. As you yourself mention this could introduce vulnerabilities, which i think is generally the wrong way to go. In your example I think ideally having any undefined values should actually throw, but that's just my 2c. |
|
@mickhansen I don't think ignoring But if we want to cover all cases I can propose a new It's just a brainstorming - any ideas are non-final and discussible |
|
Following the discussion and it does seem a bit sketchy. I tend to agree with @mickhansen and the comment made by @leahciMic in the other issue. Explicitly dropping the keys from your query params prior to passing them to sequelize shows clear intent vs potentially exposing or destroying data by accident. The original example in #8583 could be completely avoided by ensuring that the route which is handling the request validates that name is a string before the request is accepted and route handler is executed instead of assuming the param will exist. Placing this functionality behind a flag was also suggested. I feel that is a more more acceptable way to support this functionality as it explicitly show your intentions to automatically have sequelize drop keys from your query. |
|
@calmdev IMHO it's too minor and too specific thing to be flagged. If we will have a flag for each thing this size we will have hundreds of flags. And the comment you are pointing is not relevant because |
|
Yes, true. I see in the table above. My intention referencing that comment was that it's easy enough to omit the keys with undefined values before passing them to a query. I wouldn't say it's a minor thing though, because the proposal doesn't address the potential issue of exposing more data than intended which you guys have already pointed out. All the other use cases in original issue are related to selecting data. Regardless, I'm still not convinced that this should be default behavior or even the responsibility of sequelize 🤷♂️ |
|
I think we should distinguish between IMO we should keep this PR and add this slight change. I understand that one can think that this way the behavior across the system is not consistent. but it does not have to be always, there is different solution for different cases. |
|
New version 5.0.0-beta.10 published with this change. Thanks for new comments everyone, this is still a beta feature / release so we can change things. For me here are things that I don't want to support
What do you think about converting Updating my original table
|
|
@sushantdhiman , I also agree with 1. + 2. Yet I don't think that using I liked it better with the |
|
I don't think that |
|
Ignoring the parameter when it is Now I do think the developers should share in part of the onus on validating input. I think it would be better for the library to fail fast when the intention of the developer and the data given do match up. At least now in 4.x we get a query that is impossible to get results from Now for real life example 😄 In my application I had an edge case where one of the field in the where clause was undefined. In sequelize v4 this was just returning zero results. With what has been purposed / merged for v5 would have caused unauthorized users see data they should not have access to too 😢 |
|
Now Sequelize will throw for any EDIT: 5.0.0-beta.13 released with this change |
Pull Request check-list
Please make sure to review and check all of these items:
npm run testornpm run test-DIALECTpass with this change (including linting)?Description of change
Fixes #8583
Right now if a query contains an
undefinedkeys they are converted toNULLin= NULLway that makes queries absolutely useless. This PR would ignoreundefinedkeys in queries, just as settled in the issueBTW this code is treating
undefinedasNULLthat is contradict to discussion in #8478 . Should it be changed some how?