diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplSpecific.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplSpecific.qll index 2d18c789732..48199dac9f5 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplSpecific.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplSpecific.qll @@ -22,4 +22,6 @@ module PythonDataFlow implements InputSig { predicate neverSkipInPathGraph = Private::neverSkipInPathGraph/1; Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) } + + predicate ignoreFieldFlowBranchLimit(DataFlowCallable c) { exists(c.asLibraryCallable()) } } diff --git a/python/ql/test/experimental/dataflow/summaries/InlineTaintTest.expected b/python/ql/test/experimental/dataflow/summaries/InlineTaintTest.expected new file mode 100644 index 00000000000..366de37b867 --- /dev/null +++ b/python/ql/test/experimental/dataflow/summaries/InlineTaintTest.expected @@ -0,0 +1,4 @@ +argumentToEnsureNotTaintedNotMarkedAsSpurious +untaintedArgumentToEnsureTaintedNotMarkedAsMissing +testFailures +failures diff --git a/python/ql/test/experimental/dataflow/summaries/InlineTaintTest.ql b/python/ql/test/experimental/dataflow/summaries/InlineTaintTest.ql new file mode 100644 index 00000000000..96cc5c3e31f --- /dev/null +++ b/python/ql/test/experimental/dataflow/summaries/InlineTaintTest.ql @@ -0,0 +1,4 @@ +import python +import experimental.meta.InlineTaintTest +import MakeInlineTaintTest +import TestSummaries diff --git a/python/ql/test/experimental/dataflow/summaries/TestSummaries.qll b/python/ql/test/experimental/dataflow/summaries/TestSummaries.qll index b2e29e9999e..534fac62491 100644 --- a/python/ql/test/experimental/dataflow/summaries/TestSummaries.qll +++ b/python/ql/test/experimental/dataflow/summaries/TestSummaries.qll @@ -136,3 +136,108 @@ private class SummarizedCallableJsonLoads extends SummarizedCallable { preservesValue = true } } + +// Repeated summaries +private class SummarizedCallableWithSubpath extends SummarizedCallable { + SummarizedCallableWithSubpath() { this = "extracted_package.functions.with_subpath" } + + override DataFlow::CallCfgNode getACall() { + result = + API::moduleImport("extracted_package") + .getMember("functions") + .getMember("with_subpath") + .getACall() + } + + override DataFlow::ArgumentNode getACallback() { + result = + API::moduleImport("extracted_package") + .getMember("functions") + .getMember("with_subpath") + .getAValueReachableFromSource() + } + + override predicate propagatesFlow(string input, string output, boolean preservesValue) { + input = "Argument[0]" and + output = "ReturnValue" and + preservesValue = false + } +} + +private class SummarizedCallableWithSubpathAgain extends SummarizedCallable { + SummarizedCallableWithSubpathAgain() { this = "extracted_package.functions.with_subpathII" } + + override DataFlow::CallCfgNode getACall() { + result = + API::moduleImport("extracted_package") + .getMember("functions") + .getMember("with_subpath") + .getACall() + } + + override DataFlow::ArgumentNode getACallback() { + result = + API::moduleImport("extracted_package") + .getMember("functions") + .getMember("with_subpath") + .getAValueReachableFromSource() + } + + override predicate propagatesFlow(string input, string output, boolean preservesValue) { + input = "Argument[0]" and + output = "ReturnValue.Attribute[pattern]" and + preservesValue = true + } +} + +private class SummarizedCallableWithoutSubpath extends SummarizedCallable { + SummarizedCallableWithoutSubpath() { this = "extracted_package.functions.without_subpath" } + + override DataFlow::CallCfgNode getACall() { + result = + API::moduleImport("extracted_package") + .getMember("functions") + .getMember("without_subpath") + .getACall() + } + + override DataFlow::ArgumentNode getACallback() { + result = + API::moduleImport("extracted_package") + .getMember("functions") + .getMember("without_subpath") + .getAValueReachableFromSource() + } + + override predicate propagatesFlow(string input, string output, boolean preservesValue) { + input = "Argument[0]" and + output = "ReturnValue" and + preservesValue = false + } +} + +private class SummarizedCallableWithoutSubpathAgain extends SummarizedCallable { + SummarizedCallableWithoutSubpathAgain() { this = "extracted_package.functions.without_subpathII" } + + override DataFlow::CallCfgNode getACall() { + result = + API::moduleImport("extracted_package") + .getMember("functions") + .getMember("without_subpath") + .getACall() + } + + override DataFlow::ArgumentNode getACallback() { + result = + API::moduleImport("extracted_package") + .getMember("functions") + .getMember("without_subpath") + .getAValueReachableFromSource() + } + + override predicate propagatesFlow(string input, string output, boolean preservesValue) { + input = "Argument[0]" and + output = "ReturnValue.Attribute[pattern]" and + preservesValue = true + } +} diff --git a/python/ql/test/experimental/dataflow/summaries/conflicting_summaries.py b/python/ql/test/experimental/dataflow/summaries/conflicting_summaries.py new file mode 100644 index 00000000000..7b031f37a9f --- /dev/null +++ b/python/ql/test/experimental/dataflow/summaries/conflicting_summaries.py @@ -0,0 +1,18 @@ +# Bad interaction of two summaries for the same function +ts = TAINTED_STRING + +from extracted_package.functions import with_subpath, without_subpath + +# For the function `with_subpath`, flow from the first argument to the return value +# can be concluded from its definition. This seems to discard all summaries, including +# the one with flow to `ReturnValue.Attribute[pattern]`. +ensure_tainted( + with_subpath(ts).pattern, # $ tainted + with_subpath(ts), # $ tainted + with_subpath(ts), # $ tainted +) +ensure_tainted( + without_subpath(ts).pattern, # $ tainted + without_subpath(ts), # $ tainted + without_subpath(ts), # $ tainted +) \ No newline at end of file diff --git a/python/ql/test/experimental/dataflow/summaries/extracted_package/__init__.py b/python/ql/test/experimental/dataflow/summaries/extracted_package/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/experimental/dataflow/summaries/extracted_package/functions.py b/python/ql/test/experimental/dataflow/summaries/extracted_package/functions.py new file mode 100644 index 00000000000..f4780be20a3 --- /dev/null +++ b/python/ql/test/experimental/dataflow/summaries/extracted_package/functions.py @@ -0,0 +1,5 @@ +def with_subpath(x): + return x + +def without_subpath(x): + pass \ No newline at end of file