Skip to content

Commit 331d2cc

Browse files
Propose ADR-031: JVN vulnerability data source
Proposes mirroring JVN (Japan Vulnerability Notes / JVN iPedia) as a new vuln-data-source module via the ADR-010 extension point: yearly detail feeds (jvndb_detail_YYYY.rdf) with checksum-based skipping, CPE 2.2 to 2.3 normalisation, Japanese version-expression parsing into vers ranges, and all advisories stored under a new JVN source. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: mitsukado-shinagawa <mitsukado-shinagawa@users.noreply.github.com>
1 parent 419fc70 commit 331d2cc

1 file changed

Lines changed: 120 additions & 0 deletions

File tree

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
| Status | Date | Author(s) |
2+
|:---------|:-----------|:---------------------------------------------------------------|
3+
| Proposed | 2026-07-08 | [@mitsukado-shinagawa](https://github.com/mitsukado-shinagawa) |
4+
5+
## Context
6+
7+
Dependency-Track mirrors vulnerability intelligence from the NVD, GitHub Advisories, and OSV
8+
(see [ADR-010](./010-vulnerability-datasource-extension-point.md)). None of these cover the
9+
[Japan Vulnerability Notes (JVN) / JVN iPedia](https://jvndb.jvn.jp/) database operated by
10+
JPCERT/CC and IPA.
11+
12+
Organizations operating in Japan run software — both domestic-vendor products (e.g. Hitachi
13+
Cosminexus) and internationally-distributed products (e.g. Cisco) — for which JVN publishes
14+
vulnerability information, sometimes ahead of, or in addition to, the NVD. Users have asked
15+
whether JVN data can be mirrored into Dependency-Track the same way the NVD is, so that
16+
CPE-based matching (`InternalVulnAnalyzer`) can flag affected components.
17+
18+
Before committing to an implementation, a PoC measured the machine-readability of the JVN data
19+
over the **50 most recently published JVNDB entries** (2026-07-07):
20+
21+
| Metric | Result | Interpretation |
22+
|:------------------------------------|:-------------|:--------------------------------------------------|
23+
| Entries carrying a CPE | 100% (50/50) | Matching substrate is essentially always present |
24+
| CPE encoding | 100% CPE 2.2 | Must be converted to CPE 2.3 for storage |
25+
| CPE + resolvable version | 94% (47/50) | Upper bound on precise (range) CPE matching |
26+
| Entries carrying a CVE ID | 96% (48/50) | Heavy overlap with the NVD |
27+
| JVN-only (no CVE ID) | 4% (2/50) | The genuinely additive coverage |
28+
29+
Three findings dominate the design:
30+
31+
1. **Versions are not in the CPE.** Every sampled CPE was product-level
32+
(`cpe:/a:suse:rancher_fleet`), with the affected version carried in a *separate*,
33+
free-text Japanese field (`VersionNumber`, e.g. `"0.15.0 以上 0.15.2 未満"` or `"25.3.3.1"`).
34+
Storing the CPE alone yields all-versions (product-level) matches — false positives.
35+
Precise matching requires **parsing the Japanese version expressions**
36+
(`以上``>=`, `以下``<=`, `未満``<`, `より前``<`, exact → `=`) into `vers` ranges,
37+
which `BovModelConverter` already turns into `versionStart/End{Including,Excluding}`.
38+
39+
2. **96% of entries duplicate the NVD.** For CVE-carrying entries the NVD is the authoritative
40+
source with better-structured version ranges. Per
41+
[`VulnerabilityUpdatePolicy`](../../apiserver/src/main/java/org/dependencytrack/vulnanalysis/VulnerabilityUpdatePolicy.java),
42+
a non-authoritative data source cannot overwrite a vulnerability owned by an enabled
43+
authoritative source, so a JVN source cannot "enrich" an NVD-owned CVE through the standard
44+
mirror path. The unique, durable value of JVN is the **~4% JVN-only advisories**
45+
(domestic-vendor products, `JVN#`/`JVNVU` without a CVE) plus Japanese-language descriptions
46+
and publication timeliness — **not** blanket coverage of already-CVE'd products.
47+
48+
3. **The MyJVN query API cannot backfill history.** MyJVN's `getVulnOverviewList` caps at the
49+
~747 most-recently-published advisories regardless of the requested date range (measured
50+
2026-07-08: `datePublicStart=2026-06-01`→731, `=2020-01-01`→747, `=2010-01-01`→747), and
51+
bounded historical windows return `totalRes=0`. JVN however publishes **yearly detail feeds**
52+
(`.../ja/feed/detail/jvndb_detail_YYYY.rdf`, full history 1988–present) alongside a
53+
`checksum.txt` manifest with a `sha256` per feed file. The feeds use the same VULDEF schema
54+
as the API's detail responses, and are the acquisition channel used by
55+
[vulsio/go-cve-dictionary](https://github.com/vulsio/go-cve-dictionary) (Vuls).
56+
57+
## Decision
58+
59+
Add a `jvn` module under `vuln-data-source/`, implementing the `VulnDataSource` extension point
60+
of [ADR-010](./010-vulnerability-datasource-extension-point.md), disabled by default. It
61+
exchanges data as CycloneDX BOVs, reusing the existing mirror pipeline
62+
(`MirrorVulnDataSourceWorkflow``BovModelConverter``VulnerableSoftware`), so no changes to
63+
ingestion, storage, or matching are required.
64+
65+
Scope and behavior:
66+
67+
* **Fetch.** Download the yearly detail feeds `jvndb_detail_YYYY.rdf` for
68+
`startYear..currentYear`, skipping years whose `sha256` in `checksum.txt` is unchanged since
69+
the last successful run (feed-digest checkpoint in the plugin KV store, mirroring the NVD
70+
data source's checkpointing). A failed year is not checkpointed and is retried on the next run.
71+
* **CPE normalization.** Convert CPE 2.2 URIs to CPE 2.3 formatted strings before emitting them
72+
on BOV `affects` components.
73+
* **Version parsing.** Translate the `VersionNumber` free-text field into `vers` ranges. When no
74+
version expression can be parsed for a product, fall back to an all-versions (wildcard) range —
75+
the product *is* declared affected by JVN, and this matches how product-level CPEs from the NVD
76+
behave — rather than silently dropping the product.
77+
* **Source identity.** Emit **every** advisory under a new `JVN` source with its
78+
`JVNDB-YYYY-NNNNNN` identifier — no CVE→NVD routing and no NVD duplicate check. JVN is stored
79+
as-is, like the NVD source; JVN and NVD records for the same CVE coexist.
80+
81+
*Considered alternative:* emit CVE-carrying advisories under the CVE / NVD identity and reserve
82+
the `JVN` source for CVE-less entries, avoiding duplicate records. Rejected because the NVD
83+
remains authoritative for those CVEs anyway (`VulnerabilityUpdatePolicy` prevents JVN from
84+
updating them once NVD publishes), so the JVN detail — Japanese descriptions, JVN's own version
85+
ranges, JVN references — would be unreachable; and because "the JVN record you see is exactly
86+
what JVN published" is easier to reason about operationally. The cost is accepted, documented
87+
duplication (see Consequences).
88+
* **Configuration.** `enabled` (default `false`), `feedBaseUrl`
89+
(default `https://jvndb.jvn.jp/ja/feed`), `startYear` (default `1998`, the beginning of
90+
JVN iPedia data).
91+
* **`Vulnerability.Source` registration.** A data source that emits its own source identity must
92+
be registered in the `org.dependencytrack.model.Vulnerability.Source` enum: the mirror derives
93+
the source name from the data source name (`VulnDataSourceMirrorService` upper-cases it, `jvn`
94+
`JVN`) and validates it via `Source.ofName(...)` in `MirrorVulnDataSourceActivity`; an
95+
unregistered name fails the whole mirror run. `BovModelConverter` likewise resolves the BOV's
96+
source via `Source.ofName`. Adding `JVN` to the enum is therefore part of this change.
97+
98+
## Consequences
99+
100+
* Users can detect JVN advisories (notably for domestic-vendor products and JVN-only entries)
101+
via the same CPE matching they already use, by registering the affected product as a component
102+
with its CPE.
103+
* No new persistence, ingestion, or matching code — apart from the one-line `Source` enum entry,
104+
the change is isolated to one new module.
105+
* **Full-history storage and intentional duplication.** Mirroring all years stores tens of
106+
thousands of `JVN`-sourced vulnerabilities, and CVE-carrying advisories produce a `JVN` record
107+
alongside the NVD's `CVE-*` record — a component may be flagged by both. This duplication is
108+
an accepted trade-off for complete, self-contained JVN coverage. The first full backfill
109+
downloads all yearly feeds; subsequent runs only re-fetch years whose `checksum.txt` `sha256`
110+
changed.
111+
* A **Japanese version-expression parser** becomes a maintained component; its coverage caps
112+
matching precision, and un-parseable expressions degrade to all-versions matches. This parser
113+
is the primary implementation and maintenance risk.
114+
* CPE string alignment between JVN and the NVD/user-registered components is assumed; entries
115+
with non-concrete CPEs (e.g. `cpe:/a:misc:multiple_vendors`) will not match and are dropped.
116+
* For internationally-covered products (e.g. Cisco), this adds little over the existing NVD
117+
source; the value is realized on domestic-vendor and JVN-only entries.
118+
* Prior art exists for the *architecture* (fetch JVN feeds → local DB → CPE match) in
119+
[vulsio/go-cve-dictionary](https://github.com/vulsio/go-cve-dictionary), but not within
120+
Dependency-Track; this would be the first such integration.

0 commit comments

Comments
 (0)