Files
codeql/java/ql/src/utils/flowtestcasegenerator/GenerateFlowTestCase.qll
2022-04-05 14:25:34 +02:00

107 lines
3.7 KiB
Plaintext

/**
* 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 `SummaryModelCsv.row` does hold, 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
any(SummaryModelCsv model).row(result) and
(
not summaryModel(_, _, _, _, _, _, _, _, _, _, result) and
reason = "row could not be parsed"
or
exists(
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
summaryModel(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 |
summaryModel(_, _, _, _, _, _, inputSpec, _, _, _, result) and
not Private::External::interpretSpec(inputSpec, _) and
reason = "input spec could not be parsed"
)
or
exists(string outputSpec |
summaryModel(_, _, _, _, _, _, _, 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
any(SummaryModelCsv model).row(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 CSV specification of the taint-/value-propagation behavior of a test support method (`get` or `newWith` method).
*/
query string getASupportMethodModel() { result = getASupportMethod().getCsvModel() }
/**
* Gets a Java file body testing all requested CSV 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}"
}