From 06e72b7b245bac1ae86eb2f6c07602e2452b9ba2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 4 Apr 2023 09:08:38 +0200 Subject: [PATCH 001/121] Update kotlin monorepo to v1.8.20 (#719) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0de6d2939..6aa5f1be0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "1.8.10" +kotlin = "1.8.20" cpg = "6.2.1" koin = "3.3.3" detekt = "1.22.0" From ae939ae673f1f69715e6d6690743c9dffcdc7b49 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 4 Apr 2023 14:40:40 +0200 Subject: [PATCH 002/121] Update koin to v3.4.0 (#717) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6aa5f1be0..fd577f37f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] kotlin = "1.8.20" cpg = "6.2.1" -koin = "3.3.3" +koin = "3.4.0" detekt = "1.22.0" spotless = "6.17.0" dokka = "1.8.10" From a588d57d9022bb2f847cdea0bfaa1ee9b962eaac Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 17 Apr 2023 09:56:29 +0200 Subject: [PATCH 003/121] Update dependency com.diffplug.spotless:spotless-plugin-gradle to v6.18.0 (#721) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fd577f37f..065d4d5e0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ kotlin = "1.8.20" cpg = "6.2.1" koin = "3.4.0" detekt = "1.22.0" -spotless = "6.17.0" +spotless = "6.18.0" dokka = "1.8.10" From b33e771f9807002dd35f3891619866e68091f026 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 17 Apr 2023 10:31:31 +0200 Subject: [PATCH 004/121] Update dependency gradle to v8.1 (#723) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.jar | Bin 61608 -> 62076 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 7 ++++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index ccebba7710deaf9f98673a68957ea02138b60d0a..c1962a79e29d3e0ab67b14947c167a862655af9b 100644 GIT binary patch delta 8979 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 F{TFmrHLU;u delta 8469 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 Date: Mon, 17 Apr 2023 10:38:54 +0200 Subject: [PATCH 005/121] Update dependency io.mockk:mockk to v1.13.5 (#724) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 065d4d5e0..329078816 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -36,7 +36,7 @@ detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", # test junit-params = { module = "org.junit.jupiter:junit-jupiter-params", version = "5.9.2"} -mockk = { module = "io.mockk:mockk", version = "1.13.4"} +mockk = { module = "io.mockk:mockk", version = "1.13.5"} # this is necessary for the plugins to be used in the buildSrc folder kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } From 901a1482d376075c3fb817eb931d537f14c050f1 Mon Sep 17 00:00:00 2001 From: Selina Lin Date: Mon, 17 Apr 2023 14:33:52 +0200 Subject: [PATCH 006/121] Add documentation for Coko (#692) Co-authored-by: Florian Wendland Co-authored-by: maximilian-galanis --- .../coko/core/CokoBackend.kt | 4 +- .../coko/core/dsl/Annotations.kt | 2 +- docs/Codyze/index.md | 2 +- docs/Codyze/why-v3.md | 7 +- docs/Coko/index.md | 39 ++++ docs/Coko/modelling.md | 205 ++++++++++++++++++ docs/Coko/rules.md | 111 ++++++++++ docs/Getting Started/cli.md | 54 +++-- docs/Getting Started/configuration.md | 100 ++++----- docs/Getting Started/installation.md | 6 +- docs/assets/img/eclipse-formatter.png | Bin 64410 -> 0 bytes docs/assets/img/intellij-formatter.png | Bin 158529 -> 0 bytes docs/contributors/CONTRIBUTING.md | 6 +- docs/contributors/code.md | 17 +- docs/contributors/index.md | 16 +- docs/theme-overrides/home.html | 17 +- docs/theme-overrides/main-styles.html | 16 ++ docs/theme-overrides/main.html | 7 + mkdocs.yml | 6 +- 19 files changed, 495 insertions(+), 120 deletions(-) create mode 100644 docs/Coko/index.md create mode 100644 docs/Coko/modelling.md create mode 100644 docs/Coko/rules.md delete mode 100644 docs/assets/img/eclipse-formatter.png delete mode 100644 docs/assets/img/intellij-formatter.png create mode 100644 docs/theme-overrides/main.html diff --git a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/CokoBackend.kt b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/CokoBackend.kt index 4dd78adf6..0644d8a5d 100644 --- a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/CokoBackend.kt +++ b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/CokoBackend.kt @@ -42,13 +42,13 @@ interface CokoBackend : Backend { /** For each of the nodes in [this], there is a path to at least one of the nodes in [that]. */ infix fun Op.followedBy(that: Op): Evaluator - /* Ensures the order of nodes as specified in the user configured [Order] object */ + /** Ensures the order of nodes as specified in the user configured [Order] object */ fun order( baseNodes: OrderToken, block: Order.() -> Unit ): Evaluator - /* Ensures the order of nodes as specified in the user configured [Order] object */ + /** Ensures the order of nodes as specified in the user configured [Order] object */ fun order( baseNodes: Op, block: Order.() -> Unit diff --git a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/Annotations.kt b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/Annotations.kt index 0f3f1ccb8..6d1e6d1bf 100644 --- a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/Annotations.kt +++ b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/Annotations.kt @@ -27,7 +27,7 @@ package de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl @Retention(AnnotationRetention.SOURCE) annotation class Import(vararg val paths: String) -/* Marks a function that should be evaluated as a rule by Codyze */ +/** Marks a function that should be evaluated as a rule by Codyze */ @Target(AnnotationTarget.FUNCTION) @MustBeDocumented @Suppress("LongParameterList") diff --git a/docs/Codyze/index.md b/docs/Codyze/index.md index 4e7cbc424..658e36acc 100755 --- a/docs/Codyze/index.md +++ b/docs/Codyze/index.md @@ -13,7 +13,7 @@ description: > !!! info Codyze is currently being redesigned. - For the documentation for the legacy version of Codyze, please look [here](https://github.com/Fraunhofer-AISEC/codyze/tree/v2.3.0/docs). + For the documentation for the legacy version of Codyze, please look [here ](https://github.com/Fraunhofer-AISEC/codyze/tree/v2.3.0/docs){target=_blank}. Security is hard and implementing it correctly is even harder. Luckily, there are well-established and battle-proven libraries available that do the heavy lifting of security functions such as authentication, logging or encryption. But even when using these libraries in application code, developers run the risk of making subtle errors which may undermine the security of their application. This is where Codyze helps. By integrating it into an IDE or CI pipeline, developers can analyze their source code while programming and check if they are using libraries in a correct or in an insecure way. diff --git a/docs/Codyze/why-v3.md b/docs/Codyze/why-v3.md index 91429a134..73c6f8352 100644 --- a/docs/Codyze/why-v3.md +++ b/docs/Codyze/why-v3.md @@ -6,6 +6,9 @@ no_list: true description: > Differences between Codyze v3 and Codyze v2 --- + +# Redesign of Codyze + The goal of the redesign is to make Codyze more maintainable and easier extendable. This introduced a lot of changes compared to the legacy version. @@ -14,7 +17,7 @@ The core functionalities of Codyze were separated from the executable part which We introduced the concept of Executors which are responsible for evaluating rules of a specific specification language. Through Executors, Codyze can verify rules written in different specification languages and utilize their advantages as long as there is an Executor for them. -We are also working on a new specification language Coko which comes inbuilt with Codyze. +We are also working on a new specification language Coko which comes with Codyze. -Additionally, we reworked the organisation of code in Codyze to be able to handle multiple projects with their own configurations with only one Codyze instance. +Additionally, we reorganized the code in Codyze to be able to handle multiple projects with their own configurations with only one Codyze instance. This, for example, allows switching between projects in an IDE without losing the context of any analysis and should better support LSP mode. diff --git a/docs/Coko/index.md b/docs/Coko/index.md new file mode 100644 index 000000000..d03e319a3 --- /dev/null +++ b/docs/Coko/index.md @@ -0,0 +1,39 @@ + +Codyze provides a built-in domain specific language, __Coko__, which can be used to define policies for Codyze. + +## Structure of Policies + +Coko policies can be split into two parts: + +* Modelling the API through classes and functions. +* Writing rules that describe the expected usage of the modelled API. + +When modeling a library, you will typically start by describing its classes or functions and then write rules. + +## Structure of Coko + +Coko is defined as a custom Kotlin scripting language. +It serves as an API to write source code queries in a declarative way. +The rules written in Coko are executed to construct the queries for the used backend. + +The concept of Coko is that the API is modelled through classes and functions. +These classes and functions are used to declare rules, which Codyze then evaluates. +Coko is, therefore, in its concept more similar to a domain specific language and only uses the Kotlin scripting technology to load the policies into Codyze. +However, as a Kotlin script can contain any valid Kotlin code, it is also possible to execute arbitrary Kotlin code with Coko. +It is currently not possible to prevent this, but all Coko scripts that are available on our website and GitHub repository are validated by us to prevent any misuse. +For more information about custom Kotlin scripting languages, please refer to the [Kotlin documentation ](https://kotlinlang.org/docs/custom-script-deps-tutorial.html){target=_blank} and the respective [Kotlin KEEP ](https://github.com/Kotlin/KEEP/blob/master/proposals/scripting-support.md){target=_blank}. + +Syntax highlighting and code completion are available for Coko in any IDE that provides these for Kotlin. + +The syntax of Coko is the same as the syntax of Kotlin. +For writing Coko policies you will need to know how to create classes, interfaces and functions in Kotlin. +Please refer to the [Kotlin Documentation ](https://kotlinlang.org/docs/basic-syntax.html){target=_blank} for an overview. + +### Type-safe builders + +Coko also uses the concept of [type-safe builders ](https://kotlinlang.org/docs/type-safe-builders.html){target=_blank}. +Type-safe builders allow you to build objects in a semi-declarative way similar to markup languages. +They can be seen as a special syntax for nesting the construction of objects. +They will be explained in detail in the parts of Coko that use them. + + diff --git a/docs/Coko/modelling.md b/docs/Coko/modelling.md new file mode 100644 index 000000000..6b3626b1b --- /dev/null +++ b/docs/Coko/modelling.md @@ -0,0 +1,205 @@ + +To evaluate rules for an API, Coko needs an understanding of the components of the API, such as classes or functions. +In Coko these components are modelled through interfaces, classes and a class called `Op`. + +## Ops + +[`Ops` ](../../api/codyze/codyze-specification-languages/coko/coko-core/de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl/-op){target=_blank} are the basic building blocks for writing policies in Coko. +With `Ops` you can model and group functions of the API that serve a similar functionality. +They are also a way to define queries to the Codyze backend for finding calls to these functions. +Each `Op` object is one query to the backend. + +There are currently two types of `Ops`, `FunctionOps` for modelling functions and `ConstructorOps` for modelling constructors in object-oriented languages. +They are both built with [type-safe builders](index.md#type-safe-builders). +The following sections will explain the builders for each `Op` type. + +### FunctionOps + +The function [`op()` ](../../api/codyze/codyze-specification-languages/coko/coko-core/de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl/op.html){target=_blank} is the start for building `FunctionOps`. +Within the block of `op()` the fully qualified name of the functions you want to model can be specified as a string. +In the block of the fully qualified name the arguments to function can be defined. +They serve as a filter for the query. +Only calls to the function with the same number of arguments with the same value at each position will be found by the query. + +In `signature` it is also possible to specify unordered arguments. +These are arguments, that should somehow be passed to the function calls we want to find, but it is not important, in which position they are passed. + +```kotlin title="Example of defining a FunctionOp" +op { + "my.fully.qualified.name" { // (1)! + signature(5..10) // (2)! + signature(".*one.*") // (3)! + signature(0..5, listOf("one", "two")) // (4)! + signature() // (5)! + } + + "my.other.fully.qualified.name" { // (6)! + signature { // (7)! + - ".*" // (8)! + -7 // (9)! + } + signature(arrayOf(4)) { // (10)! + - 123 + } + } +} +``` + +1. The fully qualified name of the function we want to find. +2. Filters for calls to `my.fully.qualified.name` that have as only argument a number between 5 and 10. +3. Filters for calls to `my.fully.qualified.name` that have a string as only argument that contains "one", for example `my.fully.qualified.name("tone")`. +4. Filters for calls to `my.fully.qualified.name` that have a number between 0 and 5 as first argument and as second argument either the string "one" or "two". +5. Filters for calls to `my.fully.qualified.name` where no arguments were passed. +6. The fully qualified name of the other function we want to find. +7. The `signature` function can also invoke a type-safe builder. +8. In the type-safe builder of `signature` the arguments are listed using `-`. +9. The space after the `-` is optional. +10. The unordered arguments are given as an array to `signature`. In this example, the unordered argument is 4. + +### ConstructorOps + +The function of the builder for `ConstructorOps` is [`constructor()` ](../../api/codyze/codyze-specification-languages/coko/coko-core/de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl/constructor.html){target=_blank}. +The fully qualified name of the class is the first argument. +In the block of `constructor()` you can specify the arguments to the constructor like for the `FunctionOp`. +They serve the same purpose as for [`FunctionOps`](modelling.md#functionops). + +```kotlin title="Example of defining a ConstructorOp" +constructor("my.fully.qualified.MyClass") { // (1)! + signature() // (2)! +} +``` + +1. This models the constructor of the class `my.fully.qualified.MyClass`. +2. The signature is directly specified in `ConstructorOps`, because the name of the constructor is clear. + +### Special argument types + +Arguments in `Ops` are used to filter calls of a function based on the arguments that are given to the calls. +In Coko there are a few special argument types that help with the filtering. + +The first type is the `Wildcard` object. +If the `Wildcard` object is given as an argument to `Op`, the filter will allow any kind of value in the same argument position of the function calls. + +The second type is `null`. +It can be used to signify that the given arguments should be filtered out of the query. +This type will be helpful when constructing [`Op` templates with Coko functions](modelling.md#functions). + +Another special type are `ParameterGroups`. +They are a filter to express that the argument at the position should be composed of multiple values. +An example would be if the argument is a string that should contain multiple strings, for example `foo("Start string" + 12 + "End string")`. +This can be modeled with `ParameterGroups`. +Coko offers the DSL function `group`, in which these values can be specified. + +The last types are the `Type` class and the `ParamWithType` class. +The `Type` class is used to filter the type of the arguments. +The fully qualified name of the type must be given +`ParamWithType` combines the filter for the value with the filter for the type. + + +```kotlin title="Example with special argument types" +op { + "my.fully.qualified.name" { + signature(Wildcard) // (1)! + signature(Wildcard, 2) // (2)! + signature(null, 1) // (3)! + signature( + group { + - "Start string .*" + - 12 + } // (4)! + ) + signature(Type("java.util.List")) // (5)! + signature(1.0 withType "java.lang.Float") // (6)! + } +} +``` + +1. Queries for all calls to `my.fully.qualified.name` that have one argument. +2. Queries for all calls to `my.fully.qualified.name` with two arguments and where the second argument is 2. +3. Filters out all calls to `my.fully.qualified.name` with two arguments and where the second argument is 1. +4. Filters for all calls to `my.fully.qualified.name` with one argument. The argument must contain both `"Start string"` and the number 12. An example would be `my.fully.qualified.name("Start string with the number " + 12)`. +5. Queries for all calls to `my.fully.qualified.name` with one argument which must be of type `java.util.List`[^1]. +6. Queries for all calls to `my.fully.qualified.name` with one argument which has the value 1.0 and has the type `java.lang.Float`[^1]. + +[^1]: In a real example this would be redundant because this case is already covered by the first filter with `Wildcard`. + +## Functions + +Since each `Op` is interpreted by Codyze as one query for function calls to the backend, it would be helpful to have templates for `Ops` that find calls to the same function but with different filters. +This can be achieved with functions in Coko. +The parameters of these functions in Coko can be used to pass the specific values to the filter. + +```kotlin title="An example for an Op template for the function java.util.List.add" +fun add( + element: Any, // (1)! + index: Any? // (2)! +) = op { + "java.util.List.add" { + signature(element) + signature(index withType "int", element) // (3)! + } +} +``` + +1. `List.add` in Java is a generic method, so the type of `element` is not static. However, it is recommended to use `Any` even if the type of the value is static since you might want to pass one of the special argument types like `Wildcard`. +2. `index` is nullable, so it is also possible to filter for calls where no index is given. The type is `Any?` to be able to pass `Wildcard` as well. +3. The arguments can be further specified like the additional filter for the type of `index`. + +If the reference to a Coko function is used for [`order` rules](rules.md#order-evaluator), all parameters must have a nullable type. +Coko invokes them with dummy arguments and uses internal functions to query for all calls to the modelled function regardless of the specified signatures. + + +## Interfaces + +A goal of Coko is to make rules more reusable. +Aside from specific rules of an API, there might be some general policies that should be followed for all API that provide similar functionality. +An example would be that all actions executed on a database should be logged. +Instead of writing the corresponding rule for all combinations of database and logging APIs, one might want to combine this into one reusable rule. + +This reusability is achieved through interfaces. +In Coko interfaces and their functions describe the functionalities that a group of APIs has in common. +Rules can thus be written on a more conceptual level. + +When Coko encounters rules using a Coko interface as a parameter, it will use one of the available classes implementing the interface as argument to evaluate the rule. +Currently, it uses the first class it finds, however, we will implement an algorithm in the future which will try to find the implementation that fits the best for the analyzed code. + +```kotlin title="Example of a interface and its implementations in Coko" +interface Logging { + fun log(message: Any?, vararg args: Any?): Op +} + +class JavaLogging: Logging { + override fun log(message: Any?, vararg args: Any?): Op = + op { + "java.util.logging.Logger.info" { + signature { + group { + - message + args.forEach { - it } + } + } + } + } +} + +class PythonLogging: Logging { + override fun log(message: Any?, vararg args: Any?): Op = + op { + "logging.info" { + signature(args) { // (1)! + - message + } + } + } +} +``` + +1. We don't care about the order of the arguments for `args`, just that they appear somewhere as arguments for the call. + +## Classes + +Classes in Coko model the actual components of an API. +They can implement interfaces or just be normal classes. +With classes, API functions can be grouped + +For APIs written in object-oriented languages it might be sensible to create a Coko class for each class in the API. diff --git a/docs/Coko/rules.md b/docs/Coko/rules.md new file mode 100644 index 000000000..08541e78e --- /dev/null +++ b/docs/Coko/rules.md @@ -0,0 +1,111 @@ + +Rules in Coko describe how an API should be used. +They are functions that are annotated with the [`@Rule` ](../../api/codyze/codyze-specification-languages/coko/coko-core/de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl/-rule){target=_blank} annotation. + +In the `@Rule` annotation you can specify metadata about the rule such as the description of the rule. +The metadata will be used for describing the findings in the SARIF output. + +If the rule requires some instance of a model, they can be specified as parameters to the rule function. + +Each Coko rule must return an implementation of the [`Evaluator` ](../../api/codyze/codyze-specification-languages/coko/coko-core/de.fraunhofer.aisec.codyze.specificationLanguages.coko.core/-evaluator){target=_blank} interface, which Codyze can use to evaluate the rule. +Coko provides some common evaluators which will be explained in the following sections. +The example model will be used for explaining the evaluators. + +```kotlin title="Example model" +class Foo { + fun constructor() = constructor("Foo") { + signature() + } + + fun first(i: Any?) = op { + definition("Foo.first") { + signature(i) + } + } + + fun second(s: Any?) = op { + definition("Foo.second") { + signature(s) + } + } +} + +class Bar { + fun second() = op { + definition("Bar.second") { + signature() + } + } +} +``` + +## Only Evaluator +The `only` evaluator checks if all calls to an Op are only called with the specified arguments. +Therefore, it takes one `Op` as argument. + +```kotlin title="Rule example using only" +@Rule +fun `only calls to first with 1 allowed`(foo: Foo) = + only(foo.first(1)) + +``` + + +## Order Evaluator +The `order` evaluator checks if functions related to an object are called in the correct order. +It takes two arguments, the `baseNodes` and the order. +The `baseNodes` are the function calls that are the start of the order. +Usually, this is either the constructor of a class or some kind of initialization function. + +To construct the order, Coko provides a [type-safe builder](index.md#type-safe-builders). +Within the builder, the order is specified as a regular expression. + +The "alphabet" of the order regex is: + +- references to [functions that return an `Op`](modelling.md#functions) written as `::` or `::` +- `Ops` themselves. + +If all calls to a modelled function should be considered for the order regardless of the specified signatures, please use the first option. +When passing `Ops`, only functions that match the used signature and argument are considered valid. + +The builder provides a set of functions that allow you to add quantifiers to the regex or group them. + +| Function | Regex | Description | +|---------------------|----------------|----------------------------------------------------------------------------------| +| `or` | | | Represents a choice, either the first or the second expression has to be matched | +| `set` | [] | Represents multiple choices, one expression in it has to be matched | +| `maybe` | * | Matches an expression zero or more times | +| `some` | + | Matches an expression one or more times | +| `option` | ? | Matches an expression zero or one time | +| `count(n)` | {`n`} | Matches an expression exactly `n` times | +| `atLeast(min)` | {`min`,} | Matches an expression at least `min` times | +| `between(min, max)` | {`min`, `max`} | Matches an expression at least `min` and at most `max` times | + + +```kotlin title="Rule example using order" +@Rule +fun `order of Foo`(foo: Foo) = + order(foo.constructor()/* (2)! */) { // (1)! + - foo.first(...) // (3)! + maybe(foo::second) // (4)! + } + +``` + +1. This starts the type-safe builder for the order. +2. The `Op` returned from `foo.constructor` will be used as query for the function calls that are the starting point for evaluating the order. +3. This will use the filtered `Op` returned by `foo.first(...)` for the order. +4. This will consider all calls to the function modelled by `foo.second()` for the order. No filter will be applied. + + +## FollowedBy Evaluator +The `followedBy` evaluator works similarly like the implication in logic. +It takes two `Ops` and specifies that if the first `Op` is called then the second `Op` must be called as well. +Compared to the `order` evaluator, `followedBy` is more flexible because Ops from different models can be connected. + +```kotlin title="Rule example using followedBy" +@Rule +fun `if first then second`(foo: Foo, bar: Bar) = + foo.first(Wildcard) followedBy bar.second() + +``` diff --git a/docs/Getting Started/cli.md b/docs/Getting Started/cli.md index 1182c1ffd..e49e55a8e 100644 --- a/docs/Getting Started/cli.md +++ b/docs/Getting Started/cli.md @@ -11,14 +11,44 @@ description: > ## Command line mode -When running in command line interface (CLI) mode, Codyze can be used to automatically check a code base against a set of MARK rules. +When running in command line interface (CLI) mode, Codyze can be used to automatically check a code base against a set of rules given in a supported specification language like Coko. Below are short exemplary calls to start codyze in command line interface mode. -`./` refers to the top-level directory of the repository. However, for the Gradle arguments `./` refers to the directory of the project which is `codyze-cli`. +`./` refers to the top-level directory of the repository. However, for the Gradle arguments `./` refers to the directory of the project, which is `codyze-cli`. ```shell -./gradlew :codyze-cli:run --args="analyze -s --spec -o " +./gradlew :codyze-cli:run --args=" --spec -s " ``` -`analyze` enters command line mode. It will parse all files given by the `-s` argument. With `--spec` you can specify the files which contain the policies you want Codyze to verify. The findings are written in SARIF format to the file given by `-o`. +Because Codyze is built to be modular and support many specification languages as well as code analysis backends, there are subcommands to select the `executor`/`backend`. +To find what arguments each `executor`/`backend` accept, use the `--help` argument: + +To show the available `executors` use: +```shell +./gradlew :codyze-cli:run --args="--help" +``` + +To show the arguments accepted by an executor and the available `backend`s use: +```shell +./gradlew :codyze-cli:run --args=" --help" +``` + +To show the arguments accepted by a `backend` use: +```shell +./gradlew :codyze-cli:run --args=" --help" +``` + +## Analysis Example + +The repository contains examples which you can use to test Codyze. +Below are the commands to call Codyze on these examples. + +```shell +./gradlew :codyze-cli:run --args="runCoko --spec ../codyze-specification-languages/coko/coko-dsl/src/test/resources/model.codyze.kts --spec ../codyze-specification-languages/coko/coko-dsl/src/test/resources/javaimpl.codyze.kts cokoCpg -s ../codyze-specification-languages/coko/coko-dsl/src/test/resources/java/Main.java" +``` + +This configures `Codyze` to use the 'coko' executor and the 'cokoCpg' backend. +You will see the result printed to the console and a `findings.sarif` files is generated in the `codyze-cli` folder. +The spec files contain a single rule, which checks that every change to a database is logged. +The sample Java file adheres to the rule, so there should be no issues in the result. ## CI/CD Integration @@ -48,19 +78,5 @@ jobs: wget "https://github.com/Fraunhofer-AISEC/codyze/releases/download/v${CODYZE_VERSION}/codyze-${CODYZE_VERSION}.zip" && unzip codyze-${CODYZE_VERSION}.zip - name: Check compliance run: | - codyze-${CODYZE_VERSION}/bin/codyze -c -o - -m codyze-${CODYZE_VERSION}/mark -s src/main/java + codyze-${CODYZE_VERSION}/bin/codyze ``` - -## Analysis Example - -The repository contains examples which you can use to test Codyze. -Below are the commands to call Codyze on these examples. - -```shell -./gradlew :codyze-cli:run --args="analyze -s ../codyze-specification-languages/coko/coko-dsl/src/test/resources/java/Main.java --spec ../codyze-specification-languages/coko/coko-dsl/src/test/resources/model.codyze.kts --spec ../codyze-specification-languages/coko/coko-dsl/src/test/resources/javaimpl.codyze.kts" -``` - -You will see the result printed to the console. -The spec files contain a single rule which checks that every change to a database is logged. -The sample Java file adheres to the rule so there should be no issues in the result. - diff --git a/docs/Getting Started/configuration.md b/docs/Getting Started/configuration.md index ee52a9d77..348926648 100644 --- a/docs/Getting Started/configuration.md +++ b/docs/Getting Started/configuration.md @@ -14,22 +14,6 @@ If both are present, the command line options take precedence over the configura For list and map type options, the data from the configuration file can be overwritten if the normal option (e.g. `--option-name`) is used. To append the data from the command line to the one from the configuration file, use the `additions` option (e.g. `--option-name-additions`) - -## Command Line Interface -There are three execution modes in which Codyze can run: - -* Command line mode: -
Non-interactive command line client, accepts arguments from command line and runs analysis -* Language server protocol mode: -
This mode is for IDE support and binds to stdout as a server for Language Server Protocol (LSP) -* Interactive console mode: -
The text based user interface (TUI) is an interactive console that allows exploring the analyzed source code by manual queries - -Subcommands are used to enter the modes (`analyze`, `lsp`, or `interactive`). - -The help and version message can be displayed with `-h` and `-V` respectively. -The full help is only available if a subcommand is specified. - ## Configuration File The configurations can also be defined with a JSON configuration file. Use the option `--config=` to specify the path to the config file. @@ -40,17 +24,18 @@ Relative paths in the configuration file are resolved relative to the configurat The configuration structure separates the options by subcommand as seen below. ```json { - "analyze": { - "source": "src" - }, - "lsp": { - "source": "other-src" + "good-findings": false, + "runCoko": { + "spec": "./model.codyze.kts", + "cokoCpg": { + "source": "./Main.java" + } } } ``` +In this example the `good-findings` argument belongs to Codyze, the `spec` argument belongs to the `runCoko` subcommand and the `source` argument belongs to the `cokoCpg` subcommand. The value of the option is taken from the object which corresponds to the subcommand used for the execution. -This means if `codyze analyze` is called, source would be `src`, and if `codyze lsp` is called, source would be `other-src`. -An exemplary configuration file can also be found in the [GitHub repository](https://github.com/Fraunhofer-AISEC/codyze/blob/main/codyze-cli/config.json). +An exemplary configuration file can also be found in the [GitHub repository ](https://github.com/Fraunhofer-AISEC/codyze/blob/main/codyze-cli/config.json){target=_blank}. ## List of Configurations This is a list of all available configurations, their descriptions and their respective name. @@ -58,33 +43,42 @@ The names are the same for the configuration file and the CLI options. `./` denotes the working directory in which Codyze was started. -| Key | Value | Description | Default Value | -|:------------------------|:--------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------| -| source | Path[] | Path to the to be analyzed files or directories. | `[./]` | -| disabled-source | Path[] | Path to files or directories which should not be analyzed. Symbolic links are not followed when filtering out these paths. | `[]` | -| output | String | Output file in which results are written. Use "-" to print to `stdout`. | findings.sarif | -| timeout | long | Terminates analysis after given minutes. | 120 | -| spec | Path[] | Paths to Mark rule files. | `[./]` | -| disabled-spec | Path[] | The specified Mark rules will be excluded from being parsed and processed. The rule has to be specified by its fully qualified name (`package.rule`). If there is no package name, specify rule as `.rule`. Use `package.*` to disable an entire package. | `[]` | -| no-good-findings | boolean | Disables output of **positive** findings. | false | -| pedantic | boolean | Activates pedantic analysis mode. In this mode, Codyze analyzes all MARK rules and report all findings. This option overrides "disabled-mark-rules" and "no-good-finding" and ignores any Codyze source code comments. | false | -| executor | String | Manually choose Executor to use with the given spec files. If unspecified, Codyze randomly selects an executor capable of evaluating the given specification files. | randomly selected | -| typestate | `DFA/WPDS` | Specify typestate analysis mode.
`DFA`: Deterministic finite automaton (faster, intraprocedural)
`WPDS`: Weighted pushdown system (slower, interprocedural) | `DFA` | -| additional-languages | String[] | Specify programming languages of to be analyzed files (full names). | `[]` | -| unity | boolean | Only relevant for C++. A unity build refers to a build that consolidates all translation units into a single one, which has the advantage that header files are only processed once, adding far less duplicate nodes to the graph. | false | -| type-system-in-frontend | boolean | If false, the type listener system is only activated once the frontends are done building the initial AST structure. This avoids errors where the type of a node may depend on the order in which the source files have been parsed. | true | -| default-passes | boolean | Adds all default passes in cpg (1. FilenameMapper, 2. TypeHierarchyResolver, 3. ImportResolver, 4. VariableUsageResolver, 5. CallResolver, 6. EvaluationOrderGraphPass, 7. TypeResolver). | true | -| passes | String[] | Register these passes to be executed in the specified order. Please specify the passes with their fully qualified name. | `[]` | -| debug-parser | boolean | Enables debug output generation for the cpg parser. | false | -| disable-cleanup | boolean | Switch off cleaning up TypeManager memory after analysis, set to true only for testing. | false | -| code-in-nodes | boolean | Should the code of a node be shown as parameter in the node. | false | -| annotations | boolean | Enables processing annotations or annotation-like elements. | false | -| fail-on-error | boolean | Should parser/translation fail on parse/resolving errors (true) or try to continue in a best-effort manner (false). | false | -| symbols | Map | Definition of additional symbols. | `{}` | -| parallel-frontends | boolean | If true, the ASTs for the source files are parsed in parallel, but the passes afterwards will still run in a single thread. This speeds up initial parsing but makes sure that further graph enrichment algorithms remain correct. | false | -| match-comments-to-nodes | boolean | Controls whether the CPG frontend shall use a heuristic matching of comments found in the source file to match them to the closest AST node and save it in the comment property. | false | -| analyze-includes | boolean | Enables parsing of include files. If includePaths are given, the parser will resolve symbols/templates from these in include but not load their parse tree. | false | -| includes | Path[] | Paths containing include files. | `[]` | -| enabled-includes | Path[] | If includes is not empty, only the specified files will be parsed and processed in the cpg, unless it is a part of the disabled list, in which it will be ignored. | `[]` | -| disabled-includes | Path[] | If includes is not empty, the specified includes files will be excluded from being parsed and processed in the cpg. The disabled list entries always take priority over the enabled list entries. | `[]` | - +### Codyze +| Key | Value | Description | Default Value | +|:--------------|:--------|:-----------------------------------------------------------------------------------------------------------------------|:--------------| +| output | Path | The path to write the results file to. | `[./]` | +| output-format | String | Format in which the analysis results are returned. | `sarif` | +| good-findings | Boolean | Enable/Disable output of "positive" findings which indicate correct implementations. | `true` | +| pedantic | Boolean | Activates pedantic analysis mode. In this mode, Codyze analyzes all given specification files and report all findings. | `false` | + +### Executors +#### Coko +| Key | Value | Description | Default Value | +|:--------------------|:---------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------| +| spec | Path[] | Paths to CoKo rule files. | `[./]` | +| disabled-specs | Path[] | The specified files will be excluded from being parsed and processed. | `[]` | +| disabled-spec-rules | String[] | Rules that will be ignored by the analysis. | `[]` | + +### Backends +#### Cpg +| Key | Value | Description | Default Value | +|:--------------------------|:--------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------| +| source | Path[] | Path to the to be analyzed files or directories. | `[./]` | +| disabled-source | Path[] | Path to files or directories which should not be analyzed. Symbolic links are not followed when filtering out these paths. | `[]` | +| additional-language | String[] | Specify programming languages of files to be analyzed (full names). | `[]` | +| unity | boolean | Only relevant for C++. A unity build refers to a build that consolidates all translation units into a single one, which has the advantage that header files are only processed once, adding far less duplicate nodes to the graph. | false | +| type-system-in-frontend | boolean | If false, the type listener system is only activated once the frontends are done building the initial AST structure. This avoids errors where the type of a node may depend on the order in which the source files have been parsed. | true | +| default-passes | boolean | Adds all default passes in cpg (1. FilenameMapper, 2. TypeHierarchyResolver, 3. ImportResolver, 4. VariableUsageResolver, 5. CallResolver, 6. EvaluationOrderGraphPass, 7. TypeResolver). | true | +| passes | String[] | Register these passes to be executed in the specified order. Please specify the passes with their fully qualified name. | `[]` | +| debug-parser | boolean | Enables debug output generation for the cpg parser. | false | +| disable-cleanup | boolean | Switch off cleaning up TypeManager memory after analysis, set to true only for testing. | false | +| code-in-nodes | boolean | Should the code of a node be shown as parameter in the node. | false | +| annotations | boolean | Enables processing annotations or annotation-like elements. | false | +| fail-on-error | boolean | Should parser/translation fail on parse/resolving errors (true) or try to continue in a best-effort manner (false). | false | +| symbols | Map | Definition of additional symbols. | `{}` | +| parallel-frontends | boolean | If true, the ASTs for the source files are parsed in parallel, but the passes afterwards will still run in a single thread. This speeds up initial parsing but makes sure that further graph enrichment algorithms remain correct. | false | +| match-comments-to-nodes | boolean | Controls whether the CPG frontend shall use a heuristic matching of comments found in the source file to match them to the closest AST node and save it in the comment property. | false | +| analyze-includes | boolean | Enables parsing of include files. If includePaths are given, the parser will resolve symbols/templates from these in include but not load their parse tree. | false | +| includes | Path[] | Paths containing include files. | `[]` | +| enabled-includes | Path[] | If includes is not empty, only the specified files will be parsed and processed in the cpg, unless it is a part of the disabled list, in which it will be ignored. | `[]` | +| disabled-includes | Path[] | If includes is not empty, the specified includes files will be excluded from being parsed and processed in the cpg. The disabled list entries always take priority over the enabled list entries. | `[]` | diff --git a/docs/Getting Started/installation.md b/docs/Getting Started/installation.md index cee0aada9..b85734644 100644 --- a/docs/Getting Started/installation.md +++ b/docs/Getting Started/installation.md @@ -13,7 +13,7 @@ description: > ## Build from Source -Clone the source code for Codyze from the [project's GitHub repository ](https://github.com/Fraunhofer-AISEC/codyze). +Clone the source code for Codyze from the [project's GitHub repository ](https://github.com/Fraunhofer-AISEC/codyze){target=_blank}. ### Codyze v3 ##### Executable Version @@ -32,10 +32,10 @@ Arguments can be passed with the `--args` option. All following example calls in this documentation will assume that the source code was cloned and use the exact file structure. If you want to test Codyze with these calls, please clone the repository. -* Download a zipped release of Codyze from our [GitHub release page ](https://github.com/Fraunhofer-AISEC/codyze/releases) +* Download a zipped release of Codyze from our [GitHub release page ](https://github.com/Fraunhofer-AISEC/codyze/releases){target=_blank} * Unzip the file * Execute Codyze using `bin\codyze.bat` (Windows) or `bin/codyze` (Mac, Linux) ## Docker -We're also offering Codyze as a container image. You can find an image with the latest release in the [project's container registry ](https://github.com/Fraunhofer-AISEC/codyze/pkgs/container/codyze). +We're also offering Codyze as a container image. You can find an image with the latest release in the [project's container registry ](https://github.com/Fraunhofer-AISEC/codyze/pkgs/container/codyze){target=_blank}. diff --git a/docs/assets/img/eclipse-formatter.png b/docs/assets/img/eclipse-formatter.png deleted file mode 100644 index 27088047b358ed8f2984cb9655069eec86d3e74f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64410 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 A82|tP diff --git a/docs/assets/img/intellij-formatter.png b/docs/assets/img/intellij-formatter.png deleted file mode 100644 index 32e9e0209134b8218d58c18afb1556c0371d319b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 158529 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%0A5bi@fLouA$98 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%0A&>@! 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 af9zeVMSmMT*>aNs2s~Z=T-G@yGywooyHmLU diff --git a/docs/contributors/CONTRIBUTING.md b/docs/contributors/CONTRIBUTING.md index 4c5115b44..9e02c7866 100644 --- a/docs/contributors/CONTRIBUTING.md +++ b/docs/contributors/CONTRIBUTING.md @@ -8,7 +8,7 @@ just a few small guidelines you need to follow. Contributions to this project must be accompanied by a Contributor License Agreement. You (or your employer) retain the copyright to your contribution; this simply gives us permission to use and redistribute your contributions as -part of the project. Head over to to see +part of the project. Head over to [here ](https://cla.developers.google.com/){target=_blank} to see your current agreements on file or to sign a new one. You generally only need to submit a CLA once, so if you've already submitted one @@ -19,10 +19,10 @@ again. All submissions, including submissions by project members, require review. We use GitHub pull requests for this purpose. Consult -[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more +[GitHub Help ](https://help.github.com/articles/about-pull-requests/){target=_blank} for more information on using pull requests. ## Community Guidelines This project follows -[Google's Open Source Community Guidelines](https://opensource.google.com/conduct/). +[Google's Open Source Community Guidelines ](https://opensource.google.com/conduct/){target=_blank}. diff --git a/docs/contributors/code.md b/docs/contributors/code.md index 2a004def4..ab6e441bc 100644 --- a/docs/contributors/code.md +++ b/docs/contributors/code.md @@ -14,7 +14,7 @@ description: > ## Check out, build, and extend Codyze -1. Fork the [Codyze repo](https://github.com/Fraunhofer-AISEC/codyze) on GitHub and clone it, including submodules:
`git clone --recurse-submodules git://github.com//codyze` +1. Fork the [Codyze repo ](https://github.com/Fraunhofer-AISEC/codyze){target=_blank} on GitHub and clone it:
`git clone git://github.com//codyze` 1. Create a branch for your contribution. We recommend prefixing the branch name with `fix-` if you are providing a bug fix or `feature-` if you plan to add a feature:
`git checkout -b feature-MY_EPIC_FEATURE` 1. Make your changes and make sure that the project builds without errors, passes all tests, and is properly formatted: `./gradlew :codyze-cli:clean :codyze-cli:spotlessApply :codyze-cli:build :codyze-cli:installDist`
The purpose of these gradle tasks is as follows: @@ -36,16 +36,7 @@ Codyze uses the gradle _spotless_ plugin to format source code. You may import t * Simply run `./gradlew :codyze-cli:spotlessApply` -### IntelliJ +### IDEs -* In IntelliJ, open _Settings->Editor->Code Style->Java_ and click on the cog icon. -* Choose _Import Scheme->Eclipse XML Profile_ and select the file `formatter-settings.xml` in the project's root folder. - -![Import code style formatter into IntelliJ](../assets/img/intellij-formatter.png){ align=center } - -### Eclipse - -* In Eclipse, open _Window->Preferences->Java->Code Style->Formatter_ -* Click _Import_ and select the file `formatter-settings.xml` in the project's root folder. - -![Import code style formatter into Eclipse](../assets/img/eclipse-formatter.png){ align=center } +You may find plugins for your favorite IDE that can execute Spotless within your IDE for you. +Please refer to your respective IDE. diff --git a/docs/contributors/index.md b/docs/contributors/index.md index 9f79daff5..1dc1e0255 100644 --- a/docs/contributors/index.md +++ b/docs/contributors/index.md @@ -22,11 +22,11 @@ We welcome everybody to contribute to this project, be it fixing a typo in the d This documentation lives in the same repository as the code and the idea is that both are always consistent. If you make changes in the code that affect the documentation, please update the documentation in the same pull request. -We use [MkDocs](https://github.com/mkdocs/mkdocs/) for generating our documentation site and [Material for MkDocs](https://github.com/squidfunk/mkdocs-material) as our theme. +We use [MkDocs ](https://github.com/mkdocs/mkdocs/){target=_blank} for generating our documentation site and [Material for MkDocs ](https://github.com/squidfunk/mkdocs-material){target=_blank} as our theme. To extend or fix errors in this documentation, proceed as follows: -1. Fork the [Codyze repo](https://github.com/Fraunhofer-AISEC/codyze) on GitHub. +1. Fork the [Codyze repo ](https://github.com/Fraunhofer-AISEC/codyze){target=_blank} on GitHub. 1. Make your changes and send a pull request (PR). 1. If you're not yet ready for a review, add "WIP" to the PR name to indicate it's a work in progress. @@ -39,13 +39,13 @@ To extend or fix errors in this documentation, proceed as follows: If you want to run your own local server to preview your changes as you work, you can either install MkDocs or use a docker. -For installing MkDocs, please refer to the [Material for MkDocs documentation](https://squidfunk.github.io/mkdocs-material/getting-started/). +For installing MkDocs, please refer to the [Material for MkDocs documentation ](https://squidfunk.github.io/mkdocs-material/getting-started/){target=_blank}. -For the docker, please refer to the [README](https://github.com/Fraunhofer-AISEC/codyze/tree/main/docs/README.md). +For the docker, please refer to the [README ](https://github.com/Fraunhofer-AISEC/codyze/tree/main/docs/README.md){target=_blank}. ### Creating an issue -If you've found a problem in the docs, but you're not sure how to fix it yourself, please create an issue in the [Codyze repo](https://github.com/Fraunhofer-AISEC/codyze/issues) and add the label `documentation` to it. You can also create an issue about a specific page by clicking the **Create Issue** button in the top right hand corner of the page. +If you've found a problem in the docs, but you're not sure how to fix it yourself, please create an issue in the [Codyze repo ](https://github.com/Fraunhofer-AISEC/codyze/issues){target=_blank} and add the label `documentation` to it. You can also create an issue about a specific page by clicking the **Create Issue** button in the top right hand corner of the page. ## Contributing to Codyze @@ -62,8 +62,8 @@ To report a bug, make sure to include the following information: ### Useful resources -* [Material for MkDocs user guide](https://squidfunk.github.io/mkdocs-material/): All about Material, the documentation template we use, including how it manages navigation, look and feel, and multi-language support. -* [MkDocs user guide](https://www.mkdocs.org/): Comprehensive reference for MkDocs. -* [GitHub Hello World!](https://guides.github.com/activities/hello-world/): A basic introduction to GitHub concepts and workflow. +* [Material for MkDocs user guide ](https://squidfunk.github.io/mkdocs-material/){target=_blank}: All about Material, the documentation template we use, including how it manages navigation, look and feel, and multi-language support. +* [MkDocs user guide ](https://www.mkdocs.org/){target=_blank}: Comprehensive reference for MkDocs. +* [GitHub Hello World! ](https://guides.github.com/activities/hello-world/){target=_blank}: A basic introduction to GitHub concepts and workflow. diff --git a/docs/theme-overrides/home.html b/docs/theme-overrides/home.html index b3b284a26..b5327fe1f 100644 --- a/docs/theme-overrides/home.html +++ b/docs/theme-overrides/home.html @@ -1,11 +1,5 @@ {% extends "main-styles.html" %} -{% block extrahead %} - {{ super() }} - - -{% endblock %} - {% block tabs %} {{ super() }} @@ -24,7 +18,7 @@

Codyze: Automated Code Compliance

Get started - + Contribute on GitHub @@ -74,12 +68,7 @@

Verifying Compliance to Se

Codyze is a static analysis tool to support developers in verifying compliance to security requirements.

-

Head to the release page and download Codyze.
- We offer IDE plugins for Eclipse, - Visual Studio and - Visual Studio Code. - Moreover, we support LSP for integration into your favorite IDE or text editor. -

+

Head to the release page and download Codyze.

@@ -113,7 +102,7 @@

Develop and Contribute

We are happy to accept pull requests for bugfixes and improvements on GitHub!

-

If you are interested to write a bachelor/master/PhD thesis in static analysis, we encourage you to get in contact with us. Besides the open research topics offered by the Chair of IT Security at the Technical University of Munich, we are open to discuss further topics which might be of interest for you and us.

+

If you are interested to write a bachelor/master/PhD thesis in static analysis, we encourage you to get in contact with us. Besides the open research topics offered by the Chair of IT Security at the Technical University of Munich , we are open to discuss further topics which might be of interest for you and us.

diff --git a/docs/theme-overrides/main-styles.html b/docs/theme-overrides/main-styles.html index 7ade47fd3..c94953835 100644 --- a/docs/theme-overrides/main-styles.html +++ b/docs/theme-overrides/main-styles.html @@ -142,6 +142,14 @@ color: white; } + .feature-item--text a { + font-size: 16px; + line-height: 1.8em; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + color: #5981b4; + } + .video-container { overflow: hidden; position: relative; @@ -205,6 +213,14 @@ color: white; } + .community-item a { + font-size: 16px; + line-height: 1.8em; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + color: #5981b4; + } + @media screen and (max-width:30em) { .tx-hero h1 { font-size: 1.4rem diff --git a/docs/theme-overrides/main.html b/docs/theme-overrides/main.html new file mode 100644 index 000000000..992967707 --- /dev/null +++ b/docs/theme-overrides/main.html @@ -0,0 +1,7 @@ +{% extends "base.html" %} + +{% block extrahead %} +{{ super() }} + + +{% endblock %} \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index cdab38dea..e4e814604 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -158,8 +158,12 @@ nav: - 'Installing Codyze': Getting Started/installation.md - 'Analyzing with Codyze': Getting Started/cli.md - 'Configuring Codyze': Getting Started/configuration.md + - 'Write Coko Policies': + - 'What is Coko?': Coko/index.md + - 'Modelling APIs with Coko': Coko/modelling.md + - 'Coko Rules': Coko/rules.md - 'Contributing': - 'Contributing to Codyze': contributors/index.md - 'Building the project': contributors/code.md - 'How to Contribute?': contributors/CONTRIBUTING.md - - 'API': api/codyze \ No newline at end of file + - "

API  

": api/codyze" target="_blank \ No newline at end of file From 92dd3bc78862a76f7942f7f642edd1d0ae8189ab Mon Sep 17 00:00:00 2001 From: Selina Lin Date: Mon, 17 Apr 2023 15:13:19 +0200 Subject: [PATCH 007/121] Fix bug with loading order of the spec files (#718) Co-authored-by: Florian Wendland --- buildSrc/src/main/kotlin/publish.gradle.kts | 4 +- codyze-backends/cpg/build.gradle.kts | 2 +- .../coko/dsl/host/CokoExecutor.kt | 48 +++-- .../coko/dsl/host/SpecEvaluator.kt | 4 + .../coko/dsl/CokoCpgIntegrationTest.kt | 187 +++++++++++++++--- .../followedByImplementations.codyze.kts | 33 ++++ .../followedByInterfaces.codyze.kts | 11 ++ .../followedByRule.codyze.kts | 6 + .../followedByImplementations.codyze.kts} | 0 .../followedByModels.codyze.kts | 0 ...erRule.codyze.kts => orderFull.codyze.kts} | 0 .../orderImplementations.codyze.kts | 6 + .../orderTwoFiles/orderRule.codyze.kts | 8 + detekt.yml | 1 + 14 files changed, 263 insertions(+), 47 deletions(-) create mode 100644 codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByThreeFiles/followedByImplementations.codyze.kts create mode 100644 codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByThreeFiles/followedByInterfaces.codyze.kts create mode 100644 codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByThreeFiles/followedByRule.codyze.kts rename codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/{followedBy.codyze.kts => followedByTwoFiles/followedByImplementations.codyze.kts} (100%) rename codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/{ => followedByTwoFiles}/followedByModels.codyze.kts (100%) rename codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/{orderRule.codyze.kts => orderFull.codyze.kts} (100%) create mode 100644 codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/orderTwoFiles/orderImplementations.codyze.kts create mode 100644 codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/orderTwoFiles/orderRule.codyze.kts diff --git a/buildSrc/src/main/kotlin/publish.gradle.kts b/buildSrc/src/main/kotlin/publish.gradle.kts index 2681fb066..19c2b377b 100644 --- a/buildSrc/src/main/kotlin/publish.gradle.kts +++ b/buildSrc/src/main/kotlin/publish.gradle.kts @@ -9,7 +9,7 @@ plugins { } java { - //withJavadocJar() // using custom JavaDoc from Dokka; FIXME maybe there is a better way? + // withJavadocJar() // using custom JavaDoc from Dokka; FIXME maybe there is a better way? withSourcesJar() } @@ -54,4 +54,4 @@ publishing { } } } -} \ No newline at end of file +} diff --git a/codyze-backends/cpg/build.gradle.kts b/codyze-backends/cpg/build.gradle.kts index db81752dd..ea115aa87 100644 --- a/codyze-backends/cpg/build.gradle.kts +++ b/codyze-backends/cpg/build.gradle.kts @@ -24,4 +24,4 @@ publishing { } } } -} \ No newline at end of file +} diff --git a/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/CokoExecutor.kt b/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/CokoExecutor.kt index 7c70a37d9..aea4885a9 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/CokoExecutor.kt +++ b/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/CokoExecutor.kt @@ -75,15 +75,22 @@ class CokoExecutor(private val configuration: CokoConfiguration, private val bac } companion object { + /** Contains the class loaders of all evaluated scripts */ + private val classLoaders = mutableSetOf() + /** Evaluates the given project script [sourceCode] against the given [backend]. */ - fun eval(sourceCode: String, backend: CokoBackend, sharedClassLoader: ClassLoader? = null) = + fun eval( + sourceCode: String, + backend: CokoBackend, + sharedClassLoader: ClassLoader? = null + ) = eval(sourceCode.toScriptSource(), backend, sharedClassLoader) /** Evaluates the given project script [sourceCode] against the given [backend]. */ fun eval( sourceCode: SourceCode, backend: CokoBackend, - sharedClassLoader: ClassLoader? = null + baseClassLoader: ClassLoader? = null ): ResultWithDiagnostics { val compilationConfiguration = createJvmCompilationConfigurationFromTemplate() @@ -91,8 +98,10 @@ class CokoExecutor(private val configuration: CokoConfiguration, private val bac createJvmEvaluationConfigurationFromTemplate { implicitReceivers(backend) jvm { - if (sharedClassLoader != null) { - baseClassLoader.put(sharedClassLoader) + if (baseClassLoader != null) { + // The BasicJvmScriptingHost will use this `baseClassLoader` as an ancestor + // for the actual class loader that loads the compiled class + this.baseClassLoader.put(baseClassLoader) } } } @@ -116,7 +125,7 @@ class CokoExecutor(private val configuration: CokoConfiguration, private val bac backend: CokoBackend, specFiles: List ): SpecEvaluator { - var sharedClassLoader: ClassLoader? = null + var baseClassLoader: ClassLoader? = null val specEvaluator = SpecEvaluator() for (specFile in specFiles) { // compile the script @@ -124,7 +133,7 @@ class CokoExecutor(private val configuration: CokoConfiguration, private val bac eval( FileScriptSource(specFile.toFile()), backend = backend, - sharedClassLoader = sharedClassLoader + baseClassLoader = baseClassLoader ) // log script diagnostics @@ -144,13 +153,26 @@ class CokoExecutor(private val configuration: CokoConfiguration, private val bac // throw an exception if the script could not be compiled val scriptEvaluationResult = result.valueOrThrow() - // Get the class loader for the first loaded script. We give that one to all the - // other scripts to ensure they find "the same" classes. - if (sharedClassLoader == null) { - sharedClassLoader = - scriptEvaluationResult.configuration?.get( - PropertiesCollection.Key("actualClassLoader") - ) + // Get the class loader for the loaded script. + val newClassLoader = scriptEvaluationResult.configuration?.get( + PropertiesCollection.Key("actualClassLoader") + ) + + // The ScriptingHost makes a class loader for each script. + // We need to connect these class loaders of the scripts to be able to find all classes. + // This is done by creating a chaining them through the `parent` property of the ClassLoader. + // The BasicJVMScriptingHost will assign the given `baseClassLoader` as an ancestor for + // the new class loader for the compiled script. + + // Since a script might import other scripts, some classes are already included in a class + // loader when they are first compiled. + // Since their assigned class loader will be the class loader that includes them, the chain + // might be broken. All previous class loaders are therefore stored in the `classLoaders` set. + // If `newClassLoader` is not yet in `classLoaders` it is used as the `baseClassLoader` for the + // next script to continue the chain. Otherwise, the previous `baseClassLoader` is used again, + // since it is still the last class loader of the chain. + if (newClassLoader != null && classLoaders.add(newClassLoader)) { + baseClassLoader = newClassLoader } // analyze script contents diff --git a/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/SpecEvaluator.kt b/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/SpecEvaluator.kt index e2d42833a..632f9ae39 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/SpecEvaluator.kt +++ b/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/SpecEvaluator.kt @@ -60,6 +60,7 @@ class SpecEvaluator { val results = mutableMapOf>() for ((index, value) in rulesAndInstances.withIndex()) { val (rule, ruleInstance) = value + logger.info { "Start evaluating rule `${rule.name}`" } val parameterMap = mutableMapOf( rule.instanceParameter!! to ruleInstance @@ -90,8 +91,11 @@ class SpecEvaluator { } parameterMap.putAll(valueParameterMap) + logger.debug { "Obtained parameters for calling rule `${rule.name}`: $parameterMap" } + logger.debug { "${ruleInstance::class.java.classLoader}" } val ruleEvaluator = rule.callBy(parameterMap) + logger.debug { "Obtained evaluator for rule `${rule.name}`" } val ruleResult = ruleEvaluator.evaluate(EvaluationContext(rule = rule, parameterMap = valueParameterMap)) logger.info { " (${index + 1}/${rules.size}): ${rule.name} generated " + diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoCpgIntegrationTest.kt b/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoCpgIntegrationTest.kt index 3db709314..9af415894 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoCpgIntegrationTest.kt +++ b/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoCpgIntegrationTest.kt @@ -23,6 +23,11 @@ import de.fraunhofer.aisec.cpg.passes.UnreachableEOGPass import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import java.nio.file.Path +import java.util.stream.Stream import kotlin.io.path.* import kotlin.test.* @@ -65,7 +70,7 @@ class CokoCpgIntegrationTest { fun `test coko with cpg backend and multiple spec files`() { val specFiles = listOfNotNull( CokoCpgIntegrationTest::class.java.classLoader - .getResource("IntegrationTests/CokoCpg/orderRule.codyze.kts"), + .getResource("IntegrationTests/CokoCpg/orderFull.codyze.kts"), CokoCpgIntegrationTest::class.java.classLoader .getResource("IntegrationTests/CokoCpg/followedByFull.codyze.kts") ).map { Path(it.path) }.also { assertEquals(2, it.size) } @@ -92,12 +97,12 @@ class CokoCpgIntegrationTest { * And uses two spec files where one imports the other. */ @Test - fun `test coko with cpg backend and dependend spec files`() { + fun `test coko with cpg backend and dependent spec files`() { val specFiles = listOfNotNull( CokoCpgIntegrationTest::class.java.classLoader - .getResource("IntegrationTests/CokoCpg/followedBy.codyze.kts"), + .getResource("IntegrationTests/CokoCpg/followedByTwoFiles/followedByImplementations.codyze.kts"), CokoCpgIntegrationTest::class.java.classLoader - .getResource("IntegrationTests/CokoCpg/followedByModels.codyze.kts") + .getResource("IntegrationTests/CokoCpg/followedByTwoFiles/followedByModels.codyze.kts") ).map { Path(it.path) }.also { assertEquals(2, it.size) } val cokoConfiguration = @@ -119,18 +124,17 @@ class CokoCpgIntegrationTest { /** * Performs an end-to-end integration test of Codyze with the [CokoExecutor] and the [CokoCpgBackend] backend. - * Uses three spec files. Where the first one imports the second one and the last one is standalone. + * Uses three spec files which are given through `specFiles`. One of the three files imports another script file. + * The order of the files in `specFiles` is permuted to verify that the order in which the spec files are evaluated + * does not have an impact on the results. */ - @Test - fun `test coko with cpg backend and multiple and multiple dependend spec files`() { - val specFiles = listOfNotNull( - CokoCpgIntegrationTest::class.java.classLoader - .getResource("IntegrationTests/CokoCpg/followedBy.codyze.kts"), - CokoCpgIntegrationTest::class.java.classLoader - .getResource("IntegrationTests/CokoCpg/followedByModels.codyze.kts"), - CokoCpgIntegrationTest::class.java.classLoader - .getResource("IntegrationTests/CokoCpg/orderRule.codyze.kts"), - ).map { Path(it.path) }.also { assertEquals(3, it.size) } + @ParameterizedTest(name = "{index} {1}") + @MethodSource("threeFiles") + fun `test coko with cpg backend and permutation of three dependent spec files`( + specFiles: List, + fileNames: List + ) { + assertEquals(3, specFiles.size) val cokoConfiguration = CokoConfiguration( @@ -144,26 +148,22 @@ class CokoCpgIntegrationTest { val executor = CokoExecutor(cokoConfiguration, backend) val run = executor.evaluate() - - // assertions for the order rule assertEquals(run.results?.size, 16) } /** * Performs an end-to-end integration test of Codyze with the [CokoExecutor] and the [CokoCpgBackend] backend. - * Uses three spec files. Where the second one imports the third one and the first one is standalone. + * Uses four spec files which are given through `specFiles`. There are two files which each import another file. + * The order of the files in `specFiles` is permuted to verify that the order in which the spec files are evaluated + * does not have an impact on the results. */ - @Disabled("Produces an argument type mismatch error. Probably a bug in the SpecEvaluator...") - @Test - fun `test coko with cpg backend and multiple and multiple dependend spec files two`() { - val specFiles = listOfNotNull( - CokoCpgIntegrationTest::class.java.classLoader - .getResource("IntegrationTests/CokoCpg/orderRule.codyze.kts"), - CokoCpgIntegrationTest::class.java.classLoader - .getResource("IntegrationTests/CokoCpg/followedBy.codyze.kts"), - CokoCpgIntegrationTest::class.java.classLoader - .getResource("IntegrationTests/CokoCpg/followedByModels.codyze.kts"), - ).map { Path(it.path) }.also { assertEquals(3, it.size) } + @ParameterizedTest(name = "{index} {1}") + @MethodSource("fourFiles") + fun `test coko with cpg backend and permutation of four dependent spec files`( + specFiles: List, + fileNames: List + ) { + assertEquals(4, specFiles.size) val cokoConfiguration = CokoConfiguration( @@ -177,8 +177,37 @@ class CokoCpgIntegrationTest { val executor = CokoExecutor(cokoConfiguration, backend) val run = executor.evaluate() + assertEquals(run.results?.size, 16) + } - // assertions for the order rule + /** + * Performs an end-to-end integration test of Codyze with the [CokoExecutor] and the [CokoCpgBackend] backend. + * Uses four spec fileswhich are given through `specFiles`. There are three files which each import another file. + * Two of these three files import the same file. + * The order of the files in `specFiles` is permuted to verify that the order in which the spec files are evaluated + * does not have an impact on the results. + */ + @Disabled("Too many permutations (120) of the specFiles order") + @ParameterizedTest(name = "{index} {1}") + @MethodSource("fiveFiles") + fun `test coko with cpg backend and permutation of five dependent spec files`( + specFiles: List, + fileNames: List + ) { + assertEquals(5, specFiles.size) + + val cokoConfiguration = + CokoConfiguration( + goodFindings = true, + pedantic = false, + spec = specFiles, + disabledSpecRules = emptyList(), + ) + + val backend = CokoCpgBackend(cpgConfiguration) + val executor = CokoExecutor(cokoConfiguration, backend) + + val run = executor.evaluate() assertEquals(run.results?.size, 16) } @@ -189,7 +218,7 @@ class CokoCpgIntegrationTest { @Test fun `test coko with cpg backend without good findings`() { val specFiles = listOfNotNull( - CokoCpgIntegrationTest::class.java.classLoader.getResource("IntegrationTests/CokoCpg/orderRule.codyze.kts"), + CokoCpgIntegrationTest::class.java.classLoader.getResource("IntegrationTests/CokoCpg/orderFull.codyze.kts"), ).map { Path(it.path) }.also { assertEquals(1, it.size) } val cokoConfiguration = @@ -206,4 +235,100 @@ class CokoCpgIntegrationTest { val run = executor.evaluate() assertEquals(7, run.results?.size) } + + companion object { + @JvmStatic + fun threeFiles(): Stream { + val stream = Stream.builder() + val fileMap = mapOf( + '1' to CokoCpgIntegrationTest::class.java.classLoader + .getResource("IntegrationTests/CokoCpg/orderFull.codyze.kts"), + '2' to CokoCpgIntegrationTest::class.java.classLoader + .getResource("IntegrationTests/CokoCpg/followedByTwoFiles/followedByImplementations.codyze.kts"), + '3' to CokoCpgIntegrationTest::class.java.classLoader + .getResource("IntegrationTests/CokoCpg/followedByTwoFiles/followedByModels.codyze.kts"), + ) + val permutations = fileMap.permutate() + for (p in permutations) { + val (specFiles, fileNames) = p.map { Path(it.path) }.map { it to it.fileName }.unzip() + stream.add( + Arguments.of( + specFiles, + fileNames + ) + ) + } + return stream.build() + } + + @JvmStatic + fun fourFiles(): Stream { + val stream = Stream.builder() + val fileMap = mapOf( + '1' to CokoCpgIntegrationTest::class.java.classLoader + .getResource("IntegrationTests/CokoCpg/orderTwoFiles/orderRule.codyze.kts"), + '2' to CokoCpgIntegrationTest::class.java.classLoader + .getResource("IntegrationTests/CokoCpg/followedByTwoFiles/followedByImplementations.codyze.kts"), + '3' to CokoCpgIntegrationTest::class.java.classLoader + .getResource("IntegrationTests/CokoCpg/followedByTwoFiles/followedByModels.codyze.kts"), + '4' to CokoCpgIntegrationTest::class.java.classLoader + .getResource("IntegrationTests/CokoCpg/orderTwoFiles/orderImplementations.codyze.kts"), + ) + val permutations = fileMap.permutate() + for (p in permutations) { + val (specFiles, fileNames) = p.map { Path(it.path) }.map { it to it.fileName }.unzip() + stream.add( + Arguments.of( + specFiles, + fileNames + ) + ) + } + return stream.build() + } + + @JvmStatic + fun fiveFiles(): Stream { + val stream = Stream.builder() + val fileMap = mapOf( + '1' to CokoCpgIntegrationTest::class.java.classLoader + .getResource("IntegrationTests/CokoCpg/orderTwoFiles/orderRule.codyze.kts"), + '2' to CokoCpgIntegrationTest::class.java.classLoader + .getResource("IntegrationTests/CokoCpg/followedByThreeFiles/followedByImplementations.codyze.kts"), + '3' to CokoCpgIntegrationTest::class.java.classLoader + .getResource("IntegrationTests/CokoCpg/followedByThreeFiles/followedByRule.codyze.kts"), + '4' to CokoCpgIntegrationTest::class.java.classLoader + .getResource("IntegrationTests/CokoCpg/followedByThreeFiles/followedByInterfaces.codyze.kts"), + '5' to CokoCpgIntegrationTest::class.java.classLoader + .getResource("IntegrationTests/CokoCpg/orderTwoFiles/orderImplementations.codyze.kts"), + ) + val permutations = fileMap.permutate() + for (p in permutations) { + val (specFiles, fileNames) = p.map { Path(it.path) }.map { it to it.fileName }.unzip() + stream.add( + Arguments.of( + specFiles, + fileNames + ) + ) + } + return stream.build() + } + + private fun Map.permutate(): List> { + val key = String(this.keys.toCharArray()) + val keyPermutations = key.permute() + + val permutations = mutableListOf>() + + for (p in keyPermutations) { + permutations.add(p.mapNotNull { this[it] }) + } + + return permutations + } + + private fun String.permute(result: String = ""): List = + if (isEmpty()) listOf(result) else flatMapIndexed { i, c -> removeRange(i, i + 1).permute(result + c) } + } } diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByThreeFiles/followedByImplementations.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByThreeFiles/followedByImplementations.codyze.kts new file mode 100644 index 000000000..cb70a51bc --- /dev/null +++ b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByThreeFiles/followedByImplementations.codyze.kts @@ -0,0 +1,33 @@ +@file:Import("followedByInterfaces.codyze.kts") + +plugins { id("cpg") } + +class JavaLoggingTest : LoggingForTest { + override fun log(message: String?, vararg args: Any?) = op { + definition("java.util.logging.Logger.info") { + signature { + group { + +message + args.forEach { +it } + } + } + } + } +} + +class JDBCTest : ObjectRelationalMapperForTest { + override fun insert(obj: Any?) = op { + definition("java.sql.Statement.executeUpdate") { + signature { + group { + +"INSERT.*" + +obj + } + } + } + } +} + +class JavalinJWTTest : UserContextForTest { + override val user = cpgCallFqn("javalinjwt.JavalinJWT.getTokenFromHeader") +} diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByThreeFiles/followedByInterfaces.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByThreeFiles/followedByInterfaces.codyze.kts new file mode 100644 index 000000000..fb8bf1433 --- /dev/null +++ b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByThreeFiles/followedByInterfaces.codyze.kts @@ -0,0 +1,11 @@ +interface LoggingForTest { + fun log(message: String?, vararg args: Any?): Op +} + +interface ObjectRelationalMapperForTest { + fun insert(obj: Any?): Op +} + +interface UserContextForTest { + val user: Any +} diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByThreeFiles/followedByRule.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByThreeFiles/followedByRule.codyze.kts new file mode 100644 index 000000000..540bd87a6 --- /dev/null +++ b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByThreeFiles/followedByRule.codyze.kts @@ -0,0 +1,6 @@ +@file:Import("followedByInterfaces.codyze.kts") + +// TODO: can we "assert" that the ctx.user is not null here? +@Rule("This is a dummy description.") +fun DBActionsAreAlwaysLogged(db: ObjectRelationalMapperForTest, log: LoggingForTest, ctx: UserContextForTest) = + db.insert(Wildcard) followedBy log.log(".*", ctx.user) \ No newline at end of file diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedBy.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByTwoFiles/followedByImplementations.codyze.kts similarity index 100% rename from codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedBy.codyze.kts rename to codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByTwoFiles/followedByImplementations.codyze.kts diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByModels.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByTwoFiles/followedByModels.codyze.kts similarity index 100% rename from codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByModels.codyze.kts rename to codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByTwoFiles/followedByModels.codyze.kts diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/orderRule.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/orderFull.codyze.kts similarity index 100% rename from codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/orderRule.codyze.kts rename to codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/orderFull.codyze.kts diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/orderTwoFiles/orderImplementations.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/orderTwoFiles/orderImplementations.codyze.kts new file mode 100644 index 000000000..f59e1dbbd --- /dev/null +++ b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/orderTwoFiles/orderImplementations.codyze.kts @@ -0,0 +1,6 @@ +class CokoOrderImpl { + fun constructor(value: Int?) = constructor("Botan") { signature(value) } + fun init() = op { definition("Botan.set_key") { signature(Wildcard) } } + fun start() = op { definition("Botan.start") { signature(Wildcard) } } + fun finish() = op { definition("Botan.finish") { signature(Wildcard) } } +} \ No newline at end of file diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/orderTwoFiles/orderRule.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/orderTwoFiles/orderRule.codyze.kts new file mode 100644 index 000000000..a91c2e2f9 --- /dev/null +++ b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/orderTwoFiles/orderRule.codyze.kts @@ -0,0 +1,8 @@ +@file:Import("orderImplementations.codyze.kts") + +@Rule("simple order evaluation") +fun `validate CokoOrderImpl usage order`(testObj: CokoOrderImpl) = + order(testObj::constructor) { + +testObj::start + +testObj::finish + } \ No newline at end of file diff --git a/detekt.yml b/detekt.yml index 0c802923f..22d2a8d2d 100644 --- a/detekt.yml +++ b/detekt.yml @@ -685,6 +685,7 @@ style: UnusedPrivateMember: active: true allowedNames: '(_|ignored|expected|serialVersionUID)' + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**', '**/*.kts'] UseAnyOrNoneInsteadOfFind: active: true UseArrayLiteralsInAnnotations: From 6c80c74f1e82911d1183b5d85c7542420a0dcfdd Mon Sep 17 00:00:00 2001 From: maximilian-galanis Date: Tue, 18 Apr 2023 11:08:19 +0200 Subject: [PATCH 008/121] Improve coko dsl (#706) Co-authored-by: Selina Lin Co-authored-by: Wendland, Florian --- .../cpg/coko/evaluators/OrderEvaluator.kt | 67 ++++--- .../coko/ordering/CodyzeDfaOrderEvaluator.kt | 16 +- .../backends/cpg/NfaDfaConstructionTest.kt | 136 +++++++------- .../backends/cpg/OrderEvaluationTest.kt | 26 +-- .../aisec/codyze/cli/CodyzeOptionGroup.kt | 1 + .../coko/core/dsl/Op.kt | 90 +++++---- .../coko/core/dsl/Order.kt | 65 ++----- .../coko/core/dsl/Types.kt | 4 +- .../coko/core/modelling/OpComponents.kt | 34 ++-- .../coko/core/ordering/Nodes.kt | 10 +- .../coko/core/ordering/OrderBuilder.kt | 20 +- .../coko/core/ordering/OrderFragment.kt | 5 +- .../coko/core/ordering/OrderSet.kt | 2 +- .../coko/core/ordering/OrderToken.kt | 3 + .../coko/core/dsl/OpDslTest.kt | 15 +- .../coko/core/modelling/EqualityTest.kt | 139 ++++++++++++++ .../coko/core/modelling/OpComponentsTest.kt | 22 ++- .../order/OrderSyntaxTreeConstructionTest.kt | 172 ++++++++++++------ .../bouncycastle/CipherImpl.codyze.kts | 56 +++--- .../CokoCpg/followedByFull.codyze.kts | 12 +- .../followedByImplementations.codyze.kts | 8 +- .../followedByImplementations.codyze.kts | 8 +- .../CokoCpg/orderFull.codyze.kts | 10 +- .../orderTwoFiles/orderRule.codyze.kts | 4 +- .../src/test/resources/javaimpl.codyze.kts | 8 +- .../src/test/resources/pyimpl.codyze.kts | 6 +- detekt.yml | 1 + 27 files changed, 584 insertions(+), 356 deletions(-) create mode 100644 codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/modelling/EqualityTest.kt diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OrderEvaluator.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OrderEvaluator.kt index 51cf86a3b..4b78aee52 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OrderEvaluator.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OrderEvaluator.kt @@ -17,6 +17,7 @@ package de.fraunhofer.aisec.codyze.backends.cpg.coko.evaluators import de.fraunhofer.aisec.codyze.backends.cpg.coko.CokoCpgBackend import de.fraunhofer.aisec.codyze.backends.cpg.coko.CpgFinding +import de.fraunhofer.aisec.codyze.backends.cpg.coko.Nodes import de.fraunhofer.aisec.codyze.backends.cpg.coko.dsl.cpgGetAllNodes import de.fraunhofer.aisec.codyze.backends.cpg.coko.dsl.cpgGetNodes import de.fraunhofer.aisec.codyze.backends.cpg.coko.ordering.CodyzeDfaOrderEvaluator @@ -77,6 +78,37 @@ class OrderEvaluator(val baseNodes: Collection, val order: Order) : Evalua return null } + /** + * Registers [op] and its [nodes] in the two maps. + * [nodesToOp] maps each Node in [nodes] to the `hashCode` of [op] which is used for the order evaluation. + * [hashToMethod] maps the `hashCode` of [op] to the String of [op] which is used for the findings. + */ + private fun registerOpAndNodes( + op: Op, + nodes: Nodes, + hashToMethod: MutableMap, + nodesToOp: MutableMap> + ) { + val hash = op.hashCode().toString() + hashToMethod.putIfAbsent(hash, op.toString()) + for (node in nodes) { + val setOfNames = nodesToOp.getOrPut(node) { mutableSetOf() } + setOfNames.add(hash) + } + } + + /** + * Filter positive findings that also appear as negative findings. + * This happens e.g., if there are multiple possible execution order flows (if-statement etc.) and + * the order is correct in one branch, but incorrect in another + */ + private fun filteredFindings(findings: Set): Set { + val passFindings = findings.filter { it.kind == Finding.Kind.Pass }.map { it.node to it }.toSet() + val failFindings = findings.filter { it.kind == Finding.Kind.Fail }.map { it.node }.toSet() + val positiveFindingsToRemove = passFindings.filter { passPair -> passPair.first in failFindings } + return findings.minus(positiveFindingsToRemove.map { it.second }.toSet()) + } + @Suppress("UnsafeCallOnNullableType", "ReturnCount") override fun evaluate(context: EvaluationContext): Set { // first check whether it is an order rule that we can evaluate @@ -104,34 +136,31 @@ class OrderEvaluator(val baseNodes: Collection, val order: Order) : Evalua // TODO: add nodesToOp to context (the cache) for other evaluations on the same implementation? val nodesToOp = mutableMapOf>() + val hashToMethod = mutableMapOf() // now call each of those functions to get the nodes corresponding to the OP // this will detect ALL calls to the specified methods/functions regardless of the defined signatures // this is the set of all nodes the [CodyzeDfaOrderEvaluator] considers during evaluation - for (op in opsInConcept) { - val nodes = ((op.call(implementation, *(List(op.parameters.size - 1) { null }.toTypedArray()))) as Op) - .cpgGetAllNodes() - for (node in nodes) { - val setOfNames = nodesToOp.getOrPut(node) { mutableSetOf() } - setOfNames.add(op.name) - } + for (orderToken in opsInConcept) { + val op = ( + orderToken.call(implementation, *(List(orderToken.parameters.size - 1) { null }.toTypedArray())) + ) as Op + val nodes = op.cpgGetAllNodes() + registerOpAndNodes(op, nodes, hashToMethod, nodesToOp) } // the more specific nodes respecting the signature here - // - // the nodes from +testObj::start.use { testObj.start(123) } <- use makes them userDefined + // the nodes from +testObj.start(123) <- userDefined Ops are used // only allow the start nodes that take '123' as argument - for ((opName, op) in order.userDefinedOps.entries) { + for ((_, op) in order.userDefinedOps.entries) { val nodes = op.cpgGetNodes() - for (node in nodes) { - val setOfNames = nodesToOp.getOrDefault(node, mutableSetOf()) - setOfNames.add(opName) - } + registerOpAndNodes(op, nodes, hashToMethod, nodesToOp) } // create the order evaluator for this order rule val dfaEvaluator = CodyzeDfaOrderEvaluator( dfa = dfa, + hashToMethod = hashToMethod, nodeToRelevantMethod = nodesToOp, consideredBases = baseNodes.map { node -> node.followNextEOG { it.end is AssignmentTarget }!!.last().end @@ -140,7 +169,7 @@ class OrderEvaluator(val baseNodes: Collection, val order: Order) : Evalua context = context, ) - // this should be a set of MethodDeclarations or a similar top level statements + // this should be a set of MethodDeclarations or similar top level statements val topLevelCompoundStatement = baseNodes.mapNotNull { node -> node.followPrevEOGUntilEnd() }.toSet() @@ -157,12 +186,6 @@ class OrderEvaluator(val baseNodes: Collection, val order: Order) : Evalua } // filter positive findings that also appear as negative findings - // this happens e.g., if there are multiple possible execution order flows (if-statement etc.) - // and the order is correct in one branch, but incorrect in another - val passFindings = dfaEvaluator.findings.filter { it.kind == Finding.Kind.Pass }.map { it.node to it }.toSet() - val failFindings = dfaEvaluator.findings.filter { it.kind == Finding.Kind.Fail }.map { it.node }.toSet() - val positiveFindingsToRemove = passFindings.filter { passPair -> passPair.first in failFindings } - dfaEvaluator.findings.removeAll(positiveFindingsToRemove.map { it.second }.toSet()) - return dfaEvaluator.findings + return filteredFindings(dfaEvaluator.findings) } } diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/ordering/CodyzeDfaOrderEvaluator.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/ordering/CodyzeDfaOrderEvaluator.kt index 6a9917a83..3eb2ff2b4 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/ordering/CodyzeDfaOrderEvaluator.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/ordering/CodyzeDfaOrderEvaluator.kt @@ -35,6 +35,7 @@ val logger = KotlinLogging.logger {} @Suppress("LongParameterList") class CodyzeDfaOrderEvaluator( val context: EvaluationContext, + val hashToMethod: Map, dfa: DFA, consideredBases: Set, nodeToRelevantMethod: Map>, @@ -56,13 +57,10 @@ class CodyzeDfaOrderEvaluator( .takeIf { it?.isEmpty() == false } @Suppress("UnsafeCallOnNullableType") - private fun getPossibleNextEdges(edges: Set?) = edges?.map { e -> - if (e.base != null) { - "${e.base!!.split("$").last()}.${e.op}" - } else { - e.op - } - }?.sorted() + private fun getPossibleNextEdges(edges: Set?) = edges?.mapNotNull { hashToMethod[it.op] }?.sorted() + + private fun getMethods(node: Node): Set = + nodeToRelevantMethod.getOrDefault(node, emptySet()).mapNotNull { hashToMethod[it] }.toSet() /** * Collects a finding if the [node] makes an operation which violates the desired order. @@ -71,12 +69,12 @@ class CodyzeDfaOrderEvaluator( val possibleNextEdges = getPossibleNextEdges(fsm.currentState?.outgoingEdges) var defaultMessage = - "\"${node.code}\". Op \"${nodeToRelevantMethod[node]}\" is not allowed. " + + "\"${node.code}\". Op \"${getMethods(node)}\" is not allowed. " + "Expected one of: " + possibleNextEdges?.joinToString(", ") if (possibleNextEdges?.isEmpty() == true && fsm.currentState?.isAcceptingState == true) { defaultMessage = "\"${node.code}\". " + - "Op \"${nodeToRelevantMethod[node]}\" is not allowed. No other calls are allowed on this base." + "Op \"${getMethods(node)}\" is not allowed. No other calls are allowed on this base." } val message = userDefinedFailMessage ?: defaultMessage diff --git a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/NfaDfaConstructionTest.kt b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/NfaDfaConstructionTest.kt index 2abe88be4..9f288e10c 100644 --- a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/NfaDfaConstructionTest.kt +++ b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/NfaDfaConstructionTest.kt @@ -39,16 +39,16 @@ import kotlin.test.assertEquals */ class NfaDfaConstructionTest { class TestClass { - fun create() = op {} - fun init() = op {} - fun start() = op {} - fun process() = op {} - fun finish() = op {} - fun reset() = op {} + fun create() = op { "create" {} } + fun init() = op { "init" {} } + fun start() = op { "start" {} } + fun process() = op { "process" {} } + fun finish() = op { "finish" {} } + fun reset() = op { "reset" {} } } private val baseName = - "class de.fraunhofer.aisec.codyze.backends.cpg.NfaDfaConstructionTest\$TestClass" + "de.fraunhofer.aisec.codyze.backends.cpg.NfaDfaConstructionTest\$TestClass" private fun orderExpressionToNfa(block: Order.() -> Unit): NFA { val order = Order().apply(block) @@ -68,8 +68,8 @@ class NfaDfaConstructionTest { with(mockk()) { val testObj = TestClass() val nfa = orderExpressionToNfa { - +testObj::create - +testObj::init + - testObj::create + - testObj::init } val expectedNfa = NFA() @@ -77,9 +77,9 @@ class NfaDfaConstructionTest { val q1 = expectedNfa.addState() val q2 = expectedNfa.addState() val q3 = expectedNfa.addState(isAcceptingState = true) - expectedNfa.addEdge(q0, Edge(op = "create", base = baseName, nextState = q1)) + expectedNfa.addEdge(q0, Edge(op = testObj.create().hashCode().toString(), base = baseName, nextState = q1)) expectedNfa.addEdge(q1, Edge(op = NFA.EPSILON, nextState = q2)) - expectedNfa.addEdge(q2, Edge(op = "init", base = baseName, nextState = q3)) + expectedNfa.addEdge(q2, Edge(op = testObj.init().hashCode().toString(), base = baseName, nextState = q3)) assertEquals(expected = expectedNfa, actual = nfa) } @@ -104,8 +104,8 @@ class NfaDfaConstructionTest { val q4 = expectedNfa.addState(isAcceptingState = true) expectedNfa.addEdge(q0, Edge(op = NFA.EPSILON, nextState = q1)) expectedNfa.addEdge(q0, Edge(op = NFA.EPSILON, nextState = q2)) - expectedNfa.addEdge(q1, Edge(op = "create", base = baseName, nextState = q3)) - expectedNfa.addEdge(q2, Edge(op = "init", base = baseName, nextState = q4)) + expectedNfa.addEdge(q1, Edge(op = testObj.create().hashCode().toString(), base = baseName, nextState = q3)) + expectedNfa.addEdge(q2, Edge(op = testObj.init().hashCode().toString(), base = baseName, nextState = q4)) assertEquals(expected = expectedNfa, actual = nfa) } @@ -127,7 +127,7 @@ class NfaDfaConstructionTest { val q1 = expectedNfa.addState() val q2 = expectedNfa.addState(isAcceptingState = true) expectedNfa.addEdge(q0, Edge(op = NFA.EPSILON, nextState = q1)) - expectedNfa.addEdge(q1, Edge(op = "create", base = baseName, nextState = q2)) + expectedNfa.addEdge(q1, Edge(op = testObj.create().hashCode().toString(), base = baseName, nextState = q2)) expectedNfa.addEdge(q2, Edge(op = NFA.EPSILON, nextState = q0)) assertEquals(expected = expectedNfa, actual = nfa) @@ -150,7 +150,7 @@ class NfaDfaConstructionTest { val q1 = expectedNfa.addState() val q2 = expectedNfa.addState(isAcceptingState = true) expectedNfa.addEdge(q0, Edge(op = NFA.EPSILON, nextState = q1)) - expectedNfa.addEdge(q1, Edge(op = "create", base = baseName, nextState = q2)) + expectedNfa.addEdge(q1, Edge(op = testObj.create().hashCode().toString(), base = baseName, nextState = q2)) assertEquals(expected = expectedNfa, actual = nfa) } @@ -168,14 +168,14 @@ class NfaDfaConstructionTest { // order cm.create(), cm.init(), (cm.start(), cm.process()*, cm.finish())+, cm.reset()? val testObj = TestClass() val dfa = orderExpressionToDfa { - +testObj::create - +testObj::init - some { - +testObj::start - maybe(testObj::process) - +testObj::finish + - testObj::create + - testObj::init + - some { + - testObj::start + - maybe(testObj::process) + - testObj::finish } - option(testObj::reset) + - option(testObj::reset) } val expected = DFA() @@ -186,15 +186,15 @@ class NfaDfaConstructionTest { val q5 = expected.addState() val q6 = expected.addState(isAcceptingState = true) val q7 = expected.addState(isAcceptingState = true) - expected.addEdge(q1, Edge("create", baseName, q2)) - expected.addEdge(q2, Edge("init", baseName, q3)) - expected.addEdge(q3, Edge("start", baseName, q4)) - expected.addEdge(q4, Edge("process", baseName, q5)) - expected.addEdge(q4, Edge("finish", baseName, q6)) - expected.addEdge(q5, Edge("process", baseName, q5)) - expected.addEdge(q5, Edge("finish", baseName, q6)) - expected.addEdge(q6, Edge("start", baseName, q4)) - expected.addEdge(q6, Edge("reset", baseName, q7)) + expected.addEdge(q1, Edge(testObj.create().hashCode().toString(), baseName, q2)) + expected.addEdge(q2, Edge(testObj.init().hashCode().toString(), baseName, q3)) + expected.addEdge(q3, Edge(testObj.start().hashCode().toString(), baseName, q4)) + expected.addEdge(q4, Edge(testObj.process().hashCode().toString(), baseName, q5)) + expected.addEdge(q4, Edge(testObj.finish().hashCode().toString(), baseName, q6)) + expected.addEdge(q5, Edge(testObj.process().hashCode().toString(), baseName, q5)) + expected.addEdge(q5, Edge(testObj.finish().hashCode().toString(), baseName, q6)) + expected.addEdge(q6, Edge(testObj.start().hashCode().toString(), baseName, q4)) + expected.addEdge(q6, Edge(testObj.reset().hashCode().toString(), baseName, q7)) assertEquals(expected, dfa) } @@ -213,13 +213,13 @@ class NfaDfaConstructionTest { // cm.reset()? val testObj = TestClass() val dfa = orderExpressionToDfa { - +testObj::init + - testObj::init some { testObj::create or group { - +testObj::start + - testObj::start maybe(testObj::process) - +testObj::finish + - testObj::finish } } option(testObj::reset) @@ -233,19 +233,19 @@ class NfaDfaConstructionTest { val q5 = expected.addState() val q6 = expected.addState(isAcceptingState = true) val q7 = expected.addState(isAcceptingState = true) - expected.addEdge(q1, Edge("init", baseName, q2)) - expected.addEdge(q2, Edge("create", baseName, q3)) - expected.addEdge(q2, Edge("start", baseName, q4)) - expected.addEdge(q3, Edge("create", baseName, q3)) - expected.addEdge(q3, Edge("start", baseName, q4)) - expected.addEdge(q3, Edge("reset", baseName, q7)) - expected.addEdge(q4, Edge("process", baseName, q5)) - expected.addEdge(q4, Edge("finish", baseName, q6)) - expected.addEdge(q5, Edge("process", baseName, q5)) - expected.addEdge(q5, Edge("finish", baseName, q6)) - expected.addEdge(q6, Edge("create", baseName, q3)) - expected.addEdge(q6, Edge("start", baseName, q4)) - expected.addEdge(q6, Edge("reset", baseName, q7)) + expected.addEdge(q1, Edge(testObj.init().hashCode().toString(), baseName, q2)) + expected.addEdge(q2, Edge(testObj.create().hashCode().toString(), baseName, q3)) + expected.addEdge(q2, Edge(testObj.start().hashCode().toString(), baseName, q4)) + expected.addEdge(q3, Edge(testObj.create().hashCode().toString(), baseName, q3)) + expected.addEdge(q3, Edge(testObj.start().hashCode().toString(), baseName, q4)) + expected.addEdge(q3, Edge(testObj.reset().hashCode().toString(), baseName, q7)) + expected.addEdge(q4, Edge(testObj.process().hashCode().toString(), baseName, q5)) + expected.addEdge(q4, Edge(testObj.finish().hashCode().toString(), baseName, q6)) + expected.addEdge(q5, Edge(testObj.process().hashCode().toString(), baseName, q5)) + expected.addEdge(q5, Edge(testObj.finish().hashCode().toString(), baseName, q6)) + expected.addEdge(q6, Edge(testObj.create().hashCode().toString(), baseName, q3)) + expected.addEdge(q6, Edge(testObj.start().hashCode().toString(), baseName, q4)) + expected.addEdge(q6, Edge(testObj.reset().hashCode().toString(), baseName, q7)) assertEquals(expected, dfa) } @@ -265,14 +265,14 @@ class NfaDfaConstructionTest { // cm.reset()? val testObj = TestClass() val dfa = orderExpressionToNfa { - +testObj::init - some { + - testObj::init + - some { testObj::create or group { maybe(testObj::start) maybe(testObj::process) maybe(testObj::start) - +testObj::finish + - testObj::finish } } option(testObj::reset) @@ -288,24 +288,24 @@ class NfaDfaConstructionTest { val q5 = expected.addState() val q6 = expected.addState(isAcceptingState = true) val q7 = expected.addState() - expected.addEdge(q1, Edge("init", baseName, q2)) - expected.addEdge(q2, Edge("create", baseName, q3)) - expected.addEdge(q2, Edge("finish", baseName, q3)) - expected.addEdge(q2, Edge("start", baseName, q4)) - expected.addEdge(q2, Edge("process", baseName, q5)) - expected.addEdge(q3, Edge("create", baseName, q3)) - expected.addEdge(q3, Edge("finish", baseName, q3)) - expected.addEdge(q3, Edge("start", baseName, q4)) - expected.addEdge(q3, Edge("process", baseName, q5)) - expected.addEdge(q3, Edge("reset", baseName, q6)) - expected.addEdge(q4, Edge("start", baseName, q4)) - expected.addEdge(q4, Edge("finish", baseName, q3)) - expected.addEdge(q4, Edge("process", baseName, q5)) - expected.addEdge(q5, Edge("process", baseName, q5)) - expected.addEdge(q5, Edge("finish", baseName, q3)) - expected.addEdge(q5, Edge("start", baseName, q7)) - expected.addEdge(q7, Edge("start", baseName, q7)) - expected.addEdge(q7, Edge("finish", baseName, q3)) + expected.addEdge(q1, Edge(testObj.init().hashCode().toString(), baseName, q2)) + expected.addEdge(q2, Edge(testObj.create().hashCode().toString(), baseName, q3)) + expected.addEdge(q2, Edge(testObj.finish().hashCode().toString(), baseName, q3)) + expected.addEdge(q2, Edge(testObj.start().hashCode().toString(), baseName, q4)) + expected.addEdge(q2, Edge(testObj.process().hashCode().toString(), baseName, q5)) + expected.addEdge(q3, Edge(testObj.create().hashCode().toString(), baseName, q3)) + expected.addEdge(q3, Edge(testObj.finish().hashCode().toString(), baseName, q3)) + expected.addEdge(q3, Edge(testObj.start().hashCode().toString(), baseName, q4)) + expected.addEdge(q3, Edge(testObj.process().hashCode().toString(), baseName, q5)) + expected.addEdge(q3, Edge(testObj.reset().hashCode().toString(), baseName, q6)) + expected.addEdge(q4, Edge(testObj.start().hashCode().toString(), baseName, q4)) + expected.addEdge(q4, Edge(testObj.finish().hashCode().toString(), baseName, q3)) + expected.addEdge(q4, Edge(testObj.process().hashCode().toString(), baseName, q5)) + expected.addEdge(q5, Edge(testObj.process().hashCode().toString(), baseName, q5)) + expected.addEdge(q5, Edge(testObj.finish().hashCode().toString(), baseName, q3)) + expected.addEdge(q5, Edge(testObj.start().hashCode().toString(), baseName, q7)) + expected.addEdge(q7, Edge(testObj.start().hashCode().toString(), baseName, q7)) + expected.addEdge(q7, Edge(testObj.finish().hashCode().toString(), baseName, q3)) assertEquals(expected, dfa) } diff --git a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/OrderEvaluationTest.kt b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/OrderEvaluationTest.kt index 862cd921c..a01f429bd 100644 --- a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/OrderEvaluationTest.kt +++ b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/OrderEvaluationTest.kt @@ -38,9 +38,11 @@ import kotlin.test.assertEquals class OrderEvaluationTest { class CokoOrderImpl { fun constructor(value: Int?) = constructor("Botan") { signature(value) } - fun init() = op { definition("Botan.set_key") { signature(Wildcard) } } + fun init() = op { "Botan.set_key" { signature(Wildcard) } } + + // calling definition explicitly is optional fun start() = op { definition("Botan.start") { signature(Wildcard) } } - fun finish() = op { definition("Botan.finish") { signature(Wildcard) } } + fun finish() = op { "Botan.finish" { signature(Wildcard) } } } class OtherImpl { @@ -63,8 +65,8 @@ class OrderEvaluationTest { context(CokoBackend) private fun createSimpleOrder(testObj: CokoOrderImpl) = order(testObj::constructor) { - +testObj::start - +testObj::finish + - testObj::start + - testObj::finish } @Test @@ -106,9 +108,9 @@ class OrderEvaluationTest { context(CokoBackend) private fun createSimpleOrderReducedStartNodes(testObj: CokoOrderImpl) = - order(testObj::constructor.use { testObj.constructor(1) }) { - +testObj::start - +testObj::finish + order(testObj.constructor(1)) { + - testObj::start + - testObj::finish } @Test @@ -129,8 +131,8 @@ class OrderEvaluationTest { ) assertEquals(1, findings.size) assertEquals( - "Violation against Order: \"p.set_key(key);\". Op \"[init]\" is not allowed. " + - "Expected one of: CokoOrderImpl.start", + "Violation against Order: \"p.set_key(key);\". Op \"[Botan.set_key(Wildcard)]\" is not allowed. " + + "Expected one of: Botan.start(Wildcard)", findings.first().message, ) } @@ -138,9 +140,9 @@ class OrderEvaluationTest { context(CokoBackend) private fun createInvalidOrder(testObj: CokoOrderImpl, testObj2: OtherImpl) = - order(testObj::constructor.use { testObj.constructor(1) }) { - +testObj::start - +testObj2::foo + order(testObj.constructor(1)) { + - testObj::start + - testObj2::foo } @Test diff --git a/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeOptionGroup.kt b/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeOptionGroup.kt index 98bcabeab..f07f3c382 100644 --- a/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeOptionGroup.kt +++ b/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeOptionGroup.kt @@ -33,6 +33,7 @@ class CodyzeOptionGroup : OptionGroup(name = null) { val output: Path by option("-o", "--output", help = "Write results to file. Use - for stdout.") .path(mustBeWritable = true) .default(Path(System.getProperty("user.dir"), "findings.sarif")) + val outputBuilder: OutputBuilder by option( "--output-format", help = "Format in which the analysis results are returned." diff --git a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/Op.kt b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/Op.kt index 5b4c4856e..bef09a401 100644 --- a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/Op.kt +++ b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/Op.kt @@ -16,32 +16,48 @@ package de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.CokoMarker -import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.modelling.* - -@CokoMarker sealed interface Op +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.modelling.Definition +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.modelling.Parameter +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.modelling.ParameterGroup +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.modelling.Signature +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.ordering.OrderFragment +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.ordering.TerminalOrderNode + +@CokoMarker sealed interface Op : OrderFragment { + val ownerClassFqn: String + + override fun toNode(): TerminalOrderNode = TerminalOrderNode( + baseName = ownerClassFqn, + opName = hashCode().toString() + ) +} /** * Represents a group of functions that serve the same purpose in the API. * - * Two [Op]s will be considered equal if they have the same [definitions]. This means that structure - * of the [Op]s have to be equal as well as the [Definition.fqn]s but not the actual [Parameter]s + * Two [FunctionOp]s will be considered equal if they have the same [definitions]. This means that structure + * of the [FunctionOp]s have to be equal as well as the [Definition.fqn]s but not the actual [Parameter]s * that are stored in the [Signature]s. * * @property definitions stores all definitions for the different functions */ -// `Op` is defined here with an internal constructor because we only want the user to use the `op` -// function to make an `Op` object -class FunctionOp internal constructor() : Op { - val definitions = arrayListOf() +// `FunctionOp` is defined here with an internal constructor because we only want the user to use the `op` +// function to make an `FunctionOp` object +class FunctionOp internal constructor( + override val ownerClassFqn: String = "", +) : Op { + val definitions = mutableSetOf() fun add(definition: Definition) { this.definitions.removeIf { it === definition } this.definitions.add(definition) } + operator fun String.invoke(block: Definition.() -> Unit) = definition(this, block) + /** - * Two [Op]s will be considered equal if they have the same [definitions]. This means that - * structure of the [Op]s have to be equal as well as the [Definition.fqn]s but not the actual + * Two [FunctionOp]s will be considered equal if they have the same [definitions]. This means that + * structure of the [FunctionOp]s have to be equal as well as the [Definition.fqn]s but not the actual * [Parameter]s that are stored in the [Signature]s. */ override fun equals(other: Any?): Boolean { @@ -69,8 +85,11 @@ class FunctionOp internal constructor() : Op { * * @property signatures stores all different signatures of the constructor. */ -class ConstructorOp internal constructor(val classFqn: String) : Op { - val signatures = arrayListOf() +class ConstructorOp internal constructor( + val classFqn: String, + override val ownerClassFqn: String = "", +) : Op { + val signatures = mutableSetOf() fun add(signature: Signature) { this.signatures.removeIf { it === signature } @@ -95,6 +114,8 @@ class ConstructorOp internal constructor(val classFqn: String) : Op { } } +context(Any) // This is needed to have access to the owner of this function +// -> in which class is the function defined that created this [OP] /** * Create a [FunctionOp]. * @@ -103,17 +124,17 @@ class ConstructorOp internal constructor(val classFqn: String) : Op { * op { * definition("my.fully.qualified.name") { * signature { - * +arg1 - * +arg2 - * +arg3 + * - arg1 + * - arg2 + * - arg3 * } * signature(arg1, arg2) * signature { - * +arg2 - * +arg3 + * - arg2 + * - arg3 * } * } - * definition("my.other.function") { + * "my.other.function" { // the call to 'definition' can be omitted * signature(arg2, arg1) * } * } @@ -127,21 +148,28 @@ class ConstructorOp internal constructor(val classFqn: String) : Op { * my.other.function(arg2, arg1) * ``` * - * @param block defines the [Definition]s of this [Op] + * @param block defines the [Definition]s of this [FunctionOp] */ -fun op(block: FunctionOp.() -> Unit) = FunctionOp().apply(block) +fun op(block: FunctionOp.() -> Unit): FunctionOp { + val functionOp = FunctionOp(this@Any::class.java.name).apply(block) + check(functionOp.definitions.isNotEmpty()) { "Coko does not support empty OPs" } + return functionOp +} +context(Any) // This is needed to have access to the owner of this function +// -> in which class is the function defined that created this [OP] /** Create a [ConstructorOp]. */ -fun constructor(classFqn: String, block: ConstructorOp.() -> Unit) = - ConstructorOp(classFqn).apply(block) +fun constructor(classFqn: String, block: ConstructorOp.() -> Unit): ConstructorOp { + return ConstructorOp(classFqn, this@Any::class.java.name).apply(block) +} /** - * Create a [Definition] which can be added to the [Op]. + * Create a [Definition] which can be added to the [FunctionOp]. * * A minimal example * ```kt * function { - * +definition("my.fully.qualified.name") {} + * definition("my.fully.qualified.name") {} * } * ``` * @@ -166,18 +194,18 @@ inline fun Definition.signature( * Create a [Signature] which can be added to the [Definition]. The [Parameter]s are passed through * the vararg. */ -fun Definition.signature(vararg parameters: Parameter) = signature { parameters.forEach { +it } } +fun Definition.signature(vararg parameters: Parameter) = signature { parameters.forEach { this.add(it) } } /** Create a [ParameterGroup] which can be added to the [Signature]. */ inline fun Signature.group(block: ParameterGroup.() -> Unit) = ParameterGroup().apply(block).also { this.add(it) } /** Create a [ParameterGroup] which can be added to the [Signature]. */ -fun Signature.group(vararg parameters: Parameter) = group { parameters.forEach { +it } } +fun Signature.group(vararg parameters: Parameter) = group { parameters.forEach { this.add(it) } } -context(Definition) +context(Signature) /** Add unordered [Parameter]s to the [Signature]. */ -fun Signature.unordered(vararg unordered: Parameter) = - this.apply { unorderedParameters.addAll(unordered) } +fun unordered(vararg unordered: Parameter) = + this@Signature.apply { unorderedParameters.addAll(unordered) } /** * Create a [Signature] which can be added to the [ConstructorOp]. The [Parameter]s are defined in @@ -189,4 +217,4 @@ inline fun ConstructorOp.signature(block: Signature.() -> Unit) = Signature().ap * Create a [Signature] which can be added to the [ConstructorOp]. The [Parameter]s are passed * through the vararg. */ -fun ConstructorOp.signature(vararg parameters: Parameter) = signature { parameters.forEach { +it } } +fun ConstructorOp.signature(vararg parameters: Parameter) = signature { parameters.forEach { this.add(it) } } diff --git a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/Order.kt b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/Order.kt index ea5314870..83fe38802 100644 --- a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/Order.kt +++ b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/Order.kt @@ -17,37 +17,11 @@ package de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl -import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.CokoBackend import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.ordering.* /** [OrderBuilder] subclass to hide some implementation details of [OrderBuilder] to coko users. */ class Order : OrderBuilder() -// -// token conversion -// -context(OrderBuilder) -/** - * Convert a [OrderToken] into a TerminalOrderNode and specify the arguments passed to the - * [OrderToken] when evaluating the order - */ -fun OrderToken.use(block: OrderToken.() -> Op): TerminalOrderNode { - val terminalOrderNode = toTerminalOrderNode(opName = this.name + "$" + block.hashCode(), useGetNodes = true) - this@OrderBuilder.userDefinedOps[terminalOrderNode.opName] = block() - return terminalOrderNode -} - -/** - * Helper function that allows the user to call [use] outside of an [OrderBuilder] context. - * This is needed in [CokoBackend.order] for the [baseNodes] argument. - */ -fun OrderToken.use(block: OrderToken.() -> Op) = block() - -context(OrderBuilder) -/** Convert an [OrderToken] into a TerminalOrderNode */ -internal val OrderToken.token: TerminalOrderNode - get() = toTerminalOrderNode() - // // groups // @@ -58,11 +32,11 @@ context(OrderBuilder) * ```kt * order { * group { - * +arg1::func1 + * - arg1::func1 * many(arg1::func2) * option { - * +arg1::func2 - * +arg1::func3 + * - arg1::func2 + * - arg1::func3 * } * } * } @@ -132,31 +106,31 @@ inline fun atLeast( * } * ``` */ -fun OrderBuilder.group(vararg tokens: OrderToken): OrderNode = group { tokens.forEach { +it } } +fun OrderBuilder.group(vararg tokens: OrderToken): OrderNode = group { tokens.forEach { add(it) } } context(OrderBuilder) /** Minimalist way to create a group with the [maybe] ('*') qualifier. See [group]. */ -fun maybe(vararg tokens: OrderToken): OrderNode = maybe { tokens.forEach { +it } } +fun maybe(vararg tokens: OrderToken): OrderNode = maybe { tokens.forEach { add(it) } } context(OrderBuilder) /** Minimalist way to create a group with the [some] ('+') qualifier. See [group]. */ -fun some(vararg tokens: OrderToken): OrderNode = some { tokens.forEach { +it } } +fun some(vararg tokens: OrderToken): OrderNode = some { tokens.forEach { add(it) } } context(OrderBuilder) /** Minimalist way to create a group with the [option] ('?') qualifier. See [group]. */ -fun option(vararg tokens: OrderToken): OrderNode = option { tokens.forEach { +it } } +fun option(vararg tokens: OrderToken): OrderNode = option { tokens.forEach { add(it) } } context(OrderBuilder) /** Minimalist way to create a group with the [count] qualifier. See [group]. */ -fun count(count: Int, vararg tokens: OrderToken): OrderNode = count(count) { tokens.forEach { +it } } +fun count(count: Int, vararg tokens: OrderToken): OrderNode = count(count) { tokens.forEach { add(it) } } context(OrderBuilder) /** Minimalist way to create a group with the [between] qualifier. See [group]. */ -fun between(range: IntRange, vararg tokens: OrderToken): OrderNode = between(range) { tokens.forEach { +it } } +fun between(range: IntRange, vararg tokens: OrderToken): OrderNode = between(range) { tokens.forEach { add(it) } } context(OrderBuilder) /** Minimalist way to create a group with the [atLeast] qualifier. See [group]. */ -fun atLeast(min: Int, vararg tokens: OrderToken): OrderNode = atLeast(min) { tokens.forEach { +it } } +fun atLeast(min: Int, vararg tokens: OrderToken): OrderNode = atLeast(min) { tokens.forEach { add(it) } } // // sets @@ -169,7 +143,7 @@ context(OrderBuilder) * * ```kt * order { - * +set[arg1::func1, arg1::func2, arg1::func4] + * set[arg1::func1, arg1::func2, arg1::func4] * } * ``` */ @@ -179,7 +153,7 @@ context(OrderBuilder) /** * Add a set to the [Order] containing any valid OrderDsl provided by a lambda (see [group]). * - * > Match any [OrderToken] in the set. + * Match any [OrderToken] in the set. */ inline fun set(block: OrderSet.() -> Unit) = OrderSet(false).apply(block).apply { this@OrderBuilder.add(this.toNode()) } @@ -210,7 +184,7 @@ context(OrderBuilder) * It can operate within a group, or on a whole expression. The patterns will be tested in order. */ infix fun OrderFragment.or(other: OrderFragment): OrderFragment = - this@OrderBuilder.add(AlternativeOrderNode(left = toNode(), right = other.toNode())).also { + this@OrderBuilder.add(AlternativeOrderNode(left = this@OrderFragment.toNode(), right = other.toNode())).also { this@OrderBuilder.remove(this@OrderFragment) this@OrderBuilder.remove(other) } @@ -224,10 +198,7 @@ context(OrderBuilder) * * It can operate within a group, or on a whole expression. The patterns will be tested in order. */ -infix fun OrderToken.or(other: OrderFragment): OrderFragment = - this@OrderBuilder.add(AlternativeOrderNode(left = token.toNode(), right = other.toNode())).also { - this@OrderBuilder.remove(other) - } +infix fun OrderToken.or(other: OrderFragment): OrderFragment = this@OrderToken.toNode() or other context(OrderBuilder) /** @@ -238,8 +209,7 @@ context(OrderBuilder) * * It can operate within a group, or on a whole expression. The patterns will be tested in order. */ -infix fun OrderToken.or(other: OrderToken): OrderFragment = - this@OrderBuilder.add(AlternativeOrderNode(left = token.toNode(), right = other.token.toNode())) +infix fun OrderToken.or(other: OrderToken): OrderFragment = this@OrderToken.toNode() or other.toNode() context(OrderBuilder) /** @@ -250,7 +220,4 @@ context(OrderBuilder) * * It can operate within a group, or on a whole expression. The patterns will be tested in order. */ -infix fun OrderFragment.or(other: OrderToken): OrderFragment = - this@OrderBuilder.add(AlternativeOrderNode(left = toNode(), right = other.token.toNode())).also { - this@OrderBuilder.remove(this@OrderFragment) - } +infix fun OrderFragment.or(other: OrderToken): OrderFragment = this@OrderFragment or other.toNode() diff --git a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/Types.kt b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/Types.kt index 5b1998578..c0363f54e 100644 --- a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/Types.kt +++ b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/Types.kt @@ -18,7 +18,9 @@ package de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl /** Matches any value. */ -object Wildcard +object Wildcard { + override fun toString() = "Wildcard" +} typealias wildcard = Wildcard diff --git a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/modelling/OpComponents.kt b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/modelling/OpComponents.kt index 0c6302dcd..3ca148f68 100644 --- a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/modelling/OpComponents.kt +++ b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/modelling/OpComponents.kt @@ -23,9 +23,9 @@ typealias Parameter = Any? /** Represents a group of parameters that all belong to the same index */ @CokoMarker class ParameterGroup { - val parameters = arrayListOf() + val parameters = mutableSetOf() - operator fun Parameter.unaryPlus() { + operator fun Parameter.unaryMinus() { add(this) } @@ -60,7 +60,7 @@ class ParameterGroup { */ @CokoMarker class Definition(val fqn: String) { - val signatures = arrayListOf() + val signatures = mutableSetOf() fun add(signature: Signature) { this.signatures.removeIf { it === signature } @@ -86,11 +86,17 @@ class Definition(val fqn: String) { } override fun toString(): String { - return signatures.filterNot { it.parameters.contains(null) || it.unorderedParameters.contains(null) } - .joinToString( - prefix = fqn, - separator = ", $fqn" + val (nullParams, notNullParams) = signatures.partition { + it.parameters.contains(null) || it.unorderedParameters.contains( + null ) + } + return notNullParams.joinToString(prefix = fqn, separator = ", $fqn") + + if (nullParams.isNotEmpty()) { + "(" + nullParams.joinToString(prefix = fqn, separator = ", $fqn") + ")" + } else { + "" + } } } @@ -107,11 +113,11 @@ class Definition(val fqn: String) { */ @CokoMarker class Signature { - val parameters = arrayListOf() + val parameters = mutableListOf() - val unorderedParameters = arrayListOf() + val unorderedParameters = mutableSetOf() - operator fun Parameter.unaryPlus() { + operator fun Parameter.unaryMinus() { add(this) } @@ -135,15 +141,15 @@ class Signature { other as Signature - if (parameters.size != other.parameters.size) return false - if (unorderedParameters.size != other.unorderedParameters.size) return false + if (parameters != other.parameters) return false + if (unorderedParameters != other.unorderedParameters) return false return true } override fun hashCode(): Int { - var result = parameters.size.hashCode() - result = 31 * result + unorderedParameters.size.hashCode() + var result = parameters.hashCode() + result = 31 * result + unorderedParameters.hashCode() return result } diff --git a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/ordering/Nodes.kt b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/ordering/Nodes.kt index 5ba7bae36..a02ac8281 100644 --- a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/ordering/Nodes.kt +++ b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/ordering/Nodes.kt @@ -15,8 +15,6 @@ */ package de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.ordering -import kotlin.jvm.internal.CallableReference - sealed interface OrderNode : OrderFragment { /** Convert this [OrderNode] to a binary syntax tree */ override fun toNode() = this @@ -48,13 +46,7 @@ sealed interface OrderNode : OrderFragment { } /** Represents an [OrderToken]. */ -data class TerminalOrderNode(val baseName: String, val opName: String, val useGetNodes: Boolean) : OrderNode - -fun OrderToken.toTerminalOrderNode( - baseName: String = (this as CallableReference).owner.toString(), - opName: String = name, - useGetNodes: Boolean = false, -) = TerminalOrderNode(baseName, opName, useGetNodes = useGetNodes) +data class TerminalOrderNode(val baseName: String, val opName: String) : OrderNode /** Represents a regex sequence, where one [OrderNode] must be followed by another [OrderNode] */ data class SequenceOrderNode(val left: OrderNode, val right: OrderNode) : OrderNode diff --git a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/ordering/OrderBuilder.kt b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/ordering/OrderBuilder.kt index c917f2ccc..a9aa362da 100644 --- a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/ordering/OrderBuilder.kt +++ b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/ordering/OrderBuilder.kt @@ -19,7 +19,7 @@ package de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.ordering import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.CokoMarker import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.Op -import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.token +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.Order @CokoMarker /** @@ -32,14 +32,17 @@ open class OrderBuilder : OrderFragment { protected val orderNodes = mutableListOf() // this is emptied in [toNode] + /** Add an [Op] to the [userDefinedOps] */ + operator fun Op.unaryMinus(): OrderNode = this@OrderBuilder.add(this) + /** Add an [OrderToken] to the [orderNodes] */ - operator fun OrderToken.unaryPlus() = add(this) + operator fun OrderToken.unaryMinus(): OrderNode = add(this) /** Add an [OrderFragment] to the [orderNodes] */ - operator fun OrderFragment.unaryPlus() = add(this) + operator fun OrderFragment.unaryMinus(): OrderNode = add(this) /** Add an [OrderToken] to the [orderNodes] */ - fun add(token: OrderToken) = orderNodes.add(token.token) + fun add(token: OrderToken): OrderNode = add(token.toNode()) /** * Add an [OrderFragment] to the [orderNodes]. @@ -107,6 +110,15 @@ open class OrderBuilder : OrderFragment { orderNodes.add(it) } + /** + * Add an [Op] to the [userDefinedOps]. + */ + fun add(op: Op): OrderNode { + val terminalOrderNode = op.toNode() + this@OrderBuilder.userDefinedOps[terminalOrderNode.opName] = op + return add(terminalOrderNode) + } + /** Remove all instance of [fragment] from the [orderNodes] */ fun remove(fragment: OrderFragment) { orderNodes.removeIf { diff --git a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/ordering/OrderFragment.kt b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/ordering/OrderFragment.kt index f96fa0dbb..b7c4418e8 100644 --- a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/ordering/OrderFragment.kt +++ b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/ordering/OrderFragment.kt @@ -17,10 +17,7 @@ package de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.ordering -sealed interface OrderFragment { +interface OrderFragment { /** Convert this [OrderFragment] to a binary syntax tree */ fun toNode(): OrderNode - - val token: OrderFragment - get() = this } diff --git a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/ordering/OrderSet.kt b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/ordering/OrderSet.kt index b984efcbe..0ac51d313 100644 --- a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/ordering/OrderSet.kt +++ b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/ordering/OrderSet.kt @@ -56,5 +56,5 @@ class OrderSet(private var negate: Boolean) : OrderBuilder() { /** Allows the syntactic sugar to create a set with the 'get' operator. */ class OrderSetGetOperator { context(OrderBuilder) - operator fun get(vararg tokens: OrderToken) = set { tokens.forEach { +it } } + operator fun get(vararg tokens: OrderToken) = set { tokens.forEach { add(it) } } } diff --git a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/ordering/OrderToken.kt b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/ordering/OrderToken.kt index e66332fb5..2835f6bb5 100644 --- a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/ordering/OrderToken.kt +++ b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/ordering/OrderToken.kt @@ -21,3 +21,6 @@ import kotlin.reflect.KFunction typealias OrderToken = KFunction fun OrderToken.getOp(vararg arguments: Any? = Array(parameters.size) { null }) = call(*arguments) + +/** Convert an [OrderToken] into a TerminalOrderNode */ +internal fun OrderToken.toNode(): TerminalOrderNode = getOp().toNode() diff --git a/codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/OpDslTest.kt b/codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/OpDslTest.kt index 8b7de7fe5..0af42b304 100644 --- a/codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/OpDslTest.kt +++ b/codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/OpDslTest.kt @@ -60,20 +60,21 @@ class OpDslTest { val actualOp = op { definition("fqn1") { - signature { +stringParam } + signature { - stringParam } signature(stringParam, callTestParam) signature { - +stringParam - +callTestParam - }.unordered(numberParam) + - stringParam + - callTestParam + unordered(numberParam) + } signature(numberParam, collectionParam, stringParam) } definition("fqn2") { signature { - +typeParam + - typeParam group { - +stringParam - +arrayParam + - stringParam + - arrayParam } } } diff --git a/codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/modelling/EqualityTest.kt b/codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/modelling/EqualityTest.kt new file mode 100644 index 000000000..81f8f5674 --- /dev/null +++ b/codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/modelling/EqualityTest.kt @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2022, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.modelling + +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.* +import io.mockk.mockk +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals + +class EqualityTest { + + @Test + fun `test Definition equality`() { + with(mockk(relaxed = true)) { + val def1 = definition("Test.test") { + signature { + - "string" + - listOf(1, 2) + } + signature(1, 3, 5) + signature { + - "h" + } + } + + val def2 = definition("Test.test") { + signature(1, 3, 5) + signature { + - "h" + } + signature { + - "string" + - listOf(1, 2) + } + } + + assertEquals(def1, def2) + } + } + + @Test + fun `test FunctionOp equality`() { + val op1 = op { + definition("Test.test1") {} + definition("Test.test2") {} + definition("Test.test3") {} + } + + val op2 = op { + definition("Test.test2") {} + definition("Test.test1") {} + definition("Test.test3") {} + } + + assertEquals(op1, op2) + } + + @Test + fun `test ConstructorOp equality`() { + val op1 = constructor("my.Test") { + signature(1, 2, 3) + signature { + - setOf("h", "he", "hel") + - mapOf(1 to "one", 2 to "two") + } + signature("h", "test") + } + + val op2 = constructor("my.Test") { + signature("h", "test") + signature { + - setOf("h", "he", "hel") + - mapOf(1 to "one", 2 to "two") + } + signature(1, 2, 3) + } + + assertEquals(op1, op2) + } + + @Test + fun `test ParameterGroup equality`() { + with(mockk(relaxed = true)) { + val group1 = group { + - "h" + - "string" + - (1..4) + } + + val group2 = group { + - (1..4) + - "h" + - "string" + } + + assertEquals(group1, group2) + } + } + + @Test + fun `test Signature equality`() { + with(mockk(relaxed = true)) { + val sig1 = signature { + - "one" + - listOf(0.1, 3.2) + unordered(1, setOf(2, 4, 6), "test") + } + + val sig2 = signature { + - "one" + - listOf(0.1, 3.2) + unordered("test", 1, setOf(2, 4, 6)) + } + + val differentSig = signature { + - listOf(0.1, 3.2) + - "one" + unordered(1, setOf(2, 4, 6), "test") + } + + assertEquals(sig1, sig2) + assertNotEquals(sig1, differentSig) + } + } +} diff --git a/codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/modelling/OpComponentsTest.kt b/codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/modelling/OpComponentsTest.kt index 90d9a9dcc..5383ce532 100644 --- a/codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/modelling/OpComponentsTest.kt +++ b/codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/modelling/OpComponentsTest.kt @@ -32,9 +32,9 @@ class OpComponentsTest { @MethodSource("unaryPlusParamHelper") fun `test group`(expectedParams: ArrayList) { with(mockk(relaxed = true)) { - val paramGroup = group { expectedParams.forEach { +it } } + val paramGroup = group { expectedParams.forEach { - it } } - assertContentEquals(expectedParams, paramGroup.parameters) + assertEquals(expectedParams.toSet(), paramGroup.parameters) } } @@ -44,7 +44,7 @@ class OpComponentsTest { expectedParams: ArrayList ) { val sig = Signature() - with(sig) { expectedParams.forEach { +it } } + with(sig) { expectedParams.forEach { - it } } assertContentEquals( expectedParams, sig.parameters, @@ -55,7 +55,7 @@ class OpComponentsTest { @Test fun `test simple signature`() { with(mockk(relaxed = true)) { - val sig = signature { +singleParam } + val sig = signature { - singleParam } val sigShortcut = signature(singleParam) val expectedSig = Signature().apply { parameters.add(singleParam) } @@ -69,7 +69,9 @@ class OpComponentsTest { fun `test signature with unordered`() { with(mockk(relaxed = true)) { val unordered = signature(multipleParams.toTypedArray()) {} - val unorderedShortcut = signature().unordered(*multipleParams.toTypedArray()) + val unorderedShortcut = signature { + unordered(*multipleParams.toTypedArray()) + } val expectedSig = Signature().apply { unorderedParameters.addAll(multipleParams) } assertEquals(expectedSig, unorderedShortcut) @@ -80,7 +82,7 @@ class OpComponentsTest { @Test fun `test signature with group`() { with(mockk(relaxed = true)) { - val sig = signature { group { multipleParams.forEach { +it } } } + val sig = signature { group { multipleParams.forEach { - it } } } val groupShortcut = signature { group(*multipleParams.toTypedArray()) } @@ -102,11 +104,11 @@ class OpComponentsTest { with(mockk(relaxed = true)) { val sig = signature { - +singleParam - group { grouped.forEach { +it } } - ordered.forEach { +it } + - singleParam + group { grouped.forEach { - it } } + ordered.forEach { - it } + unordered(*unordered.toTypedArray()) } - .unordered(*unordered.toTypedArray()) val expectedSig = Signature().apply { diff --git a/codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/order/OrderSyntaxTreeConstructionTest.kt b/codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/order/OrderSyntaxTreeConstructionTest.kt index 9cef06068..3d92d8015 100644 --- a/codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/order/OrderSyntaxTreeConstructionTest.kt +++ b/codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/order/OrderSyntaxTreeConstructionTest.kt @@ -31,8 +31,19 @@ import kotlin.test.assertFailsWith class OrderSyntaxTreeConstructionTest { context(CokoBackend) class TestClass { - fun fun1() = op {} - fun fun2() = op {} + fun fun1(testParam: Int?) = op { + "testOp" { + signature { + group { + -testParam + } + } + } + } + + fun fun2() = op { + "empty OP" {} + } } context(CokoBackend) @@ -54,13 +65,14 @@ class OrderSyntaxTreeConstructionTest { /** Tests an order with an empty group -> this should error */ @Test - fun `test order with empty set`() { + fun `test order with empty group`() { with(mockk()) { + val testObj = TestClass() assertFailsWith( message = "Groups and sets must have at least one element.", block = { orderExpressionToSyntaxTree { - +TestClass::fun1 + -testObj::fun1 group {} } } @@ -68,19 +80,51 @@ class OrderSyntaxTreeConstructionTest { } } + /** Tests an order with a user defined OP */ + @Test + fun `test order with user defined op`() { + with(mockk()) { + val testObj = TestClass() + val syntaxTree = orderExpressionToSyntaxTree { + - testObj.fun1(1) + - testObj.fun1(1) + - testObj::fun2 + } + + val fun1 = testObj.fun1(1).toNode() + val expectedSyntaxTree = + SequenceOrderNode( + left = SequenceOrderNode( + left = TerminalOrderNode( + fun1.baseName, + fun1.opName + ), + right = TerminalOrderNode( + fun1.baseName, + fun1.opName + ) + ), + right = testObj::fun2.toNode() + ) + + assertEquals(expected = expectedSyntaxTree, actual = syntaxTree) + } + } + /** Tests a simple sequence order */ @Test fun `test simple sequence order`() { with(mockk()) { + val testObj = TestClass() val syntaxTree = orderExpressionToSyntaxTree { - +TestClass::fun1 - +TestClass::fun2 + - testObj::fun1 + - testObj::fun2 } val expectedSyntaxTree = SequenceOrderNode( - left = TestClass::fun1.toTerminalOrderNode(), - right = TestClass::fun2.toTerminalOrderNode() + left = testObj::fun1.toNode(), + right = testObj::fun2.toNode() ) assertEquals(expected = expectedSyntaxTree, actual = syntaxTree) @@ -91,12 +135,13 @@ class OrderSyntaxTreeConstructionTest { @Test fun `test simple alternative order`() { with(mockk()) { - val syntaxTree = orderExpressionToSyntaxTree { TestClass::fun1 or TestClass::fun2 } + val testObj = TestClass() + val syntaxTree = orderExpressionToSyntaxTree { testObj::fun1 or testObj::fun2 } val expectedSyntaxTree = AlternativeOrderNode( - left = TestClass::fun1.toTerminalOrderNode(), - right = TestClass::fun2.toTerminalOrderNode() + left = testObj::fun1.toNode(), + right = testObj::fun2.toNode() ) assertEquals(expected = expectedSyntaxTree, actual = syntaxTree) @@ -107,17 +152,18 @@ class OrderSyntaxTreeConstructionTest { @Test fun `test alternative order with group`() { with(mockk()) { + val testObj = TestClass() val syntaxTree = orderExpressionToSyntaxTree { - group(TestClass::fun1, TestClass::fun2) or TestClass::fun2 + group(testObj::fun1, testObj::fun2) or testObj::fun2 } val expectedSyntaxTree = AlternativeOrderNode( left = SequenceOrderNode( - left = TestClass::fun1.toTerminalOrderNode(), - right = TestClass::fun2.toTerminalOrderNode() + left = testObj::fun1.toNode(), + right = testObj::fun2.toNode() ), - right = TestClass::fun2.toTerminalOrderNode() + right = testObj::fun2.toNode() ) assertEquals(expected = expectedSyntaxTree, actual = syntaxTree) @@ -128,20 +174,21 @@ class OrderSyntaxTreeConstructionTest { @Test fun `test alternative order with quantifier`() { with(mockk()) { + val testObj = TestClass() val syntaxTree = orderExpressionToSyntaxTree { - maybe(TestClass::fun1, TestClass::fun2) or TestClass::fun2 + maybe(testObj::fun1, testObj::fun2) or testObj::fun2 } val expectedSyntaxTree = AlternativeOrderNode( left = QuantifierOrderNode( SequenceOrderNode( - left = TestClass::fun1.toTerminalOrderNode(), - right = TestClass::fun2.toTerminalOrderNode() + left = testObj::fun1.toNode(), + right = testObj::fun2.toNode() ), OrderQuantifier.MAYBE ), - right = TestClass::fun2.toTerminalOrderNode() + right = testObj::fun2.toNode() ) assertEquals(expected = expectedSyntaxTree, actual = syntaxTree) @@ -152,10 +199,11 @@ class OrderSyntaxTreeConstructionTest { @Test fun `test order with an QuantifierOrderNode`() { with(mockk()) { - val syntaxTree = orderExpressionToSyntaxTree { between(1..4, TestClass::fun1) } + val testObj = TestClass() + val syntaxTree = orderExpressionToSyntaxTree { between(1..4, testObj::fun1) } val expectedSyntaxTree = QuantifierOrderNode( - child = TestClass::fun1.toTerminalOrderNode(), + child = testObj::fun1.toNode(), type = OrderQuantifier.BETWEEN, value = 1..4 ) @@ -168,18 +216,19 @@ class OrderSyntaxTreeConstructionTest { @Test fun `test order with group`() { with(mockk()) { + val testObj = TestClass() val syntaxTree = orderExpressionToSyntaxTree { maybe { - +TestClass::fun1 - +TestClass::fun2 + - testObj::fun1 + - testObj::fun2 } } val expectedSyntaxTree = QuantifierOrderNode( SequenceOrderNode( - left = TestClass::fun1.toTerminalOrderNode(), - right = TestClass::fun2.toTerminalOrderNode() + left = testObj::fun1.toNode(), + right = testObj::fun2.toNode() ), OrderQuantifier.MAYBE ) @@ -192,23 +241,24 @@ class OrderSyntaxTreeConstructionTest { @Test fun `test order with set`() { with(mockk()) { + val testObj = TestClass() val syntaxTree = orderExpressionToSyntaxTree { set { - +TestClass::fun1 - +TestClass::fun2 + - testObj::fun1 + - testObj::fun2 } } val syntaxTreeShortcut = orderExpressionToSyntaxTree { - set[TestClass::fun1, TestClass::fun2] + set[testObj::fun1, testObj::fun2] } assertEquals(syntaxTree, syntaxTreeShortcut) val expectedSyntaxTree = AlternativeOrderNode( - TestClass::fun1.toTerminalOrderNode(), - TestClass::fun2.toTerminalOrderNode() + testObj::fun1.toNode(), + testObj::fun2.toNode() ) assertEquals(expected = expectedSyntaxTree, actual = syntaxTree) @@ -219,21 +269,22 @@ class OrderSyntaxTreeConstructionTest { @Test fun `test order with multiple usages of same object`() { with(mockk()) { - val nodeFun1 = TestClass::fun1.toTerminalOrderNode() - val nodeFun2 = TestClass::fun2.toTerminalOrderNode() + val testObj = TestClass() + val nodeFun1 = testObj::fun1.toNode() + val nodeFun2 = testObj::fun2.toNode() // regex: fun1 (fun1 fun2)* fun2 val syntaxTree = orderExpressionToSyntaxTree { // `maybe` is added to list val maybe = maybe { - +TestClass::fun1 - +TestClass::fun2 + - testObj::fun1 + - testObj::fun2 } - +TestClass::fun1 + - testObj::fun1 // `maybe` is removed from list and added again add(maybe) - +TestClass::fun2 + - testObj::fun2 } val expectedSyntaxTree = @@ -259,24 +310,25 @@ class OrderSyntaxTreeConstructionTest { @Test fun `test order with usages of same groups`() { with(mockk()) { - val nodeFun1 = TestClass::fun1.toTerminalOrderNode() - val nodeFun2 = TestClass::fun2.toTerminalOrderNode() + val testObj = TestClass() + val nodeFun1 = testObj::fun1.toNode() + val nodeFun2 = testObj::fun2.toNode() // regex: (fun1 fun2)* fun1 ((fun1 fun2)* | fun2) fun2 val syntaxTree = orderExpressionToSyntaxTree { // `maybe` is added to list maybe { - +TestClass::fun1 - +TestClass::fun2 + - testObj::fun1 + - testObj::fun2 } - +TestClass::fun1 + - testObj::fun1 // `maybe` is removed from list and added within the Node from `or` maybe { - +TestClass::fun1 - +TestClass::fun2 - } or TestClass::fun2 - +TestClass::fun2 + - testObj::fun1 + - testObj::fun2 + } or testObj::fun2 + - testObj::fun2 } val expectedSyntaxTree = @@ -314,24 +366,25 @@ class OrderSyntaxTreeConstructionTest { @Test fun `test order with multiple usages of same object in or`() { with(mockk()) { - val nodeFun1 = TestClass::fun1.toTerminalOrderNode() - val nodeFun2 = TestClass::fun2.toTerminalOrderNode() + val testObj = TestClass() + val nodeFun1 = testObj::fun1.toNode() + val nodeFun2 = testObj::fun2.toNode() // regex: fun1 fun1 ((fun1 fun2)* | fun2) fun2 val syntaxTree = orderExpressionToSyntaxTree { // `maybe` is added to list val maybe = maybe { - +TestClass::fun1 - +TestClass::fun2 + - testObj::fun1 + - testObj::fun2 } - +TestClass::fun1 + - testObj::fun1 // `maybe` is removed from list and added again add(maybe) - +TestClass::fun1 + - testObj::fun1 // `maybe` is removed from list and added within the Node from `or` - maybe or TestClass::fun2 - +TestClass::fun2 + maybe or testObj::fun2 + - testObj::fun2 } val expectedSyntaxTree = @@ -363,15 +416,16 @@ class OrderSyntaxTreeConstructionTest { @Test fun `test complex order`() { with(mockk()) { + val testObj = TestClass() val syntaxTree = orderExpressionToSyntaxTree { - between(1..4, TestClass::fun1) - maybe(TestClass::fun1, TestClass::fun2) - set[TestClass::fun2, TestClass::fun1] - +TestClass::fun1 + between(1..4, testObj::fun1) + maybe(testObj::fun1, testObj::fun2) + set[testObj::fun2, testObj::fun1] + - testObj::fun1 } - val nodeFun1 = TestClass::fun1.toTerminalOrderNode() - val nodeFun2 = TestClass::fun2.toTerminalOrderNode() + val nodeFun1 = testObj::fun1.toNode() + val nodeFun2 = testObj::fun2.toNode() val expectedSyntaxTree = SequenceOrderNode( diff --git a/codyze-specification-languages/coko/coko-dsl/src/main/resources/bouncycastle/CipherImpl.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/main/resources/bouncycastle/CipherImpl.codyze.kts index 2cd944ffc..dcc14307b 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/main/resources/bouncycastle/CipherImpl.codyze.kts +++ b/codyze-specification-languages/coko/coko-dsl/src/main/resources/bouncycastle/CipherImpl.codyze.kts @@ -7,9 +7,9 @@ class CipherImpl : Cipher { // deal with the different function signatures with a more complex configuration of the // CallExpression op { - +definition("javax.crypto.Cipher.getInstance") { - +signature(transform) - +signature(transform, provider) + definition("javax.crypto.Cipher.getInstance") { + signature(transform) + signature(transform, provider) } } @@ -21,44 +21,44 @@ class CipherImpl : Cipher { params: Any?, // optional paramspec: Any? // optional ) = op { - +definition("javax.crypto.Cipher.init") { - +signature(opmode, certificate) - +signature(opmode, certificate, random) - +signature(opmode, key) - +signature(opmode, key, params) - +signature(opmode, key, params, random) - +signature(opmode, key, random) - +signature(opmode, key, paramspec) - +signature(opmode, key, paramspec, random) + definition("javax.crypto.Cipher.init") { + signature(opmode, certificate) + signature(opmode, certificate, random) + signature(opmode, key) + signature(opmode, key, params) + signature(opmode, key, params, random) + signature(opmode, key, random) + signature(opmode, key, paramspec) + signature(opmode, key, paramspec, random) } } override fun aad(src: Any, vararg args: Any) = op { - +definition("javax.crypto.Cipher.updateAAD") { - +signature(src) + definition("javax.crypto.Cipher.updateAAD") { + signature(src) // TODO: signature(src, ...) in MARK } } override fun update(input: Any, output: Any?, vararg args: Any) = op { - +definition("javax.crypto.Cipher.update") { - +signature(input) - +signature(input, Wildcard, Wildcard) - +signature(input, Wildcard, Wildcard, output) - +signature(input, Wildcard, Wildcard, output, Wildcard) - +signature(input, output) + definition("javax.crypto.Cipher.update") { + signature(input) + signature(input, Wildcard, Wildcard) + signature(input, Wildcard, Wildcard, output) + signature(input, Wildcard, Wildcard, output, Wildcard) + signature(input, output) } } override fun finalize(input: Any?, output: Any?, vararg args: Any) = op { - +definition("javax.crypto.Cipher.doFinal") { - +signature() - +signature(input) - +signature(output, Wildcard) - +signature(input, Wildcard, Wildcard) - +signature(input, Wildcard, Wildcard, output) - +signature(input, Wildcard, Wildcard, output, Wildcard) - +signature(input, output) + definition("javax.crypto.Cipher.doFinal") { + signature() + signature(input) + signature(output, Wildcard) + signature(input, Wildcard, Wildcard) + signature(input, Wildcard, Wildcard, output) + signature(input, Wildcard, Wildcard, output, Wildcard) + signature(input, output) } } } diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByFull.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByFull.codyze.kts index 673d147aa..b24663dc3 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByFull.codyze.kts +++ b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByFull.codyze.kts @@ -20,11 +20,11 @@ fun DBActionsAreAlwaysLogged(db: ObjectRelationalMapper, log: Logging, ctx: User class JavaLogging : Logging { override fun log(message: String?, vararg args: Any?) = op { - definition("java.util.logging.Logger.info") { + "java.util.logging.Logger.info" { signature { group { - +message - args.forEach { +it } + - message + args.forEach { - it } } } } @@ -33,11 +33,11 @@ class JavaLogging : Logging { class JDBC : ObjectRelationalMapper { override fun insert(obj: Any?) = op { - definition("java.sql.Statement.executeUpdate") { + "java.sql.Statement.executeUpdate" { signature { group { - +"INSERT.*" - +obj + - "INSERT.*" + - obj } } } diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByThreeFiles/followedByImplementations.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByThreeFiles/followedByImplementations.codyze.kts index cb70a51bc..5840731c5 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByThreeFiles/followedByImplementations.codyze.kts +++ b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByThreeFiles/followedByImplementations.codyze.kts @@ -7,8 +7,8 @@ class JavaLoggingTest : LoggingForTest { definition("java.util.logging.Logger.info") { signature { group { - +message - args.forEach { +it } + - message + args.forEach { - it } } } } @@ -20,8 +20,8 @@ class JDBCTest : ObjectRelationalMapperForTest { definition("java.sql.Statement.executeUpdate") { signature { group { - +"INSERT.*" - +obj + - "INSERT.*" + - obj } } } diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByTwoFiles/followedByImplementations.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByTwoFiles/followedByImplementations.codyze.kts index 13177ba8d..a07c14328 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByTwoFiles/followedByImplementations.codyze.kts +++ b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/followedByTwoFiles/followedByImplementations.codyze.kts @@ -7,8 +7,8 @@ class JavaLoggingTest : LoggingForTest { definition("java.util.logging.Logger.info") { signature { group { - +message - args.forEach { +it } + - message + args.forEach { - it } } } } @@ -20,8 +20,8 @@ class JDBCTest : ObjectRelationalMapperForTest { definition("java.sql.Statement.executeUpdate") { signature { group { - +"INSERT.*" - +obj + - "INSERT.*" + - obj } } } diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/orderFull.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/orderFull.codyze.kts index 9ea8ed434..c8ff8dc55 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/orderFull.codyze.kts +++ b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/orderFull.codyze.kts @@ -1,13 +1,13 @@ class CokoOrderImpl { fun constructor(value: Int?) = constructor("Botan") { signature(value) } - fun init() = op { definition("Botan.set_key") { signature(Wildcard) } } - fun start() = op { definition("Botan.start") { signature(Wildcard) } } - fun finish() = op { definition("Botan.finish") { signature(Wildcard) } } + fun init() = op { "Botan.set_key" { signature(Wildcard) } } + fun start() = op { "Botan.start" { signature(Wildcard) } } + fun finish() = op { "Botan.finish" { signature(Wildcard) } } } @Rule("simple order evaluation") fun `validate CokoOrderImpl usage order`(testObj: CokoOrderImpl) = order(testObj::constructor) { - +testObj::start - +testObj::finish + - testObj::start + - testObj::finish } diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/orderTwoFiles/orderRule.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/orderTwoFiles/orderRule.codyze.kts index a91c2e2f9..84eec76c2 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/orderTwoFiles/orderRule.codyze.kts +++ b/codyze-specification-languages/coko/coko-dsl/src/test/resources/IntegrationTests/CokoCpg/orderTwoFiles/orderRule.codyze.kts @@ -3,6 +3,6 @@ @Rule("simple order evaluation") fun `validate CokoOrderImpl usage order`(testObj: CokoOrderImpl) = order(testObj::constructor) { - +testObj::start - +testObj::finish + - testObj::start + - testObj::finish } \ No newline at end of file diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/javaimpl.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/test/resources/javaimpl.codyze.kts index ab1992c09..cbe63e684 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/test/resources/javaimpl.codyze.kts +++ b/codyze-specification-languages/coko/coko-dsl/src/test/resources/javaimpl.codyze.kts @@ -7,8 +7,8 @@ class JavaLogging : Logging { definition("java.util.logging.Logger.info") { signature { group { - +message - args.forEach { +it } + - message + args.forEach { - it } } } } @@ -20,8 +20,8 @@ class JDBC : ObjectRelationalMapper { definition("java.sql.Statement.executeUpdate") { signature { group { - +"INSERT.*" - +obj + - "INSERT.*" + - obj } } } diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/pyimpl.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/test/resources/pyimpl.codyze.kts index edb7e8002..b96c316ee 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/test/resources/pyimpl.codyze.kts +++ b/codyze-specification-languages/coko/coko-dsl/src/test/resources/pyimpl.codyze.kts @@ -8,7 +8,7 @@ class PythonLogging : Logging { // We don't care about the order of the arguments. Just make sure that all objects in "args" // somehow flow into the log message/args. op { - definition("logging.info") { signature(args) { +message } } + definition("logging.info") { signature(args) { - message } } } } @@ -17,8 +17,8 @@ class Sqlite3 : ObjectRelationalMapper { definition("sqlite3.Cursor.execute") { signature { group { - +"INSERT.*" - +obj + - "INSERT.*" + - obj } } } diff --git a/detekt.yml b/detekt.yml index 22d2a8d2d..127b85bbd 100644 --- a/detekt.yml +++ b/detekt.yml @@ -893,6 +893,7 @@ formatting: autoCorrect: true SpacingAroundUnaryOperator: active: true + excludes: ['**/coko/**/test/**', '**/codyze-backends/cpg/**/test/**'] autoCorrect: true SpacingBetweenDeclarationsWithAnnotations: active: true From 74ea50a7808f9d6641cc187194560b8a1069f108 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 18 Apr 2023 11:13:34 +0200 Subject: [PATCH 009/121] Update eclipse-temurin Docker tag to v17.0.6_10-jre (#716) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Florian Wendland --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index cb8cc0539..812c4c1db 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM eclipse-temurin:17-jre +FROM eclipse-temurin:17.0.6_10-jre LABEL org.opencontainers.image.authors="Fraunhofer AISEC " From ec442700d4ad2346e773eca5e3dd22d566b05088 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 26 Apr 2023 08:16:22 +0200 Subject: [PATCH 010/121] Update kotlin monorepo to v1.8.21 (#726) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 329078816..8189e4441 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "1.8.20" +kotlin = "1.8.21" cpg = "6.2.1" koin = "3.4.0" detekt = "1.22.0" From e637e9e2fe05393e9d6d05836999a98820146af8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 26 Apr 2023 06:21:05 +0000 Subject: [PATCH 011/121] Update dependency gradle to v8.1.1 (#725) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Florian Wendland --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0c85a1f75..37aef8d3f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 7b72cd0389e1ef29499ce8b05961dae745eac3b9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 28 Apr 2023 08:12:00 +0200 Subject: [PATCH 012/121] Update eclipse-temurin Docker tag to v17.0.7_7-jre (#727) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 812c4c1db..21e2c3061 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM eclipse-temurin:17.0.6_10-jre +FROM eclipse-temurin:17.0.7_7-jre LABEL org.opencontainers.image.authors="Fraunhofer AISEC " From 477926e1c7b964dbbe99eb1003daf300d1e6853d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 28 Apr 2023 08:24:02 +0200 Subject: [PATCH 013/121] Update dependency org.junit.jupiter:junit-jupiter-params to v5.9.3 (#728) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8189e4441..69b3f0c5e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -35,7 +35,7 @@ kotlin-scripting-dependencies = { module = "org.jetbrains.kotlin:kotlin-scriptin detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt"} # test -junit-params = { module = "org.junit.jupiter:junit-jupiter-params", version = "5.9.2"} +junit-params = { module = "org.junit.jupiter:junit-jupiter-params", version = "5.9.3"} mockk = { module = "io.mockk:mockk", version = "1.13.5"} # this is necessary for the plugins to be used in the buildSrc folder From 3abcbe91916cb4673604b7af2d88b7f1a77cff94 Mon Sep 17 00:00:00 2001 From: Selina Lin Date: Tue, 9 May 2023 11:28:00 +0200 Subject: [PATCH 014/121] Add tests (#729) --- .../cpg/coko/dsl/ImplementationDsl.kt | 13 +- .../cpg/coko/ordering/NFAConstruction.kt | 2 +- .../backends/cpg/CPGConfigurationTest.kt | 99 ++++++++++++ .../codyze/backends/cpg/CpgOptionGroupTest.kt | 83 ++++++++++ .../backends/cpg/ImplementationDslTest.kt | 101 ++++++++++++ .../backends/cpg/NfaDfaConstructionTest.kt | 152 ++++++++++++++++++ .../codyze/backends/cpg/OnlyEvaluationTest.kt | 3 +- .../backends/cpg/OrderEvaluationTest.kt | 56 ++++++- .../FollowsEvaluationTest/SimpleFollows.java | 19 ++- .../ImplementationDslTest/SimpleJavaFile.java | 29 ++++ .../OrderEvaluationTest/OtherSimpleOrder.java | 71 ++++++++ .../aisec/codyze/cli/CodyzeOptionGroupTest.kt | 25 ++- .../codyze/core/config/CliktExtensions.kt | 37 ----- .../coko/core/modelling/OpComponents.kt | 7 +- .../coko/core/order/OrderNodeTest.kt | 61 +++++++ .../order/OrderSyntaxTreeConstructionTest.kt | 42 ++++- .../coko/dsl/cli/Validation.kt | 6 +- .../coko/dsl/host/SpecEvaluator.kt | 11 +- .../coko/dsl/cli/CokoOptionGroupTest.kt | 4 +- .../coko/dsl/cli/ValidationTest.kt | 64 ++++++++ .../coko/dsl/host/SpecEvaluatorTest.kt | 89 ++++++++++ .../same-file-extension/my.test.codyze.kts | 0 22 files changed, 900 insertions(+), 74 deletions(-) create mode 100644 codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGConfigurationTest.kt create mode 100644 codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/ImplementationDslTest.kt create mode 100644 codyze-backends/cpg/src/test/resources/ImplementationDslTest/SimpleJavaFile.java create mode 100644 codyze-backends/cpg/src/test/resources/OrderEvaluationTest/OtherSimpleOrder.java delete mode 100644 codyze-core/src/main/kotlin/de/fraunhofer/aisec/codyze/core/config/CliktExtensions.kt create mode 100644 codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/order/OrderNodeTest.kt create mode 100644 codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/ValidationTest.kt create mode 100644 codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/SpecEvaluatorTest.kt create mode 100644 codyze-specification-languages/coko/coko-dsl/src/test/resources/cli-test-directory/dir3-spec/same-file-extension/my.test.codyze.kts diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/dsl/ImplementationDsl.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/dsl/ImplementationDsl.kt index 0c604026c..e67d37584 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/dsl/ImplementationDsl.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/dsl/ImplementationDsl.kt @@ -133,18 +133,7 @@ context(CallExpression) * - If this is a [Node], we use the DFG of the CPG. */ infix fun Any.cpgFlowsTo(that: Node): Boolean = - if (this is Wildcard) { - true - } else { - when (this) { - is String -> Regex(this).matches((that as? Expression)?.evaluate()?.toString().orEmpty()) - is Iterable<*> -> this.any { it?.cpgFlowsTo(that) ?: false } - is Array<*> -> this.any { it?.cpgFlowsTo(that) ?: false } - is Node -> dataFlow(this, that).value - is ParameterGroup -> this.parameters.all { it?.cpgFlowsTo(that) ?: false } - else -> this == (that as? Expression)?.evaluate() - } - } + this.cpgFlowsTo(listOf(that)) // it should only be available in the context of a CallExpression context(CallExpression) diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/ordering/NFAConstruction.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/ordering/NFAConstruction.kt index b7b5efa5a..d7aecef4a 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/ordering/NFAConstruction.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/ordering/NFAConstruction.kt @@ -71,7 +71,7 @@ internal fun nfaForQuantifierOrderNode(node: QuantifierOrderNode): NFA = concatenateMultipleNfa( List((node.value as IntRange).first) { node.child.toNfa() } + List((node.value as IntRange).last - (node.value as IntRange).first) { - addMaybeQuantifierToNFA(node.child.toNfa()) + addOptionQuantifierToNFA(node.child.toNfa()) } ) } diff --git a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGConfigurationTest.kt b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGConfigurationTest.kt new file mode 100644 index 000000000..d3563cde2 --- /dev/null +++ b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGConfigurationTest.kt @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2022, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.fraunhofer.aisec.codyze.backends.cpg + +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +class CPGConfigurationTest { + + @Test + fun `test normalize`() { + val expectedCpgConfiguration = CPGConfiguration( + source = listOf(), + useUnityBuild = true, + typeSystemActiveInFrontend = false, + debugParser = false, + disableCleanup = false, + codeInNodes = true, + matchCommentsToNodes = true, + processAnnotations = false, + failOnError = false, + useParallelFrontends = false, + defaultPasses = true, + additionalLanguages = setOf(), + symbols = mapOf(), + passes = listOf(), + loadIncludes = true, + includePaths = listOf(), + includeAllowlist = listOf(), + includeBlocklist = listOf() + ) + + val cpgConfiguration = CPGConfiguration( + source = listOf(), + useUnityBuild = true, + typeSystemActiveInFrontend = false, + debugParser = false, + disableCleanup = false, + codeInNodes = true, + matchCommentsToNodes = true, + processAnnotations = false, + failOnError = false, + useParallelFrontends = false, + defaultPasses = true, + additionalLanguages = setOf(), + symbols = mapOf(), + passes = listOf(), + loadIncludes = false, + includePaths = listOf(), + includeAllowlist = listOf(), + includeBlocklist = listOf() + ) + + val actualCPGConfiguration = cpgConfiguration.normalize() + + assertEquals(expectedCpgConfiguration, actualCPGConfiguration) + } + + @Test + fun `test normalize with nothing to normalize`() { + val expectedCpgConfiguration = CPGConfiguration( + source = listOf(), + useUnityBuild = false, + typeSystemActiveInFrontend = false, + debugParser = false, + disableCleanup = false, + codeInNodes = true, + matchCommentsToNodes = true, + processAnnotations = false, + failOnError = false, + useParallelFrontends = false, + defaultPasses = true, + additionalLanguages = setOf(), + symbols = mapOf(), + passes = listOf(), + loadIncludes = true, + includePaths = listOf(), + includeAllowlist = listOf(), + includeBlocklist = listOf() + ) + + val actualCPGConfiguration = expectedCpgConfiguration.normalize() + + assertEquals(expectedCpgConfiguration, actualCPGConfiguration) + } +} diff --git a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt index eb6ec94f8..f4ce288bf 100644 --- a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt +++ b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt @@ -122,6 +122,79 @@ class CpgOptionGroupTest { assertThrows { cli.parse(argv) } } + @Test + fun `test resolveSymbols`() { + val expectedSymbols = mapOf( + "#" to "hash", + "+" to "plus", + "*" to "star" + ) + val cli = CPGOptionsCommand() + val argv = expectedSymbols + .flatMap { (key, value) -> listOf("--symbols", "$key=$value") } + + listOf( + "--source", + testDir1.toString() + ) + cli.parse(argv) + val symbols = cli.cpgOptions.symbols + assertEquals(expectedSymbols.entries, symbols.entries) + } + + @ParameterizedTest(name = "test {0} without includes") + @MethodSource("includesHelper") + fun `test enabled or disabled includes options without includes`(includeOption: String) { + val argv = listOf( + "--source", + testDir1.toString(), + includeOption, + testFile1.toString() + ) + val cli = CPGOptionsCommand() + + assertThrows { cli.parse(argv) } + } + + @Test + fun `test includeAllowlist`() { + val argv = listOf( + "--source", + testDir2.toString(), + "--includes", + topTestDir.toString(), + "--enabled-includes", + testFile1.toString(), + "--enabled-includes-additions", + testDir1.toString() + ) + + val cli = CPGOptionsCommand() + cli.parse(argv) + + val allowList = cli.cpgOptions.includeAllowlist + assertContentEquals(listOf(testFile1, testDir1.div("dir1file1.java")), allowList) + } + + @Test + fun `test includeBlocklist`() { + val argv = listOf( + "--source", + testDir2.toString(), + "--includes", + topTestDir.toString(), + "--disabled-includes", + testFile1.toString(), + "--disabled-includes-additions", + testDir1.toString() + ) + + val cli = CPGOptionsCommand() + cli.parse(argv) + + val blockList = cli.cpgOptions.includeBlocklist + assertContentEquals(listOf(testFile1, testDir1.div("dir1file1.java")), blockList) + } + companion object { lateinit var topTestDir: Path private lateinit var testDir1: Path @@ -262,5 +335,15 @@ class CpgOptionGroupTest { Arguments.of(arrayOf("--passes", translationOptionName)) ) } + + @JvmStatic + fun includesHelper(): Stream { + return Stream.of( + Arguments.of("--enabled-includes"), + Arguments.of("--enabled-includes-additions"), + Arguments.of("--disabled-includes"), + Arguments.of("--disabled-includes-additions"), + ) + } } } diff --git a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/ImplementationDslTest.kt b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/ImplementationDslTest.kt new file mode 100644 index 000000000..2085ec80d --- /dev/null +++ b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/ImplementationDslTest.kt @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2022, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.fraunhofer.aisec.codyze.backends.cpg + +import de.fraunhofer.aisec.codyze.backends.cpg.coko.CokoCpgBackend +import de.fraunhofer.aisec.codyze.backends.cpg.coko.dsl.cpgGetAllNodes +import de.fraunhofer.aisec.codyze.backends.cpg.coko.dsl.cpgGetNodes +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.Wildcard +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.op +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.signature +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import kotlin.io.path.Path +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class ImplementationDslTest { + + @Test + fun `test cpgGetAllNodes`() { + val op = op { + "Foo.fun" { + signature(2) + } + } + with(backend) { + val allNodes = op.cpgGetAllNodes() + assertEquals( + 5, + allNodes.size, + "cpgGetAllNodes returned ${allNodes.size} node(s) instead of 5 nodes fo the Op: $op." + ) + } + } + + @Test + fun `test cpgGetNodes`() { + val op = op { + "Foo.fun" { + signature(1..10) + } + } + with(backend) { + val nodes = op.cpgGetNodes() + assertEquals( + 2, + nodes.size, + "cpgGetNodes returned ${nodes.size} node(s) instead of 2 nodes for the Op: $op." + ) + } + } + + @Test + fun `test cpgGetNodes with unordered parameters`() { + val op = op { + "Foo.bar" { + signature(arrayOf(".*Test.*")) { + - Wildcard + - Wildcard + } + } + } + with(backend) { + val nodes = op.cpgGetNodes() + assertEquals( + 3, + nodes.size, + "cpgGetNodes returned ${nodes.size} node(s) instead of 3 nodes for the Op: $op." + ) + } + } + + companion object { + + lateinit var backend: CokoCpgBackend + + @BeforeAll + @JvmStatic + fun startup() { + val classLoader = ImplementationDslTest::class.java.classLoader + + val testFileResource = classLoader.getResource("ImplementationDslTest/SimpleJavaFile.java") + assertNotNull(testFileResource) + val testFile = Path(testFileResource.path) + backend = CokoCpgBackend(config = createCpgConfiguration(testFile)) + } + } +} diff --git a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/NfaDfaConstructionTest.kt b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/NfaDfaConstructionTest.kt index 9f288e10c..fabb881a0 100644 --- a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/NfaDfaConstructionTest.kt +++ b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/NfaDfaConstructionTest.kt @@ -121,6 +121,11 @@ class NfaDfaConstructionTest { with(mockk()) { val testObj = TestClass() val nfa = orderExpressionToNfa { maybe(testObj::create) } + val nfa2 = orderExpressionToNfa { + maybe { + - testObj::create + } + } val expectedNfa = NFA() val q0 = expectedNfa.addState(isStart = true, isAcceptingState = true) @@ -131,6 +136,7 @@ class NfaDfaConstructionTest { expectedNfa.addEdge(q2, Edge(op = NFA.EPSILON, nextState = q0)) assertEquals(expected = expectedNfa, actual = nfa) + assertEquals(expected = expectedNfa, actual = nfa2) } } @@ -144,6 +150,11 @@ class NfaDfaConstructionTest { with(mockk()) { val testObj = TestClass() val nfa = orderExpressionToNfa { option(testObj::create) } + val nfa2 = orderExpressionToNfa { + option { + - testObj::create + } + } val expectedNfa = NFA() val q0 = expectedNfa.addState(isStart = true, isAcceptingState = true) @@ -152,6 +163,147 @@ class NfaDfaConstructionTest { expectedNfa.addEdge(q0, Edge(op = NFA.EPSILON, nextState = q1)) expectedNfa.addEdge(q1, Edge(op = testObj.create().hashCode().toString(), base = baseName, nextState = q2)) + assertEquals(expected = expectedNfa, actual = nfa) + assertEquals(expected = expectedNfa, actual = nfa2) + } + } + + /** + * Tests a simple order with a count qualifier. + * + * The NFA can be converted to .dot format using: [NFA.toDotString]. + */ + @Test + fun `test simple order with count qualifier`() { + with(mockk()) { + val testObj = TestClass() + val nfa = orderExpressionToNfa { count(2, testObj::create) } + val nfa2 = orderExpressionToNfa { + count(2) { + - testObj::create + } + } + + val expectedNfa = NFA() + val q0 = expectedNfa.addState(isStart = true) + val q1 = expectedNfa.addState() + val q2 = expectedNfa.addState(isAcceptingState = true) + expectedNfa.addEdge(q0, Edge(op = testObj.create().hashCode().toString(), base = baseName, nextState = q1)) + expectedNfa.addEdge(q1, Edge(op = testObj.create().hashCode().toString(), base = baseName, nextState = q2)) + + assertEquals(expected = expectedNfa, actual = nfa) + assertEquals(expected = expectedNfa, actual = nfa2) + } + } + + /** + * Tests a simple order with a some qualifier. + * + * The NFA can be converted to .dot format using: [NFA.toDotString]. + */ + @Test + fun `test simple order with some qualifier`() { + with(mockk()) { + val testObj = TestClass() + val nfa = orderExpressionToNfa { some(testObj::create) } + val nfa2 = orderExpressionToNfa { + some { + - testObj::create + } + } + + val expectedNfa = NFA() + val q0 = expectedNfa.addState(isStart = true) + val q1 = expectedNfa.addState(isAcceptingState = true) + expectedNfa.addEdge(q0, Edge(op = testObj.create().hashCode().toString(), base = baseName, nextState = q1)) + expectedNfa.addEdge(q1, Edge(op = testObj.create().hashCode().toString(), base = baseName, nextState = q1)) + + assertEquals(expected = expectedNfa, actual = nfa) + assertEquals(expected = expectedNfa, actual = nfa2) + } + } + + /** + * Tests a simple order with a atLeast qualifier. + * + * The NFA can be converted to .dot format using: [NFA.toDotString]. + */ + @Test + fun `test simple order with atLeast qualifier`() { + with(mockk()) { + val testObj = TestClass() + val nfa = orderExpressionToNfa { atLeast(2, testObj::create) } + val nfa2 = orderExpressionToNfa { + atLeast(2) { + - testObj::create + } + } + + val expectedNfa = NFA() + val q0 = expectedNfa.addState(isStart = true) + val q1 = expectedNfa.addState() + val q2 = expectedNfa.addState(isAcceptingState = true) + expectedNfa.addEdge(q0, Edge(op = testObj.create().hashCode().toString(), base = baseName, nextState = q1)) + expectedNfa.addEdge(q1, Edge(op = testObj.create().hashCode().toString(), base = baseName, nextState = q2)) + expectedNfa.addEdge(q2, Edge(op = testObj.create().hashCode().toString(), base = baseName, nextState = q2)) + + assertEquals(expected = expectedNfa, actual = nfa) + assertEquals(expected = expectedNfa, actual = nfa2) + } + } + + /** + * Tests a simple order with a between qualifier. + * + * The NFA can be converted to .dot format using: [NFA.toDotString]. + */ + @Test + fun `test simple order with between qualifier`() { + with(mockk()) { + val testObj = TestClass() + val nfa = orderExpressionToNfa { between(2..4, testObj::create) } + val nfa2 = orderExpressionToNfa { + between(2..4) { + - testObj::create + } + } + + val expectedNfa = NFA() + val q0 = expectedNfa.addState(isStart = true) + val q1 = expectedNfa.addState() + val q2 = expectedNfa.addState(isAcceptingState = true) + val q3 = expectedNfa.addState(isAcceptingState = true) + val q4 = expectedNfa.addState(isAcceptingState = true) + expectedNfa.addEdge(q0, Edge(op = testObj.create().hashCode().toString(), base = baseName, nextState = q1)) + expectedNfa.addEdge(q1, Edge(op = testObj.create().hashCode().toString(), base = baseName, nextState = q2)) + expectedNfa.addEdge(q2, Edge(op = testObj.create().hashCode().toString(), base = baseName, nextState = q3)) + expectedNfa.addEdge(q3, Edge(op = testObj.create().hashCode().toString(), base = baseName, nextState = q4)) + + assertEquals(expected = expectedNfa, actual = nfa) + assertEquals(expected = expectedNfa, actual = nfa2) + } + } + + /** + * Tests a simple order with a set. + * + * The NFA can be converted to .dot format using: [NFA.toDotString]. + */ + @Test + fun `test simple order with set`() { + with(mockk()) { + val testObj = TestClass() + val nfa = orderExpressionToNfa { + set [testObj::create, testObj::init, testObj::start] + } + + val expectedNfa = NFA() + val q0 = expectedNfa.addState(isStart = true) + val q1 = expectedNfa.addState(isAcceptingState = true) + expectedNfa.addEdge(q0, Edge(op = testObj.create().hashCode().toString(), base = baseName, nextState = q1)) + expectedNfa.addEdge(q0, Edge(op = testObj.start().hashCode().toString(), base = baseName, nextState = q1)) + expectedNfa.addEdge(q0, Edge(op = testObj.init().hashCode().toString(), base = baseName, nextState = q1)) + assertEquals(expected = expectedNfa, actual = nfa) } } diff --git a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/OnlyEvaluationTest.kt b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/OnlyEvaluationTest.kt index 324df230a..887942276 100644 --- a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/OnlyEvaluationTest.kt +++ b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/OnlyEvaluationTest.kt @@ -16,7 +16,6 @@ package de.fraunhofer.aisec.codyze.backends.cpg import de.fraunhofer.aisec.codyze.backends.cpg.coko.CokoCpgBackend -import de.fraunhofer.aisec.codyze.backends.cpg.coko.evaluators.OnlyEvaluator import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.EvaluationContext import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.Finding import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.definition @@ -48,7 +47,7 @@ class OnlyEvaluationTest { val backend = CokoCpgBackend(config = createCpgConfiguration(testFile)) with(backend) { - val evaluator = OnlyEvaluator(listOf(fooInstance.first(0..10))) + val evaluator = only(fooInstance.first(0..10)) val findings = evaluator.evaluate( EvaluationContext( rule = ::dummyRule, diff --git a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/OrderEvaluationTest.kt b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/OrderEvaluationTest.kt index a01f429bd..e7f8fa616 100644 --- a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/OrderEvaluationTest.kt +++ b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/OrderEvaluationTest.kt @@ -26,6 +26,7 @@ import org.junit.jupiter.api.assertThrows import kotlin.io.path.Path import kotlin.reflect.full.valueParameters import kotlin.test.Test +import kotlin.test.assertContentEquals import kotlin.test.assertEquals /** @@ -41,7 +42,7 @@ class OrderEvaluationTest { fun init() = op { "Botan.set_key" { signature(Wildcard) } } // calling definition explicitly is optional - fun start() = op { definition("Botan.start") { signature(Wildcard) } } + fun start(i: Int?) = op { definition("Botan.start") { signature(i) } } fun finish() = op { "Botan.finish" { signature(Wildcard) } } } @@ -132,7 +133,7 @@ class OrderEvaluationTest { assertEquals(1, findings.size) assertEquals( "Violation against Order: \"p.set_key(key);\". Op \"[Botan.set_key(Wildcard)]\" is not allowed. " + - "Expected one of: Botan.start(Wildcard)", + "Expected one of: (Botan.start(null))", findings.first().message, ) } @@ -191,4 +192,55 @@ class OrderEvaluationTest { } } } + + context(CokoBackend) + private fun createOrderWithUserDefinedOps(testObj: CokoOrderImpl) = + order(testObj::constructor) { + - testObj.start(1) + - testObj.finish() + } + + @Test + fun `test order with used defined ops`() { + val sourceFile = getPath("OtherSimpleOrder.java") + val backend = CokoCpgBackend(config = createCpgConfiguration(sourceFile)) + + with(backend) { + val instance = CokoOrderImpl() + val orderEvaluator = createOrderWithUserDefinedOps(instance) + + val findings = orderEvaluator + .evaluate( + EvaluationContext( + rule = ::dummyFunction, + parameterMap = ::dummyFunction.valueParameters.associateWith { instance } + ) + ) + assertEquals(5, findings.size, "There were ${findings.size} finding(s) instead of 5 findings.") + + val passFindings = findings.filter { it.kind == Finding.Kind.Pass } + val failFindings = findings.filter { it.kind == Finding.Kind.Fail } + assertEquals( + 2, + passFindings.size, + "There were ${passFindings.size} Pass findings instead of 2." + ) + + assertEquals(3, failFindings.size, "There were ${failFindings.size} Fail findings instead of 3.") + + val passCpgFinding = passFindings.filterIsInstance() + val failCpgFinding = failFindings.filterIsInstance() + + assertContentEquals(passFindings, passCpgFinding, "Not all Pass findings were CpgFindings.") + assertContentEquals(failFindings, failCpgFinding, "Not all Fail findings were CpgFindings.") + + val expectedPassLines = listOf(15, 23) + val actualPassLines = passCpgFinding.mapNotNull { it.node?.location?.region?.startLine } + assertContentEquals(expectedPassLines, actualPassLines) + + val expectedFailLines = listOf(28, 37, 44) + val actualFailLines = failCpgFinding.mapNotNull { it.node?.location?.region?.startLine } + assertContentEquals(expectedFailLines, actualFailLines) + } + } } diff --git a/codyze-backends/cpg/src/test/resources/FollowsEvaluationTest/SimpleFollows.java b/codyze-backends/cpg/src/test/resources/FollowsEvaluationTest/SimpleFollows.java index 07f379140..87ac15506 100644 --- a/codyze-backends/cpg/src/test/resources/FollowsEvaluationTest/SimpleFollows.java +++ b/codyze-backends/cpg/src/test/resources/FollowsEvaluationTest/SimpleFollows.java @@ -26,17 +26,16 @@ public void unreachableFirstNotApplicable() { } } - // Waiting for release of cpg commit bb5ef7392220efcaafa715c468e5729e518c524d // Should be ok because the `f.f2()` branch is unreachable -// public void unreachableOk() { -// Foo f = new Foo(); -// f.first(); -// Bar b = new Bar(); -// if(false) -// f.f2(); -// else -// b.second(); -// } + public void unreachableOk() { + Foo f = new Foo(); + f.first(); + Bar b = new Bar(); + if(false) + f.f2(); + else + b.second(); + } // what behavior should follows have here? // public void notSure() { diff --git a/codyze-backends/cpg/src/test/resources/ImplementationDslTest/SimpleJavaFile.java b/codyze-backends/cpg/src/test/resources/ImplementationDslTest/SimpleJavaFile.java new file mode 100644 index 000000000..ae399bfbf --- /dev/null +++ b/codyze-backends/cpg/src/test/resources/ImplementationDslTest/SimpleJavaFile.java @@ -0,0 +1,29 @@ +import java.util.Random; + +public class SimpleJavaFile { + + public void one() { + Foo f = new Foo(); + f.fun(2); + f.bar("hello", "Test"); + f.bar("hello", "world"); + f.fun(6); + } + + public void two() { + Foo f = new Foo(); + f.fun(-1); + f.fun(1234); + f.bar("Test!", "my class!"); + f.fun(-127); + f.bar("my Test", "Test"); + } + +} + + +public class Foo { + public int fun(int i) {} + + public void bar(String s1, String s2) {} +} diff --git a/codyze-backends/cpg/src/test/resources/OrderEvaluationTest/OtherSimpleOrder.java b/codyze-backends/cpg/src/test/resources/OrderEvaluationTest/OtherSimpleOrder.java new file mode 100644 index 000000000..776af4a23 --- /dev/null +++ b/codyze-backends/cpg/src/test/resources/OrderEvaluationTest/OtherSimpleOrder.java @@ -0,0 +1,71 @@ +public class OtherSimpleOrder { + // DOES NOT COMPILE + // DOES NOT MAKE REAL SENSE + + char[] cipher; + int key; + int iv; + Cipher_Dir direction; + char[] buf; + + void ok() { + // ok: + Botan p4 = new Botan(2); + p4.start(1); + p4.finish(buf); + } + + void ok2() { + // ok: + Botan p4 = new Botan(2); + p4.start(1); + p4.foo(); // not in the entity and therefore ignored + p4.finish(buf); + } + + void nok1() { + Botan p = new Botan(1); + p.set_key(key); // not allowed as start + p.start(iv); + p.finish(buf); + p.foo(); // not in the entity and therefore ignored + p.set_key(key); + } + + void nok2() { + Botan p2 = new Botan(2); + p2.start(1); + // missing p2.finish(buf); + } + + void nok3() { + // ok: + Botan p3 = new Botan(2); + p3.start(0); // start is called with 0 instead of 1 + p3.finish(buf); + } +} + + + + + + + +public class Botan { + public Botan(int i) {} + + public void create() {} + + public void finish(char[] b) {} + + public void init() {} + + public void process() {} + + public void reset() {} + + public void start(int i) {} + + public void set_key(int i) {} +} \ No newline at end of file diff --git a/codyze-cli/src/test/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeOptionGroupTest.kt b/codyze-cli/src/test/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeOptionGroupTest.kt index c55dbae2b..cfb422979 100644 --- a/codyze-cli/src/test/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeOptionGroupTest.kt +++ b/codyze-cli/src/test/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeOptionGroupTest.kt @@ -51,9 +51,9 @@ class CodyzeOptionGroupTest : KoinTest { val expectedMessage = "Invalid value for \"--output-format\": invalid choice: txt. (choose from " - val actualMessage = exception.message + val actualMessage = exception.message.orEmpty() - assertTrue(actualMessage!!.contains(expectedMessage)) + assertContains(actualMessage, expectedMessage) } /** Test that [OutputBuilder] choices are cast correctly. */ @@ -69,4 +69,25 @@ class CodyzeOptionGroupTest : KoinTest { assertTrue(cli.codyzeOptions.outputBuilder is SarifBuilder) } + + /** Test that [OutputBuilder] choices are cast correctly. */ + @Test + fun outputBuilderOptionToConfigurationTest() { + val argv: Array = + arrayOf( + "--output-format", + "sarif", + "--no-good-findings", + "--no-pedantic" + ) + val cli = CodyzeCli(null) + cli.parse(argv) + + val config = cli.codyzeOptions.asConfiguration() + + assertEquals(cli.codyzeOptions.output, config.output) + assertEquals(cli.codyzeOptions.outputBuilder, config.outputBuilder) + assertEquals(cli.codyzeOptions.goodFindings, config.goodFindings) + assertEquals(cli.codyzeOptions.pedantic, config.pedantic) + } } diff --git a/codyze-core/src/main/kotlin/de/fraunhofer/aisec/codyze/core/config/CliktExtensions.kt b/codyze-core/src/main/kotlin/de/fraunhofer/aisec/codyze/core/config/CliktExtensions.kt deleted file mode 100644 index a331f00b4..000000000 --- a/codyze-core/src/main/kotlin/de/fraunhofer/aisec/codyze/core/config/CliktExtensions.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2022, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package de.fraunhofer.aisec.codyze.core.config - -import com.github.ajalt.clikt.parameters.options.* - -/** - * Clikt extension function to provide a 'option.validate' statement for functions that either - * return [[Any]] or throw an exception instead of returning [[true]]/[[false]]. - */ -inline fun OptionWithValues.validateFromError( - crossinline errorValidator: (AllT) -> Any -): OptionDelegate { - return validate { - val (result, message) = - try { - errorValidator(it) - true to "" - } catch (e: IllegalArgumentException) { - false to (e.message.orEmpty()) - } - require(result) { message } - } -} diff --git a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/modelling/OpComponents.kt b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/modelling/OpComponents.kt index 3ca148f68..38a8f41b9 100644 --- a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/modelling/OpComponents.kt +++ b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/modelling/OpComponents.kt @@ -91,7 +91,12 @@ class Definition(val fqn: String) { null ) } - return notNullParams.joinToString(prefix = fqn, separator = ", $fqn") + + val notNullParamsString = if (notNullParams.isNotEmpty()) { + notNullParams.joinToString(prefix = fqn, separator = ", $fqn") + } else { + "" + } + return notNullParamsString + if (nullParams.isNotEmpty()) { "(" + nullParams.joinToString(prefix = fqn, separator = ", $fqn") + ")" } else { diff --git a/codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/order/OrderNodeTest.kt b/codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/order/OrderNodeTest.kt new file mode 100644 index 000000000..6a66960f4 --- /dev/null +++ b/codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/order/OrderNodeTest.kt @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.order + +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.ordering.* +import org.junit.jupiter.api.Test +import kotlin.test.assertContentEquals + +class OrderNodeTest { + + @Test + fun `test applyToAll`() { + val terminalNodes = listOf( + TerminalOrderNode("base", "op1"), + TerminalOrderNode("base", "op2"), + TerminalOrderNode("base", "op3"), + ) + val sequenceOrderNode = SequenceOrderNode( + left = terminalNodes[0], + right = terminalNodes[1] + ) + val quantifierOrderNode = QuantifierOrderNode( + child = terminalNodes[2], + type = OrderQuantifier.MAYBE + ) + val orderNode = AlternativeOrderNode( + left = sequenceOrderNode, + right = quantifierOrderNode + ) + + val expectedList = listOf( + orderNode.toString(), + quantifierOrderNode.toString(), + terminalNodes[2].toString(), + sequenceOrderNode.toString(), + terminalNodes[1].toString(), + terminalNodes[0].toString(), + + ) + val actualList = mutableListOf() + + orderNode.applyToAll { + actualList.add(this.toString()) + } + + assertContentEquals(expectedList, actualList) + } +} diff --git a/codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/order/OrderSyntaxTreeConstructionTest.kt b/codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/order/OrderSyntaxTreeConstructionTest.kt index 3d92d8015..e71eeade3 100644 --- a/codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/order/OrderSyntaxTreeConstructionTest.kt +++ b/codyze-specification-languages/coko/coko-core/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/order/OrderSyntaxTreeConstructionTest.kt @@ -80,6 +80,23 @@ class OrderSyntaxTreeConstructionTest { } } + /** Tests an order with an empty set -> this should error */ + @Test + fun `test order with empty set`() { + with(mockk()) { + val testObj = TestClass() + assertFailsWith( + message = "Groups and sets must have at least one element.", + block = { + orderExpressionToSyntaxTree { + -testObj::fun1 + set {} + } + } + ) + } + } + /** Tests an order with a user defined OP */ @Test fun `test order with user defined op`() { @@ -239,7 +256,30 @@ class OrderSyntaxTreeConstructionTest { /** Tests an order with a set */ @Test - fun `test order with set`() { + fun `test order with set with one element`() { + with(mockk()) { + val testObj = TestClass() + val syntaxTree = orderExpressionToSyntaxTree { + set { + - testObj::fun1 + } + } + + val syntaxTreeShortcut = orderExpressionToSyntaxTree { + set[testObj::fun1] + } + + assertEquals(syntaxTree, syntaxTreeShortcut) + + val expectedSyntaxTree = testObj::fun1.toNode() + + assertEquals(expected = expectedSyntaxTree, actual = syntaxTree) + } + } + + /** Tests an order with a set */ + @Test + fun `test order with set with multiple elements`() { with(mockk()) { val testObj = TestClass() val syntaxTree = orderExpressionToSyntaxTree { diff --git a/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/Validation.kt b/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/Validation.kt index d30b3521d..df27d81bd 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/Validation.kt +++ b/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/Validation.kt @@ -18,12 +18,12 @@ package de.fraunhofer.aisec.codyze.specificationLanguages.coko.dsl.cli import java.nio.file.Path import kotlin.io.path.isRegularFile -val Path.extensions: String - get() = fileName?.toString()?.substringAfter('.', "").orEmpty() +val Path.fileNameString: String + get() = fileName?.toString().orEmpty() fun validateSpec(spec: List): List { require(spec.all { it.isRegularFile() }) { "All given spec paths must be files." } - require(spec.all { it.extensions == "codyze.kts" }) { + require(spec.all { it.fileNameString.endsWith(".codyze.kts") }) { "All given specification files must be coko specification files (*.codyze.kts)." } return spec diff --git a/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/SpecEvaluator.kt b/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/SpecEvaluator.kt index 632f9ae39..48553d8b5 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/SpecEvaluator.kt +++ b/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/SpecEvaluator.kt @@ -77,8 +77,17 @@ class SpecEvaluator { val primaryConstructor = checkNotNull(it.primaryConstructor) { "Could not create an instance of ${it.qualifiedName} to pass to rule " + - "${rule.name} because it does not have a primary constructor. Aborting." + "\"${rule.name}\" because it does not have a primary constructor. Aborting." } + require(primaryConstructor.visibility == KVisibility.PUBLIC) { + "Could not create an instance of ${it.qualifiedName} to pass to rule " + + "\"${rule.name}\" because it's primary constructor is not public. Aborting." + } + + require(primaryConstructor.parameters.isEmpty()) { + "Could not create an instance of ${it.qualifiedName} to pass to rule " + + "\"${rule.name}\" because it's primary constructor expects arguments. Aborting." + } // TODO: how do we access primaryConstructor.arity ? -> then we would // not need the try..catch try { diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/CokoOptionGroupTest.kt b/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/CokoOptionGroupTest.kt index c2b066dd9..2c16724e1 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/CokoOptionGroupTest.kt +++ b/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/CokoOptionGroupTest.kt @@ -86,9 +86,9 @@ class CokoOptionGroupTest : KoinTest { Assertions.assertThrows(IllegalArgumentException::class.java) { cli.executorOptions.spec } val expectedMessage = "All given specification files must be coko specification files (*.codyze.kts)." - val actualMessage = exception.message + val actualMessage = exception.message.orEmpty() - assertTrue(actualMessage!!.contains(expectedMessage)) + assertContains(actualMessage, expectedMessage) } companion object { diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/ValidationTest.kt b/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/ValidationTest.kt new file mode 100644 index 000000000..ba2b58949 --- /dev/null +++ b/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/ValidationTest.kt @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2022, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.fraunhofer.aisec.codyze.specificationLanguages.coko.dsl.cli + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import java.nio.file.Path +import kotlin.io.path.Path +import kotlin.test.assertContains +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class ValidationTest { + + @Test + fun `test fileNameString`() { + val fileName = "test.txt" + val path = Path(fileName) + assertEquals(fileName, path.fileNameString) + } + + @Test + fun `test validateSpec with directory`() { + val exception: Exception = + Assertions.assertThrows(IllegalArgumentException::class.java) { + validateSpec(listOf(testDir3Spec)) + } + + val expectedMessage = "All given spec paths must be files." + val actualMessage = exception.message.orEmpty() + + assertContains(actualMessage, expectedMessage) + } + + companion object { + private lateinit var testDir3Spec: Path + + @BeforeAll + @JvmStatic + fun startup() { + val testDir3SpecResource = + CokoOptionGroupTest::class + .java + .classLoader + .getResource("cli-test-directory/dir3-spec") + assertNotNull(testDir3SpecResource) + testDir3Spec = Path(testDir3SpecResource.path) + } + } +} diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/SpecEvaluatorTest.kt b/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/SpecEvaluatorTest.kt new file mode 100644 index 000000000..d6fff100e --- /dev/null +++ b/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/SpecEvaluatorTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2022, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.fraunhofer.aisec.codyze.specificationLanguages.coko.dsl.host + +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.Evaluator +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.Rule +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.op +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.signature +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import kotlin.test.assertContains + +class SpecEvaluatorTest { + + @Test + fun `test evaluate rule using class with private primary constructor`() { + val specEvaluator = SpecEvaluator() + specEvaluator.addSpec(PrivateConstructorSpec::class, PrivateConstructorSpec()) + val exception: Exception = + Assertions.assertThrows(IllegalArgumentException::class.java) { + specEvaluator.evaluate() + } + val expectedMessage = Regex( + "Could not create an instance of .*PrivateConstructor to pass to rule " + + "\"test rule\" because it's primary constructor is not public." + ) + val actualMessage = exception.message.orEmpty() + + assertContains(actualMessage, expectedMessage) + } + + @Test + fun `test evaluate rule using class with parameter in constructor`() { + val specEvaluator = SpecEvaluator() + specEvaluator.addSpec(ConstructorWithParamSpec::class, ConstructorWithParamSpec()) + val exception: Exception = + Assertions.assertThrows(IllegalArgumentException::class.java) { + specEvaluator.evaluate() + } + val expectedMessage = Regex( + "Could not create an instance of .*WithParam to pass to rule " + + "\"test rule\" because it's primary constructor expects arguments." + ) + val actualMessage = exception.message.orEmpty() + + assertContains(actualMessage, expectedMessage) + } +} + +class PrivateConstructorSpec { + + class PrivateConstructor private constructor() { + fun myOp(i: Any) = op { + "fqn" { + signature(i) + } + } + } + + @Rule + fun `test rule`(privateConstructor: PrivateConstructor) = Evaluator { emptyList() } +} + +class ConstructorWithParamSpec { + + class WithParam(val t: Int) { + fun myOp(i: Any) = op { + "fqn" { + signature(i, t) + } + } + } + + @Rule + fun `test rule`(withParam: WithParam) = Evaluator { emptyList() } +} diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/cli-test-directory/dir3-spec/same-file-extension/my.test.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/test/resources/cli-test-directory/dir3-spec/same-file-extension/my.test.codyze.kts new file mode 100644 index 000000000..e69de29bb From ce9991f79599bb5f7ef70b1bb8a520581a5aaf9d Mon Sep 17 00:00:00 2001 From: Selina Lin Date: Tue, 9 May 2023 11:42:37 +0200 Subject: [PATCH 015/121] Add `never` evaluator (#722) --- .../backends/cpg/coko/CokoCpgBackend.kt | 10 +- .../cpg/coko/evaluators/NeverEvaluator.kt | 76 ++++++++++++ .../backends/cpg/FollowsEvaluationTest.kt | 3 +- .../backends/cpg/NeverEvaluationTest.kt | 117 ++++++++++++++++++ .../NeverEvaluationTest/NeverPass.java | 20 +++ .../NeverEvaluationTest/NeverViolation.java | 20 +++ .../coko/core/CokoBackend.kt | 3 + docs/Coko/rules.md | 15 ++- 8 files changed, 256 insertions(+), 8 deletions(-) create mode 100644 codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/NeverEvaluator.kt create mode 100644 codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/NeverEvaluationTest.kt create mode 100644 codyze-backends/cpg/src/test/resources/NeverEvaluationTest/NeverPass.java create mode 100644 codyze-backends/cpg/src/test/resources/NeverEvaluationTest/NeverViolation.java diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/CokoCpgBackend.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/CokoCpgBackend.kt index 2b0e7efc4..a49fb370b 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/CokoCpgBackend.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/CokoCpgBackend.kt @@ -19,6 +19,7 @@ import de.fraunhofer.aisec.codyze.backends.cpg.CPGBackend import de.fraunhofer.aisec.codyze.backends.cpg.CPGConfiguration import de.fraunhofer.aisec.codyze.backends.cpg.coko.dsl.* import de.fraunhofer.aisec.codyze.backends.cpg.coko.evaluators.FollowsEvaluator +import de.fraunhofer.aisec.codyze.backends.cpg.coko.evaluators.NeverEvaluator import de.fraunhofer.aisec.codyze.backends.cpg.coko.evaluators.OnlyEvaluator import de.fraunhofer.aisec.codyze.backends.cpg.coko.evaluators.OrderEvaluator import de.fraunhofer.aisec.codyze.core.VersionProvider @@ -58,13 +59,13 @@ class CokoCpgBackend(config: BackendConfiguration) : } /** For each of the nodes in [this], there is a path to at least one of the nodes in [that]. */ - override infix fun Op.followedBy(that: Op) = FollowsEvaluator(ifOp = this, thenOp = that) + override infix fun Op.followedBy(that: Op): FollowsEvaluator = FollowsEvaluator(ifOp = this, thenOp = that) /* * Ensures the order of nodes as specified in the user configured [Order] object. * The order evaluation starts at the given [baseNodes]. */ - override fun order(baseNodes: OrderToken, block: Order.() -> Unit) = + override fun order(baseNodes: OrderToken, block: Order.() -> Unit): OrderEvaluator = OrderEvaluator( baseNodes = baseNodes.getOp().cpgGetAllNodes(), order = Order().apply(block) @@ -74,7 +75,7 @@ class CokoCpgBackend(config: BackendConfiguration) : * Ensures the order of nodes as specified in the user configured [Order] object. * The order evaluation starts at the given [baseNodes]. */ - override fun order(baseNodes: Op, block: Order.() -> Unit) = + override fun order(baseNodes: Op, block: Order.() -> Unit): OrderEvaluator = OrderEvaluator( baseNodes = baseNodes.cpgGetNodes(), order = Order().apply(block) @@ -83,5 +84,6 @@ class CokoCpgBackend(config: BackendConfiguration) : /** * Ensures that all calls to the [ops] have arguments that fit the parameters specified in [ops] */ - override fun only(vararg ops: Op) = OnlyEvaluator(ops.toList()) + override fun only(vararg ops: Op): OnlyEvaluator = OnlyEvaluator(ops.toList()) + override fun never(vararg ops: Op): NeverEvaluator = NeverEvaluator(ops.toList()) } diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/NeverEvaluator.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/NeverEvaluator.kt new file mode 100644 index 000000000..5b0cf63da --- /dev/null +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/NeverEvaluator.kt @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.fraunhofer.aisec.codyze.backends.cpg.coko.evaluators + +import de.fraunhofer.aisec.codyze.backends.cpg.coko.CokoCpgBackend +import de.fraunhofer.aisec.codyze.backends.cpg.coko.CpgFinding +import de.fraunhofer.aisec.codyze.backends.cpg.coko.dsl.cpgGetNodes +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.EvaluationContext +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.Evaluator +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.Finding +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.Op +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.Rule +import kotlin.reflect.full.findAnnotation + +context(CokoCpgBackend) +class NeverEvaluator(val forbiddenOps: List) : Evaluator { + + /** Default message if a violation is found */ + private val defaultFailMessage: String by lazy { + "Calls to ${forbiddenOps.joinToString()} are not allowed." + } + + /** Default message if the analyzed code complies with rule */ + private val defaultPassMessage by lazy { + "No calls to ${forbiddenOps.joinToString()} found which is in compliance with rule." + } + + override fun evaluate(context: EvaluationContext): Collection { + val ruleAnnotation = context.rule.findAnnotation() + val failMessage = ruleAnnotation?.failMessage?.takeIf { it.isNotEmpty() } ?: defaultFailMessage + val passMessage = ruleAnnotation?.passMessage?.takeIf { it.isNotEmpty() } ?: defaultPassMessage + + val findings = mutableListOf() + + for (op in forbiddenOps) { + val nodes = op.cpgGetNodes() + + if (nodes.isNotEmpty()) { + // This means there are calls to the forbidden op, so Fail findings are added + for (node in nodes) { + findings.add( + CpgFinding( + message = "Violation against rule: \"${node.code}\". $failMessage", + kind = Finding.Kind.Fail, + node = node + ) + ) + } + } + } + + // If there are no findings, there were no violations, so a Pass finding is added + if (findings.isEmpty()) { + findings.add( + CpgFinding( + message = passMessage, + kind = Finding.Kind.Pass, + ) + ) + } + return findings + } +} diff --git a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/FollowsEvaluationTest.kt b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/FollowsEvaluationTest.kt index 09a5cb2c0..e7067bcd3 100644 --- a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/FollowsEvaluationTest.kt +++ b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/FollowsEvaluationTest.kt @@ -17,7 +17,6 @@ package de.fraunhofer.aisec.codyze.backends.cpg import de.fraunhofer.aisec.codyze.backends.cpg.coko.CokoCpgBackend import de.fraunhofer.aisec.codyze.backends.cpg.coko.CpgFinding -import de.fraunhofer.aisec.codyze.backends.cpg.coko.evaluators.FollowsEvaluator import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.EvaluationContext import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.Finding import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.definition @@ -126,7 +125,7 @@ class FollowsEvaluationTest { val backend = CokoCpgBackend(config = createCpgConfiguration(testFile)) with(backend) { - val evaluator = FollowsEvaluator(fooInstance.first(), barInstance.second()) + val evaluator = fooInstance.first() followedBy barInstance.second() findings = evaluator.evaluate( EvaluationContext( rule = ::dummyRule, diff --git a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/NeverEvaluationTest.kt b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/NeverEvaluationTest.kt new file mode 100644 index 000000000..3b7e2bcad --- /dev/null +++ b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/NeverEvaluationTest.kt @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2023, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.fraunhofer.aisec.codyze.backends.cpg + +import de.fraunhofer.aisec.codyze.backends.cpg.coko.CokoCpgBackend +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.EvaluationContext +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.Finding +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.definition +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.op +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.signature +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import java.nio.file.Path +import kotlin.io.path.* +import kotlin.reflect.full.valueParameters +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertTrue + +class NeverEvaluationTest { + + class FooModel { + fun first(i: Any) = op { + definition("Foo.first") { + signature(i) + } + } + } + + @Test + fun `test never with violation`() { + val fooInstance = FooModel() + + val backend = CokoCpgBackend(config = createCpgConfiguration(violationFile)) + + with(backend) { + // Evaluator does not allow calls to `first` with -1 or a number between 1230 and 1240 + val evaluator = never( + fooInstance.first(-1), + fooInstance.first(1230..1240) + ) + val findings = evaluator.evaluate( + EvaluationContext( + rule = ::dummyRule, + parameterMap = ::dummyRule.valueParameters.associateWith { fooInstance } + ) + ) + + assertTrue("There were no findings which is unexpected") { findings.isNotEmpty() } + + val failFindings = findings.filter { it.kind == Finding.Kind.Fail } + assertEquals(2, failFindings.size, "Found ${failFindings.size} violation(s) instead of two violations") + } + } + + @Test + fun `test never with no violations`() { + val fooInstance = FooModel() + + val backend = CokoCpgBackend(config = createCpgConfiguration(passFile)) + + with(backend) { + // Evaluator does not allow calls to `first` with -1 or a number between 1230 and 1240 + val evaluator = never( + fooInstance.first(-1), + fooInstance.first(1230..1240) + ) + val findings = evaluator.evaluate( + EvaluationContext( + rule = ::dummyRule, + parameterMap = ::dummyRule.valueParameters.associateWith { fooInstance } + ) + ) + + assertTrue("There were no findings which is unexpected") { findings.isNotEmpty() } + + assertTrue("Not all findings are passes which is unexpected: ${findings.joinToString()}") { + findings.all { it.kind == Finding.Kind.Pass } + } + + assertEquals(1, findings.size, "Found ${findings.size} finding(s) instead of one pass finding") + } + } + + companion object { + + lateinit var violationFile: Path + lateinit var passFile: Path + + @BeforeAll + @JvmStatic + fun startup() { + val classLoader = OnlyEvaluationTest::class.java.classLoader + + val violationFileResource = classLoader.getResource("NeverEvaluationTest/NeverViolation.java") + assertNotNull(violationFileResource) + violationFile = Path(violationFileResource.path) + + val passFileResource = classLoader.getResource("NeverEvaluationTest/NeverPass.java") + assertNotNull(passFileResource) + passFile = Path(passFileResource.path) + } + } +} diff --git a/codyze-backends/cpg/src/test/resources/NeverEvaluationTest/NeverPass.java b/codyze-backends/cpg/src/test/resources/NeverEvaluationTest/NeverPass.java new file mode 100644 index 000000000..6e9c4a11b --- /dev/null +++ b/codyze-backends/cpg/src/test/resources/NeverEvaluationTest/NeverPass.java @@ -0,0 +1,20 @@ +public class NeverPass { + + public void ok() { + Foo f = new Foo(); + f.first(2); + f.first(6); + } + + public void ok2() { + Foo f = new Foo(); + f.first(1); + f.first(123); + } + +} + + +public class Foo { + public int first(int i) {} +} diff --git a/codyze-backends/cpg/src/test/resources/NeverEvaluationTest/NeverViolation.java b/codyze-backends/cpg/src/test/resources/NeverEvaluationTest/NeverViolation.java new file mode 100644 index 000000000..3cc690059 --- /dev/null +++ b/codyze-backends/cpg/src/test/resources/NeverEvaluationTest/NeverViolation.java @@ -0,0 +1,20 @@ +public class NeverViolation { + + public void ok() { + Foo f = new Foo(); + f.first(2); + f.first(6); + } + + public void fail() { + Foo f = new Foo(); + f.first(-1); + f.first(1234); + } + +} + + +public class Foo { + public int first(int i) {} +} diff --git a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/CokoBackend.kt b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/CokoBackend.kt index 0644d8a5d..b58623014 100644 --- a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/CokoBackend.kt +++ b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/CokoBackend.kt @@ -58,4 +58,7 @@ interface CokoBackend : Backend { * Ensures that all calls to the [ops] have arguments that fit the parameters specified in [ops] */ fun only(vararg ops: Op): Evaluator + + /** Ensures that there are no calls to the [ops] which have arguments that fit the parameters specified in [ops] */ + fun never(vararg ops: Op): Evaluator } diff --git a/docs/Coko/rules.md b/docs/Coko/rules.md index 08541e78e..879ff4e38 100644 --- a/docs/Coko/rules.md +++ b/docs/Coko/rules.md @@ -40,7 +40,7 @@ class Bar { ``` ## Only Evaluator -The `only` evaluator checks if all calls to an Op are only called with the specified arguments. +The `only` evaluator checks if all calls to an `Op` are only called with the specified arguments. Therefore, it takes one `Op` as argument. ```kotlin title="Rule example using only" @@ -101,7 +101,7 @@ fun `order of Foo`(foo: Foo) = ## FollowedBy Evaluator The `followedBy` evaluator works similarly like the implication in logic. It takes two `Ops` and specifies that if the first `Op` is called then the second `Op` must be called as well. -Compared to the `order` evaluator, `followedBy` is more flexible because Ops from different models can be connected. +Compared to the `order` evaluator, `followedBy` is more flexible because `Ops` from different models can be connected. ```kotlin title="Rule example using followedBy" @Rule @@ -109,3 +109,14 @@ fun `if first then second`(foo: Foo, bar: Bar) = foo.first(Wildcard) followedBy bar.second() ``` + +## Never Evaluator +The `never` evaluator is used to specify that calls to an `Op` with the specified arguments are forbidden. +It takes one `Op` as argument. + +```kotlin title="Rule example using never" +@Rule +fun `never call second with 1`(foo: Foo) = + never(foo.second(1)) + +``` From 0ae16ef94b1f3f0230464f1c7ae5b4a66f8c48cc Mon Sep 17 00:00:00 2001 From: Robert Haimerl Date: Tue, 9 May 2023 11:51:03 +0200 Subject: [PATCH 016/121] Matrix Strategy for the Build Workflow (#720) Co-authored-by: Florian Wendland --- .github/workflows/upgrade.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/upgrade.yml diff --git a/.github/workflows/upgrade.yml b/.github/workflows/upgrade.yml new file mode 100644 index 000000000..e3a736523 --- /dev/null +++ b/.github/workflows/upgrade.yml @@ -0,0 +1,33 @@ +name: java-upgrade + +on: + schedule: + # runs at 00:01 on the first every month + - cron: '1 0 1 * ?' + # can be triggered manually + workflow_dispatch: + +jobs: + build: + strategy: + fail-fast: false + matrix: + # TODO: include JAVA SE 21 (LTS) on release + version: [ "19", "20" ] + os: [ ubuntu-latest, macos-latest, windows-latest ] + runs-on: ${{ matrix.os }} + continue-on-error: true + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Java ${{ matrix.version }} + uses: actions/setup-java@v3 + with: + distribution: "temurin" + java-version: ${{ matrix.version }} + - name: Build + uses: gradle/gradle-build-action@v2 + # step-level 'continue-on-error' needed to mask a negative workflow result + continue-on-error: true + with: + arguments: :codyze-cli:build -x check --parallel -Pversion=0.0.0 \ No newline at end of file From 35fd05651829bd1c3fa2388cf348af8ea69dcb0e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 May 2023 08:27:15 +0200 Subject: [PATCH 017/121] Update dependency org.jetbrains.kotlinx:kotlinx-serialization-json to v1.5.1 (#730) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 69b3f0c5e..114e02f15 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,7 +9,7 @@ dokka = "1.8.10" [libraries] sarif4k = { module = "io.github.detekt.sarif4k:sarif4k", version = "0.3.0"} # The code can be found here: https://github.com/detekt/sarif4k. It was generated using https://app.quicktype.io/ -kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.5.0"} +kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.5.1"} kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin"} # CPG official releases From ef781dedc5514deeedf6a7af45582eec8d71bbf0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 23 May 2023 09:40:21 +0200 Subject: [PATCH 018/121] Update dependency io.github.detekt.sarif4k:sarif4k to v0.4.0 (#731) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 114e02f15..858708c59 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ dokka = "1.8.10" [libraries] -sarif4k = { module = "io.github.detekt.sarif4k:sarif4k", version = "0.3.0"} # The code can be found here: https://github.com/detekt/sarif4k. It was generated using https://app.quicktype.io/ +sarif4k = { module = "io.github.detekt.sarif4k:sarif4k", version = "0.4.0"} # The code can be found here: https://github.com/detekt/sarif4k. It was generated using https://app.quicktype.io/ kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.5.1"} kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin"} From d8d6614afd20e4670d0f1f559f9e0d448e3e99ac Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 May 2023 07:38:08 +0200 Subject: [PATCH 019/121] Update dependency com.diffplug.spotless:spotless-plugin-gradle to v6.19.0 (#734) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 858708c59..2a0e89251 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ kotlin = "1.8.21" cpg = "6.2.1" koin = "3.4.0" detekt = "1.22.0" -spotless = "6.18.0" +spotless = "6.19.0" dokka = "1.8.10" From dd7a931c997cf3f147c70205798bf25efe6c7067 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 May 2023 09:46:22 +0200 Subject: [PATCH 020/121] Update CPG packages to v6.2.2 (#732) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2a0e89251..34aea574d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] kotlin = "1.8.21" -cpg = "6.2.1" +cpg = "6.2.2" koin = "3.4.0" detekt = "1.22.0" spotless = "6.19.0" From df9f88f6b7473e7fc065ac0010a81dfcd0528fb4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Jun 2023 08:10:44 +0200 Subject: [PATCH 021/121] Update detekt to v1.23.0 (#733) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Selina Lin --- .../cpg/coko/evaluators/FollowsEvaluator.kt | 12 +++++----- .../coko/core/Finding.kt | 2 +- detekt.yml | 23 +++++++++---------- gradle/libs.versions.toml | 2 +- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/FollowsEvaluator.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/FollowsEvaluator.kt index 0b0a5e863..898ef1e45 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/FollowsEvaluator.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/FollowsEvaluator.kt @@ -70,12 +70,12 @@ class FollowsEvaluator(val ifOp: Op, val thenOp: Op) : Evaluator { listOf( CpgFinding( message = "Complies with rule: \"${from.code}\" is followed by ${ - reachableThatNodes.joinToString( - prefix = "\"", - separator = "\", \"", - postfix = "\"", - transform = { node -> node.code ?: node.toString() } - )}. $passMessage", + reachableThatNodes.joinToString( + prefix = "\"", + separator = "\", \"", + postfix = "\"", + transform = { node -> node.code ?: node.toString() } + )}. $passMessage", kind = Finding.Kind.Pass, node = from, relatedNodes = reachableThatNodes diff --git a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/Finding.kt b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/Finding.kt index c5d336edd..936c3fe54 100644 --- a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/Finding.kt +++ b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/Finding.kt @@ -35,6 +35,6 @@ interface Finding { NotApplicable(ResultKind.NotApplicable), Open(ResultKind.Open), Pass(ResultKind.Pass), - Review(ResultKind.Review); + Review(ResultKind.Review) } } diff --git a/detekt.yml b/detekt.yml index 127b85bbd..35a709206 100644 --- a/detekt.yml +++ b/detekt.yml @@ -307,7 +307,6 @@ naming: BooleanPropertyNaming: active: false allowedPattern: '^(is|has|are)' - ignoreOverridden: true ClassNaming: active: true classPattern: '[A-Z][a-zA-Z0-9]*' @@ -316,7 +315,6 @@ naming: parameterPattern: '[a-z][A-Za-z0-9]*' privateParameterPattern: '[a-z][A-Za-z0-9]*' excludeClassPattern: '$^' - ignoreOverridden: true EnumNaming: active: true enumEntryPattern: '[A-Z][_a-zA-Z0-9]*' @@ -334,12 +332,10 @@ naming: excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] functionPattern: '[a-z][a-zA-Z0-9]*' excludeClassPattern: '$^' - ignoreOverridden: true FunctionParameterNaming: active: true parameterPattern: '[a-z][A-Za-z0-9]*' excludeClassPattern: '$^' - ignoreOverridden: true InvalidPackageDeclaration: active: true rootPackage: 'de.fraunhofer.aisec.codyze' @@ -381,7 +377,6 @@ naming: variablePattern: '[a-z][A-Za-z0-9]*' privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' excludeClassPattern: '$^' - ignoreOverridden: true performance: active: true @@ -501,6 +496,10 @@ style: active: true AlsoCouldBeApply: active: false + BracesOnIfStatements: + active: false + singleLine: 'never' + multiLine: 'always' CanBeNonNullable: active: false CascadingCallWrapping: @@ -532,12 +531,14 @@ style: includeLineWrapping: false ForbiddenComment: active: false - values: - - 'FIXME:' - - 'STOPSHIP:' - - 'TODO:' + comments: + - reason: 'Forbidden FIXME todo marker in comment, please fix the problem.' + value: 'FIXME:' + - reason: 'Forbidden STOPSHIP todo marker in comment, please address the problem before shipping the code.' + value: 'STOPSHIP:' + - reason: 'Forbidden TODO todo marker in comment, please do the changes.' + value: 'TODO:' allowedPatterns: '' - customMessage: '' ForbiddenImport: active: false imports: [] @@ -582,8 +583,6 @@ style: ignoreEnums: false ignoreRanges: false ignoreExtensionFunctions: true - MandatoryBracesIfStatements: - active: false MandatoryBracesLoops: active: false MaxChainedCallsOnSameLine: diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 34aea574d..9beb233f2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ kotlin = "1.8.21" cpg = "6.2.2" koin = "3.4.0" -detekt = "1.22.0" +detekt = "1.23.0" spotless = "6.19.0" dokka = "1.8.10" From 44f0cc131154a3284c8bae984997fb729a201f04 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Jun 2023 12:09:13 +0200 Subject: [PATCH 022/121] Update koin to v3.4.1 (#735) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9beb233f2..49331f79e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] kotlin = "1.8.21" cpg = "6.2.2" -koin = "3.4.0" +koin = "3.4.1" detekt = "1.23.0" spotless = "6.19.0" dokka = "1.8.10" From 872708a6f7cdce66c1dc49388a4fa328752ba622 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 9 Jun 2023 08:37:21 +0200 Subject: [PATCH 023/121] Update kotlin monorepo to v1.8.22 (#739) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 49331f79e..002d6767a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "1.8.21" +kotlin = "1.8.22" cpg = "6.2.2" koin = "3.4.1" detekt = "1.23.0" From e03c1d3f345bd98bdfeb8ee6b688425dd6b38662 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 16 Jun 2023 14:54:29 +0200 Subject: [PATCH 024/121] Update CPG packages to v7 (major) (#736) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Selina Lin --- .../aisec/codyze/backends/cpg/CPGBackend.kt | 10 ++++++- .../codyze/backends/cpg/CPGConfiguration.kt | 3 +- .../codyze/backends/cpg/CPGOptionGroup.kt | 28 ++++++++++++------- .../cpg/coko/evaluators/OrderEvaluator.kt | 14 ++++++---- .../codyze/backends/cpg/CpgOptionGroupTest.kt | 8 +++--- .../aisec/codyze/backends/cpg/TestUtils.kt | 2 +- .../coko/dsl/CokoCpgIntegrationTest.kt | 12 ++++---- gradle/libs.versions.toml | 17 +++++++---- 8 files changed, 60 insertions(+), 34 deletions(-) diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGBackend.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGBackend.kt index 119aa3d74..b57f52d64 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGBackend.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGBackend.kt @@ -68,7 +68,6 @@ open class CPGBackend(config: BackendConfiguration) : Backend { .failOnError(failOnError) .useParallelFrontends(useParallelFrontends) .typeSystemActiveInFrontend(typeSystemActiveInFrontend) - .defaultLanguages() .sourceLocations(source.map { (it.toFile()) }) .symbols(symbols) .useUnityBuild(useUnityBuild) @@ -86,6 +85,15 @@ open class CPGBackend(config: BackendConfiguration) : Backend { if (defaultPasses) translationConfiguration.defaultPasses() passes.forEach { translationConfiguration.registerPass(it) } + translationConfiguration.optionalLanguage( + "de.fraunhofer.aisec.cpg.frontends.cxx.CLanguage" + ) + translationConfiguration.optionalLanguage( + "de.fraunhofer.aisec.cpg.frontends.cxx.CPPLanguage" + ) + translationConfiguration.optionalLanguage( + "de.fraunhofer.aisec.cpg.frontends.java.JavaLanguage" + ) translationConfiguration.optionalLanguage( "de.fraunhofer.aisec.cpg.frontends.python.PythonLanguage" ) diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGConfiguration.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGConfiguration.kt index 145fd4944..d87e31f38 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGConfiguration.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGConfiguration.kt @@ -19,6 +19,7 @@ import de.fraunhofer.aisec.codyze.core.backend.BackendConfiguration import de.fraunhofer.aisec.cpg.passes.Pass import mu.KotlinLogging import java.nio.file.Path +import kotlin.reflect.KClass private val logger = KotlinLogging.logger {} @@ -44,7 +45,7 @@ data class CPGConfiguration( val defaultPasses: Boolean, val additionalLanguages: Set, val symbols: Map, - val passes: List, + val passes: List>>, val loadIncludes: Boolean, val includePaths: List, val includeAllowlist: List, diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGOptionGroup.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGOptionGroup.kt index 49fcb3067..3a8c9552c 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGOptionGroup.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGOptionGroup.kt @@ -24,6 +24,9 @@ import de.fraunhofer.aisec.codyze.core.config.combineSources import de.fraunhofer.aisec.codyze.core.config.resolvePaths import de.fraunhofer.aisec.cpg.passes.Pass import java.nio.file.Path +import kotlin.reflect.KClass +import kotlin.reflect.full.isSubclassOf +import kotlin.reflect.full.isSuperclassOf /** * Holds the common CLI options for all CPG based Codyze backends. @@ -173,10 +176,10 @@ class CPGOptionGroup : BackendOptions(helpName = "CPG Backend Options") { ) } - private val rawPasses: List by option("--passes", help = "Definition of additional symbols.") + private val rawPasses: List>> by option("--passes", help = "Definition of additional symbols.") .convert { convertPass(it) } .multiple() - private val rawPassesAdditions: List by option( + private val rawPassesAdditions: List>> by option( "--passes-additions", help = "See --passes, but appends the values to the ones specified in configuration file." @@ -185,7 +188,9 @@ class CPGOptionGroup : BackendOptions(helpName = "CPG Backend Options") { .multiple() /** Lazy property that combines all symbols from the different options into a single map. */ - val passes: List by lazy { resolvePasses(passes = rawPasses, additionalPasses = rawPassesAdditions) } + val passes: List>> by lazy { + resolvePasses(passes = rawPasses, additionalPasses = rawPassesAdditions) + } val loadIncludes: Boolean by option( "--analyze-includes", @@ -285,17 +290,20 @@ class CPGOptionGroup : BackendOptions(helpName = "CPG Backend Options") { return symbols + additionalSymbols } - private fun resolvePasses(passes: List, additionalPasses: List): List { + private fun resolvePasses( + passes: List>>, + additionalPasses: List>> + ): List>> { return passes + additionalPasses } - @Suppress("SwallowedException", "ThrowsCount") - private fun convertPass(className: String) = + @Suppress("SwallowedException", "ThrowsCount", "UNCHECKED_CAST") + private fun convertPass(className: String): KClass> = try { - val clazz = Class.forName(className) - if (Pass::class.java.isAssignableFrom(clazz)) { - // TODO: use 'isSubtypeOf' ? - clazz.getDeclaredConstructor().newInstance() as Pass + val clazz = Class.forName(className).kotlin + if (clazz.isSubclassOf(Pass::class)) { + if (clazz.isSuperclassOf(Pass::class)) throw ReflectiveOperationException("Cannot register $className") + (clazz as? KClass>) ?: throw ReflectiveOperationException("$className is not a CPG Pass") } else { throw ReflectiveOperationException("$className is not a CPG Pass") } diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OrderEvaluator.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OrderEvaluator.kt index 4b78aee52..0a7f7a1e7 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OrderEvaluator.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OrderEvaluator.kt @@ -28,9 +28,9 @@ import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.FunctionO import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.Op import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.Order import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.ordering.* -import de.fraunhofer.aisec.cpg.graph.AssignmentTarget -import de.fraunhofer.aisec.cpg.graph.Node -import de.fraunhofer.aisec.cpg.graph.followNextEOG +import de.fraunhofer.aisec.cpg.graph.* +import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration +import de.fraunhofer.aisec.cpg.graph.statements.expressions.DeclaredReferenceExpression import mu.KotlinLogging import kotlin.reflect.full.createType import kotlin.reflect.full.declaredMemberFunctions @@ -162,8 +162,12 @@ class OrderEvaluator(val baseNodes: Collection, val order: Order) : Evalua dfa = dfa, hashToMethod = hashToMethod, nodeToRelevantMethod = nodesToOp, - consideredBases = baseNodes.map { node -> - node.followNextEOG { it.end is AssignmentTarget }!!.last().end + consideredBases = baseNodes.flatMap { node -> + node.followNextDFGEdgesUntilHit { next -> + next is VariableDeclaration || next is DeclaredReferenceExpression + }.fulfilled.mapNotNull { path -> + path.lastOrNull() + } }.toSet(), consideredResetNodes = baseNodes.toSet(), context = context, diff --git a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt index f4ce288bf..0bbb815fa 100644 --- a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt +++ b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt @@ -105,10 +105,10 @@ class CpgOptionGroupTest { ) val expectedPassesNames = - listOf(EdgeCachePass(), FilenameMapper(), CallResolver()).map { p -> - p::class.qualifiedName + listOf(EdgeCachePass::class, FilenameMapper::class, CallResolver::class).map { p -> + p.qualifiedName } - val actualPassesNames = cli.cpgOptions.passes.map { p -> p::class.qualifiedName } + val actualPassesNames = cli.cpgOptions.passes.map { p -> p.qualifiedName } logger.info { actualPassesNames.joinToString(",") } @@ -330,7 +330,7 @@ class CpgOptionGroupTest { assertNotNull(translationOptionName) return Stream.of( - Arguments.of(arrayOf("--passes", passName)), + Arguments.of(arrayOf("--source", testDir1.toString(), "--passes", passName)), Arguments.of(arrayOf("--passes", "my.passes.MyPass")), Arguments.of(arrayOf("--passes", translationOptionName)) ) diff --git a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/TestUtils.kt b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/TestUtils.kt index acc478177..a3cea8180 100644 --- a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/TestUtils.kt +++ b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/TestUtils.kt @@ -40,7 +40,7 @@ fun createCpgConfiguration(vararg sourceFile: Path) = includePaths = listOf(), includeAllowlist = listOf(), loadIncludes = false, - passes = listOf(EdgeCachePass(), UnreachableEOGPass()), + passes = listOf(EdgeCachePass::class, UnreachableEOGPass::class), ) @Rule diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoCpgIntegrationTest.kt b/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoCpgIntegrationTest.kt index 9af415894..0f710d243 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoCpgIntegrationTest.kt +++ b/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoCpgIntegrationTest.kt @@ -60,7 +60,7 @@ class CokoCpgIntegrationTest { includePaths = listOf(), includeAllowlist = listOf(), loadIncludes = false, - passes = listOf(UnreachableEOGPass(), EdgeCachePass()), + passes = listOf(UnreachableEOGPass::class, EdgeCachePass::class), ) /** @@ -89,7 +89,7 @@ class CokoCpgIntegrationTest { val run = executor.evaluate() // assertions for the order rule - assertEquals(run.results?.size, 16) + assertEquals(16, run.results?.size) } /** @@ -119,7 +119,7 @@ class CokoCpgIntegrationTest { val run = executor.evaluate() // assertions for the order rule - assertEquals(run.results?.size, 1) + assertEquals(1, run.results?.size) } /** @@ -148,7 +148,7 @@ class CokoCpgIntegrationTest { val executor = CokoExecutor(cokoConfiguration, backend) val run = executor.evaluate() - assertEquals(run.results?.size, 16) + assertEquals(16, run.results?.size) } /** @@ -177,7 +177,7 @@ class CokoCpgIntegrationTest { val executor = CokoExecutor(cokoConfiguration, backend) val run = executor.evaluate() - assertEquals(run.results?.size, 16) + assertEquals(16, run.results?.size) } /** @@ -208,7 +208,7 @@ class CokoCpgIntegrationTest { val executor = CokoExecutor(cokoConfiguration, backend) val run = executor.evaluate() - assertEquals(run.results?.size, 16) + assertEquals(16, run.results?.size) } /** diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 002d6767a..8c158e3c3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] kotlin = "1.8.22" -cpg = "6.2.2" +cpg = "7.0.0" koin = "3.4.1" detekt = "1.23.0" spotless = "6.19.0" @@ -13,13 +13,18 @@ kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serializa kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin"} # CPG official releases -#cpg-core = { module = "de.fraunhofer.aisec:cpg-core", version.ref = "cpg"} -#cpg-analysis = { module = "de.fraunhofer.aisec:cpg-analysis", version.ref = "cpg"} +cpg-core = { module = "de.fraunhofer.aisec:cpg-core", version.ref = "cpg"} +cpg-analysis = { module = "de.fraunhofer.aisec:cpg-analysis", version.ref = "cpg"} +cpg-language-cxx = { module = "de.fraunhofer.aisec:cpg-language-cxx", version.ref = "cpg"} +cpg-language-java = { module = "de.fraunhofer.aisec:cpg-language-java", version.ref = "cpg"} #cpg-language-go = { module = "de.fraunhofer.aisec:cpg-language-go", version.ref = "cpg"} +#cpg-language-python = { module = "de.fraunhofer.aisec:cpg-language-python", version.ref = "cpg"} +#cpg-language-llvm = { module = "de.fraunhofer.aisec:cpg-language-llvm", version.ref = "cpg"} +#cpg-language-typescript = { module = "de.fraunhofer.aisec:cpg-language-typescript", version.ref = "cpg"} # CPG GitHub builds using JitPack -cpg-core = { module = "de.fraunhofer.aisec:cpg-core", version.ref = "cpg"} -cpg-analysis = { module = "de.fraunhofer.aisec:cpg-analysis", version.ref = "cpg"} +#cpg-core = { module = "com.github.Fraunhofer-AISEC.cpg:cpg-core", version.ref = "cpg"} +#cpg-analysis = { module = "com.github.Fraunhofer-AISEC.cpg:cpg-analysis", version.ref = "cpg"} #cpg-language-go = { module = "com.github.Fraunhofer-AISEC.cpg:cpg-language-go", version.ref = "cpg"} kotlin-logging = { module = "io.github.microutils:kotlin-logging-jvm", version = "3.0.5"} @@ -49,7 +54,7 @@ dokka-base = { module = "org.jetbrains.dokka:dokka-base", version.ref = "dokka" [bundles] -cpg = ["cpg-core", "cpg-analysis"] # without "cpg-language-go" +cpg = ["cpg-core", "cpg-analysis", "cpg-language-cxx", "cpg-language-java"] # without "cpg-language-go" sarif = ["sarif4k", "kotlinx-serialization-json"] [plugins] From 0652f7c2d3563f6f37e7223aeb5af12a7f351eaf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 16 Jun 2023 15:31:37 +0200 Subject: [PATCH 025/121] Update dokka to v1.8.20 (#738) * Update dokka to v1.8.20 * Fix change in OutputDirectory API --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Wendland, Florian Co-authored-by: Selina Lin --- build.gradle.kts | 4 ++-- buildSrc/src/main/kotlin/documented.gradle.kts | 4 ++-- gradle/libs.versions.toml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 39c9ff6e8..6e23712e4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -31,13 +31,13 @@ tasks.dokkaHtmlMultiModule.configure { dokkaAssetsBaseDirectory .resolve("JetBrainsMono") .copyRecursively( - target = outputDirectory.get().resolve("styles").resolve("JetBrainsMono"), + target = file(outputDirectory).resolve("styles").resolve("JetBrainsMono"), overwrite = true, ) dokkaAssetsBaseDirectory .resolve("inter") .copyRecursively( - target = outputDirectory.get().resolve("styles").resolve("inter"), + target = file(outputDirectory).resolve("styles").resolve("inter"), overwrite = true, ) } diff --git a/buildSrc/src/main/kotlin/documented.gradle.kts b/buildSrc/src/main/kotlin/documented.gradle.kts index 8443db11f..fb68d8bd8 100644 --- a/buildSrc/src/main/kotlin/documented.gradle.kts +++ b/buildSrc/src/main/kotlin/documented.gradle.kts @@ -49,13 +49,13 @@ tasks.withType().configureEach { dokkaAssetsBaseDirectory .resolve("JetBrainsMono") .copyRecursively( - target = outputDirectory.get().resolve("styles").resolve("JetBrainsMono"), + target = file(outputDirectory).resolve("styles").resolve("JetBrainsMono"), overwrite = true, ) dokkaAssetsBaseDirectory .resolve("inter") .copyRecursively( - target = outputDirectory.get().resolve("styles").resolve("inter"), + target = file(outputDirectory).resolve("styles").resolve("inter"), overwrite = true, ) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8c158e3c3..cddbd4276 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ cpg = "7.0.0" koin = "3.4.1" detekt = "1.23.0" spotless = "6.19.0" -dokka = "1.8.10" +dokka = "1.8.20" [libraries] From f3919f475c0d21df415f0a98939a1cca01b06198 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Jun 2023 08:56:46 +0200 Subject: [PATCH 026/121] Update dependency com.github.ajalt.clikt:clikt to v3.5.4 (#740) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cddbd4276..c31561d01 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,7 +29,7 @@ cpg-language-java = { module = "de.fraunhofer.aisec:cpg-language-java", version. kotlin-logging = { module = "io.github.microutils:kotlin-logging-jvm", version = "3.0.5"} log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j18-impl", version = "2.18.0"} -clikt = { module = "com.github.ajalt.clikt:clikt", version = "3.5.2"} +clikt = { module = "com.github.ajalt.clikt:clikt", version = "3.5.4"} koin = { module = "io.insert-koin:koin-core", version.ref = "koin"} koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin"} koin-junit5 = { module = "io.insert-koin:koin-test-junit5", version.ref = "koin"} From ca584b69682b04623a925c23c33788b4bd2dc9d8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Jun 2023 13:37:48 +0200 Subject: [PATCH 027/121] Update dependency io.insert-koin:koin-core to v3.4.2 (#737) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Wendland, Florian --- gradle/libs.versions.toml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c31561d01..902e01ee2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,8 @@ [versions] kotlin = "1.8.22" cpg = "7.0.0" -koin = "3.4.1" +koin = "3.4.2" +koin-test = "3.4.1" detekt = "1.23.0" spotless = "6.19.0" dokka = "1.8.20" @@ -31,8 +32,8 @@ kotlin-logging = { module = "io.github.microutils:kotlin-logging-jvm", version = log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j18-impl", version = "2.18.0"} clikt = { module = "com.github.ajalt.clikt:clikt", version = "3.5.4"} koin = { module = "io.insert-koin:koin-core", version.ref = "koin"} -koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin"} -koin-junit5 = { module = "io.insert-koin:koin-test-junit5", version.ref = "koin"} +koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin-test"} +koin-junit5 = { module = "io.insert-koin:koin-test-junit5", version.ref = "koin-test"} kotlin-scripting-common = { module = "org.jetbrains.kotlin:kotlin-scripting-common", version.ref = "kotlin" } kotlin-scripting-jvm = { module = "org.jetbrains.kotlin:kotlin-scripting-jvm", version.ref = "kotlin" } kotlin-scripting-host = { module = "org.jetbrains.kotlin:kotlin-scripting-jvm-host", version.ref = "kotlin" } From 6ce35a4d7c647d079c37ffc85dcc20498e32a6eb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 3 Jul 2023 08:29:57 +0200 Subject: [PATCH 028/121] Update dependency gradle to v8.2 (#741) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.jar | Bin 62076 -> 63375 bytes gradle/wrapper/gradle-wrapper.properties | 3 ++- gradlew | 5 ++++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index c1962a79e29d3e0ab67b14947c167a862655af9b..033e24c4cdf41af1ab109bc7f253b2b887023340 100644 GIT binary patch delta 16170 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 delta 14892 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 37aef8d3f..62f495dfe 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index aeb74cbb4..fcb6fca14 100755 --- a/gradlew +++ b/gradlew @@ -130,10 +130,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. From 149b873f4551927e51c8325b133e7084f26349ec Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 7 Jul 2023 15:52:12 +0200 Subject: [PATCH 029/121] Update dependency com.github.ajalt.clikt:clikt to v4 (#742) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Wendland, Florian --- .../codyze/backends/cpg/CpgOptionGroupTest.kt | 18 +++++++++++++++++- .../fraunhofer/aisec/codyze/cli/CodyzeCli.kt | 4 ++-- .../aisec/codyze/cli/JsonValueSource.kt | 4 ++-- .../aisec/codyze/cli/CodyzeOptionGroupTest.kt | 7 +++++-- gradle/libs.versions.toml | 2 +- 5 files changed, 27 insertions(+), 8 deletions(-) diff --git a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt index 0bbb815fa..c8fe38d49 100644 --- a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt +++ b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt @@ -16,6 +16,7 @@ package de.fraunhofer.aisec.codyze.backends.cpg import com.github.ajalt.clikt.core.BadParameterValue +import com.github.ajalt.clikt.core.MultiUsageError import com.github.ajalt.clikt.core.NoOpCliktCommand import com.github.ajalt.clikt.parameters.groups.provideDelegate import de.fraunhofer.aisec.codyze.core.config.combineSources @@ -119,7 +120,22 @@ class CpgOptionGroupTest { @MethodSource("incorrectPassesHelper") fun incorrectPassesTest(argv: Array) { val cli = CPGOptionsCommand() - assertThrows { cli.parse(argv) } + + val incorrectPassIndex = argv.indexOf("--passes") + 1 + try { + // expected to fail + cli.parse(argv) + } catch (e: BadParameterValue) { + assertEquals("--passes", e.paramName) + + val expectedMessage = "Cannot register ${argv[incorrectPassIndex]}" + assertEquals(expectedMessage, e.message.orEmpty()) + } catch (e: MultiUsageError) { + // mutiple errors where one is caused by the '--passes' parameter + e.errors.filterIsInstance().forEach { + assertEquals("--passes", it.paramName) + } + } } @Test diff --git a/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeCli.kt b/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeCli.kt index 3a295ebaa..17b6950de 100644 --- a/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeCli.kt +++ b/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeCli.kt @@ -18,7 +18,7 @@ package de.fraunhofer.aisec.codyze.cli import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.core.NoOpCliktCommand import com.github.ajalt.clikt.core.context -import com.github.ajalt.clikt.output.CliktHelpFormatter +import com.github.ajalt.clikt.output.MordantHelpFormatter import com.github.ajalt.clikt.parameters.arguments.argument import com.github.ajalt.clikt.parameters.arguments.multiple import com.github.ajalt.clikt.parameters.groups.provideDelegate @@ -78,7 +78,7 @@ class CodyzeCli(val configFile: Path?) : ) context { valueSource = configFile?.let { JsonValueSource.from(it, requireValid = true) } - helpFormatter = CliktHelpFormatter(showDefaultValues = true, requiredOptionMarker = "*") + helpFormatter = { MordantHelpFormatter(it, requiredOptionMarker = "*", showDefaultValues = true) } } } diff --git a/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/JsonValueSource.kt b/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/JsonValueSource.kt index 493a9a2dc..19ff09036 100644 --- a/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/JsonValueSource.kt +++ b/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/JsonValueSource.kt @@ -45,8 +45,8 @@ class JsonValueSource( // listOf(OptionParser.Invocation(name = "source", values = // listOf(cursor[0].jsonPrimitive.content)))) // instead of relying on metavar, it // is also possible to parse the option and look at the return type - "PATH" -> // make all paths given in the config file relative to the path of the - // config file + context.localization.pathMetavar() -> // make all paths given in the config file relative to the path + // of the config file filePath .toAbsolutePath() // needed for when the given config file path is a // relative path diff --git a/codyze-cli/src/test/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeOptionGroupTest.kt b/codyze-cli/src/test/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeOptionGroupTest.kt index cfb422979..840b88eae 100644 --- a/codyze-cli/src/test/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeOptionGroupTest.kt +++ b/codyze-cli/src/test/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeOptionGroupTest.kt @@ -46,11 +46,14 @@ class CodyzeOptionGroupTest : KoinTest { ) val cli = CodyzeCli(null) - val exception: Exception = + val exception: BadParameterValue = Assertions.assertThrows(BadParameterValue::class.java) { cli.parse(argv) } + val expectedParameterName = "--output-format" + assertEquals(exception.paramName, expectedParameterName) + val expectedMessage = - "Invalid value for \"--output-format\": invalid choice: txt. (choose from " + "invalid choice: txt. (choose from " val actualMessage = exception.message.orEmpty() assertContains(actualMessage, expectedMessage) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 902e01ee2..3ca72fc64 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,7 +30,7 @@ cpg-language-java = { module = "de.fraunhofer.aisec:cpg-language-java", version. kotlin-logging = { module = "io.github.microutils:kotlin-logging-jvm", version = "3.0.5"} log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j18-impl", version = "2.18.0"} -clikt = { module = "com.github.ajalt.clikt:clikt", version = "3.5.4"} +clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.0.0"} koin = { module = "io.insert-koin:koin-core", version.ref = "koin"} koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin-test"} koin-junit5 = { module = "io.insert-koin:koin-test-junit5", version.ref = "koin-test"} From 381c08edcb810876602a9926fae87ab671522f77 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 7 Jul 2023 16:21:01 +0200 Subject: [PATCH 030/121] Update kotlin monorepo to v1.9.0 (#743) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3ca72fc64..259ec0018 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "1.8.22" +kotlin = "1.9.0" cpg = "7.0.0" koin = "3.4.2" koin-test = "3.4.1" From 629b90a3240062971b4602f75463b327fb6ab889 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 11 Jul 2023 09:39:01 +0200 Subject: [PATCH 031/121] Update dependency gradle to v8.2.1 (#744) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 62f495dfe..9f4197d5f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From e29dfb95ea9b743360c0a0b5e4c306df7ce6dc8a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 17 Jul 2023 08:16:16 +0200 Subject: [PATCH 032/121] Update dependency com.github.ajalt.clikt:clikt to v4.1.0 (#745) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 259ec0018..57dc51987 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,7 +30,7 @@ cpg-language-java = { module = "de.fraunhofer.aisec:cpg-language-java", version. kotlin-logging = { module = "io.github.microutils:kotlin-logging-jvm", version = "3.0.5"} log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j18-impl", version = "2.18.0"} -clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.0.0"} +clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.1.0"} koin = { module = "io.insert-koin:koin-core", version.ref = "koin"} koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin-test"} koin-junit5 = { module = "io.insert-koin:koin-test-junit5", version.ref = "koin-test"} From d9defa1e5b7db647d7cbfd32c5463ae78c09cdcf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 19 Jul 2023 07:53:55 +0200 Subject: [PATCH 033/121] Update dependency com.diffplug.spotless:spotless-plugin-gradle to v6.20.0 (#746) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 57dc51987..170f61a50 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ cpg = "7.0.0" koin = "3.4.2" koin-test = "3.4.1" detekt = "1.23.0" -spotless = "6.19.0" +spotless = "6.20.0" dokka = "1.8.20" From 345b3a25af57aa48e22cfcae81636a3cdc8dd606 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 21 Jul 2023 07:36:39 +0200 Subject: [PATCH 034/121] Update CPG packages to v7.1.0 (#747) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 170f61a50..6649d3631 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] kotlin = "1.9.0" -cpg = "7.0.0" +cpg = "7.1.0" koin = "3.4.2" koin-test = "3.4.1" detekt = "1.23.0" From 04e1812997752b39dc54d87a229a7315f4d568fe Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 26 Jul 2023 07:59:15 +0200 Subject: [PATCH 035/121] Update dependency org.junit.jupiter:junit-jupiter-params to v5.10.0 (#748) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6649d3631..264c42a80 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -41,7 +41,7 @@ kotlin-scripting-dependencies = { module = "org.jetbrains.kotlin:kotlin-scriptin detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt"} # test -junit-params = { module = "org.junit.jupiter:junit-jupiter-params", version = "5.9.3"} +junit-params = { module = "org.junit.jupiter:junit-jupiter-params", version = "5.10.0"} mockk = { module = "io.mockk:mockk", version = "1.13.5"} # this is necessary for the plugins to be used in the buildSrc folder From 9112cc500a2c35af914e3cdbdcda87b67908a907 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 26 Jul 2023 09:27:30 +0200 Subject: [PATCH 036/121] Update eclipse-temurin Docker tag to v17.0.8_7-jre (#749) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 21e2c3061..7374e3039 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM eclipse-temurin:17.0.7_7-jre +FROM eclipse-temurin:17.0.8_7-jre LABEL org.opencontainers.image.authors="Fraunhofer AISEC " From 53eba9c8943059f4928424a0c050fa10cdf3dced Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 28 Jul 2023 08:34:07 +0200 Subject: [PATCH 037/121] Update CPG packages to v7.1.2 (#750) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 264c42a80..ef9410087 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] kotlin = "1.9.0" -cpg = "7.1.0" +cpg = "7.1.2" koin = "3.4.2" koin-test = "3.4.1" detekt = "1.23.0" From 668a07b0e06e68abddd13625dc0ffa8a825018b1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 31 Jul 2023 09:31:25 +0200 Subject: [PATCH 038/121] Update detekt to v1.23.1 (#753) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ef9410087..81adecc64 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ kotlin = "1.9.0" cpg = "7.1.2" koin = "3.4.2" koin-test = "3.4.1" -detekt = "1.23.0" +detekt = "1.23.1" spotless = "6.20.0" dokka = "1.8.20" From 4954f220c830e7187a489d0dc79875a1f3fce9c5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 31 Jul 2023 07:34:24 +0000 Subject: [PATCH 039/121] Update dependency io.insert-koin:koin-core to v3.4.3 (#751) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 81adecc64..b5c3e2804 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] kotlin = "1.9.0" cpg = "7.1.2" -koin = "3.4.2" +koin = "3.4.3" koin-test = "3.4.1" detekt = "1.23.1" spotless = "6.20.0" From a68a9abfc6568d486d65bc97cf326d04fdf44507 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 1 Aug 2023 07:20:33 +0200 Subject: [PATCH 040/121] Update dependency com.github.ajalt.clikt:clikt to v4.2.0 (#754) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b5c3e2804..ed14d4b8a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,7 +30,7 @@ cpg-language-java = { module = "de.fraunhofer.aisec:cpg-language-java", version. kotlin-logging = { module = "io.github.microutils:kotlin-logging-jvm", version = "3.0.5"} log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j18-impl", version = "2.18.0"} -clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.1.0"} +clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.2.0"} koin = { module = "io.insert-koin:koin-core", version.ref = "koin"} koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin-test"} koin-junit5 = { module = "io.insert-koin:koin-test-junit5", version.ref = "koin-test"} From f6c0a3526e4dfe535df426bd13fe2fdb9fd4cdca Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 1 Aug 2023 05:23:45 +0000 Subject: [PATCH 041/121] Update koin.test to v3.4.3 (#752) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ed14d4b8a..3606f0743 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ kotlin = "1.9.0" cpg = "7.1.2" koin = "3.4.3" -koin-test = "3.4.1" +koin-test = "3.4.3" detekt = "1.23.1" spotless = "6.20.0" dokka = "1.8.20" From 76047e9a9999471c5c1cbd3caa87bddded09e0dc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 16 Aug 2023 08:00:57 +0200 Subject: [PATCH 042/121] Update dependency io.mockk:mockk to v1.13.7 (#755) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3606f0743..0710f69f9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -42,7 +42,7 @@ detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", # test junit-params = { module = "org.junit.jupiter:junit-jupiter-params", version = "5.10.0"} -mockk = { module = "io.mockk:mockk", version = "1.13.5"} +mockk = { module = "io.mockk:mockk", version = "1.13.7"} # this is necessary for the plugins to be used in the buildSrc folder kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } From 98948113216dc6193687ad7de4f528d67c42e4ab Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 18 Aug 2023 09:27:35 +0200 Subject: [PATCH 043/121] Update dependency gradle to v8.3 (#756) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.jar | Bin 63375 -> 63721 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 3 ++- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 033e24c4cdf41af1ab109bc7f253b2b887023340..7f93135c49b765f8051ef9d0a6055ff8e46073d8 100644 GIT binary patch delta 28216 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9f4197d5f..ac72c34e8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index fcb6fca14..0adc8e1a5 100755 --- a/gradlew +++ b/gradlew @@ -83,7 +83,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum From f3a0a7a1e59144ad397767e5b782d14431f99703 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 24 Aug 2023 07:56:04 +0200 Subject: [PATCH 044/121] Update dependency org.jetbrains.kotlinx:kotlinx-serialization-json to v1.6.0 (#757) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0710f69f9..81a9a46ab 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ dokka = "1.8.20" [libraries] sarif4k = { module = "io.github.detekt.sarif4k:sarif4k", version = "0.4.0"} # The code can be found here: https://github.com/detekt/sarif4k. It was generated using https://app.quicktype.io/ -kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.5.1"} +kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.6.0"} kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin"} # CPG official releases From c233c79ecc4f0afcaf1ab4e10e48ec20a0b8b545 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 08:56:43 +0200 Subject: [PATCH 045/121] Update kotlin monorepo to v1.9.10 (#758) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 81a9a46ab..45210338c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "1.9.0" +kotlin = "1.9.10" cpg = "7.1.2" koin = "3.4.3" koin-test = "3.4.3" From 9f2063b75b8e5230aa8c67969a03453a500e46d5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 07:00:06 +0000 Subject: [PATCH 046/121] Update spotless to v6.21.0 (#759) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 45210338c..357445188 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ cpg = "7.1.2" koin = "3.4.3" koin-test = "3.4.3" detekt = "1.23.1" -spotless = "6.20.0" +spotless = "6.21.0" dokka = "1.8.20" From 20c83a37d36e559fe9971028eff04a546fe6e0c3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 07:02:55 +0000 Subject: [PATCH 047/121] Update actions/checkout action to v4 (#761) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- .github/workflows/codecov.yml | 2 +- .github/workflows/detekt.yml | 2 +- .github/workflows/docs.yml | 2 +- .github/workflows/upgrade.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8d0e57be6..0c6f0917f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: permissions: contents: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: distribution: "temurin" diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index b04e6b8c5..bce5e5d56 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -13,7 +13,7 @@ jobs: analyze-code-coverage: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Java uses: actions/setup-java@v3 with: diff --git a/.github/workflows/detekt.yml b/.github/workflows/detekt.yml index bcb5c6496..a2f98fea2 100644 --- a/.github/workflows/detekt.yml +++ b/.github/workflows/detekt.yml @@ -17,7 +17,7 @@ jobs: security-events: write # for github/codeql-action/upload-sarif to upload SARIF results steps: - name: Checkout Repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Java uses: actions/setup-java@v3 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index f999071c9..5f600e802 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -10,7 +10,7 @@ jobs: permissions: contents: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: '3.x' diff --git a/.github/workflows/upgrade.yml b/.github/workflows/upgrade.yml index e3a736523..d06d0d4d4 100644 --- a/.github/workflows/upgrade.yml +++ b/.github/workflows/upgrade.yml @@ -19,7 +19,7 @@ jobs: continue-on-error: true steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Java ${{ matrix.version }} uses: actions/setup-java@v3 with: From 9c7e85d66dc4f33f82aa6c4a1eb9b1fe2dc95b7b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 07:06:04 +0000 Subject: [PATCH 048/121] Update dokka to v1.9.0 (#760) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 357445188..dca3478ae 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ koin = "3.4.3" koin-test = "3.4.3" detekt = "1.23.1" spotless = "6.21.0" -dokka = "1.8.20" +dokka = "1.9.0" [libraries] From d4daf6ce6bafaac4db87fb4a3917d4412ddf9352 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 13 Sep 2023 14:57:44 +0200 Subject: [PATCH 049/121] Update dependency io.insert-koin:koin-core to v3.5.0 (#764) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dca3478ae..e9fc29a0e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] kotlin = "1.9.10" cpg = "7.1.2" -koin = "3.4.3" +koin = "3.5.0" koin-test = "3.4.3" detekt = "1.23.1" spotless = "6.21.0" From 38ce7010864d9c985a17cbd497edbbe57ffca569 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 13 Sep 2023 15:06:52 +0200 Subject: [PATCH 050/121] Update koin.test to v3.5.0 (#765) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e9fc29a0e..0bd2ea775 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ kotlin = "1.9.10" cpg = "7.1.2" koin = "3.5.0" -koin-test = "3.4.3" +koin-test = "3.5.0" detekt = "1.23.1" spotless = "6.21.0" dokka = "1.9.0" From d42cdcf616fbac7814308d788eccd7d211c5e92d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 27 Sep 2023 18:50:39 +0200 Subject: [PATCH 051/121] Update dependency io.mockk:mockk to v1.13.8 (#767) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0bd2ea775..c29b401b4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -42,7 +42,7 @@ detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", # test junit-params = { module = "org.junit.jupiter:junit-jupiter-params", version = "5.10.0"} -mockk = { module = "io.mockk:mockk", version = "1.13.7"} +mockk = { module = "io.mockk:mockk", version = "1.13.8"} # this is necessary for the plugins to be used in the buildSrc folder kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } From 1292ca762b59985eb72b06a6d5ccfd0333e0acb7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 27 Sep 2023 16:54:49 +0000 Subject: [PATCH 052/121] Update dependency io.github.detekt.sarif4k:sarif4k to v0.5.0 (#768) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c29b401b4..bd3521eec 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,7 +9,7 @@ dokka = "1.9.0" [libraries] -sarif4k = { module = "io.github.detekt.sarif4k:sarif4k", version = "0.4.0"} # The code can be found here: https://github.com/detekt/sarif4k. It was generated using https://app.quicktype.io/ +sarif4k = { module = "io.github.detekt.sarif4k:sarif4k", version = "0.5.0"} # The code can be found here: https://github.com/detekt/sarif4k. It was generated using https://app.quicktype.io/ kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.6.0"} kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin"} From a1e76b4766fff1ee7c0e38b3fabe44f7dabdcabc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 4 Oct 2023 11:06:16 +0200 Subject: [PATCH 053/121] Update dependency com.diffplug.spotless:spotless-plugin-gradle to v6.22.0 (#770) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bd3521eec..9f449e4ca 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ cpg = "7.1.2" koin = "3.5.0" koin-test = "3.5.0" detekt = "1.23.1" -spotless = "6.21.0" +spotless = "6.22.0" dokka = "1.9.0" From 4fc0bec295552330debe5ad63fc0a23cf37762fb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 4 Oct 2023 09:09:04 +0000 Subject: [PATCH 054/121] Update dependency com.github.ajalt.clikt:clikt to v4.2.1 (#772) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9f449e4ca..3746da674 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,7 +30,7 @@ cpg-language-java = { module = "de.fraunhofer.aisec:cpg-language-java", version. kotlin-logging = { module = "io.github.microutils:kotlin-logging-jvm", version = "3.0.5"} log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j18-impl", version = "2.18.0"} -clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.2.0"} +clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.2.1"} koin = { module = "io.insert-koin:koin-core", version.ref = "koin"} koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin-test"} koin-junit5 = { module = "io.insert-koin:koin-test-junit5", version.ref = "koin-test"} From 3cc3f96581e4c752d6c55fff91625f2b38e53ed6 Mon Sep 17 00:00:00 2001 From: Florian Wendland Date: Wed, 4 Oct 2023 18:07:53 +0200 Subject: [PATCH 055/121] Transition to new group id for `kotlin-logging` (#769) The group id has changed from `io.github.microutils` to `io.github.oshai`. At the same time the root package changed from `mu` to `io.github.oshai.kotlinlogging`. --- .../de/fraunhofer/aisec/codyze/backends/cpg/CPGConfiguration.kt | 2 +- .../aisec/codyze/backends/cpg/coko/evaluators/OrderEvaluator.kt | 2 +- .../backends/cpg/coko/ordering/CodyzeDfaOrderEvaluator.kt | 2 +- .../fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt | 2 +- .../src/main/kotlin/de/fraunhofer/aisec/codyze/cli/Main.kt | 2 +- .../kotlin/de/fraunhofer/aisec/codyze/core/VersionProvider.kt | 2 +- .../de/fraunhofer/aisec/codyze/core/config/Configuration.kt | 2 +- .../codyze/specificationLanguages/coko/dsl/CokoConfiguration.kt | 2 +- .../aisec/codyze/specificationLanguages/coko/dsl/CokoScript.kt | 2 +- .../specificationLanguages/coko/dsl/cli/CokoOptionGroup.kt | 2 +- .../codyze/specificationLanguages/coko/dsl/host/CokoExecutor.kt | 2 +- .../specificationLanguages/coko/dsl/host/SpecEvaluator.kt | 2 +- gradle/libs.versions.toml | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGConfiguration.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGConfiguration.kt index d87e31f38..2621ab588 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGConfiguration.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGConfiguration.kt @@ -17,7 +17,7 @@ package de.fraunhofer.aisec.codyze.backends.cpg import de.fraunhofer.aisec.codyze.core.backend.BackendConfiguration import de.fraunhofer.aisec.cpg.passes.Pass -import mu.KotlinLogging +import io.github.oshai.kotlinlogging.KotlinLogging import java.nio.file.Path import kotlin.reflect.KClass diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OrderEvaluator.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OrderEvaluator.kt index 0a7f7a1e7..1461ed47f 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OrderEvaluator.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OrderEvaluator.kt @@ -31,7 +31,7 @@ import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.ordering.* import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration import de.fraunhofer.aisec.cpg.graph.statements.expressions.DeclaredReferenceExpression -import mu.KotlinLogging +import io.github.oshai.kotlinlogging.KotlinLogging import kotlin.reflect.full.createType import kotlin.reflect.full.declaredMemberFunctions diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/ordering/CodyzeDfaOrderEvaluator.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/ordering/CodyzeDfaOrderEvaluator.kt index 3eb2ff2b4..96869b2a5 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/ordering/CodyzeDfaOrderEvaluator.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/ordering/CodyzeDfaOrderEvaluator.kt @@ -23,7 +23,7 @@ import de.fraunhofer.aisec.cpg.analysis.fsm.DFA import de.fraunhofer.aisec.cpg.analysis.fsm.DFAOrderEvaluator import de.fraunhofer.aisec.cpg.analysis.fsm.Edge import de.fraunhofer.aisec.cpg.graph.Node -import mu.KotlinLogging +import io.github.oshai.kotlinlogging.KotlinLogging import kotlin.reflect.full.findAnnotation val logger = KotlinLogging.logger {} diff --git a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt index c8fe38d49..0f3fa74df 100644 --- a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt +++ b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt @@ -24,7 +24,7 @@ import de.fraunhofer.aisec.cpg.passes.CallResolver import de.fraunhofer.aisec.cpg.passes.EdgeCachePass import de.fraunhofer.aisec.cpg.passes.FilenameMapper import de.fraunhofer.aisec.cpg.passes.Pass -import mu.KotlinLogging +import io.github.oshai.kotlinlogging.KotlinLogging import org.junit.jupiter.api.* import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest diff --git a/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/Main.kt b/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/Main.kt index b360cdcf4..10a4689d3 100644 --- a/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/Main.kt +++ b/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/Main.kt @@ -18,7 +18,7 @@ package de.fraunhofer.aisec.codyze.cli import com.github.ajalt.clikt.core.subcommands import de.fraunhofer.aisec.codyze.core.backend.BackendCommand import de.fraunhofer.aisec.codyze.core.executor.ExecutorCommand -import mu.KotlinLogging +import io.github.oshai.kotlinlogging.KotlinLogging import org.koin.core.context.startKoin import org.koin.java.KoinJavaComponent.getKoin import java.nio.file.Path diff --git a/codyze-core/src/main/kotlin/de/fraunhofer/aisec/codyze/core/VersionProvider.kt b/codyze-core/src/main/kotlin/de/fraunhofer/aisec/codyze/core/VersionProvider.kt index c39ea335e..95d1879c7 100644 --- a/codyze-core/src/main/kotlin/de/fraunhofer/aisec/codyze/core/VersionProvider.kt +++ b/codyze-core/src/main/kotlin/de/fraunhofer/aisec/codyze/core/VersionProvider.kt @@ -15,7 +15,7 @@ */ package de.fraunhofer.aisec.codyze.core -import mu.KotlinLogging +import io.github.oshai.kotlinlogging.KotlinLogging import java.util.* private val logger = KotlinLogging.logger {} diff --git a/codyze-core/src/main/kotlin/de/fraunhofer/aisec/codyze/core/config/Configuration.kt b/codyze-core/src/main/kotlin/de/fraunhofer/aisec/codyze/core/config/Configuration.kt index 94db7c276..78b0cd2b0 100644 --- a/codyze-core/src/main/kotlin/de/fraunhofer/aisec/codyze/core/config/Configuration.kt +++ b/codyze-core/src/main/kotlin/de/fraunhofer/aisec/codyze/core/config/Configuration.kt @@ -16,7 +16,7 @@ package de.fraunhofer.aisec.codyze.core.config import de.fraunhofer.aisec.codyze.core.output.OutputBuilder -import mu.KotlinLogging +import io.github.oshai.kotlinlogging.KotlinLogging import java.nio.file.Path private val logger = KotlinLogging.logger {} diff --git a/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoConfiguration.kt b/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoConfiguration.kt index 777c89217..6f17877d7 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoConfiguration.kt +++ b/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoConfiguration.kt @@ -17,7 +17,7 @@ package de.fraunhofer.aisec.codyze.specificationLanguages.coko.dsl import de.fraunhofer.aisec.codyze.core.executor.ExecutorConfiguration import de.fraunhofer.aisec.codyze.specificationLanguages.coko.dsl.cli.validateSpec -import mu.KotlinLogging +import io.github.oshai.kotlinlogging.KotlinLogging import java.nio.file.Path private val logger = KotlinLogging.logger { } diff --git a/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoScript.kt b/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoScript.kt index 4422e5b67..6827a0f76 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoScript.kt +++ b/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoScript.kt @@ -17,7 +17,7 @@ package de.fraunhofer.aisec.codyze.specificationLanguages.coko.dsl import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.CokoBackend import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.Import -import mu.KotlinLogging +import io.github.oshai.kotlinlogging.KotlinLogging import java.io.File import kotlin.script.experimental.annotations.KotlinScript import kotlin.script.experimental.api.* diff --git a/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/CokoOptionGroup.kt b/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/CokoOptionGroup.kt index 20aa01b61..cb426b85f 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/CokoOptionGroup.kt +++ b/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/CokoOptionGroup.kt @@ -19,7 +19,7 @@ import com.github.ajalt.clikt.parameters.options.* import com.github.ajalt.clikt.parameters.types.path import de.fraunhofer.aisec.codyze.core.config.resolvePaths import de.fraunhofer.aisec.codyze.core.executor.ExecutorOptions -import mu.KotlinLogging +import io.github.oshai.kotlinlogging.KotlinLogging import java.nio.file.Path private val logger = KotlinLogging.logger {} diff --git a/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/CokoExecutor.kt b/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/CokoExecutor.kt index aea4885a9..4be8c3db2 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/CokoExecutor.kt +++ b/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/CokoExecutor.kt @@ -23,7 +23,7 @@ import de.fraunhofer.aisec.codyze.specificationLanguages.coko.dsl.CokoConfigurat import de.fraunhofer.aisec.codyze.specificationLanguages.coko.dsl.CokoSarifBuilder import de.fraunhofer.aisec.codyze.specificationLanguages.coko.dsl.CokoScript import io.github.detekt.sarif4k.Run -import mu.KotlinLogging +import io.github.oshai.kotlinlogging.KotlinLogging import java.nio.file.Path import kotlin.script.experimental.api.* import kotlin.script.experimental.api.SourceCode diff --git a/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/SpecEvaluator.kt b/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/SpecEvaluator.kt index 48553d8b5..f4559485a 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/SpecEvaluator.kt +++ b/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/host/SpecEvaluator.kt @@ -19,7 +19,7 @@ import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.CokoRule import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.EvaluationContext import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.Finding import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.Rule -import mu.KotlinLogging +import io.github.oshai.kotlinlogging.KotlinLogging import kotlin.reflect.* import kotlin.reflect.full.* diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3746da674..40cef6450 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -28,7 +28,7 @@ cpg-language-java = { module = "de.fraunhofer.aisec:cpg-language-java", version. #cpg-analysis = { module = "com.github.Fraunhofer-AISEC.cpg:cpg-analysis", version.ref = "cpg"} #cpg-language-go = { module = "com.github.Fraunhofer-AISEC.cpg:cpg-language-go", version.ref = "cpg"} -kotlin-logging = { module = "io.github.microutils:kotlin-logging-jvm", version = "3.0.5"} +kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version = "5.1.0" } log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j18-impl", version = "2.18.0"} clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.2.1"} koin = { module = "io.insert-koin:koin-core", version.ref = "koin"} From 3d83c0ff13541ad5638907a63d2d5bc73b70af73 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Oct 2023 08:41:35 +0200 Subject: [PATCH 056/121] Update dependency gradle to v8.4 (#773) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ac72c34e8..3fa8f862f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 0adc8e1a5..1aa94a426 100755 --- a/gradlew +++ b/gradlew @@ -145,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -153,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -202,11 +202,11 @@ fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ From 276a736e5f75baecd9571275c566df1addbd8f63 Mon Sep 17 00:00:00 2001 From: Robert Haimerl Date: Thu, 5 Oct 2023 09:13:00 +0200 Subject: [PATCH 057/121] Update SLF4J2 Binding + Fix File Logging (#771) Co-authored-by: Wendland, Florian --- codyze-cli/src/main/resources/log4j2.xml | 11 +++-------- gradle/libs.versions.toml | 2 +- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/codyze-cli/src/main/resources/log4j2.xml b/codyze-cli/src/main/resources/log4j2.xml index b4f4da7e1..2f5f5d5ec 100644 --- a/codyze-cli/src/main/resources/log4j2.xml +++ b/codyze-cli/src/main/resources/log4j2.xml @@ -12,14 +12,9 @@ - - - - - - - + + - + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 40cef6450..1ebafc134 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,7 +29,7 @@ cpg-language-java = { module = "de.fraunhofer.aisec:cpg-language-java", version. #cpg-language-go = { module = "com.github.Fraunhofer-AISEC.cpg:cpg-language-go", version.ref = "cpg"} kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version = "5.1.0" } -log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j18-impl", version = "2.18.0"} +log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version = "2.20.0"} clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.2.1"} koin = { module = "io.insert-koin:koin-core", version.ref = "koin"} koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin-test"} From cbcf60b5d78b7dddb4b764293b09dd8633a98a0b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 18 Oct 2023 10:37:16 +0200 Subject: [PATCH 058/121] Update dependency org.apache.logging.log4j:log4j-slf4j2-impl to v2.21.0 (#774) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1ebafc134..ce72ec248 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,7 +29,7 @@ cpg-language-java = { module = "de.fraunhofer.aisec:cpg-language-java", version. #cpg-language-go = { module = "com.github.Fraunhofer-AISEC.cpg:cpg-language-go", version.ref = "cpg"} kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version = "5.1.0" } -log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version = "2.20.0"} +log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version = "2.21.0"} clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.2.1"} koin = { module = "io.insert-koin:koin-core", version.ref = "koin"} koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin-test"} From b11ce7bacea13e3683c4b41ae8ffbb1057495955 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 18 Oct 2023 08:40:55 +0000 Subject: [PATCH 059/121] Update dokka to v1.9.10 (#775) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ce72ec248..709aada4a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ koin = "3.5.0" koin-test = "3.5.0" detekt = "1.23.1" spotless = "6.22.0" -dokka = "1.9.0" +dokka = "1.9.10" [libraries] From f98085e95a3eca9925c51e49ebbee8bd4737c6d5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 26 Oct 2023 09:10:54 +0200 Subject: [PATCH 060/121] Update dependency org.apache.logging.log4j:log4j-slf4j2-impl to v2.21.1 (#776) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 709aada4a..55fc7975a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,7 +29,7 @@ cpg-language-java = { module = "de.fraunhofer.aisec:cpg-language-java", version. #cpg-language-go = { module = "com.github.Fraunhofer-AISEC.cpg:cpg-language-go", version.ref = "cpg"} kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version = "5.1.0" } -log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version = "2.21.0"} +log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version = "2.21.1"} clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.2.1"} koin = { module = "io.insert-koin:koin-core", version.ref = "koin"} koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin-test"} From c14043d3b71fcca3ae79b5673687486425bd65d0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 Nov 2023 12:31:38 +0100 Subject: [PATCH 061/121] Update eclipse-temurin Docker tag to v17.0.9_9-jre (#777) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 7374e3039..f7bdd3215 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM eclipse-temurin:17.0.8_7-jre +FROM eclipse-temurin:17.0.9_9-jre LABEL org.opencontainers.image.authors="Fraunhofer AISEC " From a1ccf16a98a161ad6ac4886080c4e0dc90851b76 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 Nov 2023 12:39:45 +0100 Subject: [PATCH 062/121] Update kotlin monorepo to v1.9.20 (#778) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 55fc7975a..aecc66132 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "1.9.10" +kotlin = "1.9.20" cpg = "7.1.2" koin = "3.5.0" koin-test = "3.5.0" From 85ecf810361121d2562b9706824dfa5976eebd3d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 14:26:47 +0100 Subject: [PATCH 063/121] Update detekt to v1.23.3 (#779) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index aecc66132..f3a8098b1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ kotlin = "1.9.20" cpg = "7.1.2" koin = "3.5.0" koin-test = "3.5.0" -detekt = "1.23.1" +detekt = "1.23.3" spotless = "6.22.0" dokka = "1.9.10" From 3a4c382bcc39c32ebe20dc83111be8716783f90e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 7 Nov 2023 11:37:31 +0100 Subject: [PATCH 064/121] Update dependency org.junit.jupiter:junit-jupiter-params to v5.10.1 (#780) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f3a8098b1..1fe81fc31 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -41,7 +41,7 @@ kotlin-scripting-dependencies = { module = "org.jetbrains.kotlin:kotlin-scriptin detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt"} # test -junit-params = { module = "org.junit.jupiter:junit-jupiter-params", version = "5.10.0"} +junit-params = { module = "org.junit.jupiter:junit-jupiter-params", version = "5.10.1"} mockk = { module = "io.mockk:mockk", version = "1.13.8"} # this is necessary for the plugins to be used in the buildSrc folder From 22226fb3e835907f8b2a848e4f7ab28e696d5176 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 20 Nov 2023 08:41:10 +0100 Subject: [PATCH 065/121] Update dependency org.jetbrains.kotlinx:kotlinx-serialization-json to v1.6.1 (#781) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1fe81fc31..f9599e5eb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ dokka = "1.9.10" [libraries] sarif4k = { module = "io.github.detekt.sarif4k:sarif4k", version = "0.5.0"} # The code can be found here: https://github.com/detekt/sarif4k. It was generated using https://app.quicktype.io/ -kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.6.0"} +kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.6.1"} kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin"} # CPG official releases From 79cd18d7fb3c303720dcebd0040cc302afc58d50 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 10:53:46 +0100 Subject: [PATCH 066/121] Update dependency org.apache.logging.log4j:log4j-slf4j2-impl to v2.22.0 (#782) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f9599e5eb..bae07d843 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,7 +29,7 @@ cpg-language-java = { module = "de.fraunhofer.aisec:cpg-language-java", version. #cpg-language-go = { module = "com.github.Fraunhofer-AISEC.cpg:cpg-language-go", version.ref = "cpg"} kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version = "5.1.0" } -log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version = "2.21.1"} +log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version = "2.22.0"} clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.2.1"} koin = { module = "io.insert-koin:koin-core", version.ref = "koin"} koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin-test"} From 7414177edee53c4f02db8346fd579d5f81c26805 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 13:13:18 +0100 Subject: [PATCH 067/121] Update kotlin monorepo to v1.9.21 (#783) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bae07d843..d72bbe08e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "1.9.20" +kotlin = "1.9.21" cpg = "7.1.2" koin = "3.5.0" koin-test = "3.5.0" From 4b37e35f55e8b7c789ab4d680c6e4eeae173f527 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 13:32:01 +0100 Subject: [PATCH 068/121] Update detekt to v1.23.4 (#784) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d72bbe08e..895d136c4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ kotlin = "1.9.21" cpg = "7.1.2" koin = "3.5.0" koin-test = "3.5.0" -detekt = "1.23.3" +detekt = "1.23.4" spotless = "6.22.0" dokka = "1.9.10" From ded4231ab86bb280f25e5fda4f05098ae9738593 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 15:11:40 +0100 Subject: [PATCH 069/121] Update dependency com.diffplug.spotless:spotless-plugin-gradle to v6.23.0 (#785) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 895d136c4..b97f2d274 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ cpg = "7.1.2" koin = "3.5.0" koin-test = "3.5.0" detekt = "1.23.4" -spotless = "6.22.0" +spotless = "6.23.0" dokka = "1.9.10" From c9f40421986a0331fd85680a9debebf7c6e04299 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Nov 2023 14:13:54 +0000 Subject: [PATCH 070/121] Update dependency io.github.oshai:kotlin-logging-jvm to v5.1.1 (#786) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b97f2d274..ed395fb51 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -28,7 +28,7 @@ cpg-language-java = { module = "de.fraunhofer.aisec:cpg-language-java", version. #cpg-analysis = { module = "com.github.Fraunhofer-AISEC.cpg:cpg-analysis", version.ref = "cpg"} #cpg-language-go = { module = "com.github.Fraunhofer-AISEC.cpg:cpg-language-go", version.ref = "cpg"} -kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version = "5.1.0" } +kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version = "5.1.1" } log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version = "2.22.0"} clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.2.1"} koin = { module = "io.insert-koin:koin-core", version.ref = "koin"} From 6f6829637da8bcfffb1263f0eabbb3caeca3db71 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 Nov 2023 10:28:11 +0100 Subject: [PATCH 071/121] Update dependency gradle to v8.5 (#787) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.jar | Bin 63721 -> 43462 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7f93135c49b765f8051ef9d0a6055ff8e46073d8..d64cd4917707c1f8861d8cb53dd15194d4248596 100644 GIT binary patch literal 43462 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 literal 63721 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鯊%1Et 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⁢ zv5o9#E7!G0XmdzfsS{efPNi`1b44~SZ4Z8fuX!I}#8g+(wxzQwUT#Xb2(tbY1+EUhGKoT@KEU9Ktl>_0 z%bjDJg;#*gtJZv!-Zs`?^}v5eKmnbjqlvnSzE@_SP|LG_PJ6CYU+6zY6>92%E+ z=j@TZf-iW4(%U{lnYxQA;7Q!b;^brF8n0D>)`q5>|WDDXLrqYU_tKN2>=#@~OE7grMnNh?UOz-O~6 z6%rHy{#h9K0AT+lDC7q4{hw^|q6*Ry;;L%Q@)Ga}$60_q%D)rv(CtS$CQbpq9|y1e zRSrN4;$Jyl{m5bZw`$8TGvb}(LpY{-cQ)fcyJv7l3S52TLXVDsphtv&aPuDk1OzCA z4A^QtC(!11`IsNx_HnSy?>EKpHJWT^wmS~hc^p^zIIh@9f6U@I2 zC=Mve{j2^)mS#U$e{@Q?SO6%LDsXz@SY+=cK_QMmXBIU)j!$ajc-zLx3V60EXJ!qC zi<%2x8Q24YN+&8U@CIlN zrZkcT9yh%LrlGS9`G)KdP(@9Eo-AQz@8GEFWcb7U=a0H^ZVbLmz{+&M7W(nXJ4sN8 zJLR7eeK(K8`2-}j(T7JsO`L!+CvbueT%izanm-^A1Dn{`1Nw`9P?cq;7no+XfC`K(GO9?O^5zNIt4M+M8LM0=7Gz8UA@Z0N+lg+cX)NfazRu z5D)~HA^(u%w^cz+@2@_#S|u>GpB+j4KzQ^&Wcl9f z&hG#bCA(Yk0D&t&aJE^xME^&E-&xGHhXn%}psEIj641H+Nl-}boj;)Zt*t(4wZ5DN z@GXF$bL=&pBq-#vkTkh>7hl%K5|3 z{`Vn9b$iR-SoGENp}bn4;fR3>9sA%X2@1L3aE9yTra;Wb#_`xWwLSLdfu+PAu+o3| zGVnpzPr=ch{uuoHjtw7+_!L_2;knQ!DuDl0R`|%jr+}jFzXtrHIKc323?JO{l&;VF z*L1+}JU7%QJOg|5|Tc|D8fN zJORAg=_vsy{ak|o);@)Yh8Lkcg@$FG3k@ep36BRa^>~UmnRPziS>Z=`Jb2x*Q#`%A zU*i3&Vg?TluO@X0O;r2Jl6LKLUOVhSqg1*qOt^|8*c7 zo(298@+r$k_wQNGHv{|$tW(T8L+4_`FQ{kEW5Jgg{yf7ey4ss_(SNKfz(N9lx&a;< je(UuV8hP?p&}TPdm1I$XmG#(RzlD&B2izSj9sl%y5~4qc diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3fa8f862f..1af9e0930 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 3949959b7d683f15b3d72390ea4607d8dcd71b06 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 Nov 2023 10:31:22 +0100 Subject: [PATCH 072/121] Update actions/setup-java action to v4 (#788) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- .github/workflows/codecov.yml | 2 +- .github/workflows/detekt.yml | 2 +- .github/workflows/upgrade.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0c6f0917f..019dafc8e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ jobs: contents: write steps: - uses: actions/checkout@v4 - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: distribution: "temurin" java-version: 17 diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index bce5e5d56..34bcf449a 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: "temurin" java-version: 17 diff --git a/.github/workflows/detekt.yml b/.github/workflows/detekt.yml index a2f98fea2..4a38e4e9c 100644 --- a/.github/workflows/detekt.yml +++ b/.github/workflows/detekt.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: "temurin" java-version: 17 diff --git a/.github/workflows/upgrade.yml b/.github/workflows/upgrade.yml index d06d0d4d4..621d9efbe 100644 --- a/.github/workflows/upgrade.yml +++ b/.github/workflows/upgrade.yml @@ -21,7 +21,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: Setup Java ${{ matrix.version }} - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: "temurin" java-version: ${{ matrix.version }} From ce777ddb5230d4f4a293e21e5d97f70edac6d75a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 12:12:40 +0100 Subject: [PATCH 073/121] Update dependency com.diffplug.spotless:spotless-plugin-gradle to v6.23.2 (#789) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ed395fb51..35072f16d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ cpg = "7.1.2" koin = "3.5.0" koin-test = "3.5.0" detekt = "1.23.4" -spotless = "6.23.0" +spotless = "6.23.2" dokka = "1.9.10" From 23d3cc1fb5afaabca6841687004684d337e73ecf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 07:43:24 +0100 Subject: [PATCH 074/121] Update dependency org.jetbrains.kotlinx:kotlinx-serialization-json to v1.6.2 (#790) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 35072f16d..40c9988cc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ dokka = "1.9.10" [libraries] sarif4k = { module = "io.github.detekt.sarif4k:sarif4k", version = "0.5.0"} # The code can be found here: https://github.com/detekt/sarif4k. It was generated using https://app.quicktype.io/ -kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.6.1"} +kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.6.2"} kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin"} # CPG official releases From 83729c35c4c9472d352e9eef16b109ef4c2511e0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Dec 2023 09:11:31 +0100 Subject: [PATCH 075/121] Update dependency com.diffplug.spotless:spotless-plugin-gradle to v6.23.3 (#792) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 40c9988cc..9676b9878 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ cpg = "7.1.2" koin = "3.5.0" koin-test = "3.5.0" detekt = "1.23.4" -spotless = "6.23.2" +spotless = "6.23.3" dokka = "1.9.10" From c29019965d54e792b58401552a53c65d260cebbd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Dec 2023 09:21:04 +0100 Subject: [PATCH 076/121] Update actions/setup-python action to v5 (#793) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 5f600e802..f2d1c4551 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -11,7 +11,7 @@ jobs: contents: write steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: '3.x' - uses: actions/cache@v3 From 3ecd677c7b6105d87c71025956cc94fce4b168ab Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 10:33:08 +0100 Subject: [PATCH 077/121] Update github/codeql-action action to v3 (#795) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/detekt.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/detekt.yml b/.github/workflows/detekt.yml index 4a38e4e9c..54558c8c5 100644 --- a/.github/workflows/detekt.yml +++ b/.github/workflows/detekt.yml @@ -33,7 +33,7 @@ jobs: # Make sure we always run this upload task, # because the previous step may fail if there are findings. - name: Upload SARIF to Github using the upload-sarif action - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 if: success() || failure() with: sarif_file: build/reports/detekt/detekt.sarif From d3270d053317aa30799a0a8b7e1cfb5bac0b8ace Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 15 Dec 2023 09:48:26 +0100 Subject: [PATCH 078/121] Update actions/upload-artifact action to v4 (#796) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/codecov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 34bcf449a..d7c5b3b4f 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -24,7 +24,7 @@ jobs: with: arguments: testCodeCoverageReport --continue - name: Archive test reports - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: test path: ./code-coverage-report/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml From e8390dcdbfb53a2f161a575ff5325de738b99ab3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 10:13:16 +0100 Subject: [PATCH 079/121] Update koin.test to v3.5.2 (#800) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9676b9878..4394d806f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ kotlin = "1.9.21" cpg = "7.1.2" koin = "3.5.0" -koin-test = "3.5.0" +koin-test = "3.5.2" detekt = "1.23.4" spotless = "6.23.3" dokka = "1.9.10" From 8da0aad1bd01d324882d1cb7e2cf20e73f4b85d1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 09:15:49 +0000 Subject: [PATCH 080/121] Update dependency io.github.oshai:kotlin-logging-jvm to v6 (#801) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4394d806f..99931c51b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -28,7 +28,7 @@ cpg-language-java = { module = "de.fraunhofer.aisec:cpg-language-java", version. #cpg-analysis = { module = "com.github.Fraunhofer-AISEC.cpg:cpg-analysis", version.ref = "cpg"} #cpg-language-go = { module = "com.github.Fraunhofer-AISEC.cpg:cpg-language-go", version.ref = "cpg"} -kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version = "5.1.1" } +kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version = "6.0.1" } log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version = "2.22.0"} clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.2.1"} koin = { module = "io.insert-koin:koin-core", version.ref = "koin"} From 7520d1d79e4f871ce3ae3333ba831b322729c5bd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 21 Dec 2023 09:16:38 +0100 Subject: [PATCH 081/121] Update koin.test to v3.5.3 (#802) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 99931c51b..3cb500d00 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ kotlin = "1.9.21" cpg = "7.1.2" koin = "3.5.0" -koin-test = "3.5.2" +koin-test = "3.5.3" detekt = "1.23.4" spotless = "6.23.3" dokka = "1.9.10" From 87eb7e9b375234f30e210ecc29eda335818bda0c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 09:01:31 +0100 Subject: [PATCH 082/121] Update dependency org.jetbrains.kotlin:kotlin-reflect to v1.9.22 (#803) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3cb500d00..29a29dfbe 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "1.9.21" +kotlin = "1.9.22" cpg = "7.1.2" koin = "3.5.0" koin-test = "3.5.3" From af3bc8ad7668e8e0e4a7693c8ba10c06aeb70945 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 09:43:16 +0100 Subject: [PATCH 083/121] Update dependency io.insert-koin:koin-core to v3.5.3 (#799) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 29a29dfbe..e2634b626 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] kotlin = "1.9.22" cpg = "7.1.2" -koin = "3.5.0" +koin = "3.5.3" koin-test = "3.5.3" detekt = "1.23.4" spotless = "6.23.3" From fea26304b2b8aa2a6bc5c5f411b71667c8365fd6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 12:08:57 +0100 Subject: [PATCH 084/121] Update dependency org.apache.logging.log4j:log4j-slf4j2-impl to v2.22.1 (#805) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e2634b626..1aa64e909 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,7 +29,7 @@ cpg-language-java = { module = "de.fraunhofer.aisec:cpg-language-java", version. #cpg-language-go = { module = "com.github.Fraunhofer-AISEC.cpg:cpg-language-go", version.ref = "cpg"} kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version = "6.0.1" } -log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version = "2.22.0"} +log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version = "2.22.1"} clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.2.1"} koin = { module = "io.insert-koin:koin-core", version.ref = "koin"} koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin-test"} From c92b4ba202ebdab54d7d7a98ca2229142bd3b63a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 11:56:19 +0100 Subject: [PATCH 085/121] Update dependency com.github.ajalt.clikt:clikt to v4.2.2 (#806) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1aa64e909..8c1a57200 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,7 +30,7 @@ cpg-language-java = { module = "de.fraunhofer.aisec:cpg-language-java", version. kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version = "6.0.1" } log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version = "2.22.1"} -clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.2.1"} +clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.2.2"} koin = { module = "io.insert-koin:koin-core", version.ref = "koin"} koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin-test"} koin-junit5 = { module = "io.insert-koin:koin-test-junit5", version.ref = "koin-test"} From 8380d11da7bb07236e599036713767046aa0177c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 10:58:28 +0000 Subject: [PATCH 086/121] Update dependency io.mockk:mockk to v1.13.9 (#807) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8c1a57200..3825744bc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -42,7 +42,7 @@ detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", # test junit-params = { module = "org.junit.jupiter:junit-jupiter-params", version = "5.10.1"} -mockk = { module = "io.mockk:mockk", version = "1.13.8"} +mockk = { module = "io.mockk:mockk", version = "1.13.9"} # this is necessary for the plugins to be used in the buildSrc folder kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } From 610331a3f242924937e8f1dc456226b6c932f5db Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 08:23:23 +0100 Subject: [PATCH 087/121] Update dependency io.github.oshai:kotlin-logging-jvm to v6.0.2 (#808) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3825744bc..234579c89 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -28,7 +28,7 @@ cpg-language-java = { module = "de.fraunhofer.aisec:cpg-language-java", version. #cpg-analysis = { module = "com.github.Fraunhofer-AISEC.cpg:cpg-analysis", version.ref = "cpg"} #cpg-language-go = { module = "com.github.Fraunhofer-AISEC.cpg:cpg-language-go", version.ref = "cpg"} -kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version = "6.0.1" } +kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version = "6.0.2" } log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version = "2.22.1"} clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.2.2"} koin = { module = "io.insert-koin:koin-core", version.ref = "koin"} From 0ac4e48ec69582b2a07b665fb4a4adb8f444efdd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 11:55:50 +0100 Subject: [PATCH 088/121] Update dependency io.github.oshai:kotlin-logging-jvm to v6.0.3 (#810) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 234579c89..4df344686 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -28,7 +28,7 @@ cpg-language-java = { module = "de.fraunhofer.aisec:cpg-language-java", version. #cpg-analysis = { module = "com.github.Fraunhofer-AISEC.cpg:cpg-analysis", version.ref = "cpg"} #cpg-language-go = { module = "com.github.Fraunhofer-AISEC.cpg:cpg-language-go", version.ref = "cpg"} -kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version = "6.0.2" } +kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version = "6.0.3" } log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version = "2.22.1"} clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.2.2"} koin = { module = "io.insert-koin:koin-core", version.ref = "koin"} From 65aea408b95d34e7d0b307edecd5e7e740db6e72 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 11:58:33 +0100 Subject: [PATCH 089/121] Update dependency com.diffplug.spotless:spotless-plugin-gradle to v6.24.0 (#809) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4df344686..095b0f15e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ cpg = "7.1.2" koin = "3.5.3" koin-test = "3.5.3" detekt = "1.23.4" -spotless = "6.23.3" +spotless = "6.24.0" dokka = "1.9.10" From ee08c1633bf8e98ede6a8dc793da54aa81ca2226 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Jan 2024 12:43:36 +0100 Subject: [PATCH 090/121] Update actions/cache action to v4 (#811) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index f2d1c4551..94d3b6136 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/setup-python@v5 with: python-version: '3.x' - - uses: actions/cache@v3 + - uses: actions/cache@v4 with: key: ${{ github.ref }} path: .cache From 8bcbe4f78dbe2ac174be25edec0bf1391096f1bc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Jan 2024 16:33:00 +0100 Subject: [PATCH 091/121] Update dependency com.diffplug.spotless:spotless-plugin-gradle to v6.25.0 (#812) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 095b0f15e..a01f324c1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ cpg = "7.1.2" koin = "3.5.3" koin-test = "3.5.3" detekt = "1.23.4" -spotless = "6.24.0" +spotless = "6.25.0" dokka = "1.9.10" From 9f3bc2ce4093bdca83bdfc294149bc201ed1aa32 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 09:07:35 +0100 Subject: [PATCH 092/121] Update dependency io.gitlab.arturbosch.detekt:detekt-formatting to v1.23.5 (#817) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a01f324c1..3b1ea882d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ kotlin = "1.9.22" cpg = "7.1.2" koin = "3.5.3" koin-test = "3.5.3" -detekt = "1.23.4" +detekt = "1.23.5" spotless = "6.25.0" dokka = "1.9.10" From 6a7aec38de4d0c33da525d1c29c81d09cb68d39d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 09:49:11 +0100 Subject: [PATCH 093/121] Update codecov/codecov-action action to v4 (#816) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/codecov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index d7c5b3b4f..fecb22bd6 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -29,7 +29,7 @@ jobs: name: test path: ./code-coverage-report/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} files: ./code-coverage-report/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml From 7ab767f84b2b5d71dad050f63367f065b296a67b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 09:56:01 +0100 Subject: [PATCH 094/121] Update eclipse-temurin Docker tag to v17.0.10_7-jre (#813) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f7bdd3215..2a62979d7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM eclipse-temurin:17.0.9_9-jre +FROM eclipse-temurin:17.0.10_7-jre LABEL org.opencontainers.image.authors="Fraunhofer AISEC " From b400ca0f8146d2f5ce9a9903c837533ed544305f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 10:03:18 +0100 Subject: [PATCH 095/121] Update gradle/gradle-build-action action to v3 (#814) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Wendland, Florian --- .github/workflows/build.yml | 6 +++--- .github/workflows/codecov.yml | 6 +++--- .github/workflows/detekt.yml | 6 +++--- .github/workflows/docs.yml | 7 ++++--- .github/workflows/upgrade.yml | 6 +++--- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 019dafc8e..63f38cc53 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -36,10 +36,10 @@ jobs: fi # store version in GitHub environment file echo "version=$VERSION" >> $GITHUB_ENV + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 - name: Build ${{ env.version }} - uses: gradle/gradle-build-action@v2 - with: - arguments: :codyze-cli:build -x check --parallel -Pversion=${{ env.version }} + run: ./gradlew :codyze-cli:build -x check --parallel -Pversion=${{ env.version }} - name: Push Release Docker Image if: startsWith(github.ref, 'refs/tags/v') run: | diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index fecb22bd6..2bea31a01 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -19,10 +19,10 @@ jobs: with: distribution: "temurin" java-version: 17 + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 - name: Generate coverage report - uses: gradle/gradle-build-action@v2 - with: - arguments: testCodeCoverageReport --continue + run: ./gradlew testCodeCoverageReport --continue - name: Archive test reports uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/detekt.yml b/.github/workflows/detekt.yml index 54558c8c5..c574ccedd 100644 --- a/.github/workflows/detekt.yml +++ b/.github/workflows/detekt.yml @@ -25,10 +25,10 @@ jobs: distribution: "temurin" java-version: 17 + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 - name: Run analysis - uses: gradle/gradle-build-action@v2 - with: - arguments: detektMain detektTest --continue + run: ./gradlew detektMain detektTest --continue # Make sure we always run this upload task, # because the previous step may fail if there are findings. diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 94d3b6136..21e2016bd 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -22,10 +22,11 @@ jobs: run: | python -m pip install mkdocs-material python -m pip install -r ./docs/mkdocs-material-plugins.txt + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 - name: 'Build API pages' - uses: gradle/gradle-build-action@v2 - with: - arguments: | + run: | + ./gradlew dokkaHtmlMultiModule -Dorg.gradle.internal.repository.max.tentatives=5 -Dorg.gradle.internal.repository.initial.backoff=10000 diff --git a/.github/workflows/upgrade.yml b/.github/workflows/upgrade.yml index 621d9efbe..9ca8ba62e 100644 --- a/.github/workflows/upgrade.yml +++ b/.github/workflows/upgrade.yml @@ -25,9 +25,9 @@ jobs: with: distribution: "temurin" java-version: ${{ matrix.version }} + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 - name: Build - uses: gradle/gradle-build-action@v2 + run: ./gradlew :codyze-cli:build -x check --parallel -Pversion=0.0.0 # step-level 'continue-on-error' needed to mask a negative workflow result continue-on-error: true - with: - arguments: :codyze-cli:build -x check --parallel -Pversion=0.0.0 \ No newline at end of file From 125d8528f980324a72e119f37eec6ce6ef02f29e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 08:34:50 +0100 Subject: [PATCH 096/121] Update dependency gradle to v8.6 (#818) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1af9e0930..a80b22ce5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From b0355dec8eca7af88746ba8270fa8310fd9f7074 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 08:28:17 +0100 Subject: [PATCH 097/121] Update dependency org.junit.jupiter:junit-jupiter-params to v5.10.2 (#819) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3b1ea882d..8d8254d32 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -41,7 +41,7 @@ kotlin-scripting-dependencies = { module = "org.jetbrains.kotlin:kotlin-scriptin detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt"} # test -junit-params = { module = "org.junit.jupiter:junit-jupiter-params", version = "5.10.1"} +junit-params = { module = "org.junit.jupiter:junit-jupiter-params", version = "5.10.2"} mockk = { module = "io.mockk:mockk", version = "1.13.9"} # this is necessary for the plugins to be used in the buildSrc folder From 7afc168b725850084f775f08a6b1a71867917da6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 19 Feb 2024 08:47:10 +0100 Subject: [PATCH 098/121] Update dependency org.jetbrains.kotlinx:kotlinx-serialization-json to v1.6.3 (#821) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8d8254d32..e89c8f6be 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ dokka = "1.9.10" [libraries] sarif4k = { module = "io.github.detekt.sarif4k:sarif4k", version = "0.5.0"} # The code can be found here: https://github.com/detekt/sarif4k. It was generated using https://app.quicktype.io/ -kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.6.2"} +kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.6.3"} kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin"} # CPG official releases From 44703fe85c45bb2e5f7d7f618c7e1d26d7c19273 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 23 Feb 2024 10:49:37 +0100 Subject: [PATCH 099/121] Update dependency org.apache.logging.log4j:log4j-slf4j2-impl to v2.23.0 (#822) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e89c8f6be..ace379a95 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,7 +29,7 @@ cpg-language-java = { module = "de.fraunhofer.aisec:cpg-language-java", version. #cpg-language-go = { module = "com.github.Fraunhofer-AISEC.cpg:cpg-language-go", version.ref = "cpg"} kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version = "6.0.3" } -log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version = "2.22.1"} +log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version = "2.23.0"} clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.2.2"} koin = { module = "io.insert-koin:koin-core", version.ref = "koin"} koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin-test"} From c11b90c5d0c91a1ba29602907a02a3e0f1ecaca2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 13:54:21 +0100 Subject: [PATCH 100/121] Update dependency io.mockk:mockk to v1.13.10 (#823) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ace379a95..60a9e3f5e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -42,7 +42,7 @@ detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", # test junit-params = { module = "org.junit.jupiter:junit-jupiter-params", version = "5.10.2"} -mockk = { module = "io.mockk:mockk", version = "1.13.9"} +mockk = { module = "io.mockk:mockk", version = "1.13.10"} # this is necessary for the plugins to be used in the buildSrc folder kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } From a280b8005404b44e82b6629a33218c27244947d2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 08:28:09 +0100 Subject: [PATCH 101/121] Update dokka to v1.9.20 (#826) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 60a9e3f5e..718bdd711 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ koin = "3.5.3" koin-test = "3.5.3" detekt = "1.23.5" spotless = "6.25.0" -dokka = "1.9.10" +dokka = "1.9.20" [libraries] From 5c727c2678d0caa6b85e47d248c99c06a9fb3230 Mon Sep 17 00:00:00 2001 From: Florian Wendland Date: Wed, 6 Mar 2024 13:47:38 +0100 Subject: [PATCH 102/121] Fix run command for docs workflow (#820) --- .github/workflows/docs.yml | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 21e2016bd..bf2f96fbc 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -11,24 +11,26 @@ jobs: contents: write steps: - uses: actions/checkout@v4 + # Ensure Java installation to run Gradle + - uses: actions/setup-java@v4 + with: + distribution: "temurin" + java-version: 17 + - name: 'Setup Gradle' + uses: gradle/actions/setup-gradle@v3 + - name: 'Build API pages' + run: | + ./gradlew dokkaHtmlMultiModule \ + -Dorg.gradle.internal.repository.max.tentatives=5 \ + -Dorg.gradle.internal.repository.initial.backoff=10000 - uses: actions/setup-python@v5 with: python-version: '3.x' - - uses: actions/cache@v4 - with: - key: ${{ github.ref }} - path: .cache + cache: 'pip' + cache-dependency-path: './docs/mkdocs-material-plugins.txt' - name: 'Install mkdocs-material and plugins' run: | python -m pip install mkdocs-material python -m pip install -r ./docs/mkdocs-material-plugins.txt - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 - - name: 'Build API pages' - run: | - ./gradlew - dokkaHtmlMultiModule - -Dorg.gradle.internal.repository.max.tentatives=5 - -Dorg.gradle.internal.repository.initial.backoff=10000 - name: 'Build & deploy docs' run: mkdocs gh-deploy --force From f04dcc3bd1d0b87b068bdf232e479a69a349c916 Mon Sep 17 00:00:00 2001 From: Florian Wendland Date: Wed, 6 Mar 2024 15:25:08 +0100 Subject: [PATCH 103/121] Fix deprecated Gradle features (#825) --- build.gradle.kts | 4 ++-- buildSrc/src/main/kotlin/module.gradle.kts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 6e23712e4..1151e976d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -47,7 +47,7 @@ val projectProps by tasks.registering(WriteProperties::class) { description = "Write project properties in a file." // Set output file to build/project.properties - outputFile = file("$buildDir/codyze.properties") + destinationFile = layout.buildDirectory.file("codyze.properties") // Default encoding is ISO-8559-1, here we change it. encoding = "UTF-8" // Optionally we can specify the header comment. @@ -62,7 +62,7 @@ val projectProps by tasks.registering(WriteProperties::class) { } } -// configure detekt to combine the results of all submodules into a single sarif file -> for github code scanning +// configure detekt to combine the results of all submodules into a single sarif file -> for GitHub code scanning val detektReportMergeSarif by tasks.registering(ReportMergeTask::class) { output.set(rootProject.layout.buildDirectory.file("reports/detekt/detekt.sarif")) } diff --git a/buildSrc/src/main/kotlin/module.gradle.kts b/buildSrc/src/main/kotlin/module.gradle.kts index db8c34f0e..f2d3cd753 100644 --- a/buildSrc/src/main/kotlin/module.gradle.kts +++ b/buildSrc/src/main/kotlin/module.gradle.kts @@ -17,6 +17,7 @@ dependencies { // Unit tests testImplementation(kotlin("test")) testImplementation(libs.junit.params) + testRuntimeOnly("org.junit.platform:junit-platform-launcher") } repositories { From 8b96a3e211d74b7a8f47074bc39dcc443dad19c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Mar 2024 10:38:29 +0000 Subject: [PATCH 104/121] Bump golang.org/x/crypto (#797) Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.0.0-20220315160706-3147a52a75dd to 0.17.0. - [Commits](https://github.com/golang/crypto/commits/v0.17.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../coko/coko-dsl/src/test/resources/golang/go.mod | 2 +- .../coko/coko-dsl/src/test/resources/golang/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/golang/go.mod b/codyze-specification-languages/coko/coko-dsl/src/test/resources/golang/go.mod index ccedbe1fe..16cae6d6a 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/test/resources/golang/go.mod +++ b/codyze-specification-languages/coko/coko-dsl/src/test/resources/golang/go.mod @@ -13,6 +13,6 @@ require ( github.com/jinzhu/now v1.1.5 // indirect github.com/mattn/go-sqlite3 v1.14.14 // indirect github.com/pkg/errors v0.9.1 // indirect - golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect + golang.org/x/crypto v0.17.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect ) diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/golang/go.sum b/codyze-specification-languages/coko/coko-dsl/src/test/resources/golang/go.sum index 905ec12d8..2fbbb2aa1 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/test/resources/golang/go.sum +++ b/codyze-specification-languages/coko/coko-dsl/src/test/resources/golang/go.sum @@ -14,8 +14,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= From 3a66173b74a7faeb9f6f23bddda8932ab97a7f4f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 8 Mar 2024 15:05:48 +0100 Subject: [PATCH 105/121] Update kotlin monorepo to v1.9.23 (#827) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 718bdd711..61197dca1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "1.9.22" +kotlin = "1.9.23" cpg = "7.1.2" koin = "3.5.3" koin-test = "3.5.3" From afa39221d1abcc8565b65c6da138bca4a1798664 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 09:25:37 +0100 Subject: [PATCH 106/121] Update softprops/action-gh-release action to v2 (#829) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 63f38cc53..20f0475d1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,7 +50,7 @@ jobs: docker push ghcr.io/fraunhofer-aisec/codyze:latest - name: Create Release if: startsWith(github.ref, 'refs/tags/v') - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: name: ${{ env.version }} generate_release_notes: true From cac17f33beb0cc8d70cf73b77619c448176aef2c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 09:34:30 +0100 Subject: [PATCH 107/121] Update dependency org.apache.logging.log4j:log4j-slf4j2-impl to v2.23.1 (#830) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Florian Wendland --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 61197dca1..b5914294b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,7 +29,7 @@ cpg-language-java = { module = "de.fraunhofer.aisec:cpg-language-java", version. #cpg-language-go = { module = "com.github.Fraunhofer-AISEC.cpg:cpg-language-go", version.ref = "cpg"} kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version = "6.0.3" } -log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version = "2.23.0"} +log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version = "2.23.1"} clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.2.2"} koin = { module = "io.insert-koin:koin-core", version.ref = "koin"} koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin-test"} From c61e61ee0776b326c0b3503667d8b9b3e94fd2ea Mon Sep 17 00:00:00 2001 From: Florian Wendland Date: Mon, 18 Mar 2024 15:12:20 +0100 Subject: [PATCH 108/121] Fix deprecated logging functions (#824) --- .../cpg/coko/evaluators/OrderEvaluator.kt | 2 +- .../aisec/codyze/core/VersionProvider.kt | 2 +- .../aisec/codyze/core/VersionProviderTest.kt | 56 +++++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 codyze-core/src/test/kotlin/de/fraunhofer/aisec/codyze/core/VersionProviderTest.kt diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OrderEvaluator.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OrderEvaluator.kt index 1461ed47f..45de71d39 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OrderEvaluator.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OrderEvaluator.kt @@ -116,7 +116,7 @@ class OrderEvaluator(val baseNodes: Collection, val order: Order) : Evalua val usedBases = syntaxTree.filterIsInstanceToList().map { it.baseName }.toSet() if (usedBases.size > 1) { - logger.warn("Order statement contains more than one base. Not supported.") + logger.warn { "Order statement contains more than one base. Not supported." } return emptySet() } diff --git a/codyze-core/src/main/kotlin/de/fraunhofer/aisec/codyze/core/VersionProvider.kt b/codyze-core/src/main/kotlin/de/fraunhofer/aisec/codyze/core/VersionProvider.kt index 95d1879c7..09af745a3 100644 --- a/codyze-core/src/main/kotlin/de/fraunhofer/aisec/codyze/core/VersionProvider.kt +++ b/codyze-core/src/main/kotlin/de/fraunhofer/aisec/codyze/core/VersionProvider.kt @@ -37,7 +37,7 @@ object VersionProvider { if ( !props.containsKey("project.name") || props.getProperty("project.name").lowercase() != "codyze" ) { - logger.warn("Could not find correct version properties file") + logger.warn { "Could not find correct version properties file" } props.clear() } } diff --git a/codyze-core/src/test/kotlin/de/fraunhofer/aisec/codyze/core/VersionProviderTest.kt b/codyze-core/src/test/kotlin/de/fraunhofer/aisec/codyze/core/VersionProviderTest.kt new file mode 100644 index 000000000..5af5d0bf0 --- /dev/null +++ b/codyze-core/src/test/kotlin/de/fraunhofer/aisec/codyze/core/VersionProviderTest.kt @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.fraunhofer.aisec.codyze.core + +import org.junit.jupiter.api.Test +import java.io.FileOutputStream +import java.util.Properties +import kotlin.test.assertEquals + +class VersionProviderTest { + + @Test + fun `test initialisation from properties file with incorrect project name`() { + val properties = Properties() + + /* get the property file and load it; we want to fail if we can't find the property file to begin with */ + val propFile = VersionProvider::class.java.classLoader.getResource("codyze.properties")!! + propFile.openStream().use { + properties.load(it) + } + + // change property s.t. internal check fails + val oldValue = properties.setProperty("project.name", "test") as String + FileOutputStream(propFile.file).use { + properties.store(it, null) + } + + // instantiate `VersionProvider` with altered properties -> properties in VersionProvider should now be empty + val vp = VersionProvider + + // check empty properties through reflection + val vpProps = vp.javaClass.getDeclaredField("props") + .also { it.trySetAccessible() } + .let { it.get(vp) as Properties } + assertEquals(vpProps.size, 0) + + // restore original properties file + properties.setProperty("project.name", oldValue) + FileOutputStream(propFile.file).use { + properties.store(it, null) + } + } +} From 7a9410fffeecf9b18cc0b9bfab7a1f1b865d0382 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 25 Mar 2024 08:40:56 +0100 Subject: [PATCH 109/121] Update dependency gradle to v8.7 (#833) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.jar | Bin 43462 -> 43453 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew.bat | 20 ++++++++++---------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d64cd4917707c1f8861d8cb53dd15194d4248596..e6441136f3d4ba8a0da8d277868979cfbc8ad796 100644 GIT binary patch delta 34118 zcmY(qRX`kF)3u#IAjsf0xCD212@LM;?(PINyAue(f;$XO2=4Cg1P$=#e%|lo zKk1`B>Q#GH)wNd-&cJofz}3=WfYndTeo)CyX{fOHsQjGa<{e=jamMNwjdatD={CN3>GNchOE9OGPIqr)3v>RcKWR3Z zF-guIMjE2UF0Wqk1)21791y#}ciBI*bAenY*BMW_)AeSuM5}vz_~`+1i!Lo?XAEq{TlK5-efNFgHr6o zD>^vB&%3ZGEWMS>`?tu!@66|uiDvS5`?bF=gIq3rkK(j<_TybyoaDHg8;Y#`;>tXI z=tXo~e9{U!*hqTe#nZjW4z0mP8A9UUv1}C#R*@yu9G3k;`Me0-BA2&Aw6f`{Ozan2 z8c8Cs#dA-7V)ZwcGKH}jW!Ja&VaUc@mu5a@CObzNot?b{f+~+212lwF;!QKI16FDS zodx>XN$sk9;t;)maB^s6sr^L32EbMV(uvW%or=|0@U6cUkE`_!<=LHLlRGJx@gQI=B(nn z-GEjDE}*8>3U$n(t^(b^C$qSTI;}6q&ypp?-2rGpqg7b}pyT zOARu2x>0HB{&D(d3sp`+}ka+Pca5glh|c=M)Ujn_$ly^X6&u z%Q4Y*LtB_>i6(YR!?{Os-(^J`(70lZ&Hp1I^?t@~SFL1!m0x6j|NM!-JTDk)%Q^R< z@e?23FD&9_W{Bgtr&CG&*Oer3Z(Bu2EbV3T9FeQ|-vo5pwzwQ%g&=zFS7b{n6T2ZQ z*!H(=z<{D9@c`KmHO&DbUIzpg`+r5207}4D=_P$ONIc5lsFgn)UB-oUE#{r+|uHc^hzv_df zV`n8&qry%jXQ33}Bjqcim~BY1?KZ}x453Oh7G@fA(}+m(f$)TY%7n=MeLi{jJ7LMB zt(mE*vFnep?YpkT_&WPV9*f>uSi#n#@STJmV&SLZnlLsWYI@y+Bs=gzcqche=&cBH2WL)dkR!a95*Ri)JH_4c*- zl4pPLl^as5_y&6RDE@@7342DNyF&GLJez#eMJjI}#pZN{Y8io{l*D+|f_Y&RQPia@ zNDL;SBERA|B#cjlNC@VU{2csOvB8$HzU$01Q?y)KEfos>W46VMh>P~oQC8k=26-Ku)@C|n^zDP!hO}Y z_tF}0@*Ds!JMt>?4y|l3?`v#5*oV-=vL7}zehMON^=s1%q+n=^^Z{^mTs7}*->#YL z)x-~SWE{e?YCarwU$=cS>VzmUh?Q&7?#Xrcce+jeZ|%0!l|H_=D_`77hBfd4Zqk&! zq-Dnt_?5*$Wsw8zGd@?woEtfYZ2|9L8b>TO6>oMh%`B7iBb)-aCefM~q|S2Cc0t9T zlu-ZXmM0wd$!gd-dTtik{bqyx32%f;`XUvbUWWJmpHfk8^PQIEsByJm+@+-aj4J#D z4#Br3pO6z1eIC>X^yKk|PeVwX_4B+IYJyJyc3B`4 zPrM#raacGIzVOexcVB;fcsxS=s1e&V;Xe$tw&KQ`YaCkHTKe*Al#velxV{3wxx}`7@isG zp6{+s)CG%HF#JBAQ_jM%zCX5X;J%-*%&jVI?6KpYyzGbq7qf;&hFprh?E5Wyo=bZ) z8YNycvMNGp1836!-?nihm6jI`^C`EeGryoNZO1AFTQhzFJOA%Q{X(sMYlzABt!&f{ zoDENSuoJQIg5Q#@BUsNJX2h>jkdx4<+ipUymWKFr;w+s>$laIIkfP6nU}r+?J9bZg zUIxz>RX$kX=C4m(zh-Eg$BsJ4OL&_J38PbHW&7JmR27%efAkqqdvf)Am)VF$+U3WR z-E#I9H6^)zHLKCs7|Zs<7Bo9VCS3@CDQ;{UTczoEprCKL3ZZW!ffmZFkcWU-V|_M2 zUA9~8tE9<5`59W-UgUmDFp11YlORl3mS3*2#ZHjv{*-1#uMV_oVTy{PY(}AqZv#wF zJVks)%N6LaHF$$<6p8S8Lqn+5&t}DmLKiC~lE{jPZ39oj{wR&fe*LX-z0m}9ZnZ{U z>3-5Bh{KKN^n5i!M79Aw5eY=`6fG#aW1_ZG;fw7JM69qk^*(rmO{|Z6rXy?l=K=#_ zE-zd*P|(sskasO(cZ5L~_{Mz&Y@@@Q)5_8l<6vB$@226O+pDvkFaK8b>%2 zfMtgJ@+cN@w>3)(_uR;s8$sGONbYvoEZ3-)zZk4!`tNzd<0lwt{RAgplo*f@Z)uO` zzd`ljSqKfHJOLxya4_}T`k5Ok1Mpo#MSqf~&ia3uIy{zyuaF}pV6 z)@$ZG5LYh8Gge*LqM_|GiT1*J*uKes=Oku_gMj&;FS`*sfpM+ygN&yOla-^WtIU#$ zuw(_-?DS?6DY7IbON7J)p^IM?N>7x^3)(7wR4PZJu(teex%l>zKAUSNL@~{czc}bR z)I{XzXqZBU3a;7UQ~PvAx8g-3q-9AEd}1JrlfS8NdPc+!=HJ6Bs( zCG!0;e0z-22(Uzw>hkEmC&xj?{0p|kc zM}MMXCF%RLLa#5jG`+}{pDL3M&|%3BlwOi?dq!)KUdv5__zR>u^o|QkYiqr(m3HxF z6J*DyN#Jpooc$ok=b7{UAVM@nwGsr6kozSddwulf5g1{B=0#2)zv!zLXQup^BZ4sv*sEsn)+MA?t zEL)}3*R?4(J~CpeSJPM!oZ~8;8s_=@6o`IA%{aEA9!GELRvOuncE`s7sH91 zmF=+T!Q6%){?lJn3`5}oW31(^Of|$r%`~gT{eimT7R~*Mg@x+tWM3KE>=Q>nkMG$U za7r>Yz2LEaA|PsMafvJ(Y>Xzha?=>#B!sYfVob4k5Orb$INFdL@U0(J8Hj&kgWUlO zPm+R07E+oq^4f4#HvEPANGWLL_!uF{nkHYE&BCH%l1FL_r(Nj@M)*VOD5S42Gk-yT z^23oAMvpA57H(fkDGMx86Z}rtQhR^L!T2iS!788E z+^${W1V}J_NwdwdxpXAW8}#6o1(Uu|vhJvubFvQIH1bDl4J4iDJ+181KuDuHwvM?` z%1@Tnq+7>p{O&p=@QT}4wT;HCb@i)&7int<0#bj8j0sfN3s6|a(l7Bj#7$hxX@~iP z1HF8RFH}irky&eCN4T94VyKqGywEGY{Gt0Xl-`|dOU&{Q;Ao;sL>C6N zXx1y^RZSaL-pG|JN;j9ADjo^XR}gce#seM4QB1?S`L*aB&QlbBIRegMnTkTCks7JU z<0(b+^Q?HN1&$M1l&I@>HMS;!&bb()a}hhJzsmB?I`poqTrSoO>m_JE5U4=?o;OV6 zBZjt;*%1P>%2{UL=;a4(aI>PRk|mr&F^=v6Fr&xMj8fRCXE5Z2qdre&;$_RNid5!S zm^XiLK25G6_j4dWkFqjtU7#s;b8h?BYFxV?OE?c~&ME`n`$ix_`mb^AWr+{M9{^^Rl;~KREplwy2q;&xe zUR0SjHzKVYzuqQ84w$NKVPGVHL_4I)Uw<$uL2-Ml#+5r2X{LLqc*p13{;w#E*Kwb*1D|v?e;(<>vl@VjnFB^^Y;;b3 z=R@(uRj6D}-h6CCOxAdqn~_SG=bN%^9(Ac?zfRkO5x2VM0+@_qk?MDXvf=@q_* z3IM@)er6-OXyE1Z4sU3{8$Y$>8NcnU-nkyWD&2ZaqX1JF_JYL8y}>@V8A5%lX#U3E zet5PJM`z79q9u5v(OE~{by|Jzlw2<0h`hKpOefhw=fgLTY9M8h+?37k@TWpzAb2Fc zQMf^aVf!yXlK?@5d-re}!fuAWu0t57ZKSSacwRGJ$0uC}ZgxCTw>cjRk*xCt%w&hh zoeiIgdz__&u~8s|_TZsGvJ7sjvBW<(C@}Y%#l_ID2&C`0;Eg2Z+pk;IK}4T@W6X5H z`s?ayU-iF+aNr5--T-^~K~p;}D(*GWOAYDV9JEw!w8ZYzS3;W6*_`#aZw&9J ziXhBKU3~zd$kKzCAP-=t&cFDeQR*_e*(excIUxKuD@;-twSlP6>wWQU)$|H3Cy+`= z-#7OW!ZlYzZxkdQpfqVDFU3V2B_-eJS)Fi{fLtRz!K{~7TR~XilNCu=Z;{GIf9KYz zf3h=Jo+1#_s>z$lc~e)l93h&RqW1VHYN;Yjwg#Qi0yzjN^M4cuL>Ew`_-_wRhi*!f zLK6vTpgo^Bz?8AsU%#n}^EGigkG3FXen3M;hm#C38P@Zs4{!QZPAU=m7ZV&xKI_HWNt90Ef zxClm)ZY?S|n**2cNYy-xBlLAVZ=~+!|7y`(fh+M$#4zl&T^gV8ZaG(RBD!`3?9xcK zp2+aD(T%QIgrLx5au&TjG1AazI;`8m{K7^!@m>uGCSR;Ut{&?t%3AsF{>0Cm(Kf)2 z?4?|J+!BUg*P~C{?mwPQ#)gDMmro20YVNsVx5oWQMkzQ? zsQ%Y>%7_wkJqnSMuZjB9lBM(o zWut|B7w48cn}4buUBbdPBW_J@H7g=szrKEpb|aE>!4rLm+sO9K%iI75y~2HkUo^iw zJ3se$8$|W>3}?JU@3h@M^HEFNmvCp|+$-0M?RQ8SMoZ@38%!tz8f8-Ptb@106heiJ z^Bx!`0=Im z1!NUhO=9ICM*+||b3a7w*Y#5*Q}K^ar+oMMtekF0JnO>hzHqZKH0&PZ^^M(j;vwf_ z@^|VMBpcw8;4E-9J{(u7sHSyZpQbS&N{VQ%ZCh{c1UA5;?R} z+52*X_tkDQ(s~#-6`z4|Y}3N#a&dgP4S_^tsV=oZr4A1 zaSoPN1czE(UIBrC_r$0HM?RyBGe#lTBL4~JW#A`P^#0wuK)C-2$B6TvMi@@%K@JAT_IB^T7Zfqc8?{wHcSVG_?{(wUG%zhCm=%qP~EqeqKI$9UivF zv+5IUOs|%@ypo6b+i=xsZ=^G1yeWe)z6IX-EC`F=(|_GCNbHbNp(CZ*lpSu5n`FRA zhnrc4w+Vh?r>her@Ba_jv0Omp#-H7avZb=j_A~B%V0&FNi#!S8cwn0(Gg-Gi_LMI{ zCg=g@m{W@u?GQ|yp^yENd;M=W2s-k7Gw2Z(tsD5fTGF{iZ%Ccgjy6O!AB4x z%&=6jB7^}pyftW2YQpOY1w@%wZy%}-l0qJlOSKZXnN2wo3|hujU+-U~blRF!^;Tan z0w;Srh0|Q~6*tXf!5-rCD)OYE(%S|^WTpa1KHtpHZ{!;KdcM^#g8Z^+LkbiBHt85m z;2xv#83lWB(kplfgqv@ZNDcHizwi4-8+WHA$U-HBNqsZ`hKcUI3zV3d1ngJP-AMRET*A{> zb2A>Fk|L|WYV;Eu4>{a6ESi2r3aZL7x}eRc?cf|~bP)6b7%BnsR{Sa>K^0obn?yiJ zCVvaZ&;d_6WEk${F1SN0{_`(#TuOOH1as&#&xN~+JDzX(D-WU_nLEI}T_VaeLA=bc zl_UZS$nu#C1yH}YV>N2^9^zye{rDrn(rS99>Fh&jtNY7PP15q%g=RGnxACdCov47= zwf^9zfJaL{y`R#~tvVL#*<`=`Qe zj_@Me$6sIK=LMFbBrJps7vdaf_HeX?eC+P^{AgSvbEn?n<}NDWiQGQG4^ZOc|GskK z$Ve2_n8gQ-KZ=s(f`_X!+vM5)4+QmOP()2Fe#IL2toZBf+)8gTVgDSTN1CkP<}!j7 z0SEl>PBg{MnPHkj4wj$mZ?m5x!1ePVEYI(L_sb0OZ*=M%yQb?L{UL(2_*CTVbRxBe z@{)COwTK1}!*CK0Vi4~AB;HF(MmQf|dsoy(eiQ>WTKcEQlnKOri5xYsqi61Y=I4kzAjn5~{IWrz_l))|Ls zvq7xgQs?Xx@`N?f7+3XKLyD~6DRJw*uj*j?yvT3}a;(j_?YOe%hUFcPGWRVBXzpMJ zM43g6DLFqS9tcTLSg=^&N-y0dXL816v&-nqC0iXdg7kV|PY+js`F8dm z2PuHw&k+8*&9SPQ6f!^5q0&AH(i+z3I7a?8O+S5`g)>}fG|BM&ZnmL;rk)|u{1!aZ zEZHpAMmK_v$GbrrWNP|^2^s*!0waLW=-h5PZa-4jWYUt(Hr@EA(m3Mc3^uDxwt-me^55FMA9^>hpp26MhqjLg#^Y7OIJ5%ZLdNx&uDgIIqc zZRZl|n6TyV)0^DDyVtw*jlWkDY&Gw4q;k!UwqSL6&sW$B*5Rc?&)dt29bDB*b6IBY z6SY6Unsf6AOQdEf=P1inu6(6hVZ0~v-<>;LAlcQ2u?wRWj5VczBT$Op#8IhppP-1t zfz5H59Aa~yh7EN;BXJsLyjkjqARS5iIhDVPj<=4AJb}m6M@n{xYj3qsR*Q8;hVxDyC4vLI;;?^eENOb5QARj#nII5l$MtBCI@5u~(ylFi$ zw6-+$$XQ}Ca>FWT>q{k)g{Ml(Yv=6aDfe?m|5|kbGtWS}fKWI+})F6`x@||0oJ^(g|+xi zqlPdy5;`g*i*C=Q(aGeDw!eQg&w>UUj^{o?PrlFI=34qAU2u@BgwrBiaM8zoDTFJ< zh7nWpv>dr?q;4ZA?}V}|7qWz4W?6#S&m>hs4IwvCBe@-C>+oohsQZ^JC*RfDRm!?y zS4$7oxcI|##ga*y5hV>J4a%HHl^t$pjY%caL%-FlRb<$A$E!ws?8hf0@(4HdgQ!@> zds{&g$ocr9W4I84TMa9-(&^_B*&R%^=@?Ntxi|Ejnh;z=!|uVj&3fiTngDPg=0=P2 zB)3#%HetD84ayj??qrxsd9nqrBem(8^_u_UY{1@R_vK-0H9N7lBX5K(^O2=0#TtUUGSz{ z%g>qU8#a$DyZ~EMa|8*@`GOhCW3%DN%xuS91T7~iXRr)SG`%=Lfu%U~Z_`1b=lSi?qpD4$vLh$?HU6t0MydaowUpb zQr{>_${AMesCEffZo`}K0^~x>RY_ZIG{(r39MP>@=aiM@C;K)jUcfQV8#?SDvq>9D zI{XeKM%$$XP5`7p3K0T}x;qn)VMo>2t}Ib(6zui;k}<<~KibAb%p)**e>ln<=qyWU zrRDy|UXFi9y~PdEFIAXejLA{K)6<)Q`?;Q5!KsuEw({!#Rl8*5_F{TP?u|5(Hijv( ztAA^I5+$A*+*e0V0R~fc{ET-RAS3suZ}TRk3r)xqj~g_hxB`qIK5z(5wxYboz%46G zq{izIz^5xW1Vq#%lhXaZL&)FJWp0VZNO%2&ADd?+J%K$fM#T_Eke1{dQsx48dUPUY zLS+DWMJeUSjYL453f@HpRGU6Dv)rw+-c6xB>(=p4U%}_p>z^I@Ow9`nkUG21?cMIh9}hN?R-d)*6%pr6d@mcb*ixr7 z)>Lo<&2F}~>WT1ybm^9UO{6P9;m+fU^06_$o9gBWL9_}EMZFD=rLJ~&e?fhDnJNBI zKM=-WR6g7HY5tHf=V~6~QIQ~rakNvcsamU8m28YE=z8+G7K=h%)l6k zmCpiDInKL6*e#)#Pt;ANmjf`8h-nEt&d}(SBZMI_A{BI#ck-_V7nx)K9_D9K-p@?Zh81#b@{wS?wCcJ%og)8RF*-0z+~)6f#T` zWqF7_CBcnn=S-1QykC*F0YTsKMVG49BuKQBH%WuDkEy%E?*x&tt%0m>>5^HCOq|ux zuvFB)JPR-W|%$24eEC^AtG3Gp4qdK%pjRijF5Sg3X}uaKEE z-L5p5aVR!NTM8T`4|2QA@hXiLXRcJveWZ%YeFfV%mO5q#($TJ`*U>hicS+CMj%Ip# zivoL;dd*araeJK9EA<(tihD50FHWbITBgF9E<33A+eMr2;cgI3Gg6<-2o|_g9|> zv5}i932( zYfTE9?4#nQhP@a|zm#9FST2 z!y+p3B;p>KkUzH!K;GkBW}bWssz)9b>Ulg^)EDca;jDl+q=243BddS$hY^fC6lbpM z(q_bo4V8~eVeA?0LFD6ZtKcmOH^75#q$Eo%a&qvE8Zsqg=$p}u^|>DSWUP5i{6)LAYF4E2DfGZuMJ zMwxxmkxQf}Q$V3&2w|$`9_SQS^2NVbTHh;atB>=A%!}k-f4*i$X8m}Ni^ppZXk5_oYF>Gq(& z0wy{LjJOu}69}~#UFPc;$7ka+=gl(FZCy4xEsk);+he>Nnl>hb5Ud-lj!CNicgd^2 z_Qgr_-&S7*#nLAI7r()P$`x~fy)+y=W~6aNh_humoZr7MWGSWJPLk}$#w_1n%(@? z3FnHf1lbxKJbQ9c&i<$(wd{tUTX6DAKs@cXIOBv~!9i{wD@*|kwfX~sjKASrNFGvN zrFc=!0Bb^OhR2f`%hrp2ibv#KUxl)Np1aixD9{^o=)*U%n%rTHX?FSWL^UGpHpY@7 z74U}KoIRwxI#>)Pn4($A`nw1%-D}`sGRZD8Z#lF$6 zOeA5)+W2qvA%m^|$WluUU-O+KtMqd;Pd58?qZj})MbxYGO<{z9U&t4D{S2G>e+J9K ztFZ?}ya>SVOLp9hpW)}G%kTrg*KXXXsLkGdgHb+R-ZXqdkdQC0_)`?6mqo8(EU#d( zy;u&aVPe6C=YgCRPV!mJ6R6kdY*`e+VGM~`VtC>{k27!9vAZT)x2~AiX5|m1Rq}_= z;A9LX^nd$l-9&2%4s~p5r6ad-siV`HtxKF}l&xGSYJmP=z!?Mlwmwef$EQq~7;#OE z)U5eS6dB~~1pkj#9(}T3j!((8Uf%!W49FfUAozijoxInUE7z`~U3Y^}xc3xp){#9D z<^Tz2xw}@o@fdUZ@hnW#dX6gDOj4R8dV}Dw`u!h@*K)-NrxT8%2`T}EvOImNF_N1S zy?uo6_ZS>Qga4Xme3j#aX+1qdFFE{NT0Wfusa$^;eL5xGE_66!5_N8!Z~jCAH2=${ z*goHjl|z|kbmIE{cl-PloSTtD+2=CDm~ZHRgXJ8~1(g4W=1c3=2eF#3tah7ho`zm4 z05P&?nyqq$nC?iJ-nK_iBo=u5l#|Ka3H7{UZ&O`~t-=triw=SE7ynzMAE{Mv-{7E_ zViZtA(0^wD{iCCcg@c{54Ro@U5p1QZq_XlEGtdBAQ9@nT?(zLO0#)q55G8_Ug~Xnu zR-^1~hp|cy&52iogG@o?-^AD8Jb^;@&Ea5jEicDlze6%>?u$-eE};bQ`T6@(bED0J zKYtdc?%9*<<$2LCBzVx9CA4YV|q-qg*-{yQ;|0=KIgI6~z0DKTtajw2Oms3L zn{C%{P`duw!(F@*P)lFy11|Z&x`E2<=$Ln38>UR~z6~za(3r;45kQK_^QTX%!s zNzoIFFH8|Y>YVrUL5#mgA-Jh>j7)n)5}iVM4%_@^GSwEIBA2g-;43* z*)i7u*xc8jo2z8&=8t7qo|B-rsGw)b8UXnu`RgE4u!(J8yIJi(5m3~aYsADcfZ!GG zzqa7p=sg`V_KjiqI*LA-=T;uiNRB;BZZ)~88 z`C%p8%hIev2rxS12@doqsrjgMg3{A&N8A?%Ui5vSHh7!iC^ltF&HqG~;=16=h0{ygy^@HxixUb1XYcR36SB}}o3nxu z_IpEmGh_CK<+sUh@2zbK9MqO!S5cao=8LSQg0Zv4?ju%ww^mvc0WU$q@!oo#2bv24 z+?c}14L2vlDn%Y0!t*z=$*a!`*|uAVu&NO!z_arim$=btpUPR5XGCG0U3YU`v>yMr z^zmTdcEa!APX zYF>^Q-TP11;{VgtMqC}7>B^2gN-3KYl33gS-p%f!X<_Hr?`rG8{jb9jmuQA9U;BeG zHj6Pk(UB5c6zwX%SNi*Py*)gk^?+729$bAN-EUd*RKN7{CM4`Q65a1qF*-QWACA&m zrT)B(M}yih{2r!Tiv5Y&O&=H_OtaHUz96Npo_k0eN|!*s2mLe!Zkuv>^E8Xa43ZwH zOI058AZznYGrRJ+`*GmZzMi6yliFmGMge6^j?|PN%ARns!Eg$ufpcLc#1Ns!1@1 zvC7N8M$mRgnixwEtX{ypBS^n`k@t2cCh#_6L6WtQb8E~*Vu+Rr)YsKZRX~hzLG*BE zaeU#LPo?RLm(Wzltk79Jd1Y$|6aWz1)wf1K1RtqS;qyQMy@H@B805vQ%wfSJB?m&&=^m4i* zYVH`zTTFbFtNFkAI`Khe4e^CdGZw;O0 zqkQe2|NG_y6D%h(|EZNf&77_!NU%0y={^E=*gKGQ=)LdKPM3zUlM@otH2X07Awv8o zY8Y7a1^&Yy%b%m{mNQ5sWNMTIq96Wtr>a(hL>Qi&F(ckgKkyvM0IH<_}v~Fv-GqDapig=3*ZMOx!%cYY)SKzo7ECyem z9Mj3C)tCYM?C9YIlt1?zTJXNOo&oVxu&uXKJs7i+j8p*Qvu2PAnY}b`KStdpi`trk ztAO}T8eOC%x)mu+4ps8sYZ=vYJp16SVWEEgQyFKSfWQ@O5id6GfL`|2<}hMXLPszS zgK>NWOoR zBRyKeUPevpqKKShD|MZ`R;~#PdNMB3LWjqFKNvH9k+;(`;-pyXM55?qaji#nl~K8m z_MifoM*W*X9CQiXAOH{cZcP0;Bn10E1)T@62Um>et2ci!J2$5-_HPy(AGif+BJpJ^ ziHWynC_%-NlrFY+(f7HyVvbDIM$5ci_i3?22ZkF>Y8RPBhgx-7k3M2>6m5R24C|~I z&RPh9xpMGzhN4bii*ryWaN^d(`0 zTOADlU)g`1p+SVMNLztd)c+;XjXox(VHQwqzu>FROvf0`s&|NEv26}(TAe;@=FpZq zaVs6mp>W0rM3Qg*6x5f_bPJd!6dQGmh?&v0rpBNfS$DW-{4L7#_~-eA@7<2BsZV=X zow){3aATmLZOQrs>uzDkXOD=IiX;Ue*B(^4RF%H zeaZ^*MWn4tBDj(wj114r(`)P96EHq4th-;tWiHhkp2rDlrklX}I@ib-nel0slFoQO zOeTc;Rh7sMIebO`1%u)=GlEj+7HU;c|Nj>2j)J-kpR)s3#+9AiB zd$hAk6;3pu9(GCR#)#>aCGPYq%r&i02$0L9=7AlIGYdlUO5%eH&M!ZWD&6^NBAj0Y9ZDcPg@r@8Y&-}e!aq0S(`}NuQ({;aigCPnq75U9cBH&Y7 ze)W0aD>muAepOKgm7uPg3Dz7G%)nEqTUm_&^^3(>+eEI;$ia`m>m0QHEkTt^=cx^JsBC68#H(3zc~Z$E9I)oSrF$3 zUClHXhMBZ|^1ikm3nL$Z@v|JRhud*IhOvx!6X<(YSX(9LG#yYuZeB{=7-MyPF;?_8 zy2i3iVKG2q!=JHN>~!#Bl{cwa6-yB@b<;8LSj}`f9pw7#x3yTD>C=>1S@H)~(n_K4 z2-yr{2?|1b#lS`qG@+823j;&UE5|2+EdU4nVw5=m>o_gj#K>>(*t=xI7{R)lJhLU{ z4IO6!x@1f$aDVIE@1a0lraN9!(j~_uGlks)!&davUFRNYHflp<|ENwAxsp~4Hun$Q z$w>@YzXp#VX~)ZP8`_b_sTg(Gt7?oXJW%^Pf0UW%YM+OGjKS}X`yO~{7WH6nX8S6Z ztl!5AnM2Lo*_}ZLvo%?iV;D2z>#qdpMx*xY2*GGlRzmHCom`VedAoR=(A1nO)Y>;5 zCK-~a;#g5yDgf7_phlkM@)C8s!xOu)N2UnQhif-v5kL$*t=X}L9EyBRq$V(sI{90> z=ghTPGswRVbTW@dS2H|)QYTY&I$ljbpNPTc_T|FEJkSW7MV!JM4I(ksRqQ8)V5>}v z2Sf^Z9_v;dKSp_orZm09jb8;C(vzFFJgoYuWRc|Tt_&3k({wPKiD|*m!+za$(l*!gNRo{xtmqjy1=kGzFkTH=Nc>EL@1Um0BiN1)wBO$i z6rG={bRcT|%A3s3xh!Bw?=L&_-X+6}L9i~xRj2}-)7fsoq0|;;PS%mcn%_#oV#kAp zGw^23c8_0~ ze}v9(p};6HM0+qF5^^>BBEI3d=2DW&O#|(;wg}?3?uO=w+{*)+^l_-gE zSw8GV=4_%U4*OU^hibDV38{Qb7P#Y8zh@BM9pEM_o2FuFc2LWrW2jRRB<+IE)G=Vx zuu?cp2-`hgqlsn|$nx@I%TC!`>bX^G00_oKboOGGXLgyLKXoo$^@L7v;GWqfUFw3< zekKMWo0LR;TaFY}Tt4!O$3MU@pqcw!0w0 zA}SnJ6Lb597|P5W8$OsEHTku2Kw9y4V=hx*K%iSn!#LW9W#~OiWf^dXEP$^2 zaok=UyGwy3GRp)bm6Gqr>8-4h@3=2`Eto2|JE6Sufh?%U6;ut1v1d@#EfcQP2chCt z+mB{Bk5~()7G>wM3KYf7Xh?LGbwg1uWLotmc_}Z_o;XOUDyfU?{9atAT$={v82^w9 z(MW$gINHt4xB3{bdbhRR%T}L?McK?!zkLK3(e>zKyei(yq%Nsijm~LV|9mll-XHavFcc$teX7v);H>=oN-+E_Q{c|! zp

    JV~-9AH}jxf6IF!PxrB9is{_9s@PYth^`pb%DkwghLdAyDREz(csf9)HcVRq z+2Vn~>{(S&_;bq_qA{v7XbU?yR7;~JrLfo;g$Lkm#ufO1P`QW_`zWW+4+7xzQZnO$ z5&GyJs4-VGb5MEDBc5=zxZh9xEVoY(|2yRv&!T7LAlIs@tw+4n?v1T8M>;hBv}2n) zcqi+>M*U@uY>4N3eDSAH2Rg@dsl!1py>kO39GMP#qOHipL~*cCac2_vH^6x@xmO|E zkWeyvl@P$2Iy*mCgVF+b{&|FY*5Ygi8237i)9YW#Fp& z?TJTQW+7U)xCE*`Nsx^yaiJ0KSW}}jc-ub)8Z8x(|K7G>`&l{Y&~W=q#^4Gf{}aJ%6kLXsmv6cr=Hi*uB`V26;dr4C$WrPnHO>g zg1@A%DvIWPDtXzll39kY6#%j;aN7grYJP9AlJgs3FnC?crv$wC7S4_Z?<_s0j;MmE z75yQGul2=bY%`l__1X3jxju2$Ws%hNv75ywfAqjgFO7wFsFDOW^)q2%VIF~WhwEW0 z45z^+r+}sJ{q+>X-w(}OiD(!*&cy4X&yM`!L0Fe+_RUfs@=J{AH#K~gArqT=#DcGE z!FwY(h&+&811rVCVoOuK)Z<-$EX zp`TzcUQC256@YWZ*GkE@P_et4D@qpM92fWA6c$MV=^qTu7&g)U?O~-fUR&xFqNiY1 zRd=|zUs_rmFZhKI|H}dcKhy%Okl(#y#QuMi81zsY56Y@757xBQqDNkd+XhLQhp2BB zBF^aJ__D676wLu|yYo6jNJNw^B+Ce;DYK!f$!dNs1*?D^97u^jKS++7S z5qE%zG#HY-SMUn^_yru=T6v`)CM%K<>_Z>tPe|js`c<|y7?qol&)C=>uLWkg5 zmzNcSAG_sL)E9or;i+O}tY^70@h7+=bG1;YDlX{<4zF_?{)K5B&?^tKZ6<$SD%@>F zY0cl2H7)%zKeDX%Eo7`ky^mzS)s;842cP{_;dzFuyd~Npb4u!bwkkhf8-^C2e3`q8>MuPhgiv0VxHxvrN9_`rJv&GX0fWz-L-Jg^B zrTsm>)-~j0F1sV=^V?UUi{L2cp%YwpvHwwLaSsCIrGI#({{QfbgDxLKsUC6w@m?y} zg?l=7aMX-RnMxvLn_4oSB|9t;)Qf2%m-GKo_07?N1l^ahJ+Wf8C>h5~=-o1BJzV@5HBTB-ACNpsHnGt6_ku37M z{vIEB^tR=--4SEg{jfF=gEogtGwi&A$mwk7E+SV$$ZuU}#F3Y7t}o{!w4LJh8v4PW%8HfUK@dta#l*z@w*9Xzz(i)r#WXi`r1D#oBPtNM7M?Hkq zhhS1)ea5(6VY45|)tCTr*@yc$^Zc!zQzsNXU?aRN6mh7zVu~i=qTrX^>de+f6HYfDsW@6PBlw0CsDBcOWUmt&st>Z zYNJEsRCP1#g0+Htb=wITvexBY@fOpAmR7?szQNR~nM)?sPWIj)0)jG-EF8U@nnBaQZy z)ImpVYQL>lBejMDjlxA$#G4%y+^_>N;}r@Zoe2|u-9-x@vvD^ZWnV>Gm=pZa7REAf zOnomhCxBaGZgT+4kiE%aS&lH2sI1mSCM<%)Cr*Sli;#!aXcUb&@Z|Hj{VPsJyClqD%>hy`Y7z(GASs8Mqas3!D zSQE83*%uctlD|p%4)v`arra4y>yP5m25V*_+n)Ry1v>z_Fz!TV6t+N?x?#iH$q=m= z8&X{uW%LVRO87dVl=$Y*>dabJVq{o|Kx`7(D2$5DVX&}XGbg|Ua(*5b=;5qzW9;|w>m{hIO(Tu-z(ey8H=EMluJNyK4BJmGpX~ZM2O61 zk*O7js{-MBqwq>Urf0igN+6soGGc!Y?SP6hiXuJzZ1V4WZqE*?h;PG84gvG~dds6~484!kPM zMP87IP?dhdc;%|cS&LxY*Ib6P3%p|9)E3IgRmhhwtUR3eRK6iZ_6fiGW}jnL4(I|t ze`2yLvmuY42lNwO6>I#Son3$R4NOoP*WUm1R4jl#agtSLE}fSu-Z>{+*?pQIn7`s3LAzF#1pSxCAo?clr9 z9PUj#REq28*ZkJnxs$aK%8^5?P<_Q!#Z?%JH0FKVF;&zH3F#J^fz|ahl$Ycs~kFij_XP;U<`FcaDYyXYPM~&jEe1Xj1n;wyRdD;lmnq&FEro=;+Z$=v-&fYM9eK*S_D&oTXFW#b0 zRY}Y7R#bLzTfg9i7{s?=P9~qjA?$-U2p5;0?gPPu`1JY|*?*8IPO!eX>oiX=O#F!A zl`S%e5Y(csR1f)I(iKMf-;5%_rPP7h&}5Fc(8byKUH1*d7?9%QC|4aADj3L8yuo6GOv#%HDgU3bN(UHw1+(99&Om%f!DY(RYSf4&Uny% zH}*&rEXc$W5+eyeEg|I|E-HnkIO0!$1sV7Z&NXxiCZJ@`kH4eEi5}q~!Vv5qQq{MI zi4^`GYoUN-7Q(jy^SKXL4$G4K+FQXR)B}ee=pS0RyK=YC8c2bGnMA~rrOh&jd3_AT zxVaq37w^-;OU3+C`Kko-Z%l_2FC^maa=Ae0Fm@PEtXEg@cX*oka1Lt&h@jES<6?o1Oi1C9>}7+U(Ve zQ$=8RlzcnfCd59CsJ=gG^A!2Bb_PY~K2sSau{)?Ge03G7US&qrgV!3NUi>UHWZ*lo zS;~0--vn{ot+7UWMV{a(X3rZ8Z06Ps3$-sd|CWE(Y#l`swvcDbMjuReGsoA`rmZ`^ z=AaArdbeU0EtwnOuzq@u5P1rlZjH#gNgh6HIhG(>dX%4m{_!&DNTQE)8= zXD-vcpcSi|DSm3aUMnrV;DQY?svz?9*#GT$NXb~Hem=24iy>7xj367(!#RjnrHtrP-Q`T2W*PEvAR-=j ztY2|#<|JvHNVnM-tNdoS_yRSo=yFqukTZmB$|>Vclj)o=YzC9!ph8)ZOH5X=%Aq|9gNgc}^KFVLht!Lyw54v5u&D zW%vT%z`H{Ax>Ry+bD&QjHQke_wEA;oj(&E!s4|OURButQKSc7Ar-PzIiFa8F@ezkaY2J9&PH+VI1!G+{JgsQ7%da*_Gr!exT*OgJld)b-?cd)xI+|v_C`h(Cg`N~oj0`SQPTma z{@vc8L^D-rBXwS#00jT#@=-n1H-C3hvg61r2jx#ok&cr#BV~9JdPaVihyrGq*lb>bm$H6rIoc}ifaSn6mTD9% z$FRJxbNozOo6y}!OUci1VBv-7{TYZ4GkOM@46Y9?8%mSH9?l&lU59)T#Fjg(h%6I} z?ib zZ(xb8Rwr+vv>@$h{WglT2lL`#V=-9tP^c)cjvnz(g|VL^h8^CPVv12dE(o}WQ@0OP z^2-&ssBXP^#Oh`X5@F+~$PCB6kK-T7sFUK|>$lNDSkvAy%{y2qgq-&v zv}^&gm`wiYztWgMS<{^qQKYNV=>CQaOeglAY~EZvr}n~tW=yg)_+fzqF%~+*V_$3h z2hDW`e$qR;QMg?(wKE>%H_6ASS@6bkOi-m- zg6B7AzD;gBS1%OD7|47a%3BykN{w}P!Wn-nQOfpKUpx8Mk{$IO62D!%U9$kr!e%T> zlqQih?3(U&5%r!KZFZPdbwZ0laAJCj!c&pEFVzrH&_&i5m68Y_*J+-Qjlnz}Q{3oAD)`d14H zKUGmbwC|beC9Mtp>SbL~NVrlctU3WBpHz(UeIa~_{u^_4OaHs_LQt>bUwcyD`_Bbh zC=x|1vSjL)JvVHLw|xKynEvq2m)7O-6qdmjht7pZ*z|o%NA17v$9H*(5D5(MXiNo1 z72Tv}QASqr$!mY58s_Q{hHa9MY+QZ`2zX-FT@Kd?`8pczcV^9IeOKDG4WKqiP7N|S z+O977=VQTk8k5dafK`vd(4?_3pBdB?YG9*Z=R@y|$S+d%1sJf-Ka++I&v9hH)h#}} zw-MjQWJ?ME<7PR(G<1#*Z-&M?%=yzhQw$Lki(R+Pq$X~Q!9BO=fP9FyCIS8zE3n04 z8ScD%XmJnIv=pMTgt6VSxBXOZucndRE@7^aU0wefJYueY(Cb%?%0rz)zWEnsNsKhQ z+&o6d^x=R;Pt7fUa_`JVb1HPHYbXg{Jvux|atQ^bV#_|>7QZNC~P^IKUThB6{kvz2pr2*Cyxj zy37Nri8za8J!@Iw9rbt~#^<9zOaM8LOi$kPBcAGqPq-DB^-93Qeup{9@9&=zV6KQN zL)ic5S%n1!F(7b>MQ973$~<0|9MY-G!?wk?j-cQhMQlM2n{&7JoTBGsP;=fC6CBJn zxlpk^%x=B16rfb-W9pYV#9IRHQL9VG4?Uh>pN>2}0-MST2AB2pQjf*rT+TLCX-+&m z9I{ic2ogXoh=HwdI#igr(JC>>NUP|M>SA?-ux<2&>Jyx>Iko!B<3vS}{g*dKqxYW7 z0i`&U#*v)jot+keO#G&wowD!VvD(j`Z9a*-_RALKn0b(KnZ37d#Db7royLhBW~*7o zRa`=1fo9C4dgq;;R)JpP++a9^{xd)8``^fPW9!a%MCDYJc;3yicPs8IiQM>DhUX*; zeIrxE#JRrr|D$@bKgOm4C9D+e!_hQKj3LC`Js)|Aijx=J!rlgnpKeF>b+QlKhI^4* zf%Of^RmkW|xU|p#Lad44Y5LvIUIR>VGH8G zz7ZEIREG%UOy4)C!$muX6StM4@Fsh&Goa}cj10RL(#>oGtr6h~7tZDDQ_J>h)VmYlKK>9ns8w4tdx6LdN5xJQ9t-ABtTf_ zf1dKVv!mhhQFSN=ggf(#$)FtN-okyT&o6Ms+*u72Uf$5?4)78EErTECzweDUbbU)) zc*tt+9J~Pt%!M352Y5b`Mwrjn^Orp+)L_U1ORHJ}OUsB78YPcIRh4p5jzoDB7B*fb z4v`bouQeCAW#z9b1?4(M3dcwNn2F2plwC^RVHl#h&b-8n#5^o+Ll20OlJ^gOYiK2< z;MQuR!t!>`i}CAOa4a+Rh5IL|@kh4EdEL*O=3oGx4asg?XCTcUOQnmHs^6nLu6WcI zSt9q7nl*?2TIikKNb?3JZBo$cW6)b#;ZKzi+(~D-%0Ec+QW=bZZm@w|prGiThO3dy zU#TQ;RYQ+xU~*@Zj;Rf~z~iL8Da`RT!Z)b3ILBhnIl@VX9K0PSj5owH#*FJXX3vZ= zg_Zyn^G&l!WR6wN9GWvt)sM?g2^CA8&F#&t2z3_MiluRqvNbV{Me6yZ&X-_ zd6#Xdh%+6tCmSNTdCBusVkRwJ_A~<^Nd6~MNOvS;YDixM43`|8e_bmc*UWi7TLA})`T_F ztk&Nd=dgFUss#Ol$LXTRzP9l1JOSvAws~^X%(`ct$?2Im?UNpXjBec_-+8YK%rq#P zT9=h8&gCtgx?=Oj$Yr2jI3`VVuZ`lH>*N+*K11CD&>>F)?(`yr~54vHJftY*z?EorK zm`euBK<$(!XO%6-1=m>qqp6F`S@Pe3;pK5URT$8!Dd|;`eOWdmn916Ut5;iXWQoXE z0qtwxlH=m_NONP3EY2eW{Qwr-X1V3;5tV;g7tlL4BRilT#Y&~o_!f;*hWxWmvA;Pg zRb^Y$#PipnVlLXQIzKCuQP9IER0Ai4jZp+STb1Xq0w(nVn<3j(<#!vuc?7eJEZC<- zPhM7ObhgabN2`pm($tu^MaBkRLzx&jdh;>BP|^$TyD1UHt9Qvr{ZcBs^l!JI4~d-Py$P5QOYO&8eQOFe)&G zZm+?jOJioGs7MkkQBCzJSFJV6DiCav#kmdxc@IJ9j5m#&1)dhJt`y8{T!uxpBZ>&z zD^V~%GEaODak5qGj|@cA7HSH{#jHW;Q0KRdTp@PJO#Q1gGI=((a1o%X*{knz&_`ym zkRLikN^fQ%Gy1|~6%h^vx>ToJ(#aJDxoD8qyOD{CPbSvR*bC>Nm+mkw>6mD0mlD0X zGepCcS_x7+6X7dH;%e`aIfPr-NXSqlu&?$Br1R}3lSF2 zWOXDtG;v#EVLSQ!>4323VX-|E#qb+x%IxzUBDI~N23x? zXUHfTTV#_f9T$-2FPG@t)rpc9u9!@h^!4=fL^kg9 zVv%&KY3!?bU*V4X)wNT%Chr;YK()=~lc%$auOB_|oH`H)Xot@1cmk{^qdt&1C55>k zYnIkdoiAYW41zrRBfqR?9r^cpWIEqfS;|R#bIs4$cqA zoq~$yl8h{IXTSdSdH?;`ky6i%+Oc?HvwH+IS`%_a!d#CqQob9OTNIuhUnOQsX;nl_ z;1w99qO9lAb|guQ9?p4*9TmIZ5{su!h?v-jpOuShq!{AuHUYtmZ%brpgHl$BKLK_L z6q5vZodM$)RE^NNO>{ZWPb%Ce111V4wIX}?DHA=uzTu0$1h8zy!SID~m5t)(ov$!6 zB^@fP#vpx3enbrbX=vzol zj^Bg7V$Qa53#3Lptz<6Dz=!f+FvUBVIBtYPN{(%t(EcveSuxi3DI>XQ*$HX~O{KLK5Dh{H2ir87E^!(ye{9H&2U4kFxtKHkw zZPOTIa*29KbXx-U4hj&iH<9Z@0wh8B6+>qQJn{>F0mGnrj|0_{nwN}Vw_C!rm0!dC z>iRlEf}<+z&?Z4o3?C>QrLBhXP!MV0L#CgF{>;ydIBd5A{bd-S+VFn zLqq4a*HD%65IqQ5BxNz~vOGU=JJv|NG{OcW%2PU~MEfy6(bl#^TfT7+az5M-I`i&l z#g!HUfN}j#adA-21x7jbP6F;`99c8Qt|`_@u@fbhZF+Wkmr;IdVHj+F=pDb4MY?fU znDe##Hn){D}<>vVhYL#)+6p9eAT3T$?;-~bZU%l7MpPNh_mPc(h@79 z;LPOXk>e3nmIxl9lno5cI5G@Q!pE&hQ`s{$Ae4JhTebeTsj*|!6%0;g=wH?B1-p{P z`In#EP12q6=xXU)LiD+mLidPrYGHaKbe5%|vzApq9(PI6I5XjlGf<_uyy59iw8W;k zdLZ|8R8RWDc`#)n2?~}@5)vvksY9UaLW`FM=2s|vyg>Remm=QGthdNL87$nR&TKB*LB%*B}|HkG64 zZ|O4=Yq?Zwl>_KgIG@<8i{Zw#P3q_CVT7Dt zoMwoI)BkpQj8u(m!>1dfOwin(50}VNiLA>A2OG&TBXcP=H(3I;!WdPFe?r_e{%>bc6(Zk?6~Ew&;#ZxBJ| zAd1(sAHqlo_*rP;nTk)kAORe3cF&tj>m&LsvB)`-y9#$4XU=Dd^+CzvoAz%9216#f0cS`;kERxrtjbl^7pmO;_y zYBGOL7R1ne7%F9M2~0a7Srciz=MeaMU~ zV%Y#m_KV$XReYHtsraWLrdJItLtRiRo98T3J|x~(a>~)#>JHDJ z|4j!VO^qWQfCm9-$N29SpHUqvz62%#%98;2FNIF*?c9hZ7GAu$q>=0 zX_igPSK8Et(fmD)V=CvbtA-V(wS?z6WV|RX2`g=w=4D)+H|F_N(^ON!jHf72<2nCJ z^$hEygTAq7URR{Vq$)BsmFKTZ+i1i(D@SJuTGBN3W8{JpJ^J zkF=gBTz|P;Xxo1NIypGzJq8GK^#4tl)S%8$PP6E8c|GkkQ)vZ1OiB%mH#@hO1Z%Hp zv%2~Mlar^}7TRN-SscvQ*xVv+i1g8CwybQHCi3k;o$K@bmB%^-U8dILX)7b~#iPu@ z&D&W7YY2M3v`s(lNm2#^dCRFd;UYMUw1Rh2mto8laH1m`n0u;>okp5XmbsShOhQwo z@EYOehg-KNab)Rieib?m&NXls+&31)MB&H-zj_WmJsGjc1sCSOz0!2Cm1vV?y@kkQ z<1k6O$hvTQnGD*esux*aD3lEm$mUi0td0NiOtz3?7}h;Bt*vIC{tDBr@D)9rjhP^< zY*uKu^BiuSO%)&FL>C?Ng!HYZHLy`R>`rgq+lJhdXfo|df zmkzpQf{6o9%^|7Yb5v{Tu& zsP*Y~<#jK$S_}uEisRC;=y{zbq`4Owc@JyvB->nPzb#&vcMKi5n66PVV{Aub>*>q8 z=@u7jYA4Ziw2{fSED#t4QLD7Rt`au^y(Ggp3y(UcwIKtI(OMi@GHxs!bj$v~j(FZK zbdcP^gExtXQqQ8^Q#rHy1&W8q!@^aL>g1v2R45T(KErWB)1rB@rU`#n&-?g2Ti~xXCrexrLgajgzNy=N9|A6K=RZ zc3yk>w5sz1zsg~tO~-Ie?%Aplh#)l3`s632mi#CCl^75%i6IY;dzpuxu+2fliEjQn z&=~U+@fV4>{Fp=kk0oQIvBdqS#yY`Z+>Z|T&K{d;v3}=JqzKx05XU3M&@D5!uPTGydasyeZ5=1~IX-?HlM@AGB9|Mzb{{Dt@bUU8{KUPU@EX zv0fpQNvG~nD2WiOe{Vn=hE^rQD(5m+!$rs%s{w9;yg9oxRhqi0)rwsd245)igLmv* zJb@Xlet$+)oS1Ra#qTB@U|lix{Y4lGW-$5*4xOLY{9v9&RK<|K!fTd0wCKYZ)h&2f zEMcTCd+bj&YVmc#>&|?F!3?br3ChoMPTA{RH@NF(jmGMB2fMyW(<0jUT=8QFYD7-% zS0ydgp%;?W=>{V9>BOf=p$q5U511~Q0-|C!85)W0ov7eb35%XV;3mdUI@f5|x5C)R z$t?xLFZOv}A(ZjjSbF+8&%@RChpRvo>)sy>-IO8A@>i1A+8bZd^5J#(lgNH&A=V4V z*HUa0{zT{u-_FF$978RziwA@@*XkV{<-CE1N=Z!_!7;wq*xt3t((m+^$SZKaPim3K zO|Gq*w5r&7iqiQ!03SY{@*LKDkzhkHe*TzQaYAkz&jNxf^&A_-40(aGs53&}$dlKz zsel3=FvHqdeIf!UYwL&Mg3w_H?utbE_(PL9B|VAyaOo8k4qb>EvNYHrVmj^ocJQTf zL%4vl{qgmJf#@uWL@)WiB>Lm>?ivwB%uO|)i~;#--nFx4Kr6{TruZU0N_t_zqkg`? zwPFK|WiC4sI%o1H%$!1ANyq6_0OSPQJybh^vFriV=`S;kSsYkExZwB{68$dTODWJQ z@N57kBhwN(y~OHW_M}rX2W13cl@*i_tjW`TMfa~Y;I}1hzApXgWqag@(*@(|EMOg- z^qMk(s~dL#ps>>`oWZD=i1XI3(;gs7q#^Uj&L`gVu#4zn$i!BIHMoOZG!YoPO^=Gu z5`X-(KoSsHL77c<7^Y*IM2bI!dzg5j>;I@2-EeB$LgW|;csQTM&Z|R)q>yEjk@Sw% z6FQk*&zHWzcXalUJSoa&pgH24n`wKkg=2^ta$b1`(BBpBT2Ah9yQF&Kh+3jTaSE|=vChGz2_R^{$C;D`Ua(_=|OO11uLm;+3k%kO19EA`U065i;fRBoH z{Hq$cgHKRFPf0#%L?$*KeS@FDD;_TfJ#dwP7zzO5F>xntH(ONK{4)#jYUDQr6N(N< zp+fAS9l9)^c4Ss8628Zq5AzMq4zc(In_yJSXAT57Dtl}@= zvZoD7iq0cx7*#I{{r9m{%~g6@Hdr|*njKBb_5}mobCv=&X^`D9?;x6cHwRcwnlO^h zl;MiKr#LaoB*PELm8+8%btnC)b^E12!^ zMmVA!z>59e7n+^!P{PA?f9M^2FjKVw1%x~<`RY5FcXJE)AE}MTopGFDkyEjGiE|C6 z(ad%<3?v*?p;LJGopSEY18HPu2*}U!Nm|rfewc6(&y(&}B#j85d-5PeQ{}zg>>Rvl zDQ3H4E%q_P&kjuAQ>!0bqgAj){vzHpnn+h(AjQ6GO9v**l0|aCsCyXVE@uh?DU;Em zE*+7EU9tDH````D`|rM6WUlzBf1e{ht8$62#ilA6Dcw)qAzSRwu{czZJAcKv8w(Q6 zx)b$aq*=E=b5(UH-5*u)3iFlD;XQyklZrwHy}+=h6=aKtTriguHP@Inf+H@q32_LL z2tX|+X}4dMYB;*EW9~^5bydv)_!<%q#%Ocyh=1>FwL{rtZ?#2Scp{Q55%Fd-LgLU$ zM2u#|F{%vi%+O2^~uK3)?$6>9cc7_}F zWU72eFrzZ~x3ZIBH;~EMtD%51o*bnW;&QuzwWd$ds=O>Ev807cu%>Ac^ZK&7bCN;Ftk#eeQL4pG0p!W{Ri@tGw>nhIo`rC zi!Z6?70nYrNf92V{Y_i(a4DG=5>RktP=?%GcHEx?aKN$@{w{uj#Cqev$bXefo?yC6KI%Rol z%~$974WCymg;BBhd9Mv}_MeNro_8IB4!evgo*je4h?B-CAkEW-Wr-Q_V9~ef(znU& z{f-OHnj>@lZH(EcUb2TpOkc70@1BPiY0B#++1EPY5|UU?&^Vpw|C`k4ZWiB-3oAQM zgmG%M`2qDw5BMY|tG++34My2fE|^kvMSp(d+~P(Vk*d+RW1833i_bX^RYbg9tDtX` zox?y^YYfs-#fX|y7i(FN7js)66jN!`p9^r7oildEU#6J1(415H3h>W*p(p9@dI|c7 z&c*Aqzksg}o`D@i+o@WIw&jjvL!(`)JglV5zwMn)praO2M05H&CDeps0Wq8(8AkuE zPm|8MB6f0kOzg(gw}k>rzhQyo#<#sVdht~Wdk`y`=%0!jbd1&>Kxed8lS{Xq?Zw>* zU5;dM1tt``JH+A9@>H%-9f=EnW)UkRJe0+e^iqm0C5Z5?iEn#lbp}Xso ztleC}hl&*yPFcoCZ@sgvvjBA_Ew6msFml$cfLQY_(=h03WS_z+Leeh$M3#-?f9YT^Q($z z+pgaEv$rIa*9wST`WHASQio=9IaVS7l<87%;83~X*`{BX#@>>p=k`@FYo ze!K5_h8hOc`m0mK0p}LxsguM}w=9vw6Ku8y@RNrXSRPh&S`t4UQY=e-B8~3YCt1Fc zU$CtRW%hbcy{6K{>v0F*X<`rXVM3a{!muAeG$zBf`a(^l${EA9w3>J{aPwJT?mKVN2ba+v)Mp*~gQ_+Ws6= zy@D?85!U@VY0z9T=E9LMbe$?7_KIg)-R$tD)9NqIt84fb{B;f7C)n+B8)Cvo*F0t! zva6LeeC}AK4gL#d#N_HvvD& z0;mdU3@7%d5>h(xX-NBmJAOChtb(pX-qUtRLF5f$ z`X?Kpu?ENMc88>O&ym_$Jc7LZ> z#73|xJ|aa@l}PawS4Mpt9n)38w#q^P1w2N|rYKdcG;nb!_nHMZA_09L!j)pBK~e+j?tb-_A`wF8 zIyh>&%v=|n?+~h}%i1#^9UqZ?E9W!qJ0d0EHmioSt@%v7FzF`eM$X==#oaPESHBm@ zYzTXVo*y|C0~l_)|NF|F(If~YWJVkQAEMf5IbH{}#>PZpbXZU;+b^P8LWmlmDJ%Zu)4CajvRL!g_Faph`g0hpA2)D0|h zYy0h5+@4T81(s0D=crojdj|dYa{Y=<2zKp@xl&{sHO;#|!uTHtTey25f1U z#=Nyz{rJy#@SPk3_U|aALcg%vEjwIqSO$LZI59^;Mu~Swb53L+>oxWiN7J{;P*(2b@ao*aU~}-_j10 z@fQiaWnb}fRrHhNKrxKmi{aC#34BRP(a#0K>-J8D+v_2!~(V-6J%M@L{s?fU5ChwFfqn)2$siOUKw z?SmIRlbE8ot5P^z0J&G+rQ5}H=JE{FNsg`^jab7g-c}o`s{JS{-#}CRdW@hO`HfEp z1eR0DsN! zt5xmsYt{Uu;ZM`CgW)VYk=!$}N;w+Ct$Wf!*Z-7}@pA62F^1e$Ojz9O5H;TyT&rV( zr#IBM8te~-2t2;kv2xm&z%tt3pyt|s#vg2EOx1XkfsB*RM;D>ab$W-D6#Jdf zJ3{yD;P4=pFNk2GL$g~+5x;f9m*U2!ovWMK^U5`mAgBRhGpu)e`?#4vsE1aofu)iT zDm;aQIK6pNd8MMt@}h|t9c$)FT7PLDvu3e)y`otVe1SU4U=o@d!gn(DB9kC>Ac1wJ z?`{Hq$Q!rGb9h&VL#z+BKsLciCttdLJe9EmZF)J)c1MdVCrxg~EM80_b3k{ur=jVjrVhDK1GTjd3&t#ORvC0Q_&m|n>&TF1C_>k^8&ylR7oz#rG?mE%V| zepj0BlD|o?p8~LK_to`GINhGyW{{jZ{xqaO*SPvH)BYy1eH22DL_Kkn28N!0z3fzj z_+xZ3{ph_Tgkd)D$OjREak$O{F~mODA_D`5VsoobVnpxI zV0F_79%JB!?@jPs=cY73FhGuT!?fpVX1W=Wm zK5}i7(Pfh4o|Z{Ur=Y>bM1BDo2OdXBB(4Y#Z!61A8C6;7`6v-(P{ou1mAETEV?Nt< zMY&?ucJcJ$NyK0Zf@b;U#3ad?#dp`>zmNn=H1&-H`Y+)ai-TfyZJX@O&nRB*7j$ zDQF!q#a7VHL3z#Hc?Ca!MRbgL`daF zW#;L$yiQP|5VvgvRLluk3>-1cS+7MQ1)DC&DpYyS9j;!Rt$HdXK1}tG3G_)ZwXvGH zG;PB^f@CFrbEK4>3gTVj73~Tny+~k_pEHt|^eLw{?6NbG&`Ng9diB9XsMr(ztNC!{FhW8Hi!)TI`(Q|F*b z-z;#*c1T~kN67omP(l7)ZuTlxaC_XI(K8$VPfAzj?R**AMb0*p@$^PsN!LB@RYQ4U zA^xYY9sX4+;7gY%$i%ddfvneGfzbE4ZTJT5Vk3&1`?ULTy28&D#A&{dr5ZlZH&NTz zdfZr%Rw*Ukmgu@$C5$}QLOyb|PMA5syQns?iN@F|VFEvFPK321mTW^uv?GGNH6rnM zR9a2vB`}Y++T3Wumy$6`W)_c0PS*L;;0J^(T7<)`s{}lZVp`e)fM^?{$ zLbNw>N&6aw5Hlf_M)h8=)x0$*)V-w-Pw5Kh+EY{^$?#{v)_Y{9p5K{DjLnJ(ZUcyk*y(6D8wHB8=>Y)fb_Pw0v)Xybk`Sw@hNEaHP$-n`DtYP ziJyiauEXtuMpWyQjg$gdJR?e+=8w+=5GO-OT8pRaVFP1k^vI|I&agGjN-O*bJEK!M z`kt^POhUexh+PA&@And|vk-*MirW?>qB(f%y{ux z*d44UXxQOs+C`e-x4KSWhPg-!gO~kavIL8X3?!Ac2ih-dkK~Ua2qlcs1b-AIWg*8u z0QvL~51vS$LnmJSOnV4JUCUzg&4;bSsR5r_=FD@y|)Y2R_--e zMWJ;~*r=vJssF5_*n?wF0DO_>Mja=g+HvT=Yd^uBU|aw zRixHUQJX0Pgt-nFV+8&|;-n>!jNUj!8Y_YzH*%M!-_uWt6& z|Ec+lAD``i^do;u_?<(RpzsYZVJ8~}|NjUFgXltofbjhf!v&208g^#0h-x?`z8cInq!9kfVwJ|HQ;VK>p_-fn@(3q?e51Keq(=U-7C0#as-q z8Or}Ps07>O2@AAXz_%3bTOh{tKm#uRe}Sqr=w6-Wz$FCdfF3qNabEaj`-OfipxaL- zPh2R*l&%ZbcV?lv4C3+t2DAVSFaRo20^W_n4|0t(_*`?KmmUHG2sNZ*CRZlCFIyZbJqLdBCj)~%if)g|4NJr(8!R!E0iBbm$;`m;1n2@(8*E%B zH!g{hK|WK?1jUfM9zX?hlV#l%!6^p$$P+~rg}OdKg|d^Ed4WTY1$1J@WWHr$Os_(L z;-Zu1FJqhR4LrCUl)C~E7gA!^wtA6YIh10In9rX@LGSjnTPtLp+gPGp6u z3}{?J1!yT~?FwqT;O_-1%37f#4ek&DL){N}MX3RbNfRb-T;U^wXhx#De&QssA$lu~ mWkA_K7-+yz9tH*t6hj_Qg(_m7JaeTomk=)l!_+yTk^le-`GmOu delta 34176 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 SGW6ptSs!X7{rG*o_g?|vpSEZK diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a80b22ce5..b82aa23a4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew.bat b/gradlew.bat index 6689b85be..7101f8e46 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -43,11 +43,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail From 1a09888927f36fefe3505ef08fc2053ab13bc7f5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 25 Mar 2024 09:12:30 +0100 Subject: [PATCH 110/121] Update CPG packages to v8 (major) (#791) * Update CPG packages to v8 * Made it compile, but test fail * Remove obsolete CLI option * Update to latest CPG release * Update repositories for CPG v8.x --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Christian Banse Co-authored-by: Wendland, Florian --- buildSrc/src/main/kotlin/module.gradle.kts | 2 +- code-coverage-report/build.gradle.kts | 2 +- .../aisec/codyze/backends/cpg/CPGBackend.kt | 1 - .../aisec/codyze/backends/cpg/CPGConfiguration.kt | 1 - .../aisec/codyze/backends/cpg/CPGOptionGroup.kt | 11 ----------- .../aisec/codyze/backends/cpg/cli/BaseCpgBackend.kt | 1 - .../aisec/codyze/backends/cpg/cli/CokoCpgBackend.kt | 1 - .../backends/cpg/coko/evaluators/OrderEvaluator.kt | 4 ++-- .../aisec/codyze/backends/cpg/CPGConfigurationTest.kt | 3 --- .../aisec/codyze/backends/cpg/CpgOptionGroupTest.kt | 9 +++------ .../fraunhofer/aisec/codyze/backends/cpg/TestUtils.kt | 1 - .../coko/dsl/CokoCpgIntegrationTest.kt | 1 - gradle/libs.versions.toml | 2 +- 13 files changed, 8 insertions(+), 31 deletions(-) diff --git a/buildSrc/src/main/kotlin/module.gradle.kts b/buildSrc/src/main/kotlin/module.gradle.kts index f2d3cd753..25c56ca3e 100644 --- a/buildSrc/src/main/kotlin/module.gradle.kts +++ b/buildSrc/src/main/kotlin/module.gradle.kts @@ -38,7 +38,7 @@ repositories { // Eclipse CDT repo --> needed for CPG ivy { - setUrl("https://download.eclipse.org/tools/cdt/releases/11.0/cdt-11.0.0/plugins") + setUrl("https://download.eclipse.org/tools/cdt/releases/11.3/cdt-11.3.1/plugins") metadataSources { artifact() } diff --git a/code-coverage-report/build.gradle.kts b/code-coverage-report/build.gradle.kts index 43855ff89..a1bff7c6e 100644 --- a/code-coverage-report/build.gradle.kts +++ b/code-coverage-report/build.gradle.kts @@ -10,7 +10,7 @@ repositories { // Eclipse CDT repo --> needed when adding the CPG as an included build ivy { - setUrl("https://download.eclipse.org/tools/cdt/releases/11.0/cdt-11.0.0/plugins") + setUrl("https://download.eclipse.org/tools/cdt/releases/11.3/cdt-11.3.1/plugins") metadataSources { artifact() } diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGBackend.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGBackend.kt index b57f52d64..8f37c367b 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGBackend.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGBackend.kt @@ -67,7 +67,6 @@ open class CPGBackend(config: BackendConfiguration) : Backend { .processAnnotations(processAnnotations) .failOnError(failOnError) .useParallelFrontends(useParallelFrontends) - .typeSystemActiveInFrontend(typeSystemActiveInFrontend) .sourceLocations(source.map { (it.toFile()) }) .symbols(symbols) .useUnityBuild(useUnityBuild) diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGConfiguration.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGConfiguration.kt index 2621ab588..d7780e252 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGConfiguration.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGConfiguration.kt @@ -34,7 +34,6 @@ private val logger = KotlinLogging.logger {} data class CPGConfiguration( val source: List, val useUnityBuild: Boolean, - val typeSystemActiveInFrontend: Boolean, val debugParser: Boolean, val disableCleanup: Boolean, val codeInNodes: Boolean, diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGOptionGroup.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGOptionGroup.kt index 3a8c9552c..c77aa418b 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGOptionGroup.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGOptionGroup.kt @@ -82,17 +82,6 @@ class CPGOptionGroup : BackendOptions(helpName = "CPG Backend Options") { help = "Enables unity builds (C++ only) for files in the path." ) .flag("--no-unity", "--disable-unity", default = false) - val typeSystemActiveInFrontend: Boolean by option( - "--type-system-in-frontend", - help = "If deactivated, the type listener system starts after the frontends " + - "are done building the initial AST structure." - ) - .flag( - "--no-type-system-in-frontend", - "--disable-type-system-in-frontend", - default = true, - defaultForHelp = "enable" - ) val debugParser: Boolean by option("--debug-parser", help = "Generate debug output for the cpg parser.") .flag("--no-debug-parser", default = false) val disableCleanup: Boolean by option( diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/cli/BaseCpgBackend.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/cli/BaseCpgBackend.kt index f9bfc5739..c082e65e0 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/cli/BaseCpgBackend.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/cli/BaseCpgBackend.kt @@ -33,7 +33,6 @@ class BaseCpgBackend : BackendCommand("cpg") { CPGConfiguration( source = source, useUnityBuild = useUnityBuild, - typeSystemActiveInFrontend = typeSystemActiveInFrontend, debugParser = debugParser, disableCleanup = disableCleanup, codeInNodes = codeInNodes, diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/cli/CokoCpgBackend.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/cli/CokoCpgBackend.kt index f755db75c..b86ea74f6 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/cli/CokoCpgBackend.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/cli/CokoCpgBackend.kt @@ -34,7 +34,6 @@ class CokoCpgBackend : BackendCommand("cokoCpg") { CPGConfiguration( source = source, useUnityBuild = useUnityBuild, - typeSystemActiveInFrontend = typeSystemActiveInFrontend, debugParser = debugParser, disableCleanup = disableCleanup, codeInNodes = codeInNodes, diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OrderEvaluator.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OrderEvaluator.kt index 45de71d39..c85abf253 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OrderEvaluator.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OrderEvaluator.kt @@ -30,7 +30,7 @@ import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.Order import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.ordering.* import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration -import de.fraunhofer.aisec.cpg.graph.statements.expressions.DeclaredReferenceExpression +import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference import io.github.oshai.kotlinlogging.KotlinLogging import kotlin.reflect.full.createType import kotlin.reflect.full.declaredMemberFunctions @@ -164,7 +164,7 @@ class OrderEvaluator(val baseNodes: Collection, val order: Order) : Evalua nodeToRelevantMethod = nodesToOp, consideredBases = baseNodes.flatMap { node -> node.followNextDFGEdgesUntilHit { next -> - next is VariableDeclaration || next is DeclaredReferenceExpression + next is VariableDeclaration || next is Reference }.fulfilled.mapNotNull { path -> path.lastOrNull() } diff --git a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGConfigurationTest.kt b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGConfigurationTest.kt index d3563cde2..65553f2fc 100644 --- a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGConfigurationTest.kt +++ b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGConfigurationTest.kt @@ -25,7 +25,6 @@ class CPGConfigurationTest { val expectedCpgConfiguration = CPGConfiguration( source = listOf(), useUnityBuild = true, - typeSystemActiveInFrontend = false, debugParser = false, disableCleanup = false, codeInNodes = true, @@ -46,7 +45,6 @@ class CPGConfigurationTest { val cpgConfiguration = CPGConfiguration( source = listOf(), useUnityBuild = true, - typeSystemActiveInFrontend = false, debugParser = false, disableCleanup = false, codeInNodes = true, @@ -74,7 +72,6 @@ class CPGConfigurationTest { val expectedCpgConfiguration = CPGConfiguration( source = listOf(), useUnityBuild = false, - typeSystemActiveInFrontend = false, debugParser = false, disableCleanup = false, codeInNodes = true, diff --git a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt index 0f3fa74df..7ebfdfbea 100644 --- a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt +++ b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt @@ -20,10 +20,7 @@ import com.github.ajalt.clikt.core.MultiUsageError import com.github.ajalt.clikt.core.NoOpCliktCommand import com.github.ajalt.clikt.parameters.groups.provideDelegate import de.fraunhofer.aisec.codyze.core.config.combineSources -import de.fraunhofer.aisec.cpg.passes.CallResolver -import de.fraunhofer.aisec.cpg.passes.EdgeCachePass -import de.fraunhofer.aisec.cpg.passes.FilenameMapper -import de.fraunhofer.aisec.cpg.passes.Pass +import de.fraunhofer.aisec.cpg.passes.* import io.github.oshai.kotlinlogging.KotlinLogging import org.junit.jupiter.api.* import org.junit.jupiter.api.Test @@ -86,7 +83,7 @@ class CpgOptionGroupTest { fun passesTest() { val edgeCachePassName = EdgeCachePass::class.qualifiedName val filenameMapperName = FilenameMapper::class.qualifiedName - val callResolverName = CallResolver::class.qualifiedName + val callResolverName = SymbolResolver::class.qualifiedName assertNotNull(edgeCachePassName) assertNotNull(filenameMapperName) assertNotNull(callResolverName) @@ -106,7 +103,7 @@ class CpgOptionGroupTest { ) val expectedPassesNames = - listOf(EdgeCachePass::class, FilenameMapper::class, CallResolver::class).map { p -> + listOf(EdgeCachePass::class, FilenameMapper::class, SymbolResolver::class).map { p -> p.qualifiedName } val actualPassesNames = cli.cpgOptions.passes.map { p -> p.qualifiedName } diff --git a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/TestUtils.kt b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/TestUtils.kt index a3cea8180..32e22d705 100644 --- a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/TestUtils.kt +++ b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/TestUtils.kt @@ -25,7 +25,6 @@ fun createCpgConfiguration(vararg sourceFile: Path) = CPGConfiguration( source = listOf(*sourceFile), useUnityBuild = false, - typeSystemActiveInFrontend = true, debugParser = false, disableCleanup = false, codeInNodes = true, diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoCpgIntegrationTest.kt b/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoCpgIntegrationTest.kt index 0f710d243..10a603410 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoCpgIntegrationTest.kt +++ b/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoCpgIntegrationTest.kt @@ -45,7 +45,6 @@ class CokoCpgIntegrationTest { CPGConfiguration( source = sourceFiles, useUnityBuild = false, - typeSystemActiveInFrontend = true, debugParser = false, disableCleanup = false, codeInNodes = true, diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b5914294b..ba670724e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] kotlin = "1.9.23" -cpg = "7.1.2" +cpg = "8.1.2" koin = "3.5.3" koin-test = "3.5.3" detekt = "1.23.5" From e53bed60107fc6e1053c5cb45e1fccdd738c6e5a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 26 Mar 2024 11:08:09 +0100 Subject: [PATCH 111/121] Update detekt to v1.23.6 (#834) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ba670724e..99d289b0d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ kotlin = "1.9.23" cpg = "8.1.2" koin = "3.5.3" koin-test = "3.5.3" -detekt = "1.23.5" +detekt = "1.23.6" spotless = "6.25.0" dokka = "1.9.20" From c4f5d588b5cbbebd9de396bc297a195015d0d3a5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 2 Apr 2024 13:24:19 +0200 Subject: [PATCH 112/121] Update dependency com.github.ajalt.clikt:clikt to v4.3.0 (#835) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 99d289b0d..dfde6d9db 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,7 +30,7 @@ cpg-language-java = { module = "de.fraunhofer.aisec:cpg-language-java", version. kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version = "6.0.3" } log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version = "2.23.1"} -clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.2.2"} +clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.3.0"} koin = { module = "io.insert-koin:koin-core", version.ref = "koin"} koin-test = { module = "io.insert-koin:koin-test", version.ref = "koin-test"} koin-junit5 = { module = "io.insert-koin:koin-test-junit5", version.ref = "koin-test"} From 707955847f50e8ecd3cc987950d86af855399c89 Mon Sep 17 00:00:00 2001 From: Florian Wendland Date: Wed, 3 Apr 2024 11:36:20 +0200 Subject: [PATCH 113/121] Update detekt rule set (#828) --- .../src/main/kotlin/code-quality.gradle.kts | 3 +- .../coko/dsl/CokoCpgIntegrationTest.kt | 3 + detekt.yml | 875 +----------------- 3 files changed, 7 insertions(+), 874 deletions(-) diff --git a/buildSrc/src/main/kotlin/code-quality.gradle.kts b/buildSrc/src/main/kotlin/code-quality.gradle.kts index 2adeb7fd5..e2a1c38c2 100644 --- a/buildSrc/src/main/kotlin/code-quality.gradle.kts +++ b/buildSrc/src/main/kotlin/code-quality.gradle.kts @@ -28,7 +28,8 @@ tasks.jacocoTestReport { // a merged report tasks.withType().configureEach { basePath = "${rootProject.projectDir.absolutePath}" - config.setFrom(files("$rootDir/detekt.yml")) + buildUponDefaultConfig = true + config.setFrom(rootDir.resolve("detekt.yml")) } // custom task for fixing formatting issues diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoCpgIntegrationTest.kt b/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoCpgIntegrationTest.kt index 10a603410..3e4a6f14d 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoCpgIntegrationTest.kt +++ b/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoCpgIntegrationTest.kt @@ -131,6 +131,7 @@ class CokoCpgIntegrationTest { @MethodSource("threeFiles") fun `test coko with cpg backend and permutation of three dependent spec files`( specFiles: List, + @Suppress("detekt:style:UnusedParameter") // used for parameterized test names fileNames: List ) { assertEquals(3, specFiles.size) @@ -160,6 +161,7 @@ class CokoCpgIntegrationTest { @MethodSource("fourFiles") fun `test coko with cpg backend and permutation of four dependent spec files`( specFiles: List, + @Suppress("detekt:style:UnusedParameter") // used for parameterized test names fileNames: List ) { assertEquals(4, specFiles.size) @@ -191,6 +193,7 @@ class CokoCpgIntegrationTest { @MethodSource("fiveFiles") fun `test coko with cpg backend and permutation of five dependent spec files`( specFiles: List, + @Suppress("detekt:style:UnusedParameter") // used for parameterized test names fileNames: List ) { assertEquals(5, specFiles.size) diff --git a/detekt.yml b/detekt.yml index 35a709206..e7b42ebcb 100644 --- a/detekt.yml +++ b/detekt.yml @@ -1,548 +1,32 @@ build: - maxIssues: 0 - excludeCorrectable: false weights: empty-blocks: 0 performance: 0 - # complexity: 2 - # LongParameterList: 1 - # style: 1 - # comments: 1 - -config: - validation: true - warningsAsErrors: false - checkExhaustiveness: false - # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]' - excludes: '' - -processors: - active: true - exclude: - - 'DetektProgressListener' - # - 'KtFileCountProcessor' - # - 'PackageCountProcessor' - # - 'ClassCountProcessor' - # - 'FunctionCountProcessor' - # - 'PropertyCountProcessor' - # - 'ProjectComplexityProcessor' - # - 'ProjectCognitiveComplexityProcessor' - # - 'ProjectLLOCProcessor' - # - 'ProjectCLOCProcessor' - # - 'ProjectLOCProcessor' - # - 'ProjectSLOCProcessor' - # - 'LicenseHeaderLoaderExtension' - -console-reports: - active: true - exclude: - - 'ProjectStatisticsReport' - - 'ComplexityReport' - - 'NotificationReport' - - 'FindingsReport' - - 'FileBasedFindingsReport' - # - 'LiteFindingsReport' - -output-reports: - active: true - exclude: - # - 'TxtOutputReport' - # - 'XmlOutputReport' - # - 'HtmlOutputReport' - # - 'MdOutputReport' + UnusedParameter: 0 comments: - active: true AbsentOrWrongFileLicense: active: true licenseTemplateFile: 'buildSrc/src/main/resources/license.template' licenseTemplateIsRegex: true - CommentOverPrivateFunction: - active: false - CommentOverPrivateProperty: - active: false - DeprecatedBlockTag: - active: false - EndOfSentenceFormat: - active: false - endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)' KDocReferencesNonPublicProperty: active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - OutdatedDocumentation: - active: false - matchTypeParameters: true - matchDeclarationsOrder: true - allowParamOnConstructorProperties: false - UndocumentedPublicClass: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - searchInNestedClass: true - searchInInnerClass: true - searchInInnerObject: true - searchInInnerInterface: true - searchInProtectedClass: false - UndocumentedPublicFunction: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - searchProtectedFunction: false - UndocumentedPublicProperty: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - searchProtectedProperty: false - -complexity: - active: true - CognitiveComplexMethod: - active: false - threshold: 15 - ComplexCondition: - active: true - threshold: 4 - ComplexInterface: - active: false - threshold: 10 - includeStaticDeclarations: false - includePrivateDeclarations: false - ignoreOverloaded: false - CyclomaticComplexMethod: - active: true - threshold: 15 - ignoreSingleWhenExpression: false - ignoreSimpleWhenEntries: false - ignoreNestingFunctions: false - nestingFunctions: - - 'also' - - 'apply' - - 'forEach' - - 'isNotNull' - - 'ifNull' - - 'let' - - 'run' - - 'use' - - 'with' - LabeledExpression: - active: false - ignoredLabels: [] - LargeClass: - active: true - threshold: 600 - LongMethod: - active: true - threshold: 60 - LongParameterList: - active: true - functionThreshold: 6 - constructorThreshold: 7 - ignoreDefaultParameters: false - ignoreDataClasses: true - ignoreAnnotatedParameter: [] - MethodOverloading: - active: false - threshold: 6 - NamedArguments: - active: false - threshold: 3 - ignoreArgumentsMatchingNames: false - NestedBlockDepth: - active: true - threshold: 4 - NestedScopeFunctions: - active: false - threshold: 1 - functions: - - 'kotlin.apply' - - 'kotlin.run' - - 'kotlin.with' - - 'kotlin.let' - - 'kotlin.also' - ReplaceSafeCallChainWithRun: - active: false - StringLiteralDuplication: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - threshold: 3 - ignoreAnnotation: true - excludeStringsWithLessThan5Characters: true - ignoreStringsRegex: '$^' - TooManyFunctions: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - thresholdInFiles: 11 - thresholdInClasses: 11 - thresholdInInterfaces: 11 - thresholdInObjects: 11 - thresholdInEnums: 11 - ignoreDeprecated: false - ignorePrivate: false - ignoreOverridden: false - -coroutines: - active: true - GlobalCoroutineUsage: - active: false - InjectDispatcher: - active: true - dispatcherNames: - - 'IO' - - 'Default' - - 'Unconfined' - RedundantSuspendModifier: - active: true - SleepInsteadOfDelay: - active: true - SuspendFunWithCoroutineScopeReceiver: - active: false - SuspendFunWithFlowReturnType: - active: true - -empty-blocks: - active: true - EmptyCatchBlock: - active: true - allowedExceptionNameRegex: '_|(ignore|expected).*' - EmptyClassBlock: - active: true - EmptyDefaultConstructor: - active: true - EmptyDoWhileBlock: - active: true - EmptyElseBlock: - active: true - EmptyFinallyBlock: - active: true - EmptyForBlock: - active: true - EmptyFunctionBlock: - active: true - ignoreOverridden: false - EmptyIfBlock: - active: true - EmptyInitBlock: - active: true - EmptyKtFile: - active: true - EmptySecondaryConstructor: - active: true - EmptyTryBlock: - active: true - EmptyWhenBlock: - active: true - EmptyWhileBlock: - active: true - -exceptions: - active: true - ExceptionRaisedInUnexpectedLocation: - active: true - methodNames: - - 'equals' - - 'finalize' - - 'hashCode' - - 'toString' - InstanceOfCheckForException: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - NotImplementedDeclaration: - active: false - ObjectExtendsThrowable: - active: false - PrintStackTrace: - active: true - RethrowCaughtException: - active: true - ReturnFromFinally: - active: true - ignoreLabeled: false - SwallowedException: - active: true - ignoredExceptionTypes: - - 'InterruptedException' - - 'MalformedURLException' - - 'NumberFormatException' - - 'ParseException' - allowedExceptionNameRegex: '_|(ignore|expected).*' - ThrowingExceptionFromFinally: - active: true - ThrowingExceptionInMain: - active: false - ThrowingExceptionsWithoutMessageOrCause: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - exceptions: - - 'ArrayIndexOutOfBoundsException' - - 'Exception' - - 'IllegalArgumentException' - - 'IllegalMonitorStateException' - - 'IllegalStateException' - - 'IndexOutOfBoundsException' - - 'NullPointerException' - - 'RuntimeException' - - 'Throwable' - ThrowingNewInstanceOfSameException: - active: true - TooGenericExceptionCaught: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - exceptionNames: - - 'ArrayIndexOutOfBoundsException' - - 'Error' - - 'Exception' - - 'IllegalMonitorStateException' - - 'IndexOutOfBoundsException' - - 'NullPointerException' - - 'RuntimeException' - - 'Throwable' - allowedExceptionNameRegex: '_|(ignore|expected).*' - TooGenericExceptionThrown: - active: true - exceptionNames: - - 'Error' - - 'Exception' - - 'RuntimeException' - - 'Throwable' + excludes: ['**/test/**'] naming: - active: true - BooleanPropertyNaming: - active: false - allowedPattern: '^(is|has|are)' - ClassNaming: - active: true - classPattern: '[A-Z][a-zA-Z0-9]*' - ConstructorParameterNaming: - active: true - parameterPattern: '[a-z][A-Za-z0-9]*' - privateParameterPattern: '[a-z][A-Za-z0-9]*' - excludeClassPattern: '$^' - EnumNaming: - active: true - enumEntryPattern: '[A-Z][_a-zA-Z0-9]*' - ForbiddenClassName: - active: false - forbiddenName: [] - FunctionMaxLength: - active: false - maximumFunctionNameLength: 30 - FunctionMinLength: - active: false - minimumFunctionNameLength: 3 - FunctionNaming: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - functionPattern: '[a-z][a-zA-Z0-9]*' - excludeClassPattern: '$^' - FunctionParameterNaming: - active: true - parameterPattern: '[a-z][A-Za-z0-9]*' - excludeClassPattern: '$^' InvalidPackageDeclaration: active: true rootPackage: 'de.fraunhofer.aisec.codyze' requireRootInDeclaration: true - LambdaParameterNaming: - active: false - parameterPattern: '[a-z][A-Za-z0-9]*|_' - MatchingDeclarationName: - active: true - mustBeFirst: true - MemberNameEqualsClassName: - active: true - ignoreOverridden: true - NoNameShadowing: - active: true - NonBooleanPropertyPrefixedWithIs: - active: false - ObjectPropertyNaming: - active: true - constantPattern: '[A-Za-z][_A-Za-z0-9]*' - propertyPattern: '[A-Za-z][_A-Za-z0-9]*' - privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*' - PackageNaming: - active: true - packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*' - TopLevelPropertyNaming: - active: true - constantPattern: '[A-Z][_A-Z0-9]*' - propertyPattern: '[A-Za-z][_A-Za-z0-9]*' - privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*' - VariableMaxLength: - active: false - maximumVariableNameLength: 64 - VariableMinLength: - active: false - minimumVariableNameLength: 1 - VariableNaming: - active: true - variablePattern: '[a-z][A-Za-z0-9]*' - privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' - excludeClassPattern: '$^' performance: - active: true - ArrayPrimitive: - active: true CouldBeSequence: active: true threshold: 3 - ForEachOnRange: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - SpreadOperator: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - UnnecessaryPartOfBinaryExpression: - active: false - UnnecessaryTemporaryInstantiation: - active: true - -potential-bugs: - active: true - AvoidReferentialEquality: - active: true - forbiddenTypePatterns: - - 'kotlin.String' - CastToNullableType: - active: false - Deprecation: - active: false - DontDowncastCollectionTypes: - active: false - DoubleMutabilityForCollection: - active: true - mutableTypes: - - 'kotlin.collections.MutableList' - - 'kotlin.collections.MutableMap' - - 'kotlin.collections.MutableSet' - - 'java.util.ArrayList' - - 'java.util.LinkedHashSet' - - 'java.util.HashSet' - - 'java.util.LinkedHashMap' - - 'java.util.HashMap' - ElseCaseInsteadOfExhaustiveWhen: - active: false - EqualsAlwaysReturnsTrueOrFalse: - active: true - EqualsWithHashCodeExist: - active: true - ExitOutsideMain: - active: false - ExplicitGarbageCollectionCall: - active: true - HasPlatformType: - active: true - IgnoredReturnValue: - active: true - restrictToConfig: true - returnValueAnnotations: - - '*.CheckResult' - - '*.CheckReturnValue' - ignoreReturnValueAnnotations: - - '*.CanIgnoreReturnValue' - returnValueTypes: - - 'kotlin.sequences.Sequence' - - 'kotlinx.coroutines.flow.*Flow' - - 'java.util.stream.*Stream' - ignoreFunctionCall: [] - ImplicitDefaultLocale: - active: true - ImplicitUnitReturnType: - active: false - allowExplicitReturnType: true - InvalidRange: - active: true - IteratorHasNextCallsNextMethod: - active: true - IteratorNotThrowingNoSuchElementException: - active: true - LateinitUsage: - active: false - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - ignoreOnClassesPattern: '' - MapGetWithNotNullAssertionOperator: - active: true - MissingPackageDeclaration: - active: false - excludes: ['**/*.kts'] - NullCheckOnMutableProperty: - active: false - NullableToStringCall: - active: false - UnconditionalJumpStatementInLoop: - active: false - UnnecessaryNotNullCheck: - active: false - UnnecessaryNotNullOperator: - active: true - UnnecessarySafeCall: - active: true - UnreachableCatchBlock: - active: true - UnreachableCode: - active: true - UnsafeCallOnNullableType: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - UnsafeCast: - active: true - UnusedUnaryOperator: - active: true - UselessPostfixExpression: - active: true - WrongEqualsTypeParameter: - active: true style: - active: true - AlsoCouldBeApply: - active: false - BracesOnIfStatements: - active: false - singleLine: 'never' - multiLine: 'always' - CanBeNonNullable: - active: false - CascadingCallWrapping: - active: false - includeElvis: true - ClassOrdering: - active: false - CollapsibleIfStatements: - active: false - DataClassContainsFunctions: - active: false - conversionFunctionPrefix: - - 'to' - DataClassShouldBeImmutable: - active: false - DestructuringDeclarationWithTooManyEntries: - active: true - maxDestructuringEntries: 3 - EqualsNullCall: - active: true - EqualsOnSignatureLine: - active: false - ExplicitCollectionElementAccessMethod: - active: false - ExplicitItLambdaParameter: - active: true - ExpressionBodySyntax: - active: false - includeLineWrapping: false ForbiddenComment: active: false - comments: - - reason: 'Forbidden FIXME todo marker in comment, please fix the problem.' - value: 'FIXME:' - - reason: 'Forbidden STOPSHIP todo marker in comment, please address the problem before shipping the code.' - value: 'STOPSHIP:' - - reason: 'Forbidden TODO todo marker in comment, please do the changes.' - value: 'TODO:' - allowedPatterns: '' - ForbiddenImport: - active: false - imports: [] - forbiddenPatterns: '' ForbiddenMethodCall: active: true methods: @@ -550,380 +34,25 @@ style: value: 'kotlin.io.print' - reason: 'println does not allow you to configure the output stream. Use a logger instead.' value: 'kotlin.io.println' - ForbiddenSuppress: - active: false - rules: [] - ForbiddenVoid: - active: true - ignoreOverridden: false - ignoreUsageInGenerics: false - FunctionOnlyReturningConstant: - active: true - ignoreOverridableFunction: true - ignoreActualFunction: true - excludedFunctions: [] - LoopWithTooManyJumpStatements: - active: true - maxJumpCount: 1 - MagicNumber: - active: true - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**', '**/*.kts'] - ignoreNumbers: - - '-1' - - '0' - - '1' - - '2' - ignoreHashCodeFunction: true - ignorePropertyDeclaration: false - ignoreLocalVariableDeclaration: false - ignoreConstantDeclaration: true - ignoreCompanionObjectPropertyDeclaration: true - ignoreAnnotation: false - ignoreNamedArgument: true - ignoreEnums: false - ignoreRanges: false - ignoreExtensionFunctions: true - MandatoryBracesLoops: - active: false - MaxChainedCallsOnSameLine: - active: false - maxChainedCalls: 5 - MaxLineLength: - active: true - maxLineLength: 120 - excludePackageStatements: true - excludeImportStatements: true - excludeCommentStatements: false - excludeRawStrings: true - MayBeConst: - active: true - ModifierOrder: - active: true - MultilineLambdaItParameter: - active: false - MultilineRawStringIndentation: - active: false - indentSize: 4 - NestedClassesVisibility: - active: true - NewLineAtEndOfFile: - active: true - NoTabs: - active: false - NullableBooleanCheck: - active: false - ObjectLiteralToLambda: - active: true - OptionalAbstractKeyword: - active: true - OptionalUnit: - active: false - OptionalWhenBraces: - active: false - PreferToOverPairSyntax: - active: false - ProtectedMemberInFinalClass: - active: true - RedundantExplicitType: - active: false - RedundantHigherOrderMapUsage: - active: true - RedundantVisibilityModifierRule: - active: false - ReturnCount: - active: true - max: 2 - excludedFunctions: - - 'equals' - excludeLabeled: false - excludeReturnFromLambda: true - excludeGuardClauses: false - SafeCast: - active: true - SerialVersionUIDInSerializableClass: - active: true - SpacingBetweenPackageAndImports: - active: false - ThrowsCount: - active: true - max: 2 - excludeGuardClauses: false - TrailingWhitespace: - active: false - TrimMultilineRawString: - active: false - UnderscoresInNumericLiterals: - active: false - acceptableLength: 4 - allowNonStandardGrouping: false - UnnecessaryAbstractClass: - active: true - UnnecessaryAnnotationUseSiteTarget: - active: false - UnnecessaryApply: - active: true - UnnecessaryBackticks: - active: false - UnnecessaryFilter: - active: true - UnnecessaryInheritance: - active: true - UnnecessaryInnerClass: - active: false - UnnecessaryLet: - active: false - UnnecessaryParentheses: - active: false - allowForUnclearPrecedence: false - UntilInsteadOfRangeTo: - active: false - UnusedImports: - active: false - UnusedPrivateClass: - active: true - UnusedPrivateMember: - active: true - allowedNames: '(_|ignored|expected|serialVersionUID)' - excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**', '**/*.kts'] - UseAnyOrNoneInsteadOfFind: - active: true - UseArrayLiteralsInAnnotations: - active: true - UseCheckNotNull: - active: true - UseCheckOrError: - active: true - UseDataClass: - active: false - allowVars: false - UseEmptyCounterpart: - active: false - UseIfEmptyOrIfBlank: - active: false - UseIfInsteadOfWhen: - active: false - UseIsNullOrEmpty: - active: true - UseOrEmpty: - active: true - UseRequire: - active: true - UseRequireNotNull: - active: true - UseSumOfInsteadOfFlatMapSize: - active: false - UselessCallOnNotNull: - active: true - UtilityClassWithPublicConstructor: - active: true - VarCouldBeVal: - active: true - ignoreLateinitVar: false WildcardImport: active: false - excludeImports: - - 'java.util.*' formatting: - active: true - android: false - autoCorrect: true - AnnotationOnSeparateLine: - active: true - autoCorrect: true - AnnotationSpacing: - active: true - autoCorrect: true - ArgumentListWrapping: - active: true - autoCorrect: true - indentSize: 4 - maxLineLength: 120 - BlockCommentInitialStarAlignment: - active: true - autoCorrect: true - ChainWrapping: - active: true - autoCorrect: true - CommentSpacing: - active: true - autoCorrect: true - CommentWrapping: - active: true - autoCorrect: true - indentSize: 4 - DiscouragedCommentLocation: - active: false - autoCorrect: true - EnumEntryNameCase: - active: true - autoCorrect: true - Filename: - active: true - FinalNewline: - active: true - autoCorrect: true - insertFinalNewLine: true - FunKeywordSpacing: - active: true - autoCorrect: true - FunctionReturnTypeSpacing: - active: true - autoCorrect: true - FunctionSignature: - active: false - autoCorrect: true - forceMultilineWhenParameterCountGreaterOrEqualThan: 2147483647 - functionBodyExpressionWrapping: 'default' - maxLineLength: 120 - indentSize: 4 FunctionStartOfBodySpacing: active: false - autoCorrect: true FunctionTypeReferenceSpacing: active: false - autoCorrect: true - ImportOrdering: - active: true - autoCorrect: true - layout: '*,java.**,javax.**,kotlin.**,^' - Indentation: - active: true - autoCorrect: true - indentSize: 4 - KdocWrapping: - active: true - autoCorrect: true - indentSize: 4 MaximumLineLength: active: true maxLineLength: 120 ignoreBackTickedIdentifier: true ModifierListSpacing: active: false - autoCorrect: true - ModifierOrdering: - active: true - autoCorrect: true - MultiLineIfElse: - active: true - autoCorrect: true - NoBlankLineBeforeRbrace: - active: true - autoCorrect: true - NoBlankLinesInChainedMethodCalls: - active: true - autoCorrect: true - NoConsecutiveBlankLines: - active: true - autoCorrect: true - NoEmptyClassBody: - active: true - autoCorrect: true - NoEmptyFirstLineInMethodBlock: - active: true - autoCorrect: true - NoLineBreakAfterElse: - active: true - autoCorrect: true - NoLineBreakBeforeAssignment: - active: true - autoCorrect: true - NoMultipleSpaces: - active: true - autoCorrect: true - NoSemicolons: - active: true - autoCorrect: true - NoTrailingSpaces: - active: true - autoCorrect: true - NoUnitReturn: - active: true - autoCorrect: true - NoUnusedImports: - active: true - autoCorrect: true NoWildcardImports: active: false - packagesToUseImportOnDemandProperty: 'java.util.*,kotlinx.android.synthetic.**' NullableTypeSpacing: active: false - autoCorrect: true - PackageName: - active: true - autoCorrect: true - ParameterListSpacing: - active: false - autoCorrect: true - ParameterListWrapping: - active: true - autoCorrect: true - maxLineLength: 120 - SpacingAroundAngleBrackets: - active: true - autoCorrect: true - SpacingAroundColon: - active: true - autoCorrect: true - SpacingAroundComma: - active: true - autoCorrect: true - SpacingAroundCurly: - active: true - autoCorrect: true - SpacingAroundDot: - active: true - autoCorrect: true - SpacingAroundDoubleColon: - active: true - autoCorrect: true - SpacingAroundKeyword: - active: true - autoCorrect: true - SpacingAroundOperators: - active: true - autoCorrect: true - SpacingAroundParens: - active: true - autoCorrect: true - SpacingAroundRangeOperator: - active: true - autoCorrect: true SpacingAroundUnaryOperator: active: true excludes: ['**/coko/**/test/**', '**/codyze-backends/cpg/**/test/**'] autoCorrect: true - SpacingBetweenDeclarationsWithAnnotations: - active: true - autoCorrect: true - SpacingBetweenDeclarationsWithComments: - active: true - autoCorrect: true - SpacingBetweenFunctionNameAndOpeningParenthesis: - active: true - autoCorrect: true - StringTemplate: - active: true - autoCorrect: true - TrailingCommaOnCallSite: - active: false - autoCorrect: true - useTrailingCommaOnCallSite: true - TrailingCommaOnDeclarationSite: - active: false - autoCorrect: true - useTrailingCommaOnDeclarationSite: true - TypeArgumentListSpacing: - active: false - autoCorrect: true - TypeParameterListSpacing: - active: false - autoCorrect: true - UnnecessaryParenthesesBeforeTrailingLambda: - active: true - autoCorrect: true - Wrapping: - active: true - autoCorrect: true - indentSize: 4 From 0cd0f2eb65f8b353ecd5d88df79ad1ec39529b2e Mon Sep 17 00:00:00 2001 From: Robert Haimerl Date: Wed, 3 Apr 2024 16:02:45 +0200 Subject: [PATCH 114/121] Fix Windows path Issues in Coko tests (#836) Co-authored-by: Wendland, Florian --- .../codyze/backends/cpg/CpgOptionGroupTest.kt | 13 +++++-------- .../codyze/backends/cpg/FollowsEvaluationTest.kt | 2 +- .../codyze/backends/cpg/ImplementationDslTest.kt | 4 ++-- .../codyze/backends/cpg/NeverEvaluationTest.kt | 4 ++-- .../codyze/backends/cpg/OnlyEvaluationTest.kt | 2 +- .../fraunhofer/aisec/codyze/cli/CodyzeCliTest.kt | 8 ++++---- .../coko/dsl/CokoCpgIntegrationTest.kt | 14 +++++++------- .../coko/dsl/cli/CokoOptionGroupTest.kt | 6 +++--- .../coko/dsl/cli/ValidationTest.kt | 3 ++- 9 files changed, 27 insertions(+), 29 deletions(-) diff --git a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt index 7ebfdfbea..b893e2ab9 100644 --- a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt +++ b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CpgOptionGroupTest.kt @@ -30,10 +30,7 @@ import org.junit.jupiter.params.provider.MethodSource import java.nio.file.Files import java.nio.file.Path import java.util.stream.Stream -import kotlin.io.path.Path -import kotlin.io.path.absolute -import kotlin.io.path.div -import kotlin.io.path.isRegularFile +import kotlin.io.path.* import kotlin.streams.asSequence import kotlin.test.* @@ -223,19 +220,19 @@ class CpgOptionGroupTest { val topTestDirResource = CpgOptionGroupTest::class.java.classLoader.getResource("cli-test-directory") assertNotNull(topTestDirResource) - topTestDir = Path(topTestDirResource.path) + topTestDir = topTestDirResource.toURI().toPath() assertNotNull(topTestDir) // TODO: why is this necessary val testDir1Resource = CpgOptionGroupTest::class.java.classLoader.getResource("cli-test-directory/dir1") assertNotNull(testDir1Resource) - testDir1 = Path(testDir1Resource.path) + testDir1 = testDir1Resource.toURI().toPath() assertNotNull(testDir1) val testDir2Resource = CpgOptionGroupTest::class.java.classLoader.getResource("cli-test-directory/dir2") assertNotNull(testDir2Resource) - testDir2 = Path(testDir2Resource.path) + testDir2 = testDir2Resource.toURI().toPath() assertNotNull(testDir2) val testFile1Resource = @@ -244,7 +241,7 @@ class CpgOptionGroupTest { .classLoader .getResource("cli-test-directory/file1.java") assertNotNull(testFile1Resource) - testFile1 = Path(testFile1Resource.path) + testFile1 = testFile1Resource.toURI().toPath() assertNotNull(testFile1) allFiles = Files.walk(topTestDir).asSequence().filter { it.isRegularFile() }.toList() diff --git a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/FollowsEvaluationTest.kt b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/FollowsEvaluationTest.kt index e7067bcd3..231f0b736 100644 --- a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/FollowsEvaluationTest.kt +++ b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/FollowsEvaluationTest.kt @@ -117,7 +117,7 @@ class FollowsEvaluationTest { val testFileResource = classLoader.getResource("FollowsEvaluationTest/SimpleFollows.java") assertNotNull(testFileResource) - testFile = Path(testFileResource.path) + testFile = testFileResource.toURI().toPath() val fooInstance = FooModel() val barInstance = BarModel() diff --git a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/ImplementationDslTest.kt b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/ImplementationDslTest.kt index 2085ec80d..0dece26f6 100644 --- a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/ImplementationDslTest.kt +++ b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/ImplementationDslTest.kt @@ -23,7 +23,7 @@ import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.op import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.signature import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test -import kotlin.io.path.Path +import kotlin.io.path.toPath import kotlin.test.assertEquals import kotlin.test.assertNotNull @@ -94,7 +94,7 @@ class ImplementationDslTest { val testFileResource = classLoader.getResource("ImplementationDslTest/SimpleJavaFile.java") assertNotNull(testFileResource) - val testFile = Path(testFileResource.path) + val testFile = testFileResource.toURI().toPath() backend = CokoCpgBackend(config = createCpgConfiguration(testFile)) } } diff --git a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/NeverEvaluationTest.kt b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/NeverEvaluationTest.kt index 3b7e2bcad..615c531df 100644 --- a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/NeverEvaluationTest.kt +++ b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/NeverEvaluationTest.kt @@ -107,11 +107,11 @@ class NeverEvaluationTest { val violationFileResource = classLoader.getResource("NeverEvaluationTest/NeverViolation.java") assertNotNull(violationFileResource) - violationFile = Path(violationFileResource.path) + violationFile = violationFileResource.toURI().toPath() val passFileResource = classLoader.getResource("NeverEvaluationTest/NeverPass.java") assertNotNull(passFileResource) - passFile = Path(passFileResource.path) + passFile = passFileResource.toURI().toPath() } } } diff --git a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/OnlyEvaluationTest.kt b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/OnlyEvaluationTest.kt index 887942276..5b99e2a31 100644 --- a/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/OnlyEvaluationTest.kt +++ b/codyze-backends/cpg/src/test/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/OnlyEvaluationTest.kt @@ -73,7 +73,7 @@ class OnlyEvaluationTest { val testFileResource = classLoader.getResource("OnlyEvaluationTest/SimpleOnly.java") assertNotNull(testFileResource) - testFile = Path(testFileResource.path) + testFile = testFileResource.toURI().toPath() } } } diff --git a/codyze-cli/src/test/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeCliTest.kt b/codyze-cli/src/test/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeCliTest.kt index bafee3277..b4f8b913a 100644 --- a/codyze-cli/src/test/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeCliTest.kt +++ b/codyze-cli/src/test/kotlin/de/fraunhofer/aisec/codyze/cli/CodyzeCliTest.kt @@ -112,7 +112,7 @@ class CodyzeCliTest : KoinTest { val pathConfigFileResource = CodyzeCliTest::class.java.classLoader.getResource("config-files/path-config.json") assertNotNull(pathConfigFileResource) - pathConfigFile = Path(pathConfigFileResource.path) + pathConfigFile = pathConfigFileResource.toURI().toPath() assertTrue(pathConfigFile.exists()) val correctConfigFileResource = @@ -121,19 +121,19 @@ class CodyzeCliTest : KoinTest { .classLoader .getResource("config-files/correct-config.json") assertNotNull(correctConfigFileResource) - correctConfigFile = Path(correctConfigFileResource.path) + correctConfigFile = correctConfigFileResource.toURI().toPath() assertTrue(correctConfigFile.exists()) val specMarkResource = CodyzeCliTest::class.java.classLoader.getResource("config-files/spec/spec.mark") assertNotNull(specMarkResource) - specMark = Path(specMarkResource.path) + specMark = specMarkResource.toURI().toPath() assertTrue(specMark.exists()) val specMark2Resource = CodyzeCliTest::class.java.classLoader.getResource("config-files/spec2.mark") assertNotNull(specMark2Resource) - spec2Mark = Path(specMark2Resource.path) + spec2Mark = specMark2Resource.toURI().toPath() assertTrue(spec2Mark.exists()) } } diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoCpgIntegrationTest.kt b/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoCpgIntegrationTest.kt index 3e4a6f14d..810b1be18 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoCpgIntegrationTest.kt +++ b/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoCpgIntegrationTest.kt @@ -39,7 +39,7 @@ class CokoCpgIntegrationTest { .getResource("IntegrationTests/CokoCpg/Main.java"), CokoCpgIntegrationTest::class.java.classLoader .getResource("IntegrationTests/CokoCpg/SimpleOrder.java") - ).map { Path(it.path) }.also { assertEquals(2, it.size) } + ).map { it.toURI().toPath() }.also { assertEquals(2, it.size) } val cpgConfiguration = CPGConfiguration( @@ -72,7 +72,7 @@ class CokoCpgIntegrationTest { .getResource("IntegrationTests/CokoCpg/orderFull.codyze.kts"), CokoCpgIntegrationTest::class.java.classLoader .getResource("IntegrationTests/CokoCpg/followedByFull.codyze.kts") - ).map { Path(it.path) }.also { assertEquals(2, it.size) } + ).map { it.toURI().toPath() }.also { assertEquals(2, it.size) } val cokoConfiguration = CokoConfiguration( @@ -102,7 +102,7 @@ class CokoCpgIntegrationTest { .getResource("IntegrationTests/CokoCpg/followedByTwoFiles/followedByImplementations.codyze.kts"), CokoCpgIntegrationTest::class.java.classLoader .getResource("IntegrationTests/CokoCpg/followedByTwoFiles/followedByModels.codyze.kts") - ).map { Path(it.path) }.also { assertEquals(2, it.size) } + ).map { it.toURI().toPath() }.also { assertEquals(2, it.size) } val cokoConfiguration = CokoConfiguration( @@ -221,7 +221,7 @@ class CokoCpgIntegrationTest { fun `test coko with cpg backend without good findings`() { val specFiles = listOfNotNull( CokoCpgIntegrationTest::class.java.classLoader.getResource("IntegrationTests/CokoCpg/orderFull.codyze.kts"), - ).map { Path(it.path) }.also { assertEquals(1, it.size) } + ).map { it.toURI().toPath() }.also { assertEquals(1, it.size) } val cokoConfiguration = CokoConfiguration( @@ -252,7 +252,7 @@ class CokoCpgIntegrationTest { ) val permutations = fileMap.permutate() for (p in permutations) { - val (specFiles, fileNames) = p.map { Path(it.path) }.map { it to it.fileName }.unzip() + val (specFiles, fileNames) = p.map { it.toURI().toPath() }.map { it to it.fileName }.unzip() stream.add( Arguments.of( specFiles, @@ -278,7 +278,7 @@ class CokoCpgIntegrationTest { ) val permutations = fileMap.permutate() for (p in permutations) { - val (specFiles, fileNames) = p.map { Path(it.path) }.map { it to it.fileName }.unzip() + val (specFiles, fileNames) = p.map { it.toURI().toPath() }.map { it to it.fileName }.unzip() stream.add( Arguments.of( specFiles, @@ -306,7 +306,7 @@ class CokoCpgIntegrationTest { ) val permutations = fileMap.permutate() for (p in permutations) { - val (specFiles, fileNames) = p.map { Path(it.path) }.map { it to it.fileName }.unzip() + val (specFiles, fileNames) = p.map { it.toURI().toPath() }.map { it to it.fileName }.unzip() stream.add( Arguments.of( specFiles, diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/CokoOptionGroupTest.kt b/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/CokoOptionGroupTest.kt index 2c16724e1..769533cce 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/CokoOptionGroupTest.kt +++ b/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/CokoOptionGroupTest.kt @@ -23,9 +23,9 @@ import org.koin.test.KoinTest import org.koin.test.junit5.KoinTestExtension import java.nio.file.Files import java.nio.file.Path -import kotlin.io.path.Path import kotlin.io.path.div import kotlin.io.path.isRegularFile +import kotlin.io.path.toPath import kotlin.streams.asSequence import kotlin.test.* @@ -103,7 +103,7 @@ class CokoOptionGroupTest : KoinTest { val topTestDirResource = CokoOptionGroupTest::class.java.classLoader.getResource("cli-test-directory") assertNotNull(topTestDirResource) - topTestDir = Path(topTestDirResource.path) + topTestDir = topTestDirResource.toURI().toPath() val testDir3SpecResource = CokoOptionGroupTest::class @@ -111,7 +111,7 @@ class CokoOptionGroupTest : KoinTest { .classLoader .getResource("cli-test-directory/dir3-spec") assertNotNull(testDir3SpecResource) - testDir3Spec = Path(testDir3SpecResource.path) + testDir3Spec = testDir3SpecResource.toURI().toPath() allFiles = Files.walk(topTestDir).asSequence().filter { it.isRegularFile() }.toList() } diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/ValidationTest.kt b/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/ValidationTest.kt index ba2b58949..6eba531de 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/ValidationTest.kt +++ b/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/cli/ValidationTest.kt @@ -20,6 +20,7 @@ import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test import java.nio.file.Path import kotlin.io.path.Path +import kotlin.io.path.toPath import kotlin.test.assertContains import kotlin.test.assertEquals import kotlin.test.assertNotNull @@ -58,7 +59,7 @@ class ValidationTest { .classLoader .getResource("cli-test-directory/dir3-spec") assertNotNull(testDir3SpecResource) - testDir3Spec = Path(testDir3SpecResource.path) + testDir3Spec = testDir3SpecResource.toURI().toPath() } } } From d107805b196867d6b025df08c149929f063000bc Mon Sep 17 00:00:00 2001 From: Robert Haimerl Date: Fri, 5 Apr 2024 13:15:13 +0200 Subject: [PATCH 115/121] Fix upgrade workflow (#837) Co-authored-by: Florian Wendland --- .github/workflows/upgrade.yml | 62 ++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/.github/workflows/upgrade.yml b/.github/workflows/upgrade.yml index 9ca8ba62e..4f66dd235 100644 --- a/.github/workflows/upgrade.yml +++ b/.github/workflows/upgrade.yml @@ -1,9 +1,21 @@ -name: java-upgrade +# Portability testing across OSes and Java LTS versions +# --- +# Regularly check if we remain compatible with other OSes than Linux and if we +# could upgrade to newer LTS versions of Java. +# +# This acts more like an indicator if something is going to break. Our main +# target platform is Linux and we build against it during our regular workflow +# runs. Likewise, we're just testing if we could support newer LTS version of +# Java. We're still taking very deliberate decisions to upgrade to the next +# LTS version of Java. +name: 'Portability testing OSes and Java LTS versions' + +permissions: {} on: schedule: - # runs at 00:01 on the first every month - - cron: '1 0 1 * ?' + # runs at 5:00 on the first of every month + - cron: '0 5 1 * *' # can be triggered manually workflow_dispatch: @@ -12,22 +24,50 @@ jobs: strategy: fail-fast: false matrix: - # TODO: include JAVA SE 21 (LTS) on release - version: [ "19", "20" ] + java-lts: [ '17', '21' ] os: [ ubuntu-latest, macos-latest, windows-latest ] runs-on: ${{ matrix.os }} continue-on-error: true + name: 'Building with Java ${{ matrix.java-lts }} on ${{ matrix.os }}' steps: - name: Checkout uses: actions/checkout@v4 - - name: Setup Java ${{ matrix.version }} + - name: Setup Java ${{ matrix.java-lts }} uses: actions/setup-java@v4 with: distribution: "temurin" - java-version: ${{ matrix.version }} + java-version: ${{ matrix.java-lts }} - name: Setup Gradle uses: gradle/actions/setup-gradle@v3 - - name: Build - run: ./gradlew :codyze-cli:build -x check --parallel -Pversion=0.0.0 - # step-level 'continue-on-error' needed to mask a negative workflow result - continue-on-error: true + - name: Build and Test + id: build-and-test + run: ./gradlew build --parallel + - if: ${{ failure() }} + run: touch failure + - if: ${{ !cancelled() }} + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.os }}-${{ matrix.java-lts }}-failure + path: failure + if-no-files-found: ignore + retention-days: 1 + + process-failures: + if: ${{ !cancelled() }} + needs: [ build ] + runs-on: ubuntu-latest + steps: + - name: Get failures from matrix jobs + uses: actions/download-artifact@v4 + with: + path: all-failures + pattern: '*-failure' + merge-multiple: true + - name: Process failures + id: process-failures + run: test -f all-failures/failure && echo 'hasFails=true' >> "$GITHUB_OUTPUT" + - if: ${{ steps.process-failures.outputs.hasFails == 'true' }} + uses: actions/github-script@v3 + with: + script: | + core.setFailed('Some matrix jobs failed.') From 195fd0d084bfe970ffc8b3e4b299c2dabbe69d6e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 5 Apr 2024 13:32:04 +0200 Subject: [PATCH 116/121] Update actions/github-script action to v5 (#839) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Wendland, Florian --- .github/workflows/upgrade.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/upgrade.yml b/.github/workflows/upgrade.yml index 4f66dd235..0b0a79f6b 100644 --- a/.github/workflows/upgrade.yml +++ b/.github/workflows/upgrade.yml @@ -67,7 +67,7 @@ jobs: id: process-failures run: test -f all-failures/failure && echo 'hasFails=true' >> "$GITHUB_OUTPUT" - if: ${{ steps.process-failures.outputs.hasFails == 'true' }} - uses: actions/github-script@v3 + uses: actions/github-script@v7 with: script: | core.setFailed('Some matrix jobs failed.') From 1085851b84b6ffdb444dbecea8225eaffd43d87c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 10 Apr 2024 10:29:13 +0200 Subject: [PATCH 117/121] Update dependency io.github.oshai:kotlin-logging-jvm to v6.0.4 (#842) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dfde6d9db..efa2b5b6a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -28,7 +28,7 @@ cpg-language-java = { module = "de.fraunhofer.aisec:cpg-language-java", version. #cpg-analysis = { module = "com.github.Fraunhofer-AISEC.cpg:cpg-analysis", version.ref = "cpg"} #cpg-language-go = { module = "com.github.Fraunhofer-AISEC.cpg:cpg-language-go", version.ref = "cpg"} -kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version = "6.0.3" } +kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version = "6.0.4" } log4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version = "2.23.1"} clikt = { module = "com.github.ajalt.clikt:clikt", version = "4.3.0"} koin = { module = "io.insert-koin:koin-core", version.ref = "koin"} From d7e40f099605cb45cf396a8015604ca68d7c8e38 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 10 Apr 2024 10:44:58 +0200 Subject: [PATCH 118/121] Update koin.test to v3.5.4 (#841) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index efa2b5b6a..79686abd7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ kotlin = "1.9.23" cpg = "8.1.2" koin = "3.5.3" -koin-test = "3.5.3" +koin-test = "3.5.4" detekt = "1.23.6" spotless = "6.25.0" dokka = "1.9.20" From b1f0ae197aeed01fa15350f9d8d5d9910f8125a6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 10 Apr 2024 10:49:16 +0200 Subject: [PATCH 119/121] Update dependency io.insert-koin:koin-core to v3.5.4 (#840) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 79686abd7..a7cae7143 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] kotlin = "1.9.23" cpg = "8.1.2" -koin = "3.5.3" +koin = "3.5.4" koin-test = "3.5.4" detekt = "1.23.6" spotless = "6.25.0" From f0eef4660cc94e91a9b5571162ca1bc8ab51492b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 10 Apr 2024 11:01:46 +0200 Subject: [PATCH 120/121] Update dependency io.github.detekt.sarif4k:sarif4k to v0.6.0 (#831) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Wendland, Florian Co-authored-by: Robert Haimerl --- .../coko/dsl/CokoSarifBuilder.kt | 27 +- .../coko/dsl/CokoSarifBuilderTest.kt | 282 ++++++++++++++++++ .../resources/sarif/ruledefaults.codyze.kts | 3 + .../sarif/rulefulldescription.codyze.kts | 3 + .../test/resources/sarif/rulehelp.codyze.kts | 3 + .../resources/sarif/ruleseverity.codyze.kts | 3 + .../sarif/ruleshortdescription.codyze.kts | 3 + .../test/resources/sarif/ruletags.codyze.kts | 3 + gradle/libs.versions.toml | 2 +- 9 files changed, 316 insertions(+), 13 deletions(-) create mode 100644 codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoSarifBuilderTest.kt create mode 100644 codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/ruledefaults.codyze.kts create mode 100644 codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/rulefulldescription.codyze.kts create mode 100644 codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/rulehelp.codyze.kts create mode 100644 codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/ruleseverity.codyze.kts create mode 100644 codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/ruleshortdescription.codyze.kts create mode 100644 codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/ruletags.codyze.kts diff --git a/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoSarifBuilder.kt b/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoSarifBuilder.kt index d4f024a7a..a26cd24b4 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoSarifBuilder.kt +++ b/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoSarifBuilder.kt @@ -27,21 +27,24 @@ import kotlin.reflect.full.findAnnotation private fun CokoRule.toReportingDescriptor() = ReportingDescriptor( id = toString(), name = name, - shortDescription = findAnnotation()?.shortDescription?.let { desc -> - MultiformatMessageString( - text = desc - ) + shortDescription = findAnnotation()?.let { + MultiformatMessageString(text = it.shortDescription) + }, + fullDescription = findAnnotation()?.let { + MultiformatMessageString(text = it.description) + }, + defaultConfiguration = findAnnotation()?.let { + ReportingConfiguration(level = it.severity.toResultLevel()) }, - fullDescription = findAnnotation()?.description?.let { desc -> - MultiformatMessageString( - text = desc + help = findAnnotation()?.let { + MultiformatMessageString(text = it.help) + }, + properties = findAnnotation()?.let { + PropertyBag( + tags = it.tags.toSet(), + map = emptyMap() ) }, - defaultConfiguration = ReportingConfiguration(level = findAnnotation()?.severity?.toResultLevel()), - help = findAnnotation()?.help?.let { desc -> MultiformatMessageString(text = desc) }, - properties = PropertyBag( - tags = findAnnotation()?.tags?.toList() - ) // TODO: add precision, severity ) diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoSarifBuilderTest.kt b/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoSarifBuilderTest.kt new file mode 100644 index 000000000..c4851241d --- /dev/null +++ b/codyze-specification-languages/coko/coko-dsl/src/test/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoSarifBuilderTest.kt @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2024, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.fraunhofer.aisec.codyze.specificationLanguages.coko.dsl + +import de.fraunhofer.aisec.codyze.backends.cpg.CPGConfiguration +import de.fraunhofer.aisec.codyze.backends.cpg.coko.CokoCpgBackend +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.CokoRule +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.Evaluator +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.Severity +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.toResultLevel +import de.fraunhofer.aisec.codyze.specificationLanguages.coko.dsl.host.CokoExecutor +import de.fraunhofer.aisec.cpg.passes.EdgeCachePass +import de.fraunhofer.aisec.cpg.passes.UnreachableEOGPass +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +import kotlin.io.path.toPath +import kotlin.reflect.KParameter +import kotlin.reflect.KType +import kotlin.reflect.KTypeParameter +import kotlin.reflect.KVisibility + +class CokoSarifBuilderTest { + + private val cpgConfiguration = + CPGConfiguration( + source = emptyList(), + useUnityBuild = false, + debugParser = false, + disableCleanup = false, + codeInNodes = true, + matchCommentsToNodes = false, + processAnnotations = false, + failOnError = false, + useParallelFrontends = false, + defaultPasses = true, + additionalLanguages = setOf(), + symbols = mapOf(), + includeBlocklist = listOf(), + includePaths = listOf(), + includeAllowlist = listOf(), + loadIncludes = false, + passes = listOf(UnreachableEOGPass::class, EdgeCachePass::class), + ) + + private val cokoRulewithoutRuleAnnotation = object : CokoRule { + override val annotations: List + get() = emptyList() + override val name: String + get() = "norule" + + // remaining methods are not required in this test + override val isAbstract: Boolean + get() = throw UnsupportedOperationException("Not required for this test") + override val isExternal: Boolean + get() = throw UnsupportedOperationException("Not required for this test") + override val isFinal: Boolean + get() = throw UnsupportedOperationException("Not required for this test") + override val isInfix: Boolean + get() = throw UnsupportedOperationException("Not required for this test") + override val isInline: Boolean + get() = throw UnsupportedOperationException("Not required for this test") + override val isOpen: Boolean + get() = throw UnsupportedOperationException("Not required for this test") + override val isOperator: Boolean + get() = throw UnsupportedOperationException("Not required for this test") + override val isSuspend: Boolean + get() = throw UnsupportedOperationException("Not required for this test") + override val parameters: List + get() = throw UnsupportedOperationException("Not required for this test") + override val returnType: KType + get() = throw UnsupportedOperationException("Not required for this test") + override val typeParameters: List + get() = throw UnsupportedOperationException("Not required for this test") + override val visibility: KVisibility? + get() = throw UnsupportedOperationException("Not required for this test") + + override fun call(vararg args: Any?): Evaluator = + throw UnsupportedOperationException("Not required for this test") + + override fun callBy(args: Map): Evaluator = + throw UnsupportedOperationException("Not required for this test") + } + + @Test + fun `test empty rules list causes empty reportingDescriptors list`() { + val backend = CokoCpgBackend(cpgConfiguration) + val csb = CokoSarifBuilder(rules = emptyList(), backend = backend) + + assertTrue(csb.reportingDescriptors.isEmpty()) + } + + @Test + fun `test spec without rule annotation`() { + val backend = CokoCpgBackend(cpgConfiguration) + val csb = CokoSarifBuilder(rules = listOf(cokoRulewithoutRuleAnnotation), backend = backend) + + val reportingDescriptor = csb.reportingDescriptors.first() + assertNotNull(reportingDescriptor) + assertNull(reportingDescriptor.shortDescription) + assertNull(reportingDescriptor.fullDescription) + assertNull(reportingDescriptor.defaultConfiguration) + assertNull(reportingDescriptor.help) + assertNull(reportingDescriptor.properties) + } + + @Test + fun `test rule with default shortDescription`() { + val specFiles = listOfNotNull( + CokoSarifBuilderTest::class.java.classLoader + .getResource("sarif/ruledefaults.codyze.kts") + ).map { it.toURI().toPath() } + + val backend = CokoCpgBackend(cpgConfiguration) + val specEvaluator = CokoExecutor.compileScriptsIntoSpecEvaluator(backend = backend, specFiles = specFiles) + val csb = CokoSarifBuilder(rules = specEvaluator.rules, backend = backend) + + val shortDescription = csb.reportingDescriptors.first().shortDescription + assertNotNull(shortDescription) + assertTrue(shortDescription?.text!!.isEmpty()) + } + + @Test + fun `test rule with some shortDescription`() { + val specFiles = listOfNotNull( + CokoSarifBuilderTest::class.java.classLoader + .getResource("sarif/ruleshortdescription.codyze.kts") + ).map { it.toURI().toPath() } + + val backend = CokoCpgBackend(cpgConfiguration) + val specEvaluator = CokoExecutor.compileScriptsIntoSpecEvaluator(backend = backend, specFiles = specFiles) + val csb = CokoSarifBuilder(rules = specEvaluator.rules, backend = backend) + + val shortDescription = csb.reportingDescriptors.first().shortDescription + assertNotNull(shortDescription) + assertEquals(shortDescription?.text, "test") + } + + @Test + fun `test rule with default description`() { + val specFiles = listOfNotNull( + CokoSarifBuilderTest::class.java.classLoader + .getResource("sarif/ruledefaults.codyze.kts") + ).map { it.toURI().toPath() } + + val backend = CokoCpgBackend(cpgConfiguration) + val specEvaluator = CokoExecutor.compileScriptsIntoSpecEvaluator(backend = backend, specFiles = specFiles) + val csb = CokoSarifBuilder(rules = specEvaluator.rules, backend = backend) + + val fullDescription = csb.reportingDescriptors.first().fullDescription + assertNotNull(fullDescription) + assertTrue(fullDescription?.text!!.isEmpty()) + } + + @Test + fun `test rule with some description`() { + val specFiles = listOfNotNull( + CokoSarifBuilderTest::class.java.classLoader + .getResource("sarif/rulefulldescription.codyze.kts") + ).map { it.toURI().toPath() } + + val backend = CokoCpgBackend(cpgConfiguration) + val specEvaluator = CokoExecutor.compileScriptsIntoSpecEvaluator(backend = backend, specFiles = specFiles) + val csb = CokoSarifBuilder(rules = specEvaluator.rules, backend = backend) + + val fullDescription = csb.reportingDescriptors.first().fullDescription + assertNotNull(fullDescription) + assertEquals(fullDescription?.text, "some description") + } + + @Test + fun `test rule with default severity`() { + val specFiles = listOfNotNull( + CokoSarifBuilderTest::class.java.classLoader + .getResource("sarif/ruledefaults.codyze.kts") + ).map { it.toURI().toPath() } + + val backend = CokoCpgBackend(cpgConfiguration) + val specEvaluator = CokoExecutor.compileScriptsIntoSpecEvaluator(backend = backend, specFiles = specFiles) + val csb = CokoSarifBuilder(rules = specEvaluator.rules, backend = backend) + + val defaultConfiguration = csb.reportingDescriptors.first().defaultConfiguration + assertNotNull(defaultConfiguration) + + val level = defaultConfiguration?.level + assertNotNull(level) + assertTrue(level == Severity.WARNING.toResultLevel()) + } + + @Test + fun `test rule with some severity`() { + val specFiles = listOfNotNull( + CokoSarifBuilderTest::class.java.classLoader + .getResource("sarif/ruleseverity.codyze.kts") + ).map { it.toURI().toPath() } + + val backend = CokoCpgBackend(cpgConfiguration) + val specEvaluator = CokoExecutor.compileScriptsIntoSpecEvaluator(backend = backend, specFiles = specFiles) + val csb = CokoSarifBuilder(rules = specEvaluator.rules, backend = backend) + + val defaultConfiguration = csb.reportingDescriptors.first().defaultConfiguration + assertNotNull(defaultConfiguration) + + val level = defaultConfiguration?.level + assertNotNull(level) + assertTrue(level != Severity.WARNING.toResultLevel()) + } + + @Test + fun `test rule with default help`() { + val specFiles = listOfNotNull( + CokoSarifBuilderTest::class.java.classLoader + .getResource("sarif/ruledefaults.codyze.kts") + ).map { it.toURI().toPath() } + + val backend = CokoCpgBackend(cpgConfiguration) + val specEvaluator = CokoExecutor.compileScriptsIntoSpecEvaluator(backend = backend, specFiles = specFiles) + val csb = CokoSarifBuilder(rules = specEvaluator.rules, backend = backend) + + val help = csb.reportingDescriptors.first().help + assertNotNull(help) + assertTrue(help?.text!!.isEmpty()) + } + + @Test + fun `test rule with some help`() { + val specFiles = listOfNotNull( + CokoSarifBuilderTest::class.java.classLoader + .getResource("sarif/rulehelp.codyze.kts") + ).map { it.toURI().toPath() } + + val backend = CokoCpgBackend(cpgConfiguration) + val specEvaluator = CokoExecutor.compileScriptsIntoSpecEvaluator(backend = backend, specFiles = specFiles) + val csb = CokoSarifBuilder(rules = specEvaluator.rules, backend = backend) + + val help = csb.reportingDescriptors.first().help + assertNotNull(help) + assertEquals(help?.text, "some help") + } + + @Test + fun `test rule with default empty tags`() { + val specFiles = listOfNotNull( + CokoSarifBuilderTest::class.java.classLoader + .getResource("sarif/ruledefaults.codyze.kts") + ).map { it.toURI().toPath() } + + val backend = CokoCpgBackend(cpgConfiguration) + val specEvaluator = CokoExecutor.compileScriptsIntoSpecEvaluator(backend = backend, specFiles = specFiles) + val csb = CokoSarifBuilder(rules = specEvaluator.rules, backend = backend) + + val alternative = csb.reportingDescriptors.first().properties?.tags?.let { assertTrue(it.isEmpty()) } + assertNotNull(alternative) + } + + @Test + fun `test rules with some tags`() { + val specFiles = listOfNotNull( + CokoSarifBuilderTest::class.java.classLoader + .getResource("sarif/ruletags.codyze.kts") + ).map { it.toURI().toPath() } + + val backend = CokoCpgBackend(cpgConfiguration) + val specEvaluator = CokoExecutor.compileScriptsIntoSpecEvaluator(backend = backend, specFiles = specFiles) + val csb = CokoSarifBuilder(rules = specEvaluator.rules, backend = backend) + + val alternative = csb.reportingDescriptors.first().properties?.tags?.let { assertTrue(it.isNotEmpty()) } + assertNotNull(alternative) + } +} diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/ruledefaults.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/ruledefaults.codyze.kts new file mode 100644 index 000000000..11a51a51f --- /dev/null +++ b/codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/ruledefaults.codyze.kts @@ -0,0 +1,3 @@ +@Rule() +fun rule() = + Unit diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/rulefulldescription.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/rulefulldescription.codyze.kts new file mode 100644 index 000000000..6d768ee98 --- /dev/null +++ b/codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/rulefulldescription.codyze.kts @@ -0,0 +1,3 @@ +@Rule(description = "some description") +fun rule() = + Unit diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/rulehelp.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/rulehelp.codyze.kts new file mode 100644 index 000000000..453db8131 --- /dev/null +++ b/codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/rulehelp.codyze.kts @@ -0,0 +1,3 @@ +@Rule(help = "some help") +fun rule() = + Unit diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/ruleseverity.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/ruleseverity.codyze.kts new file mode 100644 index 000000000..47986abc1 --- /dev/null +++ b/codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/ruleseverity.codyze.kts @@ -0,0 +1,3 @@ +@Rule(severity = Severity.INFO) +fun rule() = + Unit diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/ruleshortdescription.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/ruleshortdescription.codyze.kts new file mode 100644 index 000000000..2ed066a0c --- /dev/null +++ b/codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/ruleshortdescription.codyze.kts @@ -0,0 +1,3 @@ +@Rule(shortDescription = "test") +fun rule() = + Unit diff --git a/codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/ruletags.codyze.kts b/codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/ruletags.codyze.kts new file mode 100644 index 000000000..e63928dbf --- /dev/null +++ b/codyze-specification-languages/coko/coko-dsl/src/test/resources/sarif/ruletags.codyze.kts @@ -0,0 +1,3 @@ +@Rule(tags = ["one", "two"]) +fun rule() = + Unit diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a7cae7143..4a2ce1731 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,7 +9,7 @@ dokka = "1.9.20" [libraries] -sarif4k = { module = "io.github.detekt.sarif4k:sarif4k", version = "0.5.0"} # The code can be found here: https://github.com/detekt/sarif4k. It was generated using https://app.quicktype.io/ +sarif4k = { module = "io.github.detekt.sarif4k:sarif4k", version = "0.6.0"} # The code can be found here: https://github.com/detekt/sarif4k. It was generated using https://app.quicktype.io/ kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.6.3"} kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin"} From 20e8d017620636af54dc5714280415d8d230bfc7 Mon Sep 17 00:00:00 2001 From: Selina Lin Date: Wed, 10 Apr 2024 12:48:43 +0200 Subject: [PATCH 121/121] Clean up (#763) Co-authored-by: Florian Wendland Co-authored-by: Robert Haimerl --- .../aisec/codyze/backends/cpg/CPGOptionGroup.kt | 6 +++--- .../{BaseCpgBackend.kt => BaseCpgBackendCommand.kt} | 2 +- .../{CokoCpgBackend.kt => CokoCpgBackendCommand.kt} | 2 +- .../backends/cpg/coko/dsl/ImplementationDsl.kt | 8 +++----- .../backends/cpg/coko/evaluators/OnlyEvaluator.kt | 6 ++---- .../de/fraunhofer/aisec/codyze/cli/KoinModules.kt | 8 ++++---- .../specificationLanguages/coko/core/dsl/Op.kt | 8 ++------ .../coko/core/modelling/OpComponents.kt | 12 +++--------- .../coko/dsl/CokoSarifBuilder.kt | 1 - 9 files changed, 19 insertions(+), 34 deletions(-) rename codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/cli/{BaseCpgBackend.kt => BaseCpgBackendCommand.kt} (96%) rename codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/cli/{CokoCpgBackend.kt => CokoCpgBackendCommand.kt} (96%) diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGOptionGroup.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGOptionGroup.kt index c77aa418b..5225dd76f 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGOptionGroup.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/CPGOptionGroup.kt @@ -17,8 +17,8 @@ package de.fraunhofer.aisec.codyze.backends.cpg import com.github.ajalt.clikt.parameters.options.* import com.github.ajalt.clikt.parameters.types.* -import de.fraunhofer.aisec.codyze.backends.cpg.cli.BaseCpgBackend -import de.fraunhofer.aisec.codyze.backends.cpg.cli.CokoCpgBackend +import de.fraunhofer.aisec.codyze.backends.cpg.cli.BaseCpgBackendCommand +import de.fraunhofer.aisec.codyze.backends.cpg.cli.CokoCpgBackendCommand import de.fraunhofer.aisec.codyze.core.backend.BackendOptions import de.fraunhofer.aisec.codyze.core.config.combineSources import de.fraunhofer.aisec.codyze.core.config.resolvePaths @@ -30,7 +30,7 @@ import kotlin.reflect.full.isSuperclassOf /** * Holds the common CLI options for all CPG based Codyze backends. - * Used in e.g., [BaseCpgBackend] and [CokoCpgBackend]. + * Used in e.g., [BaseCpgBackendCommand] and [CokoCpgBackendCommand]. */ @Suppress("UNUSED") class CPGOptionGroup : BackendOptions(helpName = "CPG Backend Options") { diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/cli/BaseCpgBackend.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/cli/BaseCpgBackendCommand.kt similarity index 96% rename from codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/cli/BaseCpgBackend.kt rename to codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/cli/BaseCpgBackendCommand.kt index c082e65e0..0427389e1 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/cli/BaseCpgBackend.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/cli/BaseCpgBackendCommand.kt @@ -24,7 +24,7 @@ import de.fraunhofer.aisec.codyze.core.backend.BackendCommand /** * The [CliktCommand] to add the plain cpg backend to the codyze-cli. */ -class BaseCpgBackend : BackendCommand("cpg") { +class BaseCpgBackendCommand : BackendCommand("cpg") { val backendOptions by CPGOptionGroup() override val backend = CPGBackend::class diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/cli/CokoCpgBackend.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/cli/CokoCpgBackendCommand.kt similarity index 96% rename from codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/cli/CokoCpgBackend.kt rename to codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/cli/CokoCpgBackendCommand.kt index b86ea74f6..d28262cba 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/cli/CokoCpgBackend.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/cli/CokoCpgBackendCommand.kt @@ -25,7 +25,7 @@ import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.CokoBackend /** * The [CliktCommand] to add the cokoCpg backend to the codyze-cli. */ -class CokoCpgBackend : BackendCommand("cokoCpg") { +class CokoCpgBackendCommand : BackendCommand("cokoCpg") { val backendOptions by CPGOptionGroup() override val backend = CokoBackend::class diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/dsl/ImplementationDsl.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/dsl/ImplementationDsl.kt index e67d37584..8423a9758 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/dsl/ImplementationDsl.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/dsl/ImplementationDsl.kt @@ -45,7 +45,7 @@ context(CokoBackend) fun Op.cpgGetAllNodes(): Nodes = when (this@Op) { is FunctionOp -> - this@Op.definitions.map { def -> this@CokoBackend.cpgCallFqn(def.fqn) }.flatten() + this@Op.definitions.flatMap { def -> this@CokoBackend.cpgCallFqn(def.fqn) } is ConstructorOp -> this@CokoBackend.cpgConstructor(this.classFqn) } @@ -58,7 +58,7 @@ fun Op.cpgGetNodes(): Nodes = when (this@Op) { is FunctionOp -> this@Op.definitions - .map { def -> + .flatMap { def -> this@CokoBackend.cpgCallFqn(def.fqn) { def.signatures.any { sig -> cpgSignature(*sig.parameters.toTypedArray()) && @@ -66,16 +66,14 @@ fun Op.cpgGetNodes(): Nodes = } } } - .flatten() is ConstructorOp -> this@Op.signatures - .map { sig -> + .flatMap { sig -> this@CokoBackend.cpgConstructor(this@Op.classFqn) { cpgSignature(*sig.parameters.toTypedArray()) && sig.unorderedParameters.all { it?.cpgFlowsTo(arguments) ?: false } } } - .flatten() } /** Returns a list of [ValueDeclaration]s with the matching name. */ diff --git a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OnlyEvaluator.kt b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OnlyEvaluator.kt index 6d6f33c95..bec29b8ec 100644 --- a/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OnlyEvaluator.kt +++ b/codyze-backends/cpg/src/main/kotlin/de/fraunhofer/aisec/codyze/backends/cpg/coko/evaluators/OnlyEvaluator.kt @@ -40,14 +40,12 @@ class OnlyEvaluator(val ops: List) : Evaluator { override fun evaluate(context: EvaluationContext): List { val correctNodes = - with(this@CokoCpgBackend) { ops.map { it.cpgGetNodes() } } - .flatten() + with(this@CokoCpgBackend) { ops.flatMap { it.cpgGetNodes() } } .toSet() val distinctOps = ops.toSet() val allNodes = - with(this@CokoCpgBackend) { distinctOps.map { it.cpgGetAllNodes() } } - .flatten() + with(this@CokoCpgBackend) { distinctOps.flatMap { it.cpgGetAllNodes() } } .toSet() // `correctNodes` is a subset of `allNodes` diff --git a/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/KoinModules.kt b/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/KoinModules.kt index c455e943e..dd72e0152 100644 --- a/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/KoinModules.kt +++ b/codyze-cli/src/main/kotlin/de/fraunhofer/aisec/codyze/cli/KoinModules.kt @@ -15,8 +15,8 @@ */ package de.fraunhofer.aisec.codyze.cli -import de.fraunhofer.aisec.codyze.backends.cpg.cli.BaseCpgBackend -import de.fraunhofer.aisec.codyze.backends.cpg.cli.CokoCpgBackend +import de.fraunhofer.aisec.codyze.backends.cpg.cli.BaseCpgBackendCommand +import de.fraunhofer.aisec.codyze.backends.cpg.cli.CokoCpgBackendCommand import de.fraunhofer.aisec.codyze.core.backend.Backend import de.fraunhofer.aisec.codyze.core.backend.BackendCommand import de.fraunhofer.aisec.codyze.core.executor.Executor @@ -32,8 +32,8 @@ import org.koin.dsl.module * Every [Backend] must provide [BackendCommand] to be selectable in the CLI. */ val backendCommands = module { - factoryOf(::BaseCpgBackend) bind(BackendCommand::class) - factoryOf(::CokoCpgBackend) bind(BackendCommand::class) + factoryOf(::BaseCpgBackendCommand) bind(BackendCommand::class) + factoryOf(::CokoCpgBackendCommand) bind(BackendCommand::class) } /** diff --git a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/Op.kt b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/Op.kt index bef09a401..8be75c30f 100644 --- a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/Op.kt +++ b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/dsl/Op.kt @@ -66,9 +66,7 @@ class FunctionOp internal constructor( other as FunctionOp - if (definitions != other.definitions) return false - - return true + return definitions == other.definitions } override fun hashCode(): Int { @@ -100,9 +98,7 @@ class ConstructorOp internal constructor( if (this === other) return true if (other !is ConstructorOp) return false - if (signatures != other.signatures) return false - - return true + return signatures == other.signatures } override fun hashCode(): Int { diff --git a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/modelling/OpComponents.kt b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/modelling/OpComponents.kt index 38a8f41b9..8e91964a7 100644 --- a/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/modelling/OpComponents.kt +++ b/codyze-specification-languages/coko/coko-core/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/core/modelling/OpComponents.kt @@ -39,9 +39,7 @@ class ParameterGroup { other as ParameterGroup - if (parameters != other.parameters) return false - - return true + return parameters == other.parameters } override fun hashCode(): Int { @@ -74,9 +72,7 @@ class Definition(val fqn: String) { other as Definition if (fqn != other.fqn) return false - if (signatures != other.signatures) return false - - return true + return signatures == other.signatures } override fun hashCode(): Int { @@ -147,9 +143,7 @@ class Signature { other as Signature if (parameters != other.parameters) return false - if (unorderedParameters != other.unorderedParameters) return false - - return true + return unorderedParameters == other.unorderedParameters } override fun hashCode(): Int { diff --git a/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoSarifBuilder.kt b/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoSarifBuilder.kt index a26cd24b4..668f82518 100644 --- a/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoSarifBuilder.kt +++ b/codyze-specification-languages/coko/coko-dsl/src/main/kotlin/de/fraunhofer/aisec/codyze/specificationLanguages/coko/dsl/CokoSarifBuilder.kt @@ -45,7 +45,6 @@ private fun CokoRule.toReportingDescriptor() = ReportingDescriptor( map = emptyMap() ) }, - // TODO: add precision, severity ) class CokoSarifBuilder(val rules: List, val backend: Backend) {