Skip to content

Commit f6c3dfb

Browse files
authored
Change output format to Sarif (#178)
1 parent 7fceff7 commit f6c3dfb

19 files changed

Lines changed: 300 additions & 250 deletions

File tree

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

Lines changed: 0 additions & 38 deletions
This file was deleted.

jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/MainIfdsUnitManager.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ class MainIfdsUnitManager<UnitType>(
152152
logger.info { "Restoring traces..." }
153153

154154
foundVulnerabilities
155-
.map { VulnerabilityInstance(it.vulnerabilityType, extendTraceGraph(it.sink.traceGraph)) }
155+
.map { VulnerabilityInstance(it.vulnerabilityDescription, extendTraceGraph(it.sink.traceGraph)) }
156156
.filter {
157157
it.traceGraph.sources.any { source ->
158158
graph.methodOf(source.statement) in startMethods || source.domainFact == ZEROFact

jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/SummaryStorage.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package org.jacodb.analysis.engine
1919
import kotlinx.coroutines.flow.Flow
2020
import kotlinx.coroutines.flow.MutableSharedFlow
2121
import kotlinx.coroutines.flow.SharedFlow
22+
import org.jacodb.analysis.sarif.VulnerabilityDescription
2223
import org.jacodb.api.JcMethod
2324
import java.util.concurrent.ConcurrentHashMap
2425

@@ -33,7 +34,7 @@ sealed interface SummaryFact {
3334
/**
3435
* [SummaryFact] that denotes a possible vulnerability at [sink]
3536
*/
36-
data class VulnerabilityLocation(val vulnerabilityType: String, val sink: IfdsVertex) : SummaryFact {
37+
data class VulnerabilityLocation(val vulnerabilityDescription: VulnerabilityDescription, val sink: IfdsVertex) : SummaryFact {
3738
override val method: JcMethod = sink.method
3839
}
3940

jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/TraceGraph.kt

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
package org.jacodb.analysis.engine
1818

19-
import org.jacodb.analysis.DumpableVulnerabilityInstance
20-
2119
/**
2220
* A directed graph with selected [sink] and [sources], where each path from one of [sources] to [sink] is a trace.
2321
*
@@ -56,17 +54,6 @@ data class TraceGraph(
5654
}
5755
}
5856

59-
fun toVulnerability(vulnerabilityType: String, maxTracesCount: Int = 100): DumpableVulnerabilityInstance {
60-
return DumpableVulnerabilityInstance(
61-
vulnerabilityType,
62-
sources.map { it.statement.toString() },
63-
sink.statement.toString(),
64-
getAllTraces().take(maxTracesCount).map { intermediatePoints ->
65-
intermediatePoints.map { it.statement.toString() }
66-
}.toList()
67-
)
68-
}
69-
7057
/**
7158
* Merges two graphs.
7259
*

jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/VulnerabilityInstance.kt

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,15 @@
1616

1717
package org.jacodb.analysis.engine
1818

19-
import org.jacodb.analysis.DumpableVulnerabilityInstance
20-
import org.jacodb.api.cfg.JcInst
19+
import org.jacodb.analysis.sarif.VulnerabilityDescription
2120

2221
/**
2322
* Represents a vulnerability (issue) found by analysis
2423
*
25-
* @property vulnerabilityType type of vulnerability as a string (e.g. "Possible NPE", "Unused variable")
24+
* @property vulnerabilityDescription type of vulnerability as a string (e.g. "Possible NPE", "Unused variable")
2625
* @property traceGraph contains sink, sources and traces that lead to occurrence of vulnerability
2726
*/
2827
data class VulnerabilityInstance(
29-
val vulnerabilityType: String,
28+
val vulnerabilityDescription: VulnerabilityDescription,
3029
val traceGraph: TraceGraph
31-
) {
32-
private fun JcInst.prettyPrint(): String {
33-
return "${toString()} (${location.method}:${location.lineNumber})"
34-
}
35-
36-
fun toDumpable(maxPathsCount: Int): DumpableVulnerabilityInstance {
37-
return DumpableVulnerabilityInstance(
38-
vulnerabilityType,
39-
traceGraph.sources.map { it.statement.prettyPrint() },
40-
traceGraph.sink.statement.prettyPrint(),
41-
traceGraph.getAllTraces().take(maxPathsCount).map { intermediatePoints ->
42-
intermediatePoints.map { it.statement.prettyPrint() }
43-
}.toList()
44-
)
45-
}
46-
}
30+
)

jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/RunnersLibrary.kt

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,8 @@ import org.jacodb.analysis.library.analyzers.NpePrecalcBackwardAnalyzerFactory
2525
import org.jacodb.analysis.library.analyzers.SqlInjectionAnalyzerFactory
2626
import org.jacodb.analysis.library.analyzers.SqlInjectionBackwardAnalyzerFactory
2727
import org.jacodb.analysis.library.analyzers.TaintAnalysisNode
28-
import org.jacodb.analysis.library.analyzers.TaintAnalyzerFactory
29-
import org.jacodb.analysis.library.analyzers.TaintBackwardAnalyzerFactory
3028
import org.jacodb.analysis.library.analyzers.TaintNode
3129
import org.jacodb.analysis.library.analyzers.UnusedVariableAnalyzerFactory
32-
import org.jacodb.api.JcMethod
3330
import org.jacodb.api.cfg.JcExpr
3431
import org.jacodb.api.cfg.JcInst
3532

@@ -52,24 +49,4 @@ fun newAliasRunnerFactory(
5249
sanitizes: (JcExpr, TaintNode) -> Boolean,
5350
sinks: (JcInst) -> List<TaintAnalysisNode>,
5451
maxPathLength: Int = 5
55-
) = BaseIfdsUnitRunnerFactory(AliasAnalyzerFactory(generates, sanitizes, sinks, maxPathLength))
56-
57-
fun newTaintRunnerFactory(
58-
isSourceMethod: (JcMethod) -> Boolean,
59-
isSanitizeMethod: (JcMethod) -> Boolean,
60-
isSinkMethod: (JcMethod) -> Boolean,
61-
maxPathLength: Int = 5
62-
) = BidiIfdsUnitRunnerFactory(
63-
BaseIfdsUnitRunnerFactory(TaintAnalyzerFactory(isSourceMethod, isSanitizeMethod, isSinkMethod, maxPathLength)),
64-
BaseIfdsUnitRunnerFactory(TaintBackwardAnalyzerFactory(isSourceMethod, isSinkMethod, maxPathLength))
65-
)
66-
67-
fun newTaintRunnerFactory(
68-
sourceMethodMatchers: List<String>,
69-
sanitizeMethodMatchers: List<String>,
70-
sinkMethodMatchers: List<String>,
71-
maxPathLength: Int = 5
72-
) = BidiIfdsUnitRunnerFactory(
73-
BaseIfdsUnitRunnerFactory(TaintAnalyzerFactory(sourceMethodMatchers, sanitizeMethodMatchers, sinkMethodMatchers, maxPathLength)),
74-
BaseIfdsUnitRunnerFactory(TaintBackwardAnalyzerFactory(sourceMethodMatchers, sinkMethodMatchers, maxPathLength))
75-
)
52+
) = BaseIfdsUnitRunnerFactory(AliasAnalyzerFactory(generates, sanitizes, sinks, maxPathLength))

jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/AliasAnalyzer.kt

Lines changed: 7 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,10 @@ import org.jacodb.analysis.engine.AnalyzerFactory
2121
import org.jacodb.analysis.engine.DomainFact
2222
import org.jacodb.analysis.engine.IfdsResult
2323
import org.jacodb.analysis.engine.IfdsVertex
24-
import org.jacodb.analysis.engine.NewSummaryFact
25-
import org.jacodb.analysis.engine.VulnerabilityLocation
26-
import org.jacodb.analysis.paths.FieldAccessor
24+
import org.jacodb.analysis.sarif.VulnerabilityDescription
2725
import org.jacodb.api.analysis.JcApplicationGraph
28-
import org.jacodb.api.cfg.JcArgument
2926
import org.jacodb.api.cfg.JcExpr
3027
import org.jacodb.api.cfg.JcInst
31-
import org.jacodb.api.cfg.JcLocal
32-
import org.jacodb.api.cfg.values
3328

3429
fun AliasAnalyzerFactory(
3530
generates: (JcInst) -> List<DomainFact>,
@@ -42,49 +37,12 @@ fun AliasAnalyzerFactory(
4237

4338
private class AliasAnalyzer(
4439
graph: JcApplicationGraph,
45-
generates: (JcInst) -> List<DomainFact>,
46-
sanitizes: (JcExpr, TaintNode) -> Boolean,
47-
sinks: (JcInst) -> List<TaintAnalysisNode>,
40+
override val generates: (JcInst) -> List<DomainFact>,
41+
override val sanitizes: (JcExpr, TaintNode) -> Boolean,
42+
override val sinks: (JcInst) -> List<TaintAnalysisNode>,
4843
maxPathLength: Int,
49-
) : TaintAnalyzer(graph, generates, sanitizes, sinks, maxPathLength) {
50-
51-
override fun handleIfdsResult(ifdsResult: IfdsResult): List<AnalysisDependentEvent> = buildList {
52-
ifdsResult.resultFacts.map { (inst, facts) ->
53-
facts.filterIsInstance<TaintAnalysisNode>().forEach { fact ->
54-
if (fact in sinks(inst)) {
55-
fact.variable.let {
56-
val name = when (val x = it.value) {
57-
is JcArgument -> x.name
58-
is JcLocal -> inst.location.method.flowGraph().instructions
59-
.first { x in it.operands.flatMap { it.values } }
60-
.lineNumber
61-
.toString()
62-
null -> (it.accesses[0] as FieldAccessor).field.enclosingClass.simpleName
63-
else -> error("Unknown local type")
64-
}
65-
66-
val fullPath = buildString {
67-
append(name)
68-
if (it.accesses.isNotEmpty()) {
69-
append(".")
70-
}
71-
append(it.accesses.joinToString("."))
72-
}
73-
74-
verticesWithTraceGraphNeeded.add(IfdsVertex(inst, fact))
44+
) : TaintAnalyzer(graph, maxPathLength) {
45+
override fun generateDescriptionForSink(sink: IfdsVertex): VulnerabilityDescription = TODO()
7546

76-
add(
77-
NewSummaryFact(
78-
VulnerabilityLocation(
79-
vulnerabilityType,
80-
IfdsVertex(inst, fact)
81-
)
82-
)
83-
)
84-
}
85-
}
86-
}
87-
}
88-
addAll(super.handleIfdsResult(ifdsResult))
89-
}
47+
override fun handleIfdsResult(ifdsResult: IfdsResult): List<AnalysisDependentEvent> = TODO()
9048
}

jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/NpeAnalyzer.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ import org.jacodb.analysis.paths.minus
3535
import org.jacodb.analysis.paths.startsWith
3636
import org.jacodb.analysis.paths.toPath
3737
import org.jacodb.analysis.paths.toPathOrNull
38+
import org.jacodb.analysis.sarif.SarifMessage
39+
import org.jacodb.analysis.sarif.VulnerabilityDescription
3840
import org.jacodb.api.JcArrayType
3941
import org.jacodb.api.JcClasspath
4042
import org.jacodb.api.JcMethod
@@ -67,14 +69,16 @@ class NpeAnalyzer(graph: JcApplicationGraph, maxPathLength: Int) : AbstractAnaly
6769
get() = true
6870

6971
companion object {
70-
const val vulnerabilityType: String = "npe-analysis"
72+
const val ruleId: String = "npe-deref"
7173
}
7274

7375
override fun handleNewEdge(edge: IfdsEdge): List<AnalysisDependentEvent> = buildList {
7476
val (inst, fact0) = edge.v
7577

7678
if (fact0 is NpeTaintNode && fact0.activation == null && fact0.variable.isDereferencedAt(inst)) {
77-
add(NewSummaryFact((VulnerabilityLocation(vulnerabilityType, edge.v))))
79+
val message = "Dereference of possibly-null ${fact0.variable}"
80+
val desc = VulnerabilityDescription(SarifMessage(message), ruleId)
81+
add(NewSummaryFact((VulnerabilityLocation(desc, edge.v))))
7882
verticesWithTraceGraphNeeded.add(edge.v)
7983
}
8084

jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/SqlInjectionAnalyzer.kt

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,45 @@
1616

1717
package org.jacodb.analysis.library.analyzers
1818

19-
fun SqlInjectionAnalyzerFactory(maxPathLength: Int) = TaintAnalyzerFactory(
20-
sqlSourceMatchers,
21-
sqlSanitizeMatchers,
22-
sqlSinkMatchers,
23-
maxPathLength
24-
)
19+
import org.jacodb.analysis.engine.AnalyzerFactory
20+
import org.jacodb.analysis.engine.IfdsVertex
21+
import org.jacodb.analysis.sarif.SarifMessage
22+
import org.jacodb.analysis.sarif.VulnerabilityDescription
23+
import org.jacodb.api.analysis.JcApplicationGraph
2524

26-
fun SqlInjectionBackwardAnalyzerFactory(maxPathLength: Int) = TaintBackwardAnalyzerFactory(
27-
sqlSourceMatchers,
28-
sqlSinkMatchers,
29-
maxPathLength
30-
)
25+
class SqlInjectionAnalyzer(
26+
graph: JcApplicationGraph,
27+
maxPathLength: Int
28+
) : TaintAnalyzer(graph, maxPathLength) {
29+
override val generates = isSourceMethodToGenerates(sqlSourceMatchers.asMethodMatchers)
30+
override val sanitizes = isSanitizeMethodToSanitizes(sqlSanitizeMatchers.asMethodMatchers)
31+
override val sinks = isSinkMethodToSinks(sqlSinkMatchers.asMethodMatchers)
32+
33+
companion object {
34+
private const val ruleId: String = "SQL-injection"
35+
private val vulnerabilityMessage = SarifMessage("SQL query with unchecked injection")
36+
37+
val vulnerabilityDescription = VulnerabilityDescription(vulnerabilityMessage, ruleId)
38+
}
39+
40+
override fun generateDescriptionForSink(sink: IfdsVertex): VulnerabilityDescription = vulnerabilityDescription
41+
}
42+
43+
class SqlInjectionBackwardAnalyzer(
44+
graph: JcApplicationGraph,
45+
maxPathLength: Int
46+
) : TaintBackwardAnalyzer(graph, maxPathLength) {
47+
override val generates = isSourceMethodToGenerates(sqlSourceMatchers.asMethodMatchers)
48+
override val sinks = isSinkMethodToSinks(sqlSinkMatchers.asMethodMatchers)
49+
}
50+
51+
fun SqlInjectionAnalyzerFactory(maxPathLength: Int) = AnalyzerFactory { graph ->
52+
SqlInjectionAnalyzer(graph, maxPathLength)
53+
}
54+
55+
fun SqlInjectionBackwardAnalyzerFactory(maxPathLength: Int) = AnalyzerFactory { graph ->
56+
SqlInjectionBackwardAnalyzer(graph, maxPathLength)
57+
}
3158

3259
private val sqlSourceMatchers = listOf(
3360
"java\\.io.+",

0 commit comments

Comments
 (0)