|
| 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