Python: Expose importNode instead of importModule/importMember

Since predicate name `import` is not allowed, I adopted `importNode` as it sort
of matches what `exprNode` does.

---

Due to only using `importMember` in `os_attr` we previously didn't handle
`import os.path as alias` :|

I did creat a hotfix for this (https://github.com/github/codeql/pull/4446), but
in doing so I realized the core of the problem: We're exposing ourselves to
making these kinds of mistakes by having BOTH importModule and importMember, and
we don't really gain anything from doing this!

We do loose the ability to easily only modeling `from mod import val` and not
`import mod.val`, but I don't think that will ever be relevant.

This change will also make us to recognize some invalid code, for example in

    import os.system as runtime_error

we would now model that `runtime_error` is a reference to the `os.system`
function (although the actual import would result in a runtime error).

Overall these are tradeoffs I'm willing to make, as it does makes things simpler
from a QL modeling point of view, and THAT sounds nice 👍
This commit is contained in:
Rasmus Wriedt Larsen
2020-10-09 14:37:23 +02:00
parent 4bfd55f1af
commit 76c9b8c49f
7 changed files with 13 additions and 43 deletions

View File

@@ -36,10 +36,8 @@ predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
* Example: If `mypkg/__init__.py` contains `foo = 42`, then `from mypkg import foo` will not import the module
* `mypkg/foo.py` but the variable `foo` containing `42` -- however, `import mypkg.foo` will always cause `mypkg.foo`
* to refer to the module.
*
* Also see `DataFlow::importMember`
*/
Node importModule(string name) {
Node importNode(string name) {
exists(Variable var, Import imp, Alias alias |
alias = imp.getAName() and
alias.getAsname() = var.getAStore() and
@@ -72,20 +70,3 @@ Node importModule(string name) {
// reference to `foo.bar`, as desired.
result.asCfgNode().getNode() = any(ImportExpr i | i.getAnImportedModuleName() = name)
}
/**
* Gets a EssaNode that holds the value imported by using fully qualified name in
*`from <moduleName> import <memberName>`.
*
* Also see `DataFlow::importModule`.
*/
EssaNode importMember(string moduleName, string memberName) {
exists(Variable var, Import imp, Alias alias, ImportMember member |
alias = imp.getAName() and
member = alias.getValue() and
moduleName = member.getModule().(ImportExpr).getImportedModuleName() and
memberName = member.getName() and
alias.getAsname() = var.getAStore() and
result.getVar().(AssignmentDefinition).getSourceVariable() = var
)
}

View File

@@ -15,7 +15,7 @@ private module Flask {
/** Gets a reference to the `flask` module. */
DataFlow::Node flask(DataFlow::TypeTracker t) {
t.start() and
result = DataFlow::importModule("flask")
result = DataFlow::importNode("flask")
or
exists(DataFlow::TypeTracker t2 | result = flask(t2).track(t2, t))
}
@@ -27,7 +27,7 @@ private module Flask {
/** Gets a reference to the `flask.request` object. */
DataFlow::Node request(DataFlow::TypeTracker t) {
t.start() and
result = DataFlow::importMember("flask", "request")
result = DataFlow::importNode("flask.request")
or
t.startInAttr("request") and
result = flask()

View File

@@ -17,7 +17,7 @@ private module Stdlib {
/** Gets a reference to the `os` module. */
private DataFlow::Node os(DataFlow::TypeTracker t) {
t.start() and
result = DataFlow::importModule("os")
result = DataFlow::importNode("os")
or
exists(DataFlow::TypeTracker t2 | result = os(t2).track(t2, t))
}
@@ -42,10 +42,10 @@ private module Stdlib {
"path"] and
(
t.start() and
result = DataFlow::importMember("os", attr_name)
result = DataFlow::importNode("os." + attr_name)
or
t.startInAttr(attr_name) and
result = DataFlow::importModule("os")
result = DataFlow::importNode("os")
)
or
// Due to bad performance when using normal setup with `os_attr(t2, attr_name).track(t2, t)`
@@ -85,7 +85,7 @@ private module Stdlib {
/** Gets a reference to the `os.path.join` function. */
private DataFlow::Node join(DataFlow::TypeTracker t) {
t.start() and
result = DataFlow::importMember("os.path", "join")
result = DataFlow::importNode("os.path.join")
or
t.startInAttr("join") and
result = os::path()
@@ -190,7 +190,7 @@ private module Stdlib {
/** Gets a reference to the `subprocess` module. */
private DataFlow::Node subprocess(DataFlow::TypeTracker t) {
t.start() and
result = DataFlow::importModule("subprocess")
result = DataFlow::importNode("subprocess")
or
exists(DataFlow::TypeTracker t2 | result = subprocess(t2).track(t2, t))
}
@@ -208,10 +208,10 @@ private module Stdlib {
attr_name in ["Popen", "call", "check_call", "check_output", "run"] and
(
t.start() and
result = DataFlow::importMember("subprocess", attr_name)
result = DataFlow::importNode("subprocess." + attr_name)
or
t.startInAttr(attr_name) and
result = DataFlow::importModule("subprocess")
result = DataFlow::importNode("subprocess")
)
or
// Due to bad performance when using normal setup with `subprocess_attr(t2, attr_name).track(t2, t)`