diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 441e0b0a3..053accc53 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,9 +28,20 @@ jobs: gradle-version: 7.6.1 arguments: | publish + -PincludeDokka=true -PsemVer=${{inputs.semVer}} -Pactor=${{ secrets.MAVEN_CENTRAL_LOGIN }} -Ptoken=${{ secrets.MAVEN_CENTRAL_TOKEN }} -PgpgKey="${{ secrets.OSSRH_GPG_SECRET_KEY }}" -PgpgPassphrase=${{ secrets.OSSRH_GPG_SECRET_PASSPHRASE }} - -PrepoUrl=https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ \ No newline at end of file + -PrepoUrl=https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ + - name: Upload release artifacts + uses: softprops/action-gh-release@v1 + with: + draft: true + files: | + jacodb-api/build/libs/jacodb-api-${{inputs.semVer}}.jar + jacodb-analysis/build/libs/jacodb-analysis-${{inputs.semVer}}.jar + jacodb-core/build/libs/jacodb-core-${{inputs.semVer}}.jar + jacodb-cli/build/libs/jacodb-cli-${{inputs.semVer}}.jar + jacodb-examples/build/libs/jacodb-examples-${{inputs.semVer}}.jar diff --git a/build.gradle.kts b/build.gradle.kts index 54a9c2646..073a480da 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,6 +4,7 @@ val kotlinVersion: String by rootProject val coroutinesVersion: String by rootProject val junit5Version: String by project val semVer: String? by project +val includeDokka: String? by project group = "org.jacodb" @@ -125,6 +126,7 @@ allprojects { archiveClassifier.set("javadoc") } + val sourcesJar by creating(Jar::class) { archiveClassifier.set("sources") from(sourceSets.getByName("main").kotlin.srcDirs) @@ -132,7 +134,9 @@ allprojects { artifacts { archives(sourcesJar) - archives(dokkaJavadocJar) + if (includeDokka != null) { + archives(dokkaJavadocJar) + } } } @@ -170,7 +174,7 @@ if (!repoUrl.isNullOrEmpty()) { listOf( project(":jacodb-api"), project(":jacodb-core"), -// project(":jacodb-analysis"), + project(":jacodb-analysis"), ) ) { publishing { diff --git a/docs/badges/branches.svg b/docs/badges/branches.svg index 3935ab969..d65dc63b9 100644 --- a/docs/badges/branches.svg +++ b/docs/badges/branches.svg @@ -1 +1 @@ -branches68.3% \ No newline at end of file +branches68.1% \ No newline at end of file diff --git a/docs/badges/coverage-summary.json b/docs/badges/coverage-summary.json index 483ff35cc..90b206ab4 100644 --- a/docs/badges/coverage-summary.json +++ b/docs/badges/coverage-summary.json @@ -1 +1 @@ -{"branches": 68.34195614683419, "coverage": 70.38198957259596} \ No newline at end of file +{"branches": 68.14246846401187, "coverage": 69.80196523053665} \ No newline at end of file diff --git a/docs/badges/jacoco.svg b/docs/badges/jacoco.svg index 31127ea7b..a1475b5b7 100644 --- a/docs/badges/jacoco.svg +++ b/docs/badges/jacoco.svg @@ -1 +1 @@ -coverage70.3% \ No newline at end of file +coverage69.8% \ No newline at end of file diff --git a/jacodb-analysis/build.gradle.kts b/jacodb-analysis/build.gradle.kts index 094ccc467..759aee1ae 100644 --- a/jacodb-analysis/build.gradle.kts +++ b/jacodb-analysis/build.gradle.kts @@ -11,14 +11,13 @@ dependencies { api(project(":jacodb-api")) testImplementation(testFixtures(project(":jacodb-core"))) - testFixturesImplementation(project(":jacodb-api")) - testFixturesImplementation("javax.servlet:servlet-api:2.5") - testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.2") - testImplementation(files("src/testFixtures/resources/juliet.jar")) - testImplementation(files("src/testFixtures/resources/pointerbench.jar")) - testImplementation("joda-time:joda-time:2.12.5") + testImplementation(project(":jacodb-api")) + testImplementation(group = "javax.servlet", name = "servlet-api", version = "2.5") + testImplementation(group = "org.junit.jupiter", name = "junit-jupiter-params", version = "5.9.2") + testImplementation(files("src/test/resources/juliet.jar")) + testImplementation(files("src/test/resources/pointerbench.jar")) + testImplementation(group = "joda-time", name = "joda-time", version = "2.12.5") - implementation("org.jetbrains.kotlinx:kotlinx-cli:0.3.5") - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1") + implementation(group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version = "1.4.1") implementation(group = "io.github.microutils", name = "kotlin-logging", version = "1.8.3") } \ No newline at end of file diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/AnalysisMain.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/AnalysisMain.kt index 4bf9b09c6..b9219abc2 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/AnalysisMain.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/AnalysisMain.kt @@ -15,16 +15,8 @@ */ package org.jacodb.analysis -import kotlinx.cli.ArgParser -import kotlinx.cli.ArgType -import kotlinx.cli.default -import kotlinx.cli.required import kotlinx.coroutines.runBlocking import kotlinx.serialization.Serializable -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.encodeToStream -import mu.KLogging import org.jacodb.analysis.analyzers.AliasAnalyzer import org.jacodb.analysis.analyzers.NpeAnalyzer import org.jacodb.analysis.analyzers.TaintAnalysisNode @@ -41,12 +33,7 @@ import org.jacodb.api.JcClasspath import org.jacodb.api.JcMethod import org.jacodb.api.analysis.JcApplicationGraph import org.jacodb.api.cfg.JcInst -import org.jacodb.api.ext.findClass -import org.jacodb.impl.features.InMemoryHierarchy -import org.jacodb.impl.features.Usages import org.jacodb.impl.features.usagesExt -import org.jacodb.impl.jacodb -import java.io.File import java.util.* @Serializable @@ -95,38 +82,40 @@ class UnusedVariableAnalysisFactory : AnalysisEngineFactory { } override val name: String - get() = "Jacodb-Unused-Variable" + get() = "unused-variable" } abstract class FlowDroidFactory : AnalysisEngineFactory { - protected abstract fun getAnalyzer(graph: JcApplicationGraph): Analyzer + protected abstract val JcApplicationGraph.analyzer: Analyzer override fun createAnalysisEngine( graph: JcApplicationGraph, points2Engine: Points2Engine, ): AnalysisEngine { - val analyzer = getAnalyzer(graph) + val analyzer = graph.analyzer return TaintAnalysisWithPointsTo(graph, analyzer, points2Engine) } override val name: String - get() = "JacoDB-FlowDroid" + get() = "flow-droid" } class NPEAnalysisFactory : FlowDroidFactory() { - override fun getAnalyzer(graph: JcApplicationGraph): Analyzer { - return NpeAnalyzer(graph) - } + override val JcApplicationGraph.analyzer: Analyzer + get() { + return NpeAnalyzer(this) + } } class AliasAnalysisFactory( private val generates: (JcInst) -> List, private val isSink: (JcInst, DomainFact) -> Boolean, ) : FlowDroidFactory() { - override fun getAnalyzer(graph: JcApplicationGraph): Analyzer { - return AliasAnalyzer(graph, generates, isSink) - } + override val JcApplicationGraph.analyzer: Analyzer + get() { + return AliasAnalyzer(this, generates, isSink) + } } interface Points2EngineFactory : Factory { @@ -140,7 +129,7 @@ interface GraphFactory : Factory { class JcSimplifiedGraphFactory( private val bannedPackagePrefixes: List? = null ) : GraphFactory { - override val name: String = "JacoDB-graph simplified for IFDS" + override val name: String = "ifds-simplification" override fun createGraph( classpath: JcClasspath @@ -154,7 +143,7 @@ class JcSimplifiedGraphFactory( } } -class JcNaivePoints2EngineFactory : Points2EngineFactory { +object JcNaivePoints2EngineFactory : Points2EngineFactory { override fun createPoints2Engine( graph: JcApplicationGraph, ): Points2Engine { @@ -163,147 +152,10 @@ class JcNaivePoints2EngineFactory : Points2EngineFactory { } override val name: String - get() = "JacoDB-P2-Naive" + get() = "naive-p2" } inline fun loadFactories(): List { assert(T::class.java != Factory::class.java) return ServiceLoader.load(T::class.java).toList() } - -private inline fun factoryChoice(): ArgType.Choice { - val factories = loadFactories() - val nameToFactory = { requiredFactoryName: String -> factories.single { it.name == requiredFactoryName } } - val factoryToName = { factory: T -> factory.name } - - return ArgType.Choice(factories, nameToFactory, factoryToName) -} - -private val logger = object : KLogging() {}.logger - - -class AnalysisMain { - fun run(args: List) = main(args.toTypedArray()) -} - -fun loadAnalysisEngineFactoriesByConfig(config: AnalysisConfig): List { - return config.analyses.mapNotNull { (analysis, _) -> - when (analysis) { - "NPE" -> NPEAnalysisFactory() - "Unused" -> UnusedVariableAnalysisFactory() - else -> { - logger.error { "Unknown analysis type: $analysis" } - null - } - } - } -} - -fun main(args: Array) { - val parser = ArgParser("taint-analysis") - val configFilePath by parser.option( - ArgType.String, - fullName = "analysisConf", - shortName = "a", - description = "File with analysis configuration in JSON format" - ).required() - val cacheDirPath by parser.option( - ArgType.String, - fullName = "cachedir", - shortName = "c", - description = "Directory with caches for analysis. All parent directories will be created if not exists. Directory will be created if not exists. Directory must be empty." - ).required() - val startClasses by parser.option( - ArgType.String, - fullName = "start", - shortName = "s", - description = "classes from which to start the analysis" - ).required() - val outputPath by parser.option( - ArgType.String, - fullName = "output", - shortName = "o", - description = "File where analysis report will be written. All parent directories will be created if not exists. File will be created if not exists. Existing file will be overwritten." - ).default("report.txt") // TODO: create SARIF here - val graphFactory by parser.option( - factoryChoice(), - fullName = "graph-type", - shortName = "g", - description = "Type of code graph to be used by analysis." - ).default(JcSimplifiedGraphFactory()) - val points2Factory by parser.option( - factoryChoice(), - fullName = "points2", - shortName = "p2", - description = "Type of points-to engine." - ).default(JcNaivePoints2EngineFactory()) - val classpath by parser.option( - ArgType.String, - fullName = "classpath", - shortName = "cp", - description = "Classpath for analysis. Used by JacoDB." - ).default(System.getProperty("java.class.path")) - - parser.parse(args) - - val outputFile = File(outputPath) - - if (outputFile.exists() && outputFile.isDirectory) { - throw IllegalArgumentException("Provided path for output file is directory, please provide correct path") - } else if (outputFile.exists()) { - logger.info { "Output file $outputFile already exists, results will be overwritten" } - } - - val cacheDir = File(cacheDirPath) - - if (!cacheDir.exists()) { - cacheDir.mkdirs() - } - - if (!cacheDir.isDirectory) { - throw IllegalArgumentException("Provided path to cache directory is not directory") - } - - val configFile = File(configFilePath) - if (!configFile.isFile) { - throw IllegalArgumentException("Can't find provided config file $configFilePath") - } - val config = Json.decodeFromString(configFile.readText()) - - val classpathAsFiles = classpath.split(File.pathSeparatorChar).sorted().map { File(it) } - val classpathHash = classpath.hashCode() - val persistentPath = cacheDir.resolve("jacodb-for-$classpathHash") - - val cp = runBlocking { - val jacodb = jacodb { - loadByteCode(classpathAsFiles) - persistent(persistentPath.absolutePath) - installFeatures(InMemoryHierarchy, Usages) - } - jacodb.classpath(classpathAsFiles) - } - - val graph = graphFactory.createGraph(cp) - val points2Engine = points2Factory.createPoints2Engine(graph) - val startJcClasses = startClasses.split(";").map { cp.findClass(it) } - - val analysisEngines = loadAnalysisEngineFactoriesByConfig(config).map { - it.createAnalysisEngine(graph, points2Engine) - } - - val analysisResults = analysisEngines.map { engine -> - startJcClasses.forEach { clazz -> - clazz.declaredMethods.forEach { - engine.addStart(it) - } - } - engine.analyze() - } - - val mergedResult = DumpableAnalysisResult(analysisResults.flatMap { it.foundVulnerabilities }) - - val json = Json { prettyPrint = true } - outputFile.outputStream().use { fileOutputStream -> - json.encodeToStream(mergedResult, fileOutputStream) - } -} \ No newline at end of file diff --git a/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/AliasAnalysisTest.kt b/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/AliasAnalysisTest.kt index ceaac46cc..7ef13da05 100644 --- a/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/AliasAnalysisTest.kt +++ b/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/AliasAnalysisTest.kt @@ -146,7 +146,7 @@ class AliasAnalysisTest : BaseTest() { .plus("pointerbench.benchmark.internal") val graph = JcSimplifiedGraphFactory(bannedPackagePrefixes).createGraph(cp) - val points2Engine = JcNaivePoints2EngineFactory().createPoints2Engine(graph) + val points2Engine = JcNaivePoints2EngineFactory.createPoints2Engine(graph) val factory = AliasAnalysisFactory(::generates, ::isSink) val ifds = factory.createAnalysisEngine(graph, points2Engine) ifds.addStart(method) diff --git a/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/JodaDateTimeAnalysisTest.kt b/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/JodaDateTimeAnalysisTest.kt index 48bb5f2fd..7555e8cfc 100644 --- a/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/JodaDateTimeAnalysisTest.kt +++ b/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/JodaDateTimeAnalysisTest.kt @@ -38,7 +38,7 @@ class JodaDateTimeAnalysisTest : BaseTest() { val clazz = cp.findClass() val graph = JcSimplifiedGraphFactory().createGraph(cp) - val points2Engine = JcNaivePoints2EngineFactory().createPoints2Engine(graph) + val points2Engine = JcNaivePoints2EngineFactory.createPoints2Engine(graph) val ifds = factory.createAnalysisEngine(graph, points2Engine) clazz.declaredMethods .forEach { ifds.addStart(it) } diff --git a/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/NpeAnalysisTest.kt b/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/NpeAnalysisTest.kt index 40738de7f..ea857e173 100644 --- a/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/NpeAnalysisTest.kt +++ b/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/NpeAnalysisTest.kt @@ -24,7 +24,6 @@ import org.jacodb.analysis.NPEAnalysisFactory import org.jacodb.analysis.VulnerabilityInstance import org.jacodb.analysis.analyzers.NpeAnalyzer import org.jacodb.analysis.graph.JcApplicationGraphImpl -import org.jacodb.analysis.samples.NPEExamples import org.jacodb.api.JcClassOrInterface import org.jacodb.api.JcMethod import org.jacodb.api.ext.constructors @@ -37,6 +36,7 @@ import org.jacodb.impl.features.usagesExt import org.jacodb.testing.BaseTest import org.jacodb.testing.WithDB import org.jacodb.testing.allClasspath +import org.jacodb.testing.analysis.NPEExamples import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest @@ -273,7 +273,7 @@ class NpeAnalysisTest : BaseTest() { private fun findNpeSources(method: JcMethod): List { val graph = JcSimplifiedGraphFactory().createGraph(cp) - val points2Engine = JcNaivePoints2EngineFactory().createPoints2Engine(graph) + val points2Engine = JcNaivePoints2EngineFactory.createPoints2Engine(graph) val ifds = NPEAnalysisFactory().createAnalysisEngine(graph, points2Engine) ifds.addStart(method) val result = ifds.analyze() diff --git a/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/UnusedVariableTest.kt b/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/UnusedVariableTest.kt index 5680858be..2b4be562e 100644 --- a/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/UnusedVariableTest.kt +++ b/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/UnusedVariableTest.kt @@ -110,7 +110,7 @@ class UnusedVariableTest : BaseTest() { private fun findUnusedVariables(method: JcMethod): List { val graph = JcSimplifiedGraphFactory().createGraph(cp) - val points2Engine = JcNaivePoints2EngineFactory().createPoints2Engine(graph) + val points2Engine = JcNaivePoints2EngineFactory.createPoints2Engine(graph) val ifds = UnusedVariableAnalysisFactory().createAnalysisEngine(graph, points2Engine) ifds.addStart(method) val result = ifds.analyze() diff --git a/jacodb-analysis/src/testFixtures/resources/juliet.jar b/jacodb-analysis/src/test/resources/juliet.jar similarity index 100% rename from jacodb-analysis/src/testFixtures/resources/juliet.jar rename to jacodb-analysis/src/test/resources/juliet.jar diff --git a/jacodb-analysis/src/testFixtures/resources/pointerbench.jar b/jacodb-analysis/src/test/resources/pointerbench.jar similarity index 100% rename from jacodb-analysis/src/testFixtures/resources/pointerbench.jar rename to jacodb-analysis/src/test/resources/pointerbench.jar diff --git a/jacodb-cli/build.gradle.kts b/jacodb-cli/build.gradle.kts new file mode 100644 index 000000000..67b8c7c38 --- /dev/null +++ b/jacodb-cli/build.gradle.kts @@ -0,0 +1,13 @@ +val kotlinVersion: String by rootProject +val coroutinesVersion: String by rootProject + +dependencies { + api(project(":jacodb-core")) + api(project(":jacodb-analysis")) + api(project(":jacodb-api")) + + testImplementation(testFixtures(project(":jacodb-core"))) + implementation(group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version = "1.4.1") + implementation(group = "org.jetbrains.kotlinx", name = "kotlinx-cli", version = "0.3.5") + implementation(group = "io.github.microutils", name = "kotlin-logging", version = "1.8.3") +} \ No newline at end of file diff --git a/jacodb-cli/src/main/kotlin/org/jacodb/cli/main.kt b/jacodb-cli/src/main/kotlin/org/jacodb/cli/main.kt new file mode 100644 index 000000000..8f7f46527 --- /dev/null +++ b/jacodb-cli/src/main/kotlin/org/jacodb/cli/main.kt @@ -0,0 +1,173 @@ +/* + * Copyright 2022 UnitTestBot contributors (utbot.org) + *

+ * 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 org.jacodb.cli + +import kotlinx.cli.ArgParser +import kotlinx.cli.ArgType +import kotlinx.cli.default +import kotlinx.cli.required +import kotlinx.coroutines.runBlocking +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.encodeToStream +import mu.KLogging +import org.jacodb.analysis.AnalysisConfig +import org.jacodb.analysis.AnalysisEngineFactory +import org.jacodb.analysis.DumpableAnalysisResult +import org.jacodb.analysis.Factory +import org.jacodb.analysis.GraphFactory +import org.jacodb.analysis.JcNaivePoints2EngineFactory +import org.jacodb.analysis.JcSimplifiedGraphFactory +import org.jacodb.analysis.NPEAnalysisFactory +import org.jacodb.analysis.Points2EngineFactory +import org.jacodb.analysis.UnusedVariableAnalysisFactory +import org.jacodb.analysis.loadFactories +import org.jacodb.api.ext.findClass +import org.jacodb.impl.features.InMemoryHierarchy +import org.jacodb.impl.features.Usages +import org.jacodb.impl.jacodb +import java.io.File + + +private inline fun factoryChoice(): ArgType.Choice { + val factories = loadFactories() + val nameToFactory = { requiredFactoryName: String -> factories.single { it.name == requiredFactoryName } } + val factoryToName = { factory: T -> factory.name } + + return ArgType.Choice(factories, nameToFactory, factoryToName) +} + +private val logger = object : KLogging() {}.logger + + +class AnalysisMain { + fun run(args: List) = main(args.toTypedArray()) +} + +fun loadAnalysisEngineFactoriesByConfig(config: AnalysisConfig): List { + return config.analyses.mapNotNull { (analysis, _) -> + when (analysis) { + "NPE" -> NPEAnalysisFactory() + "Unused" -> UnusedVariableAnalysisFactory() + else -> { + logger.error { "Unknown analysis type: $analysis" } + null + } + } + } +} + + +fun main(args: Array) { + val parser = ArgParser("taint-analysis") + val configFilePath by parser.option( + ArgType.String, + fullName = "analysisConf", + shortName = "a", + description = "File with analysis configuration in JSON format" + ).required() + val dbLocation by parser.option( + ArgType.String, + fullName = "dbLocation", + shortName = "l", + description = "Location of SQLite database for storing bytecode data" + ) + val startClasses by parser.option( + ArgType.String, + fullName = "start", + shortName = "s", + description = "classes from which to start the analysis" + ).required() + val outputPath by parser.option( + ArgType.String, + fullName = "output", + shortName = "o", + description = "File where analysis report will be written. All parent directories will be created if not exists. File will be created if not exists. Existing file will be overwritten." + ).default("report.txt") // TODO: create SARIF here + val graphFactory by parser.option( + factoryChoice(), + fullName = "graph-type", + shortName = "g", + description = "Type of code graph to be used by analysis." + ).default(JcSimplifiedGraphFactory()) + val points2Factory by parser.option( + factoryChoice(), + fullName = "points2", + shortName = "p2", + description = "Type of points-to engine." + ).default(JcNaivePoints2EngineFactory) + val classpath by parser.option( + ArgType.String, + fullName = "classpath", + shortName = "cp", + description = "Classpath for analysis. Used by JacoDB." + ).default(System.getProperty("java.class.path")) + + parser.parse(args) + + val outputFile = File(outputPath) + + if (outputFile.exists() && outputFile.isDirectory) { + throw IllegalArgumentException("Provided path for output file is directory, please provide correct path") + } else if (outputFile.exists()) { + logger.info { "Output file $outputFile already exists, results will be overwritten" } + } + + val configFile = File(configFilePath) + if (!configFile.isFile) { + throw IllegalArgumentException("Can't find provided config file $configFilePath") + } + val config = Json.decodeFromString(configFile.readText()) + + val classpathAsFiles = classpath.split(File.pathSeparatorChar).sorted().map { File(it) } + val classpathHash = classpath.hashCode() + + val cp = runBlocking { + val jacodb = jacodb { + loadByteCode(classpathAsFiles) + dbLocation?.let { + persistent(it) + } + installFeatures(InMemoryHierarchy, Usages) + } + jacodb.classpath(classpathAsFiles) + } + + val graph = graphFactory.createGraph(cp) + val points2Engine = points2Factory.createPoints2Engine(graph) + val startJcClasses = startClasses.split(";").map { cp.findClass(it) } + + val analysisEngines = loadAnalysisEngineFactoriesByConfig(config).map { + it.createAnalysisEngine(graph, points2Engine) + } + + val analysisResults = analysisEngines.map { engine -> + startJcClasses.forEach { clazz -> + clazz.declaredMethods.forEach { + engine.addStart(it) + } + } + engine.analyze() + } + + val mergedResult = DumpableAnalysisResult(analysisResults.flatMap { it.foundVulnerabilities }) + + val json = Json { prettyPrint = true } + outputFile.outputStream().use { fileOutputStream -> + json.encodeToStream(mergedResult, fileOutputStream) + } +} \ No newline at end of file diff --git a/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/CliTest.kt b/jacodb-cli/src/test/kotlin/org/jacodb/cli/CliTest.kt similarity index 84% rename from jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/CliTest.kt rename to jacodb-cli/src/test/kotlin/org/jacodb/cli/CliTest.kt index ccb166879..c5dd8effc 100644 --- a/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/CliTest.kt +++ b/jacodb-cli/src/test/kotlin/org/jacodb/cli/CliTest.kt @@ -14,9 +14,9 @@ * limitations under the License. */ -package org.jacodb.analysis.impl +package org.jacodb.cli -import org.jacodb.analysis.AnalysisMain +import org.jacodb.testing.analysis.NPEExamples import org.junit.jupiter.api.Test class CliTest { @@ -24,8 +24,7 @@ class CliTest { fun `test basic analysis cli api`() { val args = listOf( "-a", CliTest::class.java.getResource("/config.json")?.file ?: error("Can't find file with config"), - "-c", "tmp-analysis-db", - "-s", "org.jacodb.analysis.samples.NPEExamples" + "-s", NPEExamples::class.java.name ) AnalysisMain().run(args) } diff --git a/jacodb-analysis/src/test/resources/config.json b/jacodb-cli/src/test/resources/config.json similarity index 100% rename from jacodb-analysis/src/test/resources/config.json rename to jacodb-cli/src/test/resources/config.json diff --git a/jacodb-analysis/src/testFixtures/java/org/jacodb/analysis/samples/NPEExamples.java b/jacodb-core/src/testFixtures/java/org/jacodb/testing/analysis/NPEExamples.java similarity index 99% rename from jacodb-analysis/src/testFixtures/java/org/jacodb/analysis/samples/NPEExamples.java rename to jacodb-core/src/testFixtures/java/org/jacodb/testing/analysis/NPEExamples.java index 1ded46c2e..7fe9576a7 100644 --- a/jacodb-analysis/src/testFixtures/java/org/jacodb/analysis/samples/NPEExamples.java +++ b/jacodb-core/src/testFixtures/java/org/jacodb/testing/analysis/NPEExamples.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.analysis.samples; +package org.jacodb.testing.analysis; import org.jetbrains.annotations.NotNull; diff --git a/settings.gradle.kts b/settings.gradle.kts index a2f4877d9..417713c63 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -17,3 +17,4 @@ include("jacodb-core") include("jacodb-analysis") include("jacodb-examples") include("jacodb-benchmarks") +include("jacodb-cli")