/** * Test-case generator for flow summaries. See the accompanying `GenerateFlowTestCase.py` for full * documentation and usage information. */ import java private import semmle.code.java.dataflow.internal.DataFlowUtil private import semmle.code.java.dataflow.ExternalFlow private import semmle.code.java.dataflow.FlowSummary private import semmle.code.java.dataflow.internal.FlowSummaryImpl import FlowTestCase private import FlowTestCaseSupportMethods private import FlowTestCaseUtils /** * Gets a CSV row for which a test has been requested, and where there exist a summary, but * nonetheless we can't generate a test case for it, indicating we cannot resolve either the callable * spec or an input or output spec. */ query string getAParseFailure(string reason) { any(TargetSummaryModelCsv target).row(result) and summaryModelRow(_, _, _, _, _, _, _, _, _, _, result) and ( not summaryModelRow(_, _, _, _, _, _, _, _, _, _, result) and reason = "row could not be parsed" or exists( string namespace, string type, boolean subtypes, string name, string signature, string ext | summaryModelRow(namespace, type, subtypes, name, signature, ext, _, _, _, _, result) and not interpretElement(namespace, type, subtypes, name, signature, ext) instanceof Callable and reason = "callable could not be resolved" ) or exists(string inputSpec | summaryModelRow(_, _, _, _, _, _, inputSpec, _, _, _, result) and not Private::External::interpretSpec(inputSpec, _) and reason = "input spec could not be parsed" ) or exists(string outputSpec | summaryModelRow(_, _, _, _, _, _, _, outputSpec, _, _, result) and not Private::External::interpretSpec(outputSpec, _) and reason = "output spec could not be parsed" ) ) } /** * Gets a CSV row for which a test was requested and was correctly parsed, * but for which no test case could be generated due to a limitation of the query. */ query string noTestCaseGenerated() { any(TargetSummaryModelCsv target).row(result) and summaryModelRow(_, _, _, _, _, _, _, _, _, _, result) and not exists(getAParseFailure(_)) and not exists(any(TestCase tc).getATestSnippetForRow(result)) } /** * Gets a valid test case, i.e. one that has a test snippet. */ TestCase getAValidTestCase() { exists(result.getATestSnippetForRow(_)) } /** * Returns an import statement to include in the test case header. */ string getAnImportStatement() { exists(RefType t | t = getAValidTestCase().getADesiredImport() and isImportable(t) and t.getPackage().getName() != "java.lang" | result = "import " + t.getPackage().getName() + "." + t.getName() + ";" ) } /** * Returns a support method to include in the generated test class. */ SupportMethod getASupportMethod() { result instanceof SourceMethod or result instanceof SinkMethod or result = getAValidTestCase().getASupportMethod() } /** * Returns a data extension specification of the taint-/value-propagation behavior of a test support method (`get` or `newWith` method). */ query string getASupportMethodModel() { result = getASupportMethod().getDataExtensionModel() } /** * Gets a Java file body testing all requested Models as Data rows against whatever classes and methods they resolve against. */ query string getTestCase() { result = "package generatedtest;\n\n" + concat(getAnImportStatement() + "\n") + "\n// Test case generated by GenerateFlowTestCase.ql\npublic class Test {\n\n" + concat("\t" + getASupportMethod().getDefinition() + "\n") + "\n\tpublic void test() throws Exception {\n\n" + concat(string row, string snippet | snippet = any(TestCase tc).getATestSnippetForRow(row) | snippet order by row ) + "\n\t}\n\n}" }