Skip to content

Commit ee411cc

Browse files
authored
Merge pull request #15936 from yoff/python/test-conflicting-summaries
Python: No `fieldFlowBranchLimit` for `SummarizedCallable`s
2 parents f5fed84 + 2a0c451 commit ee411cc

File tree

7 files changed

+138
-0
lines changed

7 files changed

+138
-0
lines changed

python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplSpecific.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,6 @@ module PythonDataFlow implements InputSig<Python::Location> {
2222
predicate neverSkipInPathGraph = Private::neverSkipInPathGraph/1;
2323

2424
Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) }
25+
26+
predicate ignoreFieldFlowBranchLimit(DataFlowCallable c) { exists(c.asLibraryCallable()) }
2527
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
argumentToEnsureNotTaintedNotMarkedAsSpurious
2+
untaintedArgumentToEnsureTaintedNotMarkedAsMissing
3+
testFailures
4+
failures
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import python
2+
import experimental.meta.InlineTaintTest
3+
import MakeInlineTaintTest<TestTaintTrackingConfig>
4+
import TestSummaries

python/ql/test/experimental/dataflow/summaries/TestSummaries.qll

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,108 @@ private class SummarizedCallableJsonLoads extends SummarizedCallable {
136136
preservesValue = true
137137
}
138138
}
139+
140+
// Repeated summaries
141+
private class SummarizedCallableWithSubpath extends SummarizedCallable {
142+
SummarizedCallableWithSubpath() { this = "extracted_package.functions.with_subpath" }
143+
144+
override DataFlow::CallCfgNode getACall() {
145+
result =
146+
API::moduleImport("extracted_package")
147+
.getMember("functions")
148+
.getMember("with_subpath")
149+
.getACall()
150+
}
151+
152+
override DataFlow::ArgumentNode getACallback() {
153+
result =
154+
API::moduleImport("extracted_package")
155+
.getMember("functions")
156+
.getMember("with_subpath")
157+
.getAValueReachableFromSource()
158+
}
159+
160+
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
161+
input = "Argument[0]" and
162+
output = "ReturnValue" and
163+
preservesValue = false
164+
}
165+
}
166+
167+
private class SummarizedCallableWithSubpathAgain extends SummarizedCallable {
168+
SummarizedCallableWithSubpathAgain() { this = "extracted_package.functions.with_subpathII" }
169+
170+
override DataFlow::CallCfgNode getACall() {
171+
result =
172+
API::moduleImport("extracted_package")
173+
.getMember("functions")
174+
.getMember("with_subpath")
175+
.getACall()
176+
}
177+
178+
override DataFlow::ArgumentNode getACallback() {
179+
result =
180+
API::moduleImport("extracted_package")
181+
.getMember("functions")
182+
.getMember("with_subpath")
183+
.getAValueReachableFromSource()
184+
}
185+
186+
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
187+
input = "Argument[0]" and
188+
output = "ReturnValue.Attribute[pattern]" and
189+
preservesValue = true
190+
}
191+
}
192+
193+
private class SummarizedCallableWithoutSubpath extends SummarizedCallable {
194+
SummarizedCallableWithoutSubpath() { this = "extracted_package.functions.without_subpath" }
195+
196+
override DataFlow::CallCfgNode getACall() {
197+
result =
198+
API::moduleImport("extracted_package")
199+
.getMember("functions")
200+
.getMember("without_subpath")
201+
.getACall()
202+
}
203+
204+
override DataFlow::ArgumentNode getACallback() {
205+
result =
206+
API::moduleImport("extracted_package")
207+
.getMember("functions")
208+
.getMember("without_subpath")
209+
.getAValueReachableFromSource()
210+
}
211+
212+
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
213+
input = "Argument[0]" and
214+
output = "ReturnValue" and
215+
preservesValue = false
216+
}
217+
}
218+
219+
private class SummarizedCallableWithoutSubpathAgain extends SummarizedCallable {
220+
SummarizedCallableWithoutSubpathAgain() { this = "extracted_package.functions.without_subpathII" }
221+
222+
override DataFlow::CallCfgNode getACall() {
223+
result =
224+
API::moduleImport("extracted_package")
225+
.getMember("functions")
226+
.getMember("without_subpath")
227+
.getACall()
228+
}
229+
230+
override DataFlow::ArgumentNode getACallback() {
231+
result =
232+
API::moduleImport("extracted_package")
233+
.getMember("functions")
234+
.getMember("without_subpath")
235+
.getAValueReachableFromSource()
236+
}
237+
238+
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
239+
input = "Argument[0]" and
240+
output = "ReturnValue.Attribute[pattern]" and
241+
preservesValue = true
242+
}
243+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Bad interaction of two summaries for the same function
2+
ts = TAINTED_STRING
3+
4+
from extracted_package.functions import with_subpath, without_subpath
5+
6+
# For the function `with_subpath`, flow from the first argument to the return value
7+
# can be concluded from its definition. This seems to discard all summaries, including
8+
# the one with flow to `ReturnValue.Attribute[pattern]`.
9+
ensure_tainted(
10+
with_subpath(ts).pattern, # $ tainted
11+
with_subpath(ts), # $ tainted
12+
with_subpath(ts), # $ tainted
13+
)
14+
ensure_tainted(
15+
without_subpath(ts).pattern, # $ tainted
16+
without_subpath(ts), # $ tainted
17+
without_subpath(ts), # $ tainted
18+
)

python/ql/test/experimental/dataflow/summaries/extracted_package/__init__.py

Whitespace-only changes.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
def with_subpath(x):
2+
return x
3+
4+
def without_subpath(x):
5+
pass

0 commit comments

Comments
 (0)