From cd13879b302c125d38aebe5be2f10209718b880f Mon Sep 17 00:00:00 2001 From: Ivan Volkov Date: Fri, 21 Jul 2023 13:21:32 +0300 Subject: [PATCH 1/6] [jacodb-ifds] Add KDocs, minor stylistic improvements --- .../org/jacodb/analysis/AnalysisMain.kt | 10 ++ .../jacodb/analysis/analyzers/TaintNode.kt | 6 +- .../jacodb/analysis/engine/FlowFunctions.kt | 53 +++++++++ .../analysis/engine/IdLikeFlowFunction.kt | 34 ------ .../analysis/engine/IfdsBaseUnitRunner.kt | 101 ++++++++++++++---- .../org/jacodb/analysis/engine/IfdsEdge.kt | 7 ++ .../org/jacodb/analysis/engine/IfdsResult.kt | 6 ++ .../jacodb/analysis/engine/IfdsUnitManager.kt | 52 ++++++++- .../jacodb/analysis/engine/IfdsUnitRunner.kt | 38 ++++++- .../org/jacodb/analysis/engine/TraceGraph.kt | 20 +++- .../jacodb/analysis/engine/UnitResolvers.kt | 8 ++ .../analysis/graph/JcApplicationGraphImpl.kt | 5 +- .../jacodb/analysis/impl/AliasAnalysisTest.kt | 4 +- .../analysis/impl/JodaDateTimeAnalysisTest.kt | 4 +- .../jacodb/analysis/impl/NpeAnalysisTest.kt | 4 +- .../analysis/impl/SqlInjectionAnalysisTest.kt | 4 +- .../analysis/impl/UnusedVariableTest.kt | 4 +- .../jacodb/api/analysis/ApplicationGraph.kt | 3 + .../jacodb/api/analysis/JcApplicationGraph.kt | 3 + .../src/main/kotlin/org/jacodb/cli/main.kt | 7 +- 20 files changed, 291 insertions(+), 82 deletions(-) delete mode 100644 jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IdLikeFlowFunction.kt 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 ac8fe31e0..451c7d0bb 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/AnalysisMain.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/AnalysisMain.kt @@ -34,6 +34,9 @@ import org.jacodb.api.JcMethod import org.jacodb.api.cfg.JcExpr import org.jacodb.api.cfg.JcInst +/** + * Simplified version of [VulnerabilityInstance] that contains only serializable data. + */ @Serializable data class DumpableVulnerabilityInstance( val vulnerabilityType: String, @@ -45,6 +48,12 @@ data class DumpableVulnerabilityInstance( @Serializable data class DumpableAnalysisResult(val foundVulnerabilities: List) +/** + * Represents a vulnerability (issue) found by analysis + * + * @property vulnerabilityType type of vulnerability as a string (e.g. "Possible NPE", "Unused variable") + * @property traceGraph contains sink, sources and traces that lead to occurrence of vulnerability + */ data class VulnerabilityInstance( val vulnerabilityType: String, val traceGraph: TraceGraph @@ -113,4 +122,5 @@ fun newTaintRunner( IfdsBaseUnitRunner(TaintBackwardAnalyzerFactory(sourceMethodMatchers, sinkMethodMatchers, maxPathLength)) ) + internal val logger = object : KLogging() {}.logger \ No newline at end of file diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/TaintNode.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/TaintNode.kt index 6d700e9aa..d0e7a7057 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/TaintNode.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/TaintNode.kt @@ -21,7 +21,11 @@ import org.jacodb.analysis.paths.AccessPath import org.jacodb.api.cfg.JcInst /** - * activation == null <=> activation point is passed + * Abstract implementation for [DomainFact] that can be used for analysis where dataflow facts correlate with + * variables/values + * + * @property activation is the activation point, as described in ARF14. Null value means that activation point was + * passed (so, for analyses that do not use backward runner to taint aliases, [activation] will always be null). */ abstract class TaintNode(val variable: AccessPath, val activation: JcInst? = null): DomainFact { protected abstract val nodeType: String diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/FlowFunctions.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/FlowFunctions.kt index 2f31b5b8d..ec402b44c 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/FlowFunctions.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/FlowFunctions.kt @@ -20,17 +20,34 @@ import org.jacodb.api.JcMethod import org.jacodb.api.analysis.JcApplicationGraph import org.jacodb.api.cfg.JcInst +/** + * Interface for flow functions -- mappings of kind DomainFact -> Collection of DomainFacts + */ fun interface FlowFunctionInstance { fun compute(fact: DomainFact): Collection } +/** + * An interface with which facts appearing in analyses should be marked + */ interface DomainFact +/** + * A special [DomainFact] that always holds + */ object ZEROFact : DomainFact { override fun toString() = "[ZERO fact]" } +/** + * Implementations of the interface should provide all four kinds of flow functions mentioned in RHS95, + * thus fully describing how the facts are propagated through the supergraph. + */ interface FlowFunctionsSpace { + /** + * @return facts that may hold when analysis is started from [startStatement] + * (these are needed to initiate worklist in ifds analysis) + */ fun obtainPossibleStartFacts(startStatement: JcInst): Collection fun obtainSequentFlowFunction(current: JcInst, next: JcInst): FlowFunctionInstance fun obtainCallToStartFlowFunction(callStatement: JcInst, callee: JcMethod): FlowFunctionInstance @@ -38,17 +55,53 @@ interface FlowFunctionsSpace { fun obtainExitToReturnSiteFlowFunction(callStatement: JcInst, returnSite: JcInst, exitStatement: JcInst): FlowFunctionInstance } +/** + * [Analyzer] interface describes how facts are propagated and how vulnerabilities are produced by these facts during + * the run of tabulation algorithm by [IfdsBaseUnitRunner]. + * + * There are two methods that can turn facts into vulnerabilities or other [SummaryFact]s: [getSummaryFacts] and + * [getSummaryFactsPostIfds]. First is called during the analysis, each time a new path edge is found, and second + * is called only after all path edges were found. + * While some analyses really need full set of facts to find vulnerabilities, most analyses can report [SummaryFact]s + * right after some fact is reached, so [getSummaryFacts] is a recommended way to report vulnerabilities when possible. + * + * Note that methods and properties of this interface may be accessed concurrently from different threads, + * so the implementations should be thread-safe. + * + * @property flowFunctions a [FlowFunctionsSpace] instance that describes how facts are generated and propagated + * during run of tabulation algorithm. + * + * @property saveSummaryEdgesAndCrossUnitCalls when true, summary edges and cross-unit calls will be automatically + * saved to summary (usually this property is true for forward analyzers and false for backward analyzers). + */ interface Analyzer { val flowFunctions: FlowFunctionsSpace val saveSummaryEdgesAndCrossUnitCalls: Boolean get() = true + /** + * This method is called by [IfdsBaseUnitRunner] each time a new path edge is found. + * + * @return [SummaryFact]s that are produced by this edge, that need to be saved to summary. + */ fun getSummaryFacts(edge: IfdsEdge): List = emptyList() + /** + * This method is called once by [IfdsBaseUnitRunner] when the propagation of facts is finished + * (normally or due to cancellation). + * + * @return [SummaryFact]s that can be obtained after the facts propagation was completed. + */ fun getSummaryFactsPostIfds(ifdsResult: IfdsResult): List = emptyList() } +/** + * A functional interface that allows to produce [Analyzer] by [JcApplicationGraph]. + * + * It simplifies instantiation of [IfdsUnitRunner]s because this way you don't have to pass graph and reversed + * graph to [Analyzer]s directly, relying on runner to do it by itself. + */ fun interface AnalyzerFactory { fun newAnalyzer(graph: JcApplicationGraph): Analyzer } \ No newline at end of file diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IdLikeFlowFunction.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IdLikeFlowFunction.kt deleted file mode 100644 index c7fc0f15c..000000000 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IdLikeFlowFunction.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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.analysis.engine - -/** - * Flow function which is equal to id for all elements from [domain] except those in [nonId], for which the result is stored in the map - * For now, this class is not used, but it may be helpful for some analysis - */ -abstract class IdLikeFlowFunction( - private val domain: Set, - private val nonId: Map> -): FlowFunctionInstance { - - override fun compute(fact: DomainFact): Collection { - nonId[fact]?.let { - return it - } - return if (domain.contains(fact)) listOf(fact) else emptyList() - } -} \ No newline at end of file diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsBaseUnitRunner.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsBaseUnitRunner.kt index 1fd50e757..273b2a23c 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsBaseUnitRunner.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsBaseUnitRunner.kt @@ -33,6 +33,11 @@ import org.jacodb.api.analysis.JcApplicationGraph import org.jacodb.api.cfg.JcInst import java.util.concurrent.ConcurrentHashMap +/** + * This is a basic [IfdsUnitRunner], which launches one [IfdsInstance] for each [run] call. + * + * @property analyzerFactory used to build [Analyzer] instance, which then will be used by launched [IfdsInstance]. + */ class IfdsBaseUnitRunner(private val analyzerFactory: AnalyzerFactory) : IfdsUnitRunner { override suspend fun run( graph: JcApplicationGraph, @@ -47,13 +52,16 @@ class IfdsBaseUnitRunner(private val analyzerFactory: AnalyzerFactory) : IfdsUni } } +/** + * Encapsulates launch of tabulation algorithm, described in RHS95, for one unit + */ private class IfdsInstance( private val graph: ApplicationGraph, private val analyzer: Analyzer, private val summary: Summary, private val unitResolver: UnitResolver, private val unit: UnitType, - private val startMethods: List + startMethods: List ) { private val pathEdges: MutableSet = ConcurrentHashMap.newKeySet() @@ -66,6 +74,29 @@ private class IfdsInstance( private val flowSpace get() = analyzer.flowFunctions + /** + * Queue containing all unprocessed path edges. + */ + private val workList = Channel(Channel.UNLIMITED) + + + init { + // Adding initial facts to workList + for (method in startMethods) { + require(unitResolver.resolve(method) == unit) + for (sPoint in graph.entryPoint(method)) { + for (sFact in flowSpace.obtainPossibleStartFacts(sPoint)) { + val vertex = IfdsVertex(sPoint, sFact) + val edge = IfdsEdge(vertex, vertex) + propagate(edge, PathEdgePredecessor(edge, PredecessorKind.NoPredecessor)) + } + } + } + } + + /** + * Sends given [fact] to [summary], also remembering to restore [TraceGraph], if needed. + */ private fun stashSummaryFact(fact: SummaryFact) { val handler = summaryHandlers.computeIfAbsent(fact.method) { summary.createSender(fact.method) } handler.send(fact) @@ -77,17 +108,14 @@ private class IfdsInstance( } } - private fun addStartMethod(method: JcMethod) { - require(unitResolver.resolve(method) == unit) - for (sPoint in graph.entryPoint(method)) { - for (sFact in flowSpace.obtainPossibleStartFacts(sPoint)) { - val vertex = IfdsVertex(sPoint, sFact) - val edge = IfdsEdge(vertex, vertex) - propagate(edge, PathEdgePredecessor(edge, PredecessorKind.NoPredecessor)) - } - } - } - + /** + * This method should be called each time new path edge is observed. + * It will check if the edge is new and, if success, add it to [workList] + * and summarize all [SummaryFact]s produces by the edge. + * + * @param e the new path edge + * @param pred the description of predecessor of the edge + */ private fun propagate(e: IfdsEdge, pred: PathEdgePredecessor): Boolean { pathEdgesPreds.computeIfAbsent(e) { ConcurrentHashMap.newKeySet() @@ -112,16 +140,22 @@ private class IfdsInstance( private val JcMethod.isExtern: Boolean get() = unitResolver.resolve(this) != unit - private val workList = Channel(Channel.UNLIMITED) - + /** + * Implementation of tabulation algorithm, based on RHS95. It slightly differs from the original in the following: + * + * - We do not analyze the whole supergraph (represented by [graph]), but only the methods that belong to our [unit]; + * - Path edges are added to [workList] not only by the main cycle, but they can also be obtained from [summary]; + * - By summary edge we understand the path edge from the start node of the method to its exit node; + * - The supergraph is explored dynamically, and we do not inverse flow functions when new summary edge is found, i.e. + * the extension from Chapter 4 of NLR10 is implemented. + */ private suspend fun runTabulationAlgorithm(): Unit = coroutineScope { - startMethods.forEach { addStartMethod(it) } - while (isActive) { val curEdge = workList.tryReceive().getOrNull() ?: throw EmptyQueueCancellationException if (visitedMethods.add(curEdge.method)) { - summary // Listen for incoming updates + // Listen for incoming updates + summary .getFactsFiltered(curEdge.method, null) .filter { it.edge.u.statement in graph.entryPoint(curEdge.method) } // Filter out backward edges .onEach { propagate(it.edge, PathEdgePredecessor(it.edge, PredecessorKind.Unknown)) } @@ -132,30 +166,39 @@ private class IfdsInstance( val (curVertex, curFact) = v val callees = graph.callees(curVertex).toList() - if (callees.isNotEmpty()) { + val curVertexIsCall = callees.isNotEmpty() + val curVertexIsExit = curVertex in graph.exitPoints(graph.methodOf(curVertex)) + + if (curVertexIsCall) { for (returnSite in graph.successors(curVertex)) { + // Propagating through call-to-return-site edges (in RHS95 it is done in lines 17-19) for (fact in flowSpace.obtainCallToReturnFlowFunction(curVertex, returnSite).compute(curFact)) { val newEdge = IfdsEdge(u, IfdsVertex(returnSite, fact)) propagate(newEdge, PathEdgePredecessor(curEdge, PredecessorKind.Sequent)) } + for (callee in callees) { val factsAtStart = flowSpace.obtainCallToStartFlowFunction(curVertex, callee).compute(curFact) for (sPoint in graph.entryPoint(callee)) { for (sFact in factsAtStart) { val sVertex = IfdsVertex(sPoint, sFact) + // Requesting to analyze callee from sVertex, similar to lines 14-16 of RHS95 + // Also, receiving summary edges for callee that start from sVertex val exitVertices: Flow = if (callee.isExtern) { -// stashSummaryFact(PathEdgeFact(IfdsEdge(sVertex, sVertex))) // Requesting info for this start fact +// stashSummaryFact(PathEdgeFact(IfdsEdge(sVertex, sVertex))) summary.getFactsFiltered(callee, sVertex) .filter { it.edge.u == sVertex && it.edge.v.statement in graph.exitPoints(callee) } .map { it.edge.v } } else { val nextEdge = IfdsEdge(sVertex, sVertex) propagate(nextEdge, PathEdgePredecessor(curEdge, PredecessorKind.CallToStart)) + // .toList() is needed below to avoid ConcurrentModificationException summaryEdges[sVertex].orEmpty().toList().asFlow() } + // Propagation through summary edges exitVertices.onEach { (eStatement, eFact) -> val finalFacts = flowSpace.obtainExitToReturnSiteFlowFunction(curVertex, returnSite, eStatement) @@ -167,6 +210,7 @@ private class IfdsInstance( } }.launchIn(this) + // Saving additional info if (callee.isExtern) { if (analyzer.saveSummaryEdgesAndCrossUnitCalls) { // launch { @@ -185,7 +229,9 @@ private class IfdsInstance( } } } else { - if (curVertex in graph.exitPoints(graph.methodOf(curVertex))) { + if (curVertexIsExit) { + // Propagating through newly found summary edge, similar to lines 22-31 of RHS95 + // TODO: rewrite this in a more reactive way for (callerEdge in callSitesOf[u].orEmpty()) { val callerStatement = callerEdge.v.statement for (returnSite in graph.successors(callerStatement)) { @@ -199,6 +245,9 @@ private class IfdsInstance( summaryEdges.getOrPut(curEdge.u) { mutableSetOf() }.add(curEdge.v) } + // Simple propagation through intraprocedural edge, as in lines 34-36 of RHS95 + // Note that generally speaking, exit vertices may have successors (in case of exceptional flow, etc.), + // so this part should be done for exit vertices as well for (nextInst in graph.successors(curVertex)) { val nextFacts = flowSpace.obtainSequentFlowFunction(curVertex, nextInst).compute(curFact) for (nextFact in nextFacts) { @@ -211,7 +260,7 @@ private class IfdsInstance( } } - private val fullResults: IfdsResult by lazy { + private val ifdsResult: IfdsResult by lazy { val allEdges = pathEdges.toList() val resultFacts = allEdges.groupBy({ it.v.statement }) { @@ -221,17 +270,23 @@ private class IfdsInstance( IfdsResult(allEdges, resultFacts, pathEdgesPreds) } + /** + * Finds summary facts that need [IfdsResult] and restores traces for all vertices that need it + */ private fun postActions() { - analyzer.getSummaryFactsPostIfds(fullResults).forEach { vulnerability -> + analyzer.getSummaryFactsPostIfds(ifdsResult).forEach { vulnerability -> stashSummaryFact(vulnerability) } verticesWithTraceGraphNeeded.forEach { vertex -> - val graph = fullResults.resolveTraceGraph(vertex) + val graph = ifdsResult.resolveTraceGraph(vertex) stashSummaryFact(TraceGraphFact(graph)) } } + /** + * Runs tabulation algorithm and updates [summary] with everything that is relevant. + */ suspend fun analyze() = coroutineScope { try { runTabulationAlgorithm() diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsEdge.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsEdge.kt index cea466ea3..3e081a9b7 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsEdge.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsEdge.kt @@ -18,6 +18,9 @@ package org.jacodb.analysis.engine import org.jacodb.api.JcMethod +/** + * Represents a directed (from [u] to [v]) edge between two ifds vertices + */ data class IfdsEdge(val u: IfdsVertex, val v: IfdsVertex) { init { require(u.method == v.method) @@ -35,6 +38,10 @@ sealed interface PredecessorKind { class ThroughSummary(val summaryEdge: IfdsEdge) : PredecessorKind } +/** + * Contains info about predecessor of path edge. + * Used mainly to restore traces. + */ data class PathEdgePredecessor( val predEdge: IfdsEdge, val kind: PredecessorKind diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsResult.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsResult.kt index fbcdaf1ec..23971e4dc 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsResult.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsResult.kt @@ -17,6 +17,9 @@ package org.jacodb.analysis.engine import org.jacodb.api.cfg.JcInst +/** + * Aggregates all facts and edges found by tabulation algorithm + */ class IfdsResult( val pathEdges: List, val resultFacts: Map>, @@ -102,6 +105,9 @@ class IfdsResult( } } + /** + * Builds a graph with traces to given [vertex]. + */ fun resolveTraceGraph(vertex: IfdsVertex): TraceGraph { return TraceGraphBuilder(vertex).build() } diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsUnitManager.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsUnitManager.kt index a8fd61206..c23b2ab19 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsUnitManager.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsUnitManager.kt @@ -27,16 +27,21 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withTimeout import org.jacodb.analysis.VulnerabilityInstance import org.jacodb.analysis.logger +import org.jacodb.analysis.graph.newApplicationGraphForAnalysis import org.jacodb.api.JcMethod import org.jacodb.api.analysis.JcApplicationGraph import java.util.concurrent.ConcurrentHashMap +/** + * Used to launch and manage [ifdsUnitRunner] jobs for units, reachable from [startMethods]. + * See [runAnalysis] for more info. + */ class IfdsUnitManager( private val graph: JcApplicationGraph, private val unitResolver: UnitResolver, private val ifdsUnitRunner: IfdsUnitRunner, - private val timeoutMillis: Long, - private val initMethods: List + private val startMethods: List, + private val timeoutMillis: Long ) { private val unitsQueue: MutableSet = mutableSetOf() @@ -46,7 +51,7 @@ class IfdsUnitManager( private val summary: Summary = SummaryImpl() init { - initMethods.forEach { addStart(it) } + startMethods.forEach { addStart(it) } } private val IfdsVertex.traceGraph: TraceGraph @@ -56,6 +61,10 @@ class IfdsUnitManager( .singleOrNull { it.sink == this } ?: TraceGraph.bySink(this) + /** + * Launches [ifdsUnitRunner] for each observed unit, handles respective jobs, + * and gathers results into list of vulnerabilities, restoring full traces + */ fun analyze(): List = runBlocking(Dispatchers.Default) { val unitJobs: MutableMap = ConcurrentHashMap() val unitsCount = unitsQueue.size @@ -101,7 +110,7 @@ class IfdsUnitManager( .map { VulnerabilityInstance(it.vulnerabilityType, extendTraceGraph(it.sink.traceGraph)) } .filter { it.traceGraph.sources.any { source -> - graph.methodOf(source.statement) in initMethods || source.domainFact == ZEROFact + graph.methodOf(source.statement) in startMethods || source.domainFact == ZEROFact } } } @@ -112,6 +121,12 @@ class IfdsUnitManager( listOf(graph.methodOf(sink.statement))).distinct() } + /** + * Given a [traceGraph], searches for other traceGraphs (from different units) + * and merges them into given if they extend any path leading to sink. + * + * This method allows to restore traces that pass through several units. + */ private fun extendTraceGraph(traceGraph: TraceGraph): TraceGraph { var result = traceGraph val methodQueue: MutableSet = traceGraph.methods.toMutableSet() @@ -162,6 +177,33 @@ class IfdsUnitManager( } } +/** + * This is the entry point for every analysis. + * Calling this function will find all vulnerabilities reachable from [methods]. + * + * @param graph instance of [JcApplicationGraph] that provides mixture of CFG and call graph + * (called supergraph in RHS95). + * Usually built by [newApplicationGraphForAnalysis]. + * + * @param unitResolver instance of [UnitResolver] which splits all methods into groups of methods, called units. + * Units are analyzed concurrently, one unit will be analyzed with one call to [IfdsUnitRunner.run] method. + * In general, larger units mean more precise, but also more resource-consuming analysis, so [unitResolver] allows + * to reach compromise. + * It is guaranteed that [Summary] passed to all units is the same, so they can share information through it. + * However, the order of launching and terminating analysis for units is an implementation detail and may vary even for + * consecutive calls of this method with same arguments. + * + * @param ifdsUnitRunner an [IfdsUnitRunner] instance that will be launched for each unit. + * This is the main argument that defines the analysis. + * + * @param methods the list of method for analysis. + * Each vulnerability will only be reported if it is reachable from one of these. + * + * @param timeoutMillis the maximum time for analysis. + * Note that this does not include time for precalculations + * (like searching for reachable methods and splitting them into units) and postcalculations (like restoring traces), so + * the actual running time of this method may be longer. + */ fun runAnalysis( graph: JcApplicationGraph, unitResolver: UnitResolver<*>, @@ -169,5 +211,5 @@ fun runAnalysis( methods: List, timeoutMillis: Long = Long.MAX_VALUE ): List { - return IfdsUnitManager(graph, unitResolver, ifdsUnitRunner, timeoutMillis, methods).analyze() + return IfdsUnitManager(graph, unitResolver, ifdsUnitRunner, methods, timeoutMillis).analyze() } \ No newline at end of file diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsUnitRunner.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsUnitRunner.kt index 69d2099a9..3416bbfbb 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsUnitRunner.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsUnitRunner.kt @@ -22,7 +22,23 @@ import org.jacodb.analysis.graph.reversed import org.jacodb.api.JcMethod import org.jacodb.api.analysis.JcApplicationGraph +/** + * Allows to run analysis for any given unit. + * Note that multiple calls of [run] can be made concurrently for one instance of runner, + * so the implementations should be thread-safe. + */ interface IfdsUnitRunner { + /** + * Launches some analysis for given [unit], using given [startMethods] as entry points. + * All start methods should belong to the [unit]. + * + * @param graph provides supergraph for application (including methods, belonging to other units) + * + * @param summary can be used as a knowledge base about other units. + * Also, anything observed during analysis may be saved to [summary] so that it can be seen by other units. + * + * @param unitResolver may be used to get units of methods observed during analysis. + */ suspend fun run( graph: JcApplicationGraph, summary: Summary, @@ -32,6 +48,13 @@ interface IfdsUnitRunner { ) } +/** + * A composite [IfdsUnitRunner] that launches two runners (backward and forward) in parallel + * on the same unit with the same startMethods. + * + * [backwardRunner] is launched on the reversed application graph, + * while [forwardRunner] is launched on the direct graph. + */ class ParallelBidiIfdsUnitRunner( private val forwardRunner: IfdsUnitRunner, private val backwardRunner: IfdsUnitRunner @@ -53,9 +76,16 @@ class ParallelBidiIfdsUnitRunner( } } +/** + * A composite [IfdsUnitRunner] that launches two runners (backward and forward) sequentially + * on the same unit with the same startMethods. + * + * First, it launches [backwardRunner] on the reversed graph and waits for its completion. + * Then it launches [forwardRunner] on the direct graph. + */ class SequentialBidiIfdsUnitRunner( - private val forward: IfdsUnitRunner, - private val backward: IfdsUnitRunner, + private val forwardRunner: IfdsUnitRunner, + private val backwardRunner: IfdsUnitRunner, ) : IfdsUnitRunner { override suspend fun run( @@ -65,8 +95,8 @@ class SequentialBidiIfdsUnitRunner( unit: UnitType, startMethods: List ) { - backward.run(graph.reversed, summary, unitResolver, unit, startMethods) + backwardRunner.run(graph.reversed, summary, unitResolver, unit, startMethods) - forward.run(graph, summary, unitResolver, unit, startMethods) + forwardRunner.run(graph, summary, unitResolver, unit, startMethods) } } \ No newline at end of file diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/TraceGraph.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/TraceGraph.kt index 693defd7f..da95389e5 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/TraceGraph.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/TraceGraph.kt @@ -18,7 +18,13 @@ package org.jacodb.analysis.engine import org.jacodb.analysis.DumpableVulnerabilityInstance -class TraceGraph( +/** + * A directed graph with selected [sink] and [sources], where each path from one of [sources] to [sink] is a trace. + * + * @property sink is usually some interesting vertex that we want to reach (e.g. vertex that produces vulnerability) + * @property sources are the entry points, e.g. the vertices with [ZEROFact] or method starts + */ +data class TraceGraph( val sink: IfdsVertex, val sources: Set, val edges: Map>, @@ -41,6 +47,9 @@ class TraceGraph( } } + /** + * Returns a sequence with all traces from [sources] to [sink] + */ fun getAllTraces(): Sequence> = sequence { sources.forEach { yieldAll(getAllTraces(mutableListOf(it))) @@ -58,6 +67,15 @@ class TraceGraph( ) } + /** + * Merges two graphs. + * + * [sink] will be chosen from receiver, and edges from both graphs will be merged. + * Also, all edges from [upGraph]'s sink to [entryPoints] will be added + * (these are edges "connecting" [upGraph] with receiver). + * + * Informally, this method extends receiver's traces from one side using [upGraph]. + */ fun mergeWithUpGraph(upGraph: TraceGraph, entryPoints: Set): TraceGraph { val validEntryPoints = entryPoints.intersect(edges.keys).ifEmpty { return this diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/UnitResolvers.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/UnitResolvers.kt index 80fd57307..32050b041 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/UnitResolvers.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/UnitResolvers.kt @@ -20,6 +20,14 @@ import org.jacodb.api.JcClassOrInterface import org.jacodb.api.JcMethod import org.jacodb.api.ext.packageName +/** + * Sets a mapping from [JcMethod] to abstract domain [UnitType]. + * + * Therefore, it splits all methods into units, containing one or more method each + * (unit is a set of methods with same value of [UnitType] returned by [resolve]). + * + * To get more info about how it is used in analysis, see [runAnalysis]. + */ fun interface UnitResolver { fun resolve(method: JcMethod): UnitType diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/JcApplicationGraphImpl.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/JcApplicationGraphImpl.kt index 488f9fc63..dafa1c9cd 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/JcApplicationGraphImpl.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/JcApplicationGraphImpl.kt @@ -75,7 +75,10 @@ open class JcApplicationGraphImpl( } } -suspend fun JcClasspath.newApplicationGraph(bannedPackagePrefixes: List? = null): JcApplicationGraph { +/** + * Creates an instance of [SimplifiedJcApplicationGraph], see its docs for more info. + */ +suspend fun JcClasspath.newApplicationGraphForAnalysis(bannedPackagePrefixes: List? = null): JcApplicationGraph { val mainGraph = JcApplicationGraphImpl(this, usagesExt()) return if (bannedPackagePrefixes != null) { SimplifiedJcApplicationGraph(mainGraph, bannedPackagePrefixes) 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 f17b52017..cbb5ef091 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 @@ -22,7 +22,7 @@ import org.jacodb.analysis.analyzers.TaintNode import org.jacodb.analysis.engine.MethodUnitResolver import org.jacodb.analysis.engine.runAnalysis import org.jacodb.analysis.graph.SimplifiedJcApplicationGraph -import org.jacodb.analysis.graph.newApplicationGraph +import org.jacodb.analysis.graph.newApplicationGraphForAnalysis import org.jacodb.analysis.newAliasRunner import org.jacodb.analysis.paths.toPath import org.jacodb.analysis.toDumpable @@ -148,7 +148,7 @@ class AliasAnalysisTest : BaseTest() { .plus("pointerbench.benchmark.internal") val graph = runBlocking { - cp.newApplicationGraph( + cp.newApplicationGraphForAnalysis( SimplifiedJcApplicationGraph.defaultBannedPackagePrefixes + bannedPackagePrefixes ) } 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 040904360..2d60bc44e 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 @@ -25,7 +25,7 @@ import org.jacodb.analysis.engine.IfdsUnitRunner import org.jacodb.analysis.engine.MethodUnitResolver import org.jacodb.analysis.engine.UnitResolver import org.jacodb.analysis.engine.runAnalysis -import org.jacodb.analysis.graph.newApplicationGraph +import org.jacodb.analysis.graph.newApplicationGraphForAnalysis import org.jacodb.analysis.newNpeRunner import org.jacodb.analysis.toDumpable import org.jacodb.api.ext.findClass @@ -59,6 +59,6 @@ class JodaDateTimeAnalysisTest : BaseTest() { } private val graph = runBlocking { - cp.newApplicationGraph() + cp.newApplicationGraphForAnalysis() } } \ No newline at end of file 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 bde766a0f..db9ecd80c 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 @@ -22,7 +22,7 @@ import org.jacodb.analysis.analyzers.NpeAnalyzer import org.jacodb.analysis.engine.SingletonUnitResolver import org.jacodb.analysis.engine.runAnalysis import org.jacodb.analysis.graph.JcApplicationGraphImpl -import org.jacodb.analysis.graph.newApplicationGraph +import org.jacodb.analysis.graph.newApplicationGraphForAnalysis import org.jacodb.analysis.newNpeRunner import org.jacodb.api.JcMethod import org.jacodb.api.ext.constructors @@ -213,7 +213,7 @@ class NpeAnalysisTest : BaseAnalysisTest() { override fun launchAnalysis(methods: List): List { val graph = runBlocking { - cp.newApplicationGraph() + cp.newApplicationGraphForAnalysis() } return runAnalysis(graph, SingletonUnitResolver, newNpeRunner(), methods) } diff --git a/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/SqlInjectionAnalysisTest.kt b/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/SqlInjectionAnalysisTest.kt index dbb8da392..9b90b6797 100644 --- a/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/SqlInjectionAnalysisTest.kt +++ b/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/SqlInjectionAnalysisTest.kt @@ -21,7 +21,7 @@ import org.jacodb.analysis.VulnerabilityInstance import org.jacodb.analysis.analyzers.TaintAnalyzer import org.jacodb.analysis.engine.SingletonUnitResolver import org.jacodb.analysis.engine.runAnalysis -import org.jacodb.analysis.graph.newApplicationGraph +import org.jacodb.analysis.graph.newApplicationGraphForAnalysis import org.jacodb.analysis.newSqlInjectionRunner import org.jacodb.api.JcMethod import org.jacodb.impl.features.InMemoryHierarchy @@ -48,7 +48,7 @@ class SqlInjectionAnalysisTest : BaseAnalysisTest() { override fun launchAnalysis(methods: List): List { val graph = runBlocking { - cp.newApplicationGraph() + cp.newApplicationGraphForAnalysis() } return runAnalysis(graph, SingletonUnitResolver, newSqlInjectionRunner(), methods) } 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 8440c72c3..9c8bd8279 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 @@ -22,7 +22,7 @@ import org.jacodb.analysis.VulnerabilityInstance import org.jacodb.analysis.analyzers.UnusedVariableAnalyzer import org.jacodb.analysis.engine.SingletonUnitResolver import org.jacodb.analysis.engine.runAnalysis -import org.jacodb.analysis.graph.newApplicationGraph +import org.jacodb.analysis.graph.newApplicationGraphForAnalysis import org.jacodb.api.JcMethod import org.jacodb.impl.features.InMemoryHierarchy import org.jacodb.impl.features.Usages @@ -62,7 +62,7 @@ class UnusedVariableTest : BaseAnalysisTest() { override fun launchAnalysis(methods: List): List { val graph = runBlocking { - cp.newApplicationGraph() + cp.newApplicationGraphForAnalysis() } return runAnalysis(graph, SingletonUnitResolver, UnusedVariableRunner, methods) } diff --git a/jacodb-api/src/main/kotlin/org/jacodb/api/analysis/ApplicationGraph.kt b/jacodb-api/src/main/kotlin/org/jacodb/api/analysis/ApplicationGraph.kt index b2136c57c..000da830a 100644 --- a/jacodb-api/src/main/kotlin/org/jacodb/api/analysis/ApplicationGraph.kt +++ b/jacodb-api/src/main/kotlin/org/jacodb/api/analysis/ApplicationGraph.kt @@ -16,6 +16,9 @@ package org.jacodb.api.analysis +/** + * Provides both CFG and call graph (i.e., the supergraph in terms of RHS95 paper). + */ interface ApplicationGraph { fun predecessors(node: Statement): Sequence fun successors(node: Statement): Sequence diff --git a/jacodb-api/src/main/kotlin/org/jacodb/api/analysis/JcApplicationGraph.kt b/jacodb-api/src/main/kotlin/org/jacodb/api/analysis/JcApplicationGraph.kt index 0df975fe6..ab72101b8 100644 --- a/jacodb-api/src/main/kotlin/org/jacodb/api/analysis/JcApplicationGraph.kt +++ b/jacodb-api/src/main/kotlin/org/jacodb/api/analysis/JcApplicationGraph.kt @@ -20,6 +20,9 @@ import org.jacodb.api.JcClasspath import org.jacodb.api.JcMethod import org.jacodb.api.cfg.JcInst +/** + * Interface for [ApplicationGraph] built with jacodb. + */ interface JcApplicationGraph : ApplicationGraph { val classpath: JcClasspath } \ 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 index 6bae47ecd..fc7193d73 100644 --- a/jacodb-cli/src/main/kotlin/org/jacodb/cli/main.kt +++ b/jacodb-cli/src/main/kotlin/org/jacodb/cli/main.kt @@ -31,7 +31,7 @@ import org.jacodb.analysis.VulnerabilityInstance import org.jacodb.analysis.engine.MethodUnitResolver import org.jacodb.analysis.engine.UnitResolver import org.jacodb.analysis.engine.runAnalysis -import org.jacodb.analysis.graph.newApplicationGraph +import org.jacodb.analysis.graph.newApplicationGraphForAnalysis import org.jacodb.analysis.newNpeRunner import org.jacodb.analysis.newSqlInjectionRunner import org.jacodb.analysis.toDumpable @@ -45,7 +45,6 @@ import java.io.File private val logger = object : KLogging() {}.logger - class AnalysisMain { fun run(args: List) = main(args.toTypedArray()) } @@ -66,6 +65,7 @@ fun launchAnalysesByConfig(config: AnalysisConfig, graph: JcApplicationGraph, me } } + logger.info { "Launching analysis $analysis" } runAnalysis(graph, unitResolver, runner, methods) } } @@ -134,8 +134,9 @@ fun main(args: Array) { } val graph = runBlocking { - cp.newApplicationGraph() + cp.newApplicationGraphForAnalysis() } + val startJcClasses = startClasses.split(";").map { cp.findClass(it) } val startJcMethods = startJcClasses.flatMap { it.declaredMethods } From 98fcc99b66a810b6f7a7cb7e9226cdcda75884cf Mon Sep 17 00:00:00 2001 From: Ivan Volkov Date: Fri, 21 Jul 2023 18:31:04 +0300 Subject: [PATCH 2/6] [jacodb-ifds] Minor refactoring to improve logic and Java API --- .../org/jacodb/analysis/AnalysisMain.kt | 96 +++++++++---------- .../org/jacodb/analysis/RunnersLibrary.kt | 73 ++++++++++++++ .../jacodb/analysis/engine/IfdsUnitManager.kt | 39 +------- .../jacodb/analysis/impl/AliasAnalysisTest.kt | 2 +- .../analysis/impl/JodaDateTimeAnalysisTest.kt | 2 +- .../jacodb/analysis/impl/NpeAnalysisTest.kt | 2 +- .../analysis/impl/SqlInjectionAnalysisTest.kt | 2 +- .../analysis/impl/UnusedVariableTest.kt | 2 +- .../src/main/kotlin/org/jacodb/cli/main.kt | 2 +- 9 files changed, 123 insertions(+), 97 deletions(-) create mode 100644 jacodb-analysis/src/main/kotlin/org/jacodb/analysis/RunnersLibrary.kt 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 451c7d0bb..4f9cf3af4 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/AnalysisMain.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/AnalysisMain.kt @@ -17,21 +17,14 @@ package org.jacodb.analysis import kotlinx.serialization.Serializable import mu.KLogging -import org.jacodb.analysis.analyzers.AliasAnalyzerFactory -import org.jacodb.analysis.analyzers.NpeAnalyzerFactory -import org.jacodb.analysis.analyzers.NpePrecalcBackwardAnalyzerFactory -import org.jacodb.analysis.analyzers.SqlInjectionAnalyzerFactory -import org.jacodb.analysis.analyzers.SqlInjectionBackwardAnalyzerFactory -import org.jacodb.analysis.analyzers.TaintAnalysisNode -import org.jacodb.analysis.analyzers.TaintAnalyzerFactory -import org.jacodb.analysis.analyzers.TaintBackwardAnalyzerFactory -import org.jacodb.analysis.analyzers.TaintNode -import org.jacodb.analysis.analyzers.UnusedVariableAnalyzerFactory -import org.jacodb.analysis.engine.IfdsBaseUnitRunner -import org.jacodb.analysis.engine.SequentialBidiIfdsUnitRunner +import org.jacodb.analysis.engine.IfdsUnitManager +import org.jacodb.analysis.engine.IfdsUnitRunner +import org.jacodb.analysis.engine.Summary import org.jacodb.analysis.engine.TraceGraph +import org.jacodb.analysis.engine.UnitResolver +import org.jacodb.analysis.graph.newApplicationGraphForAnalysis import org.jacodb.api.JcMethod -import org.jacodb.api.cfg.JcExpr +import org.jacodb.api.analysis.JcApplicationGraph import org.jacodb.api.cfg.JcInst /** @@ -83,44 +76,41 @@ typealias AnalysesOptions = Map @Serializable data class AnalysisConfig(val analyses: Map) -val UnusedVariableRunner = IfdsBaseUnitRunner(UnusedVariableAnalyzerFactory) +internal val logger = object : KLogging() {}.logger -fun newSqlInjectionRunner(maxPathLength: Int = 5) = SequentialBidiIfdsUnitRunner( - IfdsBaseUnitRunner(SqlInjectionAnalyzerFactory(maxPathLength)), - IfdsBaseUnitRunner(SqlInjectionBackwardAnalyzerFactory(maxPathLength)), -) - -fun newNpeRunner(maxPathLength: Int = 5) = SequentialBidiIfdsUnitRunner( - IfdsBaseUnitRunner(NpeAnalyzerFactory(maxPathLength)), - IfdsBaseUnitRunner(NpePrecalcBackwardAnalyzerFactory(maxPathLength)), -) - -fun newAliasRunner( - generates: (JcInst) -> List, - sanitizes: (JcExpr, TaintNode) -> Boolean, - sinks: (JcInst) -> List, - maxPathLength: Int = 5 -) = IfdsBaseUnitRunner(AliasAnalyzerFactory(generates, sanitizes, sinks, maxPathLength)) - -fun newTaintRunner( - isSourceMethod: (JcMethod) -> Boolean, - isSanitizeMethod: (JcMethod) -> Boolean, - isSinkMethod: (JcMethod) -> Boolean, - maxPathLength: Int = 5 -) = SequentialBidiIfdsUnitRunner( - IfdsBaseUnitRunner(TaintAnalyzerFactory(isSourceMethod, isSanitizeMethod, isSinkMethod, maxPathLength)), - IfdsBaseUnitRunner(TaintBackwardAnalyzerFactory(isSourceMethod, isSinkMethod, maxPathLength)) -) - -fun newTaintRunner( - sourceMethodMatchers: List, - sanitizeMethodMatchers: List, - sinkMethodMatchers: List, - maxPathLength: Int = 5 -) = SequentialBidiIfdsUnitRunner( - IfdsBaseUnitRunner(TaintAnalyzerFactory(sourceMethodMatchers, sanitizeMethodMatchers, sinkMethodMatchers, maxPathLength)), - IfdsBaseUnitRunner(TaintBackwardAnalyzerFactory(sourceMethodMatchers, sinkMethodMatchers, maxPathLength)) -) - - -internal val logger = object : KLogging() {}.logger \ No newline at end of file +/** + * This is the entry point for every analysis. + * Calling this function will find all vulnerabilities reachable from [methods]. + * + * @param graph instance of [JcApplicationGraph] that provides mixture of CFG and call graph + * (called supergraph in RHS95). + * Usually built by [newApplicationGraphForAnalysis]. + * + * @param unitResolver instance of [UnitResolver] which splits all methods into groups of methods, called units. + * Units are analyzed concurrently, one unit will be analyzed with one call to [IfdsUnitRunner.run] method. + * In general, larger units mean more precise, but also more resource-consuming analysis, so [unitResolver] allows + * to reach compromise. + * It is guaranteed that [Summary] passed to all units is the same, so they can share information through it. + * However, the order of launching and terminating analysis for units is an implementation detail and may vary even for + * consecutive calls of this method with same arguments. + * + * @param ifdsUnitRunner an [IfdsUnitRunner] instance that will be launched for each unit. + * This is the main argument that defines the analysis. + * + * @param methods the list of method for analysis. + * Each vulnerability will only be reported if it is reachable from one of these. + * + * @param timeoutMillis the maximum time for analysis. + * Note that this does not include time for precalculations + * (like searching for reachable methods and splitting them into units) and postcalculations (like restoring traces), so + * the actual running time of this method may be longer. + */ +fun runAnalysis( + graph: JcApplicationGraph, + unitResolver: UnitResolver<*>, + ifdsUnitRunner: IfdsUnitRunner, + methods: List, + timeoutMillis: Long = Long.MAX_VALUE +): List { + return IfdsUnitManager(graph, unitResolver, ifdsUnitRunner, methods, timeoutMillis).analyze() +} \ No newline at end of file diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/RunnersLibrary.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/RunnersLibrary.kt new file mode 100644 index 000000000..900887c1f --- /dev/null +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/RunnersLibrary.kt @@ -0,0 +1,73 @@ +/* + * 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. + */ + +@file:JvmName("RunnersLibrary") +package org.jacodb.analysis + +import org.jacodb.analysis.analyzers.AliasAnalyzerFactory +import org.jacodb.analysis.analyzers.NpeAnalyzerFactory +import org.jacodb.analysis.analyzers.NpePrecalcBackwardAnalyzerFactory +import org.jacodb.analysis.analyzers.SqlInjectionAnalyzerFactory +import org.jacodb.analysis.analyzers.SqlInjectionBackwardAnalyzerFactory +import org.jacodb.analysis.analyzers.TaintAnalysisNode +import org.jacodb.analysis.analyzers.TaintAnalyzerFactory +import org.jacodb.analysis.analyzers.TaintBackwardAnalyzerFactory +import org.jacodb.analysis.analyzers.TaintNode +import org.jacodb.analysis.analyzers.UnusedVariableAnalyzerFactory +import org.jacodb.analysis.engine.IfdsBaseUnitRunner +import org.jacodb.analysis.engine.SequentialBidiIfdsUnitRunner +import org.jacodb.api.JcMethod +import org.jacodb.api.cfg.JcExpr +import org.jacodb.api.cfg.JcInst + +val UnusedVariableRunner = IfdsBaseUnitRunner(UnusedVariableAnalyzerFactory) + +fun newSqlInjectionRunner(maxPathLength: Int = 5) = SequentialBidiIfdsUnitRunner( + IfdsBaseUnitRunner(SqlInjectionAnalyzerFactory(maxPathLength)), + IfdsBaseUnitRunner(SqlInjectionBackwardAnalyzerFactory(maxPathLength)), +) + +fun newNpeRunner(maxPathLength: Int = 5) = SequentialBidiIfdsUnitRunner( + IfdsBaseUnitRunner(NpeAnalyzerFactory(maxPathLength)), + IfdsBaseUnitRunner(NpePrecalcBackwardAnalyzerFactory(maxPathLength)), +) + +fun newAliasRunner( + generates: (JcInst) -> List, + sanitizes: (JcExpr, TaintNode) -> Boolean, + sinks: (JcInst) -> List, + maxPathLength: Int = 5 +) = IfdsBaseUnitRunner(AliasAnalyzerFactory(generates, sanitizes, sinks, maxPathLength)) + +fun newTaintRunner( + isSourceMethod: (JcMethod) -> Boolean, + isSanitizeMethod: (JcMethod) -> Boolean, + isSinkMethod: (JcMethod) -> Boolean, + maxPathLength: Int = 5 +) = SequentialBidiIfdsUnitRunner( + IfdsBaseUnitRunner(TaintAnalyzerFactory(isSourceMethod, isSanitizeMethod, isSinkMethod, maxPathLength)), + IfdsBaseUnitRunner(TaintBackwardAnalyzerFactory(isSourceMethod, isSinkMethod, maxPathLength)) +) + +fun newTaintRunner( + sourceMethodMatchers: List, + sanitizeMethodMatchers: List, + sinkMethodMatchers: List, + maxPathLength: Int = 5 +) = SequentialBidiIfdsUnitRunner( + IfdsBaseUnitRunner(TaintAnalyzerFactory(sourceMethodMatchers, sanitizeMethodMatchers, sinkMethodMatchers, maxPathLength)), + IfdsBaseUnitRunner(TaintBackwardAnalyzerFactory(sourceMethodMatchers, sinkMethodMatchers, maxPathLength)) +) \ No newline at end of file diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsUnitManager.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsUnitManager.kt index c23b2ab19..401032f63 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsUnitManager.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsUnitManager.kt @@ -27,7 +27,7 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withTimeout import org.jacodb.analysis.VulnerabilityInstance import org.jacodb.analysis.logger -import org.jacodb.analysis.graph.newApplicationGraphForAnalysis +import org.jacodb.analysis.runAnalysis import org.jacodb.api.JcMethod import org.jacodb.api.analysis.JcApplicationGraph import java.util.concurrent.ConcurrentHashMap @@ -175,41 +175,4 @@ class IfdsUnitManager( dependencies.forEach { addStart(it) } dependsOn[unit] = dependsOn.getOrDefault(unit, 0) + dependencies.size } -} - -/** - * This is the entry point for every analysis. - * Calling this function will find all vulnerabilities reachable from [methods]. - * - * @param graph instance of [JcApplicationGraph] that provides mixture of CFG and call graph - * (called supergraph in RHS95). - * Usually built by [newApplicationGraphForAnalysis]. - * - * @param unitResolver instance of [UnitResolver] which splits all methods into groups of methods, called units. - * Units are analyzed concurrently, one unit will be analyzed with one call to [IfdsUnitRunner.run] method. - * In general, larger units mean more precise, but also more resource-consuming analysis, so [unitResolver] allows - * to reach compromise. - * It is guaranteed that [Summary] passed to all units is the same, so they can share information through it. - * However, the order of launching and terminating analysis for units is an implementation detail and may vary even for - * consecutive calls of this method with same arguments. - * - * @param ifdsUnitRunner an [IfdsUnitRunner] instance that will be launched for each unit. - * This is the main argument that defines the analysis. - * - * @param methods the list of method for analysis. - * Each vulnerability will only be reported if it is reachable from one of these. - * - * @param timeoutMillis the maximum time for analysis. - * Note that this does not include time for precalculations - * (like searching for reachable methods and splitting them into units) and postcalculations (like restoring traces), so - * the actual running time of this method may be longer. - */ -fun runAnalysis( - graph: JcApplicationGraph, - unitResolver: UnitResolver<*>, - ifdsUnitRunner: IfdsUnitRunner, - methods: List, - timeoutMillis: Long = Long.MAX_VALUE -): List { - return IfdsUnitManager(graph, unitResolver, ifdsUnitRunner, methods, timeoutMillis).analyze() } \ 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 cbb5ef091..86d52a996 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 @@ -20,7 +20,7 @@ import kotlinx.coroutines.runBlocking import org.jacodb.analysis.analyzers.TaintAnalysisNode import org.jacodb.analysis.analyzers.TaintNode import org.jacodb.analysis.engine.MethodUnitResolver -import org.jacodb.analysis.engine.runAnalysis +import org.jacodb.analysis.runAnalysis import org.jacodb.analysis.graph.SimplifiedJcApplicationGraph import org.jacodb.analysis.graph.newApplicationGraphForAnalysis import org.jacodb.analysis.newAliasRunner 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 2d60bc44e..789ead157 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 @@ -24,9 +24,9 @@ import org.jacodb.analysis.engine.ClassUnitResolver import org.jacodb.analysis.engine.IfdsUnitRunner import org.jacodb.analysis.engine.MethodUnitResolver import org.jacodb.analysis.engine.UnitResolver -import org.jacodb.analysis.engine.runAnalysis import org.jacodb.analysis.graph.newApplicationGraphForAnalysis import org.jacodb.analysis.newNpeRunner +import org.jacodb.analysis.runAnalysis import org.jacodb.analysis.toDumpable import org.jacodb.api.ext.findClass import org.jacodb.impl.features.InMemoryHierarchy 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 db9ecd80c..fd7fd4c9a 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 @@ -20,7 +20,7 @@ import kotlinx.coroutines.runBlocking import org.jacodb.analysis.VulnerabilityInstance import org.jacodb.analysis.analyzers.NpeAnalyzer import org.jacodb.analysis.engine.SingletonUnitResolver -import org.jacodb.analysis.engine.runAnalysis +import org.jacodb.analysis.runAnalysis import org.jacodb.analysis.graph.JcApplicationGraphImpl import org.jacodb.analysis.graph.newApplicationGraphForAnalysis import org.jacodb.analysis.newNpeRunner diff --git a/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/SqlInjectionAnalysisTest.kt b/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/SqlInjectionAnalysisTest.kt index 9b90b6797..319dcbff5 100644 --- a/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/SqlInjectionAnalysisTest.kt +++ b/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/SqlInjectionAnalysisTest.kt @@ -20,7 +20,7 @@ import kotlinx.coroutines.runBlocking import org.jacodb.analysis.VulnerabilityInstance import org.jacodb.analysis.analyzers.TaintAnalyzer import org.jacodb.analysis.engine.SingletonUnitResolver -import org.jacodb.analysis.engine.runAnalysis +import org.jacodb.analysis.runAnalysis import org.jacodb.analysis.graph.newApplicationGraphForAnalysis import org.jacodb.analysis.newSqlInjectionRunner import org.jacodb.api.JcMethod 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 9c8bd8279..df5a1e872 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 @@ -21,7 +21,7 @@ import org.jacodb.analysis.UnusedVariableRunner import org.jacodb.analysis.VulnerabilityInstance import org.jacodb.analysis.analyzers.UnusedVariableAnalyzer import org.jacodb.analysis.engine.SingletonUnitResolver -import org.jacodb.analysis.engine.runAnalysis +import org.jacodb.analysis.runAnalysis import org.jacodb.analysis.graph.newApplicationGraphForAnalysis import org.jacodb.api.JcMethod import org.jacodb.impl.features.InMemoryHierarchy diff --git a/jacodb-cli/src/main/kotlin/org/jacodb/cli/main.kt b/jacodb-cli/src/main/kotlin/org/jacodb/cli/main.kt index fc7193d73..02fa41286 100644 --- a/jacodb-cli/src/main/kotlin/org/jacodb/cli/main.kt +++ b/jacodb-cli/src/main/kotlin/org/jacodb/cli/main.kt @@ -30,7 +30,7 @@ import org.jacodb.analysis.UnusedVariableRunner import org.jacodb.analysis.VulnerabilityInstance import org.jacodb.analysis.engine.MethodUnitResolver import org.jacodb.analysis.engine.UnitResolver -import org.jacodb.analysis.engine.runAnalysis +import org.jacodb.analysis.runAnalysis import org.jacodb.analysis.graph.newApplicationGraphForAnalysis import org.jacodb.analysis.newNpeRunner import org.jacodb.analysis.newSqlInjectionRunner From 02dd99c2c5e416dfbe0f8c1467bdbb33d7a096fa Mon Sep 17 00:00:00 2001 From: Ivan Volkov Date: Tue, 25 Jul 2023 14:51:00 +0300 Subject: [PATCH 3/6] [jacodb-ifds] Better Java api and docs --- .../org/jacodb/analysis/AnalysisMain.kt | 50 +---------- .../kotlin/org/jacodb/analysis/Dumpable.kt | 38 +++++++++ .../jacodb/analysis/engine/IfdsUnitManager.kt | 1 - .../{UnitResolvers.kt => UnitResolver.kt} | 25 ++---- .../analysis/engine/VulnerabilityInstance.kt | 46 ++++++++++ .../analysis/graph/ApplicationGraphFactory.kt | 52 ++++++++++++ ...nGraph.kt => BackwardApplicationGraphs.kt} | 9 +- .../analysis/graph/JcApplicationGraphImpl.kt | 17 +--- .../graph/SimplifiedJcApplicationGraph.kt | 13 +-- .../analysis/{ => library}/RunnersLibrary.kt | 23 ++--- .../analysis/library/UnitResolversLibrary.kt | 41 +++++++++ .../AbstractTaintBackwardFunctions.kt | 2 +- .../AbstractTaintForwardFunctions.kt | 2 +- .../{ => library}/analyzers/AliasAnalyzer.kt | 2 +- .../{ => library}/analyzers/NpeAnalyzer.kt | 2 +- .../analyzers/SqlInjectionAnalyzer.kt | 2 +- .../{ => library}/analyzers/TaintAnalyzer.kt | 2 +- .../{ => library}/analyzers/TaintNode.kt | 2 +- .../analyzers/UnusedVariableAnalyzer.kt | 2 +- .../analysis/{ => library}/analyzers/Util.kt | 2 +- .../analysis/impl/JavaAnalysisApiTest.java | 83 +++++++++++++++++++ .../jacodb/analysis/impl/AliasAnalysisTest.kt | 16 ++-- .../jacodb/analysis/impl/BaseAnalysisTest.kt | 2 +- .../analysis/impl/JodaDateTimeAnalysisTest.kt | 10 +-- .../jacodb/analysis/impl/NpeAnalysisTest.kt | 10 +-- .../analysis/impl/SqlInjectionAnalysisTest.kt | 8 +- .../analysis/impl/UnusedVariableTest.kt | 10 +-- .../src/main/kotlin/org/jacodb/cli/main.kt | 14 ++-- 28 files changed, 336 insertions(+), 150 deletions(-) create mode 100644 jacodb-analysis/src/main/kotlin/org/jacodb/analysis/Dumpable.kt rename jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/{UnitResolvers.kt => UnitResolver.kt} (68%) create mode 100644 jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/VulnerabilityInstance.kt create mode 100644 jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/ApplicationGraphFactory.kt rename jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/{BackwardApplicationGraph.kt => BackwardApplicationGraphs.kt} (89%) rename jacodb-analysis/src/main/kotlin/org/jacodb/analysis/{ => library}/RunnersLibrary.kt (77%) create mode 100644 jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/UnitResolversLibrary.kt rename jacodb-analysis/src/main/kotlin/org/jacodb/analysis/{ => library}/analyzers/AbstractTaintBackwardFunctions.kt (99%) rename jacodb-analysis/src/main/kotlin/org/jacodb/analysis/{ => library}/analyzers/AbstractTaintForwardFunctions.kt (99%) rename jacodb-analysis/src/main/kotlin/org/jacodb/analysis/{ => library}/analyzers/AliasAnalyzer.kt (98%) rename jacodb-analysis/src/main/kotlin/org/jacodb/analysis/{ => library}/analyzers/NpeAnalyzer.kt (99%) rename jacodb-analysis/src/main/kotlin/org/jacodb/analysis/{ => library}/analyzers/SqlInjectionAnalyzer.kt (96%) rename jacodb-analysis/src/main/kotlin/org/jacodb/analysis/{ => library}/analyzers/TaintAnalyzer.kt (99%) rename jacodb-analysis/src/main/kotlin/org/jacodb/analysis/{ => library}/analyzers/TaintNode.kt (98%) rename jacodb-analysis/src/main/kotlin/org/jacodb/analysis/{ => library}/analyzers/UnusedVariableAnalyzer.kt (99%) rename jacodb-analysis/src/main/kotlin/org/jacodb/analysis/{ => library}/analyzers/Util.kt (97%) create mode 100644 jacodb-analysis/src/test/java/org/jacodb/analysis/impl/JavaAnalysisApiTest.java 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 4f9cf3af4..97315d8d9 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/AnalysisMain.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/AnalysisMain.kt @@ -14,69 +14,27 @@ * limitations under the License. */ +@file:JvmName("AnalysisMain") package org.jacodb.analysis + import kotlinx.serialization.Serializable import mu.KLogging import org.jacodb.analysis.engine.IfdsUnitManager import org.jacodb.analysis.engine.IfdsUnitRunner import org.jacodb.analysis.engine.Summary -import org.jacodb.analysis.engine.TraceGraph import org.jacodb.analysis.engine.UnitResolver +import org.jacodb.analysis.engine.VulnerabilityInstance import org.jacodb.analysis.graph.newApplicationGraphForAnalysis import org.jacodb.api.JcMethod import org.jacodb.api.analysis.JcApplicationGraph -import org.jacodb.api.cfg.JcInst - -/** - * Simplified version of [VulnerabilityInstance] that contains only serializable data. - */ -@Serializable -data class DumpableVulnerabilityInstance( - val vulnerabilityType: String, - val sources: List, - val sink: String, - val traces: List> -) - -@Serializable -data class DumpableAnalysisResult(val foundVulnerabilities: List) -/** - * Represents a vulnerability (issue) found by analysis - * - * @property vulnerabilityType type of vulnerability as a string (e.g. "Possible NPE", "Unused variable") - * @property traceGraph contains sink, sources and traces that lead to occurrence of vulnerability - */ -data class VulnerabilityInstance( - val vulnerabilityType: String, - val traceGraph: TraceGraph -) { - private fun JcInst.prettyPrint(): String { - return "${toString()} (${location.method}:${location.lineNumber})" - } - - fun toDumpable(maxPathsCount: Int): DumpableVulnerabilityInstance { - return DumpableVulnerabilityInstance( - vulnerabilityType, - traceGraph.sources.map { it.statement.prettyPrint() }, - traceGraph.sink.statement.prettyPrint(), - traceGraph.getAllTraces().take(maxPathsCount).map { intermediatePoints -> - intermediatePoints.map { it.statement.prettyPrint() } - }.toList() - ) - } -} - -fun List.toDumpable(maxPathsCount: Int = 3): DumpableAnalysisResult { - return DumpableAnalysisResult(map { it.toDumpable(maxPathsCount) }) -} +internal val logger = object : KLogging() {}.logger typealias AnalysesOptions = Map @Serializable data class AnalysisConfig(val analyses: Map) -internal val logger = object : KLogging() {}.logger /** * This is the entry point for every analysis. diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/Dumpable.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/Dumpable.kt new file mode 100644 index 000000000..de0c5d91f --- /dev/null +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/Dumpable.kt @@ -0,0 +1,38 @@ +/* + * 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.analysis + +import kotlinx.serialization.Serializable +import org.jacodb.analysis.engine.VulnerabilityInstance + +/** + * Simplified version of [VulnerabilityInstance] that contains only serializable data. + */ +@Serializable +data class DumpableVulnerabilityInstance( + val vulnerabilityType: String, + val sources: List, + val sink: String, + val traces: List> +) + +@Serializable +data class DumpableAnalysisResult(val foundVulnerabilities: List) + +fun List.toDumpable(maxPathsCount: Int = 3): DumpableAnalysisResult { + return DumpableAnalysisResult(map { it.toDumpable(maxPathsCount) }) +} \ No newline at end of file diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsUnitManager.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsUnitManager.kt index 401032f63..0037355a7 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsUnitManager.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/IfdsUnitManager.kt @@ -25,7 +25,6 @@ import kotlinx.coroutines.joinAll import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withTimeout -import org.jacodb.analysis.VulnerabilityInstance import org.jacodb.analysis.logger import org.jacodb.analysis.runAnalysis import org.jacodb.api.JcMethod diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/UnitResolvers.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/UnitResolver.kt similarity index 68% rename from jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/UnitResolvers.kt rename to jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/UnitResolver.kt index 32050b041..8b882699d 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/UnitResolvers.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/UnitResolver.kt @@ -16,9 +16,12 @@ package org.jacodb.analysis.engine -import org.jacodb.api.JcClassOrInterface +import org.jacodb.analysis.library.MethodUnitResolver +import org.jacodb.analysis.library.PackageUnitResolver +import org.jacodb.analysis.library.SingletonUnitResolver +import org.jacodb.analysis.library.getClassUnitResolver +import org.jacodb.analysis.runAnalysis import org.jacodb.api.JcMethod -import org.jacodb.api.ext.packageName /** * Sets a mapping from [JcMethod] to abstract domain [UnitType]. @@ -35,27 +38,11 @@ fun interface UnitResolver { fun getByName(name: String): UnitResolver<*> { return when (name) { "method" -> MethodUnitResolver - "class" -> ClassUnitResolver(false) + "class" -> getClassUnitResolver(false) "package" -> PackageUnitResolver "singleton" -> SingletonUnitResolver else -> error("Unknown unit resolver $name") } } } -} - -val MethodUnitResolver = UnitResolver { method -> method } - -val PackageUnitResolver = UnitResolver { method -> method.enclosingClass.packageName } - -val SingletonUnitResolver = UnitResolver { _ -> Unit } - -class ClassUnitResolver(private val includeNested: Boolean): UnitResolver { - override fun resolve(method: JcMethod): JcClassOrInterface { - return if (includeNested) { - generateSequence(method.enclosingClass) { it.outerClass }.last() - } else { - method.enclosingClass - } - } } \ No newline at end of file diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/VulnerabilityInstance.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/VulnerabilityInstance.kt new file mode 100644 index 000000000..bd832efd1 --- /dev/null +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/engine/VulnerabilityInstance.kt @@ -0,0 +1,46 @@ +/* + * 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.analysis.engine + +import org.jacodb.analysis.DumpableVulnerabilityInstance +import org.jacodb.api.cfg.JcInst + +/** + * Represents a vulnerability (issue) found by analysis + * + * @property vulnerabilityType type of vulnerability as a string (e.g. "Possible NPE", "Unused variable") + * @property traceGraph contains sink, sources and traces that lead to occurrence of vulnerability + */ +data class VulnerabilityInstance( + val vulnerabilityType: String, + val traceGraph: TraceGraph +) { + private fun JcInst.prettyPrint(): String { + return "${toString()} (${location.method}:${location.lineNumber})" + } + + fun toDumpable(maxPathsCount: Int): DumpableVulnerabilityInstance { + return DumpableVulnerabilityInstance( + vulnerabilityType, + traceGraph.sources.map { it.statement.prettyPrint() }, + traceGraph.sink.statement.prettyPrint(), + traceGraph.getAllTraces().take(maxPathsCount).map { intermediatePoints -> + intermediatePoints.map { it.statement.prettyPrint() } + }.toList() + ) + } +} \ No newline at end of file diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/ApplicationGraphFactory.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/ApplicationGraphFactory.kt new file mode 100644 index 000000000..e8f081517 --- /dev/null +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/ApplicationGraphFactory.kt @@ -0,0 +1,52 @@ +/* + * 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. + */ + +@file:JvmName("ApplicationGraphFactory") +package org.jacodb.analysis.graph + +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.future.future +import org.jacodb.api.JcClasspath +import org.jacodb.api.analysis.JcApplicationGraph +import org.jacodb.impl.features.usagesExt +import java.util.concurrent.CompletableFuture + +/** + * Creates an instance of [SimplifiedJcApplicationGraph], see its docs for more info. + */ +suspend fun JcClasspath.newApplicationGraphForAnalysis(bannedPackagePrefixes: List? = null): JcApplicationGraph { + val mainGraph = JcApplicationGraphImpl(this, usagesExt()) + return if (bannedPackagePrefixes != null) { + SimplifiedJcApplicationGraph(mainGraph, bannedPackagePrefixes) + } else { + SimplifiedJcApplicationGraph(mainGraph, defaultBannedPackagePrefixes) + } +} + +fun JcClasspath.asyncNewApplicationGraphForAnalysis( + bannedPackagePrefixes: List? = null +): CompletableFuture { + return GlobalScope.future { + newApplicationGraphForAnalysis(bannedPackagePrefixes) + } +} + +val defaultBannedPackagePrefixes: List = listOf( + "kotlin.", + "java.", + "jdk.internal.", + "sun.", +) \ No newline at end of file diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/BackwardApplicationGraph.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/BackwardApplicationGraphs.kt similarity index 89% rename from jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/BackwardApplicationGraph.kt rename to jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/BackwardApplicationGraphs.kt index 831d66390..6dfaf7168 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/BackwardApplicationGraph.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/BackwardApplicationGraphs.kt @@ -14,6 +14,7 @@ * limitations under the License. */ +@file:JvmName("BackwardApplicationGraphs") package org.jacodb.analysis.graph import org.jacodb.api.JcClasspath @@ -22,7 +23,7 @@ import org.jacodb.api.analysis.ApplicationGraph import org.jacodb.api.analysis.JcApplicationGraph import org.jacodb.api.cfg.JcInst -class BackwardApplicationGraph( +private class BackwardApplicationGraph( val forward: ApplicationGraph ) : ApplicationGraph { override fun predecessors(node: Statement) = forward.successors(node) @@ -43,9 +44,11 @@ class BackwardApplicationGraph( val ApplicationGraph.reversed get() = if (this is BackwardApplicationGraph) { this.forward - } else BackwardApplicationGraph(this) + } else { + BackwardApplicationGraph(this) + } -class BackwardJcApplicationGraph(val forward: JcApplicationGraph) : +private class BackwardJcApplicationGraph(val forward: JcApplicationGraph) : JcApplicationGraph, ApplicationGraph by BackwardApplicationGraph(forward) { override val classpath: JcClasspath get() = forward.classpath diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/JcApplicationGraphImpl.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/JcApplicationGraphImpl.kt index dafa1c9cd..c78cc78f6 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/JcApplicationGraphImpl.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/JcApplicationGraphImpl.kt @@ -22,14 +22,13 @@ import org.jacodb.api.analysis.JcApplicationGraph import org.jacodb.api.cfg.JcInst import org.jacodb.api.ext.cfg.callExpr import org.jacodb.impl.features.SyncUsagesExtension -import org.jacodb.impl.features.usagesExt /** * Possible we will need JcRawInst instead of JcInst */ -open class JcApplicationGraphImpl( +class JcApplicationGraphImpl( override val classpath: JcClasspath, - protected val usages: SyncUsagesExtension + private val usages: SyncUsagesExtension ) : JcApplicationGraph { private val methods = mutableSetOf() @@ -73,16 +72,4 @@ open class JcApplicationGraphImpl( override fun methodOf(node: JcInst): JcMethod { return node.location.method.also { methods.add(it) } } -} - -/** - * Creates an instance of [SimplifiedJcApplicationGraph], see its docs for more info. - */ -suspend fun JcClasspath.newApplicationGraphForAnalysis(bannedPackagePrefixes: List? = null): JcApplicationGraph { - val mainGraph = JcApplicationGraphImpl(this, usagesExt()) - return if (bannedPackagePrefixes != null) { - SimplifiedJcApplicationGraph(mainGraph, bannedPackagePrefixes) - } else { - SimplifiedJcApplicationGraph(mainGraph) - } } \ No newline at end of file diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/SimplifiedJcApplicationGraph.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/SimplifiedJcApplicationGraph.kt index beef0d6d2..1f3fcd708 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/SimplifiedJcApplicationGraph.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/graph/SimplifiedJcApplicationGraph.kt @@ -37,9 +37,9 @@ import org.jacodb.impl.features.hierarchyExt * 3. Adds a special [JcNoopInst] instruction to the beginning of each method * (because backward analysis may want for method to start with neutral instruction) */ -class SimplifiedJcApplicationGraph( +internal class SimplifiedJcApplicationGraph( private val impl: JcApplicationGraphImpl, - private val bannedPackagePrefixes: List = defaultBannedPackagePrefixes, + private val bannedPackagePrefixes: List, ) : JcApplicationGraph by impl { private val hierarchyExtension = runBlocking { classpath.hierarchyExt() @@ -128,15 +128,6 @@ class SimplifiedJcApplicationGraph( override fun entryPoint(method: JcMethod): Sequence = sequenceOf(getStartInst(method)) companion object { - val defaultBannedPackagePrefixes: List = listOf( - "kotlin.", - "java.", - "jdk.internal.", - "sun.", -// "kotlin.jvm.internal.", -// "java.security.", -// "java.util.regex." - ) } } diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/RunnersLibrary.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/RunnersLibrary.kt similarity index 77% rename from jacodb-analysis/src/main/kotlin/org/jacodb/analysis/RunnersLibrary.kt rename to jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/RunnersLibrary.kt index 900887c1f..ab5407852 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/RunnersLibrary.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/RunnersLibrary.kt @@ -15,24 +15,25 @@ */ @file:JvmName("RunnersLibrary") -package org.jacodb.analysis +package org.jacodb.analysis.library -import org.jacodb.analysis.analyzers.AliasAnalyzerFactory -import org.jacodb.analysis.analyzers.NpeAnalyzerFactory -import org.jacodb.analysis.analyzers.NpePrecalcBackwardAnalyzerFactory -import org.jacodb.analysis.analyzers.SqlInjectionAnalyzerFactory -import org.jacodb.analysis.analyzers.SqlInjectionBackwardAnalyzerFactory -import org.jacodb.analysis.analyzers.TaintAnalysisNode -import org.jacodb.analysis.analyzers.TaintAnalyzerFactory -import org.jacodb.analysis.analyzers.TaintBackwardAnalyzerFactory -import org.jacodb.analysis.analyzers.TaintNode -import org.jacodb.analysis.analyzers.UnusedVariableAnalyzerFactory import org.jacodb.analysis.engine.IfdsBaseUnitRunner import org.jacodb.analysis.engine.SequentialBidiIfdsUnitRunner +import org.jacodb.analysis.library.analyzers.AliasAnalyzerFactory +import org.jacodb.analysis.library.analyzers.NpeAnalyzerFactory +import org.jacodb.analysis.library.analyzers.NpePrecalcBackwardAnalyzerFactory +import org.jacodb.analysis.library.analyzers.SqlInjectionAnalyzerFactory +import org.jacodb.analysis.library.analyzers.SqlInjectionBackwardAnalyzerFactory +import org.jacodb.analysis.library.analyzers.TaintAnalysisNode +import org.jacodb.analysis.library.analyzers.TaintAnalyzerFactory +import org.jacodb.analysis.library.analyzers.TaintBackwardAnalyzerFactory +import org.jacodb.analysis.library.analyzers.TaintNode +import org.jacodb.analysis.library.analyzers.UnusedVariableAnalyzerFactory import org.jacodb.api.JcMethod import org.jacodb.api.cfg.JcExpr import org.jacodb.api.cfg.JcInst +//TODO: add docs here val UnusedVariableRunner = IfdsBaseUnitRunner(UnusedVariableAnalyzerFactory) fun newSqlInjectionRunner(maxPathLength: Int = 5) = SequentialBidiIfdsUnitRunner( diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/UnitResolversLibrary.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/UnitResolversLibrary.kt new file mode 100644 index 000000000..67556e70b --- /dev/null +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/UnitResolversLibrary.kt @@ -0,0 +1,41 @@ +/* + * 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. + */ + +@file:JvmName("UnitResolversLibrary") +package org.jacodb.analysis.library + +import org.jacodb.analysis.engine.UnitResolver +import org.jacodb.api.JcClassOrInterface +import org.jacodb.api.JcMethod +import org.jacodb.api.ext.packageName + +val MethodUnitResolver = UnitResolver { method -> method } +val PackageUnitResolver = UnitResolver { method -> method.enclosingClass.packageName } +val SingletonUnitResolver = UnitResolver { _ -> Unit } + +fun getClassUnitResolver(includeNested: Boolean): UnitResolver { + return ClassUnitResolver(includeNested) +} + +private class ClassUnitResolver(private val includeNested: Boolean): UnitResolver { + override fun resolve(method: JcMethod): JcClassOrInterface { + return if (includeNested) { + generateSequence(method.enclosingClass) { it.outerClass }.last() + } else { + method.enclosingClass + } + } +} \ No newline at end of file diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/AbstractTaintBackwardFunctions.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/AbstractTaintBackwardFunctions.kt similarity index 99% rename from jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/AbstractTaintBackwardFunctions.kt rename to jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/AbstractTaintBackwardFunctions.kt index aded8aea9..5b21a6794 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/AbstractTaintBackwardFunctions.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/AbstractTaintBackwardFunctions.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.analysis.analyzers +package org.jacodb.analysis.library.analyzers import org.jacodb.analysis.engine.DomainFact import org.jacodb.analysis.engine.FlowFunctionInstance diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/AbstractTaintForwardFunctions.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/AbstractTaintForwardFunctions.kt similarity index 99% rename from jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/AbstractTaintForwardFunctions.kt rename to jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/AbstractTaintForwardFunctions.kt index ff103d755..3edc9efa6 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/AbstractTaintForwardFunctions.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/AbstractTaintForwardFunctions.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.analysis.analyzers +package org.jacodb.analysis.library.analyzers import org.jacodb.analysis.engine.DomainFact import org.jacodb.analysis.engine.FlowFunctionInstance diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/AliasAnalyzer.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/AliasAnalyzer.kt similarity index 98% rename from jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/AliasAnalyzer.kt rename to jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/AliasAnalyzer.kt index e35086664..741a4f103 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/AliasAnalyzer.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/AliasAnalyzer.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.analysis.analyzers +package org.jacodb.analysis.library.analyzers import org.jacodb.analysis.engine.AnalyzerFactory import org.jacodb.analysis.engine.DomainFact diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/NpeAnalyzer.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/NpeAnalyzer.kt similarity index 99% rename from jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/NpeAnalyzer.kt rename to jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/NpeAnalyzer.kt index 7db14b058..ac3d32c9a 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/NpeAnalyzer.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/NpeAnalyzer.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.analysis.analyzers +package org.jacodb.analysis.library.analyzers import org.jacodb.analysis.engine.Analyzer import org.jacodb.analysis.engine.AnalyzerFactory diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/SqlInjectionAnalyzer.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/SqlInjectionAnalyzer.kt similarity index 96% rename from jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/SqlInjectionAnalyzer.kt rename to jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/SqlInjectionAnalyzer.kt index 9dd9b84b3..0457e6a7b 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/SqlInjectionAnalyzer.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/SqlInjectionAnalyzer.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.analysis.analyzers +package org.jacodb.analysis.library.analyzers fun SqlInjectionAnalyzerFactory(maxPathLength: Int) = TaintAnalyzerFactory( sqlSourceMatchers, diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/TaintAnalyzer.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/TaintAnalyzer.kt similarity index 99% rename from jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/TaintAnalyzer.kt rename to jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/TaintAnalyzer.kt index 7b94e7f96..7ac83e837 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/TaintAnalyzer.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/TaintAnalyzer.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.analysis.analyzers +package org.jacodb.analysis.library.analyzers import org.jacodb.analysis.engine.Analyzer import org.jacodb.analysis.engine.AnalyzerFactory diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/TaintNode.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/TaintNode.kt similarity index 98% rename from jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/TaintNode.kt rename to jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/TaintNode.kt index d0e7a7057..fcb815c6d 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/TaintNode.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/TaintNode.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.analysis.analyzers +package org.jacodb.analysis.library.analyzers import org.jacodb.analysis.engine.DomainFact import org.jacodb.analysis.paths.AccessPath diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/UnusedVariableAnalyzer.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/UnusedVariableAnalyzer.kt similarity index 99% rename from jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/UnusedVariableAnalyzer.kt rename to jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/UnusedVariableAnalyzer.kt index d667733e1..e0c803fb7 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/UnusedVariableAnalyzer.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/UnusedVariableAnalyzer.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.analysis.analyzers +package org.jacodb.analysis.library.analyzers import org.jacodb.analysis.engine.Analyzer import org.jacodb.analysis.engine.AnalyzerFactory diff --git a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/Util.kt b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/Util.kt similarity index 97% rename from jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/Util.kt rename to jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/Util.kt index a89d7ebad..ce5b20210 100644 --- a/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/analyzers/Util.kt +++ b/jacodb-analysis/src/main/kotlin/org/jacodb/analysis/library/analyzers/Util.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jacodb.analysis.analyzers +package org.jacodb.analysis.library.analyzers import org.jacodb.analysis.paths.AccessPath import org.jacodb.analysis.paths.minus diff --git a/jacodb-analysis/src/test/java/org/jacodb/analysis/impl/JavaAnalysisApiTest.java b/jacodb-analysis/src/test/java/org/jacodb/analysis/impl/JavaAnalysisApiTest.java new file mode 100644 index 000000000..bf3950478 --- /dev/null +++ b/jacodb-analysis/src/test/java/org/jacodb/analysis/impl/JavaAnalysisApiTest.java @@ -0,0 +1,83 @@ +/* + * 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.analysis.impl; + +import org.jacodb.analysis.AnalysisMain; +import org.jacodb.analysis.engine.IfdsUnitRunner; +import org.jacodb.analysis.engine.UnitResolver; +import org.jacodb.analysis.graph.ApplicationGraphFactory; +import org.jacodb.analysis.library.RunnersLibrary; +import org.jacodb.analysis.library.UnitResolversLibrary; +import org.jacodb.api.JcClassOrInterface; +import org.jacodb.api.JcClasspath; +import org.jacodb.api.JcDatabase; +import org.jacodb.api.JcMethod; +import org.jacodb.api.analysis.JcApplicationGraph; +import org.jacodb.impl.JacoDB; +import org.jacodb.impl.JcSettings; +import org.jacodb.impl.features.InMemoryHierarchy; +import org.jacodb.impl.features.Usages; +import org.jacodb.testing.LibrariesMixinKt; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; + +public class JavaAnalysisApiTest { + private JcClasspath createJcClasspath() throws ExecutionException, InterruptedException { + JcDatabase instance = JacoDB.async(new JcSettings().installFeatures(Usages.INSTANCE, InMemoryHierarchy.INSTANCE)).get(); + return instance.asyncClasspath(LibrariesMixinKt.getAllClasspath()).get(); + } + + @Test + public void testJavaAnalysisApi() throws ExecutionException, InterruptedException { + JcClasspath classpath = createJcClasspath(); + JcClassOrInterface analyzedClass = classpath.findClassOrNull("org.jacodb.testing.analysis.NpeExamples"); + Assertions.assertNotNull(analyzedClass); + + List methodsToAnalyze = analyzedClass.getDeclaredMethods(); + JcApplicationGraph applicationGraph = ApplicationGraphFactory + .asyncNewApplicationGraphForAnalysis(classpath, null) + .get(); + UnitResolver resolver = UnitResolversLibrary.getMethodUnitResolver(); + IfdsUnitRunner runner = RunnersLibrary.getUnusedVariableRunner(); + + AnalysisMain.runAnalysis( + applicationGraph, + resolver, + runner, + methodsToAnalyze, + Integer.MAX_VALUE + ); + } + + @Test + public void testCustomBannedPackagesApi() throws ExecutionException, InterruptedException { + JcClasspath classpath = createJcClasspath(); + Assertions.assertNotNull(classpath); + + List bannedPackages = new ArrayList<>(ApplicationGraphFactory.getDefaultBannedPackagePrefixes()); + bannedPackages.add("my.package.that.wont.be.analyzed"); + + JcApplicationGraph customGraph = ApplicationGraphFactory + .asyncNewApplicationGraphForAnalysis(classpath, bannedPackages) + .get(); + Assertions.assertNotNull(customGraph); + } +} \ 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 86d52a996..bcf530126 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 @@ -17,14 +17,14 @@ package org.jacodb.analysis.impl import kotlinx.coroutines.runBlocking -import org.jacodb.analysis.analyzers.TaintAnalysisNode -import org.jacodb.analysis.analyzers.TaintNode -import org.jacodb.analysis.engine.MethodUnitResolver -import org.jacodb.analysis.runAnalysis -import org.jacodb.analysis.graph.SimplifiedJcApplicationGraph +import org.jacodb.analysis.graph.defaultBannedPackagePrefixes import org.jacodb.analysis.graph.newApplicationGraphForAnalysis -import org.jacodb.analysis.newAliasRunner +import org.jacodb.analysis.library.MethodUnitResolver +import org.jacodb.analysis.library.analyzers.TaintAnalysisNode +import org.jacodb.analysis.library.analyzers.TaintNode +import org.jacodb.analysis.library.newAliasRunner import org.jacodb.analysis.paths.toPath +import org.jacodb.analysis.runAnalysis import org.jacodb.analysis.toDumpable import org.jacodb.api.JcMethod import org.jacodb.api.cfg.JcAssignInst @@ -144,12 +144,12 @@ class AliasAnalysisTest : BaseTest() { private fun sinks(inst: JcInst): List = TODO() private fun findTaints(method: JcMethod): List { - val bannedPackagePrefixes = SimplifiedJcApplicationGraph.defaultBannedPackagePrefixes + val bannedPackagePrefixes = defaultBannedPackagePrefixes .plus("pointerbench.benchmark.internal") val graph = runBlocking { cp.newApplicationGraphForAnalysis( - SimplifiedJcApplicationGraph.defaultBannedPackagePrefixes + bannedPackagePrefixes + defaultBannedPackagePrefixes + bannedPackagePrefixes ) } diff --git a/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/BaseAnalysisTest.kt b/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/BaseAnalysisTest.kt index 195e62921..387a1ff58 100644 --- a/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/BaseAnalysisTest.kt +++ b/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/BaseAnalysisTest.kt @@ -18,7 +18,7 @@ package org.jacodb.analysis.impl import juliet.testcasesupport.AbstractTestCase import kotlinx.coroutines.runBlocking -import org.jacodb.analysis.VulnerabilityInstance +import org.jacodb.analysis.engine.VulnerabilityInstance import org.jacodb.analysis.toDumpable import org.jacodb.api.JcClassOrInterface import org.jacodb.api.JcMethod 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 789ead157..ee976ddcd 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 @@ -19,13 +19,13 @@ package org.jacodb.analysis.impl import kotlinx.coroutines.runBlocking import kotlinx.serialization.json.Json import kotlinx.serialization.json.encodeToStream -import org.jacodb.analysis.UnusedVariableRunner -import org.jacodb.analysis.engine.ClassUnitResolver import org.jacodb.analysis.engine.IfdsUnitRunner -import org.jacodb.analysis.engine.MethodUnitResolver import org.jacodb.analysis.engine.UnitResolver import org.jacodb.analysis.graph.newApplicationGraphForAnalysis -import org.jacodb.analysis.newNpeRunner +import org.jacodb.analysis.library.MethodUnitResolver +import org.jacodb.analysis.library.UnusedVariableRunner +import org.jacodb.analysis.library.getClassUnitResolver +import org.jacodb.analysis.library.newNpeRunner import org.jacodb.analysis.runAnalysis import org.jacodb.analysis.toDumpable import org.jacodb.api.ext.findClass @@ -50,7 +50,7 @@ class JodaDateTimeAnalysisTest : BaseTest() { @Test fun `test Unused variable analysis`() { - testOne(ClassUnitResolver(false), UnusedVariableRunner) + testOne(getClassUnitResolver(false), UnusedVariableRunner) } @Test 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 fd7fd4c9a..4022ec6ac 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 @@ -17,13 +17,13 @@ package org.jacodb.analysis.impl import kotlinx.coroutines.runBlocking -import org.jacodb.analysis.VulnerabilityInstance -import org.jacodb.analysis.analyzers.NpeAnalyzer -import org.jacodb.analysis.engine.SingletonUnitResolver -import org.jacodb.analysis.runAnalysis +import org.jacodb.analysis.engine.VulnerabilityInstance import org.jacodb.analysis.graph.JcApplicationGraphImpl import org.jacodb.analysis.graph.newApplicationGraphForAnalysis -import org.jacodb.analysis.newNpeRunner +import org.jacodb.analysis.library.SingletonUnitResolver +import org.jacodb.analysis.library.analyzers.NpeAnalyzer +import org.jacodb.analysis.library.newNpeRunner +import org.jacodb.analysis.runAnalysis import org.jacodb.api.JcMethod import org.jacodb.api.ext.constructors import org.jacodb.api.ext.findClass diff --git a/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/SqlInjectionAnalysisTest.kt b/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/SqlInjectionAnalysisTest.kt index 319dcbff5..4132fa3e2 100644 --- a/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/SqlInjectionAnalysisTest.kt +++ b/jacodb-analysis/src/test/kotlin/org/jacodb/analysis/impl/SqlInjectionAnalysisTest.kt @@ -17,12 +17,12 @@ package org.jacodb.analysis.impl import kotlinx.coroutines.runBlocking -import org.jacodb.analysis.VulnerabilityInstance -import org.jacodb.analysis.analyzers.TaintAnalyzer -import org.jacodb.analysis.engine.SingletonUnitResolver +import org.jacodb.analysis.engine.VulnerabilityInstance +import org.jacodb.analysis.library.analyzers.TaintAnalyzer +import org.jacodb.analysis.library.SingletonUnitResolver import org.jacodb.analysis.runAnalysis import org.jacodb.analysis.graph.newApplicationGraphForAnalysis -import org.jacodb.analysis.newSqlInjectionRunner +import org.jacodb.analysis.library.newSqlInjectionRunner import org.jacodb.api.JcMethod import org.jacodb.impl.features.InMemoryHierarchy import org.jacodb.impl.features.Usages 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 df5a1e872..b3710f25e 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 @@ -17,12 +17,12 @@ package org.jacodb.analysis.impl import kotlinx.coroutines.runBlocking -import org.jacodb.analysis.UnusedVariableRunner -import org.jacodb.analysis.VulnerabilityInstance -import org.jacodb.analysis.analyzers.UnusedVariableAnalyzer -import org.jacodb.analysis.engine.SingletonUnitResolver -import org.jacodb.analysis.runAnalysis +import org.jacodb.analysis.engine.VulnerabilityInstance import org.jacodb.analysis.graph.newApplicationGraphForAnalysis +import org.jacodb.analysis.library.SingletonUnitResolver +import org.jacodb.analysis.library.UnusedVariableRunner +import org.jacodb.analysis.library.analyzers.UnusedVariableAnalyzer +import org.jacodb.analysis.runAnalysis import org.jacodb.api.JcMethod import org.jacodb.impl.features.InMemoryHierarchy import org.jacodb.impl.features.Usages diff --git a/jacodb-cli/src/main/kotlin/org/jacodb/cli/main.kt b/jacodb-cli/src/main/kotlin/org/jacodb/cli/main.kt index 02fa41286..6d749fd11 100644 --- a/jacodb-cli/src/main/kotlin/org/jacodb/cli/main.kt +++ b/jacodb-cli/src/main/kotlin/org/jacodb/cli/main.kt @@ -26,14 +26,14 @@ import kotlinx.serialization.json.Json import kotlinx.serialization.json.encodeToStream import mu.KLogging import org.jacodb.analysis.AnalysisConfig -import org.jacodb.analysis.UnusedVariableRunner -import org.jacodb.analysis.VulnerabilityInstance -import org.jacodb.analysis.engine.MethodUnitResolver import org.jacodb.analysis.engine.UnitResolver -import org.jacodb.analysis.runAnalysis +import org.jacodb.analysis.engine.VulnerabilityInstance import org.jacodb.analysis.graph.newApplicationGraphForAnalysis -import org.jacodb.analysis.newNpeRunner -import org.jacodb.analysis.newSqlInjectionRunner +import org.jacodb.analysis.library.MethodUnitResolver +import org.jacodb.analysis.library.UnusedVariableRunner +import org.jacodb.analysis.library.newNpeRunner +import org.jacodb.analysis.library.newSqlInjectionRunner +import org.jacodb.analysis.runAnalysis import org.jacodb.analysis.toDumpable import org.jacodb.api.JcMethod import org.jacodb.api.analysis.JcApplicationGraph @@ -96,7 +96,7 @@ fun main(args: Array) { 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 + ).default("report.json") // TODO: create SARIF here val classpath by parser.option( ArgType.String, fullName = "classpath", From 7a7829b25aac71039b18851eba22b380fe4e5f4c Mon Sep 17 00:00:00 2001 From: Ivan Volkov Date: Thu, 27 Jul 2023 13:09:28 +0300 Subject: [PATCH 4/6] [jacodb-ifds] Start classes can be specified by prefixes in cli --- .../src/main/kotlin/org/jacodb/cli/main.kt | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/jacodb-cli/src/main/kotlin/org/jacodb/cli/main.kt b/jacodb-cli/src/main/kotlin/org/jacodb/cli/main.kt index 6d749fd11..ad684b0d6 100644 --- a/jacodb-cli/src/main/kotlin/org/jacodb/cli/main.kt +++ b/jacodb-cli/src/main/kotlin/org/jacodb/cli/main.kt @@ -35,13 +35,15 @@ import org.jacodb.analysis.library.newNpeRunner import org.jacodb.analysis.library.newSqlInjectionRunner import org.jacodb.analysis.runAnalysis import org.jacodb.analysis.toDumpable +import org.jacodb.api.JcClassOrInterface +import org.jacodb.api.JcClassProcessingTask import org.jacodb.api.JcMethod import org.jacodb.api.analysis.JcApplicationGraph -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 +import java.util.concurrent.ConcurrentHashMap private val logger = object : KLogging() {}.logger @@ -133,13 +135,22 @@ fun main(args: Array) { jacodb.classpath(classpathAsFiles) } + val startClassesAsList = startClasses.split(";") + val startJcClasses = ConcurrentHashMap.newKeySet() + cp.executeAsync(object : JcClassProcessingTask { + override fun process(clazz: JcClassOrInterface) { + if (startClassesAsList.any { clazz.name.startsWith(it) }) { + startJcClasses.add(clazz) + } + } + }).get() + val startJcMethods = startJcClasses.flatMap { it.declaredMethods } + + val graph = runBlocking { cp.newApplicationGraphForAnalysis() } - val startJcClasses = startClasses.split(";").map { cp.findClass(it) } - val startJcMethods = startJcClasses.flatMap { it.declaredMethods } - val analysesResults = launchAnalysesByConfig(config, graph, startJcMethods).flatten().toDumpable() val json = Json { prettyPrint = true } From 4a947949aac64d82d0768462880dab7bab93d3c3 Mon Sep 17 00:00:00 2001 From: Ivan Volkov Date: Thu, 27 Jul 2023 13:23:55 +0300 Subject: [PATCH 5/6] [jacodb-ifds] Speed up JavaAnalysisApiTest --- .../jacodb/analysis/impl/JavaAnalysisApiTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/jacodb-analysis/src/test/java/org/jacodb/analysis/impl/JavaAnalysisApiTest.java b/jacodb-analysis/src/test/java/org/jacodb/analysis/impl/JavaAnalysisApiTest.java index bf3950478..17dc88212 100644 --- a/jacodb-analysis/src/test/java/org/jacodb/analysis/impl/JavaAnalysisApiTest.java +++ b/jacodb-analysis/src/test/java/org/jacodb/analysis/impl/JavaAnalysisApiTest.java @@ -33,6 +33,7 @@ import org.jacodb.impl.features.Usages; import org.jacodb.testing.LibrariesMixinKt; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -40,14 +41,16 @@ import java.util.concurrent.ExecutionException; public class JavaAnalysisApiTest { - private JcClasspath createJcClasspath() throws ExecutionException, InterruptedException { + private static JcClasspath classpath; + + @BeforeAll + public static void initClasspath() throws ExecutionException, InterruptedException { JcDatabase instance = JacoDB.async(new JcSettings().installFeatures(Usages.INSTANCE, InMemoryHierarchy.INSTANCE)).get(); - return instance.asyncClasspath(LibrariesMixinKt.getAllClasspath()).get(); + classpath = instance.asyncClasspath(LibrariesMixinKt.getAllClasspath()).get(); } @Test public void testJavaAnalysisApi() throws ExecutionException, InterruptedException { - JcClasspath classpath = createJcClasspath(); JcClassOrInterface analyzedClass = classpath.findClassOrNull("org.jacodb.testing.analysis.NpeExamples"); Assertions.assertNotNull(analyzedClass); @@ -69,9 +72,6 @@ public void testJavaAnalysisApi() throws ExecutionException, InterruptedExceptio @Test public void testCustomBannedPackagesApi() throws ExecutionException, InterruptedException { - JcClasspath classpath = createJcClasspath(); - Assertions.assertNotNull(classpath); - List bannedPackages = new ArrayList<>(ApplicationGraphFactory.getDefaultBannedPackagePrefixes()); bannedPackages.add("my.package.that.wont.be.analyzed"); From 2eaa5ad7c78f4477004f49deb5dc0408833ead30 Mon Sep 17 00:00:00 2001 From: Ivan Volkov Date: Thu, 27 Jul 2023 15:53:21 +0300 Subject: [PATCH 6/6] [jacodb-ifds] Add README for analysis module --- jacodb-analysis/README.md | 72 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/jacodb-analysis/README.md b/jacodb-analysis/README.md index a0fc78273..1141945e4 100644 --- a/jacodb-analysis/README.md +++ b/jacodb-analysis/README.md @@ -1,11 +1,73 @@ # Module jacodb-analysis -Module for custom analysis +Analysis module allows launching dataflow analyses of applications. +It contains API to write custom analyses, along with several implemented ready-to-use analyses. -## IFDS +## Concept of units -TODO +The [IFDS](https://dx.doi.org/10.1145/199448.199462) framework is used as the basis for this module. +However, in order to be scalable, the analyzed code is split into so-called units, so that the framework +can analyze them concurrently. +Information is shared between the units via summaries, but the lifecycle of each unit is controlled +separately. -## Points To +## Get started -TODO \ No newline at end of file +The entry point of the analysis is the [runAnalysis] method. In order to call it, you have to provide: +* `graph` — an application graph that is used for analysis. To obtain this graph, one should call the [newApplicationGraphForAnalysis] method. +* `unitResolver` — an object that groups methods into units. Choose one from `UnitResolversLibrary`. +Note that in general, larger units mean more precise but also more resource-consuming analysis. +* `ifdsUnitRunner` — an [IfdsUnitRunner] instance, which is used to analyze each unit. This is what defines concrete analysis. + Ready-to-use runners are located in `RunnersLibrary`. +* `methods` — a list of methods to analyze. + +For example, to detect unused variables in the given `analyzedClass` methods, you may run the following code +(assuming that `classpath` is an instance of [JcClasspath]): + +```kotlin +val applicationGraph = runBlocking { + classpath.newApplicationGraphForAnalysis() +} + +val methodsToAnalyze = analyzedClass.declaredMethods +val unitResolver = MethodUnitResolver +val runner = UnusedVariableRunner + +runAnalysis(applicationGraph, unitResolver, runner, methodsToAnalyze) +``` + +## Implemented runners + +By now, the following runners are implemented: +* `UnusedVariableRunner` that can detect issues like unused variable declaration, unused return value, etc. +* `NpeRunner` that can find instructions with possible null-value dereference. +* Generic `TaintRunner` that can perform taint analysis. +* `SqlInjectionRunner` which find places vulnerable to sql injections, thus performing a specific kind of taint analysis. + +## Implementing your own analysis + +To implement a simple one-pass analysis, use [IfdsBaseUnitRunner]. +To instantiate it, you need an [AnalyzerFactory] instance, which is an object that can create [Analyzer] via +[JcApplicationGraph]. + +To instantiate an [Analyzer] interface, you have to specify the following: + +* `flowFunctions` which describe dataflow facts and their transmissions during the analysis. + +* How vulnerabilities are produced by these facts, i.e. you have to implement `getSummaryFacts` and `getSummaryFactsPostIfds` methods. + +To implement bidirectional analysis, you may use composite [SequentialBidiIfdsUnitRunner] and [ParallelBidiIfdsUnitRunner]. + + + + +[runAnalysis]: https://jacodb.org/docs/jacodb-analysis/org.jacodb.analysis/run-analysis.html +[newApplicationGraphForAnalysis]: https://jacodb.org/docs/jacodb-analysis/org.jacodb.analysis/new-application-graph-for-analysis.html +[IfdsUnitRunner]: https://jacodb.org/docs/jacodb-analysis/org.jacodb.analysis.engine/-ifds-unit-runner/index.html +[JcClasspath]: https://jacodb.org/docs/jacodb-api/org.jacodb.api/-jc-classpath/index.html +[IfdsBaseUnitRunner]: https://jacodb.org/docs/jacodb-analysis/org.jacodb.analysis.engine/-ifds-base-unit-runner/index.html +[AnalyzerFactory]: https://jacodb.org/docs/jacodb-analysis/org.jacodb.analysis.engine/-analyzer-factory/index.html +[Analyzer]: https://jacodb.org/docs/jacodb-analysis/org.jacodb.analysis.engine/-analyzer/index.html +[JcApplicationGraph]: https://jacodb.org/docs/jacodb-api/org.jacodb.api.analysis/-jc-application-graph/index.html +[SequentialBidiIfdsUnitRunner]: https://jacodb.org/docs/jacodb-analysis/org.jacodb.analysis.engine/-sequential-bidi-ifds-base-unit-runner/index.html +[ParallelBidiIfdsUnitRunner]: https://jacodb.org/docs/jacodb-analysis/org.jacodb.analysis.engine/-parallel-bidi-ifds-base-unit-runner/index.html