From 7d333d7dbe21c500e8b1f0787a2adeab86bbb283 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Mon, 1 Nov 2021 13:30:38 +0000 Subject: [PATCH] Add InlineFlowTest as simple inline expectation test --- ql/test/TestUtilities/InlineFlowTest.qll | 100 +++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 ql/test/TestUtilities/InlineFlowTest.qll diff --git a/ql/test/TestUtilities/InlineFlowTest.qll b/ql/test/TestUtilities/InlineFlowTest.qll new file mode 100644 index 00000000000..b6bf3d20308 --- /dev/null +++ b/ql/test/TestUtilities/InlineFlowTest.qll @@ -0,0 +1,100 @@ +/** + * Provides a simple base test for flow-related tests using inline expectations. + * + * Example for a test.ql: + * ```ql + * import java + * import TestUtilities.InlineFlowTest + * ``` + * + * To declare expecations, you can use the $hasTaintFlow or $hasValueFlow comments within the test source files. + * Example of the corresponding test file, e.g. Test.java + * ```go + * public class Test { + * + * Object source() { return null; } + * String taint() { return null; } + * void sink(Object o) { } + * + * public void test() { + * Object s = source(); + * sink(s); //$hasValueFlow + * String t = "foo" + taint(); + * sink(t); //$hasTaintFlow + * } + * + * } + * ``` + * + * If you're not interested in a specific flow type, you can disable either value or taint flow expectations as follows: + * ```ql + * class HasFlowTest extends InlineFlowTest { + * override DataFlow::Configuration getTaintFlowConfig() { none() } + * + * override DataFlow::Configuration getValueFlowConfig() { none() } + * } + * ``` + * + * If you need more fine-grained tuning, consider implementing a test using `InlineExpectationsTest`. + */ + +import go +import TestUtilities.InlineExpectationsTest + +private predicate defaultSource(DataFlow::Node source) { + exists(Function fn | fn.hasQualifiedName(_, ["source", "taint"]) | + source = fn.getACall().getResult() + ) +} + +class DefaultValueFlowConf extends DataFlow::Configuration { + DefaultValueFlowConf() { this = "qltest:defaultValueFlowConf" } + + override predicate isSource(DataFlow::Node source) { defaultSource(source) } + + override predicate isSink(DataFlow::Node sink) { + exists(Function fn | fn.hasQualifiedName(_, "sink") | sink = fn.getACall().getAnArgument()) + } + + override int fieldFlowBranchLimit() { result = 1000 } +} + +class DefaultTaintFlowConf extends TaintTracking::Configuration { + DefaultTaintFlowConf() { this = "qltest:defaultTaintFlowConf" } + + override predicate isSource(DataFlow::Node source) { defaultSource(source) } + + override predicate isSink(DataFlow::Node sink) { + exists(Function fn | fn.hasQualifiedName(_, "sink") | sink = fn.getACall().getAnArgument()) + } + + override int fieldFlowBranchLimit() { result = 1000 } +} + +class InlineFlowTest extends InlineExpectationsTest { + InlineFlowTest() { this = "HasFlowTest" } + + override string getARelevantTag() { result = ["hasValueFlow", "hasTaintFlow"] } + + override predicate hasActualResult(string file, int line, string element, string tag, string value) { + tag = "hasValueFlow" and + exists(DataFlow::Node src, DataFlow::Node sink | getValueFlowConfig().hasFlow(src, sink) | + sink.hasLocationInfo(file, line, _, _, _) and + element = sink.toString() and + value = "\"" + sink.toString() + "\"" + ) + or + tag = "hasTaintFlow" and + exists(DataFlow::Node src, DataFlow::Node sink | + getTaintFlowConfig().hasFlow(src, sink) and not getValueFlowConfig().hasFlow(src, sink) + | + sink.hasLocationInfo(file, line, _, _, _) and + element = sink.toString() and + value = "\"" + sink.toString() + "\"" + ) + } + + DataFlow::Configuration getValueFlowConfig() { result = any(DefaultValueFlowConf config) } + + DataFlow::Configuration getTaintFlowConfig() { result = any(DefaultTaintFlowConf config) } +}