Skip to content

Commit cb39a1e

Browse files
Add JVN (Japan Vulnerability Notes) vulnerability data source
Implements ADR-031. Adds a vuln-data-source/jvn module (disabled by default) that mirrors JVN iPedia from the yearly detail feeds (jvndb_detail_YYYY.rdf, 1998-present), skipping years whose sha256 in checksum.txt is unchanged since the last successful run. Advisories are parsed from the VULDEF schema, converted to CycloneDX BOVs and ingested through the existing mirror pipeline; no changes to ingestion, storage, or matching. JVN-specific handling: - CPE 2.2 URIs are normalised to CPE 2.3 formatted strings. - The free-text Japanese VersionNumber field is parsed into vers ranges (e.g. "0.15.0 以上 0.15.2 未満" -> vers:generic/>=0.15.0|<0.15.2); products without a parseable version fall back to an all-versions range. - CWE ids are extracted from <RelatedItem type="cwe"> entries; their glossary URLs are skipped rather than emitted as advisory references. - The Impact and Solution section descriptions are mapped to the CycloneDX detail and recommendation fields. - Every advisory is stored under the JVN source, keyed by its JVNDB id. Registers JVN in Vulnerability.Source, as the mirror validates source names via Source.ofName. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: mitsukado-shinagawa <mitsukado-shinagawa@users.noreply.github.com>
1 parent 331d2cc commit cb39a1e

20 files changed

Lines changed: 2053 additions & 0 deletions

File tree

apiserver/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,11 @@
222222
<artifactId>vuln-data-source-github</artifactId>
223223
<version>${project.version}</version>
224224
</dependency>
225+
<dependency>
226+
<groupId>org.dependencytrack</groupId>
227+
<artifactId>vuln-data-source-jvn</artifactId>
228+
<version>${project.version}</version>
229+
</dependency>
225230
<dependency>
226231
<groupId>org.dependencytrack</groupId>
227232
<artifactId>vuln-data-source-nvd</artifactId>

apiserver/src/main/java/org/dependencytrack/model/Vulnerability.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ public enum Source {
133133
INTERNAL, // Internally-managed (and manually entered) vulnerability
134134
OSV, // Google OSV Advisories
135135
SNYK, // Snyk Purl Vulnerability
136+
JVN, // Japan Vulnerability Notes (JVN iPedia)
136137
UNKNOWN; // Unknown vulnerability sources
137138

138139
public static boolean isKnownSource(String source) {

vuln-data-source/jvn/pom.xml

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ This file is part of Dependency-Track.
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
~
17+
~ SPDX-License-Identifier: Apache-2.0
18+
~ Copyright (c) OWASP Foundation. All Rights Reserved.
19+
-->
20+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
21+
<modelVersion>4.0.0</modelVersion>
22+
<parent>
23+
<groupId>org.dependencytrack</groupId>
24+
<artifactId>vuln-data-source-parent</artifactId>
25+
<version>5.1.0-SNAPSHOT</version>
26+
</parent>
27+
28+
<artifactId>vuln-data-source-jvn</artifactId>
29+
<packaging>jar</packaging>
30+
31+
<name>Vulnerability Data Source :: JVN</name>
32+
33+
<properties>
34+
<project.parentBaseDir>${project.basedir}/../..</project.parentBaseDir>
35+
</properties>
36+
37+
<dependencies>
38+
<dependency>
39+
<groupId>org.dependencytrack</groupId>
40+
<artifactId>vuln-data-source-api</artifactId>
41+
<version>${project.version}</version>
42+
<scope>provided</scope>
43+
</dependency>
44+
<dependency>
45+
<groupId>org.dependencytrack</groupId>
46+
<artifactId>plugin-testing</artifactId>
47+
<version>${project.version}</version>
48+
<scope>test</scope>
49+
</dependency>
50+
51+
<dependency>
52+
<groupId>us.springett</groupId>
53+
<artifactId>cpe-parser</artifactId>
54+
</dependency>
55+
56+
<!-- Required at compile time by the jsonschema2pojo-generated config class. -->
57+
<dependency>
58+
<groupId>com.fasterxml.jackson.core</groupId>
59+
<artifactId>jackson-annotations</artifactId>
60+
</dependency>
61+
62+
<!-- Parses the JVN feed checksum.txt manifest; provided at runtime by Dependency-Track. -->
63+
<dependency>
64+
<groupId>com.fasterxml.jackson.core</groupId>
65+
<artifactId>jackson-databind</artifactId>
66+
<scope>provided</scope>
67+
</dependency>
68+
69+
<dependency>
70+
<groupId>com.google.protobuf</groupId>
71+
<artifactId>protobuf-java-util</artifactId>
72+
</dependency>
73+
74+
<dependency>
75+
<groupId>io.github.nscuro</groupId>
76+
<artifactId>versatile-core</artifactId>
77+
</dependency>
78+
79+
<dependency>
80+
<groupId>org.slf4j</groupId>
81+
<artifactId>slf4j-api</artifactId>
82+
<scope>provided</scope>
83+
</dependency>
84+
<dependency>
85+
<groupId>org.slf4j</groupId>
86+
<artifactId>slf4j-simple</artifactId>
87+
<scope>test</scope>
88+
</dependency>
89+
90+
<dependency>
91+
<groupId>org.junit.jupiter</groupId>
92+
<artifactId>junit-jupiter</artifactId>
93+
<scope>test</scope>
94+
</dependency>
95+
<dependency>
96+
<groupId>org.wiremock</groupId>
97+
<artifactId>wiremock-standalone</artifactId>
98+
<scope>test</scope>
99+
</dependency>
100+
</dependencies>
101+
102+
<build>
103+
<plugins>
104+
<plugin>
105+
<groupId>org.jsonschema2pojo</groupId>
106+
<artifactId>jsonschema2pojo-maven-plugin</artifactId>
107+
<executions>
108+
<execution>
109+
<goals>
110+
<goal>generate</goal>
111+
</goals>
112+
</execution>
113+
</executions>
114+
<configuration>
115+
<sourceDirectory>${basedir}/src/main/resources/org/dependencytrack/vulndatasource/jvn</sourceDirectory>
116+
<targetPackage>org.dependencytrack.vulndatasource.jvn</targetPackage>
117+
<generateBuilders>true</generateBuilders>
118+
</configuration>
119+
</plugin>
120+
</plugins>
121+
</build>
122+
123+
</project>
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* This file is part of Dependency-Track.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* Copyright (c) OWASP Foundation. All Rights Reserved.
18+
*/
19+
package org.dependencytrack.vulndatasource.jvn;
20+
21+
import org.jspecify.annotations.Nullable;
22+
23+
import java.time.Instant;
24+
import java.util.List;
25+
26+
/**
27+
* A parsed JVN advisory, as returned by the MyJVN {@code getVulnDetailInfo} API.
28+
*
29+
* @since 5.1.0
30+
*/
31+
record JvnAdvisory(
32+
String jvnDbId,
33+
@Nullable String title,
34+
@Nullable String overview,
35+
@Nullable String detail,
36+
@Nullable String recommendation,
37+
List<String> cveIds,
38+
List<Integer> cweIds,
39+
List<Cvss> cvssList,
40+
List<AffectedProduct> affected,
41+
List<String> referenceUrls,
42+
@Nullable Instant datePublic,
43+
@Nullable Instant dateLastUpdated) {
44+
45+
/** An affected product: a single (product-level) CPE plus its Japanese version expressions. */
46+
record AffectedProduct(
47+
@Nullable String vendor,
48+
@Nullable String productName,
49+
String cpe22,
50+
List<String> versionTexts) {
51+
}
52+
53+
/** A CVSS rating attached to the advisory. */
54+
record Cvss(
55+
@Nullable String version,
56+
@Nullable String severity,
57+
@Nullable Double baseScore,
58+
@Nullable String vector) {
59+
}
60+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* This file is part of Dependency-Track.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* Copyright (c) OWASP Foundation. All Rights Reserved.
18+
*/
19+
package org.dependencytrack.vulndatasource.jvn;
20+
21+
import com.fasterxml.jackson.databind.JsonNode;
22+
import com.fasterxml.jackson.databind.ObjectMapper;
23+
24+
import java.io.IOException;
25+
import java.net.URI;
26+
import java.net.http.HttpClient;
27+
import java.net.http.HttpRequest;
28+
import java.net.http.HttpResponse;
29+
import java.net.http.HttpResponse.BodyHandlers;
30+
import java.time.Duration;
31+
import java.util.HashMap;
32+
import java.util.Map;
33+
34+
/**
35+
* A thin client for the JVN data feeds published under {@code https://jvndb.jvn.jp/ja/feed}.
36+
* <p>
37+
* Fetches the per-year detail feeds ({@code detail/jvndb_detail_YYYY.rdf}, full history 1988–present)
38+
* and the {@code checksum.txt} manifest used to skip years whose feed has not changed. This is the
39+
* same acquisition strategy used by go-cve-dictionary / Vuls, and — unlike the MyJVN
40+
* {@code getVulnOverviewList} API — is not capped to the most-recent advisories.
41+
*
42+
* @since 5.1.0
43+
*/
44+
final class JvnClient {
45+
46+
static final String DEFAULT_FEED_BASE_URL = "https://jvndb.jvn.jp/ja/feed";
47+
48+
private static final ObjectMapper JSON = new ObjectMapper();
49+
50+
private final HttpClient httpClient;
51+
private final String feedBaseUrl;
52+
53+
JvnClient(final HttpClient httpClient, final String feedBaseUrl) {
54+
this.httpClient = httpClient;
55+
this.feedBaseUrl = feedBaseUrl.endsWith("/")
56+
? feedBaseUrl.substring(0, feedBaseUrl.length() - 1)
57+
: feedBaseUrl;
58+
}
59+
60+
/** Downloads the full detail feed (a VULDEF document) for a single publication year. */
61+
byte[] fetchDetailFeed(final int year) throws IOException, InterruptedException {
62+
return get(feedBaseUrl + "/detail/" + detailFeedFilename(year));
63+
}
64+
65+
/**
66+
* Fetches {@code checksum.txt} and returns a map of feed filename to its {@code sha256} digest,
67+
* so callers can skip re-downloading years whose feed is unchanged since the last run.
68+
*/
69+
Map<String, String> fetchChecksums() throws IOException, InterruptedException {
70+
final JsonNode array = JSON.readTree(get(feedBaseUrl + "/checksum.txt"));
71+
final Map<String, String> digestByFilename = new HashMap<>();
72+
if (array.isArray()) {
73+
for (final JsonNode entry : array) {
74+
final String filename = entry.path("filename").asText(null);
75+
final String sha256 = entry.path("sha256").asText(null);
76+
if (filename != null && sha256 != null) {
77+
digestByFilename.put(filename, sha256);
78+
}
79+
}
80+
}
81+
return digestByFilename;
82+
}
83+
84+
/** The {@code checksum.txt} filename for a year's detail feed (e.g. {@code jvndb_detail_2015.rdf}). */
85+
static String detailFeedFilename(final int year) {
86+
return "jvndb_detail_" + year + ".rdf";
87+
}
88+
89+
String feedBaseUrl() {
90+
return feedBaseUrl;
91+
}
92+
93+
private byte[] get(final String url) throws IOException, InterruptedException {
94+
final HttpRequest request = HttpRequest.newBuilder()
95+
.uri(URI.create(url))
96+
.timeout(Duration.ofSeconds(60))
97+
.header("User-Agent", "Dependency-Track JVN data source")
98+
.GET()
99+
.build();
100+
final HttpResponse<byte[]> response = httpClient.send(request, BodyHandlers.ofByteArray());
101+
if (response.statusCode() != 200) {
102+
throw new IOException("Unexpected response code " + response.statusCode()
103+
+ " from JVN feed: " + url);
104+
}
105+
return response.body();
106+
}
107+
}

0 commit comments

Comments
 (0)