Add miscellaneous tests
This PR adds some more miscellaneous tests for more code coverage.
MODIFY
src/test/java/de/fraunhofer/aisec/codyze/legacy/analysis/markevaluation/ExpressionEvaluatorTest.kt
src/test/java/de/fraunhofer/aisec/codyze/legacy/analysis/markevaluation/ExpressionEvaluatorTest.kt
@@ -94,13 +94,14 @@ class ExpressionEvaluatorTest : AbstractTest() {
// directly use the xtext classes
val convertedMark = MarkModelLoader().load(mapOf("" to model))
assertNotNull(convertedMark)
-
- val convertedRule = convertedMark.rules.first()
- assertNotNull(convertedRule)
+ assertNotNull(convertedMark.rules)
val convertedEntity = convertedMark.getEntity("MyClass")
assertNotNull(convertedEntity)
+ for (convertedRule in convertedMark.rules) {
+ assertNotNull(convertedRule)
+
val eval =
ExpressionEvaluator(
graph,
@@ -117,7 +118,38 @@ class ExpressionEvaluatorTest : AbstractTest() {
val ctx = result[0]
assertNotNull(ctx)
- assertEquals(ConstantValue.of(true), ctx)
+ assertEquals(
+ ConstantValue.of(true),
+ ctx,
+ "${convertedRule.name} was not evaluated to true"
+ )
+ }
+ }
+
+ @Test
+ fun nullExpressionTest() {
+ val evaluator = Evaluator(mark, ServerConfiguration.builder().build())
+
+ // Let's provide the list of entities and nodes, this is normally done by
+ // findInstancesForEntities. In this case, we simply point it towards our variable
+ // declaration
+ val entities = listOf(listOf(Pair("a", a)))
+
+ val markContextHolder = evaluator.createMarkContext(entities)
+ assertEquals(1, markContextHolder.allContexts.size)
+
+ val eval =
+ ExpressionEvaluator(
+ graph,
+ null,
+ null,
+ null,
+ ServerConfiguration.builder().build(),
+ markContextHolder
+ )
+
+ val result = eval.evaluateExpression(null)
+ assertEquals(markContextHolder.generateNullResult(), result)
}
companion object {
@@ -181,7 +213,18 @@ class ExpressionEvaluatorTest : AbstractTest() {
graph = result.graph
var myEntity: EntityDeclaration? = null
- var myRule: RuleDeclaration? = null
+ var eqRule: RuleDeclaration? = null
+ var multRule: RuleDeclaration? = null
+ var divRule: RuleDeclaration? = null
+ var modRule: RuleDeclaration? = null
+ var leftShiftRule: RuleDeclaration? = null
+ var rightShiftRule: RuleDeclaration? = null
+ var bitwiseAndRule: RuleDeclaration? = null
+ var bitwiseOrRule: RuleDeclaration? = null
+ var plusSignRule: RuleDeclaration? = null
+ var minusSignRule: RuleDeclaration? = null
+ var logicalNotRule: RuleDeclaration? = null
+ var bitwiseComplementRule: RuleDeclaration? = null
model = mark {
myEntity =
@@ -189,18 +232,174 @@ class ExpressionEvaluatorTest : AbstractTest() {
variable("field")
op("init") { stmt { call("MyClass") { param("field") } } }
}
- myRule =
- rule("myRule") {
+ eqRule =
+ rule("eqRule") {
statement {
using(myEntity!!, "a")
ensure {
+ // comparison is: a == 1
+ exp =
comparison(left = operand("a.field"), op = "==", right = lit(1))
}
}
}
+ multRule =
+ rule("multRule") {
+ statement {
+ using(myEntity!!, "a")
+ ensure {
+ // comparison is: a == 1 * 1
+ exp =
+ comparison(
+ left = operand("a.field"),
+ op = "==",
+ right = mul(left = lit(1), op = "*", right = lit(1))
+ )
+ }
+ }
+ }
+ divRule =
+ rule("divRule") {
+ statement {
+ using(myEntity!!, "a")
+ ensure {
+ // comparison is: a == 2 / 2
+ exp =
+ comparison(
+ left = operand("a.field"),
+ op = "==",
+ right = mul(left = lit(2), op = "/", right = lit(2))
+ )
+ }
+ }
+ }
+ modRule =
+ rule("modRule") {
+ statement {
+ using(myEntity!!, "a")
+ ensure {
+ // comparison is: a == 19 % 6
+ exp =
+ comparison(
+ left = operand("a.field"),
+ op = "==",
+ right = mul(left = lit(19), op = "%", right = lit(6))
+ )
+ }
+ }
+ }
+ leftShiftRule =
+ rule("leftShiftRule") {
+ statement {
+ ensure {
+ // comparison is: 32 == 2 << 4
+ exp =
+ comparison(
+ left = lit(32),
+ op = "==",
+ right = mul(left = lit(2), op = "<<", right = lit(4))
+ )
+ }
+ }
+ }
+ rightShiftRule =
+ rule("rightShiftRule") {
+ statement {
+ ensure {
+ // comparison is: 4 == 32 >> 3
+ exp =
+ comparison(
+ left = lit(4),
+ op = "==",
+ right = mul(left = lit(32), op = ">>", right = lit(3))
+ )
+ }
+ }
+ }
+ bitwiseAndRule =
+ rule("bitwiseAndRule") {
+ statement {
+ ensure {
+ // comparison is: 2 == 11 & 6 (0b10 == 0b1011 & 0b110)
+ exp =
+ comparison(
+ left = lit(2),
+ op = "==",
+ right = mul(left = lit(11), op = "&", right = lit(6))
+ )
+ }
+ }
+ }
+
+ bitwiseOrRule =
+ rule("bitwiseOrRule") {
+ statement {
+ ensure {
+ // comparison is: 5 == 7 & 2 (0b101 == 0b111 & 0b10)
+ exp =
+ comparison(
+ left = lit(5),
+ op = "==",
+ right = mul(left = lit(7), op = "&^", right = lit(2))
+ )
+ }
+ }
+ }
+ plusSignRule =
+ rule("plusSignRule") {
+ statement {
+ ensure {
+ // comparison is: 5 == +5
+ exp =
+ comparison(
+ left = lit(5),
+ op = "==",
+ right = unary(exp = lit(5), op = "+")
+ )
+ }
+ }
+ }
+ minusSignRule =
+ rule("minusSignRule") {
+ statement {
+ ensure {
+ // comparison is: 5 != -(5)
+ exp =
+ comparison(
+ left = lit(5),
+ op = "!=",
+ right = unary(exp = lit(5), op = "-")
+ )
+ }
+ }
+ }
+ logicalNotRule =
+ rule("logicalNotRule") {
+ statement {
+ ensure {
+ // comparison is: true == !false
+ exp =
+ comparison(
+ left = lit(true),
+ op = "==",
+ right = unary(exp = lit(false), op = "!")
+ )
+ }
+ }
+ }
}
- assertNotNull(myRule)
+ assertNotNull(eqRule)
+ assertNotNull(multRule)
+ assertNotNull(divRule)
+ assertNotNull(modRule)
+ assertNotNull(leftShiftRule)
+ assertNotNull(rightShiftRule)
+ assertNotNull(bitwiseAndRule)
+ assertNotNull(bitwiseOrRule)
+ assertNotNull(plusSignRule)
+ assertNotNull(minusSignRule)
+ assertNotNull(logicalNotRule)
assertNotNull(myEntity)
mark = MarkModelLoader().load(mapOf("" to model))

MODIFY
src/test/java/de/fraunhofer/aisec/codyze/legacy/crymlin/AbstractTest.kt
src/test/java/de/fraunhofer/aisec/codyze/legacy/crymlin/AbstractTest.kt
package de.fraunhofer.aisec.codyze.legacy.crymlin
+import de.fraunhofer.aisec.codyze.legacy.config.CodyzeConfiguration
+import de.fraunhofer.aisec.codyze.legacy.config.Configuration
+import de.fraunhofer.aisec.codyze.legacy.config.CpgConfiguration
import de.fraunhofer.aisec.cpg.TranslationConfiguration
import de.fraunhofer.aisec.cpg.TranslationManager
import de.fraunhofer.aisec.cpg.passes.EdgeCachePass
@@ -12,7 +15,7 @@ abstract class AbstractTest {
* Helper method for initializing an Analysis Run.
*
* @param sourceLocations
- * @return
+ * @return TranslationManager
*/
@JvmStatic
protected fun newAnalysisRun(vararg sourceLocations: File): TranslationManager {
@@ -30,5 +33,23 @@ abstract class AbstractTest {
)
.build()
}
+
+ /**
+ * Helper method for initializing an Analysis Run. Returned Configuration can be used to
+ * initialize AnalysisServer and start the analysis with {@link
+ * de.fraunhofer.aisec.codyze.legacy.analysis.AnalysisServer#analyze(String url)}.
+ *
+ * @param codyzeConfig
+ * @return Configuration
+ */
+ @JvmStatic
+ protected fun newAnalysisRun(codyzeConfig: CodyzeConfiguration): Configuration {
+ val cpgConfig = CpgConfiguration()
+ cpgConfig.debugParser = true
+ cpgConfig.failOnError = false
+ cpgConfig.defaultPasses = true
+ cpgConfig.passes = listOf(IdentifierPass(), EdgeCachePass())
+ return Configuration(codyzeConfig, cpgConfig)
+ }
}
}

ADD
src/test/java/de/fraunhofer/aisec/codyze/legacy/crymlin/AnalysisServerWithFileTest.kt
+package de.fraunhofer.aisec.codyze.legacy.crymlin
+
+import de.fraunhofer.aisec.codyze.legacy.analysis.AnalysisContext
+import de.fraunhofer.aisec.codyze.legacy.analysis.AnalysisServer
+import de.fraunhofer.aisec.codyze.legacy.config.CodyzeConfiguration
+import java.io.*
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.TimeoutException
+import java.util.function.Consumer
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import org.junit.jupiter.api.*
+
+internal class AnalysisServerWithFileTest : AbstractTest() {
+ @Test
+ fun markModelTest() {
+ val markModel = server.markModel
+ assertNotNull(markModel)
+
+ val rules = markModel.rules
+ assertEquals(7, rules.size)
+
+ val entities = markModel.entities
+ assertEquals(9, entities.size)
+ }
+
+ @Test
+ fun markEvaluationTest() {
+ val ctx = result
+ assertNotNull(ctx)
+
+ val findings: MutableList<String> = ArrayList()
+ assertNotNull(ctx.findings)
+
+ ctx.findings.forEach(Consumer { findings.add(it.toString()) })
+
+ println("Findings")
+ for (finding in findings) {
+ println(finding)
+ }
+ }
+
+ companion object {
+ private lateinit var server: AnalysisServer
+ private var result: AnalysisContext? = null
+ @BeforeAll
+ @Throws(Exception::class)
+ @JvmStatic
+ fun startup() {
+ val classLoader = AnalysisServerBotanTest::class.java.classLoader
+ var resource = classLoader.getResource("legacy/mark_cpp/symm_block_cipher.cpp")
+ assertNotNull(resource)
+ val cppFile = File(resource.file)
+ assertNotNull(cppFile)
+ resource = classLoader.getResource("legacy/mark/PoC_MS1/Botan_AutoSeededRNG.mark")
+ assertNotNull(resource)
+ val markPoC1 = File(resource.file)
+ assertNotNull(markPoC1)
+ val markModelFiles = markPoC1.parent
+
+ // Start an analysis server
+ val codyze = CodyzeConfiguration()
+ codyze.mark = arrayOf(File(markModelFiles))
+
+ val config = newAnalysisRun(codyze)
+ config.executionMode.isCli = false
+ config.executionMode.isLsp = false
+
+ server = AnalysisServer(config)
+ server.start()
+
+ // Start the analysis (BOTAN Symmetric Example by Oliver)
+ val analyze = server.analyze(cppFile.absolutePath)
+ try {
+ result = analyze[5, TimeUnit.MINUTES]
+ } catch (t: TimeoutException) {
+ analyze.cancel(true)
+ throw t
+ }
+ }
+
+ @AfterAll
+ fun teardown() {
+ // Stop the analysis server
+ server.stop()
+ }
+ }
+}

ADD
src/test/java/de/fraunhofer/aisec/codyze/legacy/crymlin/EvaluationHelperTest.kt
+package de.fraunhofer.aisec.codyze.legacy.crymlin
+
+import de.fraunhofer.aisec.codyze.legacy.analysis.markevaluation.followNextDFG
+import de.fraunhofer.aisec.cpg.graph.Node
+import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation
+import de.fraunhofer.aisec.cpg.sarif.Region
+import java.net.URI
+import kotlin.test.*
+import org.junit.jupiter.api.Test
+
+class EvaluationHelperTest {
+ @Test
+ fun followNextDFGTest() {
+ val node = Node()
+ node.id = 0
+ node.location = PhysicalLocation(URI("0"), Region(0, 0, 0, 0))
+
+ val expectedResult = mutableListOf<Node>()
+ var current = node
+ for (i in 1..4) {
+ val next = Node()
+ next.id = i.toLong()
+ next.location = PhysicalLocation(URI("$i"), Region(i, i, i, i))
+ if (i <= 3) expectedResult.add(next)
+
+ current.addNextDFG(next)
+ current = next
+ }
+
+ val result = node.followNextDFG { n -> n.id == 3.toLong() }
+ assertNotNull(result)
+ assertEquals(expectedResult.size, result.size, "Size was not as expected")
+ for (n in expectedResult) {
+ assertContains(result, n, "Did not contain node $n")
+ }
+ }
+}

MODIFY
src/test/java/de/fraunhofer/aisec/codyze/legacy/crymlin/ExpressionHelperTest.kt
src/test/java/de/fraunhofer/aisec/codyze/legacy/crymlin/ExpressionHelperTest.kt
package de.fraunhofer.aisec.codyze.legacy.crymlin
import de.fraunhofer.aisec.codyze.legacy.analysis.markevaluation.ExpressionHelper
+import de.fraunhofer.aisec.mark.markDsl.impl.*
+import kotlin.test.assertContains
import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
import org.junit.jupiter.api.*
internal class ExpressionHelperTest {
@@ -28,11 +32,96 @@ internal class ExpressionHelperTest {
)
}
+ @Test
+ fun testComparableFloat() {
+ assertEquals("0.0", ExpressionHelper.toComparableString(0.toFloat()))
+ assertEquals("0.0", ExpressionHelper.toComparableString(0.0f))
+ assertEquals("1.2", ExpressionHelper.toComparableString(1.2f))
+ assertEquals("-1.2", ExpressionHelper.toComparableString(-1.2f))
+ assertEquals("1.4E-45", ExpressionHelper.toComparableString(Float.MIN_VALUE))
+ assertEquals("3.4028235E38", ExpressionHelper.toComparableString(Float.MAX_VALUE))
+ }
+
@Test
fun testComparableString() {
assertEquals("", ExpressionHelper.toComparableString(""))
assertEquals("a", ExpressionHelper.toComparableString("a"))
assertEquals("1.0", ExpressionHelper.toComparableString("1"))
assertEquals("-1.0", ExpressionHelper.toComparableString("-1"))
+ assertEquals("a", ExpressionHelper.toComparableString("\"a"))
+
+ assertEquals("ab", ExpressionHelper.toComparableString("ab\""))
+ assertEquals("abc", ExpressionHelper.toComparableString("\"abc\""))
+
+ assertEquals("4.0", ExpressionHelper.toComparableString(4.toByte()))
+ assertEquals("a", ExpressionHelper.toComparableString('a'))
+ }
+
+ @Test
+ fun testComparableStringObject() {
+ // uses String implementation -> NullPointerException
+ // assertEquals("", ExpressionHelper.toComparableString(null))
+
+ val nullObject: kotlin.reflect.jvm.internal.impl.load.kotlin.JvmType.Object? = null
+ assertEquals("", ExpressionHelper.toComparableString(nullObject))
+ }
+
+ @Test
+ fun testCollectVars() {
+ val vars = mutableSetOf<String>()
+ val expr =
+ funcCall(
+ "function",
+ logicalAnd(
+ left =
+ comparison(
+ left = mul(left = operand("b"), op = "*", right = lit(1)),
+ op = "==",
+ right = operand("a")
+ ),
+ op = "&&",
+ right =
+ logicalOr(
+ left =
+ comparison(
+ left = operand("a"),
+ op = "==",
+ right = unary(exp = operand("c"), op = "-")
+ ),
+ op = "||",
+ right = comparison(left = lit(1), op = "==", right = lit(1))
+ )
+ ),
+ litList(lit(1), lit("s"), lit("1"))
+ )
+ assertNotNull(expr)
+
+ val expectedVars = setOf("a", "b", "c")
+
+ ExpressionHelper.collectVars(expr, vars)
+ assertEquals(expectedVars.size, vars.size, "Size of vars set was not equal")
+ for (variable in expectedVars) {
+ assertContains(vars, variable, "\"$variable\" was not in vars set")
+ }
+ }
+
+ @Test
+ fun testAsString() {
+ assertNull(ExpressionHelper.asString(null))
+ assertEquals("myString", ExpressionHelper.asString("myString"))
+ }
+
+ @Test
+ fun testAsNumber() {
+ assertNull(ExpressionHelper.asNumber(null))
+ assertEquals(1, ExpressionHelper.asNumber(1))
+ assertNull(ExpressionHelper.asNumber("myString"))
+ }
+
+ @Test
+ fun testAsBoolean() {
+ assertNull(ExpressionHelper.asBoolean(null))
+ assertEquals(true, ExpressionHelper.asBoolean(true))
+ assertNull(ExpressionHelper.asBoolean("true"))
}
}

ADD
src/test/java/de/fraunhofer/aisec/codyze/legacy/crymlin/UtilsTest.kt
+package de.fraunhofer.aisec.codyze.legacy.crymlin
+
+import de.fraunhofer.aisec.codyze.legacy.analysis.utils.Utils
+import de.fraunhofer.aisec.cpg.graph.Node
+import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation
+import de.fraunhofer.aisec.cpg.sarif.Region
+import java.net.URI
+import kotlin.test.*
+import org.junit.jupiter.api.Test
+
+internal class UtilsTest {
+ @Test
+ fun getRegionTest() {
+ val node = Node()
+ val resultNoLocation = Utils.getRegion(node)
+ assertEquals(-1, resultNoLocation.endColumn, "endColumn was not equal")
+ assertEquals(-1, resultNoLocation.startColumn, "startColumn was not equal")
+ assertEquals(-1, resultNoLocation.endLine, "endLine was not equal")
+ assertEquals(-1, resultNoLocation.startLine, "startLine was not equal")
+
+ val region = Region(0, 0, 0, 0)
+ node.location = PhysicalLocation(URI("0"), region)
+ val resultWithLocation = Utils.getRegion(node)
+ assertEquals(0, resultWithLocation.endColumn, "endColumn was not equal")
+ assertEquals(0, resultWithLocation.startColumn, "startColumn was not equal")
+ assertEquals(0, resultWithLocation.endLine, "endLine was not equal")
+ assertEquals(0, resultWithLocation.startLine, "startLine was not equal")
+ }
+
+ @Test
+ fun stripQuotedCharacterTest() {
+ assertEquals("s", Utils.stripQuotedCharacter("\'s\'"))
+ assertEquals("s\'", Utils.stripQuotedCharacter("s\'"))
+ assertEquals("\'s", Utils.stripQuotedCharacter("\'s"))
+ assertEquals("s", Utils.stripQuotedCharacter("s"))
+ }
+
+ @Test
+ fun extractMethodNameTest() {
+ assertEquals("cppMethod", Utils.extractMethodName("CppClass::cppMethod"))
+ assertEquals("method", Utils.extractMethodName("class->method"))
+ assertEquals("javaMethod", Utils.extractMethodName("package.JavaClass.javaMethod"))
+ assertEquals("typo:method", Utils.extractMethodName("typo:method"))
+ }
+}

MODIFY
src/test/java/de/fraunhofer/aisec/mark/markDsl/impl/Expressions.kt
src/test/java/de/fraunhofer/aisec/mark/markDsl/impl/Expressions.kt
@@ -2,16 +2,42 @@ package de.fraunhofer.aisec.mark.markDsl.impl
import de.fraunhofer.aisec.mark.markDsl.*
-fun EnsureStatement.comparison(
- left: Expression,
- right: Expression,
- op: String
-): ComparisonExpression {
+fun comparison(left: Expression, right: Expression, op: String): ComparisonExpression {
val expression = ComparisonExpressionImpl()
expression.left = left
expression.right = right
expression.op = op
- this.exp = expression
+ return expression
+}
+
+fun mul(left: Expression, right: Expression, op: String): MultiplicationExpression {
+ val expression = MultiplicationExpressionImpl()
+ expression.left = left
+ expression.right = right
+ expression.op = op
+ return expression
+}
+
+fun unary(exp: Expression, op: String): UnaryExpression {
+ val expression = UnaryExpressionImpl()
+ expression.exp = exp
+ expression.op = op
+ return expression
+}
+
+fun logicalAnd(left: Expression, right: Expression, op: String): LogicalAndExpression {
+ val expression = LogicalAndExpressionImpl()
+ expression.left = left
+ expression.right = right
+ expression.op = op
+ return expression
+}
+
+fun logicalOr(left: Expression, right: Expression, op: String): LogicalOrExpression {
+ val expression = LogicalOrExpressionImpl()
+ expression.left = left
+ expression.right = right
+ expression.op = op
return expression
}
@@ -29,6 +55,25 @@ fun lit(value: String): Literal {
fun lit(value: Int): Literal {
val expression = IntegerLiteralImpl()
+ expression.value = "0x${Integer.toHexString(value)}"
+ return expression
+}
+
+fun lit(value: Boolean): Literal {
+ val expression = BooleanLiteralImpl()
expression.value = value.toString()
return expression
}
+
+fun litList(vararg values: Literal): LiteralListExpression {
+ val expression = LiteralListExpressionImpl()
+ expression.getValues().addAll(values)
+ return expression
+}
+
+fun funcCall(name: String, vararg args: Argument): FunctionCallExpression {
+ val expression = FunctionCallExpressionImpl()
+ expression.getArgs().addAll(args)
+ expression.name = name
+ return expression
+}

Add miscellaneous tests (#471)

This PR adds some more miscellaneous tests for more code coverage.

Co-authored-by: Wendland, Florian <florian.wendland@aisec.fraunhofer.de>
Co-authored-by: Maximilian Kaul <maximilian.kaul@aisec.fraunhofer.de>