Skip to content

Commit 3e8274e

Browse files
authored
Merge pull request github#8812 from RasmusWL/stdlib-FileSystemAccess-improvement
Python: Minor Stdlib file system access improvement
2 parents 06e5962 + 03c0366 commit 3e8274e

File tree

3 files changed

+73
-3
lines changed

3 files changed

+73
-3
lines changed

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

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -959,13 +959,18 @@ private module StdlibPrivate {
959959
}
960960
}
961961

962-
/** A call to `os.path.samefile` will raise an exception if an `os.stat()` call on either pathname fails. */
962+
/**
963+
* A call to `os.path.samefile` will raise an exception if an `os.stat()` call on either pathname fails.
964+
*
965+
* See https://docs.python.org/3.10/library/os.path.html#os.path.samefile
966+
*/
963967
private class OsPathSamefileCall extends FileSystemAccess::Range, DataFlow::CallCfgNode {
964968
OsPathSamefileCall() { this = OS::path().getMember("samefile").getACall() }
965969

966970
override DataFlow::Node getAPathArgument() {
967971
result in [
968-
this.getArg(0), this.getArgByName("path1"), this.getArg(1), this.getArgByName("path2")
972+
// note that the f1/f2 names doesn't match the documentation, but is what actually works (tested on 3.8.10)
973+
this.getArg(0), this.getArgByName("f1"), this.getArg(1), this.getArgByName("f2")
969974
]
970975
}
971976
}
@@ -2534,6 +2539,56 @@ private module StdlibPrivate {
25342539
PathLibOpenCall() { attrbuteName = "open" }
25352540
}
25362541

2542+
/**
2543+
* A call to the `link_to`, `hardlink_to`, or `symlink_to` method on a `pathlib.Path` instance.
2544+
*
2545+
* See
2546+
* - https://docs.python.org/3/library/pathlib.html#pathlib.Path.link_to
2547+
* - https://docs.python.org/3/library/pathlib.html#pathlib.Path.hardlink_to
2548+
* - https://docs.python.org/3/library/pathlib.html#pathlib.Path.symlink_to
2549+
*/
2550+
private class PathLibLinkToCall extends PathlibFileAccess, API::CallNode {
2551+
PathLibLinkToCall() { attrbuteName in ["link_to", "hardlink_to", "symlink_to"] }
2552+
2553+
override DataFlow::Node getAPathArgument() {
2554+
result = super.getAPathArgument()
2555+
or
2556+
result = this.getParameter(0, "target").getARhs()
2557+
}
2558+
}
2559+
2560+
/**
2561+
* A call to the `replace` or `rename` method on a `pathlib.Path` instance.
2562+
*
2563+
* See
2564+
* - https://docs.python.org/3/library/pathlib.html#pathlib.Path.replace
2565+
* - https://docs.python.org/3/library/pathlib.html#pathlib.Path.rename
2566+
*/
2567+
private class PathLibReplaceCall extends PathlibFileAccess, API::CallNode {
2568+
PathLibReplaceCall() { attrbuteName in ["replace", "rename"] }
2569+
2570+
override DataFlow::Node getAPathArgument() {
2571+
result = super.getAPathArgument()
2572+
or
2573+
result = this.getParameter(0, "target").getARhs()
2574+
}
2575+
}
2576+
2577+
/**
2578+
* A call to the `samefile` method on a `pathlib.Path` instance.
2579+
*
2580+
* See https://docs.python.org/3/library/pathlib.html#pathlib.Path.samefile
2581+
*/
2582+
private class PathLibSameFileCall extends PathlibFileAccess, API::CallNode {
2583+
PathLibSameFileCall() { attrbuteName = "samefile" }
2584+
2585+
override DataFlow::Node getAPathArgument() {
2586+
result = super.getAPathArgument()
2587+
or
2588+
result = this.getParameter(0, "other_path").getARhs()
2589+
}
2590+
}
2591+
25372592
/** An additional taint steps for objects of type `pathlib.Path` */
25382593
private class PathlibPathTaintStep extends TaintTracking::AdditionalTaintStep {
25392594
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,15 @@
2121

2222
wb = p.write_bytes
2323
wb(b"hello") # $ getAPathArgument=p fileWriteData=b"hello"
24+
25+
p.link_to("target") # $ getAPathArgument=p getAPathArgument="target"
26+
p.link_to(target="target") # $ getAPathArgument=p getAPathArgument="target"
27+
28+
p.samefile("other_path") # $ getAPathArgument=p getAPathArgument="other_path"
29+
p.samefile(other_path="other_path") # $ getAPathArgument=p getAPathArgument="other_path"
30+
31+
p.rename("target") # $ getAPathArgument=p getAPathArgument="target"
32+
p.rename(target="target") # $ getAPathArgument=p getAPathArgument="target"
33+
34+
p.replace("target") # $ getAPathArgument=p getAPathArgument="target"
35+
p.replace(target="target") # $ getAPathArgument=p getAPathArgument="target"

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ def through_function(open_file):
4848
os.path.ismount("path") # $ getAPathArgument="path"
4949
os.path.ismount(path="path") # $ getAPathArgument="path"
5050

51+
os.path.samefile("f1", "f2") # $ getAPathArgument="f1" getAPathArgument="f2"
52+
os.path.samefile(f1="f1", f2="f2") # $ getAPathArgument="f1" getAPathArgument="f2"
53+
5154
# actual os.path implementations
5255
import posixpath
5356
import ntpath
@@ -269,4 +272,4 @@ def test_fspath():
269272
shutil.copystat(src="src", dst="dst") # $ getAPathArgument="src" getAPathArgument="dst"
270273

271274
shutil.disk_usage("path") # $ getAPathArgument="path"
272-
shutil.disk_usage(path="path") # $ getAPathArgument="path"
275+
shutil.disk_usage(path="path") # $ getAPathArgument="path"

0 commit comments

Comments
 (0)