Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
60dc1af
Python: prepare to promote NoSqlInjection
yoff Aug 15, 2023
55707d3
Python: Make things compile in their new location
yoff Aug 15, 2023
db04597
Python: rename file
yoff Aug 23, 2023
087961d
Python: Refactor to allow customizations
yoff Aug 23, 2023
19046ea
Python: more renames
yoff Aug 23, 2023
bf8bfd9
Python: Add inline query test
yoff Sep 7, 2023
114984b
Python: Added tests based on security analysis
yoff Aug 29, 2023
c0b3245
Python: Enrich the NoSql concept
yoff Aug 31, 2023
7edebbe
Python: Add QLDocs
yoff Sep 6, 2023
f253f97
Python: update test expectations
yoff Sep 6, 2023
970e881
Python: Follow naming convention
yoff Sep 7, 2023
b07d085
Python: make test PoC a proper package
yoff Sep 7, 2023
d91cd21
Python: rename file
yoff Sep 8, 2023
154a369
Python: Add test for function
yoff Sep 11, 2023
d9f63e1
Python: Split modelling of query operators
yoff Sep 11, 2023
a063d7d
Python: sinks -> decodings
yoff Sep 11, 2023
4614b1a
Python: add change note
yoff Sep 18, 2023
5611bda
Python: add test for `$accumulator`
yoff Sep 19, 2023
30c37ca
Python: model `§accumulator`
yoff Sep 19, 2023
7c085ec
Python: Add test for `map_reduce`
yoff Sep 20, 2023
4ec8b3f
Python: Model `map_reduce`
yoff Sep 20, 2023
12dab88
Python: rename concept
yoff Sep 20, 2023
8156fa9
Apply naming suggestions from code review
yoff Sep 28, 2023
37a4f35
Python: further rename
yoff Sep 28, 2023
3fb579e
Python: add test for type tracking
yoff Sep 28, 2023
d90630a
Python: fix query file
yoff Sep 28, 2023
c2b6383
Apply suggestions from code review
yoff Sep 28, 2023
9682c82
Python: rename file
yoff Sep 28, 2023
2a739b3
Python: rename module
yoff Sep 28, 2023
eb1be08
Python: split modelling
yoff Sep 28, 2023
2a7b593
Python: Fix QL alerts
yoff Sep 28, 2023
a8e0023
Python: forgot to list framework
yoff Sep 28, 2023
d5b64c5
Python: update test expectations
yoff Sep 28, 2023
3043633
Python: Some renaming of flow states
yoff Sep 28, 2023
2e028a4
Apply suggestions from code review
yoff Sep 29, 2023
74d6f37
Python: update meta query `TaintSinks`
yoff Sep 29, 2023
2d845e3
Python: nicer paths
yoff Sep 29, 2023
e170805
Python: fix QL alert
yoff Sep 29, 2023
f3a0161
Python: rename flow states
yoff Sep 29, 2023
9769668
Python: require dict sinks be dangerous.
yoff Sep 29, 2023
16e1a00
Python: NoSQLInjection -> NoSqlInjection
RasmusWL Sep 29, 2023
d7ad5a0
Python: List NoSQL injection sinks
RasmusWL Sep 29, 2023
3676262
Python: Clean trailing whitespace
RasmusWL Sep 29, 2023
d6d13f8
Python: -> NoSQL in QLDocs
RasmusWL Sep 29, 2023
9b73bbf
Python: Add keyword argument support
RasmusWL Sep 29, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Python: Refactor to allow customizations
Also use new DataFlow API
  • Loading branch information
yoff committed Sep 7, 2023
commit 087961d179fa9809bf379aff916644605654fe6b
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.RemoteFlowSources
import semmle.python.Concepts

module NoSqlInjection {
private newtype TFlowState =
TStringInput() or
TDictInput()

abstract class FlowState extends TFlowState {
abstract string toString();
}

class StringInput extends FlowState, TStringInput {
override string toString() { result = "StringInput" }
}

class DictInput extends FlowState, TDictInput {
override string toString() { result = "DictInput" }
}

abstract class StringSource extends DataFlow::Node { }

abstract class DictSource extends DataFlow::Node { }

abstract class StringSink extends DataFlow::Node { }

abstract class DictSink extends DataFlow::Node { }

abstract class StringToDictConversion extends DataFlow::Node {
abstract DataFlow::Node getAnInput();

abstract DataFlow::Node getOutput();
}

class RemoteFlowSourceAsStringSource extends RemoteFlowSource, StringSource { }

class NoSqlQueryAsDictSink extends DictSink {
NoSqlQueryAsDictSink() { this = any(NoSqlQuery noSqlQuery).getQuery() }
}

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
@@ -1,53 +1,48 @@
import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking
import semmle.python.dataflow.new.RemoteFlowSources
import semmle.python.Concepts
private import NoSQLInjectionCustomizations::NoSqlInjection as C
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where did this as C pattern come from? I have not seen it before, and would have thought we could do without it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is to communicate the FlowState class from the imported module C to the declared module NoSqlInjectionConfig. Otherwise, we should rename the FlowState class inside C, but that seems a bit wrong..

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. C for customizations?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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..


module NoSqlInjection {
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "NoSQLInjection" }
module Config implements DataFlow::StateConfigSig {
class FlowState = C::FlowState;

override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
source instanceof RemoteFlowSource and
state instanceof RemoteInput
}

override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
sink = any(NoSqlQuery noSqlQuery).getQuery() and
state instanceof ConvertedToDict
}

override predicate isSanitizer(DataFlow::Node node, DataFlow::FlowState state) {
// Block `RemoteInput` paths here, since they change state to `ConvertedToDict`
exists(Decoding decoding | decoding.getFormat() = "JSON" and node = decoding.getOutput()) and
state instanceof RemoteInput
}
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
}

override predicate isAdditionalTaintStep(
DataFlow::Node nodeFrom, DataFlow::FlowState stateFrom, DataFlow::Node nodeTo,
DataFlow::FlowState stateTo
) {
exists(Decoding decoding | decoding.getFormat() = "JSON" |
nodeFrom = decoding.getAnInput() and
nodeTo = decoding.getOutput()
) and
stateFrom instanceof RemoteInput and
stateTo instanceof ConvertedToDict
}
predicate isSink(DataFlow::Node source, FlowState state) {
source instanceof C::StringSink and
state instanceof C::StringInput
or
source instanceof C::DictSink and
state instanceof C::DictInput
}

override predicate isSanitizer(DataFlow::Node sanitizer) {
sanitizer = any(NoSqlSanitizer noSqlSanitizer).getAnInput()
}
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
}

/** A flow state signifying remote input. */
class RemoteInput extends DataFlow::FlowState {
RemoteInput() { this = "RemoteInput" }
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
}

/** A flow state signifying remote input converted to a dictionary. */
class ConvertedToDict extends DataFlow::FlowState {
ConvertedToDict() { this = "ConvertedToDict" }
predicate isBarrier(DataFlow::Node node) {
node = any(NoSqlSanitizer noSqlSanitizer).getAnInput()
}
}

module Flow = TaintTracking::GlobalWithState<Config>;
8 changes: 4 additions & 4 deletions python/ql/src/Security/CWE-943/NoSQLInjection.ql
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@

import python
import semmle.python.security.dataflow.NoSQLInjectionQuery
import DataFlow::PathGraph
import Flow::PathGraph

from NoSqlInjection::Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink, source, sink, "This NoSQL query contains an unsanitized $@.", source,
from Flow::PathNode source, Flow::PathNode sink
where Flow::flowPath(source, sink)
select sink.getNode(), source, sink, "This NoSQL query contains an unsanitized $@.", source,
"user-provided value"