Skip to content

Commit d88e5bd

Browse files
committed
Python: Model io.open as FileSystemAccess
1 parent e39bb56 commit d88e5bd

File tree

2 files changed

+60
-3
lines changed

2 files changed

+60
-3
lines changed

python/ql/src/semmle/python/frameworks/Stdlib.qll

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,11 @@ private module Stdlib {
736736
private class OpenCall extends FileSystemAccess::Range, DataFlow::CfgNode {
737737
override CallNode node;
738738

739-
OpenCall() { node.getFunction() = builtins_attr("open").asCfgNode() }
739+
OpenCall() {
740+
node.getFunction() = builtins_attr("open").asCfgNode()
741+
or
742+
node.getFunction() = io_attr("open").asCfgNode()
743+
}
740744

741745
override DataFlow::Node getAPathArgument() {
742746
result.asCfgNode() in [node.getArg(0), node.getArgByName("file")]
@@ -888,6 +892,59 @@ private module Stdlib {
888892
)
889893
}
890894
}
895+
896+
// ---------------------------------------------------------------------------
897+
// io
898+
// ---------------------------------------------------------------------------
899+
/** Gets a reference to the `io` module. */
900+
private DataFlow::Node io(DataFlow::TypeTracker t) {
901+
t.start() and
902+
result = DataFlow::importNode("io")
903+
or
904+
exists(DataFlow::TypeTracker t2 | result = io(t2).track(t2, t))
905+
}
906+
907+
/** Gets a reference to the `io` module. */
908+
DataFlow::Node io() { result = io(DataFlow::TypeTracker::end()) }
909+
910+
/**
911+
* Gets a reference to the attribute `attr_name` of the `io` module.
912+
* WARNING: Only holds for a few predefined attributes.
913+
*/
914+
private DataFlow::Node io_attr(DataFlow::TypeTracker t, string attr_name) {
915+
attr_name in ["open"] and
916+
(
917+
t.start() and
918+
result = DataFlow::importNode("io" + "." + attr_name)
919+
or
920+
t.startInAttr(attr_name) and
921+
result = io()
922+
)
923+
or
924+
// Due to bad performance when using normal setup with `io_attr(t2, attr_name).track(t2, t)`
925+
// we have inlined that code and forced a join
926+
exists(DataFlow::TypeTracker t2 |
927+
exists(DataFlow::StepSummary summary |
928+
io_attr_first_join(t2, attr_name, result, summary) and
929+
t = t2.append(summary)
930+
)
931+
)
932+
}
933+
934+
pragma[nomagic]
935+
private predicate io_attr_first_join(
936+
DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary
937+
) {
938+
DataFlow::StepSummary::step(io_attr(t2, attr_name), res, summary)
939+
}
940+
941+
/**
942+
* Gets a reference to the attribute `attr_name` of the `io` module.
943+
* WARNING: Only holds for a few predefined attributes.
944+
*/
945+
private DataFlow::Node io_attr(string attr_name) {
946+
result = io_attr(DataFlow::TypeTracker::end(), attr_name)
947+
}
891948
}
892949

893950
// ---------------------------------------------------------------------------

python/ql/test/experimental/library-tests/frameworks/stdlib/FileSystemAccess.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@
1414
builtins.open(file="filepath") # $getAPathArgument="filepath"
1515

1616

17-
io.open("filepath") # $ MISSING: getAPathArgument="filepath"
18-
io.open(file="filepath") # $ MISSING: getAPathArgument="filepath"
17+
io.open("filepath") # $getAPathArgument="filepath"
18+
io.open(file="filepath") # $getAPathArgument="filepath"

0 commit comments

Comments
 (0)