-
Notifications
You must be signed in to change notification settings - Fork 294
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
Detect SQL injection with sequelize #3154
Conversation
Overall package sizeSelf size: 4.19 MB Dependency sizes
🤖 This report was automatically generated by heaviest-objects-in-the-universe |
Codecov Report
@@ Coverage Diff @@
## master #3154 +/- ##
==========================================
+ Coverage 86.54% 86.57% +0.03%
==========================================
Files 333 333
Lines 11895 11922 +27
Branches 33 33
==========================================
+ Hits 10295 10322 +27
Misses 1600 1600
... and 2 files with indirect coverage changes 📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more |
86a79af
to
95d4666
Compare
bc238c1
to
5a707ad
Compare
packages/dd-trace/test/appsec/iast/analyzers/sql-injection-analyzer.sequelize.plugin.spec.js
Outdated
Show resolved
Hide resolved
packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js
Outdated
Show resolved
Hide resolved
if (parentStore) { | ||
this.analyze(sql, dialect.toUpperCase()) | ||
|
||
storage.enterWith({ ...parentStore, sqlAnalyzed: true, sequelizeParentStore: parentStore }) |
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.
This data doesn't seem to ever get cleared from the store so it may have some unintended side-effects. 🤔
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.
This is restored on datadog:sequelize:query:finish
The purpose of this lines is prevent double detections of the same vulnerability. As sequelize library uses pg
or mysql2
libraries, we need a way to set in store that we are already in the query. For that reason we are entering with new store in :start
and restoring the old one in :finish
.
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.
Ah, true. Do you know if sequelize could trigger these in a nested way? Setting on the store rather than storing on the span could make things leak beyond where it should. 🤔
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.
Good point. I've reviewed it after your comment. The answer is not, sequelize.query method does not call internally again to the same query method.
sequelize.query method calls to the query method of the select dialect (pg, sqlite, mysql...)
With sequelize we are not creating new span, so current span is the parent span, and the parent span can have multiple calls to sequelize.query
method, that the reason that we should save the data in AsyncResource (that is new asyncresource, created in the instrumentation point) and not in the parent span.
64d6e66
to
fd087a9
Compare
const startCh = channel('datadog:sequelize:query:start') | ||
const finishCh = channel('datadog:sequelize:query:finish') | ||
|
||
shimmer.wrap(Sequelize.prototype, 'query', query => function (sql) { |
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.
VERY weird syntax, i didn't know you could even do that, maybe it's worth to declare the functions above with names instead of putting everything inline
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.
Maybe I can just do something like, that looks easier to read and understand.
shimmer.wrap(Sequelize.prototype, 'query', query => {
return function (sql) {
...
}
}
(I'm doing this because I've seen it in other files:
shimmer.wrap(Connection.prototype, 'addCommand', addCommand => function (cmd) { |
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.
This is a fairly common pattern actually, but usually it would all be arrow functions, for example query => sql => {}
. The reason for using regular functions originally was to be able to name them, but I'm not sure if that's relevant here.
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.
If we use arrow functions to wrap methods, we will override the this
parameter into the methods, because we are not going to be able to get the original this
because in the arrow functions, this
makes reference to the parent function this
.
Co-authored-by: Stephen Belanger <stephen.belanger@datadoghq.com>
Co-authored-by: Igor Unanua <igor.unanua@datadoghq.com>
d31cc9e
to
da59b75
Compare
--------- Co-authored-by: Stephen Belanger <stephen.belanger@datadoghq.com> Co-authored-by: Igor Unanua <igor.unanua@datadoghq.com>
--------- Co-authored-by: Stephen Belanger <stephen.belanger@datadoghq.com> Co-authored-by: Igor Unanua <igor.unanua@datadoghq.com>
--------- Co-authored-by: Stephen Belanger <stephen.belanger@datadoghq.com> Co-authored-by: Igor Unanua <igor.unanua@datadoghq.com>
--------- Co-authored-by: Stephen Belanger <stephen.belanger@datadoghq.com> Co-authored-by: Igor Unanua <igor.unanua@datadoghq.com>
--------- Co-authored-by: Stephen Belanger <stephen.belanger@datadoghq.com> Co-authored-by: Igor Unanua <igor.unanua@datadoghq.com>
--------- Co-authored-by: Stephen Belanger <stephen.belanger@datadoghq.com> Co-authored-by: Igor Unanua <igor.unanua@datadoghq.com>
--------- Co-authored-by: Stephen Belanger <stephen.belanger@datadoghq.com> Co-authored-by: Igor Unanua <igor.unanua@datadoghq.com>
What does this PR do?
With sequelize is used as
sequelize.query(sql)
if the sql comes from the request, the point will be reported as vulnerable.Motivation
we are already supporting frameworks like pg or mysql, but supporting sequelize we can support all the databases that it supports (oracle, sqlite...) and calculate an accurate vulnerability file and line.
Plugin Checklist
Additional Notes
For APM ownership changes:
This PR just adds hooks for sequelize library, to be able to analyze the query method.