diff --git a/README.md b/README.md index 7c5b791cb32..185ec6de983 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ In order to get familiar with the graph itself, you can use the subproject [cpg- ### As Library The most recent version is being published to Maven central and can be used as a simple dependency, either using Maven or Gradle. Since Eclipse CDT is not published on maven central, it is necessary to add a repository with a custom layout to find the released CDT files. For example, using Gradle's Kotlin syntax: -``` +```kotlin repositories { ivy { setUrl("https://download.eclipse.org/tools/cdt/releases/11.0/cdt-11.0.0/plugins") @@ -121,7 +121,7 @@ Instead of manually editing the `gradle.properties` file, you can also use the ` #### Golang -In the case of Golang, the necessary native code can be found in the `src/main/golang` folder of the `cpg-language-go` submodule. Gradle should automatically find JNI headers and stores the finished library in the `src/main/golang` folder. This currently only works for Linux and macOS. In order to use it in an external project, the resulting library needs to be placed somewhere in `java.library.path`. +In the case of Golang, the necessary native code can be found in the `src/main/golang` folder of the `cpg-language-go` submodule. Gradle should automatically store the finished library in the `src/main/golang` folder. This currently only works for Linux and macOS. #### Python @@ -145,7 +145,6 @@ Through the `JepSingleton`, the CPG library will look for well known paths on Li For parsing TypeScript, the necessary NodeJS-based code can be found in the `src/main/nodejs` directory of the `cpg-language-typescript` submodule. Gradle should build the script automatically, provided NodeJS (>=16) is installed. The bundles script will be placed inside the jar's resources and should work out of the box. - ### Code Style We use [Google Java Style](https://github.com/google/google-java-format) as a formatting. Please install the appropriate plugin for your IDE, such as the [google-java-format IntelliJ plugin](https://plugins.jetbrains.com/plugin/8527-google-java-format) or [google-java-format Eclipse plugin](https://github.com/google/google-java-format/releases/download/google-java-format-1.6/google-java-format-eclipse-plugin_1.6.0.jar). @@ -183,8 +182,7 @@ The following authors have contributed to this project (in alphabetical order): ## Contributing -We are currently discussing the implementation of a Contributor License Agreement (CLA). Unfortunately, -we cannot merge external pull requests until this issue is resolved. +Before accepting external contributions, you need to sign our [CLA](https://cla-assistant.io/Fraunhofer-AISEC/cpg). Our CLA assistent will check, whether you already signed the CLA when you open your first pull request. ## Further reading diff --git a/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluator.kt b/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluator.kt index 04ddcdd46c7..4c3c2a14c72 100644 --- a/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluator.kt +++ b/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluator.kt @@ -163,6 +163,11 @@ open class ValueEvaluator( "/=" -> handleDiv(lhsValue, rhsValue, expr) "*", "*=" -> handleTimes(lhsValue, rhsValue, expr) + "<<" -> handleShiftLeft(lhsValue, rhsValue, expr) + ">>" -> handleShiftRight(lhsValue, rhsValue, expr) + "&" -> handleBitwiseAnd(lhsValue, rhsValue, expr) + "|" -> handleBitwiseOr(lhsValue, rhsValue, expr) + "^" -> handleBitwiseXor(lhsValue, rhsValue, expr) ">" -> handleGreater(lhsValue, rhsValue, expr) ">=" -> handleGEq(lhsValue, rhsValue, expr) "<" -> handleLess(lhsValue, rhsValue, expr) @@ -202,6 +207,51 @@ open class ValueEvaluator( } } + private fun handleShiftLeft(lhsValue: Any?, rhsValue: Any?, expr: Expression?): Any? { + return when { + // right side must always be an int + lhsValue is Int && rhsValue is Int -> lhsValue shl rhsValue + lhsValue is Long && rhsValue is Int -> lhsValue shl rhsValue + else -> cannotEvaluate(expr, this) + } + } + + private fun handleShiftRight(lhsValue: Any?, rhsValue: Any?, expr: Expression?): Any? { + return when { + // right side must always be an int + lhsValue is Int && rhsValue is Int -> lhsValue shr rhsValue + lhsValue is Long && rhsValue is Int -> lhsValue shr rhsValue + else -> cannotEvaluate(expr, this) + } + } + + private fun handleBitwiseAnd(lhsValue: Any?, rhsValue: Any?, expr: Expression?): Any? { + return when { + // left and right must be equal and only long and int are supported + lhsValue is Int && rhsValue is Int -> lhsValue and rhsValue + lhsValue is Long && rhsValue is Long -> lhsValue and rhsValue + else -> cannotEvaluate(expr, this) + } + } + + private fun handleBitwiseOr(lhsValue: Any?, rhsValue: Any?, expr: Expression?): Any? { + return when { + // left and right must be equal and only long and int are supported + lhsValue is Int && rhsValue is Int -> lhsValue or rhsValue + lhsValue is Long && rhsValue is Long -> lhsValue or rhsValue + else -> cannotEvaluate(expr, this) + } + } + + private fun handleBitwiseXor(lhsValue: Any?, rhsValue: Any?, expr: Expression?): Any? { + return when { + // left and right must be equal and only long and int are supported + lhsValue is Int && rhsValue is Int -> lhsValue xor rhsValue + lhsValue is Long && rhsValue is Long -> lhsValue xor rhsValue + else -> cannotEvaluate(expr, this) + } + } + private fun handleGreater(lhsValue: Any?, rhsValue: Any?, expr: Expression?): Any? { return if (lhsValue is Number && rhsValue is Number) { lhsValue.compareTo(rhsValue) > 0 diff --git a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluatorTest.kt b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluatorTest.kt index 91d8deb9b08..500144e2ab6 100644 --- a/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluatorTest.kt +++ b/cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/analysis/ValueEvaluatorTest.kt @@ -639,6 +639,106 @@ class ValueEvaluatorTest { } } + @Test + fun testHandleShiftLeft() { + with(TestHandler(TestLanguageFrontend())) { + val binOp = newBinaryOperator("<<") + // Int.plus + binOp.lhs = newLiteral(3, primitiveType("int")) + binOp.rhs = newLiteral(2, primitiveType("int")) + assertEquals(12, ValueEvaluator().evaluate(binOp)) + + // Long.plus + binOp.lhs = newLiteral(3L, primitiveType("long")) + binOp.rhs = newLiteral(2, primitiveType("int")) + assertEquals(12L, ValueEvaluator().evaluate(binOp)) + + binOp.lhs = newLiteral("Hello", primitiveType("string")) + binOp.rhs = newLiteral(" world", primitiveType("string")) + assertEquals("{<<}", ValueEvaluator().evaluate(binOp)) + } + } + + @Test + fun testHandleShiftRight() { + with(TestHandler(TestLanguageFrontend())) { + val binOp = newBinaryOperator(">>") + // Int.plus + binOp.lhs = newLiteral(3, primitiveType("int")) + binOp.rhs = newLiteral(2, primitiveType("int")) + assertEquals(0, ValueEvaluator().evaluate(binOp)) + + // Long.plus + binOp.lhs = newLiteral(3L, primitiveType("long")) + binOp.rhs = newLiteral(2, primitiveType("int")) + assertEquals(0L, ValueEvaluator().evaluate(binOp)) + + binOp.lhs = newLiteral("Hello", primitiveType("string")) + binOp.rhs = newLiteral(" world", primitiveType("string")) + assertEquals("{>>}", ValueEvaluator().evaluate(binOp)) + } + } + + @Test + fun testHandleBitwiseAnd() { + with(TestHandler(TestLanguageFrontend())) { + val binOp = newBinaryOperator("&") + // Int.plus + binOp.lhs = newLiteral(3, primitiveType("int")) + binOp.rhs = newLiteral(2, primitiveType("int")) + assertEquals(2, ValueEvaluator().evaluate(binOp)) + + // Long.plus + binOp.lhs = newLiteral(3L, primitiveType("long")) + binOp.rhs = newLiteral(2L, primitiveType("long")) + assertEquals(2L, ValueEvaluator().evaluate(binOp)) + + binOp.lhs = newLiteral("Hello", primitiveType("string")) + binOp.rhs = newLiteral(" world", primitiveType("string")) + assertEquals("{&}", ValueEvaluator().evaluate(binOp)) + } + } + + @Test + fun testHandleBitwiseOr() { + with(TestHandler(TestLanguageFrontend())) { + val binOp = newBinaryOperator("|") + // Int.plus + binOp.lhs = newLiteral(3, primitiveType("int")) + binOp.rhs = newLiteral(2, primitiveType("int")) + assertEquals(3, ValueEvaluator().evaluate(binOp)) + + // Long.plus + binOp.lhs = newLiteral(3L, primitiveType("long")) + binOp.rhs = newLiteral(2L, primitiveType("long")) + assertEquals(3L, ValueEvaluator().evaluate(binOp)) + + binOp.lhs = newLiteral("Hello", primitiveType("string")) + binOp.rhs = newLiteral(" world", primitiveType("string")) + assertEquals("{|}", ValueEvaluator().evaluate(binOp)) + } + } + + @Test + fun testHandleBitwiseXor() { + with(TestHandler(TestLanguageFrontend())) { + val binOp = newBinaryOperator("^") + // Int.plus + binOp.lhs = newLiteral(3, primitiveType("int")) + binOp.rhs = newLiteral(2, primitiveType("int")) + assertEquals(1, ValueEvaluator().evaluate(binOp)) + + // Long.plus + binOp.lhs = newLiteral(3L, primitiveType("long")) + binOp.rhs = newLiteral(2L, primitiveType("long")) + assertEquals(1L, ValueEvaluator().evaluate(binOp)) + + binOp.lhs = newLiteral("Hello", primitiveType("string")) + binOp.rhs = newLiteral(" world", primitiveType("string")) + assertEquals("{^}", ValueEvaluator().evaluate(binOp)) + } + } + @Test fun testHandleUnary() { with(TestHandler(TestLanguageFrontend())) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt index 695b7332bc3..75aadb26443 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt @@ -164,6 +164,9 @@ abstract class Language> : Node() { "-", "*", "/" -> arithmeticOpTypePropagation(operation.lhs.type, operation.rhs.type) + "&", + "|", + "^", "<<", ">>" -> if (operation.lhs.type.isPrimitive && operation.rhs.type.isPrimitive) { diff --git a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/DeclarationTest.kt b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/DeclarationTest.kt index 62a1cb78db6..6a3be0a5646 100644 --- a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/DeclarationTest.kt +++ b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/DeclarationTest.kt @@ -303,10 +303,16 @@ class DeclarationTest { with(tu) { val values = mapOf( + "zeroShift" to Pair(0, objectType("int")), + "zeroAnd" to Pair(0, objectType("int")), "one" to Pair(1, objectType("p.custom")), "oneAsWell" to Pair(1, objectType("p.custom")), + "oneShift" to Pair(1, primitiveType("int")), "two" to Pair(2, primitiveType("int")), + "twoShift" to Pair(2, primitiveType("int")), "three" to Pair(3, primitiveType("int")), + "threeOr" to Pair(3, primitiveType("int")), + "threeXor" to Pair(3, primitiveType("int")), "four" to Pair(4, primitiveType("int")), "tenAsWell" to Pair(10, primitiveType("int")), "five" to Pair(5, primitiveType("int")), diff --git a/cpg-language-go/src/test/resources/golang/const.go b/cpg-language-go/src/test/resources/golang/const.go index bd87af22061..3338dbfa3c3 100644 --- a/cpg-language-go/src/test/resources/golang/const.go +++ b/cpg-language-go/src/test/resources/golang/const.go @@ -24,3 +24,12 @@ const ( fiveAsWell = 5 + iota*100 onehundredandfive ) + +const ( + oneShift = 1 << iota + twoShift + zeroShift = 1 >> iota + zeroAnd = oneShift & twoShift + threeOr = oneShift | twoShift + threeXor = oneShift ^ twoShift +) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 65b269ff084..636b0ce83b4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] kotlin = "1.9.0" neo4j = "4.0.6" -log4j = "2.20.0" +log4j = "2.21.0" sonarqube = "4.4.0.3356" spotless = "6.22.0" nexus-publish = "1.3.0"