Skip to content

feat(detector): route Fortinet to vuls2#2591

Merged
MaineK00n merged 4 commits into
masterfrom
shino/cpe-detect-fortinet
Jun 30, 2026
Merged

feat(detector): route Fortinet to vuls2#2591
MaineK00n merged 4 commits into
masterfrom
shino/cpe-detect-fortinet

Conversation

@shino

@shino shino commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

What

Move Fortinet CPE detection from go-cve-dictionary to the vuls2 path, mirroring the Cisco (#2581) and Palo Alto (#2589) migrations. Fortinet now comes from the two extractors — fortinet-cvrf and fortinet-csaf — both rooted on the FG-IR PSIRT advisory.

Note

Detection requires both fortinet-cvrf and fortinet-csaf in the vuls2 DB — wired in vulsio/vuls-data-db#187 (nightly active; db-main.mk stays commented until this PR ships, so :0/:latest consumers aren't fed Fortinet ranges before vuls can parse them).

How

Detection

  • detectCpeURIsCvesWithGoCVEDictionary drops Fortinet from the admission gate and nils detail.Fortinets before confidence selection (same treatment as NVD / Cisco / Palo Alto); the go-cve-dictionary Fortinet DistroAdvisory synthesis is removed.
  • FillCvesWithGoCVEDictionary no longer fills Fortinet CveContent.
  • walkVulnerabilityDatas is unchanged: Fortinet is detected like any other CPE source. Fortinet is advisory-rooted (root = FG-IR PSIRT advisory). The CVRF stub is bare → sparse CveContent + DistroAdvisory; the CSAF stub is rich (CVSS/CWE/refs) → full CveContent. Either way the advisory carries no vendor SIR (Fortinet severity is CVSS), so DistroAdvisory.Severity is empty.
  • Every Fortinet source switch (toCveContentType, toVuls0Confidence ×2, advisoryReference, compareSourceID) covers FortinetCVRF and FortinetCSAF. The legacy bare fortinet source id is intentionally not routed (not produced into the vuls2 DB). CSAF is preferred over CVRF in compareSourceID (tier 5 vs 4, matching the Cisco/Palo Alto CSAF > CVRF/JSON ordering), so when both detect the same CVE the CSAF content wins the merge.
  • cveContentSourceLink gains a models.Fortinet case returning https://www.fortiguard.com/psirt/<FG-IR-...> (keyed by the root ID), matching advisoryReference.

Enrich

  • Not needed / not added (per design): Fortinet is detection-only.

Dependency bump

  • vuls2 → v0.0.1-alpha.0.20260630120316-69ab575c2ebe, vuls-data-update → v0.0.0-20260630113343-f3c7f13ad814 (post chore(deps): bump vuls-data-update to nightly, for fortinet MaineK00n/vuls2#396, which bumped vuls-data-update to the merged Fortinet CSAF extractor). This pulls in github.com/vulsio/go-fortinet-version, the backend for the fortinet-* cpecriterion RangeTypes, and retains the pan-os RangeType from feat(detector): route Palo Alto to vuls2 #2589. Required both for the feature and for vuls0 to read any vuls2 DB that contains Fortinet (the old vuls2 fails on the fortinet-* ranges / unsupported source: fortinet-cvrf).

Testing

  • postConvert unit tests cover: CSAF (rich CveContent with CVSS/CWE/refs, PSIRT source link, empty-Severity DistroAdvisory, fortinet-fortipam range); CVRF (bare stub → sparse CveContent + DistroAdvisory); CSAF + CVRF both detecting the same CVE (CSAF preferred in the merge); and NVD-v2 + Fortinet-CSAF coexistence (both CveContents kept, since they are distinct CveContentTypes). Fixtures model real FG-IR-25-032 / FG-IR-24-041.
  • GOWORK=off go test ./detector/... green; golangci-lint clean.
  • End-to-end OLD (go-cve) vs NEW (vuls2) over 10 Fortinet CPEs (fortios/proxy/pam/portal/manager/analyzer/web/client/switch), with fortinet-cvrf+fortinet-csaf db-added: 137 CVEs in common, OLD=138 / NEW=141. The 1 OLD-only is a go-cve vendor:product over-match (scanned version outside the affected range; vuls2 correctly excludes it); the 4 NEW-only are go-cve data gaps (missing product coverage / missing CVE) that vuls2 version-confirms. No regressions — vuls2 Fortinet detection is at parity or better. (The full master-binary harness can't run the OLD side here because master can't read a Fortinet-containing vuls2 DB — which is exactly the dependency this PR resolves.)

🤖 Generated with Claude Code

Base automatically changed from shino/cpe-detect-paloalto to master June 30, 2026 06:22
shino and others added 2 commits June 30, 2026 15:24
Mirror the Cisco / Palo Alto migrations: move Fortinet CPE detection from
go-cve-dictionary to the vuls2 path. Fortinet now comes from the two new
extractors — fortinet-cvrf (merged) and fortinet-csaf (MaineK00n/vuls-data-update
#853) — both rooted on the FG-IR PSIRT advisory.

- detector.go: drop Fortinet from the go-cve-dictionary admission gate and the
  FillCvesWithGoCVEDictionary CveContent fill, remove the DistroAdvisory
  synthesis, and nil detail.Fortinets before confidence selection (same as
  NVD / Cisco / Palo Alto).
- vuls2/vendor.go: extend every Fortinet source switch (toCveContentType,
  toVuls0Confidence x2, advisoryReference, compareSourceID) to cover
  FortinetCVRF and FortinetCSAF, with CSAF preferred over CVRF in
  compareSourceID (tier 5 vs 4, like the Cisco/Palo Alto CSAF>CVRF/JSON order).
  cveContentSourceLink gains a models.Fortinet case returning the PSIRT advisory
  page keyed by the root ID (FG-IR-...), matching advisoryReference.

Fortinet is advisory-rooted like Cisco. The CVRF stub is bare (sparse
CveContent + DistroAdvisory); the CSAF stub is rich (CVSS/CWE/refs), so the
generic path builds a full CveContent. Either way the advisory carries no vendor
SIR (Fortinet severity is CVSS), so DistroAdvisory.Severity is empty. No enrich.

Bumps vuls-data-update to MaineK00n/vuls-data-update#853 head (3263815) for the
FortinetCSAF source id and the fortinet-* cpecriterion RangeTypes; keeps the
pan-os RangeType from the Palo Alto work.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…op untested bare Fortinet

- Add a CVRF-only postConvert case: a bare vulnerability stub yields a sparse
  Fortinet CveContent (no CVSS) plus a DistroAdvisory, source link to the PSIRT
  advisory (root id), DistroAdvisory.Severity empty (advisory CVSS, not vendor).
- Add a csaf+cvrf both-detect case: the same FG-IR advisory / CVE is detected by
  both sources and merges to the csaf-preferred result — csaf scalars (title /
  summary / CVSS) win, the DistroAdvisory takes csaf's (later Updated), and
  vuls2-sources lists both. Verifies the compareSourceID CSAF > CVRF ordering.
- Drop the bare sourceTypes.Fortinet from the source switches: only
  FortinetCVRF / FortinetCSAF are produced into the vuls2 DB and exercised by
  tests; the legacy "fortinet" source id is no longer routed.

Built the real extracted data (vuls-data-update extract fortinet-cvrf/csaf from
the GHCR raw) to model these on FG-IR-25-032 / CVE-2025-54838.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@shino shino force-pushed the shino/cpe-detect-fortinet branch from d042419 to ac7ef71 Compare June 30, 2026 06:25
shino and others added 2 commits June 30, 2026 15:46
…veContents

Confirm that a CVE detected by both NVD (feed v2) and Fortinet CSAF retains both
CveContents in the merged result: models.Nvd and models.Fortinet are distinct
CveContentTypes, so mergeVulnInfo keys them separately and neither overwrites the
other (and both confidences are kept). Distinct from the csaf+cvrf case, where
the two contents share models.Fortinet and merge into one.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…saf versions

MaineK00n/vuls2#396 ("bump vuls-data-update to nightly (fortinet csaf extractor)")
is merged, so drop the PR-head pins and use the released nightly commits:
- github.com/MaineK00n/vuls2        -> v0.0.1-alpha.0.20260630120316-69ab575c2ebe
- github.com/MaineK00n/vuls-data-update -> v0.0.0-20260630113343-f3c7f13ad814
  (pulls in github.com/vulsio/go-fortinet-version, the fortinet-* RangeType backend)

This replaces the temporary vuls-data-update#853-head pin now that the Fortinet
CSAF extractor and range types have landed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
shino added a commit to vulsio/vuls-data-db that referenced this pull request Jun 30, 2026
* ci(db): add fortinet-cvrf + fortinet-csaf (nightly active, main commented)

Add the two new Fortinet extractors to the DB build, both rooted on the FG-IR
PSIRT advisory:
- db-nightly.mk: enable fortinet-cvrf and fortinet-csaf so the nightly DB carries
  Fortinet detection criteria.
- db-main.mk: add them commented out — keep the production main (:0/:latest) DB
  unchanged until vuls is ready to consume them.

Required for vuls's Fortinet CPE detection once it routes Fortinet through vuls2
(future-architect/vuls#2591).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* ci: wire fortinet-cvrf + fortinet-csaf through extract/backup/gc/restore/truncate

The db-add lines alone are not enough — a new extracted source must also be
listed in the surrounding CI workflows (mirrors the paloalto wiring in #182).
The raw side (fetch-fortinet-csaf/cvrf, raw backup/gc/archive/restore) was
already wired, so this only adds the extract/extracted side:

- extract-all.yml / extract-main.yml: uncomment fortinet-csaf and fortinet-cvrf
  (fortinet-handmade stays commented).
- backup-daily/monthly/weekly.yml, gc.yml, restore-all.yml, truncate.yml: add
  vuls-data-extracted-fortinet-csaf / -cvrf to the extracted matrices.
- gc-extracted.yml: add the two extracted tags with the standard pack settings.
- truncate-extracted.yml: add the bare fortinet-csaf / fortinet-cvrf names.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* ci(archive-raw): add missing "fortinet" raw source

archive-raw.yml listed fortinet-csaf and fortinet-cvrf but not the bare
"fortinet" raw source, which every other raw workflow (archive, gc, gc-raw,
backup-*, restore-all) already includes. Add it for consistency.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* ci(db): order fortinet-csaf before fortinet-cvrf for consistency

Match the csaf-then-cvrf ordering used by the workflow / backup / gc / truncate
lists in this PR (and alphabetical), so the db-add lists stay easy to diff and
keep in sync.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@shino shino marked this pull request as ready for review June 30, 2026 12:07
@shino shino requested a review from Copilot June 30, 2026 12:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Routes Fortinet CPE-based detection away from go-cve-dictionary and into the vuls2 detection pipeline, aligning Fortinet with the prior Cisco/Palo Alto migrations and enabling Fortinet CVRF/CSAF-backed CPE criteria and source-linking.

Changes:

  • Stop admitting/filling Fortinet data via go-cve-dictionary (detection gate + Fortinet CveContent fill removed; Fortinet details nilled before confidence selection).
  • Add/extend vuls2 vendor/source mappings for Fortinet CVRF + CSAF (content type/confidence mapping, source preference ordering, PSIRT advisory source links).
  • Add vuls2 postConvert test coverage for Fortinet CSAF/CVRF behavior and merging semantics; bump vuls-data-update/vuls2 deps accordingly.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
go.mod Bumps vuls-data-update/vuls2 and adds an indirect Fortinet version lib dependency.
go.sum Updates checksums for the dependency bumps and new indirect dependency.
detector/detector.go Removes Fortinet go-cve-dictionary fill and Fortinet-only admission; nils Fortinet details before confidence selection.
detector/vuls2/vendor.go Adds Fortinet CVRF/CSAF source handling (reference/source link, confidence mapping, source preference).
detector/vuls2/vuls2_test.go Adds Fortinet-focused postConvert tests for CSAF/CVRF detection and merge behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread go.mod

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 5 changed files in this pull request and generated 1 comment.

Comment thread go.mod

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 5 changed files in this pull request and generated no new comments.

@shino shino requested a review from MaineK00n June 30, 2026 12:28

@MaineK00n MaineK00n left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🥦

@MaineK00n MaineK00n merged commit 1297680 into master Jun 30, 2026
11 checks passed
@MaineK00n MaineK00n deleted the shino/cpe-detect-fortinet branch June 30, 2026 12:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants