Skip to content

Commit eaef783

Browse files
committed
Python: Add partial model for copy.replace
Extends our modelling to partially cover the behaviour of `copy.replace`. In particular, we model this in two ways: Firstly, we extend the existing Models-as-Data row for `copy` and `deepcopy` to also cover `replace`. This means that we treat the result of `replace` as containing all of the fields of the original object. This is somewhat _more_ than we want, as strictly speaking the fields that are overwritten should _not_ propagate flow through the `replace` call, but currently we don't have a good way of modelling this blocking of flow. Secondly, we add a flow summary that adds flow from named arguments of the `replace` call to the corresponding fields on the base object. This ensures that we at least have the new flow arising from the `replace` call. Note that the flow summary adds this flow for _all_ named arguments of _all_ `replace` calls throughout the codebase. However, since any particular `replace` call will only populate a subset of these (the subset consisting of exactly those named arguments that are in that particular call), this does not cause any unwanted crosstalk between different `replace` calls.§
1 parent 6f2cfa0 commit eaef783

File tree

2 files changed

+27
-1
lines changed

2 files changed

+27
-1
lines changed

python/ql/lib/semmle/python/frameworks/Stdlib.model.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ extensions:
4545
# See https://docs.python.org/3/library/contextlib.html#contextlib.ExitStack
4646
- ["contextlib.ExitStack", "Member[enter_context]", "Argument[0,cm:]", "ReturnValue", "taint"]
4747
# See https://docs.python.org/3/library/copy.html#copy.deepcopy
48-
- ["copy", "Member[copy,deepcopy]", "Argument[0,x:]", "ReturnValue", "value"]
48+
- ["copy", "Member[copy,deepcopy,replace]", "Argument[0,x:]", "ReturnValue", "value"]
4949
# See
5050
# - https://docs.python.org/3/library/ctypes.html#ctypes.create_string_buffer
5151
# - https://docs.python.org/3/library/ctypes.html#ctypes.create_unicode_buffer

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4549,6 +4549,32 @@ module StdlibPrivate {
45494549
}
45504550
}
45514551

4552+
/** A flow summary for `copy.replace`. */
4553+
class ReplaceSummary extends SummarizedCallable {
4554+
ReplaceSummary() { this = "copy.replace" }
4555+
4556+
override DataFlow::CallCfgNode getACall() {
4557+
result = API::moduleImport("copy").getMember("replace").getACall()
4558+
}
4559+
4560+
override DataFlow::ArgumentNode getACallback() {
4561+
result = API::moduleImport("copy").getMember("replace").getAValueReachableFromSource()
4562+
}
4563+
4564+
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
4565+
exists(CallNode c, string name, ControlFlowNode n, DataFlow::AttributeContent ac |
4566+
c.getFunction().(NameNode).getId() = "replace" or
4567+
c.getFunction().(AttrNode).getName() = "replace"
4568+
|
4569+
n = c.getArgByName(name) and
4570+
ac.getAttribute() = name and
4571+
input = "Argument[" + name + ":]" and
4572+
output = "ReturnValue." + ac.getMaDRepresentation() and
4573+
preservesValue = true
4574+
)
4575+
}
4576+
}
4577+
45524578
/**
45534579
* A flow summary for `pop` either for list or set.
45544580
* This ignores the index if given, since content is

0 commit comments

Comments
 (0)