Skip to content

Commit a5c9154

Browse files
committed
Improve scan command output
Signed-off-by: nscuro <[email protected]>
1 parent 7e802ae commit a5c9154

File tree

1 file changed

+72
-32
lines changed

1 file changed

+72
-32
lines changed

src/main/java/org/dependencytrack/vulndb/cli/ScanCommand.java

+72-32
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.jdbi.v3.core.statement.Query;
1414
import org.jdbi.v3.sqlite3.SQLitePlugin;
1515
import picocli.CommandLine.Command;
16+
import picocli.CommandLine.Help.Ansi;
1617
import picocli.CommandLine.Option;
1718
import picocli.CommandLine.Parameters;
1819
import us.springett.parsers.cpe.Cpe;
@@ -23,10 +24,13 @@
2324
import java.nio.file.Files;
2425
import java.nio.file.Path;
2526
import java.util.ArrayList;
27+
import java.util.Comparator;
2628
import java.util.HashMap;
2729
import java.util.HashSet;
2830
import java.util.List;
31+
import java.util.Map;
2932
import java.util.Set;
33+
import java.util.TreeMap;
3034
import java.util.concurrent.Callable;
3135

3236
@Command(name = "scan", description = "Test a database by scanning BOMs.")
@@ -47,49 +51,65 @@ public Integer call() throws Exception {
4751
.create("jdbc:sqlite:%s".formatted(databaseFilePath))
4852
.installPlugin(new SQLitePlugin());
4953

50-
if (ensureIndexes) {
51-
jdbi.useHandle(handle -> {
52-
handle.execute("""
53-
create index if not exists matching_criteria_purl_ns_idx
54-
on matching_criteria(purl_type, purl_namespace, purl_name)
55-
where purl_namespace is not null;
56-
""");
57-
58-
handle.execute("""
59-
create index if not exists matching_criteria_purl_idx
60-
on matching_criteria(purl_type, purl_name)
61-
where purl_namespace is null;
62-
""");
63-
});
64-
}
54+
maybeCreateIndexes(jdbi);
6555

6656
final byte[] bomBytes = Files.readAllBytes(bomFilePath);
6757
final Bom bom = BomParserFactory.createParser(bomBytes).parse(bomBytes);
6858

59+
final var matchesByComponentByVulnId = new TreeMap<String, Map<Component, Set<MatchMetadata>>>();
60+
6961
try (final Handle handle = jdbi.open()) {
7062
// TODO: Consider metadata.component, nested components etc.
7163

7264
for (final Component component : bom.getComponents()) {
7365
final Set<MatchMetadata> matches = scan(handle, component);
74-
if (!matches.isEmpty()) {
75-
String componentName = component.getName();
76-
if (component.getGroup() != null) {
77-
componentName = component.getGroup() + "/" + componentName;
78-
}
79-
if (component.getVersion() != null) {
80-
componentName = componentName + "@" + component.getVersion();
81-
}
66+
if (matches.isEmpty()) {
67+
continue;
68+
}
8269

83-
// TODO: Move reporting to the very end.
84-
System.out.println(componentName + ":");
85-
for (final MatchMetadata match : matches) {
86-
System.out.println("- %s\n Matched range: %s\n Source: %s".formatted(
87-
match.vulnId(),
88-
match.criteriaVers(),
89-
match.criteriaSource()));
90-
}
91-
System.out.println();
70+
for (final MatchMetadata match : matches) {
71+
final Map<Component, Set<MatchMetadata>> matchesByComponent =
72+
matchesByComponentByVulnId.computeIfAbsent(
73+
match.vulnId(), ignored -> new TreeMap<>(
74+
Comparator.comparing(Component::getName)
75+
.thenComparing(Component::getVersion)));
76+
77+
matchesByComponent.computeIfAbsent(
78+
component, ignored -> new HashSet<>()).add(match);
79+
}
80+
}
81+
}
82+
83+
if (matchesByComponentByVulnId.isEmpty()) {
84+
System.out.println("@|bold,green no vulnerabilities identified|@");
85+
return 0;
86+
}
87+
88+
for (final String vulnId : matchesByComponentByVulnId.keySet()) {
89+
final Map<Component, Set<MatchMetadata>> matchesByComponent = matchesByComponentByVulnId.get(vulnId);
90+
91+
System.out.println(Ansi.AUTO.string("@|bold,red,underline %s|@".formatted(vulnId)));
92+
93+
for (final Map.Entry<Component, Set<MatchMetadata>> entry : matchesByComponent.entrySet()) {
94+
final Component component = entry.getKey();
95+
final Set<MatchMetadata> matches = entry.getValue();
96+
97+
String componentName = component.getName();
98+
if (component.getGroup() != null) {
99+
componentName = component.getGroup() + "/" + componentName;
92100
}
101+
if (component.getVersion() != null) {
102+
componentName = componentName + "@" + component.getVersion();
103+
}
104+
105+
System.out.println("- " + componentName);
106+
107+
for (final MatchMetadata match : matches) {
108+
System.out.println(Ansi.AUTO.string(" + matched: @|italic %s|@ (source: %s)".formatted(
109+
match.criteriaVers(), match.criteriaSource())));
110+
}
111+
112+
System.out.println();
93113
}
94114
}
95115

@@ -100,6 +120,26 @@ on matching_criteria(purl_type, purl_name)
100120
return 0;
101121
}
102122

123+
private void maybeCreateIndexes(final Jdbi jdbi) {
124+
if (!ensureIndexes) {
125+
return;
126+
}
127+
128+
jdbi.useHandle(handle -> {
129+
handle.execute("""
130+
create index if not exists matching_criteria_purl_ns_idx
131+
on matching_criteria(purl_type, purl_namespace, purl_name)
132+
where purl_namespace is not null;
133+
""");
134+
135+
handle.execute("""
136+
create index if not exists matching_criteria_purl_idx
137+
on matching_criteria(purl_type, purl_name)
138+
where purl_namespace is null;
139+
""");
140+
});
141+
}
142+
103143
private record MatchMetadata(
104144
String vulnId,
105145
String criteriaSource,

0 commit comments

Comments
 (0)