diff --git a/codyze-specification-languages/cpg-native/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguage/cpg/native/CPGQueryExecutor.kt b/codyze-specification-languages/cpg-native/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguage/cpg/native/CPGQueryExecutor.kt index d0b0959c2..5e221d8ba 100644 --- a/codyze-specification-languages/cpg-native/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguage/cpg/native/CPGQueryExecutor.kt +++ b/codyze-specification-languages/cpg-native/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguage/cpg/native/CPGQueryExecutor.kt @@ -6,6 +6,8 @@ import de.fraunhofer.aisec.codyze.specificationLanguage.cpg.native.queries.CPGQu import de.fraunhofer.aisec.codyze.specificationLanguage.cpg.native.queries.ExampleQuery import io.github.detekt.sarif4k.Run import io.github.oshai.kotlinlogging.KotlinLogging +import java.io.FileOutputStream +import java.io.PrintStream private val logger = KotlinLogging.logger {} @@ -31,7 +33,9 @@ class CPGQueryExecutor(private val configuration: CPGQueryConfiguration, private } val informationExtractor = TSFIInformationExtractor() informationExtractor.extractInformation(backend.cpg) - informationExtractor.printInformation(XMLFormatter(),System.out) + + informationExtractor.printInformation(XMLFormatter(), + PrintStream(FileOutputStream("sf.xml")), PrintStream(FileOutputStream("tsfi.xml"))) val cpgQuerySarifBuilder = CPGQuerySarifBuilder(queries = queries, backend = backend) return cpgQuerySarifBuilder.buildRun(findings = findings) diff --git a/codyze-specification-languages/cpg-native/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguage/cpg/native/InformationExtractor.kt b/codyze-specification-languages/cpg-native/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguage/cpg/native/InformationExtractor.kt index a079d2036..7f2413a85 100644 --- a/codyze-specification-languages/cpg-native/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguage/cpg/native/InformationExtractor.kt +++ b/codyze-specification-languages/cpg-native/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguage/cpg/native/InformationExtractor.kt @@ -16,14 +16,22 @@ open abstract class InformationExtractor { */ public abstract fun extractInformation(result: TranslationResult); - public fun printInformation(formatter: Formatter, printer: PrintStream){ - printer.print(formatInformation(formatter)) + public fun printInformation(formatter: Formatter, printerF1: PrintStream, printerF2:PrintStream){ + printerF1.print(formatSFInformation(formatter)) + printerF2.print(formatTSFIInformation(formatter)) } /** * The extractor specific information needs to be given to the formatter in a key, value base fashion. The * implementation of this function can nest key values, generated lists etc. */ - protected abstract fun formatInformation(formatter: Formatter):String; + protected abstract fun formatTSFIInformation(formatter: Formatter):String; + + /** + * The extractor specific information needs to be given to the formatter in a key, value base fashion. The + * implementation of this function can nest key values, generated lists etc. + * + */ + protected abstract fun formatSFInformation(formatter: Formatter):String; } \ No newline at end of file diff --git a/codyze-specification-languages/cpg-native/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguage/cpg/native/TSFIInformationExtractor.kt b/codyze-specification-languages/cpg-native/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguage/cpg/native/TSFIInformationExtractor.kt index 0cc8ae67c..ef6e87e69 100644 --- a/codyze-specification-languages/cpg-native/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguage/cpg/native/TSFIInformationExtractor.kt +++ b/codyze-specification-languages/cpg-native/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguage/cpg/native/TSFIInformationExtractor.kt @@ -9,6 +9,7 @@ import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.Literal import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference +import io.github.oshai.kotlinlogging.KotlinLogging import org.xml.sax.InputSource import java.io.StringReader import java.io.StringWriter @@ -20,10 +21,14 @@ import javax.xml.transform.stream.StreamResult class TSFIInformationExtractor: InformationExtractor() { + private val logger = KotlinLogging.logger {} + val securityFunctionMap: MutableMap = mutableMapOf() val securityBehavior: MutableSet = mutableSetOf() + val tsfiDeclarations: MutableList = mutableListOf() + /** * Reverse map to more efficiently find SFs related to an appearance of an action. */ @@ -43,7 +48,7 @@ class TSFIInformationExtractor: InformationExtractor() { for (sf in sfs){ if (!securityFunctionMap.contains(sf.name)){ securityFunctionMap.put(sf.name, SecurityFunction(sf.name,(sf.comment?:"").trimIndent().trim().replace("\n",""), - mutableSetOf(), mutableSetOf(), mutableSetOf(), mutableSetOf())) + mutableSetOf(), mutableSetOf(), mutableSetOf())) } } @@ -99,6 +104,9 @@ class TSFIInformationExtractor: InformationExtractor() { } val tsfis = annotatedNode.filter { it.annotations.any {it.name.lastPartsMatch(Name("TSFI")) } } + var preSF = 0 + var postSF = 0 + var identifiedSF = 0 for (tsfi in tsfis){ val tsfiSFs: MutableSet = mutableSetOf() @@ -116,32 +124,38 @@ class TSFIInformationExtractor: InformationExtractor() { tsfiAnnotation.members.forEach { if(it.value is MemberExpression){ - behavior = Name(it.value.toString()) + behavior = Name(it.value?.code.toString()?:it.name.toString()) }else{ - tsfiSFs.addAll(it.value.allChildren().map { Name(it.toString()) }.toMutableSet()) + tsfiSFs.addAll(it.value.allChildren().map { Name(it.code.toString()) }.toMutableSet()) } - if(tsfiSFs.isEmpty()){ - val calls = annotationExtendedNode.flatMap { reachableCalls(it) } - calls.forEach { call -> - actionsToSFReverseMap.keys.forEach { actionKey -> - if(call.name.toString().startsWith(actionKey.toString())){ - tsfiSFs.addAll(actionsToSFReverseMap[actionKey]?.map { it.name }?: setOf()) - } + } + + if(tsfiSFs.isEmpty()){ + val calls = annotationExtendedNode.flatMap { reachableCalls(it) } + calls.forEach { call -> + actionsToSFReverseMap.keys.forEach { actionKey -> + if(call.name.toString().startsWith(actionKey.toString())){ + tsfiSFs.addAll(actionsToSFReverseMap[actionKey]?.map { it.name }?: setOf()) } } - if(tsfiSFs.isEmpty()){ - // TODO Here we could investigate why we do not find the SF for it. - } } - - } - val description = annotationExtendedNode.map { it.comment?:"" }.joinToString("\n") - val tsfiDeclaration = TSFI(description, behavior?:Name(""), annotationExtendedNode) - tsfiSFs.forEach { - securityFunctionMap[it]?.tsfis?.add(tsfiDeclaration) + if(tsfiSFs.isEmpty()){ + // TODO Here we could investigate why we do not find the SF for it. + }else{ + postSF++ + } + }else{ + preSF++ } + + val description = annotationExtendedNode.map { it.comment?:"" }.joinToString("").trimIndent().trim().replace("\n","") + val tsfiDeclaration = TSFI(description, behavior?:Name(""), annotationExtendedNode, + tsfiSFs.flatMap { tsfiSFName -> + securityFunctionMap.entries.filter { it.key.toString().substringAfterLast(".") == tsfiSFName.toString().substringAfterLast(".") }.map { it.value } }.toMutableSet()) + tsfiDeclarations.add(tsfiDeclaration) } + logger.info { "TSFI: ${tsfiDeclarations.size}: $preSF security functions explicitly specified, $postSF security functions identified through analysis." } } @@ -166,7 +180,7 @@ class TSFIInformationExtractor: InformationExtractor() { } - override fun formatInformation(formatter: Formatter): String { + override fun formatSFInformation(formatter: Formatter): String { var xml = "" for (sf in securityFunctionMap.values){ @@ -194,6 +208,46 @@ class TSFIInformationExtractor: InformationExtractor() { return prettyPrint(formatter.format("security-specification", xml, mapOf()),2,true) } + override fun formatTSFIInformation(formatter: Formatter): String { + var xml = "" + var id = 0 + for (tsfi in tsfiDeclarations){ + var tsfiContent = formatter.format("description", tsfi.description, mapOf()) + + var parametersContent = "" + + if(parametersContent.isEmpty()) parametersContent = " " + tsfiContent += formatter.format("parameters", parametersContent, mapOf()) + + var errorsContent = "" + + if(errorsContent.isEmpty()) errorsContent = " " + tsfiContent += formatter.format("errors", errorsContent, mapOf()) + + + var afContent = "" + for(sf in tsfi.sf){ + afContent += formatter.format("ref", "", mapOf("target" to replaceSFName(sf.name.toString()))) + } + if(afContent.isEmpty()) afContent = " " + + tsfiContent += formatter.format("security-functions", afContent, mapOf()) + + + var actionsContent = "" + if(actionsContent.isEmpty()) actionsContent = " " + + tsfiContent += formatter.format("actions", actionsContent, mapOf()) + + + xml += formatter.format("tsfi", tsfiContent, mapOf("id" to "tsfi$id", "security" to tsfi.securityBehavior.localName.substringAfterLast(".").lowercase())) + id++ + } + + + return prettyPrint(formatter.format("functional-specification", xml, mapOf()),2,true) + } + private fun replaceSFName(name:String): String{ var sfname = name.substringAfterLast("de.fraunhofer.aisec.codyze.").replace(".TOEDefinitions.SecurityFunction","") return sfname.lowercase() @@ -222,7 +276,7 @@ class TSFIInformationExtractor: InformationExtractor() { * In contrast to the annotation the data class does not contain the security function.When the TSFIs are parsed, the * associated security functions are either provided in the annotation or identified through static code analysis. */ - data class TSFI(val description: String, val securityBehavior:Name, val functions:Set) + data class TSFI(val description: String, val securityBehavior:Name, val functions:Set, val sf:MutableSet) - data class SecurityFunction(val name:Name, val description:String, val objectives:MutableSet, val requirements: MutableSet, val actions: MutableSet, val tsfis:MutableSet) + data class SecurityFunction(val name:Name, val description:String, val objectives:MutableSet, val requirements: MutableSet, val actions: MutableSet) } \ No newline at end of file