Add config normalizing
This PR adds a normalize function to the Configuration class. The function handles all interactions between configurations that cannot be specified through PicoCLI and the configuration file.
MODIFY
src/main/java/de/fraunhofer/aisec/codyze/ManifestVersionProvider.java
src/main/java/de/fraunhofer/aisec/codyze/ManifestVersionProvider.java
+
package de.fraunhofer.aisec.codyze;
import picocli.CommandLine;

MODIFY
src/main/java/de/fraunhofer/aisec/codyze/analysis/markevaluation/CodyzeDFAOrderEvaluator.kt
src/main/java/de/fraunhofer/aisec/codyze/analysis/markevaluation/CodyzeDFAOrderEvaluator.kt
@@ -12,8 +12,8 @@ import de.fraunhofer.aisec.cpg.graph.Node
import java.io.File
/**
- * Codyze-specific implementation of the [DFAOrderEvaluator].
- * Its main purpose is to collect the findings in case of violations to the order.
+ * Codyze-specific implementation of the [DFAOrderEvaluator]. Its main purpose is to collect the
+ * findings in case of violations to the order.
*/
class CodyzeDFAOrderEvaluator(
referencedVertices: Set<Long>,
@@ -25,8 +25,8 @@ class CodyzeDFAOrderEvaluator(
) : DFAOrderEvaluator(referencedVertices, nodesToOp, thisPositionOfNode) {
/**
- * Collects the finding in the AnalysisContext as the [node] makes an
- * operation which violates the desired order.
+ * Collects the finding in the AnalysisContext as the [node] makes an operation which violates
+ * the desired order.
*/
override fun actionMissingTransitionForNode(node: Node, fsm: DFA) {
val region = Utils.getRegionByNode(node)
@@ -64,9 +64,8 @@ class CodyzeDFAOrderEvaluator(
}
/**
- * Collects the finding in the AnalysisContext because the DFA finished
- * analyzing the function but the [base] did not terminate in an accepting
- * state (i.e., some operations are missing).
+ * Collects the finding in the AnalysisContext because the DFA finished analyzing the function
+ * but the [base] did not terminate in an accepting state (i.e., some operations are missing).
*/
override fun actionNonAcceptingTermination(base: String, fsm: DFA) {
if (fsm.executionTrace.size == 1)

MODIFY
src/main/java/de/fraunhofer/aisec/codyze/analysis/markevaluation/OrderEvaluation.kt
src/main/java/de/fraunhofer/aisec/codyze/analysis/markevaluation/OrderEvaluation.kt
@@ -16,9 +16,8 @@ class OrderEvaluation {
private val log = LoggerFactory.getLogger(OrderEvaluation::class.java)
/**
- * Collects the mark instances of the [orderExpression], maps CPG nodes
- * to the string representation of the MARK-Operators and starts the order
- * evaluation.
+ * Collects the mark instances of the [orderExpression], maps CPG nodes to the string
+ * representation of the MARK-Operators and starts the order evaluation.
*/
fun evaluateDFA(
rule: MRule,

MODIFY
src/main/java/de/fraunhofer/aisec/codyze/config/Configuration.kt
src/main/java/de/fraunhofer/aisec/codyze/config/Configuration.kt
@@ -69,46 +69,22 @@ class Configuration {
this.cpg = cpgConfiguration
}
- // Parse CLI arguments into config class
- private fun parseCLI(vararg args: String?) {
-
- CommandLine(this)
- // Added as Mixin so the already initialized objects are used instead of new ones
- // created
- .addMixin("codyze", codyze)
- .addMixin("cpg", cpg)
- .addMixin("analysis", codyze.analysis)
- .addMixin("translation", cpg.translation)
- .registerConverter(Pass::class.java, PassTypeConverter())
- .setCaseInsensitiveEnumValuesAllowed(true)
- // setUnmatchedArgumentsAllowed is true because both classes don't have the config path
- // option which would result in exceptions, side effect is that all unknown options are
- // ignored
- .setUnmatchedArgumentsAllowed(true)
- .parseArgs(*args)
- }
-
/**
* Builds ServerConfiguration object with available configurations
*
* @return ServerConfiguration
*/
fun buildServerConfiguration(): ServerConfiguration {
+ this.normalize()
val config =
ServerConfiguration.builder()
.launchLsp(executionMode.isLsp)
.launchConsole(executionMode.isTui)
.typestateAnalysis(codyze.analysis.tsMode)
- .disableGoodFindings(
- if (codyze.pedantic) {
- false
- } else {
- codyze.noGoodFindings
- }
- )
+ .disableGoodFindings(codyze.noGoodFindings)
.markFiles(*codyze.mark.map { m -> m.absolutePath }.toTypedArray())
+ .pedantic(codyze.pedantic)
- if (!codyze.pedantic) {
val disabledRulesMap = mutableMapOf<String, DisabledMarkRulesValue>()
for (mName in codyze.disabledMarkRules) {
val index = mName.lastIndexOf('.')
@@ -116,14 +92,12 @@ class Configuration {
val markName = mName.subSequence(index + 1, mName.length).toString()
if (markName.isNotEmpty()) {
disabledRulesMap.putIfAbsent(packageName, DisabledMarkRulesValue())
- if (markName == "*")
- disabledRulesMap.getValue(packageName).isDisablePackage = true
+ if (markName == "*") disabledRulesMap.getValue(packageName).isDisablePackage = true
else disabledRulesMap[packageName]?.disabledMarkRuleNames?.add(markName)
} else
log.warn(
"Error while parsing disabled-mark-rules: \'$mName\' is not a valid name for a mark rule. Continue parsing disabled-mark-rules"
)
- }
config.disableMark(disabledRulesMap)
}
@@ -136,13 +110,13 @@ class Configuration {
* @return TranslationConfiguration
*/
fun buildTranslationConfiguration(vararg sources: File): TranslationConfiguration {
+ this.normalize()
val translationConfig =
TranslationConfiguration.builder()
.debugParser(if (executionMode.isLsp) false else cpg.debugParser)
.failOnError(cpg.failOnError)
.codeInNodes(cpg.codeInNodes)
- // we need to force load includes for unity builds, otherwise nothing will be parsed
- .loadIncludes(cpg.translation.analyzeIncludes || cpg.useUnityBuild)
+ .loadIncludes(cpg.translation.analyzeIncludes)
.useUnityBuild(cpg.useUnityBuild)
.processAnnotations(cpg.processAnnotations)
.symbols(cpg.symbols)
@@ -205,6 +179,41 @@ class Configuration {
return translationConfig.build()
}
+ private fun normalize() {
+ // In pedantic analysis mode all MARK rules are analyzed and all findings reported
+ if (codyze.pedantic) {
+ codyze.noGoodFindings = false
+ codyze.disabledMarkRules = emptyList()
+ }
+
+ // we need to force load includes for unity builds, otherwise nothing will be parsed
+ if (cpg.useUnityBuild) cpg.translation.analyzeIncludes = true
+
+ if (executionMode.isLsp) {
+ // we don't want the parser to print to the terminal when in LSP mode
+ cpg.debugParser = false
+ }
+ }
+
+ // Parse CLI arguments into config class
+ private fun parseCLI(vararg args: String?) {
+
+ CommandLine(this)
+ // Added as Mixin so the already initialized objects are used instead of new ones
+ // created
+ .addMixin("codyze", codyze)
+ .addMixin("cpg", cpg)
+ .addMixin("analysis", codyze.analysis)
+ .addMixin("translation", cpg.translation)
+ .registerConverter(Pass::class.java, PassTypeConverter())
+ .setCaseInsensitiveEnumValuesAllowed(true)
+ // setUnmatchedArgumentsAllowed is true because both classes don't have the config path
+ // option which would result in exceptions, side effect is that all unknown options are
+ // ignored
+ .setUnmatchedArgumentsAllowed(true)
+ .parseArgs(*args)
+ }
+
companion object {
private val log = LoggerFactory.getLogger(Configuration::class.java)

MODIFY
src/main/java/de/fraunhofer/aisec/codyze/config/LanguageDeserializer.kt
src/main/java/de/fraunhofer/aisec/codyze/config/LanguageDeserializer.kt
@@ -9,7 +9,9 @@ import java.io.IOException
import java.util.*
import org.slf4j.LoggerFactory
-/** Custom deserializer to populate a list of languages derived from strings from the Jackson parser */
+/**
+ * Custom deserializer to populate a list of languages derived from strings from the Jackson parser
+ */
class LanguageDeserializer : StdDeserializer<EnumSet<Language>?>(null as JavaType?) {
@Throws(IOException::class)

ADD
src/test/java/de/fraunhofer/aisec/codyze/crymlin/BuildServerConfigTest.kt
+package de.fraunhofer.aisec.codyze.crymlin
+
+import de.fraunhofer.aisec.codyze.config.Configuration
+import kotlin.test.*
+import org.junit.jupiter.api.Test
+
+internal class BuildServerConfigTest {
+ @Test
+ fun pedanticNoGoodFindingsTest() {
+ val cliParameters = arrayOf("-c", "--pedantic", "--noGoodFindings")
+ val config = Configuration.initConfig(null, *cliParameters)
+ val serverConfig = config.buildServerConfiguration()
+
+ assertTrue(serverConfig.pedantic, "pedantic should be true but was false")
+ assertFalse(
+ serverConfig.disableGoodFindings,
+ "disableGoodFindings should be false because in pedantic mode all findings should be reported"
+ )
+ }
+
+ @Test
+ fun pedanticDisabledMarkRulesTest() {
+ val cliParameters =
+ arrayOf("-c", "--pedantic", "--disabled-mark-rules=java.UseValidAlgorithm")
+ val config = Configuration.initConfig(null, *cliParameters)
+ val serverConfig = config.buildServerConfiguration()
+
+ assertTrue(serverConfig.pedantic, "pedantic should be true but was false")
+ assertEquals(
+ 0,
+ serverConfig.packageToDisabledMarkRules.size,
+ "disableGoodFindings should be false because in pedantic mode all findings should be reported"
+ )
+ }
+}

MODIFY
src/test/java/de/fraunhofer/aisec/codyze/crymlin/BuildTranslationConfigTest.kt
src/test/java/de/fraunhofer/aisec/codyze/crymlin/BuildTranslationConfigTest.kt
@@ -120,6 +120,18 @@ internal class BuildTranslationConfigTest {
)
}
+ @Test
+ fun lspTest() {
+ val cliParameters = arrayOf("-l", "--debug-parser", "--source=test.java")
+ val config = Configuration.initConfig(null, *cliParameters)
+ val translationConfig = config.buildTranslationConfiguration()
+
+ assertFalse(
+ translationConfig.debugParser,
+ "DebugParser has to be set to false if in lsp mode"
+ )
+ }
+
companion object {
private lateinit var additionalOptionFile: File

Add config normalizing (#442)

This PR adds a normalize function to the Configuration class. The function handles all interactions between configurations that cannot be specified through PicoCLI and the configuration file.

Co-authored-by: Florian Wendland <florian.wendland@aisec.fraunhofer.de>