Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New ConceptPass based on tasks #2096

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,20 @@ import de.fraunhofer.aisec.cpg.graph.*
import de.fraunhofer.aisec.cpg.graph.concepts.config.Configuration
import de.fraunhofer.aisec.cpg.graph.concepts.config.ConfigurationGroup
import de.fraunhofer.aisec.cpg.graph.concepts.config.ConfigurationOption
import de.fraunhofer.aisec.cpg.graph.concepts.config.ConfigurationSource
import de.fraunhofer.aisec.cpg.graph.concepts.config.LoadConfiguration
import de.fraunhofer.aisec.cpg.graph.concepts.config.ReadConfigurationGroup
import de.fraunhofer.aisec.cpg.graph.concepts.config.ReadConfigurationOption
import de.fraunhofer.aisec.cpg.graph.concepts.config.RegisterConfigurationGroup
import de.fraunhofer.aisec.cpg.graph.concepts.config.RegisterConfigurationOption
import de.fraunhofer.aisec.cpg.graph.statements.expressions.SubscriptExpression
import de.fraunhofer.aisec.cpg.passes.concepts.config.ini.IniFileConfigurationPass
import de.fraunhofer.aisec.cpg.passes.concepts.config.python.PythonStdLibConfigurationPass
import de.fraunhofer.aisec.cpg.passes.ImportResolver
import de.fraunhofer.aisec.cpg.passes.PassConfiguration
import de.fraunhofer.aisec.cpg.passes.concepts.ConceptPass
import de.fraunhofer.aisec.cpg.passes.concepts.config.ProvideConfigTask
import de.fraunhofer.aisec.cpg.passes.concepts.config.ini.IniFileConfigurationSourceTask
import de.fraunhofer.aisec.cpg.passes.concepts.config.ini.IniFileImportTask
import de.fraunhofer.aisec.cpg.passes.concepts.config.python.PythonStdLibConfigurationTask
import de.fraunhofer.aisec.cpg.test.analyze
import java.io.File
import kotlin.test.Test
Expand All @@ -55,8 +61,16 @@ class ConfigurationPassTest {
analyze(listOf(), topLevel.toPath(), true) {
it.registerLanguage<PythonLanguage>()
it.registerLanguage<IniFileLanguage>()
it.registerPass<IniFileConfigurationPass>()
it.registerPass<PythonStdLibConfigurationPass>()
it.registerPass<ConceptPass>()
it.configurePass<ConceptPass>(
PassConfiguration()
.registerTask<IniFileConfigurationSourceTask>()
.registerTask<PythonStdLibConfigurationTask>()
.registerTask<ProvideConfigTask>()
)
it.configurePass<ImportResolver>(
PassConfiguration().registerTask<IniFileImportTask>()
)
it.softwareComponents(
mutableMapOf(
"conf" to listOf(topLevel.resolve("conf")),
Expand All @@ -76,6 +90,21 @@ class ConfigurationPassTest {

val conf = result.conceptNodes.filterIsInstance<Configuration>().singleOrNull()
assertNotNull(conf, "There should be a single configuration node")
assertEquals(11, conf.allOps.size, "There should be 21 overall ops in the configuration")

val confSources = result.conceptNodes.filterIsInstance<ConfigurationSource>()
assertNotNull(confSources, "There should be three configuration source nodes")
assertEquals(3, confSources.size, "There should be 2 configuration source nodes")

confSources
.filter { it.name.toString() != "unused.ini" }
.forEach {
assertEquals(
5,
it.allOps.size,
"There should be 5 overall ops in each configuration source (except for the unused one)",
)
}

val loadConfig = result.operationNodes.filterIsInstance<LoadConfiguration>().singleOrNull()
assertNotNull(loadConfig)
Expand All @@ -89,6 +118,11 @@ class ConfigurationPassTest {

val sslGroup = groups["ssl"]
assertNotNull(sslGroup)
assertEquals(
2,
sslGroup.ops.size,
"There should be two ops in the ssl group (register and read)",
)

val readGroupOps = result.operationNodes.filterIsInstance<ReadConfigurationGroup>()
assertEquals(
Expand Down Expand Up @@ -134,5 +168,13 @@ class ConfigurationPassTest {
val sslEnabled = result.refs["ssl_enabled"]
assertNotNull(sslEnabled)
assertEquals(setOf("true", "false"), sslEnabled.evaluate(MultiValueEvaluator()))

val unused = confSources["unused.ini"]
assertNotNull(unused)
assertEquals(0, unused.allOps.size, "There should be no ops in the unused configuration")

val verifySSL = unused.groups["DEFAULT"]?.options["verify_ssl"]
assertNotNull(verifySSL)
assertEquals(setOf("true"), verifySSL.evaluate(MultiValueEvaluator()))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ import de.fraunhofer.aisec.cpg.graph.concepts.memory.LoadLibrary
import de.fraunhofer.aisec.cpg.graph.concepts.memory.LoadSymbol
import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration
import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression
import de.fraunhofer.aisec.cpg.passes.concepts.memory.cxx.CXXDynamicLoadingPass
import de.fraunhofer.aisec.cpg.passes.ImportResolver
import de.fraunhofer.aisec.cpg.passes.PassConfiguration
import de.fraunhofer.aisec.cpg.passes.concepts.ConceptPass
import de.fraunhofer.aisec.cpg.passes.concepts.memory.cxx.CXXDynamicLoadingImportTask
import de.fraunhofer.aisec.cpg.passes.concepts.memory.cxx.CXXDynamicLoadingTask
import de.fraunhofer.aisec.cpg.test.analyze
import de.fraunhofer.aisec.cpg.test.assertInvokes
import java.io.File
Expand All @@ -50,7 +54,13 @@ class DynamicLoadingTest {
val result =
analyze(listOf(), topLevel.toPath(), true) {
it.registerLanguage<CLanguage>()
it.registerPass<CXXDynamicLoadingPass>()
it.registerPass<ConceptPass>()
it.configurePass<ConceptPass>(
PassConfiguration().registerTask<CXXDynamicLoadingTask>()
)
it.configurePass<ImportResolver>(
PassConfiguration().registerTask<CXXDynamicLoadingImportTask>()
)
it.softwareComponents(
mutableMapOf(
"main" to listOf(topLevel.resolve("main")),
Expand Down Expand Up @@ -120,7 +130,13 @@ class DynamicLoadingTest {
val result =
analyze(listOf(), topLevel.toPath(), true) {
it.registerLanguage<CLanguage>()
it.registerPass<CXXDynamicLoadingPass>()
it.registerPass<ConceptPass>()
it.configurePass<ConceptPass>(
PassConfiguration().registerTask<CXXDynamicLoadingTask>()
)
it.configurePass<ImportResolver>(
PassConfiguration().registerTask<CXXDynamicLoadingImportTask>()
)
it.softwareComponents(
mutableMapOf(
"winmain" to listOf(topLevel.resolve("winmain")),
Expand All @@ -146,6 +162,7 @@ class DynamicLoadingTest {
loadLibrary.what,
"\"what\" of the LoadLibrary should be the winexample component",
)
assertEquals(1, loadLibrary.entryPoints.size)
assertEquals(
dllMain,
loadLibrary.entryPoints.singleOrNull()?.underlyingNode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ import de.fraunhofer.aisec.cpg.graph.concepts.arch.POSIX
import de.fraunhofer.aisec.cpg.graph.concepts.arch.Win32
import de.fraunhofer.aisec.cpg.graph.concepts.flows.Main
import de.fraunhofer.aisec.cpg.graph.get
import de.fraunhofer.aisec.cpg.passes.concepts.flows.cxx.CXXEntryPointsPass
import de.fraunhofer.aisec.cpg.passes.PassConfiguration
import de.fraunhofer.aisec.cpg.passes.concepts.ConceptPass
import de.fraunhofer.aisec.cpg.passes.concepts.flows.cxx.CXXEntryPointTask
import de.fraunhofer.aisec.cpg.test.analyze
import java.io.File
import kotlin.test.assertIs
Expand All @@ -45,7 +47,8 @@ class EntryPointTest {
val result =
analyze(listOf(), topLevel.toPath(), true) {
it.registerLanguage<CLanguage>()
it.registerPass<CXXEntryPointsPass>()
it.registerPass<ConceptPass>()
it.configurePass<ConceptPass>(PassConfiguration().registerTask<CXXEntryPointTask>())
it.softwareComponents(
mutableMapOf(
"main" to listOf(topLevel.resolve("main")),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[DEFAULT]
verify_ssl = true
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import de.fraunhofer.aisec.cpg.graph.concepts.Concept
import de.fraunhofer.aisec.cpg.graph.concepts.Operation
import de.fraunhofer.aisec.cpg.graph.declarations.FieldDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression

/**
Expand All @@ -44,6 +45,17 @@ import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression
*/
class Configuration(underlyingNode: Node) : Concept(underlyingNode = underlyingNode) {
var groups: MutableList<ConfigurationGroup> = mutableListOf()

/**
* The individual operations that target parts of the configuration are assigned to
* [Concept.ops] of their respective concept. For example a [ReadConfigurationGroup] is part of
* the [ConfigurationGroup]'s ops. This property returns all operations of all groups and
* options as well as the ones targeting the complete configuration.
*/
val allOps: Set<Operation>
get() {
return ops + groups.flatMap { it.ops + it.options.flatMap { option -> option.ops } }
}
}

/**
Expand Down Expand Up @@ -110,10 +122,9 @@ class LoadConfiguration(
*/
class ReadConfigurationGroup(
underlyingNode: Node,
conf: Configuration,
/** The config group that is being read with this operation. */
var group: ConfigurationGroup,
) : ConfigurationOperation(underlyingNode = underlyingNode, concept = conf) {
) : ConfigurationOperation(underlyingNode = underlyingNode, concept = group) {
init {
name = group.name
}
Expand All @@ -125,10 +136,9 @@ class ReadConfigurationGroup(
*/
class ReadConfigurationOption(
underlyingNode: Node,
conf: Configuration,
/** The config option that is being read with this operation. */
var option: ConfigurationOption,
) : ConfigurationOperation(underlyingNode = underlyingNode, concept = conf) {
) : ConfigurationOperation(underlyingNode = underlyingNode, concept = option) {
init {
name = option.name
}
Expand All @@ -146,10 +156,9 @@ class ReadConfigurationOption(
*/
class RegisterConfigurationGroup(
underlyingNode: Node,
conf: Configuration,
/** The config group that is being registered with this operation. */
var group: ConfigurationGroup,
) : ConfigurationOperation(underlyingNode = underlyingNode, concept = conf) {
) : ConfigurationOperation(underlyingNode = underlyingNode, concept = group) {
init {
name = group.name
}
Expand All @@ -167,56 +176,107 @@ class RegisterConfigurationGroup(
*/
class RegisterConfigurationOption(
underlyingNode: Node,
conf: Configuration,
/** The config option that is being registered with this operation. */
var option: ConfigurationOption,
/** An optional default value of the option. */
var defaultValue: Node? = null,
) : ConfigurationOperation(underlyingNode = underlyingNode, concept = conf) {
) : ConfigurationOperation(underlyingNode = underlyingNode, concept = option) {
init {
name = option.name
}
}

/**
* Represents an operation to provide a [Configuration], e.g., in the form of a configuration file.
* Represents an operation to provide a [Configuration], e.g., in the form of a configuration file
* (through a [ConfigurationSource]).
*
* When the configuration file is loaded, a [LoadConfiguration] operation would be found in the code
* component (matching the configuration file's name in [LoadConfiguration.fileExpression]) and the
* [ProvideConfiguration] operation would be found in the configuration component.
*
* But also other sources of configuration could be represented by a [ProvideConfiguration]
* operation, such as environment variables or command-line arguments.
*
* Note: The [ProvideConfiguration] operation is part of the [ConfigurationSource.ops] and not of
* the [Configuration.ops] as its an operation of the source, not the target.
*/
class ProvideConfiguration(underlyingNode: Node, var conf: Configuration) :
ConfigurationOperation(underlyingNode = underlyingNode, concept = conf)
class ProvideConfiguration(
underlyingNode: Node,
var source: ConfigurationSource,
var conf: Configuration,
) : ConfigurationOperation(underlyingNode = underlyingNode, concept = source)

/**
* Represents an operation to provide a [ConfigurationGroup]. For example, when loading an INI file
* with our INI file frontend, each section is presented as a [RecordDeclaration]. This record
* declaration would "provide" the configuration group.
* Represents an operation to provide a [ConfigurationGroup]. It connects a
* [ConfigurationGroupSource] with a [ConfigurationGroup].
*/
class ProvideConfigurationGroup(
underlyingNode: Node,
conf: Configuration,
var source: ConfigurationGroupSource,
var group: ConfigurationGroup,
) : ConfigurationOperation(underlyingNode = underlyingNode, concept = conf) {
) : ConfigurationOperation(underlyingNode = underlyingNode, concept = source) {
init {
name = group.name
}
}

/**
* Represents an operation to provide a [ConfigurationOption]. For example, when loading an INI file
* with our INI file frontend, each key-value pair is presented as a [FieldDeclaration]. This field
* declaration would "provide" the configuration option.
* Represents an operation to provide a [ConfigurationOption]. It connects a
* [ConfigurationOptionSource] with a [ConfigurationOption].
*/
class ProvideConfigurationOption(
underlyingNode: Node,
var source: ConfigurationOptionSource,
var option: ConfigurationOption,
var value: Node?,
) : ConfigurationOperation(underlyingNode = underlyingNode, concept = option.group.conf) {
) : ConfigurationOperation(underlyingNode = underlyingNode, concept = source) {
init {
name = option.name
}
}

/**
* Represents a possible source for a configuration. For example, when loading an INI file with our
* INI file frontend, the whole file would be represented as a [TranslationUnitDeclaration]. This
* translation unit declaration would be the source of the configuration.
*/
class ConfigurationSource(underlyingNode: Node) : Concept(underlyingNode = underlyingNode) {
val groups: MutableList<ConfigurationGroupSource> = mutableListOf()

/**
* The individual operations that target parts of the configuration are assigned to
* [Concept.ops] of their respective concept. For example a [ReadConfigurationGroup] is part of
* the [ConfigurationGroup]'s ops. This property returns all operations of all groups and
* options as well as the ones targeting the complete configuration.
*/
val allOps: Set<Operation>
get() {
return ops + groups.flatMap { it.ops + it.options.flatMap { option -> option.ops } }
}
}

/**
* Represents a possible group source for a configuration group. For example, when loading an INI
* file with our INI file frontend, each section is presented as a [RecordDeclaration]. This record
* declaration would be the source of the configuration group.
*/
class ConfigurationGroupSource(underlyingNode: Node, conf: ConfigurationSource) :
Concept(underlyingNode = underlyingNode) {
val options: MutableList<ConfigurationOptionSource> = mutableListOf()

init {
conf.groups += this
}
}

/**
* Represents a possible option source for a configuration option. For example, when loading an INI
* file with our INI file frontend, each key-value pair is presented as a [FieldDeclaration]. This
* field declaration would be the source to the configuration option.
*/
class ConfigurationOptionSource(underlyingNode: Node, var group: ConfigurationGroupSource) :
Concept(underlyingNode = underlyingNode) {
init {
group.options += this
}
}
Loading
Loading