ChainResolver.kt

1
package com.depanalyzer.core.graph
2
3
import com.depanalyzer.report.Vulnerability
4
5
object ChainResolver {
6
7
    private const val MAX_DEPTH = 50
8
9
10
    fun resolveAllChains(
11
        graph: DependencyGraph,
12
        vulnerabilities: Map<String, List<Vulnerability>>
13
    ): List<VulnerabilityChain> {
14 1 1. resolveAllChains : negated conditional → KILLED
        if (vulnerabilities.isEmpty()) {
15 1 1. resolveAllChains : replaced return value with Collections.emptyList for com/depanalyzer/core/graph/ChainResolver::resolveAllChains → SURVIVED
            return emptyList()
16
        }
17
18
        val vulnerableNodes = graph.getAllVulnerableNodes()
19
        val allChains = mutableListOf<VulnerabilityChain>()
20
21
        vulnerableNodes.forEach { vulnNode ->
22 1 1. resolveAllChains : negated conditional → KILLED
            val nodeVulns = vulnerabilities[vulnNode.id] ?: return@forEach
23 1 1. resolveAllChains : negated conditional → KILLED
            if (nodeVulns.isEmpty()) return@forEach
24
25
            val allPaths = findAllPaths(vulnNode, graph, mutableSetOf())
26
27
            allPaths.forEach { path ->
28
                val chain = VulnerabilityChain(
29
                    chain = path,
30
                    vulnerabilities = nodeVulns,
31
                    isShortestPath = false,
32
                    classification = classifyVulnerability(path)
33
                )
34
                allChains.add(chain)
35
            }
36
        }
37
38 1 1. resolveAllChains : removed call to com/depanalyzer/core/graph/ChainResolver::markShortestPaths → KILLED
        markShortestPaths(allChains)
39
40 1 1. resolveAllChains : replaced return value with Collections.emptyList for com/depanalyzer/core/graph/ChainResolver::resolveAllChains → KILLED
        return deduplicateChains(allChains)
41
    }
42
43
    private fun findAllPaths(
44
        vulnerableNode: DependencyNode,
45
        graph: DependencyGraph,
46
        visited: MutableSet<String>
47
    ): List<List<DependencyNode>> {
48
        val paths = mutableListOf<List<DependencyNode>>()
49
50 1 1. findAllPaths : negated conditional → KILLED
        if (vulnerableNode.isDirectDependency()) {
51
            paths.add(listOf(vulnerableNode))
52 1 1. findAllPaths : replaced return value with Collections.emptyList for com/depanalyzer/core/graph/ChainResolver::findAllPaths → KILLED
            return paths
53
        }
54
55 1 1. findAllPaths : removed call to com/depanalyzer/core/graph/ChainResolver::dfsBackwardsToRoots → KILLED
        dfsBackwardsToRoots(
56
            vulnerableNode,
57
            graph,
58
            mutableListOf(vulnerableNode),
59
            visited.toMutableSet(),
60
            paths
61
        )
62
63 1 1. findAllPaths : replaced return value with Collections.emptyList for com/depanalyzer/core/graph/ChainResolver::findAllPaths → KILLED
        return paths
64
    }
65
66
    private fun dfsBackwardsToRoots(
67
        current: DependencyNode,
68
        graph: DependencyGraph,
69
        currentPath: MutableList<DependencyNode>,
70
        visited: MutableSet<String>,
71
        results: MutableList<List<DependencyNode>>
72
    ) {
73 2 1. dfsBackwardsToRoots : changed conditional boundary → SURVIVED
2. dfsBackwardsToRoots : negated conditional → KILLED
        if (currentPath.size > MAX_DEPTH) {
74
            return
75
        }
76
77 1 1. dfsBackwardsToRoots : negated conditional → KILLED
        if (current.isDirectDependency()) {
78
            results.add(currentPath.reversed())
79
            return
80
        }
81
82 1 1. dfsBackwardsToRoots : negated conditional → KILLED
        if (visited.contains(current.id)) {
83
            return
84
        }
85
86
        visited.add(current.id)
87
88 1 1. dfsBackwardsToRoots : negated conditional → KILLED
        if (current.parent != null) {
89
            val parent = current.parent
90
            currentPath.add(parent)
91 1 1. dfsBackwardsToRoots : removed call to com/depanalyzer/core/graph/ChainResolver::dfsBackwardsToRoots → KILLED
            dfsBackwardsToRoots(parent, graph, currentPath, visited.toMutableSet(), results)
92 1 1. dfsBackwardsToRoots : Replaced integer subtraction with addition → KILLED
            currentPath.removeAt(currentPath.size - 1)
93
        } else {
94
            results.add(currentPath.reversed())
95
        }
96
    }
97
98
    private fun markShortestPaths(chains: MutableList<VulnerabilityChain>) {
99
        val groupedByKey = chains.groupBy { chain ->
100
            Triple(
101
                chain.directDependency.id,
102
                chain.vulnerableNode.id,
103
                chain.cveIds.joinToString(",")
104
            )
105
        }
106
107 1 1. markShortestPaths : removed call to java/util/List::replaceAll → KILLED
        chains.replaceAll { original ->
108
            val groupKey = Triple(
109
                original.directDependency.id,
110
                original.vulnerableNode.id,
111
                original.cveIds.joinToString(",")
112
            )
113 2 1. markShortestPaths$lambda$1 : replaced return value with null for com/depanalyzer/core/graph/ChainResolver::markShortestPaths$lambda$1 → NO_COVERAGE
2. markShortestPaths$lambda$1 : negated conditional → KILLED
            val group = groupedByKey[groupKey] ?: return@replaceAll original
114
            val shortest = group.minByOrNull { it.depth }
115 4 1. markShortestPaths$lambda$1 : negated conditional → KILLED
2. markShortestPaths$lambda$1 : negated conditional → KILLED
3. markShortestPaths$lambda$1 : negated conditional → KILLED
4. markShortestPaths$lambda$1 : replaced return value with null for com/depanalyzer/core/graph/ChainResolver::markShortestPaths$lambda$1 → KILLED
            if (original.depth == shortest?.depth) original.copy(isShortestPath = true) else original
116
        }
117
    }
118
119
    private fun classifyVulnerability(path: List<DependencyNode>): VulnerabilityClassification {
120
        val directDep = path.first()
121
        val vulnerableNode = path.last()
122
123 1 1. classifyVulnerability : replaced return value with null for com/depanalyzer/core/graph/ChainResolver::classifyVulnerability → KILLED
        return when {
124 1 1. classifyVulnerability : negated conditional → KILLED
            directDep.id == vulnerableNode.id -> 
125
                VulnerabilityClassification.DIRECTLY_VULNERABLE
126
127 2 1. classifyVulnerability : negated conditional → KILLED
2. classifyVulnerability : negated conditional → KILLED
            directDep.isDirectDependency() && directDep.id != vulnerableNode.id ->
128
                VulnerabilityClassification.INDIRECTLY_VULNERABLE
129
130
            else ->
131
                VulnerabilityClassification.TRANSITIVE_VULNERABLE
132
        }
133
    }
134
135
    private fun deduplicateChains(chains: List<VulnerabilityChain>): List<VulnerabilityChain> {
136
        val grouped = chains.groupBy { chain ->
137
            Triple(
138
                chain.directDependency.id,
139
                chain.vulnerableNode.id,
140
                chain.cveIds.toSet()
141
            )
142
        }
143
144 1 1. deduplicateChains : replaced return value with Collections.emptyList for com/depanalyzer/core/graph/ChainResolver::deduplicateChains → KILLED
        return grouped.values.map { group ->
145 1 1. deduplicateChains : negated conditional → SURVIVED
            group.minByOrNull { it.depth } ?: group.first()
146
        }
147
    }
148
}

Mutations

14

1.1
Location : resolveAllChains
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:handles circular references without infinite loops()]
negated conditional → KILLED

15

1.1
Location : resolveAllChains
Killed by : none
replaced return value with Collections.emptyList for com/depanalyzer/core/graph/ChainResolver::resolveAllChains → SURVIVED
Covering tests

22

1.1
Location : resolveAllChains
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:handles circular references without infinite loops()]
negated conditional → KILLED

23

1.1
Location : resolveAllChains
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:handles circular references without infinite loops()]
negated conditional → KILLED

38

1.1
Location : resolveAllChains
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:marks shortest paths correctly()]
removed call to com/depanalyzer/core/graph/ChainResolver::markShortestPaths → KILLED

40

1.1
Location : resolveAllChains
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:handles circular references without infinite loops()]
replaced return value with Collections.emptyList for com/depanalyzer/core/graph/ChainResolver::resolveAllChains → KILLED

50

1.1
Location : findAllPaths
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:classifies direct vulnerabilities correctly()]
negated conditional → KILLED

52

1.1
Location : findAllPaths
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:handles circular references without infinite loops()]
replaced return value with Collections.emptyList for com/depanalyzer/core/graph/ChainResolver::findAllPaths → KILLED

55

1.1
Location : findAllPaths
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:classifies direct vulnerabilities correctly()]
removed call to com/depanalyzer/core/graph/ChainResolver::dfsBackwardsToRoots → KILLED

63

1.1
Location : findAllPaths
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:classifies direct vulnerabilities correctly()]
replaced return value with Collections.emptyList for com/depanalyzer/core/graph/ChainResolver::findAllPaths → KILLED

73

1.1
Location : dfsBackwardsToRoots
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

2.2
Location : dfsBackwardsToRoots
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:classifies direct vulnerabilities correctly()]
negated conditional → KILLED

77

1.1
Location : dfsBackwardsToRoots
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:classifies direct vulnerabilities correctly()]
negated conditional → KILLED

82

1.1
Location : dfsBackwardsToRoots
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:classifies direct vulnerabilities correctly()]
negated conditional → KILLED

88

1.1
Location : dfsBackwardsToRoots
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:classifies direct vulnerabilities correctly()]
negated conditional → KILLED

91

1.1
Location : dfsBackwardsToRoots
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:classifies direct vulnerabilities correctly()]
removed call to com/depanalyzer/core/graph/ChainResolver::dfsBackwardsToRoots → KILLED

92

1.1
Location : dfsBackwardsToRoots
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:classifies direct vulnerabilities correctly()]
Replaced integer subtraction with addition → KILLED

107

1.1
Location : markShortestPaths
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:marks shortest paths correctly()]
removed call to java/util/List::replaceAll → KILLED

113

1.1
Location : markShortestPaths$lambda$1
Killed by : none
replaced return value with null for com/depanalyzer/core/graph/ChainResolver::markShortestPaths$lambda$1 → NO_COVERAGE

2.2
Location : markShortestPaths$lambda$1
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:marks shortest paths correctly()]
negated conditional → KILLED

115

1.1
Location : markShortestPaths$lambda$1
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:marks shortest paths correctly()]
negated conditional → KILLED

2.2
Location : markShortestPaths$lambda$1
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:marks shortest paths correctly()]
negated conditional → KILLED

3.3
Location : markShortestPaths$lambda$1
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:marks shortest paths correctly()]
negated conditional → KILLED

4.4
Location : markShortestPaths$lambda$1
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:handles circular references without infinite loops()]
replaced return value with null for com/depanalyzer/core/graph/ChainResolver::markShortestPaths$lambda$1 → KILLED

123

1.1
Location : classifyVulnerability
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:handles circular references without infinite loops()]
replaced return value with null for com/depanalyzer/core/graph/ChainResolver::classifyVulnerability → KILLED

124

1.1
Location : classifyVulnerability
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:classifies direct vulnerabilities correctly()]
negated conditional → KILLED

127

1.1
Location : classifyVulnerability
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:classifies direct vulnerabilities correctly()]
negated conditional → KILLED

2.2
Location : classifyVulnerability
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:classifies direct vulnerabilities correctly()]
negated conditional → KILLED

144

1.1
Location : deduplicateChains
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:handles circular references without infinite loops()]
replaced return value with Collections.emptyList for com/depanalyzer/core/graph/ChainResolver::deduplicateChains → KILLED

145

1.1
Location : deduplicateChains
Killed by : none
negated conditional → SURVIVED
Covering tests

159

1.1
Location : markShortestPaths
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:handles circular references without infinite loops()]
negated conditional → KILLED

173

1.1
Location : deduplicateChains
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:handles circular references without infinite loops()]
negated conditional → KILLED

188

1.1
Location : deduplicateChains
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:handles circular references without infinite loops()]
negated conditional → KILLED

193

1.1
Location : deduplicateChains
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : deduplicateChains
Killed by : none
changed conditional boundary → NO_COVERAGE

197

1.1
Location : deduplicateChains
Killed by : none
negated conditional → NO_COVERAGE

201

1.1
Location : markShortestPaths$lambda$1
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:marks shortest paths correctly()]
negated conditional → KILLED

203

1.1
Location : markShortestPaths$lambda$1
Killed by : com.depanalyzer.core.graph.ChainResolverTest.[engine:junit-jupiter]/[class:com.depanalyzer.core.graph.ChainResolverTest]/[method:handles circular references without infinite loops()]
negated conditional → KILLED

208

1.1
Location : markShortestPaths$lambda$1
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : markShortestPaths$lambda$1
Killed by : none
changed conditional boundary → NO_COVERAGE

212

1.1
Location : markShortestPaths$lambda$1
Killed by : none
negated conditional → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.22.1