-
Notifications
You must be signed in to change notification settings - Fork 2k
Python: promote nosql query #14070
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
Python: promote nosql query #14070
Changes from 12 commits
60dc1af
55707d3
db04597
087961d
19046ea
bf8bfd9
114984b
c0b3245
7edebbe
f253f97
970e881
b07d085
d91cd21
154a369
d9f63e1
a063d7d
4614b1a
5611bda
30c37ca
7c085ec
4ec8b3f
12dab88
8156fa9
37a4f35
3fb579e
d90630a
c2b6383
9682c82
2a739b3
eb1be08
2a7b593
a8e0023
d5b64c5
3043633
2e028a4
74d6f37
2d845e3
e170805
f3a0161
9769668
16e1a00
d7ad5a0
3676262
d6d13f8
9b73bbf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -378,6 +378,68 @@ module SqlExecution { | |
| } | ||
| } | ||
|
|
||
| /** Provides a class for modeling NoSql execution APIs. */ | ||
| module NoSqlQuery { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you consider naming this
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just kept the name from the submission, but I agree that I should probably change it 👍 |
||
| /** | ||
| * A data-flow node that executes NoSQL queries. | ||
| * | ||
| * Extend this class to model new APIs. If you want to refine existing API models, | ||
| * extend `NoSQLQuery` instead. | ||
|
yoff marked this conversation as resolved.
Outdated
|
||
| */ | ||
| abstract class Range extends DataFlow::Node { | ||
| /** Gets the argument that specifies the NoSql query to be executed. */ | ||
| abstract DataFlow::Node getQuery(); | ||
|
|
||
| /** Holds if this query will unpack/interpret a dictionary */ | ||
| abstract predicate interpretsDict(); | ||
|
|
||
| /** Holds if this query can be dangerous when run on a user-controlled string */ | ||
| abstract predicate vulnerableToStrings(); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * A data-flow node that executes NoSQL queries. | ||
| * | ||
| * Extend this class to refine existing API models. If you want to model new APIs, | ||
| * extend `NoSQLQuery::Range` instead. | ||
|
yoff marked this conversation as resolved.
Outdated
|
||
| */ | ||
| class NoSqlQuery extends DataFlow::Node instanceof NoSqlQuery::Range { | ||
| /** Gets the argument that specifies the NoSql query to be executed. */ | ||
| DataFlow::Node getQuery() { result = super.getQuery() } | ||
|
|
||
| /** Holds if this query will unpack/interpret a dictionary */ | ||
| predicate interpretsDict() { super.interpretsDict() } | ||
|
|
||
| /** Holds if this query can be dangerous when run on a user-controlled string */ | ||
| predicate vulnerableToStrings() { super.vulnerableToStrings() } | ||
| } | ||
|
|
||
| /** Provides classes for modeling NoSql sanitization-related APIs. */ | ||
| module NoSqlSanitizer { | ||
| /** | ||
| * A data-flow node that collects functions sanitizing NoSQL queries. | ||
| * | ||
| * Extend this class to model new APIs. If you want to refine existing API models, | ||
| * extend `NoSQLSanitizer` instead. | ||
| */ | ||
| abstract class Range extends DataFlow::Node { | ||
| /** Gets the argument that specifies the NoSql query to be sanitized. */ | ||
| abstract DataFlow::Node getAnInput(); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * A data-flow node that collects functions sanitizing NoSQL queries. | ||
| * | ||
| * Extend this class to model new APIs. If you want to refine existing API models, | ||
| * extend `NoSQLSanitizer::Range` instead. | ||
| */ | ||
| class NoSqlSanitizer extends DataFlow::Node instanceof NoSqlSanitizer::Range { | ||
| /** Gets the argument that specifies the NoSql query to be sanitized. */ | ||
| DataFlow::Node getAnInput() { result = super.getAnInput() } | ||
| } | ||
|
|
||
| /** | ||
| * A data-flow node that executes a regular expression. | ||
| * | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| /** | ||
| * Provides default sources, sinks and sanitizers for detecting | ||
| * "NoSql injection" | ||
| * vulnerabilities, as well as extension points for adding your own. | ||
| */ | ||
|
|
||
| import python | ||
| import semmle.python.dataflow.new.DataFlow | ||
| import semmle.python.dataflow.new.RemoteFlowSources | ||
| import semmle.python.Concepts | ||
|
|
||
| /** | ||
| * Provides default sources, sinks and sanitizers for detecting | ||
| * "NoSql injection" | ||
| * vulnerabilities, as well as extension points for adding your own. | ||
| */ | ||
| module NoSqlInjection { | ||
| private newtype TFlowState = | ||
| TStringInput() or | ||
| TDictInput() | ||
|
|
||
| /** A flow state, tracking the structure of the input. */ | ||
| abstract class FlowState extends TFlowState { | ||
| /** Gets a textual representation of this element. */ | ||
| abstract string toString(); | ||
| } | ||
|
|
||
| /** A state where input is only a string. */ | ||
| class StringInput extends FlowState, TStringInput { | ||
| override string toString() { result = "StringInput" } | ||
| } | ||
|
|
||
| /** A state where input is a dictionary. */ | ||
| class DictInput extends FlowState, TDictInput { | ||
| override string toString() { result = "DictInput" } | ||
| } | ||
|
|
||
| /** A source allowing string inputs. */ | ||
| abstract class StringSource extends DataFlow::Node { } | ||
|
|
||
| /** A source allowing dictionary inputs. */ | ||
| abstract class DictSource extends DataFlow::Node { } | ||
|
|
||
| /** A sink vulnerable to user controlled strings. */ | ||
| abstract class StringSink extends DataFlow::Node { } | ||
|
|
||
| /** A sink vulnerable to user controlled dictionaries. */ | ||
| abstract class DictSink extends DataFlow::Node { } | ||
|
|
||
| /** A data flow node where a string is converted into a dictionary. */ | ||
| abstract class StringToDictConversion extends DataFlow::Node { | ||
| /** Gets the argument that specifies the string to be converted. */ | ||
| abstract DataFlow::Node getAnInput(); | ||
|
|
||
| /** Gets the resulting dictionary. */ | ||
| abstract DataFlow::Node getOutput(); | ||
| } | ||
|
|
||
| /** A remote flow source considered a source of user controlled strings. */ | ||
| class RemoteFlowSourceAsStringSource extends RemoteFlowSource, StringSource { } | ||
|
|
||
| /** A NoSQL query that is vulnerable to user controlled strings. */ | ||
| class NoSqlQueryAsStringSink extends StringSink { | ||
| NoSqlQueryAsStringSink() { | ||
| exists(NoSqlQuery noSqlQuery | this = noSqlQuery.getQuery() | | ||
| noSqlQuery.vulnerableToStrings() | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| /** A NoSQL query that is vulnerable to user controlled dictionaries. */ | ||
| class NoSqlQueryAsDictSink extends DictSink { | ||
| NoSqlQueryAsDictSink() { this = any(NoSqlQuery noSqlQuery).getQuery() } | ||
| } | ||
|
|
||
| /** A JSON decoding converts a string to a dictionary. */ | ||
| class JsonDecoding extends Decoding, StringToDictConversion { | ||
| JsonDecoding() { this.getFormat() = "JSON" } | ||
|
|
||
| override DataFlow::Node getAnInput() { result = Decoding.super.getAnInput() } | ||
|
|
||
| override DataFlow::Node getOutput() { result = Decoding.super.getOutput() } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,60 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Provides a taint-tracking configuration for detecting NoSQL injection vulnerabilities | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import python | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import semmle.python.dataflow.new.DataFlow | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import semmle.python.dataflow.new.TaintTracking | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import semmle.python.Concepts | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private import NoSQLInjectionCustomizations::NoSqlInjection as C | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where did this
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is to communicate the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see. C for customizations?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that was the thinking. I guess it is a little too short to convey anything.. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * A taint-tracking configuration for detecting NoSQL injection vulnerabilities. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| module NoSQLInjectionConfig implements DataFlow::StateConfigSig { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
yoff marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class FlowState = C::FlowState; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| predicate isSource(DataFlow::Node source, FlowState state) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| source instanceof C::StringSource and | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| state instanceof C::StringInput | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| or | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| source instanceof C::DictSource and | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| state instanceof C::DictInput | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| predicate isSink(DataFlow::Node source, FlowState state) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| source instanceof C::StringSink and | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| state instanceof C::StringInput | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| or | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // since dictionaries can encode strings | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| state instanceof C::DictInput | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| or | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| source instanceof C::DictSink and | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| state instanceof C::DictInput | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| predicate isBarrier(DataFlow::Node node, FlowState state) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Block `StringInput` paths here, since they change state to `DictInput` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exists(C::StringToDictConversion c | node = c.getOutput()) and | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| state instanceof C::StringInput | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's a bit strange to block state StringInput, only to include DictInput for StringSinks in the sinks... could we remove the barrier instead?
Suggested change
Otherwise, we might consider renaming
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is a fair point. A reformulation of the modelling might indeed make things simpler..
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I changed the names of the flow states and the comments. I did not actually change the above code, in the end:
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| predicate isAdditionalFlowStep( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DataFlow::Node nodeFrom, FlowState stateFrom, DataFlow::Node nodeTo, FlowState stateTo | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exists(C::StringToDictConversion c | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nodeFrom = c.getAnInput() and | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nodeTo = c.getOutput() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) and | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| stateFrom instanceof C::StringInput and | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| stateTo instanceof C::DictInput | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| predicate isBarrier(DataFlow::Node node) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| node = any(NoSqlSanitizer noSqlSanitizer).getAnInput() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| module Flow = TaintTracking::GlobalWithState<NoSQLInjectionConfig>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
yoff marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.