diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll index 643d3347327..1b22e8740bc 100644 --- a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -134,9 +134,25 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathTokenBase token) { token.getAnArgument() = "any-named" and result = node.getKeywordParameter(_) ) + or + // content based steps + // + // note: if we want to migrate to use `FlowSummaryImpl::Input::encodeContent` like + // they do in Ruby, be aware that we currently don't make + // `DataFlow::DictionaryElementContent` just from seeing a subscript read, so we would + // need to add that. (also need to handle things like `DictionaryElementAny` which + // doesn't have any value for .getAnArgument()) + ( + token.getName() = "DictionaryElement" and + result = node.getSubscript(token.getAnArgument()) + or + token.getName() = "DictionaryElementAny" and + result = node.getASubscript() and + not exists(token.getAnArgument()) + // TODO: ListElement/SetElement/TupleElement + ) // Some features don't have MaD tokens yet, they would need to be added to API-graphs first. // - decorators ("DecoratedClass", "DecoratedMember", "DecoratedParameter") - // - Array/Map elements ("ArrayElement", "Element", "MapKey", "MapValue") } /** @@ -242,7 +258,11 @@ InvokeNode getAnInvocationOf(API::Node node) { result = node.getACall() } */ bindingset[name] predicate isExtraValidTokenNameInIdentifyingAccessPath(string name) { - name = ["Member", "Instance", "Awaited", "Call", "Method", "Subclass"] + name = + [ + "Member", "Instance", "Awaited", "Call", "Method", "Subclass", "DictionaryElement", + "DictionaryElementAny" + ] } /** @@ -250,7 +270,7 @@ predicate isExtraValidTokenNameInIdentifyingAccessPath(string name) { * in an identifying access path. */ predicate isExtraValidNoArgumentTokenInIdentifyingAccessPath(string name) { - name = ["Instance", "Awaited", "Call", "Subclass"] + name = ["Instance", "Awaited", "Call", "Subclass", "DictionaryElementAny"] } /** @@ -259,7 +279,7 @@ predicate isExtraValidNoArgumentTokenInIdentifyingAccessPath(string name) { */ bindingset[name, argument] predicate isExtraValidTokenArgumentInIdentifyingAccessPath(string name, string argument) { - name = ["Member", "Method"] and + name = ["Member", "Method", "DictionaryElement"] and exists(argument) or name = ["Argument", "Parameter"] and diff --git a/python/ql/test/library-tests/frameworks/data/test.expected b/python/ql/test/library-tests/frameworks/data/test.expected index 5f3e3b3b4f3..1e229a28039 100644 --- a/python/ql/test/library-tests/frameworks/data/test.expected +++ b/python/ql/test/library-tests/frameworks/data/test.expected @@ -106,6 +106,8 @@ isSource | test.py:117:31:117:41 | ControlFlowNode for getSource() | test-source | | test.py:118:35:118:45 | ControlFlowNode for getSource() | test-source | | test.py:119:20:119:30 | ControlFlowNode for getSource() | test-source | +| test.py:124:1:124:33 | ControlFlowNode for Attribute() | test-source | +| test.py:126:11:126:43 | ControlFlowNode for Attribute() | test-source | syntaxErrors | Member[foo | | Member[foo] .Member[bar] |