Skip to content

Commit ff1a367

Browse files
authored
fix: de-duplicate and sort both includedBy and projectReferences in reports (#8440)
Signed-off-by: Chad Wilson <29788154+chadlwilson@users.noreply.github.com>
1 parent a07529a commit ff1a367

9 files changed

Lines changed: 127 additions & 51 deletions

File tree

core/src/main/java/org/owasp/dependencycheck/dependency/Dependency.java

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@
1919

2020
import com.github.packageurl.MalformedPackageURLException;
2121
import com.github.packageurl.PackageURL;
22+
import com.google.common.collect.ImmutableSortedSet;
2223
import org.apache.commons.lang3.builder.EqualsBuilder;
2324
import org.apache.commons.lang3.builder.HashCodeBuilder;
25+
import org.jspecify.annotations.NonNull;
26+
import org.jspecify.annotations.Nullable;
2427
import org.owasp.dependencycheck.data.nexus.MavenArtifact;
2528
import org.owasp.dependencycheck.utils.Checksum;
2629
import org.slf4j.Logger;
@@ -104,9 +107,7 @@ public class Dependency extends EvidenceCollection implements Serializable {
104107
private final SortedSet<Dependency> relatedDependencies = new TreeSet<>(Dependency.NAME_COMPARATOR);
105108
/**
106109
* The set of dependencies that included this dependency (i.e., this is a
107-
* transitive dependency because it was included by X). This is a pair where
108-
* the left element is the includedBy and the right element is the type
109-
* (e.g. buildEnv, plugins).
110+
* transitive dependency because it was included by X).
110111
*/
111112
private final Set<IncludedByReference> includedBy = new HashSet<>();
112113
/**
@@ -798,7 +799,17 @@ public synchronized void clearRelatedDependencies() {
798799
* @return the unmodifiable set of includedBy
799800
*/
800801
public synchronized Set<IncludedByReference> getIncludedBy() {
801-
return Collections.unmodifiableSet(new HashSet<>(includedBy));
802+
return Set.copyOf(includedBy);
803+
}
804+
805+
/**
806+
* Get the unmodifiable set of includedBy (the list of parents of this
807+
* transitive dependency), sorted by natural comparator.
808+
*
809+
* @return the unmodifiable set of includedBy, sorted
810+
*/
811+
public synchronized SortedSet<IncludedByReference> getIncludedBySorted() {
812+
return ImmutableSortedSet.copyOf(includedBy);
802813
}
803814

804815
/**
@@ -807,7 +818,7 @@ public synchronized Set<IncludedByReference> getIncludedBy() {
807818
*
808819
* @param includedBy a project reference
809820
*/
810-
public synchronized void addIncludedBy(String includedBy) {
821+
public synchronized void addIncludedBy(@NonNull String includedBy) {
811822
this.includedBy.add(new IncludedByReference(includedBy, null));
812823
}
813824

@@ -818,7 +829,7 @@ public synchronized void addIncludedBy(String includedBy) {
818829
* @param includedBy a project reference
819830
* @param type the type of project reference (i.e. 'plugins', 'buildEnv')
820831
*/
821-
public synchronized void addIncludedBy(String includedBy, String type) {
832+
public synchronized void addIncludedBy(@NonNull String includedBy, @Nullable String type) {
822833
this.includedBy.add(new IncludedByReference(includedBy, type));
823834
}
824835

@@ -827,7 +838,7 @@ public synchronized void addIncludedBy(String includedBy, String type) {
827838
*
828839
* @param includedBy a set of project references
829840
*/
830-
public synchronized void addAllIncludedBy(Set<IncludedByReference> includedBy) {
841+
public synchronized void addAllIncludedBy(@NonNull Set<IncludedByReference> includedBy) {
831842
this.includedBy.addAll(includedBy);
832843
}
833844

@@ -837,15 +848,24 @@ public synchronized void addAllIncludedBy(Set<IncludedByReference> includedBy) {
837848
* @return the unmodifiable set of projectReferences
838849
*/
839850
public synchronized Set<String> getProjectReferences() {
840-
return Collections.unmodifiableSet(new HashSet<>(projectReferences));
851+
return Set.copyOf(projectReferences);
852+
}
853+
854+
/**
855+
* Get the unmodifiable set of projectReferences, sorted by natural comparator.
856+
*
857+
* @return the unmodifiable set of projectReferences, sorted
858+
*/
859+
public synchronized SortedSet<String> getProjectReferencesSorted() {
860+
return ImmutableSortedSet.copyOf(projectReferences);
841861
}
842862

843863
/**
844864
* Adds a project reference.
845865
*
846866
* @param projectReference a project reference
847867
*/
848-
public synchronized void addProjectReference(String projectReference) {
868+
public synchronized void addProjectReference(@NonNull String projectReference) {
849869
this.projectReferences.add(projectReference);
850870
}
851871

@@ -854,7 +874,7 @@ public synchronized void addProjectReference(String projectReference) {
854874
*
855875
* @param projectReferences a set of project references
856876
*/
857-
public synchronized void addAllProjectReferences(Set<String> projectReferences) {
877+
public synchronized void addAllProjectReferences(@NonNull Set<String> projectReferences) {
858878
this.projectReferences.addAll(projectReferences);
859879
}
860880

@@ -897,15 +917,15 @@ public synchronized void removeRelatedDependencies(Dependency dependency) {
897917
* @return the value of availableVersions
898918
*/
899919
public synchronized List<String> getAvailableVersions() {
900-
return Collections.unmodifiableList(new ArrayList<>(availableVersions));
920+
return List.copyOf(availableVersions);
901921
}
902922

903923
/**
904924
* Adds a version to the available version list.
905925
*
906926
* @param version the version to add to the list
907927
*/
908-
public synchronized void addAvailableVersion(String version) {
928+
public synchronized void addAvailableVersion(@NonNull String version) {
909929
this.availableVersions.add(version);
910930
}
911931

@@ -927,7 +947,7 @@ public boolean isVirtual() {
927947
*/
928948
@Override
929949
public boolean equals(Object obj) {
930-
if (obj == null || !(obj instanceof Dependency)) {
950+
if (!(obj instanceof Dependency)) {
931951
return false;
932952
}
933953
if (this == obj) {

core/src/main/java/org/owasp/dependencycheck/dependency/IncludedByReference.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,19 @@
1717
*/
1818
package org.owasp.dependencycheck.dependency;
1919

20+
import org.apache.commons.lang3.builder.CompareToBuilder;
21+
import org.jspecify.annotations.NonNull;
22+
2023
import java.io.Serializable;
24+
import java.util.Objects;
2125

2226
/**
2327
* POJO to store a reference to the "included by" node in a dependency tree;
2428
* where included by is the root node that caused a dependency to be included.
2529
*
2630
* @author Jeremy Long
2731
*/
28-
public class IncludedByReference implements Serializable {
32+
public class IncludedByReference implements Serializable, Comparable<IncludedByReference> {
2933

3034
/**
3135
* The serial version UID for serialization.
@@ -70,4 +74,28 @@ public String getType() {
7074
return type;
7175
}
7276

77+
@Override
78+
public boolean equals(Object o) {
79+
if (!(o instanceof IncludedByReference)) return false;
80+
IncludedByReference that = (IncludedByReference) o;
81+
return Objects.equals(type, that.type) && Objects.equals(reference, that.reference);
82+
}
83+
84+
@Override
85+
public int hashCode() {
86+
return Objects.hash(type, reference);
87+
}
88+
89+
@Override
90+
public int compareTo(@NonNull IncludedByReference o) {
91+
return new CompareToBuilder()
92+
.append(type, o.type) // Group by type (nulls-first)
93+
.append(reference, o.reference) // then the actual reference
94+
.toComparison();
95+
}
96+
97+
@Override
98+
public String toString() {
99+
return "IncludedByReference{reference='" + reference + "', type='" + type + "'}";
100+
}
73101
}

core/src/main/resources/templates/gitlabReport.vsl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@
8787
## "direct": false, --> not implemented
8888
## we don't have a good way of assigning iids, so this won't work
8989
##"dependency_path": [
90-
## #foreach( $inc in $dependency.includedBy )
90+
## #foreach( $inc in $dependency.includedBySorted )
9191
## {
9292
## "iid":
9393
## }

core/src/main/resources/templates/htmlReport.vsl

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -887,23 +887,24 @@ Getting Help: <a href="https://github.com/dependency-check/DependencyCheck/issue
887887
<b>SHA1:</b>&nbsp;$enc.html($dependency.Sha1sum)<br/>
888888
<b>SHA256:</b>$enc.html($dependency.Sha256sum)
889889
#end
890-
#if ($dependency.projectReferences.size()==1)
891-
<br/><b>Referenced In Project/Scope:</b> $enc.html($dependency.projectReferences.iterator().next())
890+
#set($projectReferences = $dependency.projectReferencesSorted)
891+
#if ($projectReferences.size()==1)
892+
<br/><b>Referenced In Project/Scope:</b> $enc.html($projectReferences.first())
892893
#end
893-
#if ($dependency.projectReferences.size()>1)
894+
#if ($projectReferences.size()>1)
894895
<br/><b>Referenced In Projects/Scopes:</b><ul>
895-
#foreach($ref in $dependency.projectReferences)
896+
#foreach($ref in $projectReferences)
896897
<li>$enc.html($ref)</li>
897898
#end
898899
</ul>
899900
#end
900-
#if ($dependency.includedBy.size()==1)
901-
#set($incBy=$dependency.includedBy.iterator().next())
902-
<br/><span class="tooltip"><span class="tooltiptext">$enc.html($dependency.DisplayFileName) is in the transitive dependency tree of the listed items.</span><b>Included by:</b></span> $enc.html($incBy.getReference())#if($incBy.getType()) ($enc.html($incBy.getType()))#end
901+
#set($includedBy = $dependency.includedBySorted)
902+
#if ($includedBy.size()==1)
903+
<br/><span class="tooltip"><span class="tooltiptext">$enc.html($dependency.DisplayFileName) is in the transitive dependency tree of the listed items.</span><b>Included by:</b></span> $enc.html($includedBy.first().getReference())#if($includedBy.first().getType()) ($enc.html($includedBy.first().getType()))#end
903904
#end
904-
#if ($dependency.includedBy.size()>1)
905+
#if ($includedBy.size()>1)
905906
<br/><span class="tooltip"><span class="tooltiptext">$enc.html($dependency.DisplayFileName) is in the transitive dependency tree of the listed items.</span><b>Included by:</b></span><ul>
906-
#foreach($parent in $dependency.includedBy)
907+
#foreach($parent in $includedBy)
907908
<li>$enc.html($parent.getReference())#if($parent.getType()) ($enc.html($parent.getType()))#end</li>
908909
#end
909910
</ul>
@@ -1124,23 +1125,24 @@ Getting Help: <a href="https://github.com/dependency-check/DependencyCheck/issue
11241125
<b>SHA1:</b>&nbsp;$enc.html($dependency.Sha1sum)<br/>
11251126
<b>SHA256:</b>&nbsp;$enc.html($dependency.Sha256sum)
11261127
#end
1127-
#if ($dependency.projectReferences.size()==1)
1128-
<br/><b>Referenced In Project/Scope:</b> $enc.html($dependency.projectReferences.iterator().next())
1128+
#set($projectReferences = $dependency.projectReferencesSorted)
1129+
#if ($projectReferences.size()==1)
1130+
<br/><b>Referenced In Project/Scope:</b> $enc.html($projectReferences.first())
11291131
#end
1130-
#if ($dependency.projectReferences.size()>1)
1132+
#if ($projectReferences.size()>1)
11311133
<br/><b>Referenced In Projects/Scopes:</b><ul>
1132-
#foreach($ref in $dependency.projectReferences)
1134+
#foreach($ref in $projectReferences)
11331135
<li>$enc.html($ref)</li>
11341136
#end
11351137
</ul>
11361138
#end
1137-
#if ($dependency.includedBy.size()==1)
1138-
#set($incBy=$dependency.includedBy.iterator().next())
1139-
<br/><span class="tooltip"><span class="tooltiptext">$enc.html($dependency.DisplayFileName) is in the transitive dependency tree of the listed items.</span><b>Included by:</b></span> $enc.html($incBy.getReference())#if($incBy.getType()) ($enc.html($incBy.getType()))#end
1139+
#set($includedBy = $dependency.includedBySorted)
1140+
#if ($includedBy.size()==1)
1141+
<br/><span class="tooltip"><span class="tooltiptext">$enc.html($dependency.DisplayFileName) is in the transitive dependency tree of the listed items.</span><b>Included by:</b></span> $enc.html($includedBy.first().getReference())#if($includedBy.first().getType()) ($enc.html($includedBy.first().getType()))#end
11401142
#end
1141-
#if ($dependency.includedBy.size()>1)
1143+
#if ($includedBy.size()>1)
11421144
<br/><b>Included by:</b><ul>
1143-
#foreach($parent in $dependency.includedBy)
1145+
#foreach($parent in $includedBy)
11441146
<li>$enc.html($parent.getReference())#if($parent.getType()) ($enc.html($parent.getType()))#end</li>
11451147
#end
11461148
</ul>

core/src/main/resources/templates/jenkinsReport.vsl

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -521,23 +521,24 @@ Getting Help: <a href="https://github.com/dependency-check/DependencyCheck/issue
521521
<b>SHA1:</b>&nbsp;$enc.html($dependency.Sha1sum)<br/>
522522
<b>SHA256:</b>$enc.html($dependency.Sha256sum)
523523
#end
524-
#if ($dependency.projectReferences.size()==1)
525-
<br/><b>Referenced In Project/Scope:</b> $enc.html($dependency.projectReferences.iterator().next())
524+
#set($projectReferences = $dependency.projectReferencesSorted)
525+
#if ($projectReferences.size()==1)
526+
<br/><b>Referenced In Project/Scope:</b> $enc.html($projectReferences.first())
526527
#end
527-
#if ($dependency.projectReferences.size()>1)
528+
#if ($projectReferences.size()>1)
528529
<br/><b>Referenced In Projects/Scopes:</b><ul>
529-
#foreach($ref in $dependency.projectReferences)
530+
#foreach($ref in $projectReferences)
530531
<li>$enc.html($ref)</li>
531532
#end
532533
</ul>
533534
#end
534-
#if ($dependency.includedBy && $dependency.includedBy.size()==1)
535-
#set($incBy=$dependency.includedBy.iterator().next())
536-
<br/><span class="tooltip"><span class="tooltiptext">$enc.html($dependency.DisplayFileName) is in the transitive dependency tree of the listed items.</span><b>Included by:</b></span> $enc.html($incBy.getReference())#if($incBy.getType()) ($enc.html($incBy.getType()))#end
535+
#set($includedBy = $dependency.includedBySorted)
536+
#if ($includedBy.size()==1)
537+
<br/><span class="tooltip"><span class="tooltiptext">$enc.html($dependency.DisplayFileName) is in the transitive dependency tree of the listed items.</span><b>Included by:</b></span> $enc.html($includedBy.first().getReference())#if($includedBy.first().getType()) ($enc.html($includedBy.first().getType()))#end
537538
#end
538-
#if ($dependency.includedBy && $dependency.includedBy.size()>1)
539+
#if ($includedBy.size()>1)
539540
<br/><span class="tooltip"><span class="tooltiptext">$enc.html($dependency.DisplayFileName) is in the transitive dependency tree of the listed items.</span><b>Included by:</b></span><ul>
540-
#foreach($parent in $dependency.includedBy)
541+
#foreach($parent in $includedBy)
541542
<li>$enc.html($parent.getReference())#if($parent.getType()) ($enc.html($parent.getType()))#end</li>
542543
#end
543544
</ul>

core/src/main/resources/templates/jsonReport.vsl

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,19 @@
6565
"sha256": "$enc.json($dependency.Sha256sum)"#end
6666
#if($dependency.description),"description": "$enc.json($dependency.description)"#end
6767
#if($dependency.license),"license": "$enc.json($dependency.license)"#end
68-
#if ($dependency.projectReferences.size()>0)
68+
#set($projectReferences = $dependency.projectReferencesSorted)
69+
#if ($projectReferences.size()>0)
6970
,"projectReferences": [
70-
#foreach($ref in $dependency.projectReferences)
71+
#foreach($ref in $projectReferences)
7172
#if($foreach.count > 1),#end
7273
"$enc.json($ref)"
7374
#end
7475
]
7576
#end
76-
#if ($dependency.includedBy.size()>0)
77+
#set($includedBy = $dependency.includedBySorted)
78+
#if ($includedBy.size()>0)
7779
,"includedBy": [
78-
#foreach($ref in $dependency.includedBy)
80+
#foreach($ref in $includedBy)
7981
#if($foreach.count > 1),#end
8082
{ "reference":"$enc.json($ref.getReference())"#if($ref.getType()),"type":"$enc.json($ref.getType())"#end }
8183
#end

core/src/main/resources/templates/junitReport.vsl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@
9393
#end]]>#end</system-out>
9494
#set($referencedProjects="")
9595
#set($projectReferenced=false)
96-
#foreach($ref in $dependency.projectReferences)
96+
#foreach($ref in $dependency.projectReferencesSorted)
9797
#if($projectReferenced)
9898
#set($referencedProjects="$referencedProjects, $ref")
9999
#else

core/src/main/resources/templates/xmlReport.vsl

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,18 @@ Copyright (c) 2018 Jeremy Long. All Rights Reserved.
8888
#if ($dependency.license)
8989
<license>$enc.xml($dependency.license)</license>
9090
#end
91-
#if ($dependency.projectReferences.size()>0)
91+
#set($projectReferences = $dependency.projectReferencesSorted)
92+
#if ($projectReferences.size()>0)
9293
<projectReferences>
93-
#foreach($ref in $dependency.projectReferences)
94+
#foreach($ref in $projectReferences)
9495
<projectReference>$enc.xml($ref)</projectReference>
9596
#end
9697
</projectReferences>
9798
#end
98-
#if ($dependency.includedBy.size()>0)
99+
#set($includedBy = $dependency.includedBySorted)
100+
#if ($includedBy.size()>0)
99101
<includedBy>
100-
#foreach($ref in $dependency.includedBy)
102+
#foreach($ref in $includedBy)
101103
<reference#if($ref.getType()) type="$enc.xml($ref.getType())"#end>$enc.xml($ref.getReference())</reference>
102104
#end
103105
</includedBy>

0 commit comments

Comments
 (0)