diff --git a/python/ql/consistency-queries/DataFlowConsistency.ql b/python/ql/consistency-queries/DataFlowConsistency.ql index 829aa6debef2..e0ed207dc218 100644 --- a/python/ql/consistency-queries/DataFlowConsistency.ql +++ b/python/ql/consistency-queries/DataFlowConsistency.ql @@ -36,6 +36,8 @@ private module Input implements InputSig { // parameter, but dataflow-consistency queries should _not_ complain about there not // being a post-update node for the synthetic `**kwargs` parameter. n instanceof SynthDictSplatParameterNode + or + Private::Conversions::readStep(n, _, _) } predicate uniqueParameterNodePositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) { diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll index fffd0150008e..1c9ec5dff17b 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll @@ -753,7 +753,7 @@ predicate jumpStepNotSharedWithTypeTracker(Node nodeFrom, Node nodeTo) { * As of 2024-04-02 the type-tracking library only supports precise content, so there is * no reason to include steps for list content right now. */ -predicate storeStepCommon(Node nodeFrom, ContentSet c, Node nodeTo) { +predicate storeStepCommon(Node nodeFrom, Content c, Node nodeTo) { tupleStoreStep(nodeFrom, c, nodeTo) or dictStoreStep(nodeFrom, c, nodeTo) @@ -767,29 +767,31 @@ predicate storeStepCommon(Node nodeFrom, ContentSet c, Node nodeTo) { * Holds if data can flow from `nodeFrom` to `nodeTo` via an assignment to * content `c`. */ -predicate storeStep(Node nodeFrom, ContentSet c, Node nodeTo) { - storeStepCommon(nodeFrom, c, nodeTo) - or - listStoreStep(nodeFrom, c, nodeTo) - or - setStoreStep(nodeFrom, c, nodeTo) - or - attributeStoreStep(nodeFrom, c, nodeTo) - or - matchStoreStep(nodeFrom, c, nodeTo) - or - any(Orm::AdditionalOrmSteps es).storeStep(nodeFrom, c, nodeTo) +predicate storeStep(Node nodeFrom, ContentSet cs, Node nodeTo) { + exists(Content c | cs = singleton(c) | + storeStepCommon(nodeFrom, c, nodeTo) + or + listStoreStep(nodeFrom, c, nodeTo) + or + setStoreStep(nodeFrom, c, nodeTo) + or + attributeStoreStep(nodeFrom, c, nodeTo) + or + matchStoreStep(nodeFrom, c, nodeTo) + or + any(Orm::AdditionalOrmSteps es).storeStep(nodeFrom, c, nodeTo) + or + synthStarArgsElementParameterNodeStoreStep(nodeFrom, c, nodeTo) + or + synthDictSplatArgumentNodeStoreStep(nodeFrom, c, nodeTo) + or + yieldStoreStep(nodeFrom, c, nodeTo) + or + VariableCapture::storeStep(nodeFrom, c, nodeTo) + ) or - FlowSummaryImpl::Private::Steps::summaryStoreStep(nodeFrom.(FlowSummaryNode).getSummaryNode(), c, + FlowSummaryImpl::Private::Steps::summaryStoreStep(nodeFrom.(FlowSummaryNode).getSummaryNode(), cs, nodeTo.(FlowSummaryNode).getSummaryNode()) - or - synthStarArgsElementParameterNodeStoreStep(nodeFrom, c, nodeTo) - or - synthDictSplatArgumentNodeStoreStep(nodeFrom, c, nodeTo) - or - yieldStoreStep(nodeFrom, c, nodeTo) - or - VariableCapture::storeStep(nodeFrom, c, nodeTo) } /** @@ -985,7 +987,7 @@ predicate attributeStoreStep(Node nodeFrom, AttributeContent c, Node nodeTo) { /** * Subset of `readStep` that should be shared with type-tracking. */ -predicate readStepCommon(Node nodeFrom, ContentSet c, Node nodeTo) { +predicate readStepCommon(Node nodeFrom, Content c, Node nodeTo) { subscriptReadStep(nodeFrom, c, nodeTo) or iterableUnpackingReadStep(nodeFrom, c, nodeTo) @@ -994,21 +996,25 @@ predicate readStepCommon(Node nodeFrom, ContentSet c, Node nodeTo) { /** * Holds if data can flow from `nodeFrom` to `nodeTo` via a read of content `c`. */ -predicate readStep(Node nodeFrom, ContentSet c, Node nodeTo) { - readStepCommon(nodeFrom, c, nodeTo) - or - matchReadStep(nodeFrom, c, nodeTo) - or - forReadStep(nodeFrom, c, nodeTo) - or - attributeReadStep(nodeFrom, c, nodeTo) +predicate readStep(Node nodeFrom, ContentSet cs, Node nodeTo) { + exists(Content c | cs = singleton(c) | + readStepCommon(nodeFrom, c, nodeTo) + or + matchReadStep(nodeFrom, c, nodeTo) + or + forReadStep(nodeFrom, c, nodeTo) + or + attributeReadStep(nodeFrom, c, nodeTo) + or + synthDictSplatParameterNodeReadStep(nodeFrom, c, nodeTo) + or + VariableCapture::readStep(nodeFrom, c, nodeTo) + ) or - FlowSummaryImpl::Private::Steps::summaryReadStep(nodeFrom.(FlowSummaryNode).getSummaryNode(), c, + FlowSummaryImpl::Private::Steps::summaryReadStep(nodeFrom.(FlowSummaryNode).getSummaryNode(), cs, nodeTo.(FlowSummaryNode).getSummaryNode()) or - synthDictSplatParameterNodeReadStep(nodeFrom, c, nodeTo) - or - VariableCapture::readStep(nodeFrom, c, nodeTo) + Conversions::readStep(nodeFrom, cs, nodeTo) } /** Data flows from a sequence to a subscript of the sequence. */ @@ -1064,23 +1070,68 @@ predicate attributeReadStep(Node nodeFrom, AttributeContent c, AttrRead nodeTo) nodeTo.accesses(nodeFrom, c.getAttribute()) } +module Conversions { + private import semmle.python.Concepts + + predicate decoderReadStep(Node nodeFrom, ContentSet c, Node nodeTo) { + exists(Decoding decoding | + nodeFrom = decoding.getAnInput() and + nodeTo = decoding.getOutput() + ) and + (c.isAnyTupleElement() or c.isAnyDictionaryElement()) + } + + predicate encoderReadStep(Node nodeFrom, ContentSet c, Node nodeTo) { + exists(Encoding encoding | + nodeFrom = encoding.getAnInput() and + nodeTo = encoding.getOutput() + ) and + (c.isAnyTupleElement() or c.isAnyDictionaryElement()) + } + + predicate formatReadStep(Node nodeFrom, ContentSet c, Node nodeTo) { + // % formatting + exists(BinaryExprNode fmt | fmt = nodeTo.asCfgNode() | + fmt.getOp() instanceof Mod and + fmt.getRight() = nodeFrom.asCfgNode() + ) and + c.isAnyTupleElement() + or + // format_map + // see https://docs.python.org/3/library/stdtypes.html#str.format_map + nodeTo.(MethodCallNode).calls(_, "format_map") and + nodeTo.(MethodCallNode).getArg(0) = nodeFrom and + c.isAnyDictionaryElement() + } + + predicate readStep(Node nodeFrom, ContentSet c, Node nodeTo) { + decoderReadStep(nodeFrom, c, nodeTo) + or + encoderReadStep(nodeFrom, c, nodeTo) + or + formatReadStep(nodeFrom, c, nodeTo) + } +} + /** * Holds if values stored inside content `c` are cleared at node `n`. For example, * any value stored inside `f` is cleared at the pre-update node associated with `x` * in `x.f = newValue`. */ -predicate clearsContent(Node n, ContentSet c) { - matchClearStep(n, c) - or - attributeClearStep(n, c) - or - dictClearStep(n, c) - or - FlowSummaryImpl::Private::Steps::summaryClearsContent(n.(FlowSummaryNode).getSummaryNode(), c) - or - dictSplatParameterNodeClearStep(n, c) +predicate clearsContent(Node n, ContentSet cs) { + exists(Content c | cs = singleton(c) | + matchClearStep(n, c) + or + attributeClearStep(n, c) + or + dictClearStep(n, c) + or + dictSplatParameterNodeClearStep(n, c) + or + VariableCapture::clearsContent(n, c) + ) or - VariableCapture::clearsContent(n, c) + FlowSummaryImpl::Private::Steps::summaryClearsContent(n.(FlowSummaryNode).getSummaryNode(), cs) } /** diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll index 8612d4a253e0..f832b37c774e 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll @@ -758,6 +758,8 @@ newtype TContent = // data-flow-private) index in [0 .. 7] } or + /** An element of a tuple at any index. */ + TTupleElementAnyContent() or /** An element of a dictionary under a specific key. */ TDictionaryElementContent(string key) { // {"key": ...} @@ -870,6 +872,13 @@ class DictionaryElementAnyContent extends TDictionaryElementAnyContent, Content override string getMaDRepresentation() { result = "DictionaryElementAny" } } +/** An element of a tuple at any index. */ +class TupleElementAnyContent extends TTupleElementAnyContent, Content { + override string toString() { result = "Any tuple element" } + + override string getMaDRepresentation() { result = "TupleElementAny" } +} + /** An object attribute. */ class AttributeContent extends TAttributeContent, Content { private string attr; @@ -898,19 +907,65 @@ class CapturedVariableContent extends Content, TCapturedVariableContent { override string getMaDRepresentation() { none() } } +/** + * An entity that represents a set of `Content`s. + * + * Most `ContentSet`s are singletons (i.e. they consist of a single `Content`), + * but `AnyDictionaryElement` and `AnyTupleElement` act as wildcards on the + * read side: a read at such a `ContentSet` matches any specific dictionary + * key / tuple index store, as well as the "unknown-bucket" Content + * (`DictionaryElementAnyContent` / `TupleElementAnyContent`). + * + * Keeping these as wildcard `ContentSet`s (rather than enumerating one + * `ContentSet` per key/index) keeps the dataflow `readSetEx` relation small + * when implicit reads are used (e.g. at sinks via `defaultImplicitTaintRead`). + */ +private newtype TContentSet = + TSingletonContent(Content c) or + TAnyTupleElement() or + TAnyDictionaryElement() + /** * An entity that represents a set of `Content`s. * * The set may be interpreted differently depending on whether it is * stored into (`getAStoreContent`) or read from (`getAReadContent`). */ -class ContentSet instanceof Content { +class ContentSet extends TContentSet { + /** Holds if this content set is the singleton `{c}`. */ + predicate isSingleton(Content c) { this = TSingletonContent(c) } + + /** Holds if this content set is the wildcard for all tuple elements. */ + predicate isAnyTupleElement() { this = TAnyTupleElement() } + + /** Holds if this content set is the wildcard for all dictionary elements. */ + predicate isAnyDictionaryElement() { this = TAnyDictionaryElement() } + /** Gets a content that may be stored into when storing into this set. */ - Content getAStoreContent() { result = this } + Content getAStoreContent() { this = TSingletonContent(result) } /** Gets a content that may be read from when reading from this set. */ - Content getAReadContent() { result = this } + Content getAReadContent() { + this = TSingletonContent(result) + or + // Wildcard expansion: a read at "any tuple element" matches a store at any + // specific tuple index, as well as the unknown-index bucket. + this = TAnyTupleElement() and + (result instanceof TupleElementContent or result instanceof TupleElementAnyContent) + or + this = TAnyDictionaryElement() and + (result instanceof DictionaryElementContent or result instanceof DictionaryElementAnyContent) + } /** Gets a textual representation of this content set. */ - string toString() { result = super.toString() } + string toString() { + exists(Content c | this = TSingletonContent(c) | result = c.toString()) + or + this = TAnyTupleElement() and result = "Any tuple element" + or + this = TAnyDictionaryElement() and result = "Any dictionary element" + } } + +/** Gets the singleton `ContentSet` wrapping the `Content` `c`. */ +ContentSet singleton(Content c) { result = TSingletonContent(c) } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll b/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll index 41cb0368b507..59ef297b20f0 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll @@ -66,21 +66,29 @@ module Input implements InputSig } string encodeContent(ContentSet cs, string arg) { - cs = TListElementContent() and result = "ListElement" and arg = "" - or - cs = TSetElementContent() and result = "SetElement" and arg = "" - or - exists(int index | - cs = TTupleElementContent(index) and result = "TupleElement" and arg = index.toString() - ) - or - exists(string key | - cs = TDictionaryElementContent(key) and result = "DictionaryElement" and arg = key + exists(Content c | cs.isSingleton(c) | + c = TListElementContent() and result = "ListElement" and arg = "" + or + c = TSetElementContent() and result = "SetElement" and arg = "" + or + exists(int index | + c = TTupleElementContent(index) and result = "TupleElement" and arg = index.toString() + ) + or + c = TTupleElementAnyContent() and result = "TupleElementAny" and arg = "" + or + exists(string key | + c = TDictionaryElementContent(key) and result = "DictionaryElement" and arg = key + ) + or + c = TDictionaryElementAnyContent() and result = "DictionaryElementAny" and arg = "" + or + exists(string attr | c = TAttributeContent(attr) and result = "Attribute" and arg = attr) ) or - cs = TDictionaryElementAnyContent() and result = "DictionaryElementAny" and arg = "" + cs.isAnyTupleElement() and result = "AnyTupleElement" and arg = "" or - exists(string attr | cs = TAttributeContent(attr) and result = "Attribute" and arg = attr) + cs.isAnyDictionaryElement() and result = "AnyDictionaryElement" and arg = "" } bindingset[token] @@ -139,27 +147,34 @@ module Private { predicate withContent = SC::withContent/1; /** Gets a summary component that represents a list element. */ - SummaryComponent listElement() { result = content(any(ListElementContent c)) } + SummaryComponent listElement() { result = content(singleton(any(ListElementContent c))) } /** Gets a summary component that represents a set element. */ - SummaryComponent setElement() { result = content(any(SetElementContent c)) } + SummaryComponent setElement() { result = content(singleton(any(SetElementContent c))) } /** Gets a summary component that represents a tuple element. */ SummaryComponent tupleElement(int index) { - exists(TupleElementContent c | c.getIndex() = index and result = content(c)) + exists(TupleElementContent c | c.getIndex() = index and result = content(singleton(c))) + } + + /** Gets a summary component that represents a tuple element at any index. */ + SummaryComponent tupleElementAny() { + result = content(singleton(any(TupleElementAnyContent c))) } /** Gets a summary component that represents a dictionary element. */ SummaryComponent dictionaryElement(string key) { - exists(DictionaryElementContent c | c.getKey() = key and result = content(c)) + exists(DictionaryElementContent c | c.getKey() = key and result = content(singleton(c))) } /** Gets a summary component that represents a dictionary element at any key. */ - SummaryComponent dictionaryElementAny() { result = content(any(DictionaryElementAnyContent c)) } + SummaryComponent dictionaryElementAny() { + result = content(singleton(any(DictionaryElementAnyContent c))) + } /** Gets a summary component that represents an attribute element. */ SummaryComponent attribute(string attr) { - exists(AttributeContent c | c.getAttribute() = attr and result = content(c)) + exists(AttributeContent c | c.getAttribute() = attr and result = content(singleton(c))) } /** Gets a summary component that represents the return value of a call. */ diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/TaintTrackingPrivate.qll b/python/ql/lib/semmle/python/dataflow/new/internal/TaintTrackingPrivate.qll index 62f5a76309b4..2d2cceb73c1a 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/TaintTrackingPrivate.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/TaintTrackingPrivate.qll @@ -16,7 +16,17 @@ predicate defaultTaintSanitizer(DataFlow::Node node) { none() } * of `c` at sinks and inputs to additional taint steps. */ bindingset[node] -predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet c) { none() } +predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet c) { + // We allow implicit reads of precise content; imprecise content has already + // bubbled up. We use the wildcard content sets here rather than the + // per-key/per-index ones to avoid blowing up the size of `Stage1::readSetEx` + // (otherwise this predicate would expand to one row per (node, distinct key + // or index) and the framework's read-set relation grows quadratically). + // `ContentSet.getAReadContent` expands these wildcards back to the specific + // contents when matching against stores. + exists(node) and + (c.isAnyTupleElement() or c.isAnyDictionaryElement()) +} private module Cached { /** @@ -176,10 +186,6 @@ predicate containerStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { or DataFlowPrivate::setStoreStep(nodeFrom, _, nodeTo) or - DataFlowPrivate::tupleStoreStep(nodeFrom, _, nodeTo) - or - DataFlowPrivate::dictStoreStep(nodeFrom, _, nodeTo) - or // comprehension, so there is taint-flow from `x` in `[x for x in xs]` to the // resulting list of the list-comprehension. // diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackingImpl.qll b/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackingImpl.qll index 95434b05451d..215c7906e655 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackingImpl.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackingImpl.qll @@ -241,7 +241,7 @@ module TypeTrackingInput implements Shared::TypeTrackingInput { // is only fed set/list content) not nodeFrom instanceof DataFlowPublic::IterableElementNode or - TypeTrackerSummaryFlow::basicStoreStep(nodeFrom, nodeTo, content) + TypeTrackerSummaryFlow::basicStoreStep(nodeFrom, nodeTo, DataFlowPublic::singleton(content)) } /** @@ -272,14 +272,15 @@ module TypeTrackingInput implements Shared::TypeTrackingInput { nodeFrom.asCfgNode() instanceof SequenceNode ) or - TypeTrackerSummaryFlow::basicLoadStep(nodeFrom, nodeTo, content) + TypeTrackerSummaryFlow::basicLoadStep(nodeFrom, nodeTo, DataFlowPublic::singleton(content)) } /** * Holds if the `loadContent` of `nodeFrom` is stored in the `storeContent` of `nodeTo`. */ predicate loadStoreStep(Node nodeFrom, Node nodeTo, Content loadContent, Content storeContent) { - TypeTrackerSummaryFlow::basicLoadStoreStep(nodeFrom, nodeTo, loadContent, storeContent) + TypeTrackerSummaryFlow::basicLoadStoreStep(nodeFrom, nodeTo, + DataFlowPublic::singleton(loadContent), DataFlowPublic::singleton(storeContent)) } /** diff --git a/python/ql/lib/semmle/python/frameworks/Stdlib.qll b/python/ql/lib/semmle/python/frameworks/Stdlib.qll index 5d3b994880a1..1e0bd8461818 100644 --- a/python/ql/lib/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/lib/semmle/python/frameworks/Stdlib.qll @@ -4244,8 +4244,15 @@ module StdlibPrivate { ) // TODO: Once we have DictKeyContent, we need to transform that into ListElementContent ) and - output = "ReturnValue.ListElement" and - preservesValue = true + ( + // Element content is mutated into list element content + output = "ReturnValue.ListElement" and + preservesValue = true + or + // Since list content is imprecise, we also taint the list. + output = "ReturnValue" and + preservesValue = false + ) or input = "Argument[0]" and output = "ReturnValue" and diff --git a/python/ql/src/Variables/LoopVariableCapture/LoopVariableCaptureQuery.qll b/python/ql/src/Variables/LoopVariableCapture/LoopVariableCaptureQuery.qll index 987740236f24..ad40b5860ee7 100644 --- a/python/ql/src/Variables/LoopVariableCapture/LoopVariableCaptureQuery.qll +++ b/python/ql/src/Variables/LoopVariableCapture/LoopVariableCaptureQuery.qll @@ -60,11 +60,11 @@ module EscapingCaptureFlowConfig implements DataFlow::ConfigSig { predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet cs) { isSink(node) and - ( - cs.(DataFlow::TupleElementContent).getIndex() in [0 .. 10] or - cs instanceof DataFlow::ListElementContent or - cs instanceof DataFlow::SetElementContent or - cs instanceof DataFlow::DictionaryElementAnyContent + exists(DataFlow::Content c | cs.isSingleton(c) | + c.(DataFlow::TupleElementContent).getIndex() in [0 .. 10] or + c instanceof DataFlow::ListElementContent or + c instanceof DataFlow::SetElementContent or + c instanceof DataFlow::DictionaryElementAnyContent ) } } diff --git a/python/ql/test/2/query-tests/Variables/undefined/UndefinedGlobal.expected b/python/ql/test/2/query-tests/Variables/undefined/UndefinedGlobal.expected index e69de29bb2d1..8b137891791f 100644 --- a/python/ql/test/2/query-tests/Variables/undefined/UndefinedGlobal.expected +++ b/python/ql/test/2/query-tests/Variables/undefined/UndefinedGlobal.expected @@ -0,0 +1 @@ + diff --git a/python/ql/test/experimental/query-tests/Security/CWE-1427-PromptInjection/PromptInjection.expected b/python/ql/test/experimental/query-tests/Security/CWE-1427-PromptInjection/PromptInjection.expected index 6acb03ce7f51..fbf4ae3fd98c 100644 --- a/python/ql/test/experimental/query-tests/Security/CWE-1427-PromptInjection/PromptInjection.expected +++ b/python/ql/test/experimental/query-tests/Security/CWE-1427-PromptInjection/PromptInjection.expected @@ -13,7 +13,6 @@ | openai_test.py:17:22:17:46 | ControlFlowNode for BinaryExpr | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | openai_test.py:17:22:17:46 | ControlFlowNode for BinaryExpr | This prompt construction depends on a $@. | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | | openai_test.py:18:15:18:19 | ControlFlowNode for query | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | openai_test.py:18:15:18:19 | ControlFlowNode for query | This prompt construction depends on a $@. | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | | openai_test.py:22:22:22:46 | ControlFlowNode for BinaryExpr | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | openai_test.py:22:22:22:46 | ControlFlowNode for BinaryExpr | This prompt construction depends on a $@. | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | -| openai_test.py:23:15:37:9 | ControlFlowNode for List | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | openai_test.py:23:15:37:9 | ControlFlowNode for List | This prompt construction depends on a $@. | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | | openai_test.py:26:28:26:51 | ControlFlowNode for BinaryExpr | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | openai_test.py:26:28:26:51 | ControlFlowNode for BinaryExpr | This prompt construction depends on a $@. | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | | openai_test.py:33:33:33:37 | ControlFlowNode for query | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | openai_test.py:33:33:33:37 | ControlFlowNode for query | This prompt construction depends on a $@. | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | | openai_test.py:41:22:41:46 | ControlFlowNode for BinaryExpr | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | openai_test.py:41:22:41:46 | ControlFlowNode for BinaryExpr | This prompt construction depends on a $@. | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | @@ -61,7 +60,6 @@ edges | openai_test.py:2:26:2:32 | ControlFlowNode for request | openai_test.py:13:13:13:19 | ControlFlowNode for request | provenance | | | openai_test.py:12:5:12:11 | ControlFlowNode for persona | openai_test.py:17:22:17:46 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:10 | | openai_test.py:12:5:12:11 | ControlFlowNode for persona | openai_test.py:22:22:22:46 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:10 | -| openai_test.py:12:5:12:11 | ControlFlowNode for persona | openai_test.py:23:15:37:9 | ControlFlowNode for List | provenance | Sink:MaD:9 | | openai_test.py:12:5:12:11 | ControlFlowNode for persona | openai_test.py:26:28:26:51 | ControlFlowNode for BinaryExpr | provenance | | | openai_test.py:12:5:12:11 | ControlFlowNode for persona | openai_test.py:41:22:41:46 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:10 | | openai_test.py:12:5:12:11 | ControlFlowNode for persona | openai_test.py:63:28:63:51 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:8 | @@ -72,7 +70,6 @@ edges | openai_test.py:12:15:12:26 | ControlFlowNode for Attribute | openai_test.py:12:15:12:41 | ControlFlowNode for Attribute() | provenance | dict.get | | openai_test.py:12:15:12:41 | ControlFlowNode for Attribute() | openai_test.py:12:5:12:11 | ControlFlowNode for persona | provenance | | | openai_test.py:13:5:13:9 | ControlFlowNode for query | openai_test.py:18:15:18:19 | ControlFlowNode for query | provenance | Sink:MaD:9 | -| openai_test.py:13:5:13:9 | ControlFlowNode for query | openai_test.py:23:15:37:9 | ControlFlowNode for List | provenance | Sink:MaD:9 | | openai_test.py:13:5:13:9 | ControlFlowNode for query | openai_test.py:33:33:33:37 | ControlFlowNode for query | provenance | | | openai_test.py:13:5:13:9 | ControlFlowNode for query | openai_test.py:42:15:42:19 | ControlFlowNode for query | provenance | Sink:MaD:9 | | openai_test.py:13:5:13:9 | ControlFlowNode for query | openai_test.py:53:33:53:37 | ControlFlowNode for query | provenance | | @@ -139,7 +136,6 @@ nodes | openai_test.py:17:22:17:46 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | | openai_test.py:18:15:18:19 | ControlFlowNode for query | semmle.label | ControlFlowNode for query | | openai_test.py:22:22:22:46 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | -| openai_test.py:23:15:37:9 | ControlFlowNode for List | semmle.label | ControlFlowNode for List | | openai_test.py:26:28:26:51 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | | openai_test.py:33:33:33:37 | ControlFlowNode for query | semmle.label | ControlFlowNode for query | | openai_test.py:41:22:41:46 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | diff --git a/python/ql/test/experimental/query-tests/Security/CWE-1427-PromptInjection/openai_test.py b/python/ql/test/experimental/query-tests/Security/CWE-1427-PromptInjection/openai_test.py index 8ea014c62b49..2b25609670c5 100644 --- a/python/ql/test/experimental/query-tests/Security/CWE-1427-PromptInjection/openai_test.py +++ b/python/ql/test/experimental/query-tests/Security/CWE-1427-PromptInjection/openai_test.py @@ -34,7 +34,7 @@ async def get_input_openai(): } ] } - ] # $ Alert[py/prompt-injection] + ] ) response3 = await async_client.responses.create( diff --git a/python/ql/test/library-tests/dataflow/coverage/argumentRoutingTest.expected b/python/ql/test/library-tests/dataflow/coverage/argumentRoutingTest.expected index e69de29bb2d1..8b137891791f 100644 --- a/python/ql/test/library-tests/dataflow/coverage/argumentRoutingTest.expected +++ b/python/ql/test/library-tests/dataflow/coverage/argumentRoutingTest.expected @@ -0,0 +1 @@ + diff --git a/python/ql/test/library-tests/dataflow/sensitive-data/TestSensitiveDataSources.expected b/python/ql/test/library-tests/dataflow/sensitive-data/TestSensitiveDataSources.expected index e69de29bb2d1..8b137891791f 100644 --- a/python/ql/test/library-tests/dataflow/sensitive-data/TestSensitiveDataSources.expected +++ b/python/ql/test/library-tests/dataflow/sensitive-data/TestSensitiveDataSources.expected @@ -0,0 +1 @@ + diff --git a/python/ql/test/library-tests/dataflow/sensitive-data/test.py b/python/ql/test/library-tests/dataflow/sensitive-data/test.py index 77238a5e1dc1..d4a10511030b 100644 --- a/python/ql/test/library-tests/dataflow/sensitive-data/test.py +++ b/python/ql/test/library-tests/dataflow/sensitive-data/test.py @@ -131,6 +131,5 @@ def call_wrapper(func): print(password) # $ SensitiveUse=password _config = {"sleep_timer": 5, "mysql_password": password} -# since we have taint-step from store of `password`, we will consider any item in the -# dictionary to be a password :( -print(_config["sleep_timer"]) # $ SPURIOUS: SensitiveUse=password +# since we have precise dictionary content, other items of the config are not tainted +print(_config["sleep_timer"]) diff --git a/python/ql/test/library-tests/dataflow/tainttracking/defaultAdditionalTaintStep/test_collections.py b/python/ql/test/library-tests/dataflow/tainttracking/defaultAdditionalTaintStep/test_collections.py index 6c86d1c75d58..b9fa1ebffd44 100644 --- a/python/ql/test/library-tests/dataflow/tainttracking/defaultAdditionalTaintStep/test_collections.py +++ b/python/ql/test/library-tests/dataflow/tainttracking/defaultAdditionalTaintStep/test_collections.py @@ -32,7 +32,6 @@ def test_construction(): list(tainted_tuple), # $ tainted list(tainted_set), # $ tainted list(tainted_dict.values()), # $ tainted - list(tainted_dict.items()), # $ tainted tuple(tainted_list), # $ tainted set(tainted_list), # $ tainted @@ -44,7 +43,8 @@ def test_construction(): ) ensure_not_tainted( - dict(k = tainted_string)["k1"] + dict(k = tainted_string)["k1"], + list(tainted_dict.items()), ) diff --git a/python/ql/test/library-tests/dataflow/tainttracking/defaultAdditionalTaintStep/test_unpacking.py b/python/ql/test/library-tests/dataflow/tainttracking/defaultAdditionalTaintStep/test_unpacking.py index d8bfe71dbc44..2816e848470c 100644 --- a/python/ql/test/library-tests/dataflow/tainttracking/defaultAdditionalTaintStep/test_unpacking.py +++ b/python/ql/test/library-tests/dataflow/tainttracking/defaultAdditionalTaintStep/test_unpacking.py @@ -53,7 +53,7 @@ def contrived_1(): (a, b, c), (d, e, f) = tainted_list, no_taint_list ensure_tainted(a, b, c) # $ tainted - ensure_not_tainted(d, e, f) # $ SPURIOUS: tainted + ensure_not_tainted(d, e, f) def contrived_2(): diff --git a/python/ql/test/library-tests/frameworks/stdlib/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/stdlib/InlineTaintTest.expected index 020c338fd192..511df2367420 100644 --- a/python/ql/test/library-tests/frameworks/stdlib/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/stdlib/InlineTaintTest.expected @@ -1,3 +1,9 @@ argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing testFailures +| test_re.py:12:5:12:22 | re.search(..) | Unexpected result: tainted | +| test_re.py:13:5:13:21 | re.match(..) | Unexpected result: tainted | +| test_re.py:14:5:14:25 | re.fullmatch(..) | Unexpected result: tainted | +| test_re.py:16:5:16:27 | compiled_pat.search(..) | Unexpected result: tainted | +| test_re.py:17:5:17:26 | compiled_pat.match(..) | Unexpected result: tainted | +| test_re.py:18:5:18:30 | compiled_pat.fullmatch(..) | Unexpected result: tainted | diff --git a/python/ql/test/library-tests/frameworks/stdlib/test_re.py b/python/ql/test/library-tests/frameworks/stdlib/test_re.py index 4cfe5d972b7b..8ed6f620bfa4 100644 --- a/python/ql/test/library-tests/frameworks/stdlib/test_re.py +++ b/python/ql/test/library-tests/frameworks/stdlib/test_re.py @@ -80,9 +80,9 @@ ) ensure_not_tainted( - re.subn(pat, repl="safe", string=ts), re.subn(pat, repl="safe", string=ts)[1], # // the number of substitutions made ) ensure_tainted( + re.subn(pat, repl="safe", string=ts), # $ tainted // implicit read at sink re.subn(pat, repl="safe", string=ts)[0], # $ tainted // the string ) diff --git a/python/ql/test/library-tests/frameworks/tornado/taint_test.py b/python/ql/test/library-tests/frameworks/tornado/taint_test.py index 697a9e30af61..d6dac013fbc4 100644 --- a/python/ql/test/library-tests/frameworks/tornado/taint_test.py +++ b/python/ql/test/library-tests/frameworks/tornado/taint_test.py @@ -63,7 +63,8 @@ def get(self, name = "World!", number="0", foo="foo"): # $ requestHandler route request.headers["header-name"], # $ tainted request.headers.get_list("header-name"), # $ tainted request.headers.get_all(), # $ tainted - [(k, v) for (k, v) in request.headers.get_all()], # $ tainted + [(k, v) for (k, v) in request.headers.get_all()][0], # $ tainted + list([(k, v) for (k, v) in request.headers.get_all()])[0], # $ tainted # Dict[str, http.cookies.Morsel] request.cookies, # $ tainted @@ -73,6 +74,11 @@ def get(self, name = "World!", number="0", foo="foo"): # $ requestHandler route request.cookies["cookie-name"].coded_value, # $ tainted ) + ensure_not_tainted( + [(k, v) for (k, v) in request.headers.get_all()], # The comprehension is not tainted, only the elements + list([(k, v) for (k, v) in request.headers.get_all()]), # Here, all the elements of the list are tainted, but the list is not. + ) + def make_app(): return tornado.web.Application( diff --git a/python/ql/test/query-tests/Security/CVE-2018-1281/BindToAllInterfaces.expected b/python/ql/test/query-tests/Security/CVE-2018-1281/BindToAllInterfaces.expected index 0b96b2df6508..c478fe78fd77 100644 --- a/python/ql/test/query-tests/Security/CVE-2018-1281/BindToAllInterfaces.expected +++ b/python/ql/test/query-tests/Security/CVE-2018-1281/BindToAllInterfaces.expected @@ -11,10 +11,13 @@ edges | BindToAllInterfaces_test.py:5:9:5:17 | ControlFlowNode for StringLiteral | BindToAllInterfaces_test.py:5:9:5:24 | ControlFlowNode for Tuple | provenance | Sink:MaD:63 | | BindToAllInterfaces_test.py:9:9:9:10 | ControlFlowNode for StringLiteral | BindToAllInterfaces_test.py:9:9:9:16 | ControlFlowNode for Tuple | provenance | Sink:MaD:63 | -| BindToAllInterfaces_test.py:16:1:16:10 | ControlFlowNode for ALL_LOCALS | BindToAllInterfaces_test.py:17:9:17:24 | ControlFlowNode for Tuple | provenance | Sink:MaD:63 | -| BindToAllInterfaces_test.py:16:1:16:10 | ControlFlowNode for ALL_LOCALS | BindToAllInterfaces_test.py:20:1:20:3 | ControlFlowNode for tup | provenance | | +| BindToAllInterfaces_test.py:16:1:16:10 | ControlFlowNode for ALL_LOCALS | BindToAllInterfaces_test.py:17:9:17:18 | ControlFlowNode for ALL_LOCALS | provenance | | +| BindToAllInterfaces_test.py:16:1:16:10 | ControlFlowNode for ALL_LOCALS | BindToAllInterfaces_test.py:20:8:20:17 | ControlFlowNode for ALL_LOCALS | provenance | | | BindToAllInterfaces_test.py:16:14:16:22 | ControlFlowNode for StringLiteral | BindToAllInterfaces_test.py:16:1:16:10 | ControlFlowNode for ALL_LOCALS | provenance | | -| BindToAllInterfaces_test.py:20:1:20:3 | ControlFlowNode for tup | BindToAllInterfaces_test.py:21:8:21:10 | ControlFlowNode for tup | provenance | Sink:MaD:63 | +| BindToAllInterfaces_test.py:17:9:17:18 | ControlFlowNode for ALL_LOCALS | BindToAllInterfaces_test.py:17:9:17:24 | ControlFlowNode for Tuple | provenance | Sink:MaD:63 | +| BindToAllInterfaces_test.py:20:1:20:3 | ControlFlowNode for tup [Tuple element at index 0] | BindToAllInterfaces_test.py:21:8:21:10 | ControlFlowNode for tup | provenance | Sink:MaD:63 | +| BindToAllInterfaces_test.py:20:8:20:17 | ControlFlowNode for ALL_LOCALS | BindToAllInterfaces_test.py:20:8:20:23 | ControlFlowNode for Tuple [Tuple element at index 0] | provenance | | +| BindToAllInterfaces_test.py:20:8:20:23 | ControlFlowNode for Tuple [Tuple element at index 0] | BindToAllInterfaces_test.py:20:1:20:3 | ControlFlowNode for tup [Tuple element at index 0] | provenance | | | BindToAllInterfaces_test.py:26:9:26:12 | ControlFlowNode for StringLiteral | BindToAllInterfaces_test.py:26:9:26:18 | ControlFlowNode for Tuple | provenance | Sink:MaD:63 | | BindToAllInterfaces_test.py:33:18:33:21 | ControlFlowNode for self [Return] [Attribute bind_addr] | BindToAllInterfaces_test.py:41:10:41:17 | ControlFlowNode for Server() [Attribute bind_addr] | provenance | | | BindToAllInterfaces_test.py:34:9:34:12 | [post] ControlFlowNode for self [Attribute bind_addr] | BindToAllInterfaces_test.py:33:18:33:21 | ControlFlowNode for self [Return] [Attribute bind_addr] | provenance | | @@ -25,9 +28,10 @@ edges | BindToAllInterfaces_test.py:41:1:41:6 | ControlFlowNode for server [Attribute bind_addr] | BindToAllInterfaces_test.py:42:1:42:6 | ControlFlowNode for server [Attribute bind_addr] | provenance | | | BindToAllInterfaces_test.py:41:10:41:17 | ControlFlowNode for Server() [Attribute bind_addr] | BindToAllInterfaces_test.py:41:1:41:6 | ControlFlowNode for server [Attribute bind_addr] | provenance | | | BindToAllInterfaces_test.py:42:1:42:6 | ControlFlowNode for server [Attribute bind_addr] | BindToAllInterfaces_test.py:37:15:37:18 | ControlFlowNode for self [Attribute bind_addr] | provenance | | -| BindToAllInterfaces_test.py:46:1:46:4 | ControlFlowNode for host | BindToAllInterfaces_test.py:48:9:48:18 | ControlFlowNode for Tuple | provenance | Sink:MaD:63 | +| BindToAllInterfaces_test.py:46:1:46:4 | ControlFlowNode for host | BindToAllInterfaces_test.py:48:9:48:12 | ControlFlowNode for host | provenance | | | BindToAllInterfaces_test.py:46:8:46:44 | ControlFlowNode for Attribute() | BindToAllInterfaces_test.py:46:1:46:4 | ControlFlowNode for host | provenance | | | BindToAllInterfaces_test.py:46:35:46:43 | ControlFlowNode for StringLiteral | BindToAllInterfaces_test.py:46:8:46:44 | ControlFlowNode for Attribute() | provenance | dict.get | +| BindToAllInterfaces_test.py:48:9:48:12 | ControlFlowNode for host | BindToAllInterfaces_test.py:48:9:48:18 | ControlFlowNode for Tuple | provenance | Sink:MaD:63 | | BindToAllInterfaces_test.py:53:10:53:18 | ControlFlowNode for StringLiteral | BindToAllInterfaces_test.py:53:10:53:25 | ControlFlowNode for Tuple | provenance | Sink:MaD:63 | | BindToAllInterfaces_test.py:58:10:58:18 | ControlFlowNode for StringLiteral | BindToAllInterfaces_test.py:58:10:58:25 | ControlFlowNode for Tuple | provenance | Sink:MaD:63 | nodes @@ -37,8 +41,11 @@ nodes | BindToAllInterfaces_test.py:9:9:9:16 | ControlFlowNode for Tuple | semmle.label | ControlFlowNode for Tuple | | BindToAllInterfaces_test.py:16:1:16:10 | ControlFlowNode for ALL_LOCALS | semmle.label | ControlFlowNode for ALL_LOCALS | | BindToAllInterfaces_test.py:16:14:16:22 | ControlFlowNode for StringLiteral | semmle.label | ControlFlowNode for StringLiteral | +| BindToAllInterfaces_test.py:17:9:17:18 | ControlFlowNode for ALL_LOCALS | semmle.label | ControlFlowNode for ALL_LOCALS | | BindToAllInterfaces_test.py:17:9:17:24 | ControlFlowNode for Tuple | semmle.label | ControlFlowNode for Tuple | -| BindToAllInterfaces_test.py:20:1:20:3 | ControlFlowNode for tup | semmle.label | ControlFlowNode for tup | +| BindToAllInterfaces_test.py:20:1:20:3 | ControlFlowNode for tup [Tuple element at index 0] | semmle.label | ControlFlowNode for tup [Tuple element at index 0] | +| BindToAllInterfaces_test.py:20:8:20:17 | ControlFlowNode for ALL_LOCALS | semmle.label | ControlFlowNode for ALL_LOCALS | +| BindToAllInterfaces_test.py:20:8:20:23 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | | BindToAllInterfaces_test.py:21:8:21:10 | ControlFlowNode for tup | semmle.label | ControlFlowNode for tup | | BindToAllInterfaces_test.py:26:9:26:12 | ControlFlowNode for StringLiteral | semmle.label | ControlFlowNode for StringLiteral | | BindToAllInterfaces_test.py:26:9:26:18 | ControlFlowNode for Tuple | semmle.label | ControlFlowNode for Tuple | @@ -55,6 +62,7 @@ nodes | BindToAllInterfaces_test.py:46:1:46:4 | ControlFlowNode for host | semmle.label | ControlFlowNode for host | | BindToAllInterfaces_test.py:46:8:46:44 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | BindToAllInterfaces_test.py:46:35:46:43 | ControlFlowNode for StringLiteral | semmle.label | ControlFlowNode for StringLiteral | +| BindToAllInterfaces_test.py:48:9:48:12 | ControlFlowNode for host | semmle.label | ControlFlowNode for host | | BindToAllInterfaces_test.py:48:9:48:18 | ControlFlowNode for Tuple | semmle.label | ControlFlowNode for Tuple | | BindToAllInterfaces_test.py:53:10:53:18 | ControlFlowNode for StringLiteral | semmle.label | ControlFlowNode for StringLiteral | | BindToAllInterfaces_test.py:53:10:53:25 | ControlFlowNode for Tuple | semmle.label | ControlFlowNode for Tuple | diff --git a/python/ql/test/query-tests/Security/CWE-209-StackTraceExposure/StackTraceExposure.expected b/python/ql/test/query-tests/Security/CWE-209-StackTraceExposure/StackTraceExposure.expected index e0321cab12eb..b24fd261ea88 100644 --- a/python/ql/test/query-tests/Security/CWE-209-StackTraceExposure/StackTraceExposure.expected +++ b/python/ql/test/query-tests/Security/CWE-209-StackTraceExposure/StackTraceExposure.expected @@ -7,7 +7,8 @@ edges | test.py:50:29:50:31 | ControlFlowNode for err | test.py:50:16:50:32 | ControlFlowNode for format_error() | provenance | | | test.py:50:29:50:31 | ControlFlowNode for err | test.py:52:18:52:20 | ControlFlowNode for msg | provenance | | | test.py:52:18:52:20 | ControlFlowNode for msg | test.py:53:12:53:27 | ControlFlowNode for BinaryExpr | provenance | | -| test.py:65:25:65:25 | ControlFlowNode for e | test.py:66:24:66:40 | ControlFlowNode for Dict | provenance | | +| test.py:65:25:65:25 | ControlFlowNode for e | test.py:66:34:66:39 | ControlFlowNode for str() | provenance | | +| test.py:66:34:66:39 | ControlFlowNode for str() | test.py:66:24:66:40 | ControlFlowNode for Dict | provenance | | nodes | test.py:16:16:16:37 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | test.py:23:25:23:25 | ControlFlowNode for e | semmle.label | ControlFlowNode for e | @@ -23,6 +24,7 @@ nodes | test.py:53:12:53:27 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | | test.py:65:25:65:25 | ControlFlowNode for e | semmle.label | ControlFlowNode for e | | test.py:66:24:66:40 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | +| test.py:66:34:66:39 | ControlFlowNode for str() | semmle.label | ControlFlowNode for str() | subpaths | test.py:50:29:50:31 | ControlFlowNode for err | test.py:52:18:52:20 | ControlFlowNode for msg | test.py:53:12:53:27 | ControlFlowNode for BinaryExpr | test.py:50:16:50:32 | ControlFlowNode for format_error() | #select diff --git a/python/ql/test/query-tests/Security/CWE-312-CleartextLogging/CleartextLogging.expected b/python/ql/test/query-tests/Security/CWE-312-CleartextLogging/CleartextLogging.expected index 7cb9e0151907..5da1b60eee12 100644 --- a/python/ql/test/query-tests/Security/CWE-312-CleartextLogging/CleartextLogging.expected +++ b/python/ql/test/query-tests/Security/CWE-312-CleartextLogging/CleartextLogging.expected @@ -22,8 +22,6 @@ edges | test.py:67:38:67:48 | ControlFlowNode for bank_number | test.py:70:15:70:25 | ControlFlowNode for bank_number | provenance | | | test.py:67:76:67:78 | ControlFlowNode for ccn | test.py:73:15:73:17 | ControlFlowNode for ccn | provenance | | | test.py:67:81:67:88 | ControlFlowNode for user_ccn | test.py:74:15:74:22 | ControlFlowNode for user_ccn | provenance | | -| test.py:101:5:101:10 | ControlFlowNode for config | test.py:105:11:105:31 | ControlFlowNode for Subscript | provenance | | -| test.py:103:21:103:37 | ControlFlowNode for Attribute | test.py:101:5:101:10 | ControlFlowNode for config | provenance | | nodes | test.py:19:5:19:12 | ControlFlowNode for password | semmle.label | ControlFlowNode for password | | test.py:19:16:19:29 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() | @@ -68,9 +66,6 @@ nodes | test.py:70:15:70:25 | ControlFlowNode for bank_number | semmle.label | ControlFlowNode for bank_number | | test.py:73:15:73:17 | ControlFlowNode for ccn | semmle.label | ControlFlowNode for ccn | | test.py:74:15:74:22 | ControlFlowNode for user_ccn | semmle.label | ControlFlowNode for user_ccn | -| test.py:101:5:101:10 | ControlFlowNode for config | semmle.label | ControlFlowNode for config | -| test.py:103:21:103:37 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| test.py:105:11:105:31 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | subpaths #select | test.py:20:48:20:55 | ControlFlowNode for password | test.py:19:16:19:29 | ControlFlowNode for get_password() | test.py:20:48:20:55 | ControlFlowNode for password | This expression logs $@ as clear text. | test.py:19:16:19:29 | ControlFlowNode for get_password() | sensitive data (password) | @@ -97,4 +92,3 @@ subpaths | test.py:70:15:70:25 | ControlFlowNode for bank_number | test.py:67:38:67:48 | ControlFlowNode for bank_number | test.py:70:15:70:25 | ControlFlowNode for bank_number | This expression logs $@ as clear text. | test.py:67:38:67:48 | ControlFlowNode for bank_number | sensitive data (private) | | test.py:73:15:73:17 | ControlFlowNode for ccn | test.py:67:76:67:78 | ControlFlowNode for ccn | test.py:73:15:73:17 | ControlFlowNode for ccn | This expression logs $@ as clear text. | test.py:67:76:67:78 | ControlFlowNode for ccn | sensitive data (private) | | test.py:74:15:74:22 | ControlFlowNode for user_ccn | test.py:67:81:67:88 | ControlFlowNode for user_ccn | test.py:74:15:74:22 | ControlFlowNode for user_ccn | This expression logs $@ as clear text. | test.py:67:81:67:88 | ControlFlowNode for user_ccn | sensitive data (private) | -| test.py:105:11:105:31 | ControlFlowNode for Subscript | test.py:103:21:103:37 | ControlFlowNode for Attribute | test.py:105:11:105:31 | ControlFlowNode for Subscript | This expression logs $@ as clear text. | test.py:103:21:103:37 | ControlFlowNode for Attribute | sensitive data (password) | diff --git a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/PartialServerSideRequestForgery.expected b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/PartialServerSideRequestForgery.expected index 0b8756071573..a8d907793129 100644 --- a/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/PartialServerSideRequestForgery.expected +++ b/python/ql/test/query-tests/Security/CWE-918-ServerSideRequestForgery/PartialServerSideRequestForgery.expected @@ -82,14 +82,19 @@ edges | full_partial_test.py:61:5:61:7 | ControlFlowNode for url | full_partial_test.py:63:18:63:20 | ControlFlowNode for url | provenance | | | full_partial_test.py:66:5:66:14 | ControlFlowNode for user_input | full_partial_test.py:70:5:70:7 | ControlFlowNode for url | provenance | | | full_partial_test.py:66:5:66:14 | ControlFlowNode for user_input | full_partial_test.py:74:5:74:7 | ControlFlowNode for url | provenance | | -| full_partial_test.py:66:5:66:14 | ControlFlowNode for user_input | full_partial_test.py:78:5:78:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:66:5:66:14 | ControlFlowNode for user_input | full_partial_test.py:78:38:78:47 | ControlFlowNode for user_input | provenance | | | full_partial_test.py:66:18:66:24 | ControlFlowNode for request | full_partial_test.py:66:5:66:14 | ControlFlowNode for user_input | provenance | AdditionalTaintStep | | full_partial_test.py:66:18:66:24 | ControlFlowNode for request | full_partial_test.py:67:5:67:13 | ControlFlowNode for query_val | provenance | AdditionalTaintStep | -| full_partial_test.py:67:5:67:13 | ControlFlowNode for query_val | full_partial_test.py:78:5:78:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:67:5:67:13 | ControlFlowNode for query_val | full_partial_test.py:78:50:78:58 | ControlFlowNode for query_val | provenance | | | full_partial_test.py:67:17:67:23 | ControlFlowNode for request | full_partial_test.py:67:5:67:13 | ControlFlowNode for query_val | provenance | AdditionalTaintStep | | full_partial_test.py:70:5:70:7 | ControlFlowNode for url | full_partial_test.py:72:18:72:20 | ControlFlowNode for url | provenance | | | full_partial_test.py:74:5:74:7 | ControlFlowNode for url | full_partial_test.py:76:18:76:20 | ControlFlowNode for url | provenance | | | full_partial_test.py:78:5:78:7 | ControlFlowNode for url | full_partial_test.py:80:18:80:20 | ControlFlowNode for url | provenance | | +| full_partial_test.py:78:11:78:59 | ControlFlowNode for BinaryExpr | full_partial_test.py:78:5:78:7 | ControlFlowNode for url | provenance | | +| full_partial_test.py:78:38:78:47 | ControlFlowNode for user_input | full_partial_test.py:78:38:78:58 | ControlFlowNode for Tuple [Tuple element at index 0] | provenance | | +| full_partial_test.py:78:38:78:58 | ControlFlowNode for Tuple [Tuple element at index 0] | full_partial_test.py:78:11:78:59 | ControlFlowNode for BinaryExpr | provenance | | +| full_partial_test.py:78:38:78:58 | ControlFlowNode for Tuple [Tuple element at index 1] | full_partial_test.py:78:11:78:59 | ControlFlowNode for BinaryExpr | provenance | | +| full_partial_test.py:78:50:78:58 | ControlFlowNode for query_val | full_partial_test.py:78:38:78:58 | ControlFlowNode for Tuple [Tuple element at index 1] | provenance | | | full_partial_test.py:83:5:83:14 | ControlFlowNode for user_input | full_partial_test.py:87:5:87:7 | ControlFlowNode for url | provenance | | | full_partial_test.py:83:5:83:14 | ControlFlowNode for user_input | full_partial_test.py:91:5:91:7 | ControlFlowNode for url | provenance | | | full_partial_test.py:83:5:83:14 | ControlFlowNode for user_input | full_partial_test.py:95:5:95:7 | ControlFlowNode for url | provenance | | @@ -274,6 +279,11 @@ nodes | full_partial_test.py:74:5:74:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | | full_partial_test.py:76:18:76:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | | full_partial_test.py:78:5:78:7 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | +| full_partial_test.py:78:11:78:59 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| full_partial_test.py:78:38:78:47 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | +| full_partial_test.py:78:38:78:58 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| full_partial_test.py:78:38:78:58 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | +| full_partial_test.py:78:50:78:58 | ControlFlowNode for query_val | semmle.label | ControlFlowNode for query_val | | full_partial_test.py:80:18:80:20 | ControlFlowNode for url | semmle.label | ControlFlowNode for url | | full_partial_test.py:83:5:83:14 | ControlFlowNode for user_input | semmle.label | ControlFlowNode for user_input | | full_partial_test.py:83:18:83:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | diff --git a/python/ql/test/query-tests/Security/CWE-943-NoSqlInjection/NoSqlInjection.expected b/python/ql/test/query-tests/Security/CWE-943-NoSqlInjection/NoSqlInjection.expected index 810ece4f1074..2ff9a0d10b72 100644 --- a/python/ql/test/query-tests/Security/CWE-943-NoSqlInjection/NoSqlInjection.expected +++ b/python/ql/test/query-tests/Security/CWE-943-NoSqlInjection/NoSqlInjection.expected @@ -7,25 +7,34 @@ edges | PoC/server.py:1:26:1:32 | ControlFlowNode for request | PoC/server.py:98:14:98:20 | ControlFlowNode for request | provenance | | | PoC/server.py:26:5:26:17 | ControlFlowNode for author_string | PoC/server.py:27:25:27:37 | ControlFlowNode for author_string | provenance | | | PoC/server.py:26:21:26:27 | ControlFlowNode for request | PoC/server.py:26:5:26:17 | ControlFlowNode for author_string | provenance | AdditionalTaintStep | -| PoC/server.py:27:5:27:10 | ControlFlowNode for author | PoC/server.py:30:27:30:44 | ControlFlowNode for Dict | provenance | | -| PoC/server.py:27:5:27:10 | ControlFlowNode for author | PoC/server.py:31:34:31:51 | ControlFlowNode for Dict | provenance | | +| PoC/server.py:27:5:27:10 | ControlFlowNode for author | PoC/server.py:30:38:30:43 | ControlFlowNode for author | provenance | | +| PoC/server.py:27:5:27:10 | ControlFlowNode for author | PoC/server.py:31:45:31:50 | ControlFlowNode for author | provenance | | | PoC/server.py:27:14:27:38 | ControlFlowNode for Attribute() | PoC/server.py:27:5:27:10 | ControlFlowNode for author | provenance | | | PoC/server.py:27:25:27:37 | ControlFlowNode for author_string | PoC/server.py:27:14:27:38 | ControlFlowNode for Attribute() | provenance | Config | +| PoC/server.py:30:38:30:43 | ControlFlowNode for author | PoC/server.py:30:27:30:44 | ControlFlowNode for Dict | provenance | | +| PoC/server.py:31:45:31:50 | ControlFlowNode for author | PoC/server.py:31:34:31:51 | ControlFlowNode for Dict | provenance | | | PoC/server.py:43:5:43:10 | ControlFlowNode for author | PoC/server.py:47:38:47:67 | ControlFlowNode for BinaryExpr | provenance | | | PoC/server.py:43:14:43:20 | ControlFlowNode for request | PoC/server.py:43:5:43:10 | ControlFlowNode for author | provenance | AdditionalTaintStep | | PoC/server.py:47:38:47:67 | ControlFlowNode for BinaryExpr | PoC/server.py:47:27:47:68 | ControlFlowNode for Dict | provenance | Config | | PoC/server.py:52:5:52:10 | ControlFlowNode for author | PoC/server.py:54:17:54:70 | ControlFlowNode for BinaryExpr | provenance | | | PoC/server.py:52:14:52:20 | ControlFlowNode for request | PoC/server.py:52:5:52:10 | ControlFlowNode for author | provenance | AdditionalTaintStep | -| PoC/server.py:53:5:53:10 | ControlFlowNode for search | PoC/server.py:61:27:61:58 | ControlFlowNode for Dict | provenance | | +| PoC/server.py:53:5:53:10 | ControlFlowNode for search | PoC/server.py:61:51:61:56 | ControlFlowNode for search | provenance | | | PoC/server.py:53:14:57:5 | ControlFlowNode for Dict | PoC/server.py:53:5:53:10 | ControlFlowNode for search | provenance | | | PoC/server.py:54:17:54:70 | ControlFlowNode for BinaryExpr | PoC/server.py:53:14:57:5 | ControlFlowNode for Dict | provenance | Config | +| PoC/server.py:61:37:61:57 | ControlFlowNode for Dict [Dictionary element at key $function] | PoC/server.py:61:27:61:58 | ControlFlowNode for Dict | provenance | | +| PoC/server.py:61:51:61:56 | ControlFlowNode for search | PoC/server.py:61:37:61:57 | ControlFlowNode for Dict [Dictionary element at key $function] | provenance | | | PoC/server.py:77:5:77:10 | ControlFlowNode for author | PoC/server.py:80:23:80:101 | ControlFlowNode for BinaryExpr | provenance | | | PoC/server.py:77:14:77:20 | ControlFlowNode for request | PoC/server.py:77:5:77:10 | ControlFlowNode for author | provenance | AdditionalTaintStep | -| PoC/server.py:78:5:78:15 | ControlFlowNode for accumulator | PoC/server.py:84:5:84:9 | ControlFlowNode for group | provenance | | +| PoC/server.py:78:5:78:15 | ControlFlowNode for accumulator | PoC/server.py:86:37:86:47 | ControlFlowNode for accumulator | provenance | | | PoC/server.py:78:19:83:5 | ControlFlowNode for Dict | PoC/server.py:78:5:78:15 | ControlFlowNode for accumulator | provenance | | | PoC/server.py:80:23:80:101 | ControlFlowNode for BinaryExpr | PoC/server.py:78:19:83:5 | ControlFlowNode for Dict | provenance | Config | -| PoC/server.py:84:5:84:9 | ControlFlowNode for group | PoC/server.py:91:29:91:47 | ControlFlowNode for Dict | provenance | | -| PoC/server.py:84:5:84:9 | ControlFlowNode for group | PoC/server.py:92:38:92:56 | ControlFlowNode for Dict | provenance | | +| PoC/server.py:84:5:84:9 | ControlFlowNode for group [Dictionary element at key author, Dictionary element at key $accumulator] | PoC/server.py:91:41:91:45 | ControlFlowNode for group [Dictionary element at key author, Dictionary element at key $accumulator] | provenance | | +| PoC/server.py:84:5:84:9 | ControlFlowNode for group [Dictionary element at key author, Dictionary element at key $accumulator] | PoC/server.py:92:50:92:54 | ControlFlowNode for group [Dictionary element at key author, Dictionary element at key $accumulator] | provenance | | +| PoC/server.py:84:13:87:5 | ControlFlowNode for Dict [Dictionary element at key author, Dictionary element at key $accumulator] | PoC/server.py:84:5:84:9 | ControlFlowNode for group [Dictionary element at key author, Dictionary element at key $accumulator] | provenance | | +| PoC/server.py:86:19:86:49 | ControlFlowNode for Dict [Dictionary element at key $accumulator] | PoC/server.py:84:13:87:5 | ControlFlowNode for Dict [Dictionary element at key author, Dictionary element at key $accumulator] | provenance | | +| PoC/server.py:86:37:86:47 | ControlFlowNode for accumulator | PoC/server.py:86:19:86:49 | ControlFlowNode for Dict [Dictionary element at key $accumulator] | provenance | | +| PoC/server.py:91:41:91:45 | ControlFlowNode for group [Dictionary element at key author, Dictionary element at key $accumulator] | PoC/server.py:91:29:91:47 | ControlFlowNode for Dict | provenance | | +| PoC/server.py:92:50:92:54 | ControlFlowNode for group [Dictionary element at key author, Dictionary element at key $accumulator] | PoC/server.py:92:38:92:56 | ControlFlowNode for Dict | provenance | | | PoC/server.py:98:5:98:10 | ControlFlowNode for author | PoC/server.py:99:5:99:10 | ControlFlowNode for mapper | provenance | | | PoC/server.py:98:14:98:20 | ControlFlowNode for request | PoC/server.py:98:5:98:10 | ControlFlowNode for author | provenance | AdditionalTaintStep | | PoC/server.py:99:5:99:10 | ControlFlowNode for mapper | PoC/server.py:102:9:102:14 | ControlFlowNode for mapper | provenance | | @@ -39,16 +48,18 @@ edges | flask_mongoengine_bad.py:20:30:20:42 | ControlFlowNode for unsafe_search | flask_mongoengine_bad.py:20:19:20:43 | ControlFlowNode for Attribute() | provenance | Config | | flask_mongoengine_bad.py:26:5:26:17 | ControlFlowNode for unsafe_search | flask_mongoengine_bad.py:27:30:27:42 | ControlFlowNode for unsafe_search | provenance | | | flask_mongoengine_bad.py:26:21:26:27 | ControlFlowNode for request | flask_mongoengine_bad.py:26:5:26:17 | ControlFlowNode for unsafe_search | provenance | AdditionalTaintStep | -| flask_mongoengine_bad.py:27:5:27:15 | ControlFlowNode for json_search | flask_mongoengine_bad.py:30:39:30:59 | ControlFlowNode for Dict | provenance | | +| flask_mongoengine_bad.py:27:5:27:15 | ControlFlowNode for json_search | flask_mongoengine_bad.py:30:48:30:58 | ControlFlowNode for json_search | provenance | | | flask_mongoengine_bad.py:27:19:27:43 | ControlFlowNode for Attribute() | flask_mongoengine_bad.py:27:5:27:15 | ControlFlowNode for json_search | provenance | | | flask_mongoengine_bad.py:27:30:27:42 | ControlFlowNode for unsafe_search | flask_mongoengine_bad.py:27:19:27:43 | ControlFlowNode for Attribute() | provenance | Config | +| flask_mongoengine_bad.py:30:48:30:58 | ControlFlowNode for json_search | flask_mongoengine_bad.py:30:39:30:59 | ControlFlowNode for Dict | provenance | | | flask_pymongo_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | flask_pymongo_bad.py:1:26:1:32 | ControlFlowNode for request | provenance | | | flask_pymongo_bad.py:1:26:1:32 | ControlFlowNode for request | flask_pymongo_bad.py:11:21:11:27 | ControlFlowNode for request | provenance | | | flask_pymongo_bad.py:11:5:11:17 | ControlFlowNode for unsafe_search | flask_pymongo_bad.py:12:30:12:42 | ControlFlowNode for unsafe_search | provenance | | | flask_pymongo_bad.py:11:21:11:27 | ControlFlowNode for request | flask_pymongo_bad.py:11:5:11:17 | ControlFlowNode for unsafe_search | provenance | AdditionalTaintStep | -| flask_pymongo_bad.py:12:5:12:15 | ControlFlowNode for json_search | flask_pymongo_bad.py:14:31:14:51 | ControlFlowNode for Dict | provenance | | +| flask_pymongo_bad.py:12:5:12:15 | ControlFlowNode for json_search | flask_pymongo_bad.py:14:40:14:50 | ControlFlowNode for json_search | provenance | | | flask_pymongo_bad.py:12:19:12:43 | ControlFlowNode for Attribute() | flask_pymongo_bad.py:12:5:12:15 | ControlFlowNode for json_search | provenance | | | flask_pymongo_bad.py:12:30:12:42 | ControlFlowNode for unsafe_search | flask_pymongo_bad.py:12:19:12:43 | ControlFlowNode for Attribute() | provenance | Config | +| flask_pymongo_bad.py:14:40:14:50 | ControlFlowNode for json_search | flask_pymongo_bad.py:14:31:14:51 | ControlFlowNode for Dict | provenance | | | mongoengine_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | mongoengine_bad.py:1:26:1:32 | ControlFlowNode for request | provenance | | | mongoengine_bad.py:1:26:1:32 | ControlFlowNode for request | mongoengine_bad.py:18:21:18:27 | ControlFlowNode for request | provenance | | | mongoengine_bad.py:1:26:1:32 | ControlFlowNode for request | mongoengine_bad.py:26:21:26:27 | ControlFlowNode for request | provenance | | @@ -58,24 +69,28 @@ edges | mongoengine_bad.py:1:26:1:32 | ControlFlowNode for request | mongoengine_bad.py:57:21:57:27 | ControlFlowNode for request | provenance | | | mongoengine_bad.py:18:5:18:17 | ControlFlowNode for unsafe_search | mongoengine_bad.py:19:30:19:42 | ControlFlowNode for unsafe_search | provenance | | | mongoengine_bad.py:18:21:18:27 | ControlFlowNode for request | mongoengine_bad.py:18:5:18:17 | ControlFlowNode for unsafe_search | provenance | AdditionalTaintStep | -| mongoengine_bad.py:19:5:19:15 | ControlFlowNode for json_search | mongoengine_bad.py:22:26:22:46 | ControlFlowNode for Dict | provenance | | +| mongoengine_bad.py:19:5:19:15 | ControlFlowNode for json_search | mongoengine_bad.py:22:35:22:45 | ControlFlowNode for json_search | provenance | | | mongoengine_bad.py:19:19:19:43 | ControlFlowNode for Attribute() | mongoengine_bad.py:19:5:19:15 | ControlFlowNode for json_search | provenance | | | mongoengine_bad.py:19:30:19:42 | ControlFlowNode for unsafe_search | mongoengine_bad.py:19:19:19:43 | ControlFlowNode for Attribute() | provenance | Config | +| mongoengine_bad.py:22:35:22:45 | ControlFlowNode for json_search | mongoengine_bad.py:22:26:22:46 | ControlFlowNode for Dict | provenance | | | mongoengine_bad.py:26:5:26:17 | ControlFlowNode for unsafe_search | mongoengine_bad.py:27:30:27:42 | ControlFlowNode for unsafe_search | provenance | | | mongoengine_bad.py:26:21:26:27 | ControlFlowNode for request | mongoengine_bad.py:26:5:26:17 | ControlFlowNode for unsafe_search | provenance | AdditionalTaintStep | -| mongoengine_bad.py:27:5:27:15 | ControlFlowNode for json_search | mongoengine_bad.py:30:26:30:46 | ControlFlowNode for Dict | provenance | | +| mongoengine_bad.py:27:5:27:15 | ControlFlowNode for json_search | mongoengine_bad.py:30:35:30:45 | ControlFlowNode for json_search | provenance | | | mongoengine_bad.py:27:19:27:43 | ControlFlowNode for Attribute() | mongoengine_bad.py:27:5:27:15 | ControlFlowNode for json_search | provenance | | | mongoengine_bad.py:27:30:27:42 | ControlFlowNode for unsafe_search | mongoengine_bad.py:27:19:27:43 | ControlFlowNode for Attribute() | provenance | Config | +| mongoengine_bad.py:30:35:30:45 | ControlFlowNode for json_search | mongoengine_bad.py:30:26:30:46 | ControlFlowNode for Dict | provenance | | | mongoengine_bad.py:34:5:34:17 | ControlFlowNode for unsafe_search | mongoengine_bad.py:35:30:35:42 | ControlFlowNode for unsafe_search | provenance | | | mongoengine_bad.py:34:21:34:27 | ControlFlowNode for request | mongoengine_bad.py:34:5:34:17 | ControlFlowNode for unsafe_search | provenance | AdditionalTaintStep | -| mongoengine_bad.py:35:5:35:15 | ControlFlowNode for json_search | mongoengine_bad.py:38:26:38:46 | ControlFlowNode for Dict | provenance | | +| mongoengine_bad.py:35:5:35:15 | ControlFlowNode for json_search | mongoengine_bad.py:38:35:38:45 | ControlFlowNode for json_search | provenance | | | mongoengine_bad.py:35:19:35:43 | ControlFlowNode for Attribute() | mongoengine_bad.py:35:5:35:15 | ControlFlowNode for json_search | provenance | | | mongoengine_bad.py:35:30:35:42 | ControlFlowNode for unsafe_search | mongoengine_bad.py:35:19:35:43 | ControlFlowNode for Attribute() | provenance | Config | +| mongoengine_bad.py:38:35:38:45 | ControlFlowNode for json_search | mongoengine_bad.py:38:26:38:46 | ControlFlowNode for Dict | provenance | | | mongoengine_bad.py:42:5:42:17 | ControlFlowNode for unsafe_search | mongoengine_bad.py:43:30:43:42 | ControlFlowNode for unsafe_search | provenance | | | mongoengine_bad.py:42:21:42:27 | ControlFlowNode for request | mongoengine_bad.py:42:5:42:17 | ControlFlowNode for unsafe_search | provenance | AdditionalTaintStep | -| mongoengine_bad.py:43:5:43:15 | ControlFlowNode for json_search | mongoengine_bad.py:46:26:46:46 | ControlFlowNode for Dict | provenance | | +| mongoengine_bad.py:43:5:43:15 | ControlFlowNode for json_search | mongoengine_bad.py:46:35:46:45 | ControlFlowNode for json_search | provenance | | | mongoengine_bad.py:43:19:43:43 | ControlFlowNode for Attribute() | mongoengine_bad.py:43:5:43:15 | ControlFlowNode for json_search | provenance | | | mongoengine_bad.py:43:30:43:42 | ControlFlowNode for unsafe_search | mongoengine_bad.py:43:19:43:43 | ControlFlowNode for Attribute() | provenance | Config | +| mongoengine_bad.py:46:35:46:45 | ControlFlowNode for json_search | mongoengine_bad.py:46:26:46:46 | ControlFlowNode for Dict | provenance | | | mongoengine_bad.py:50:5:50:17 | ControlFlowNode for unsafe_search | mongoengine_bad.py:51:30:51:42 | ControlFlowNode for unsafe_search | provenance | | | mongoengine_bad.py:50:21:50:27 | ControlFlowNode for request | mongoengine_bad.py:50:5:50:17 | ControlFlowNode for unsafe_search | provenance | AdditionalTaintStep | | mongoengine_bad.py:51:5:51:15 | ControlFlowNode for json_search | mongoengine_bad.py:53:34:53:44 | ControlFlowNode for json_search | provenance | | @@ -83,9 +98,10 @@ edges | mongoengine_bad.py:51:30:51:42 | ControlFlowNode for unsafe_search | mongoengine_bad.py:51:19:51:43 | ControlFlowNode for Attribute() | provenance | Config | | mongoengine_bad.py:57:5:57:17 | ControlFlowNode for unsafe_search | mongoengine_bad.py:58:30:58:42 | ControlFlowNode for unsafe_search | provenance | | | mongoengine_bad.py:57:21:57:27 | ControlFlowNode for request | mongoengine_bad.py:57:5:57:17 | ControlFlowNode for unsafe_search | provenance | AdditionalTaintStep | -| mongoengine_bad.py:58:5:58:15 | ControlFlowNode for json_search | mongoengine_bad.py:61:29:61:49 | ControlFlowNode for Dict | provenance | | +| mongoengine_bad.py:58:5:58:15 | ControlFlowNode for json_search | mongoengine_bad.py:61:38:61:48 | ControlFlowNode for json_search | provenance | | | mongoengine_bad.py:58:19:58:43 | ControlFlowNode for Attribute() | mongoengine_bad.py:58:5:58:15 | ControlFlowNode for json_search | provenance | | | mongoengine_bad.py:58:30:58:42 | ControlFlowNode for unsafe_search | mongoengine_bad.py:58:19:58:43 | ControlFlowNode for Attribute() | provenance | Config | +| mongoengine_bad.py:61:38:61:48 | ControlFlowNode for json_search | mongoengine_bad.py:61:29:61:49 | ControlFlowNode for Dict | provenance | | | pymongo_test.py:1:26:1:32 | ControlFlowNode for ImportMember | pymongo_test.py:1:26:1:32 | ControlFlowNode for request | provenance | | | pymongo_test.py:1:26:1:32 | ControlFlowNode for request | pymongo_test.py:12:21:12:27 | ControlFlowNode for request | provenance | | | pymongo_test.py:1:26:1:32 | ControlFlowNode for request | pymongo_test.py:29:27:29:33 | ControlFlowNode for request | provenance | | @@ -93,9 +109,10 @@ edges | pymongo_test.py:1:26:1:32 | ControlFlowNode for request | pymongo_test.py:52:26:52:32 | ControlFlowNode for request | provenance | | | pymongo_test.py:12:5:12:17 | ControlFlowNode for unsafe_search | pymongo_test.py:13:30:13:42 | ControlFlowNode for unsafe_search | provenance | | | pymongo_test.py:12:21:12:27 | ControlFlowNode for request | pymongo_test.py:12:5:12:17 | ControlFlowNode for unsafe_search | provenance | AdditionalTaintStep | -| pymongo_test.py:13:5:13:15 | ControlFlowNode for json_search | pymongo_test.py:15:42:15:62 | ControlFlowNode for Dict | provenance | | +| pymongo_test.py:13:5:13:15 | ControlFlowNode for json_search | pymongo_test.py:15:51:15:61 | ControlFlowNode for json_search | provenance | | | pymongo_test.py:13:19:13:43 | ControlFlowNode for Attribute() | pymongo_test.py:13:5:13:15 | ControlFlowNode for json_search | provenance | | | pymongo_test.py:13:30:13:42 | ControlFlowNode for unsafe_search | pymongo_test.py:13:19:13:43 | ControlFlowNode for Attribute() | provenance | Config | +| pymongo_test.py:15:51:15:61 | ControlFlowNode for json_search | pymongo_test.py:15:42:15:62 | ControlFlowNode for Dict | provenance | | | pymongo_test.py:29:5:29:12 | ControlFlowNode for event_id | pymongo_test.py:33:45:33:72 | ControlFlowNode for Fstring | provenance | | | pymongo_test.py:29:16:29:51 | ControlFlowNode for Attribute() | pymongo_test.py:29:5:29:12 | ControlFlowNode for event_id | provenance | | | pymongo_test.py:29:27:29:33 | ControlFlowNode for request | pymongo_test.py:29:27:29:50 | ControlFlowNode for Subscript | provenance | AdditionalTaintStep | @@ -112,13 +129,22 @@ edges | pymongo_test.py:52:15:52:50 | ControlFlowNode for Attribute() | pymongo_test.py:52:5:52:11 | ControlFlowNode for decoded | provenance | | | pymongo_test.py:52:26:52:32 | ControlFlowNode for request | pymongo_test.py:52:26:52:49 | ControlFlowNode for Subscript | provenance | AdditionalTaintStep | | pymongo_test.py:52:26:52:49 | ControlFlowNode for Subscript | pymongo_test.py:52:15:52:50 | ControlFlowNode for Attribute() | provenance | Config | -| pymongo_test.py:54:5:54:10 | ControlFlowNode for search | pymongo_test.py:59:25:59:56 | ControlFlowNode for Dict | provenance | | +| pymongo_test.py:54:5:54:10 | ControlFlowNode for search | pymongo_test.py:59:49:59:54 | ControlFlowNode for search | provenance | | +| pymongo_test.py:54:5:54:10 | ControlFlowNode for search [Dictionary element at key body] | pymongo_test.py:59:49:59:54 | ControlFlowNode for search [Dictionary element at key body] | provenance | | | pymongo_test.py:54:14:58:5 | ControlFlowNode for Dict | pymongo_test.py:54:5:54:10 | ControlFlowNode for search | provenance | | -| pymongo_test.py:55:17:55:23 | ControlFlowNode for decoded | pymongo_test.py:54:14:58:5 | ControlFlowNode for Dict | provenance | | +| pymongo_test.py:54:14:58:5 | ControlFlowNode for Dict [Dictionary element at key body] | pymongo_test.py:54:5:54:10 | ControlFlowNode for search [Dictionary element at key body] | provenance | | | pymongo_test.py:55:17:55:23 | ControlFlowNode for decoded | pymongo_test.py:54:14:58:5 | ControlFlowNode for Dict | provenance | Decoding-NoSQL | -| pymongo_test.py:55:17:55:23 | ControlFlowNode for decoded | pymongo_test.py:61:25:61:57 | ControlFlowNode for Dict | provenance | | -| pymongo_test.py:55:17:55:23 | ControlFlowNode for decoded | pymongo_test.py:62:25:62:42 | ControlFlowNode for Dict | provenance | | +| pymongo_test.py:55:17:55:23 | ControlFlowNode for decoded | pymongo_test.py:54:14:58:5 | ControlFlowNode for Dict [Dictionary element at key body] | provenance | | +| pymongo_test.py:55:17:55:23 | ControlFlowNode for decoded | pymongo_test.py:61:49:61:55 | ControlFlowNode for decoded | provenance | | +| pymongo_test.py:55:17:55:23 | ControlFlowNode for decoded | pymongo_test.py:62:35:62:41 | ControlFlowNode for decoded | provenance | | | pymongo_test.py:55:17:55:23 | ControlFlowNode for decoded | pymongo_test.py:63:25:63:31 | ControlFlowNode for decoded | provenance | | +| pymongo_test.py:59:35:59:55 | ControlFlowNode for Dict [Dictionary element at key $function, Dictionary element at key body] | pymongo_test.py:59:25:59:56 | ControlFlowNode for Dict | provenance | | +| pymongo_test.py:59:35:59:55 | ControlFlowNode for Dict [Dictionary element at key $function] | pymongo_test.py:59:25:59:56 | ControlFlowNode for Dict | provenance | | +| pymongo_test.py:59:49:59:54 | ControlFlowNode for search | pymongo_test.py:59:35:59:55 | ControlFlowNode for Dict [Dictionary element at key $function] | provenance | | +| pymongo_test.py:59:49:59:54 | ControlFlowNode for search [Dictionary element at key body] | pymongo_test.py:59:35:59:55 | ControlFlowNode for Dict [Dictionary element at key $function, Dictionary element at key body] | provenance | | +| pymongo_test.py:61:35:61:56 | ControlFlowNode for Dict [Dictionary element at key $function] | pymongo_test.py:61:25:61:57 | ControlFlowNode for Dict | provenance | | +| pymongo_test.py:61:49:61:55 | ControlFlowNode for decoded | pymongo_test.py:61:35:61:56 | ControlFlowNode for Dict [Dictionary element at key $function] | provenance | | +| pymongo_test.py:62:35:62:41 | ControlFlowNode for decoded | pymongo_test.py:62:25:62:42 | ControlFlowNode for Dict | provenance | | nodes | PoC/server.py:1:26:1:32 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | | PoC/server.py:1:26:1:32 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | @@ -128,7 +154,9 @@ nodes | PoC/server.py:27:14:27:38 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | PoC/server.py:27:25:27:37 | ControlFlowNode for author_string | semmle.label | ControlFlowNode for author_string | | PoC/server.py:30:27:30:44 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | +| PoC/server.py:30:38:30:43 | ControlFlowNode for author | semmle.label | ControlFlowNode for author | | PoC/server.py:31:34:31:51 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | +| PoC/server.py:31:45:31:50 | ControlFlowNode for author | semmle.label | ControlFlowNode for author | | PoC/server.py:43:5:43:10 | ControlFlowNode for author | semmle.label | ControlFlowNode for author | | PoC/server.py:43:14:43:20 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | PoC/server.py:47:27:47:68 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | @@ -139,14 +167,21 @@ nodes | PoC/server.py:53:14:57:5 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | | PoC/server.py:54:17:54:70 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | | PoC/server.py:61:27:61:58 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | +| PoC/server.py:61:37:61:57 | ControlFlowNode for Dict [Dictionary element at key $function] | semmle.label | ControlFlowNode for Dict [Dictionary element at key $function] | +| PoC/server.py:61:51:61:56 | ControlFlowNode for search | semmle.label | ControlFlowNode for search | | PoC/server.py:77:5:77:10 | ControlFlowNode for author | semmle.label | ControlFlowNode for author | | PoC/server.py:77:14:77:20 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | PoC/server.py:78:5:78:15 | ControlFlowNode for accumulator | semmle.label | ControlFlowNode for accumulator | | PoC/server.py:78:19:83:5 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | | PoC/server.py:80:23:80:101 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | -| PoC/server.py:84:5:84:9 | ControlFlowNode for group | semmle.label | ControlFlowNode for group | +| PoC/server.py:84:5:84:9 | ControlFlowNode for group [Dictionary element at key author, Dictionary element at key $accumulator] | semmle.label | ControlFlowNode for group [Dictionary element at key author, Dictionary element at key $accumulator] | +| PoC/server.py:84:13:87:5 | ControlFlowNode for Dict [Dictionary element at key author, Dictionary element at key $accumulator] | semmle.label | ControlFlowNode for Dict [Dictionary element at key author, Dictionary element at key $accumulator] | +| PoC/server.py:86:19:86:49 | ControlFlowNode for Dict [Dictionary element at key $accumulator] | semmle.label | ControlFlowNode for Dict [Dictionary element at key $accumulator] | +| PoC/server.py:86:37:86:47 | ControlFlowNode for accumulator | semmle.label | ControlFlowNode for accumulator | | PoC/server.py:91:29:91:47 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | +| PoC/server.py:91:41:91:45 | ControlFlowNode for group [Dictionary element at key author, Dictionary element at key $accumulator] | semmle.label | ControlFlowNode for group [Dictionary element at key author, Dictionary element at key $accumulator] | | PoC/server.py:92:38:92:56 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | +| PoC/server.py:92:50:92:54 | ControlFlowNode for group [Dictionary element at key author, Dictionary element at key $accumulator] | semmle.label | ControlFlowNode for group [Dictionary element at key author, Dictionary element at key $accumulator] | | PoC/server.py:98:5:98:10 | ControlFlowNode for author | semmle.label | ControlFlowNode for author | | PoC/server.py:98:14:98:20 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | PoC/server.py:99:5:99:10 | ControlFlowNode for mapper | semmle.label | ControlFlowNode for mapper | @@ -165,6 +200,7 @@ nodes | flask_mongoengine_bad.py:27:19:27:43 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | flask_mongoengine_bad.py:27:30:27:42 | ControlFlowNode for unsafe_search | semmle.label | ControlFlowNode for unsafe_search | | flask_mongoengine_bad.py:30:39:30:59 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | +| flask_mongoengine_bad.py:30:48:30:58 | ControlFlowNode for json_search | semmle.label | ControlFlowNode for json_search | | flask_pymongo_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | | flask_pymongo_bad.py:1:26:1:32 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | flask_pymongo_bad.py:11:5:11:17 | ControlFlowNode for unsafe_search | semmle.label | ControlFlowNode for unsafe_search | @@ -173,6 +209,7 @@ nodes | flask_pymongo_bad.py:12:19:12:43 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | flask_pymongo_bad.py:12:30:12:42 | ControlFlowNode for unsafe_search | semmle.label | ControlFlowNode for unsafe_search | | flask_pymongo_bad.py:14:31:14:51 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | +| flask_pymongo_bad.py:14:40:14:50 | ControlFlowNode for json_search | semmle.label | ControlFlowNode for json_search | | mongoengine_bad.py:1:26:1:32 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | | mongoengine_bad.py:1:26:1:32 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | mongoengine_bad.py:18:5:18:17 | ControlFlowNode for unsafe_search | semmle.label | ControlFlowNode for unsafe_search | @@ -181,24 +218,28 @@ nodes | mongoengine_bad.py:19:19:19:43 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | mongoengine_bad.py:19:30:19:42 | ControlFlowNode for unsafe_search | semmle.label | ControlFlowNode for unsafe_search | | mongoengine_bad.py:22:26:22:46 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | +| mongoengine_bad.py:22:35:22:45 | ControlFlowNode for json_search | semmle.label | ControlFlowNode for json_search | | mongoengine_bad.py:26:5:26:17 | ControlFlowNode for unsafe_search | semmle.label | ControlFlowNode for unsafe_search | | mongoengine_bad.py:26:21:26:27 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | mongoengine_bad.py:27:5:27:15 | ControlFlowNode for json_search | semmle.label | ControlFlowNode for json_search | | mongoengine_bad.py:27:19:27:43 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | mongoengine_bad.py:27:30:27:42 | ControlFlowNode for unsafe_search | semmle.label | ControlFlowNode for unsafe_search | | mongoengine_bad.py:30:26:30:46 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | +| mongoengine_bad.py:30:35:30:45 | ControlFlowNode for json_search | semmle.label | ControlFlowNode for json_search | | mongoengine_bad.py:34:5:34:17 | ControlFlowNode for unsafe_search | semmle.label | ControlFlowNode for unsafe_search | | mongoengine_bad.py:34:21:34:27 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | mongoengine_bad.py:35:5:35:15 | ControlFlowNode for json_search | semmle.label | ControlFlowNode for json_search | | mongoengine_bad.py:35:19:35:43 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | mongoengine_bad.py:35:30:35:42 | ControlFlowNode for unsafe_search | semmle.label | ControlFlowNode for unsafe_search | | mongoengine_bad.py:38:26:38:46 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | +| mongoengine_bad.py:38:35:38:45 | ControlFlowNode for json_search | semmle.label | ControlFlowNode for json_search | | mongoengine_bad.py:42:5:42:17 | ControlFlowNode for unsafe_search | semmle.label | ControlFlowNode for unsafe_search | | mongoengine_bad.py:42:21:42:27 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | mongoengine_bad.py:43:5:43:15 | ControlFlowNode for json_search | semmle.label | ControlFlowNode for json_search | | mongoengine_bad.py:43:19:43:43 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | mongoengine_bad.py:43:30:43:42 | ControlFlowNode for unsafe_search | semmle.label | ControlFlowNode for unsafe_search | | mongoengine_bad.py:46:26:46:46 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | +| mongoengine_bad.py:46:35:46:45 | ControlFlowNode for json_search | semmle.label | ControlFlowNode for json_search | | mongoengine_bad.py:50:5:50:17 | ControlFlowNode for unsafe_search | semmle.label | ControlFlowNode for unsafe_search | | mongoengine_bad.py:50:21:50:27 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | mongoengine_bad.py:51:5:51:15 | ControlFlowNode for json_search | semmle.label | ControlFlowNode for json_search | @@ -211,6 +252,7 @@ nodes | mongoengine_bad.py:58:19:58:43 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | mongoengine_bad.py:58:30:58:42 | ControlFlowNode for unsafe_search | semmle.label | ControlFlowNode for unsafe_search | | mongoengine_bad.py:61:29:61:49 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | +| mongoengine_bad.py:61:38:61:48 | ControlFlowNode for json_search | semmle.label | ControlFlowNode for json_search | | pymongo_test.py:1:26:1:32 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | | pymongo_test.py:1:26:1:32 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | pymongo_test.py:12:5:12:17 | ControlFlowNode for unsafe_search | semmle.label | ControlFlowNode for unsafe_search | @@ -219,6 +261,7 @@ nodes | pymongo_test.py:13:19:13:43 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | pymongo_test.py:13:30:13:42 | ControlFlowNode for unsafe_search | semmle.label | ControlFlowNode for unsafe_search | | pymongo_test.py:15:42:15:62 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | +| pymongo_test.py:15:51:15:61 | ControlFlowNode for json_search | semmle.label | ControlFlowNode for json_search | | pymongo_test.py:29:5:29:12 | ControlFlowNode for event_id | semmle.label | ControlFlowNode for event_id | | pymongo_test.py:29:16:29:51 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | pymongo_test.py:29:27:29:33 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | @@ -236,11 +279,20 @@ nodes | pymongo_test.py:52:26:52:32 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | pymongo_test.py:52:26:52:49 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | | pymongo_test.py:54:5:54:10 | ControlFlowNode for search | semmle.label | ControlFlowNode for search | +| pymongo_test.py:54:5:54:10 | ControlFlowNode for search [Dictionary element at key body] | semmle.label | ControlFlowNode for search [Dictionary element at key body] | | pymongo_test.py:54:14:58:5 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | +| pymongo_test.py:54:14:58:5 | ControlFlowNode for Dict [Dictionary element at key body] | semmle.label | ControlFlowNode for Dict [Dictionary element at key body] | | pymongo_test.py:55:17:55:23 | ControlFlowNode for decoded | semmle.label | ControlFlowNode for decoded | | pymongo_test.py:59:25:59:56 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | +| pymongo_test.py:59:35:59:55 | ControlFlowNode for Dict [Dictionary element at key $function, Dictionary element at key body] | semmle.label | ControlFlowNode for Dict [Dictionary element at key $function, Dictionary element at key body] | +| pymongo_test.py:59:35:59:55 | ControlFlowNode for Dict [Dictionary element at key $function] | semmle.label | ControlFlowNode for Dict [Dictionary element at key $function] | +| pymongo_test.py:59:49:59:54 | ControlFlowNode for search | semmle.label | ControlFlowNode for search | +| pymongo_test.py:59:49:59:54 | ControlFlowNode for search [Dictionary element at key body] | semmle.label | ControlFlowNode for search [Dictionary element at key body] | | pymongo_test.py:61:25:61:57 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | +| pymongo_test.py:61:35:61:56 | ControlFlowNode for Dict [Dictionary element at key $function] | semmle.label | ControlFlowNode for Dict [Dictionary element at key $function] | +| pymongo_test.py:61:49:61:55 | ControlFlowNode for decoded | semmle.label | ControlFlowNode for decoded | | pymongo_test.py:62:25:62:42 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | +| pymongo_test.py:62:35:62:41 | ControlFlowNode for decoded | semmle.label | ControlFlowNode for decoded | | pymongo_test.py:63:25:63:31 | ControlFlowNode for decoded | semmle.label | ControlFlowNode for decoded | subpaths #select