Skip to content

Commit 6139a09

Browse files
authored
Splitting analysis module into two: analysis and cli (#89)
splitting `analysis` module into two: `analysis` and `cli`
1 parent 012ee48 commit 6139a09

File tree

19 files changed

+239
-187
lines changed

19 files changed

+239
-187
lines changed

.github/workflows/release.yml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,20 @@ jobs:
2828
gradle-version: 7.6.1
2929
arguments: |
3030
publish
31+
-PincludeDokka=true
3132
-PsemVer=${{inputs.semVer}}
3233
-Pactor=${{ secrets.MAVEN_CENTRAL_LOGIN }}
3334
-Ptoken=${{ secrets.MAVEN_CENTRAL_TOKEN }}
3435
-PgpgKey="${{ secrets.OSSRH_GPG_SECRET_KEY }}"
3536
-PgpgPassphrase=${{ secrets.OSSRH_GPG_SECRET_PASSPHRASE }}
36-
-PrepoUrl=https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
37+
-PrepoUrl=https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
38+
- name: Upload release artifacts
39+
uses: softprops/action-gh-release@v1
40+
with:
41+
draft: true
42+
files: |
43+
jacodb-api/build/libs/jacodb-api-${{inputs.semVer}}.jar
44+
jacodb-analysis/build/libs/jacodb-analysis-${{inputs.semVer}}.jar
45+
jacodb-core/build/libs/jacodb-core-${{inputs.semVer}}.jar
46+
jacodb-cli/build/libs/jacodb-cli-${{inputs.semVer}}.jar
47+
jacodb-examples/build/libs/jacodb-examples-${{inputs.semVer}}.jar

build.gradle.kts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ val kotlinVersion: String by rootProject
44
val coroutinesVersion: String by rootProject
55
val junit5Version: String by project
66
val semVer: String? by project
7+
val includeDokka: String? by project
78

89
group = "org.jacodb"
910

@@ -125,14 +126,17 @@ allprojects {
125126
archiveClassifier.set("javadoc")
126127
}
127128

129+
128130
val sourcesJar by creating(Jar::class) {
129131
archiveClassifier.set("sources")
130132
from(sourceSets.getByName("main").kotlin.srcDirs)
131133
}
132134

133135
artifacts {
134136
archives(sourcesJar)
135-
archives(dokkaJavadocJar)
137+
if (includeDokka != null) {
138+
archives(dokkaJavadocJar)
139+
}
136140
}
137141
}
138142

@@ -170,7 +174,7 @@ if (!repoUrl.isNullOrEmpty()) {
170174
listOf(
171175
project(":jacodb-api"),
172176
project(":jacodb-core"),
173-
// project(":jacodb-analysis"),
177+
project(":jacodb-analysis"),
174178
)
175179
) {
176180
publishing {

docs/badges/branches.svg

Lines changed: 1 addition & 1 deletion
Loading

docs/badges/coverage-summary.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"branches": 68.34195614683419, "coverage": 70.38198957259596}
1+
{"branches": 68.14246846401187, "coverage": 69.80196523053665}

docs/badges/jacoco.svg

Lines changed: 1 addition & 1 deletion
Loading

jacodb-analysis/build.gradle.kts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,13 @@ dependencies {
1111
api(project(":jacodb-api"))
1212

1313
testImplementation(testFixtures(project(":jacodb-core")))
14-
testFixturesImplementation(project(":jacodb-api"))
15-
testFixturesImplementation("javax.servlet:servlet-api:2.5")
16-
testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.2")
17-
testImplementation(files("src/testFixtures/resources/juliet.jar"))
18-
testImplementation(files("src/testFixtures/resources/pointerbench.jar"))
19-
testImplementation("joda-time:joda-time:2.12.5")
14+
testImplementation(project(":jacodb-api"))
15+
testImplementation(group = "javax.servlet", name = "servlet-api", version = "2.5")
16+
testImplementation(group = "org.junit.jupiter", name = "junit-jupiter-params", version = "5.9.2")
17+
testImplementation(files("src/test/resources/juliet.jar"))
18+
testImplementation(files("src/test/resources/pointerbench.jar"))
19+
testImplementation(group = "joda-time", name = "joda-time", version = "2.12.5")
2020

21-
implementation("org.jetbrains.kotlinx:kotlinx-cli:0.3.5")
22-
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1")
21+
implementation(group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version = "1.4.1")
2322
implementation(group = "io.github.microutils", name = "kotlin-logging", version = "1.8.3")
2423
}

jacodb-analysis/src/main/kotlin/org/jacodb/analysis/AnalysisMain.kt

Lines changed: 15 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,8 @@
1515
*/
1616

1717
package org.jacodb.analysis
18-
import kotlinx.cli.ArgParser
19-
import kotlinx.cli.ArgType
20-
import kotlinx.cli.default
21-
import kotlinx.cli.required
2218
import kotlinx.coroutines.runBlocking
2319
import kotlinx.serialization.Serializable
24-
import kotlinx.serialization.decodeFromString
25-
import kotlinx.serialization.json.Json
26-
import kotlinx.serialization.json.encodeToStream
27-
import mu.KLogging
2820
import org.jacodb.analysis.analyzers.AliasAnalyzer
2921
import org.jacodb.analysis.analyzers.NpeAnalyzer
3022
import org.jacodb.analysis.analyzers.TaintAnalysisNode
@@ -41,12 +33,7 @@ import org.jacodb.api.JcClasspath
4133
import org.jacodb.api.JcMethod
4234
import org.jacodb.api.analysis.JcApplicationGraph
4335
import org.jacodb.api.cfg.JcInst
44-
import org.jacodb.api.ext.findClass
45-
import org.jacodb.impl.features.InMemoryHierarchy
46-
import org.jacodb.impl.features.Usages
4736
import org.jacodb.impl.features.usagesExt
48-
import org.jacodb.impl.jacodb
49-
import java.io.File
5037
import java.util.*
5138

5239
@Serializable
@@ -95,38 +82,40 @@ class UnusedVariableAnalysisFactory : AnalysisEngineFactory {
9582
}
9683

9784
override val name: String
98-
get() = "Jacodb-Unused-Variable"
85+
get() = "unused-variable"
9986
}
10087

10188
abstract class FlowDroidFactory : AnalysisEngineFactory {
10289

103-
protected abstract fun getAnalyzer(graph: JcApplicationGraph): Analyzer
90+
protected abstract val JcApplicationGraph.analyzer: Analyzer
10491

10592
override fun createAnalysisEngine(
10693
graph: JcApplicationGraph,
10794
points2Engine: Points2Engine,
10895
): AnalysisEngine {
109-
val analyzer = getAnalyzer(graph)
96+
val analyzer = graph.analyzer
11097
return TaintAnalysisWithPointsTo(graph, analyzer, points2Engine)
11198
}
11299

113100
override val name: String
114-
get() = "JacoDB-FlowDroid"
101+
get() = "flow-droid"
115102
}
116103

117104
class NPEAnalysisFactory : FlowDroidFactory() {
118-
override fun getAnalyzer(graph: JcApplicationGraph): Analyzer {
119-
return NpeAnalyzer(graph)
120-
}
105+
override val JcApplicationGraph.analyzer: Analyzer
106+
get() {
107+
return NpeAnalyzer(this)
108+
}
121109
}
122110

123111
class AliasAnalysisFactory(
124112
private val generates: (JcInst) -> List<TaintAnalysisNode>,
125113
private val isSink: (JcInst, DomainFact) -> Boolean,
126114
) : FlowDroidFactory() {
127-
override fun getAnalyzer(graph: JcApplicationGraph): Analyzer {
128-
return AliasAnalyzer(graph, generates, isSink)
129-
}
115+
override val JcApplicationGraph.analyzer: Analyzer
116+
get() {
117+
return AliasAnalyzer(this, generates, isSink)
118+
}
130119
}
131120

132121
interface Points2EngineFactory : Factory {
@@ -140,7 +129,7 @@ interface GraphFactory : Factory {
140129
class JcSimplifiedGraphFactory(
141130
private val bannedPackagePrefixes: List<String>? = null
142131
) : GraphFactory {
143-
override val name: String = "JacoDB-graph simplified for IFDS"
132+
override val name: String = "ifds-simplification"
144133

145134
override fun createGraph(
146135
classpath: JcClasspath
@@ -154,7 +143,7 @@ class JcSimplifiedGraphFactory(
154143
}
155144
}
156145

157-
class JcNaivePoints2EngineFactory : Points2EngineFactory {
146+
object JcNaivePoints2EngineFactory : Points2EngineFactory {
158147
override fun createPoints2Engine(
159148
graph: JcApplicationGraph,
160149
): Points2Engine {
@@ -163,147 +152,10 @@ class JcNaivePoints2EngineFactory : Points2EngineFactory {
163152
}
164153

165154
override val name: String
166-
get() = "JacoDB-P2-Naive"
155+
get() = "naive-p2"
167156
}
168157

169158
inline fun <reified T : Factory> loadFactories(): List<T> {
170159
assert(T::class.java != Factory::class.java)
171160
return ServiceLoader.load(T::class.java).toList()
172161
}
173-
174-
private inline fun <reified T : Factory> factoryChoice(): ArgType.Choice<T> {
175-
val factories = loadFactories<T>()
176-
val nameToFactory = { requiredFactoryName: String -> factories.single { it.name == requiredFactoryName } }
177-
val factoryToName = { factory: T -> factory.name }
178-
179-
return ArgType.Choice(factories, nameToFactory, factoryToName)
180-
}
181-
182-
private val logger = object : KLogging() {}.logger
183-
184-
185-
class AnalysisMain {
186-
fun run(args: List<String>) = main(args.toTypedArray())
187-
}
188-
189-
fun loadAnalysisEngineFactoriesByConfig(config: AnalysisConfig): List<AnalysisEngineFactory> {
190-
return config.analyses.mapNotNull { (analysis, _) ->
191-
when (analysis) {
192-
"NPE" -> NPEAnalysisFactory()
193-
"Unused" -> UnusedVariableAnalysisFactory()
194-
else -> {
195-
logger.error { "Unknown analysis type: $analysis" }
196-
null
197-
}
198-
}
199-
}
200-
}
201-
202-
fun main(args: Array<String>) {
203-
val parser = ArgParser("taint-analysis")
204-
val configFilePath by parser.option(
205-
ArgType.String,
206-
fullName = "analysisConf",
207-
shortName = "a",
208-
description = "File with analysis configuration in JSON format"
209-
).required()
210-
val cacheDirPath by parser.option(
211-
ArgType.String,
212-
fullName = "cachedir",
213-
shortName = "c",
214-
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."
215-
).required()
216-
val startClasses by parser.option(
217-
ArgType.String,
218-
fullName = "start",
219-
shortName = "s",
220-
description = "classes from which to start the analysis"
221-
).required()
222-
val outputPath by parser.option(
223-
ArgType.String,
224-
fullName = "output",
225-
shortName = "o",
226-
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."
227-
).default("report.txt") // TODO: create SARIF here
228-
val graphFactory by parser.option(
229-
factoryChoice<GraphFactory>(),
230-
fullName = "graph-type",
231-
shortName = "g",
232-
description = "Type of code graph to be used by analysis."
233-
).default(JcSimplifiedGraphFactory())
234-
val points2Factory by parser.option(
235-
factoryChoice<Points2EngineFactory>(),
236-
fullName = "points2",
237-
shortName = "p2",
238-
description = "Type of points-to engine."
239-
).default(JcNaivePoints2EngineFactory())
240-
val classpath by parser.option(
241-
ArgType.String,
242-
fullName = "classpath",
243-
shortName = "cp",
244-
description = "Classpath for analysis. Used by JacoDB."
245-
).default(System.getProperty("java.class.path"))
246-
247-
parser.parse(args)
248-
249-
val outputFile = File(outputPath)
250-
251-
if (outputFile.exists() && outputFile.isDirectory) {
252-
throw IllegalArgumentException("Provided path for output file is directory, please provide correct path")
253-
} else if (outputFile.exists()) {
254-
logger.info { "Output file $outputFile already exists, results will be overwritten" }
255-
}
256-
257-
val cacheDir = File(cacheDirPath)
258-
259-
if (!cacheDir.exists()) {
260-
cacheDir.mkdirs()
261-
}
262-
263-
if (!cacheDir.isDirectory) {
264-
throw IllegalArgumentException("Provided path to cache directory is not directory")
265-
}
266-
267-
val configFile = File(configFilePath)
268-
if (!configFile.isFile) {
269-
throw IllegalArgumentException("Can't find provided config file $configFilePath")
270-
}
271-
val config = Json.decodeFromString<AnalysisConfig>(configFile.readText())
272-
273-
val classpathAsFiles = classpath.split(File.pathSeparatorChar).sorted().map { File(it) }
274-
val classpathHash = classpath.hashCode()
275-
val persistentPath = cacheDir.resolve("jacodb-for-$classpathHash")
276-
277-
val cp = runBlocking {
278-
val jacodb = jacodb {
279-
loadByteCode(classpathAsFiles)
280-
persistent(persistentPath.absolutePath)
281-
installFeatures(InMemoryHierarchy, Usages)
282-
}
283-
jacodb.classpath(classpathAsFiles)
284-
}
285-
286-
val graph = graphFactory.createGraph(cp)
287-
val points2Engine = points2Factory.createPoints2Engine(graph)
288-
val startJcClasses = startClasses.split(";").map { cp.findClass(it) }
289-
290-
val analysisEngines = loadAnalysisEngineFactoriesByConfig(config).map {
291-
it.createAnalysisEngine(graph, points2Engine)
292-
}
293-
294-
val analysisResults = analysisEngines.map { engine ->
295-
startJcClasses.forEach { clazz ->
296-
clazz.declaredMethods.forEach {
297-
engine.addStart(it)
298-
}
299-
}
300-
engine.analyze()
301-
}
302-
303-
val mergedResult = DumpableAnalysisResult(analysisResults.flatMap { it.foundVulnerabilities })
304-
305-
val json = Json { prettyPrint = true }
306-
outputFile.outputStream().use { fileOutputStream ->
307-
json.encodeToStream(mergedResult, fileOutputStream)
308-
}
309-
}

jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/AliasAnalysisTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ class AliasAnalysisTest : BaseTest() {
146146
.plus("pointerbench.benchmark.internal")
147147

148148
val graph = JcSimplifiedGraphFactory(bannedPackagePrefixes).createGraph(cp)
149-
val points2Engine = JcNaivePoints2EngineFactory().createPoints2Engine(graph)
149+
val points2Engine = JcNaivePoints2EngineFactory.createPoints2Engine(graph)
150150
val factory = AliasAnalysisFactory(::generates, ::isSink)
151151
val ifds = factory.createAnalysisEngine(graph, points2Engine)
152152
ifds.addStart(method)

jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/JodaDateTimeAnalysisTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class JodaDateTimeAnalysisTest : BaseTest() {
3838
val clazz = cp.findClass<DateTime>()
3939

4040
val graph = JcSimplifiedGraphFactory().createGraph(cp)
41-
val points2Engine = JcNaivePoints2EngineFactory().createPoints2Engine(graph)
41+
val points2Engine = JcNaivePoints2EngineFactory.createPoints2Engine(graph)
4242
val ifds = factory.createAnalysisEngine(graph, points2Engine)
4343
clazz.declaredMethods
4444
.forEach { ifds.addStart(it) }

jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/NpeAnalysisTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import org.jacodb.analysis.NPEAnalysisFactory
2424
import org.jacodb.analysis.VulnerabilityInstance
2525
import org.jacodb.analysis.analyzers.NpeAnalyzer
2626
import org.jacodb.analysis.graph.JcApplicationGraphImpl
27-
import org.jacodb.analysis.samples.NPEExamples
2827
import org.jacodb.api.JcClassOrInterface
2928
import org.jacodb.api.JcMethod
3029
import org.jacodb.api.ext.constructors
@@ -37,6 +36,7 @@ import org.jacodb.impl.features.usagesExt
3736
import org.jacodb.testing.BaseTest
3837
import org.jacodb.testing.WithDB
3938
import org.jacodb.testing.allClasspath
39+
import org.jacodb.testing.analysis.NPEExamples
4040
import org.junit.jupiter.api.Assertions.*
4141
import org.junit.jupiter.api.Test
4242
import org.junit.jupiter.params.ParameterizedTest
@@ -273,7 +273,7 @@ class NpeAnalysisTest : BaseTest() {
273273

274274
private fun findNpeSources(method: JcMethod): List<VulnerabilityInstance> {
275275
val graph = JcSimplifiedGraphFactory().createGraph(cp)
276-
val points2Engine = JcNaivePoints2EngineFactory().createPoints2Engine(graph)
276+
val points2Engine = JcNaivePoints2EngineFactory.createPoints2Engine(graph)
277277
val ifds = NPEAnalysisFactory().createAnalysisEngine(graph, points2Engine)
278278
ifds.addStart(method)
279279
val result = ifds.analyze()

0 commit comments

Comments
 (0)