<?xml version='1.0' encoding='UTF-8'?>
<?xml-stylesheet href="/static/style.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <id>https://vulnerability.circl.lu/sightings/feed</id>
  <title>Most recent sightings.</title>
  <updated>2026-07-04T09:01:48.681150+00:00</updated>
  <author>
    <name>Vulnerability-Lookup</name>
    <email>info@circl.lu</email>
  </author>
  <link href="https://vulnerability.circl.lu" rel="alternate"/>
  <generator uri="https://lkiesow.github.io/python-feedgen" version="1.0.0">python-feedgen</generator>
  <subtitle>Contains only the most 10 recent sightings.</subtitle>
  <entry>
    <id>https://vulnerability.circl.lu/sighting/62124af3-a033-4200-881c-4f6249e29e8d/export</id>
    <title>62124af3-a033-4200-881c-4f6249e29e8d</title>
    <updated>2026-07-04T09:01:48.693193+00:00</updated>
    <author>
      <name>Automation user</name>
      <uri>https://cvepremium.circl.lu/user/automation</uri>
    </author>
    <content>{"uuid": "62124af3-a033-4200-881c-4f6249e29e8d", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-0000-0000", "type": "seen", "source": "https://gist.github.com/tu-trinh-scale/d7f43279e0916c2dae67dd88be0af0cb", "content": "diff --git a/README.md b/README.md\nindex 57102d1..6b1b437 100644\n--- a/README.md\n+++ b/README.md\n@@ -90,8 +90,14 @@ Vuls is a tool created to solve the problems listed above. It has the following\n   - [US-CERT](https://www.us-cert.gov/ncas/alerts)\n   - [JPCERT](http://www.jpcert.or.jp/at/2019.html)\n \n-- CISA(Cybersecurity &amp;amp; Infrastructure Security Agency)\n-  - [Known Exploited Vulnerabilities Catalog](https://www.cisa.gov/known-exploited-vulnerabilities-catalog)\n+- Known Exploited Vulnerabilities\n+  - [CISA Known Exploited Vulnerabilities Catalog](https://www.cisa.gov/known-exploited-vulnerabilities-catalog)\n+  - VulnCheck KEV data via go-kev\n+\n+Example configuration and output:\n+\n+- `config.toml.example` shows a synthetic, ready-to-use KEV-enabled setup using placeholder values.\n+- `docs/sample-kev-scan-result.json` shows the `kevs` field populated with synthetic CISA and VulnCheck entries.\n \n - Cyber Threat Intelligence(MITRE ATT&amp;amp;CK and CAPEC)\n   - [mitre/cti](https://github.com/mitre/cti)\ndiff --git a/config.toml.example b/config.toml.example\nnew file mode 100644\nindex 0000000..9abfc3d\n--- /dev/null\n+++ b/config.toml.example\n@@ -0,0 +1,42 @@\n+# Synthetic example configuration for enabling KEV reporting.\n+# Do not paste production credentials into this file.\n+\n+[cveDict]\n+type = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/cve.sqlite3\"\n+\n+[ovalDict]\n+type = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/oval.sqlite3\"\n+\n+[gost]\n+type = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/gost.sqlite3\"\n+\n+[exploit]\n+type = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/go-exploitdb.sqlite3\"\n+\n+[metasploit]\n+type = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/go-msfdb.sqlite3\"\n+\n+[kevuln]\n+type = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/go-kev.sqlite3\"\n+\n+[cti]\n+type = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/go-cti.sqlite3\"\n+\n+[default]\n+port = \"22\"\n+user = \"vuls\"\n+scanMode = [\"fast-root\"]\n+scanModules = [\"ospkg\", \"port\"]\n+\n+[servers]\n+\n+[servers.example-host]\n+host = \"192.0.2.10\"\n+memo = \"Synthetic example target\"\ndiff --git a/detector/kevuln.go b/detector/kevuln.go\nindex 41afdfe..f2f2f6b 100644\n--- a/detector/kevuln.go\n+++ b/detector/kevuln.go\n@@ -6,6 +6,7 @@ package detector\n import (\n \t\"encoding/json\"\n \t\"net/http\"\n+\t\"strings\"\n \t\"time\"\n \n \t\"github.com/cenkalti/backoff\"\n@@ -79,18 +80,14 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging\n \t\t\t\treturn err\n \t\t\t}\n \n-\t\t\talerts := []models.Alert{}\n-\t\t\tif len(kevulns) &amp;gt; 0 {\n-\t\t\t\talerts = append(alerts, models.Alert{\n-\t\t\t\t\tTitle: \"Known Exploited Vulnerabilities Catalog\",\n-\t\t\t\t\tURL:   \"https://www.cisa.gov/known-exploited-vulnerabilities-catalog\",\n-\t\t\t\t\tTeam:  \"cisa\",\n-\t\t\t\t})\n+\t\t\tkevs, err := convertKEVulns(kevulns)\n+\t\t\tif err != nil {\n+\t\t\t\treturn err\n \t\t\t}\n \n \t\t\tv, ok := r.ScannedCves[res.request.cveID]\n-\t\t\tif ok {\n-\t\t\t\tv.AlertDict.CISA = alerts\n+\t\t\tif ok &amp;amp;&amp;amp; len(kevs) &amp;gt; 0 {\n+\t\t\t\tv.KEVs = kevs\n \t\t\t\tnKEV++\n \t\t\t}\n \t\t\tr.ScannedCves[res.request.cveID] = v\n@@ -108,17 +105,15 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging\n \t\t\t\tcontinue\n \t\t\t}\n \n-\t\t\talerts := []models.Alert{}\n-\t\t\tif len(kevulns) &amp;gt; 0 {\n-\t\t\t\talerts = append(alerts, models.Alert{\n-\t\t\t\t\tTitle: \"Known Exploited Vulnerabilities Catalog\",\n-\t\t\t\t\tURL:   \"https://www.cisa.gov/known-exploited-vulnerabilities-catalog\",\n-\t\t\t\t\tTeam:  \"cisa\",\n-\t\t\t\t})\n+\t\t\tkevs, err := convertKEVulns(kevulns)\n+\t\t\tif err != nil {\n+\t\t\t\treturn err\n \t\t\t}\n \n-\t\t\tvuln.AlertDict.CISA = alerts\n-\t\t\tnKEV++\n+\t\t\tif len(kevs) &amp;gt; 0 {\n+\t\t\t\tvuln.KEVs = kevs\n+\t\t\t\tnKEV++\n+\t\t\t}\n \t\t\tr.ScannedCves[cveID] = vuln\n \t\t}\n \t}\n@@ -127,6 +122,222 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging\n \treturn nil\n }\n \n+func convertKEVulns(kevulns []kevulnmodels.KEVuln) ([]models.KEV, error) {\n+\tkevs := []models.KEV{}\n+\tfor _, kevuln := range kevulns {\n+\t\tb, err := json.Marshal(kevuln)\n+\t\tif err != nil {\n+\t\t\treturn nil, xerrors.Errorf(\"Failed to marshal KEVuln. err: %w\", err)\n+\t\t}\n+\n+\t\tvar m map[string]json.RawMessage\n+\t\tif err := json.Unmarshal(b, &amp;amp;m); err != nil {\n+\t\t\treturn nil, xerrors.Errorf(\"Failed to unmarshal KEVuln. err: %w\", err)\n+\t\t}\n+\n+\t\tif cisa, ok, err := parseCISAKEV(rawFor(m, \"cisa\", \"CISA\", \"cisaKEV\", \"CISAKEV\")); err != nil {\n+\t\t\treturn nil, err\n+\t\t} else if ok {\n+\t\t\tkevs = append(kevs, cisa)\n+\t\t}\n+\n+\t\tif vulncheck, ok, err := parseVulnCheckKEV(rawFor(m, \"vulncheck\", \"vulnCheck\", \"VulnCheck\", \"vulnCheckKEV\", \"VulnCheckKEV\")); err != nil {\n+\t\t\treturn nil, err\n+\t\t} else if ok {\n+\t\t\tkevs = append(kevs, vulncheck)\n+\t\t}\n+\t}\n+\treturn kevs, nil\n+}\n+\n+func parseCISAKEV(raw json.RawMessage) (models.KEV, bool, error) {\n+\tif len(raw) == 0 || string(raw) == \"null\" {\n+\t\treturn models.KEV{}, false, nil\n+\t}\n+\tvar m map[string]json.RawMessage\n+\tif err := json.Unmarshal(raw, &amp;amp;m); err != nil {\n+\t\treturn models.KEV{}, false, xerrors.Errorf(\"Failed to unmarshal CISA KEV. err: %w\", err)\n+\t}\n+\tif isEmptyRawMap(m) {\n+\t\treturn models.KEV{}, false, nil\n+\t}\n+\n+\tdueDate, err := rawTimePtr(rawFor(m, \"dueDate\", \"DueDate\", \"due_date\"))\n+\tif err != nil {\n+\t\treturn models.KEV{}, false, err\n+\t}\n+\tdateAdded, err := rawTime(rawFor(m, \"dateAdded\", \"DateAdded\", \"date_added\"))\n+\tif err != nil {\n+\t\treturn models.KEV{}, false, err\n+\t}\n+\n+\treturn models.KEV{\n+\t\tType:                       models.CISAKEVType,\n+\t\tVendorProject:              rawString(rawFor(m, \"vendorProject\", \"VendorProject\", \"vendor_project\")),\n+\t\tProduct:                    rawString(rawFor(m, \"product\", \"Product\")),\n+\t\tVulnerabilityName:          rawString(rawFor(m, \"vulnerabilityName\", \"VulnerabilityName\", \"vulnerability_name\")),\n+\t\tShortDescription:           rawString(rawFor(m, \"shortDescription\", \"ShortDescription\", \"short_description\")),\n+\t\tRequiredAction:             rawString(rawFor(m, \"requiredAction\", \"RequiredAction\", \"required_action\")),\n+\t\tKnownRansomwareCampaignUse: rawString(rawFor(m, \"knownRansomwareCampaignUse\", \"KnownRansomwareCampaignUse\", \"known_ransomware_campaign_use\")),\n+\t\tDateAdded:                  dateAdded,\n+\t\tDueDate:                    dueDate,\n+\t\tCISA: &amp;amp;models.CISAKEV{\n+\t\t\tNote: rawString(rawFor(m, \"note\", \"Note\", \"notes\")),\n+\t\t},\n+\t}, true, nil\n+}\n+\n+func parseVulnCheckKEV(raw json.RawMessage) (models.KEV, bool, error) {\n+\tif len(raw) == 0 || string(raw) == \"null\" {\n+\t\treturn models.KEV{}, false, nil\n+\t}\n+\tvar m map[string]json.RawMessage\n+\tif err := json.Unmarshal(raw, &amp;amp;m); err != nil {\n+\t\treturn models.KEV{}, false, xerrors.Errorf(\"Failed to unmarshal VulnCheck KEV. err: %w\", err)\n+\t}\n+\tif isEmptyRawMap(m) {\n+\t\treturn models.KEV{}, false, nil\n+\t}\n+\n+\tdueDate, err := rawTimePtr(rawFor(m, \"dueDate\", \"DueDate\", \"due_date\"))\n+\tif err != nil {\n+\t\treturn models.KEV{}, false, err\n+\t}\n+\tdateAdded, err := rawTime(rawFor(m, \"dateAdded\", \"DateAdded\", \"date_added\"))\n+\tif err != nil {\n+\t\treturn models.KEV{}, false, err\n+\t}\n+\txdb, err := parseVulnCheckXDB(rawFor(m, \"xdb\", \"XDB\"))\n+\tif err != nil {\n+\t\treturn models.KEV{}, false, err\n+\t}\n+\treportedExploitation, err := parseVulnCheckReportedExploitation(rawFor(m, \"reportedExploitation\", \"ReportedExploitation\", \"reported_exploitation\"))\n+\tif err != nil {\n+\t\treturn models.KEV{}, false, err\n+\t}\n+\n+\treturn models.KEV{\n+\t\tType:                       models.VulnCheckKEVType,\n+\t\tVendorProject:              rawString(rawFor(m, \"vendorProject\", \"VendorProject\", \"vendor_project\")),\n+\t\tProduct:                    rawString(rawFor(m, \"product\", \"Product\")),\n+\t\tVulnerabilityName:          rawString(rawFor(m, \"vulnerabilityName\", \"VulnerabilityName\", \"vulnerability_name\")),\n+\t\tShortDescription:           rawString(rawFor(m, \"shortDescription\", \"ShortDescription\", \"short_description\")),\n+\t\tRequiredAction:             rawString(rawFor(m, \"requiredAction\", \"RequiredAction\", \"required_action\")),\n+\t\tKnownRansomwareCampaignUse: rawString(rawFor(m, \"knownRansomwareCampaignUse\", \"KnownRansomwareCampaignUse\", \"known_ransomware_campaign_use\")),\n+\t\tDateAdded:                  dateAdded,\n+\t\tDueDate:                    dueDate,\n+\t\tVulnCheck: &amp;amp;models.VulnCheckKEV{\n+\t\t\tXDB:                  xdb,\n+\t\t\tReportedExploitation: reportedExploitation,\n+\t\t},\n+\t}, true, nil\n+}\n+\n+func parseVulnCheckXDB(raw json.RawMessage) ([]models.VulnCheckXDB, error) {\n+\tif len(raw) == 0 || string(raw) == \"null\" {\n+\t\treturn nil, nil\n+\t}\n+\tvar xs []map[string]json.RawMessage\n+\tif err := json.Unmarshal(raw, &amp;amp;xs); err != nil {\n+\t\treturn nil, xerrors.Errorf(\"Failed to unmarshal VulnCheck XDB. err: %w\", err)\n+\t}\n+\n+\txdbs := []models.VulnCheckXDB{}\n+\tfor _, x := range xs {\n+\t\tdateAdded, err := rawTime(rawFor(x, \"dateAdded\", \"DateAdded\", \"date_added\"))\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n+\t\t}\n+\t\txdbs = append(xdbs, models.VulnCheckXDB{\n+\t\t\tXDBID:       rawString(rawFor(x, \"xdbID\", \"XDBID\", \"xdb_id\")),\n+\t\t\tXDBURL:      rawString(rawFor(x, \"xdbURL\", \"XDBURL\", \"xdb_url\")),\n+\t\t\tDateAdded:   dateAdded,\n+\t\t\tExploitType: rawString(rawFor(x, \"exploitType\", \"ExploitType\", \"exploit_type\")),\n+\t\t\tCloneSSHURL: rawString(rawFor(x, \"cloneSSHURL\", \"CloneSSHURL\", \"clone_ssh_url\")),\n+\t\t})\n+\t}\n+\treturn xdbs, nil\n+}\n+\n+func parseVulnCheckReportedExploitation(raw json.RawMessage) ([]models.VulnCheckReportedExploitation, error) {\n+\tif len(raw) == 0 || string(raw) == \"null\" {\n+\t\treturn nil, nil\n+\t}\n+\tvar rs []map[string]json.RawMessage\n+\tif err := json.Unmarshal(raw, &amp;amp;rs); err != nil {\n+\t\treturn nil, xerrors.Errorf(\"Failed to unmarshal VulnCheck reported exploitation. err: %w\", err)\n+\t}\n+\n+\treports := []models.VulnCheckReportedExploitation{}\n+\tfor _, r := range rs {\n+\t\tdateAdded, err := rawTime(rawFor(r, \"dateAdded\", \"DateAdded\", \"date_added\"))\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n+\t\t}\n+\t\treports = append(reports, models.VulnCheckReportedExploitation{\n+\t\t\tURL:       rawString(rawFor(r, \"url\", \"URL\")),\n+\t\t\tDateAdded: dateAdded,\n+\t\t})\n+\t}\n+\treturn reports, nil\n+}\n+\n+func rawFor(m map[string]json.RawMessage, keys ...string) json.RawMessage {\n+\tfor _, key := range keys {\n+\t\tif raw, ok := m[key]; ok {\n+\t\t\treturn raw\n+\t\t}\n+\t}\n+\treturn nil\n+}\n+\n+func rawString(raw json.RawMessage) string {\n+\tif len(raw) == 0 || string(raw) == \"null\" {\n+\t\treturn \"\"\n+\t}\n+\tvar s string\n+\tif err := json.Unmarshal(raw, &amp;amp;s); err == nil {\n+\t\treturn s\n+\t}\n+\treturn strings.Trim(string(raw), `\"`)\n+}\n+\n+func rawTime(raw json.RawMessage) (time.Time, error) {\n+\tif len(raw) == 0 || string(raw) == \"null\" {\n+\t\treturn time.Time{}, nil\n+\t}\n+\ts := rawString(raw)\n+\tif s == \"\" || s == \"0001-01-01T00:00:00Z\" {\n+\t\treturn time.Time{}, nil\n+\t}\n+\tfor _, layout := range []string{time.RFC3339Nano, time.RFC3339, \"2006-01-02\"} {\n+\t\tif t, err := time.Parse(layout, s); err == nil {\n+\t\t\treturn t, nil\n+\t\t}\n+\t}\n+\treturn time.Time{}, xerrors.Errorf(\"Failed to parse KEV date: %s\", s)\n+}\n+\n+func rawTimePtr(raw json.RawMessage) (*time.Time, error) {\n+\tt, err := rawTime(raw)\n+\tif err != nil || t.IsZero() {\n+\t\treturn nil, err\n+\t}\n+\treturn &amp;amp;t, nil\n+}\n+\n+func isEmptyRawMap(m map[string]json.RawMessage) bool {\n+\tif len(m) == 0 {\n+\t\treturn true\n+\t}\n+\tfor _, raw := range m {\n+\t\tif len(raw) != 0 &amp;amp;&amp;amp; string(raw) != \"null\" &amp;amp;&amp;amp; string(raw) != `\"\"` &amp;amp;&amp;amp; string(raw) != \"[]\" &amp;amp;&amp;amp; string(raw) != \"{}\" {\n+\t\t\treturn false\n+\t\t}\n+\t}\n+\treturn true\n+}\n+\n type kevulnResponse struct {\n \trequest kevulnRequest\n \tjson    string\ndiff --git a/docs/sample-kev-scan-result.json b/docs/sample-kev-scan-result.json\nnew file mode 100644\nindex 0000000..83a0c71\n--- /dev/null\n+++ b/docs/sample-kev-scan-result.json\n@@ -0,0 +1,60 @@\n+{\n+  \"jsonVersion\": 4,\n+  \"serverName\": \"example-host\",\n+  \"family\": \"ubuntu\",\n+  \"release\": \"22.04\",\n+  \"scannedCves\": {\n+    \"CVE-0000-0000\": {\n+      \"cveID\": \"CVE-0000-0000\",\n+      \"kevs\": [\n+        {\n+          \"type\": \"cisa\",\n+          \"vendorProject\": \"Example Vendor\",\n+          \"product\": \"Example Product\",\n+          \"vulnerabilityName\": \"Example Product Placeholder Vulnerability\",\n+          \"shortDescription\": \"Synthetic CISA KEV example for documentation.\",\n+          \"requiredAction\": \"Apply vendor-provided updates or mitigations.\",\n+          \"knownRansomwareCampaignUse\": \"Unknown\",\n+          \"dateAdded\": \"2026-01-02T00:00:00Z\",\n+          \"dueDate\": \"2026-02-01T00:00:00Z\",\n+          \"cisa\": {\n+            \"note\": \"Synthetic sample; not real vulnerability intelligence.\"\n+          }\n+        },\n+        {\n+          \"type\": \"vulncheck\",\n+          \"vendorProject\": \"Example Vendor\",\n+          \"product\": \"Example Product\",\n+          \"vulnerabilityName\": \"Example Product Placeholder Vulnerability\",\n+          \"shortDescription\": \"Synthetic VulnCheck KEV example for documentation.\",\n+          \"requiredAction\": \"Review exploitation evidence and remediate affected assets.\",\n+          \"knownRansomwareCampaignUse\": \"Unknown\",\n+          \"dateAdded\": \"2026-01-03T00:00:00Z\",\n+          \"vulncheck\": {\n+            \"xdb\": [\n+              {\n+                \"xdbID\": \"XDB-000000\",\n+                \"xdbURL\": \"https://example.com/xdb/XDB-000000\",\n+                \"dateAdded\": \"2026-01-03T00:00:00Z\",\n+                \"exploitType\": \"remote\",\n+                \"cloneSSHURL\": \"git@example.com:example/xdb-000000.git\"\n+              }\n+            ],\n+            \"reportedExploitation\": [\n+              {\n+                \"url\": \"https://example.com/reports/CVE-0000-0000\",\n+                \"dateAdded\": \"2026-01-04T00:00:00Z\"\n+              }\n+            ]\n+          }\n+        }\n+      ],\n+      \"affectedPackages\": [\n+        {\n+          \"name\": \"example-package\",\n+          \"fixedIn\": \"1.2.3\"\n+        }\n+      ]\n+    }\n+  }\n+}\ndiff --git a/models/scanresults.go b/models/scanresults.go\nindex 508b992..33c32c7 100644\n--- a/models/scanresults.go\n+++ b/models/scanresults.go\n@@ -197,13 +197,14 @@ func (r ScanResult) FormatTextReportHeader() string {\n \t\tpkgs = fmt.Sprintf(\"%s, %d libs\", pkgs, r.LibraryScanners.Total())\n \t}\n \n-\treturn fmt.Sprintf(\"%s\\n%s\\n%s\\n%s, %s, %s, %s\\n%s\\n\",\n+\treturn fmt.Sprintf(\"%s\\n%s\\n%s\\n%s, %s, %s, %s, %s\\n%s\\n\",\n \t\tr.ServerInfo(),\n \t\tbuf.String(),\n \t\tr.ScannedCves.FormatCveSummary(),\n \t\tr.ScannedCves.FormatFixedStatus(r.Packages),\n \t\tr.FormatExploitCveSummary(),\n \t\tr.FormatMetasploitCveSummary(),\n+\t\tr.FormatKEVCveSummary(),\n \t\tr.FormatAlertSummary(),\n \t\tpkgs)\n }\n@@ -251,15 +252,22 @@ func (r ScanResult) FormatMetasploitCveSummary() string {\n \treturn fmt.Sprintf(\"%d exploits\", nMetasploitCve)\n }\n \n+// FormatKEVCveSummary returns a summary of CVEs with KEV entries.\n+func (r ScanResult) FormatKEVCveSummary() string {\n+\tnKEVCve := 0\n+\tfor _, vuln := range r.ScannedCves {\n+\t\tif 0 &amp;lt; len(vuln.KEVs) {\n+\t\t\tnKEVCve++\n+\t\t}\n+\t}\n+\treturn fmt.Sprintf(\"%d kevs\", nKEVCve)\n+}\n+\n // FormatAlertSummary returns a summary of CERT alerts\n func (r ScanResult) FormatAlertSummary() string {\n-\tcisaCnt := 0\n \tuscertCnt := 0\n \tjpcertCnt := 0\n \tfor _, vuln := range r.ScannedCves {\n-\t\tif len(vuln.AlertDict.CISA) &amp;gt; 0 {\n-\t\t\tcisaCnt += len(vuln.AlertDict.CISA)\n-\t\t}\n \t\tif len(vuln.AlertDict.USCERT) &amp;gt; 0 {\n \t\t\tuscertCnt += len(vuln.AlertDict.USCERT)\n \t\t}\n@@ -267,7 +275,7 @@ func (r ScanResult) FormatAlertSummary() string {\n \t\t\tjpcertCnt += len(vuln.AlertDict.JPCERT)\n \t\t}\n \t}\n-\treturn fmt.Sprintf(\"cisa: %d, uscert: %d, jpcert: %d alerts\", cisaCnt, uscertCnt, jpcertCnt)\n+\treturn fmt.Sprintf(\"uscert: %d, jpcert: %d alerts\", uscertCnt, jpcertCnt)\n }\n \n func (r ScanResult) isDisplayUpdatableNum(mode config.ScanMode) bool {\n@@ -422,6 +430,12 @@ func (r *ScanResult) SortForJSONOutput() {\n \t\tsort.Slice(v.Metasploits, func(i, j int) bool {\n \t\t\treturn v.Metasploits[i].Name &amp;lt; v.Metasploits[j].Name\n \t\t})\n+\t\tsort.Slice(v.KEVs, func(i, j int) bool {\n+\t\t\tif v.KEVs[i].Type != v.KEVs[j].Type {\n+\t\t\t\treturn v.KEVs[i].Type &amp;lt; v.KEVs[j].Type\n+\t\t\t}\n+\t\t\treturn v.KEVs[i].VulnerabilityName &amp;lt; v.KEVs[j].VulnerabilityName\n+\t\t})\n \t\tsort.Slice(v.Mitigations, func(i, j int) bool {\n \t\t\treturn v.Mitigations[i].URL &amp;lt; v.Mitigations[j].URL\n \t\t})\n@@ -434,9 +448,6 @@ func (r *ScanResult) SortForJSONOutput() {\n \t\tsort.Slice(v.AlertDict.JPCERT, func(i, j int) bool {\n \t\t\treturn v.AlertDict.JPCERT[i].Title &amp;lt; v.AlertDict.JPCERT[j].Title\n \t\t})\n-\t\tsort.Slice(v.AlertDict.CISA, func(i, j int) bool {\n-\t\t\treturn v.AlertDict.CISA[i].Title &amp;lt; v.AlertDict.CISA[j].Title\n-\t\t})\n \t\tr.ScannedCves[k] = v\n \t}\n }\ndiff --git a/models/vulninfos.go b/models/vulninfos.go\nindex 3e85e81..8a6e0dc 100644\n--- a/models/vulninfos.go\n+++ b/models/vulninfos.go\n@@ -265,6 +265,7 @@ type VulnInfo struct {\n \tCveContents          CveContents          `json:\"cveContents,omitempty\"`\n \tExploits             []Exploit            `json:\"exploits,omitempty\"`\n \tMetasploits          []Metasploit         `json:\"metasploits,omitempty\"`\n+\tKEVs                 []KEV                `json:\"kevs,omitempty\"`\n \tMitigations          []Mitigation         `json:\"mitigations,omitempty\"`\n \tCtis                 []string             `json:\"ctis,omitempty\"`\n \tAlertDict            AlertDict            `json:\"alertDict,omitempty\"`\n@@ -284,6 +285,57 @@ type Alert struct {\n \tTeam  string `json:\"team,omitempty\"`\n }\n \n+// KEVType identifies the source of known exploited vulnerability data.\n+type KEVType string\n+\n+const (\n+\t// CISAKEVType represents CISA Known Exploited Vulnerabilities Catalog data.\n+\tCISAKEVType KEVType = \"cisa\"\n+\t// VulnCheckKEVType represents VulnCheck KEV data.\n+\tVulnCheckKEVType KEVType = \"vulncheck\"\n+)\n+\n+// KEV has known exploited vulnerability information.\n+type KEV struct {\n+\tType                       KEVType       `json:\"type,omitempty\"`\n+\tVendorProject              string        `json:\"vendorProject,omitempty\"`\n+\tProduct                    string        `json:\"product,omitempty\"`\n+\tVulnerabilityName          string        `json:\"vulnerabilityName,omitempty\"`\n+\tShortDescription           string        `json:\"shortDescription,omitempty\"`\n+\tRequiredAction             string        `json:\"requiredAction,omitempty\"`\n+\tKnownRansomwareCampaignUse string        `json:\"knownRansomwareCampaignUse,omitempty\"`\n+\tDateAdded                  time.Time     `json:\"dateAdded,omitempty\"`\n+\tDueDate                    *time.Time    `json:\"dueDate,omitempty\"`\n+\tCISA                       *CISAKEV      `json:\"cisa,omitempty\"`\n+\tVulnCheck                  *VulnCheckKEV `json:\"vulncheck,omitempty\"`\n+}\n+\n+// CISAKEV has CISA-specific KEV fields.\n+type CISAKEV struct {\n+\tNote string `json:\"note,omitempty\"`\n+}\n+\n+// VulnCheckKEV has VulnCheck-specific KEV fields.\n+type VulnCheckKEV struct {\n+\tXDB                  []VulnCheckXDB                  `json:\"xdb,omitempty\"`\n+\tReportedExploitation []VulnCheckReportedExploitation `json:\"reportedExploitation,omitempty\"`\n+}\n+\n+// VulnCheckXDB has VulnCheck exploit database metadata.\n+type VulnCheckXDB struct {\n+\tXDBID       string    `json:\"xdbID,omitempty\"`\n+\tXDBURL      string    `json:\"xdbURL,omitempty\"`\n+\tDateAdded   time.Time `json:\"dateAdded,omitempty\"`\n+\tExploitType string    `json:\"exploitType,omitempty\"`\n+\tCloneSSHURL string    `json:\"cloneSSHURL,omitempty\"`\n+}\n+\n+// VulnCheckReportedExploitation has VulnCheck reported exploitation metadata.\n+type VulnCheckReportedExploitation struct {\n+\tURL       string    `json:\"url,omitempty\"`\n+\tDateAdded time.Time `json:\"dateAdded,omitempty\"`\n+}\n+\n // GitHubSecurityAlerts is a list of GitHubSecurityAlert\n type GitHubSecurityAlerts []GitHubSecurityAlert\n \n@@ -910,24 +962,21 @@ type Mitigation struct {\n \tURL            string         `json:\"url,omitempty\"`\n }\n \n-// AlertDict has target cve JPCERT, USCERT and CISA alert data\n+// AlertDict has target cve JPCERT and USCERT alert data.\n type AlertDict struct {\n-\tCISA   []Alert `json:\"cisa\"`\n+\tCISA   []Alert `json:\"-\"`\n \tJPCERT []Alert `json:\"jpcert\"`\n \tUSCERT []Alert `json:\"uscert\"`\n }\n \n // IsEmpty checks if the content of AlertDict is empty\n func (a AlertDict) IsEmpty() bool {\n-\treturn len(a.CISA) == 0 &amp;amp;&amp;amp; len(a.JPCERT) == 0 &amp;amp;&amp;amp; len(a.USCERT) == 0\n+\treturn len(a.JPCERT) == 0 &amp;amp;&amp;amp; len(a.USCERT) == 0\n }\n \n // FormatSource returns which source has this alert\n func (a AlertDict) FormatSource() string {\n \tvar s []string\n-\tif len(a.CISA) != 0 {\n-\t\ts = append(s, \"CISA\")\n-\t}\n \tif len(a.USCERT) != 0 || len(a.JPCERT) != 0 {\n \t\ts = append(s, \"CERT\")\n \t}\ndiff --git a/reporter/util.go b/reporter/util.go\nindex d9cfdaa..a2dfbd0 100644\n--- a/reporter/util.go\n+++ b/reporter/util.go\n@@ -204,6 +204,7 @@ func formatOneLineSummary(rs ...models.ScanResult) string {\n \t\t\t\tr.FormatUpdatablePkgsSummary(),\n \t\t\t\tr.FormatExploitCveSummary(),\n \t\t\t\tr.FormatMetasploitCveSummary(),\n+\t\t\t\tr.FormatKEVCveSummary(),\n \t\t\t\tr.FormatAlertSummary(),\n \t\t\t}\n \t\t} else {\n@@ -565,10 +566,6 @@ No CVE-IDs are found in updatable packages.\n \t\t})\n \t\tdata = append(data, ds...)\n \n-\t\tfor _, alert := range vuln.AlertDict.CISA {\n-\t\t\tdata = append(data, []string{\"CISA Alert\", alert.URL})\n-\t\t}\n-\n \t\tfor _, alert := range vuln.AlertDict.JPCERT {\n \t\t\tdata = append(data, []string{\"JPCERT Alert\", alert.URL})\n \t\t}\ndiff --git a/tui/tui.go b/tui/tui.go\nindex 4407f56..80afe95 100644\n--- a/tui/tui.go\n+++ b/tui/tui.go\n@@ -812,16 +812,6 @@ func setChangelogLayout(g *gocui.Gui) error {\n \t\t\t}\n \t\t}\n \n-\t\tif len(vinfo.AlertDict.CISA) &amp;gt; 0 {\n-\t\t\tlines = append(lines, \"\\n\",\n-\t\t\t\t\"CISA Alert\",\n-\t\t\t\t\"===========\",\n-\t\t\t)\n-\t\t\tfor _, alert := range vinfo.AlertDict.CISA {\n-\t\t\t\tlines = append(lines, fmt.Sprintf(\"* [%s](%s)\", alert.Title, alert.URL))\n-\t\t\t}\n-\t\t}\n-\n \t\tif len(vinfo.AlertDict.USCERT) &amp;gt; 0 {\n \t\t\tlines = append(lines, \"\\n\",\n \t\t\t\t\"USCERT Alert\",\n", "creation_timestamp": "2026-07-04T00:01:07.916595Z"}</content>
    <link href="https://vulnerability.circl.lu/sighting/62124af3-a033-4200-881c-4f6249e29e8d/export"/>
    <published>2026-07-04T00:01:07.916595+00:00</published>
  </entry>
  <entry>
    <id>https://vulnerability.circl.lu/sighting/f71acbdf-1493-48d8-a9a2-4c17cd5356d2/export</id>
    <title>f71acbdf-1493-48d8-a9a2-4c17cd5356d2</title>
    <updated>2026-07-04T09:01:48.696597+00:00</updated>
    <author>
      <name>Automation user</name>
      <uri>https://cvepremium.circl.lu/user/automation</uri>
    </author>
    <content>{"uuid": "f71acbdf-1493-48d8-a9a2-4c17cd5356d2", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-0000-0000", "type": "seen", "source": "https://gist.github.com/tu-trinh-scale/3aa722cf98ecac7b0e3cdc34b78ae151", "content": "diff --git a/README.md b/README.md\nindex 57102d1..9d75fdf 100644\n--- a/README.md\n+++ b/README.md\n@@ -93,6 +93,9 @@ Vuls is a tool created to solve the problems listed above. It has the following\n - CISA(Cybersecurity &amp;amp; Infrastructure Security Agency)\n   - [Known Exploited Vulnerabilities Catalog](https://www.cisa.gov/known-exploited-vulnerabilities-catalog)\n \n+- VulnCheck\n+  - [VulnCheck KEV](https://vulncheck.com/kev)\n+\n - Cyber Threat Intelligence(MITRE ATT&amp;amp;CK and CAPEC)\n   - [mitre/cti](https://github.com/mitre/cti)\n \n@@ -174,6 +177,13 @@ Vuls has some options to detect the vulnerabilities\n \n ----\n \n+## Examples\n+\n+- `examples/config.toml.example` shows a synthetic, ready-to-edit configuration with KEV reporting enabled through `[kevuln]`.\n+- `examples/kev-scan-result.json` shows the `scannedCves[].kevs` JSON shape for CISA and VulnCheck KEV entries.\n+\n+----\n+\n ## Document\n \n For more information such as Installation, Tutorial, Usage, visit [vuls.io](https://vuls.io/)  \ndiff --git a/detector/kevuln.go b/detector/kevuln.go\nindex 41afdfe..5cc914c 100644\n--- a/detector/kevuln.go\n+++ b/detector/kevuln.go\n@@ -6,6 +6,8 @@ package detector\n import (\n \t\"encoding/json\"\n \t\"net/http\"\n+\t\"reflect\"\n+\t\"strings\"\n \t\"time\"\n \n \t\"github.com/cenkalti/backoff\"\n@@ -79,18 +81,9 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging\n \t\t\t\treturn err\n \t\t\t}\n \n-\t\t\talerts := []models.Alert{}\n-\t\t\tif len(kevulns) &amp;gt; 0 {\n-\t\t\t\talerts = append(alerts, models.Alert{\n-\t\t\t\t\tTitle: \"Known Exploited Vulnerabilities Catalog\",\n-\t\t\t\t\tURL:   \"https://www.cisa.gov/known-exploited-vulnerabilities-catalog\",\n-\t\t\t\t\tTeam:  \"cisa\",\n-\t\t\t\t})\n-\t\t\t}\n-\n \t\t\tv, ok := r.ScannedCves[res.request.cveID]\n-\t\t\tif ok {\n-\t\t\t\tv.AlertDict.CISA = alerts\n+\t\t\tif ok &amp;amp;&amp;amp; len(kevulns) &amp;gt; 0 {\n+\t\t\t\tv.KEVs = kevulnsToModels(kevulns)\n \t\t\t\tnKEV++\n \t\t\t}\n \t\t\tr.ScannedCves[res.request.cveID] = v\n@@ -108,16 +101,7 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging\n \t\t\t\tcontinue\n \t\t\t}\n \n-\t\t\talerts := []models.Alert{}\n-\t\t\tif len(kevulns) &amp;gt; 0 {\n-\t\t\t\talerts = append(alerts, models.Alert{\n-\t\t\t\t\tTitle: \"Known Exploited Vulnerabilities Catalog\",\n-\t\t\t\t\tURL:   \"https://www.cisa.gov/known-exploited-vulnerabilities-catalog\",\n-\t\t\t\t\tTeam:  \"cisa\",\n-\t\t\t\t})\n-\t\t\t}\n-\n-\t\t\tvuln.AlertDict.CISA = alerts\n+\t\t\tvuln.KEVs = kevulnsToModels(kevulns)\n \t\t\tnKEV++\n \t\t\tr.ScannedCves[cveID] = vuln\n \t\t}\n@@ -127,6 +111,151 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging\n \treturn nil\n }\n \n+func kevulnsToModels(kevulns []kevulnmodels.KEVuln) (kevs []models.KEV) {\n+\tfor _, kevuln := range kevulns {\n+\t\tkev := kevulnToModel(kevuln)\n+\t\tif kev.Type == \"\" {\n+\t\t\tkev.Type = models.CISAKEVType\n+\t\t}\n+\t\tkevs = append(kevs, kev)\n+\t}\n+\treturn kevs\n+}\n+\n+func kevulnToModel(kevuln kevulnmodels.KEVuln) models.KEV {\n+\tv := reflect.ValueOf(kevuln)\n+\tif v.Kind() == reflect.Pointer {\n+\t\tif v.IsNil() {\n+\t\t\treturn models.KEV{}\n+\t\t}\n+\t\tv = v.Elem()\n+\t}\n+\tif v.Kind() != reflect.Struct {\n+\t\treturn models.KEV{}\n+\t}\n+\n+\ttyp := models.KEVType(strings.ToLower(fieldString(v, \"Type\", \"Source\")))\n+\tkev := models.KEV{\n+\t\tType:                       typ,\n+\t\tVendorProject:              fieldString(v, \"VendorProject\", \"Vendor\", \"Project\"),\n+\t\tProduct:                    fieldString(v, \"Product\"),\n+\t\tVulnerabilityName:          fieldString(v, \"VulnerabilityName\", \"Name\"),\n+\t\tShortDescription:           fieldString(v, \"ShortDescription\", \"Description\"),\n+\t\tRequiredAction:             fieldString(v, \"RequiredAction\"),\n+\t\tKnownRansomwareCampaignUse: fieldString(v, \"KnownRansomwareCampaignUse\", \"RansomwareCampaignUse\"),\n+\t\tDateAdded:                  fieldTime(v, \"DateAdded\"),\n+\t\tDueDate:                    fieldTimePtr(v, \"DueDate\"),\n+\t}\n+\n+\tif kev.Type == models.VulnCheckKEVType {\n+\t\tkev.VulnCheck = &amp;amp;models.VulnCheckKEV{\n+\t\t\tXDB:                  vulnCheckXDBs(v),\n+\t\t\tReportedExploitation: vulnCheckReportedExploitations(v),\n+\t\t}\n+\t\treturn kev\n+\t}\n+\n+\tkev.Type = models.CISAKEVType\n+\tkev.CISA = &amp;amp;models.CISAKEV{Note: fieldString(v, \"Notes\", \"Note\")}\n+\treturn kev\n+}\n+\n+func fieldString(v reflect.Value, names ...string) string {\n+\tfor _, name := range names {\n+\t\tf := v.FieldByName(name)\n+\t\tif f.IsValid() &amp;amp;&amp;amp; f.Kind() == reflect.String {\n+\t\t\treturn f.String()\n+\t\t}\n+\t}\n+\treturn \"\"\n+}\n+\n+func fieldTime(v reflect.Value, names ...string) time.Time {\n+\tfor _, name := range names {\n+\t\tf := v.FieldByName(name)\n+\t\tif f.IsValid() &amp;amp;&amp;amp; f.CanInterface() {\n+\t\t\tif t, ok := f.Interface().(time.Time); ok {\n+\t\t\t\treturn t\n+\t\t\t}\n+\t\t\tif f.Kind() == reflect.Pointer &amp;amp;&amp;amp; !f.IsNil() {\n+\t\t\t\tif t, ok := f.Interface().(*time.Time); ok {\n+\t\t\t\t\treturn *t\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn time.Time{}\n+}\n+\n+func fieldTimePtr(v reflect.Value, names ...string) *time.Time {\n+\tfor _, name := range names {\n+\t\tf := v.FieldByName(name)\n+\t\tif !f.IsValid() || !f.CanInterface() {\n+\t\t\tcontinue\n+\t\t}\n+\t\tif t, ok := f.Interface().(time.Time); ok &amp;amp;&amp;amp; !t.IsZero() {\n+\t\t\treturn &amp;amp;t\n+\t\t}\n+\t\tif f.Kind() == reflect.Pointer &amp;amp;&amp;amp; !f.IsNil() {\n+\t\t\tif t, ok := f.Interface().(*time.Time); ok {\n+\t\t\t\treturn t\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn nil\n+}\n+\n+func vulnCheckXDBs(v reflect.Value) (xdbs []models.VulnCheckXDB) {\n+\tf := v.FieldByName(\"XDB\")\n+\tif !f.IsValid() || f.Kind() != reflect.Slice {\n+\t\treturn nil\n+\t}\n+\tfor i := 0; i &amp;lt; f.Len(); i++ {\n+\t\tx := f.Index(i)\n+\t\tif x.Kind() == reflect.Pointer {\n+\t\t\tif x.IsNil() {\n+\t\t\t\tcontinue\n+\t\t\t}\n+\t\t\tx = x.Elem()\n+\t\t}\n+\t\tif x.Kind() != reflect.Struct {\n+\t\t\tcontinue\n+\t\t}\n+\t\txdbs = append(xdbs, models.VulnCheckXDB{\n+\t\t\tXDBID:       fieldString(x, \"XDBID\", \"ID\"),\n+\t\t\tXDBURL:      fieldString(x, \"XDBURL\", \"URL\"),\n+\t\t\tDateAdded:   fieldTime(x, \"DateAdded\"),\n+\t\t\tExploitType: fieldString(x, \"ExploitType\"),\n+\t\t\tCloneSSHURL: fieldString(x, \"CloneSSHURL\"),\n+\t\t})\n+\t}\n+\treturn xdbs\n+}\n+\n+func vulnCheckReportedExploitations(v reflect.Value) (reports []models.VulnCheckReportedExploitation) {\n+\tf := v.FieldByName(\"ReportedExploitation\")\n+\tif !f.IsValid() || f.Kind() != reflect.Slice {\n+\t\treturn nil\n+\t}\n+\tfor i := 0; i &amp;lt; f.Len(); i++ {\n+\t\tr := f.Index(i)\n+\t\tif r.Kind() == reflect.Pointer {\n+\t\t\tif r.IsNil() {\n+\t\t\t\tcontinue\n+\t\t\t}\n+\t\t\tr = r.Elem()\n+\t\t}\n+\t\tif r.Kind() != reflect.Struct {\n+\t\t\tcontinue\n+\t\t}\n+\t\treports = append(reports, models.VulnCheckReportedExploitation{\n+\t\t\tURL:       fieldString(r, \"URL\"),\n+\t\t\tDateAdded: fieldTime(r, \"DateAdded\"),\n+\t\t})\n+\t}\n+\treturn reports\n+}\n+\n type kevulnResponse struct {\n \trequest kevulnRequest\n \tjson    string\ndiff --git a/examples/config.toml.example b/examples/config.toml.example\nnew file mode 100644\nindex 0000000..33dbf5f\n--- /dev/null\n+++ b/examples/config.toml.example\n@@ -0,0 +1,37 @@\n+# Synthetic example configuration for KEV reporting.\n+# Replace every placeholder with values for your environment before use.\n+\n+[cveDict]\n+type = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/cve.sqlite3\"\n+\n+[ovalDict]\n+type = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/oval.sqlite3\"\n+\n+[gost]\n+type = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/gost.sqlite3\"\n+\n+[exploit]\n+type = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/go-exploitdb.sqlite3\"\n+\n+[metasploit]\n+type = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/go-msfdb.sqlite3\"\n+\n+[kevuln]\n+type = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/go-kev.sqlite3\"\n+\n+[cti]\n+type = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/go-cti.sqlite3\"\n+\n+[servers.example-host]\n+host = \"192.0.2.10\"\n+port = \"22\"\n+user = \"vuls-scan\"\n+keyPath = \"/home/vuls/.ssh/id_ed25519\"\n+scanMode = [\"fast-root\"]\ndiff --git a/examples/kev-scan-result.json b/examples/kev-scan-result.json\nnew file mode 100644\nindex 0000000..e140794\n--- /dev/null\n+++ b/examples/kev-scan-result.json\n@@ -0,0 +1,69 @@\n+{\n+  \"jsonVersion\": 4,\n+  \"lang\": \"en\",\n+  \"serverUUID\": \"00000000-0000-0000-0000-000000000000\",\n+  \"serverName\": \"example-host\",\n+  \"family\": \"ubuntu\",\n+  \"release\": \"22.04\",\n+  \"scannedCves\": {\n+    \"CVE-0000-0000\": {\n+      \"cveID\": \"CVE-0000-0000\",\n+      \"kevs\": [\n+        {\n+          \"type\": \"cisa\",\n+          \"vendorProject\": \"Example Vendor\",\n+          \"product\": \"Example Product\",\n+          \"vulnerabilityName\": \"Example Product Vulnerability\",\n+          \"shortDescription\": \"Synthetic example showing CISA KEV output shape.\",\n+          \"requiredAction\": \"Apply updates per vendor instructions.\",\n+          \"knownRansomwareCampaignUse\": \"Unknown\",\n+          \"dateAdded\": \"2026-01-02T00:00:00Z\",\n+          \"dueDate\": \"2026-01-23T00:00:00Z\",\n+          \"cisa\": {\n+            \"note\": \"Synthetic sample only.\"\n+          }\n+        },\n+        {\n+          \"type\": \"vulncheck\",\n+          \"vendorProject\": \"Example Vendor\",\n+          \"product\": \"Example Product\",\n+          \"vulnerabilityName\": \"Example Product Vulnerability\",\n+          \"shortDescription\": \"Synthetic example showing VulnCheck KEV output shape.\",\n+          \"requiredAction\": \"Apply updates per vendor instructions.\",\n+          \"knownRansomwareCampaignUse\": \"Unknown\",\n+          \"dateAdded\": \"2026-01-03T00:00:00Z\",\n+          \"vulnCheck\": {\n+            \"xdb\": [\n+              {\n+                \"xdbID\": \"XDB-000000\",\n+                \"xdbURL\": \"https://example.com/xdb/XDB-000000\",\n+                \"dateAdded\": \"2026-01-03T00:00:00Z\",\n+                \"exploitType\": \"proof-of-concept\",\n+                \"cloneSSHURL\": \"git@example.com:placeholder/example.git\"\n+              }\n+            ],\n+            \"reportedExploitation\": [\n+              {\n+                \"url\": \"https://example.com/advisories/CVE-0000-0000\",\n+                \"dateAdded\": \"2026-01-04T00:00:00Z\"\n+              }\n+            ]\n+          }\n+        }\n+      ],\n+      \"affectedPackages\": [\n+        {\n+          \"name\": \"example-package\",\n+          \"fixedIn\": \"1.2.3\"\n+        }\n+      ]\n+    }\n+  },\n+  \"packages\": {\n+    \"example-package\": {\n+      \"name\": \"example-package\",\n+      \"version\": \"1.2.2\",\n+      \"newVersion\": \"1.2.3\"\n+    }\n+  }\n+}\ndiff --git a/models/scanresults.go b/models/scanresults.go\nindex 508b992..f17b095 100644\n--- a/models/scanresults.go\n+++ b/models/scanresults.go\n@@ -197,13 +197,14 @@ func (r ScanResult) FormatTextReportHeader() string {\n \t\tpkgs = fmt.Sprintf(\"%s, %d libs\", pkgs, r.LibraryScanners.Total())\n \t}\n \n-\treturn fmt.Sprintf(\"%s\\n%s\\n%s\\n%s, %s, %s, %s\\n%s\\n\",\n+\treturn fmt.Sprintf(\"%s\\n%s\\n%s\\n%s, %s, %s, %s, %s\\n%s\\n\",\n \t\tr.ServerInfo(),\n \t\tbuf.String(),\n \t\tr.ScannedCves.FormatCveSummary(),\n \t\tr.ScannedCves.FormatFixedStatus(r.Packages),\n \t\tr.FormatExploitCveSummary(),\n \t\tr.FormatMetasploitCveSummary(),\n+\t\tr.FormatKEVCveSummary(),\n \t\tr.FormatAlertSummary(),\n \t\tpkgs)\n }\n@@ -251,15 +252,22 @@ func (r ScanResult) FormatMetasploitCveSummary() string {\n \treturn fmt.Sprintf(\"%d exploits\", nMetasploitCve)\n }\n \n+// FormatKEVCveSummary returns a summary of KEV CVEs.\n+func (r ScanResult) FormatKEVCveSummary() string {\n+\tnKEVCve := 0\n+\tfor _, vuln := range r.ScannedCves {\n+\t\tif 0 &amp;lt; len(vuln.KEVs) {\n+\t\t\tnKEVCve++\n+\t\t}\n+\t}\n+\treturn fmt.Sprintf(\"%d kevs\", nKEVCve)\n+}\n+\n // FormatAlertSummary returns a summary of CERT alerts\n func (r ScanResult) FormatAlertSummary() string {\n-\tcisaCnt := 0\n \tuscertCnt := 0\n \tjpcertCnt := 0\n \tfor _, vuln := range r.ScannedCves {\n-\t\tif len(vuln.AlertDict.CISA) &amp;gt; 0 {\n-\t\t\tcisaCnt += len(vuln.AlertDict.CISA)\n-\t\t}\n \t\tif len(vuln.AlertDict.USCERT) &amp;gt; 0 {\n \t\t\tuscertCnt += len(vuln.AlertDict.USCERT)\n \t\t}\n@@ -267,7 +275,7 @@ func (r ScanResult) FormatAlertSummary() string {\n \t\t\tjpcertCnt += len(vuln.AlertDict.JPCERT)\n \t\t}\n \t}\n-\treturn fmt.Sprintf(\"cisa: %d, uscert: %d, jpcert: %d alerts\", cisaCnt, uscertCnt, jpcertCnt)\n+\treturn fmt.Sprintf(\"uscert: %d, jpcert: %d alerts\", uscertCnt, jpcertCnt)\n }\n \n func (r ScanResult) isDisplayUpdatableNum(mode config.ScanMode) bool {\n@@ -425,6 +433,12 @@ func (r *ScanResult) SortForJSONOutput() {\n \t\tsort.Slice(v.Mitigations, func(i, j int) bool {\n \t\t\treturn v.Mitigations[i].URL &amp;lt; v.Mitigations[j].URL\n \t\t})\n+\t\tsort.Slice(v.KEVs, func(i, j int) bool {\n+\t\t\tif v.KEVs[i].Type != v.KEVs[j].Type {\n+\t\t\t\treturn v.KEVs[i].Type &amp;lt; v.KEVs[j].Type\n+\t\t\t}\n+\t\t\treturn v.KEVs[i].VulnerabilityName &amp;lt; v.KEVs[j].VulnerabilityName\n+\t\t})\n \n \t\tv.CveContents.Sort()\n \n@@ -434,9 +448,6 @@ func (r *ScanResult) SortForJSONOutput() {\n \t\tsort.Slice(v.AlertDict.JPCERT, func(i, j int) bool {\n \t\t\treturn v.AlertDict.JPCERT[i].Title &amp;lt; v.AlertDict.JPCERT[j].Title\n \t\t})\n-\t\tsort.Slice(v.AlertDict.CISA, func(i, j int) bool {\n-\t\t\treturn v.AlertDict.CISA[i].Title &amp;lt; v.AlertDict.CISA[j].Title\n-\t\t})\n \t\tr.ScannedCves[k] = v\n \t}\n }\ndiff --git a/models/scanresults_kev_test.go b/models/scanresults_kev_test.go\nnew file mode 100644\nindex 0000000..159ebfb\n--- /dev/null\n+++ b/models/scanresults_kev_test.go\n@@ -0,0 +1,57 @@\n+package models\n+\n+import (\n+\t\"reflect\"\n+\t\"testing\"\n+)\n+\n+func TestScanResult_FormatKEVCveSummary(t *testing.T) {\n+\tr := ScanResult{\n+\t\tScannedCves: VulnInfos{\n+\t\t\t\"CVE-0000-0001\": VulnInfo{KEVs: []KEV{{Type: CISAKEVType}}},\n+\t\t\t\"CVE-0000-0002\": VulnInfo{KEVs: []KEV{{Type: CISAKEVType}, {Type: VulnCheckKEVType}}},\n+\t\t\t\"CVE-0000-0003\": VulnInfo{},\n+\t\t},\n+\t}\n+\n+\tif got, want := r.FormatKEVCveSummary(), \"2 kevs\"; got != want {\n+\t\tt.Fatalf(\"FormatKEVCveSummary() = %q, want %q\", got, want)\n+\t}\n+}\n+\n+func TestScanResult_SortForJSONOutputKEVs(t *testing.T) {\n+\tr := &amp;amp;ScanResult{\n+\t\tScannedCves: VulnInfos{\n+\t\t\t\"CVE-0000-0001\": VulnInfo{\n+\t\t\t\tKEVs: []KEV{\n+\t\t\t\t\t{Type: VulnCheckKEVType, VulnerabilityName: \"beta\"},\n+\t\t\t\t\t{Type: CISAKEVType, VulnerabilityName: \"zulu\"},\n+\t\t\t\t\t{Type: CISAKEVType, VulnerabilityName: \"alpha\"},\n+\t\t\t\t},\n+\t\t\t},\n+\t\t},\n+\t}\n+\n+\tr.SortForJSONOutput()\n+\n+\tgot := r.ScannedCves[\"CVE-0000-0001\"].KEVs\n+\twant := []KEV{\n+\t\t{Type: CISAKEVType, VulnerabilityName: \"alpha\"},\n+\t\t{Type: CISAKEVType, VulnerabilityName: \"zulu\"},\n+\t\t{Type: VulnCheckKEVType, VulnerabilityName: \"beta\"},\n+\t}\n+\tif !reflect.DeepEqual(got, want) {\n+\t\tt.Fatalf(\"sorted KEVs = %+v, want %+v\", got, want)\n+\t}\n+}\n+\n+func TestAlertDictIgnoresDeprecatedCISA(t *testing.T) {\n+\talerts := AlertDict{CISA: []Alert{{Title: \"legacy KEV alert\"}}}\n+\n+\tif !alerts.IsEmpty() {\n+\t\tt.Fatal(\"AlertDict with only deprecated CISA alerts should be empty\")\n+\t}\n+\tif got, want := alerts.FormatSource(), \"\"; got != want {\n+\t\tt.Fatalf(\"FormatSource() = %q, want %q\", got, want)\n+\t}\n+}\ndiff --git a/models/vulninfos.go b/models/vulninfos.go\nindex 3e85e81..4e13120 100644\n--- a/models/vulninfos.go\n+++ b/models/vulninfos.go\n@@ -263,6 +263,7 @@ type VulnInfo struct {\n \tAffectedPackages     PackageFixStatuses   `json:\"affectedPackages,omitempty\"`\n \tDistroAdvisories     DistroAdvisories     `json:\"distroAdvisories,omitempty\"` // for Amazon, RHEL, Fedora, FreeBSD, Microsoft\n \tCveContents          CveContents          `json:\"cveContents,omitempty\"`\n+\tKEVs                 []KEV                `json:\"kevs,omitempty\"`\n \tExploits             []Exploit            `json:\"exploits,omitempty\"`\n \tMetasploits          []Metasploit         `json:\"metasploits,omitempty\"`\n \tMitigations          []Mitigation         `json:\"mitigations,omitempty\"`\n@@ -284,6 +285,57 @@ type Alert struct {\n \tTeam  string `json:\"team,omitempty\"`\n }\n \n+// KEVType is a source of known exploited vulnerability data.\n+type KEVType string\n+\n+const (\n+\t// CISAKEVType is CISA Known Exploited Vulnerabilities data.\n+\tCISAKEVType KEVType = \"cisa\"\n+\t// VulnCheckKEVType is VulnCheck Known Exploited Vulnerabilities data.\n+\tVulnCheckKEVType KEVType = \"vulncheck\"\n+)\n+\n+// KEV has known exploited vulnerability information.\n+type KEV struct {\n+\tType                       KEVType       `json:\"type,omitempty\"`\n+\tVendorProject              string        `json:\"vendorProject,omitempty\"`\n+\tProduct                    string        `json:\"product,omitempty\"`\n+\tVulnerabilityName          string        `json:\"vulnerabilityName,omitempty\"`\n+\tShortDescription           string        `json:\"shortDescription,omitempty\"`\n+\tRequiredAction             string        `json:\"requiredAction,omitempty\"`\n+\tKnownRansomwareCampaignUse string        `json:\"knownRansomwareCampaignUse,omitempty\"`\n+\tDateAdded                  time.Time     `json:\"dateAdded,omitempty\"`\n+\tDueDate                    *time.Time    `json:\"dueDate,omitempty\"`\n+\tCISA                       *CISAKEV      `json:\"cisa,omitempty\"`\n+\tVulnCheck                  *VulnCheckKEV `json:\"vulnCheck,omitempty\"`\n+}\n+\n+// CISAKEV has CISA-specific KEV information.\n+type CISAKEV struct {\n+\tNote string `json:\"note,omitempty\"`\n+}\n+\n+// VulnCheckKEV has VulnCheck-specific KEV information.\n+type VulnCheckKEV struct {\n+\tXDB                  []VulnCheckXDB                  `json:\"xdb,omitempty\"`\n+\tReportedExploitation []VulnCheckReportedExploitation `json:\"reportedExploitation,omitempty\"`\n+}\n+\n+// VulnCheckXDB has VulnCheck XDB exploit metadata.\n+type VulnCheckXDB struct {\n+\tXDBID       string    `json:\"xdbID,omitempty\"`\n+\tXDBURL      string    `json:\"xdbURL,omitempty\"`\n+\tDateAdded   time.Time `json:\"dateAdded,omitempty\"`\n+\tExploitType string    `json:\"exploitType,omitempty\"`\n+\tCloneSSHURL string    `json:\"cloneSSHURL,omitempty\"`\n+}\n+\n+// VulnCheckReportedExploitation has VulnCheck reported exploitation metadata.\n+type VulnCheckReportedExploitation struct {\n+\tURL       string    `json:\"url,omitempty\"`\n+\tDateAdded time.Time `json:\"dateAdded,omitempty\"`\n+}\n+\n // GitHubSecurityAlerts is a list of GitHubSecurityAlert\n type GitHubSecurityAlerts []GitHubSecurityAlert\n \n@@ -910,24 +962,21 @@ type Mitigation struct {\n \tURL            string         `json:\"url,omitempty\"`\n }\n \n-// AlertDict has target cve JPCERT, USCERT and CISA alert data\n+// AlertDict has target cve JPCERT and USCERT alert data\n type AlertDict struct {\n-\tCISA   []Alert `json:\"cisa\"`\n+\tCISA   []Alert `json:\"cisa\"` // Deprecated: KEV data is represented by VulnInfo.KEVs.\n \tJPCERT []Alert `json:\"jpcert\"`\n \tUSCERT []Alert `json:\"uscert\"`\n }\n \n // IsEmpty checks if the content of AlertDict is empty\n func (a AlertDict) IsEmpty() bool {\n-\treturn len(a.CISA) == 0 &amp;amp;&amp;amp; len(a.JPCERT) == 0 &amp;amp;&amp;amp; len(a.USCERT) == 0\n+\treturn len(a.JPCERT) == 0 &amp;amp;&amp;amp; len(a.USCERT) == 0\n }\n \n // FormatSource returns which source has this alert\n func (a AlertDict) FormatSource() string {\n \tvar s []string\n-\tif len(a.CISA) != 0 {\n-\t\ts = append(s, \"CISA\")\n-\t}\n \tif len(a.USCERT) != 0 || len(a.JPCERT) != 0 {\n \t\ts = append(s, \"CERT\")\n \t}\ndiff --git a/reporter/util.go b/reporter/util.go\nindex d9cfdaa..a2dfbd0 100644\n--- a/reporter/util.go\n+++ b/reporter/util.go\n@@ -204,6 +204,7 @@ func formatOneLineSummary(rs ...models.ScanResult) string {\n \t\t\t\tr.FormatUpdatablePkgsSummary(),\n \t\t\t\tr.FormatExploitCveSummary(),\n \t\t\t\tr.FormatMetasploitCveSummary(),\n+\t\t\t\tr.FormatKEVCveSummary(),\n \t\t\t\tr.FormatAlertSummary(),\n \t\t\t}\n \t\t} else {\n@@ -565,10 +566,6 @@ No CVE-IDs are found in updatable packages.\n \t\t})\n \t\tdata = append(data, ds...)\n \n-\t\tfor _, alert := range vuln.AlertDict.CISA {\n-\t\t\tdata = append(data, []string{\"CISA Alert\", alert.URL})\n-\t\t}\n-\n \t\tfor _, alert := range vuln.AlertDict.JPCERT {\n \t\t\tdata = append(data, []string{\"JPCERT Alert\", alert.URL})\n \t\t}\ndiff --git a/tui/tui.go b/tui/tui.go\nindex 4407f56..80afe95 100644\n--- a/tui/tui.go\n+++ b/tui/tui.go\n@@ -812,16 +812,6 @@ func setChangelogLayout(g *gocui.Gui) error {\n \t\t\t}\n \t\t}\n \n-\t\tif len(vinfo.AlertDict.CISA) &amp;gt; 0 {\n-\t\t\tlines = append(lines, \"\\n\",\n-\t\t\t\t\"CISA Alert\",\n-\t\t\t\t\"===========\",\n-\t\t\t)\n-\t\t\tfor _, alert := range vinfo.AlertDict.CISA {\n-\t\t\t\tlines = append(lines, fmt.Sprintf(\"* [%s](%s)\", alert.Title, alert.URL))\n-\t\t\t}\n-\t\t}\n-\n \t\tif len(vinfo.AlertDict.USCERT) &amp;gt; 0 {\n \t\t\tlines = append(lines, \"\\n\",\n \t\t\t\t\"USCERT Alert\",\n", "creation_timestamp": "2026-07-04T00:01:07.449537Z"}</content>
    <link href="https://vulnerability.circl.lu/sighting/f71acbdf-1493-48d8-a9a2-4c17cd5356d2/export"/>
    <published>2026-07-04T00:01:07.449537+00:00</published>
  </entry>
  <entry>
    <id>https://vulnerability.circl.lu/sighting/c9bec138-3fd2-46c7-b1f2-43a14e453d4c/export</id>
    <title>c9bec138-3fd2-46c7-b1f2-43a14e453d4c</title>
    <updated>2026-07-04T09:01:48.697491+00:00</updated>
    <author>
      <name>Automation user</name>
      <uri>https://cvepremium.circl.lu/user/automation</uri>
    </author>
    <content>{"uuid": "c9bec138-3fd2-46c7-b1f2-43a14e453d4c", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-0000-0000", "type": "seen", "source": "https://gist.github.com/tu-trinh-scale/cac33ea32f93e96a33598e30cf7f77e7", "content": "diff --git a/README.md b/README.md\nindex 57102d1..40c4de2 100644\n--- a/README.md\n+++ b/README.md\n@@ -165,6 +165,7 @@ Vuls has some options to detect the vulnerabilities\n   - Auto-detection of servers set using CIDR, generate configuration file template\n - Email and Slack notification is possible (supports Japanese language)\n - Scan result is viewable on accessory software, TUI Viewer in a terminal or Web UI ([VulsRepo](https://github.com/ishiDACo/vulsrepo)).\n+- For configuration, a ready-to-use example configuration is provided in [config.toml.example](config.toml.example).\n \n ----\n \ndiff --git a/config.toml.example b/config.toml.example\nnew file mode 100644\nindex 0000000..5d83447\n--- /dev/null\n+++ b/config.toml.example\n@@ -0,0 +1,50 @@\n+[cveDict]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/cve.sqlite3\"\n+\n+[ovalDict]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/oval.sqlite3\"\n+\n+[gost]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/gost.sqlite3\"\n+\n+[exploit]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/go-exploitdb.sqlite3\"\n+\n+[metasploit]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/go-msfdb.sqlite3\"\n+\n+[cisa]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/cisa.sqlite3\"\n+\n+[vulncheck]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/vulncheck.sqlite3\"\n+\n+[slack]\n+hookURL = \"https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX\"\n+channel = \"#example-channel\"\n+authUser = \"username\"\n+notifyUsers = [\"@user1\", \"@user2\"]\n+\n+[email]\n+smtpServer = \"smtp.example.com\"\n+smtpPort = \"587\"\n+user = \"user@example.com\"\n+password = \"placeholder-smtp-password\"\n+from = \"vuls@example.com\"\n+to = [\"admin@example.com\"]\n+\n+[servers]\n+\n+[servers.example-host]\n+host = \"192.168.0.10\"\n+port = \"22\"\n+user = \"admin\"\n+keyPath = \"/home/user/.ssh/id_rsa\"\n+# passphrase = \"placeholder-ssh-passphrase\"\ndiff --git a/detector/kevuln.go b/detector/kevuln.go\nindex 41afdfe..7624e44 100644\n--- a/detector/kevuln.go\n+++ b/detector/kevuln.go\n@@ -79,18 +79,33 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging\n \t\t\t\treturn err\n \t\t\t}\n \n-\t\t\talerts := []models.Alert{}\n+\t\t\tkevs := []models.KEV{}\n \t\t\tif len(kevulns) &amp;gt; 0 {\n-\t\t\t\talerts = append(alerts, models.Alert{\n-\t\t\t\t\tTitle: \"Known Exploited Vulnerabilities Catalog\",\n-\t\t\t\t\tURL:   \"https://www.cisa.gov/known-exploited-vulnerabilities-catalog\",\n-\t\t\t\t\tTeam:  \"cisa\",\n-\t\t\t\t})\n+\t\t\t\tfor _, k := range kevulns {\n+\t\t\t\t\tvar due *time.Time\n+\t\t\t\t\tif !k.DueDate.IsZero() {\n+\t\t\t\t\t\tdue = &amp;amp;k.DueDate\n+\t\t\t\t\t}\n+\t\t\t\t\tkevs = append(kevs, models.KEV{\n+\t\t\t\t\t\tType: models.CISAKEVType,\n+\t\t\t\t\t\tVendorProject: k.VendorProject,\n+\t\t\t\t\t\tProduct: k.Product,\n+\t\t\t\t\t\tVulnerabilityName: k.VulnerabilityName,\n+\t\t\t\t\t\tShortDescription: k.ShortDescription,\n+\t\t\t\t\t\tRequiredAction: k.RequiredAction,\n+\t\t\t\t\t\tKnownRansomwareCampaignUse: k.KnownRansomwareCampaignUse,\n+\t\t\t\t\t\tDateAdded: k.DateAdded,\n+\t\t\t\t\t\tDueDate: due,\n+\t\t\t\t\t\tCISA: &amp;amp;models.CISAKEV{\n+\t\t\t\t\t\t\tNote: k.Notes,\n+\t\t\t\t\t\t},\n+\t\t\t\t\t})\n+\t\t\t\t}\n \t\t\t}\n \n \t\t\tv, ok := r.ScannedCves[res.request.cveID]\n \t\t\tif ok {\n-\t\t\t\tv.AlertDict.CISA = alerts\n+\t\t\t\tv.KEVs = append(v.KEVs, kevs...)\n \t\t\t\tnKEV++\n \t\t\t}\n \t\t\tr.ScannedCves[res.request.cveID] = v\n@@ -108,16 +123,31 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging\n \t\t\t\tcontinue\n \t\t\t}\n \n-\t\t\talerts := []models.Alert{}\n+\t\t\tkevs := []models.KEV{}\n \t\t\tif len(kevulns) &amp;gt; 0 {\n-\t\t\t\talerts = append(alerts, models.Alert{\n-\t\t\t\t\tTitle: \"Known Exploited Vulnerabilities Catalog\",\n-\t\t\t\t\tURL:   \"https://www.cisa.gov/known-exploited-vulnerabilities-catalog\",\n-\t\t\t\t\tTeam:  \"cisa\",\n-\t\t\t\t})\n+\t\t\t\tfor _, k := range kevulns {\n+\t\t\t\t\tvar due *time.Time\n+\t\t\t\t\tif !k.DueDate.IsZero() {\n+\t\t\t\t\t\tdue = &amp;amp;k.DueDate\n+\t\t\t\t\t}\n+\t\t\t\t\tkevs = append(kevs, models.KEV{\n+\t\t\t\t\t\tType: models.CISAKEVType,\n+\t\t\t\t\t\tVendorProject: k.VendorProject,\n+\t\t\t\t\t\tProduct: k.Product,\n+\t\t\t\t\t\tVulnerabilityName: k.VulnerabilityName,\n+\t\t\t\t\t\tShortDescription: k.ShortDescription,\n+\t\t\t\t\t\tRequiredAction: k.RequiredAction,\n+\t\t\t\t\t\tKnownRansomwareCampaignUse: k.KnownRansomwareCampaignUse,\n+\t\t\t\t\t\tDateAdded: k.DateAdded,\n+\t\t\t\t\t\tDueDate: due,\n+\t\t\t\t\t\tCISA: &amp;amp;models.CISAKEV{\n+\t\t\t\t\t\t\tNote: k.Notes,\n+\t\t\t\t\t\t},\n+\t\t\t\t\t})\n+\t\t\t\t}\n \t\t\t}\n \n-\t\t\tvuln.AlertDict.CISA = alerts\n+\t\t\tvuln.KEVs = append(vuln.KEVs, kevs...)\n \t\t\tnKEV++\n \t\t\tr.ScannedCves[cveID] = vuln\n \t\t}\ndiff --git a/models/scanresults.go b/models/scanresults.go\nindex 508b992..a5a7299 100644\n--- a/models/scanresults.go\n+++ b/models/scanresults.go\n@@ -197,7 +197,7 @@ func (r ScanResult) FormatTextReportHeader() string {\n \t\tpkgs = fmt.Sprintf(\"%s, %d libs\", pkgs, r.LibraryScanners.Total())\n \t}\n \n-\treturn fmt.Sprintf(\"%s\\n%s\\n%s\\n%s, %s, %s, %s\\n%s\\n\",\n+\treturn fmt.Sprintf(\"%s\\n%s\\n%s\\n%s, %s, %s, %s, %s\\n%s\\n\",\n \t\tr.ServerInfo(),\n \t\tbuf.String(),\n \t\tr.ScannedCves.FormatCveSummary(),\n@@ -205,6 +205,7 @@ func (r ScanResult) FormatTextReportHeader() string {\n \t\tr.FormatExploitCveSummary(),\n \t\tr.FormatMetasploitCveSummary(),\n \t\tr.FormatAlertSummary(),\n+\t\tr.FormatKEVCveSummary(),\n \t\tpkgs)\n }\n \n@@ -253,13 +254,9 @@ func (r ScanResult) FormatMetasploitCveSummary() string {\n \n // FormatAlertSummary returns a summary of CERT alerts\n func (r ScanResult) FormatAlertSummary() string {\n-\tcisaCnt := 0\n \tuscertCnt := 0\n \tjpcertCnt := 0\n \tfor _, vuln := range r.ScannedCves {\n-\t\tif len(vuln.AlertDict.CISA) &amp;gt; 0 {\n-\t\t\tcisaCnt += len(vuln.AlertDict.CISA)\n-\t\t}\n \t\tif len(vuln.AlertDict.USCERT) &amp;gt; 0 {\n \t\t\tuscertCnt += len(vuln.AlertDict.USCERT)\n \t\t}\n@@ -267,7 +264,18 @@ func (r ScanResult) FormatAlertSummary() string {\n \t\t\tjpcertCnt += len(vuln.AlertDict.JPCERT)\n \t\t}\n \t}\n-\treturn fmt.Sprintf(\"cisa: %d, uscert: %d, jpcert: %d alerts\", cisaCnt, uscertCnt, jpcertCnt)\n+\treturn fmt.Sprintf(\"uscert: %d, jpcert: %d alerts\", uscertCnt, jpcertCnt)\n+}\n+\n+// FormatKEVCveSummary returns a summary of KEVs\n+func (r ScanResult) FormatKEVCveSummary() string {\n+\tnKevs := 0\n+\tfor _, vuln := range r.ScannedCves {\n+\t\tif len(vuln.KEVs) &amp;gt; 0 {\n+\t\t\tnKevs++\n+\t\t}\n+\t}\n+\treturn fmt.Sprintf(\"%d kevs\", nKevs)\n }\n \n func (r ScanResult) isDisplayUpdatableNum(mode config.ScanMode) bool {\n@@ -434,8 +442,11 @@ func (r *ScanResult) SortForJSONOutput() {\n \t\tsort.Slice(v.AlertDict.JPCERT, func(i, j int) bool {\n \t\t\treturn v.AlertDict.JPCERT[i].Title &amp;lt; v.AlertDict.JPCERT[j].Title\n \t\t})\n-\t\tsort.Slice(v.AlertDict.CISA, func(i, j int) bool {\n-\t\t\treturn v.AlertDict.CISA[i].Title &amp;lt; v.AlertDict.CISA[j].Title\n+\t\tsort.Slice(v.KEVs, func(i, j int) bool {\n+\t\t\tif v.KEVs[i].Type != v.KEVs[j].Type {\n+\t\t\t\treturn v.KEVs[i].Type &amp;lt; v.KEVs[j].Type\n+\t\t\t}\n+\t\t\treturn v.KEVs[i].VulnerabilityName &amp;lt; v.KEVs[j].VulnerabilityName\n \t\t})\n \t\tr.ScannedCves[k] = v\n \t}\ndiff --git a/models/scanresults_test.go b/models/scanresults_test.go\nindex d429281..d286096 100644\n--- a/models/scanresults_test.go\n+++ b/models/scanresults_test.go\n@@ -225,10 +225,10 @@ func TestScanResult_Sort(t *testing.T) {\n \t\t\t\t\t\t\t\t{Title: \"a\"},\n \t\t\t\t\t\t\t\t{Title: \"b\"},\n \t\t\t\t\t\t\t},\n-\t\t\t\t\t\t\tCISA: []Alert{\n-\t\t\t\t\t\t\t\t{Title: \"a\"},\n-\t\t\t\t\t\t\t\t{Title: \"b\"},\n-\t\t\t\t\t\t\t},\n+\t\t\t\t\t\t},\n+\t\t\t\t\t\tKEVs: []KEV{\n+\t\t\t\t\t\t\t{Type: CISAKEVType, VulnerabilityName: \"a\"},\n+\t\t\t\t\t\t\t{Type: CISAKEVType, VulnerabilityName: \"b\"},\n \t\t\t\t\t\t},\n \t\t\t\t\t},\n \t\t\t\t},\n@@ -288,10 +288,10 @@ func TestScanResult_Sort(t *testing.T) {\n \t\t\t\t\t\t\t\t{Title: \"a\"},\n \t\t\t\t\t\t\t\t{Title: \"b\"},\n \t\t\t\t\t\t\t},\n-\t\t\t\t\t\t\tCISA: []Alert{\n-\t\t\t\t\t\t\t\t{Title: \"a\"},\n-\t\t\t\t\t\t\t\t{Title: \"b\"},\n-\t\t\t\t\t\t\t},\n+\t\t\t\t\t\t},\n+\t\t\t\t\t\tKEVs: []KEV{\n+\t\t\t\t\t\t\t{Type: CISAKEVType, VulnerabilityName: \"a\"},\n+\t\t\t\t\t\t\t{Type: CISAKEVType, VulnerabilityName: \"b\"},\n \t\t\t\t\t\t},\n \t\t\t\t\t},\n \t\t\t\t},\n@@ -354,10 +354,10 @@ func TestScanResult_Sort(t *testing.T) {\n \t\t\t\t\t\t\t\t{Title: \"b\"},\n \t\t\t\t\t\t\t\t{Title: \"a\"},\n \t\t\t\t\t\t\t},\n-\t\t\t\t\t\t\tCISA: []Alert{\n-\t\t\t\t\t\t\t\t{Title: \"b\"},\n-\t\t\t\t\t\t\t\t{Title: \"a\"},\n-\t\t\t\t\t\t\t},\n+\t\t\t\t\t\t},\n+\t\t\t\t\t\tKEVs: []KEV{\n+\t\t\t\t\t\t\t{Type: VulnCheckKEVType, VulnerabilityName: \"b\"},\n+\t\t\t\t\t\t\t{Type: CISAKEVType, VulnerabilityName: \"a\"},\n \t\t\t\t\t\t},\n \t\t\t\t\t},\n \t\t\t\t},\n@@ -417,10 +417,10 @@ func TestScanResult_Sort(t *testing.T) {\n \t\t\t\t\t\t\t\t{Title: \"a\"},\n \t\t\t\t\t\t\t\t{Title: \"b\"},\n \t\t\t\t\t\t\t},\n-\t\t\t\t\t\t\tCISA: []Alert{\n-\t\t\t\t\t\t\t\t{Title: \"a\"},\n-\t\t\t\t\t\t\t\t{Title: \"b\"},\n-\t\t\t\t\t\t\t},\n+\t\t\t\t\t\t},\n+\t\t\t\t\t\tKEVs: []KEV{\n+\t\t\t\t\t\t\t{Type: CISAKEVType, VulnerabilityName: \"a\"},\n+\t\t\t\t\t\t\t{Type: VulnCheckKEVType, VulnerabilityName: \"b\"},\n \t\t\t\t\t\t},\n \t\t\t\t\t},\n \t\t\t\t},\ndiff --git a/models/vulninfos.go b/models/vulninfos.go\nindex 3e85e81..d2d935d 100644\n--- a/models/vulninfos.go\n+++ b/models/vulninfos.go\n@@ -268,6 +268,7 @@ type VulnInfo struct {\n \tMitigations          []Mitigation         `json:\"mitigations,omitempty\"`\n \tCtis                 []string             `json:\"ctis,omitempty\"`\n \tAlertDict            AlertDict            `json:\"alertDict,omitempty\"`\n+\tKEVs                 []KEV                `json:\"kevs,omitempty\"`\n \tCpeURIs              []string             `json:\"cpeURIs,omitempty\"` // CpeURIs related to this CVE defined in config.toml\n \tGitHubSecurityAlerts GitHubSecurityAlerts `json:\"gitHubSecurityAlerts,omitempty\"`\n \tWpPackageFixStats    WpPackageFixStats    `json:\"wpPackageFixStats,omitempty\"`\n@@ -910,24 +911,65 @@ type Mitigation struct {\n \tURL            string         `json:\"url,omitempty\"`\n }\n \n-// AlertDict has target cve JPCERT, USCERT and CISA alert data\n+// KEVType is a type for KEV\n+type KEVType string\n+\n+const (\n+\tCISAKEVType      = KEVType(\"cisa\")\n+\tVulnCheckKEVType = KEVType(\"vulncheck\")\n+)\n+\n+// KEV has Known Exploited Vulnerabilities information\n+type KEV struct {\n+\tType                       KEVType       `json:\"type,omitempty\"`\n+\tVendorProject              string        `json:\"vendorProject,omitempty\"`\n+\tProduct                    string        `json:\"product,omitempty\"`\n+\tVulnerabilityName          string        `json:\"vulnerabilityName,omitempty\"`\n+\tShortDescription           string        `json:\"shortDescription,omitempty\"`\n+\tRequiredAction             string        `json:\"requiredAction,omitempty\"`\n+\tKnownRansomwareCampaignUse string        `json:\"knownRansomwareCampaignUse,omitempty\"`\n+\tDateAdded                  time.Time     `json:\"dateAdded,omitempty\"`\n+\tDueDate                    *time.Time    `json:\"dueDate,omitempty\"`\n+\tCISA                       *CISAKEV      `json:\"cisa,omitempty\"`\n+\tVulnCheck                  *VulnCheckKEV `json:\"vulncheck,omitempty\"`\n+}\n+\n+type CISAKEV struct {\n+\tNote string `json:\"note,omitempty\"`\n+}\n+\n+type VulnCheckKEV struct {\n+\tXDB                  []VulnCheckXDB                  `json:\"xdb,omitempty\"`\n+\tReportedExploitation []VulnCheckReportedExploitation `json:\"reportedExploitation,omitempty\"`\n+}\n+\n+type VulnCheckXDB struct {\n+\tXDBID       string    `json:\"xdbID,omitempty\"`\n+\tXDBURL      string    `json:\"xdbURL,omitempty\"`\n+\tDateAdded   time.Time `json:\"dateAdded,omitempty\"`\n+\tExploitType string    `json:\"exploitType,omitempty\"`\n+\tCloneSSHURL string    `json:\"cloneSSHURL,omitempty\"`\n+}\n+\n+type VulnCheckReportedExploitation struct {\n+\tURL       string    `json:\"url,omitempty\"`\n+\tDateAdded time.Time `json:\"dateAdded,omitempty\"`\n+}\n+\n+// AlertDict has target cve JPCERT and USCERT alert data\n type AlertDict struct {\n-\tCISA   []Alert `json:\"cisa\"`\n \tJPCERT []Alert `json:\"jpcert\"`\n \tUSCERT []Alert `json:\"uscert\"`\n }\n \n // IsEmpty checks if the content of AlertDict is empty\n func (a AlertDict) IsEmpty() bool {\n-\treturn len(a.CISA) == 0 &amp;amp;&amp;amp; len(a.JPCERT) == 0 &amp;amp;&amp;amp; len(a.USCERT) == 0\n+\treturn len(a.JPCERT) == 0 &amp;amp;&amp;amp; len(a.USCERT) == 0\n }\n \n // FormatSource returns which source has this alert\n func (a AlertDict) FormatSource() string {\n \tvar s []string\n-\tif len(a.CISA) != 0 {\n-\t\ts = append(s, \"CISA\")\n-\t}\n \tif len(a.USCERT) != 0 || len(a.JPCERT) != 0 {\n \t\ts = append(s, \"CERT\")\n \t}\ndiff --git a/reporter/util.go b/reporter/util.go\nindex d9cfdaa..a44ae57 100644\n--- a/reporter/util.go\n+++ b/reporter/util.go\n@@ -565,10 +565,6 @@ No CVE-IDs are found in updatable packages.\n \t\t})\n \t\tdata = append(data, ds...)\n \n-\t\tfor _, alert := range vuln.AlertDict.CISA {\n-\t\t\tdata = append(data, []string{\"CISA Alert\", alert.URL})\n-\t\t}\n-\n \t\tfor _, alert := range vuln.AlertDict.JPCERT {\n \t\t\tdata = append(data, []string{\"JPCERT Alert\", alert.URL})\n \t\t}\ndiff --git a/synthetic_scan_result_sample.json b/synthetic_scan_result_sample.json\nnew file mode 100644\nindex 0000000..611a1a3\n--- /dev/null\n+++ b/synthetic_scan_result_sample.json\n@@ -0,0 +1,38 @@\n+{\n+  \"jsonVersion\": 1,\n+  \"lang\": \"en\",\n+  \"serverUUID\": \"00000000-0000-0000-0000-000000000000\",\n+  \"serverName\": \"example-host\",\n+  \"family\": \"ubuntu\",\n+  \"release\": \"20.04\",\n+  \"scannedAt\": \"2023-10-01T10:00:00Z\",\n+  \"scannedCves\": {\n+    \"CVE-0000-0000\": {\n+      \"cveID\": \"CVE-0000-0000\",\n+      \"kevs\": [\n+        {\n+          \"type\": \"cisa\",\n+          \"vulnerabilityName\": \"Example Vulnerability\",\n+          \"dateAdded\": \"2023-10-01T00:00:00Z\",\n+          \"shortDescription\": \"This is a documentation CVE.\"\n+        },\n+        {\n+          \"type\": \"vulncheck\",\n+          \"vulnerabilityName\": \"Example Vulnerability\",\n+          \"dateAdded\": \"2023-10-01T00:00:00Z\",\n+          \"vulncheck\": {\n+             \"xdb\": [\n+                {\n+                   \"xdbID\": \"xdb-example-1\",\n+                   \"xdbURL\": \"https://example.com/exploit\",\n+                   \"dateAdded\": \"2023-10-01T00:00:00Z\",\n+                   \"exploitType\": \"local\",\n+                   \"cloneSSHURL\": \"git@example.com:exploit.git\"\n+                }\n+             ]\n+          }\n+        }\n+      ]\n+    }\n+  }\n+}\n\\ No newline at end of file\ndiff --git a/tui/tui.go b/tui/tui.go\nindex 4407f56..80afe95 100644\n--- a/tui/tui.go\n+++ b/tui/tui.go\n@@ -812,16 +812,6 @@ func setChangelogLayout(g *gocui.Gui) error {\n \t\t\t}\n \t\t}\n \n-\t\tif len(vinfo.AlertDict.CISA) &amp;gt; 0 {\n-\t\t\tlines = append(lines, \"\\n\",\n-\t\t\t\t\"CISA Alert\",\n-\t\t\t\t\"===========\",\n-\t\t\t)\n-\t\t\tfor _, alert := range vinfo.AlertDict.CISA {\n-\t\t\t\tlines = append(lines, fmt.Sprintf(\"* [%s](%s)\", alert.Title, alert.URL))\n-\t\t\t}\n-\t\t}\n-\n \t\tif len(vinfo.AlertDict.USCERT) &amp;gt; 0 {\n \t\t\tlines = append(lines, \"\\n\",\n \t\t\t\t\"USCERT Alert\",\n", "creation_timestamp": "2026-07-04T00:01:07.410345Z"}</content>
    <link href="https://vulnerability.circl.lu/sighting/c9bec138-3fd2-46c7-b1f2-43a14e453d4c/export"/>
    <published>2026-07-04T00:01:07.410345+00:00</published>
  </entry>
  <entry>
    <id>https://vulnerability.circl.lu/sighting/39f2e628-a76a-4bc9-96d2-a0868d5f3fa4/export</id>
    <title>39f2e628-a76a-4bc9-96d2-a0868d5f3fa4</title>
    <updated>2026-07-04T09:01:48.698163+00:00</updated>
    <author>
      <name>Automation user</name>
      <uri>https://cvepremium.circl.lu/user/automation</uri>
    </author>
    <content>{"uuid": "39f2e628-a76a-4bc9-96d2-a0868d5f3fa4", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-0000-0000", "type": "seen", "source": "https://gist.github.com/tu-trinh-scale/40a238ab0abdb443a194769a3efdd184", "content": "diff --git a/README.md b/README.md\nindex 57102d1..1a65354 100644\n--- a/README.md\n+++ b/README.md\n@@ -22,6 +22,36 @@ Twitter: [@vuls_en](https://twitter.com/vuls_en)\n \n ----\n \n+## Example configuration\n+\n+Here is an example `config.toml` with KEV (Known Exploited Vulnerabilities) reporting enabled:\n+\n+```toml\n+[cveDict]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/cve.sqlite3\"\n+\n+[ovalDict]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/oval.sqlite3\"\n+\n+[gost]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/gost.sqlite3\"\n+\n+[kev]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/kev.sqlite3\"\n+\n+[servers]\n+\n+[servers.example-host]\n+host         = \"192.168.0.1\"\n+port         = \"22\"\n+user         = \"example-user\"\n+keyPath      = \"/home/example-user/.ssh/id_rsa\"\n+```\n+\n ## Abstract\n \n For a system administrator, having to perform security vulnerability analysis and software update on a daily basis can be a burden.\ndiff --git a/config.toml.example b/config.toml.example\nnew file mode 100644\nindex 0000000..c44f894\n--- /dev/null\n+++ b/config.toml.example\n@@ -0,0 +1,23 @@\n+[cveDict]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/cve.sqlite3\"\n+\n+[ovalDict]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/oval.sqlite3\"\n+\n+[gost]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/gost.sqlite3\"\n+\n+[kev]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/kev.sqlite3\"\n+\n+[servers]\n+\n+[servers.example-host]\n+host         = \"192.168.0.1\"\n+port         = \"22\"\n+user         = \"example-user\"\n+keyPath      = \"/home/example-user/.ssh/id_rsa\"\ndiff --git a/detector/kevuln.go b/detector/kevuln.go\nindex 41afdfe..06294c6 100644\n--- a/detector/kevuln.go\n+++ b/detector/kevuln.go\n@@ -79,18 +79,31 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging\n \t\t\t\treturn err\n \t\t\t}\n \n-\t\t\talerts := []models.Alert{}\n-\t\t\tif len(kevulns) &amp;gt; 0 {\n-\t\t\t\talerts = append(alerts, models.Alert{\n-\t\t\t\t\tTitle: \"Known Exploited Vulnerabilities Catalog\",\n-\t\t\t\t\tURL:   \"https://www.cisa.gov/known-exploited-vulnerabilities-catalog\",\n-\t\t\t\t\tTeam:  \"cisa\",\n+\t\t\tkevs := []models.KEV{}\n+\t\t\tfor _, k := range kevulns {\n+\t\t\t\tvar dueDate *time.Time\n+\t\t\t\tif !k.DueDate.IsZero() {\n+\t\t\t\t\tdueDate = &amp;amp;k.DueDate\n+\t\t\t\t}\n+\t\t\t\tkevs = append(kevs, models.KEV{\n+\t\t\t\t\tType:                       models.CISAKEVType,\n+\t\t\t\t\tVendorProject:              k.VendorProject,\n+\t\t\t\t\tProduct:                    k.Product,\n+\t\t\t\t\tVulnerabilityName:          k.VulnerabilityName,\n+\t\t\t\t\tShortDescription:           k.ShortDescription,\n+\t\t\t\t\tRequiredAction:             k.RequiredAction,\n+\t\t\t\t\tKnownRansomwareCampaignUse: k.KnownRansomwareCampaignUse,\n+\t\t\t\t\tDateAdded:                  k.DateAdded,\n+\t\t\t\t\tDueDate:                    dueDate,\n+\t\t\t\t\tCISA: &amp;amp;models.CISAKEV{\n+\t\t\t\t\t\tNote: k.Notes,\n+\t\t\t\t\t},\n \t\t\t\t})\n \t\t\t}\n \n \t\t\tv, ok := r.ScannedCves[res.request.cveID]\n-\t\t\tif ok {\n-\t\t\t\tv.AlertDict.CISA = alerts\n+\t\t\tif ok &amp;amp;&amp;amp; len(kevs) &amp;gt; 0 {\n+\t\t\t\tv.KEVs = append(v.KEVs, kevs...)\n \t\t\t\tnKEV++\n \t\t\t}\n \t\t\tr.ScannedCves[res.request.cveID] = v\n@@ -108,17 +121,31 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging\n \t\t\t\tcontinue\n \t\t\t}\n \n-\t\t\talerts := []models.Alert{}\n-\t\t\tif len(kevulns) &amp;gt; 0 {\n-\t\t\t\talerts = append(alerts, models.Alert{\n-\t\t\t\t\tTitle: \"Known Exploited Vulnerabilities Catalog\",\n-\t\t\t\t\tURL:   \"https://www.cisa.gov/known-exploited-vulnerabilities-catalog\",\n-\t\t\t\t\tTeam:  \"cisa\",\n+\t\t\tkevs := []models.KEV{}\n+\t\t\tfor _, k := range kevulns {\n+\t\t\t\tvar dueDate *time.Time\n+\t\t\t\tif !k.DueDate.IsZero() {\n+\t\t\t\t\tdueDate = &amp;amp;k.DueDate\n+\t\t\t\t}\n+\t\t\t\tkevs = append(kevs, models.KEV{\n+\t\t\t\t\tType:                       models.CISAKEVType,\n+\t\t\t\t\tVendorProject:              k.VendorProject,\n+\t\t\t\t\tProduct:                    k.Product,\n+\t\t\t\t\tVulnerabilityName:          k.VulnerabilityName,\n+\t\t\t\t\tShortDescription:           k.ShortDescription,\n+\t\t\t\t\tRequiredAction:             k.RequiredAction,\n+\t\t\t\t\tKnownRansomwareCampaignUse: k.KnownRansomwareCampaignUse,\n+\t\t\t\t\tDateAdded:                  k.DateAdded,\n+\t\t\t\t\tDueDate:                    dueDate,\n+\t\t\t\t\tCISA: &amp;amp;models.CISAKEV{\n+\t\t\t\t\t\tNote: k.Notes,\n+\t\t\t\t\t},\n \t\t\t\t})\n \t\t\t}\n-\n-\t\t\tvuln.AlertDict.CISA = alerts\n-\t\t\tnKEV++\n+\t\t\tif len(kevs) &amp;gt; 0 {\n+\t\t\t\tvuln.KEVs = append(vuln.KEVs, kevs...)\n+\t\t\t\tnKEV++\n+\t\t\t}\n \t\t\tr.ScannedCves[cveID] = vuln\n \t\t}\n \t}\ndiff --git a/docs/sample_kev_scan.json b/docs/sample_kev_scan.json\nnew file mode 100644\nindex 0000000..9952ac2\n--- /dev/null\n+++ b/docs/sample_kev_scan.json\n@@ -0,0 +1,61 @@\n+{\n+  \"jsonVersion\": 1,\n+  \"lang\": \"en\",\n+  \"serverUUID\": \"dummy-uuid\",\n+  \"serverName\": \"example-host\",\n+  \"family\": \"ubuntu\",\n+  \"release\": \"20.04\",\n+  \"scannedAt\": \"2023-10-01T00:00:00Z\",\n+  \"scanMode\": \"fast\",\n+  \"scannedVersion\": \"v0.0.0\",\n+  \"scannedRevision\": \"hash\",\n+  \"scannedBy\": \"user\",\n+  \"scannedVia\": \"local\",\n+  \"reportedAt\": \"2023-10-01T00:01:00Z\",\n+  \"reportedVersion\": \"v0.0.0\",\n+  \"reportedRevision\": \"hash\",\n+  \"reportedBy\": \"user\",\n+  \"errors\": [],\n+  \"warnings\": [],\n+  \"scannedCves\": {\n+    \"CVE-0000-0000\": {\n+      \"cveID\": \"CVE-0000-0000\",\n+      \"kevs\": [\n+        {\n+          \"type\": \"cisa\",\n+          \"vulnerabilityName\": \"Test Vulnerability Name\",\n+          \"shortDescription\": \"Test Short Description\",\n+          \"requiredAction\": \"Apply updates per vendor instructions.\",\n+          \"dateAdded\": \"2021-11-03T00:00:00Z\",\n+          \"dueDate\": \"2021-11-17T00:00:00Z\",\n+          \"cisa\": {\n+            \"note\": \"Test CISA Note\"\n+          }\n+        },\n+        {\n+          \"type\": \"vulncheck\",\n+          \"vulnerabilityName\": \"Test VulnCheck Name\",\n+          \"vulncheck\": {\n+            \"xdb\": [\n+              {\n+                \"xdbID\": \"test-xdb-1\",\n+                \"xdbURL\": \"https://example.com/test\",\n+                \"dateAdded\": \"2021-11-03T00:00:00Z\"\n+              }\n+            ]\n+          }\n+        }\n+      ]\n+    }\n+  },\n+  \"runningKernel\": {\n+    \"release\": \"5.4.0-generic\",\n+    \"version\": \"1\",\n+    \"rebootRequired\": false\n+  },\n+  \"packages\": {},\n+  \"config\": {\n+    \"scan\": {},\n+    \"report\": {}\n+  }\n+}\n\\ No newline at end of file\ndiff --git a/models/scanresults.go b/models/scanresults.go\nindex 508b992..4b059ca 100644\n--- a/models/scanresults.go\n+++ b/models/scanresults.go\n@@ -197,7 +197,7 @@ func (r ScanResult) FormatTextReportHeader() string {\n \t\tpkgs = fmt.Sprintf(\"%s, %d libs\", pkgs, r.LibraryScanners.Total())\n \t}\n \n-\treturn fmt.Sprintf(\"%s\\n%s\\n%s\\n%s, %s, %s, %s\\n%s\\n\",\n+\treturn fmt.Sprintf(\"%s\\n%s\\n%s\\n%s, %s, %s, %s, %s\\n%s\\n\",\n \t\tr.ServerInfo(),\n \t\tbuf.String(),\n \t\tr.ScannedCves.FormatCveSummary(),\n@@ -205,6 +205,7 @@ func (r ScanResult) FormatTextReportHeader() string {\n \t\tr.FormatExploitCveSummary(),\n \t\tr.FormatMetasploitCveSummary(),\n \t\tr.FormatAlertSummary(),\n+\t\tr.FormatKEVCveSummary(),\n \t\tpkgs)\n }\n \n@@ -229,6 +230,17 @@ func (r ScanResult) FormatUpdatablePkgsSummary() string {\n \t\tnUpdatable)\n }\n \n+// FormatKEVCveSummary returns a summary of KEV cves\n+func (r ScanResult) FormatKEVCveSummary() string {\n+\tnKEVCve := 0\n+\tfor _, vuln := range r.ScannedCves {\n+\t\tif 0 &amp;lt; len(vuln.KEVs) {\n+\t\t\tnKEVCve++\n+\t\t}\n+\t}\n+\treturn fmt.Sprintf(\"%d kevs\", nKEVCve)\n+}\n+\n // FormatExploitCveSummary returns a summary of exploit cve\n func (r ScanResult) FormatExploitCveSummary() string {\n \tnExploitCve := 0\n@@ -253,13 +265,9 @@ func (r ScanResult) FormatMetasploitCveSummary() string {\n \n // FormatAlertSummary returns a summary of CERT alerts\n func (r ScanResult) FormatAlertSummary() string {\n-\tcisaCnt := 0\n \tuscertCnt := 0\n \tjpcertCnt := 0\n \tfor _, vuln := range r.ScannedCves {\n-\t\tif len(vuln.AlertDict.CISA) &amp;gt; 0 {\n-\t\t\tcisaCnt += len(vuln.AlertDict.CISA)\n-\t\t}\n \t\tif len(vuln.AlertDict.USCERT) &amp;gt; 0 {\n \t\t\tuscertCnt += len(vuln.AlertDict.USCERT)\n \t\t}\n@@ -267,7 +275,7 @@ func (r ScanResult) FormatAlertSummary() string {\n \t\t\tjpcertCnt += len(vuln.AlertDict.JPCERT)\n \t\t}\n \t}\n-\treturn fmt.Sprintf(\"cisa: %d, uscert: %d, jpcert: %d alerts\", cisaCnt, uscertCnt, jpcertCnt)\n+\treturn fmt.Sprintf(\"uscert: %d, jpcert: %d alerts\", uscertCnt, jpcertCnt)\n }\n \n func (r ScanResult) isDisplayUpdatableNum(mode config.ScanMode) bool {\n@@ -434,9 +442,14 @@ func (r *ScanResult) SortForJSONOutput() {\n \t\tsort.Slice(v.AlertDict.JPCERT, func(i, j int) bool {\n \t\t\treturn v.AlertDict.JPCERT[i].Title &amp;lt; v.AlertDict.JPCERT[j].Title\n \t\t})\n-\t\tsort.Slice(v.AlertDict.CISA, func(i, j int) bool {\n-\t\t\treturn v.AlertDict.CISA[i].Title &amp;lt; v.AlertDict.CISA[j].Title\n+\t\t\n+\t\tsort.Slice(v.KEVs, func(i, j int) bool {\n+\t\t\tif v.KEVs[i].Type != v.KEVs[j].Type {\n+\t\t\t\treturn v.KEVs[i].Type &amp;lt; v.KEVs[j].Type\n+\t\t\t}\n+\t\t\treturn v.KEVs[i].VulnerabilityName &amp;lt; v.KEVs[j].VulnerabilityName\n \t\t})\n+\n \t\tr.ScannedCves[k] = v\n \t}\n }\ndiff --git a/models/scanresults_test.go b/models/scanresults_test.go\nindex d429281..8e44906 100644\n--- a/models/scanresults_test.go\n+++ b/models/scanresults_test.go\n@@ -225,10 +225,10 @@ func TestScanResult_Sort(t *testing.T) {\n \t\t\t\t\t\t\t\t{Title: \"a\"},\n \t\t\t\t\t\t\t\t{Title: \"b\"},\n \t\t\t\t\t\t\t},\n-\t\t\t\t\t\t\tCISA: []Alert{\n-\t\t\t\t\t\t\t\t{Title: \"a\"},\n-\t\t\t\t\t\t\t\t{Title: \"b\"},\n-\t\t\t\t\t\t\t},\n+\t\t\t\t\t\t},\n+\t\t\t\t\t\tKEVs: []KEV{\n+\t\t\t\t\t\t\t{Type: \"cisa\", VulnerabilityName: \"a\"},\n+\t\t\t\t\t\t\t{Type: \"cisa\", VulnerabilityName: \"b\"},\n \t\t\t\t\t\t},\n \t\t\t\t\t},\n \t\t\t\t},\n@@ -288,10 +288,10 @@ func TestScanResult_Sort(t *testing.T) {\n \t\t\t\t\t\t\t\t{Title: \"a\"},\n \t\t\t\t\t\t\t\t{Title: \"b\"},\n \t\t\t\t\t\t\t},\n-\t\t\t\t\t\t\tCISA: []Alert{\n-\t\t\t\t\t\t\t\t{Title: \"a\"},\n-\t\t\t\t\t\t\t\t{Title: \"b\"},\n-\t\t\t\t\t\t\t},\n+\t\t\t\t\t\t},\n+\t\t\t\t\t\tKEVs: []KEV{\n+\t\t\t\t\t\t\t{Type: \"cisa\", VulnerabilityName: \"a\"},\n+\t\t\t\t\t\t\t{Type: \"cisa\", VulnerabilityName: \"b\"},\n \t\t\t\t\t\t},\n \t\t\t\t\t},\n \t\t\t\t},\n@@ -354,10 +354,10 @@ func TestScanResult_Sort(t *testing.T) {\n \t\t\t\t\t\t\t\t{Title: \"b\"},\n \t\t\t\t\t\t\t\t{Title: \"a\"},\n \t\t\t\t\t\t\t},\n-\t\t\t\t\t\t\tCISA: []Alert{\n-\t\t\t\t\t\t\t\t{Title: \"b\"},\n-\t\t\t\t\t\t\t\t{Title: \"a\"},\n-\t\t\t\t\t\t\t},\n+\t\t\t\t\t\t},\n+\t\t\t\t\t\tKEVs: []KEV{\n+\t\t\t\t\t\t\t{Type: \"cisa\", VulnerabilityName: \"b\"},\n+\t\t\t\t\t\t\t{Type: \"cisa\", VulnerabilityName: \"a\"},\n \t\t\t\t\t\t},\n \t\t\t\t\t},\n \t\t\t\t},\n@@ -417,10 +417,10 @@ func TestScanResult_Sort(t *testing.T) {\n \t\t\t\t\t\t\t\t{Title: \"a\"},\n \t\t\t\t\t\t\t\t{Title: \"b\"},\n \t\t\t\t\t\t\t},\n-\t\t\t\t\t\t\tCISA: []Alert{\n-\t\t\t\t\t\t\t\t{Title: \"a\"},\n-\t\t\t\t\t\t\t\t{Title: \"b\"},\n-\t\t\t\t\t\t\t},\n+\t\t\t\t\t\t},\n+\t\t\t\t\t\tKEVs: []KEV{\n+\t\t\t\t\t\t\t{Type: \"cisa\", VulnerabilityName: \"a\"},\n+\t\t\t\t\t\t\t{Type: \"cisa\", VulnerabilityName: \"b\"},\n \t\t\t\t\t\t},\n \t\t\t\t\t},\n \t\t\t\t},\ndiff --git a/models/vulninfos.go b/models/vulninfos.go\nindex 3e85e81..b6bd4d2 100644\n--- a/models/vulninfos.go\n+++ b/models/vulninfos.go\n@@ -268,6 +268,7 @@ type VulnInfo struct {\n \tMitigations          []Mitigation         `json:\"mitigations,omitempty\"`\n \tCtis                 []string             `json:\"ctis,omitempty\"`\n \tAlertDict            AlertDict            `json:\"alertDict,omitempty\"`\n+\tKEVs                 []KEV                `json:\"kevs,omitempty\"`\n \tCpeURIs              []string             `json:\"cpeURIs,omitempty\"` // CpeURIs related to this CVE defined in config.toml\n \tGitHubSecurityAlerts GitHubSecurityAlerts `json:\"gitHubSecurityAlerts,omitempty\"`\n \tWpPackageFixStats    WpPackageFixStats    `json:\"wpPackageFixStats,omitempty\"`\n@@ -910,24 +911,72 @@ type Mitigation struct {\n \tURL            string         `json:\"url,omitempty\"`\n }\n \n-// AlertDict has target cve JPCERT, USCERT and CISA alert data\n+// KEVType represents the source of the KEV\n+type KEVType string\n+\n+const (\n+\t// CISAKEVType is for CISA KEV\n+\tCISAKEVType KEVType = \"cisa\"\n+\t// VulnCheckKEVType is for VulnCheck KEV\n+\tVulnCheckKEVType KEVType = \"vulncheck\"\n+)\n+\n+// KEV has KEV information\n+type KEV struct {\n+\tType                       KEVType    `json:\"type\"`\n+\tVendorProject              string     `json:\"vendorProject,omitempty\"`\n+\tProduct                    string     `json:\"product,omitempty\"`\n+\tVulnerabilityName          string     `json:\"vulnerabilityName,omitempty\"`\n+\tShortDescription           string     `json:\"shortDescription,omitempty\"`\n+\tRequiredAction             string     `json:\"requiredAction,omitempty\"`\n+\tKnownRansomwareCampaignUse string     `json:\"knownRansomwareCampaignUse,omitempty\"`\n+\tDateAdded                  time.Time  `json:\"dateAdded,omitempty\"`\n+\tDueDate                    *time.Time `json:\"dueDate,omitempty\"`\n+\n+\tCISA      *CISAKEV      `json:\"cisa,omitempty\"`\n+\tVulnCheck *VulnCheckKEV `json:\"vulncheck,omitempty\"`\n+}\n+\n+// CISAKEV has CISA specific KEV information\n+type CISAKEV struct {\n+\tNote string `json:\"note,omitempty\"`\n+}\n+\n+// VulnCheckKEV has VulnCheck specific KEV information\n+type VulnCheckKEV struct {\n+\tXDB                  []VulnCheckXDB                  `json:\"xdb,omitempty\"`\n+\tReportedExploitation []VulnCheckReportedExploitation `json:\"reportedExploitation,omitempty\"`\n+}\n+\n+// VulnCheckXDB has VulnCheck XDB information\n+type VulnCheckXDB struct {\n+\tXDBID         string    `json:\"xdbID,omitempty\"`\n+\tXDBURL        string    `json:\"xdbURL,omitempty\"`\n+\tDateAdded     time.Time `json:\"dateAdded,omitempty\"`\n+\tExploitType   string    `json:\"exploitType,omitempty\"`\n+\tCloneSSHURL   string    `json:\"cloneSSHURL,omitempty\"`\n+}\n+\n+// VulnCheckReportedExploitation has VulnCheck reported exploitation information\n+type VulnCheckReportedExploitation struct {\n+\tURL       string    `json:\"url,omitempty\"`\n+\tDateAdded time.Time `json:\"dateAdded,omitempty\"`\n+}\n+\n+// AlertDict has target cve JPCERT, USCERT alert data\n type AlertDict struct {\n-\tCISA   []Alert `json:\"cisa\"`\n \tJPCERT []Alert `json:\"jpcert\"`\n \tUSCERT []Alert `json:\"uscert\"`\n }\n \n // IsEmpty checks if the content of AlertDict is empty\n func (a AlertDict) IsEmpty() bool {\n-\treturn len(a.CISA) == 0 &amp;amp;&amp;amp; len(a.JPCERT) == 0 &amp;amp;&amp;amp; len(a.USCERT) == 0\n+\treturn len(a.JPCERT) == 0 &amp;amp;&amp;amp; len(a.USCERT) == 0\n }\n \n // FormatSource returns which source has this alert\n func (a AlertDict) FormatSource() string {\n \tvar s []string\n-\tif len(a.CISA) != 0 {\n-\t\ts = append(s, \"CISA\")\n-\t}\n \tif len(a.USCERT) != 0 || len(a.JPCERT) != 0 {\n \t\ts = append(s, \"CERT\")\n \t}\ndiff --git a/reporter/util.go b/reporter/util.go\nindex d9cfdaa..5b8697f 100644\n--- a/reporter/util.go\n+++ b/reporter/util.go\n@@ -205,6 +205,7 @@ func formatOneLineSummary(rs ...models.ScanResult) string {\n \t\t\t\tr.FormatExploitCveSummary(),\n \t\t\t\tr.FormatMetasploitCveSummary(),\n \t\t\t\tr.FormatAlertSummary(),\n+\t\t\t\tr.FormatKEVCveSummary(),\n \t\t\t}\n \t\t} else {\n \t\t\tcols = []interface{}{\n@@ -565,10 +566,6 @@ No CVE-IDs are found in updatable packages.\n \t\t})\n \t\tdata = append(data, ds...)\n \n-\t\tfor _, alert := range vuln.AlertDict.CISA {\n-\t\t\tdata = append(data, []string{\"CISA Alert\", alert.URL})\n-\t\t}\n-\n \t\tfor _, alert := range vuln.AlertDict.JPCERT {\n \t\t\tdata = append(data, []string{\"JPCERT Alert\", alert.URL})\n \t\t}\ndiff --git a/testkev.go b/testkev.go\nnew file mode 100644\nindex 0000000..064c3c9\n--- /dev/null\n+++ b/testkev.go\n@@ -0,0 +1,15 @@\n+package main\n+\n+import (\n+\t\"fmt\"\n+\t\"reflect\"\n+\t\"github.com/vulsio/go-kev/models\"\n+)\n+\n+func main() {\n+\tt := reflect.TypeOf(models.KEVuln{})\n+\tfor i := 0; i &amp;lt; t.NumField(); i++ {\n+\t\tfield := t.Field(i)\n+\t\tfmt.Printf(\"%s %s\\n\", field.Name, field.Type)\n+\t}\n+}\n\\ No newline at end of file\ndiff --git a/tracking_issue_example.md b/tracking_issue_example.md\nnew file mode 100644\nindex 0000000..b09fe6a\n--- /dev/null\n+++ b/tracking_issue_example.md\n@@ -0,0 +1,32 @@\n+# Tracking Issue Example for KEV Promotion\n+\n+Here is a redacted sample JSON payload showing the new `kevs` field replacing the old `AlertDict.CISA` field:\n+\n+```json\n+{\n+  \"jsonVersion\": 1,\n+  \"lang\": \"en\",\n+  \"serverUUID\": \"dummy-uuid\",\n+  \"serverName\": \"example-host\",\n+  \"family\": \"ubuntu\",\n+  \"release\": \"20.04\",\n+  \"scannedCves\": {\n+    \"CVE-0000-0000\": {\n+      \"cveID\": \"CVE-0000-0000\",\n+      \"kevs\": [\n+        {\n+          \"type\": \"cisa\",\n+          \"vulnerabilityName\": \"Test Vulnerability Name\",\n+          \"shortDescription\": \"Test Short Description\",\n+          \"requiredAction\": \"Apply updates per vendor instructions.\",\n+          \"dateAdded\": \"2021-11-03T00:00:00Z\",\n+          \"dueDate\": \"2021-11-17T00:00:00Z\",\n+          \"cisa\": {\n+            \"note\": \"Test CISA Note\"\n+          }\n+        }\n+      ]\n+    }\n+  }\n+}\n+```\n\\ No newline at end of file\ndiff --git a/tui/tui.go b/tui/tui.go\nindex 4407f56..30dac8c 100644\n--- a/tui/tui.go\n+++ b/tui/tui.go\n@@ -812,13 +812,13 @@ func setChangelogLayout(g *gocui.Gui) error {\n \t\t\t}\n \t\t}\n \n-\t\tif len(vinfo.AlertDict.CISA) &amp;gt; 0 {\n+\t\tif len(vinfo.KEVs) &amp;gt; 0 {\n \t\t\tlines = append(lines, \"\\n\",\n-\t\t\t\t\"CISA Alert\",\n-\t\t\t\t\"===========\",\n+\t\t\t\t\"Known Exploited Vulnerabilities\",\n+\t\t\t\t\"===============================\",\n \t\t\t)\n-\t\t\tfor _, alert := range vinfo.AlertDict.CISA {\n-\t\t\t\tlines = append(lines, fmt.Sprintf(\"* [%s](%s)\", alert.Title, alert.URL))\n+\t\t\tfor _, kev := range vinfo.KEVs {\n+\t\t\t\tlines = append(lines, fmt.Sprintf(\"* [%s] %s: %s\", kev.Type, kev.VulnerabilityName, kev.ShortDescription))\n \t\t\t}\n \t\t}\n \n", "creation_timestamp": "2026-07-04T00:01:07.364761Z"}</content>
    <link href="https://vulnerability.circl.lu/sighting/39f2e628-a76a-4bc9-96d2-a0868d5f3fa4/export"/>
    <published>2026-07-04T00:01:07.364761+00:00</published>
  </entry>
  <entry>
    <id>https://vulnerability.circl.lu/sighting/41def01e-757f-4588-ba5f-100efea3df56/export</id>
    <title>41def01e-757f-4588-ba5f-100efea3df56</title>
    <updated>2026-07-04T09:01:48.698618+00:00</updated>
    <author>
      <name>Automation user</name>
      <uri>https://cvepremium.circl.lu/user/automation</uri>
    </author>
    <content>{"uuid": "41def01e-757f-4588-ba5f-100efea3df56", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-0000-0000", "type": "seen", "source": "https://gist.github.com/tu-trinh-scale/f28f61dfebac866cd95125fe0ed35450", "content": "diff --git a/README.md b/README.md\nindex 57102d1..9d9a9aa 100644\n--- a/README.md\n+++ b/README.md\n@@ -181,6 +181,13 @@ For more information such as Installation, Tutorial, Usage, visit [vuls.io](http\n \n ----\n \n+## Configuration Example\n+\n+An example configuration file is available at [config.toml.example](config.toml.example).\n+A sample scan result with KEV fields is available at [sample-scan-result.json](sample-scan-result.json).\n+\n+----\n+\n ## Authors\n \n kotakanbe ([@kotakanbe](https://twitter.com/kotakanbe)) created vuls and [these fine people](https://github.com/future-architect/vuls/graphs/contributors) have contributed.\ndiff --git a/config.toml.example b/config.toml.example\nnew file mode 100644\nindex 0000000..87b510e\n--- /dev/null\n+++ b/config.toml.example\n@@ -0,0 +1,31 @@\n+[cveDict]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/cve.sqlite3\"\n+\n+[ovalDict]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/oval.sqlite3\"\n+\n+[gost]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/gost.sqlite3\"\n+\n+[exploit]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/go-exploitdb.sqlite3\"\n+\n+[msf]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/go-msfdb.sqlite3\"\n+\n+[kev]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/go-kev.sqlite3\"\n+\n+[servers]\n+\n+[servers.example-host]\n+host = \"192.0.2.1\"\n+port = \"22\"\n+user = \"vuls-user\"\n+keyPath = \"/path/to/id_rsa\"\ndiff --git a/detector/kevuln.go b/detector/kevuln.go\nindex 41afdfe..9f612f6 100644\n--- a/detector/kevuln.go\n+++ b/detector/kevuln.go\n@@ -79,21 +79,22 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging\n \t\t\t\treturn err\n \t\t\t}\n \n-\t\t\talerts := []models.Alert{}\n \t\t\tif len(kevulns) &amp;gt; 0 {\n-\t\t\t\talerts = append(alerts, models.Alert{\n-\t\t\t\t\tTitle: \"Known Exploited Vulnerabilities Catalog\",\n-\t\t\t\t\tURL:   \"https://www.cisa.gov/known-exploited-vulnerabilities-catalog\",\n-\t\t\t\t\tTeam:  \"cisa\",\n-\t\t\t\t})\n+\t\t\t\tvar kevs []models.KEV\n+\t\t\t\tfor _, k := range kevulns {\n+\t\t\t\t\tkevs = append(kevs, models.KEV{\n+\t\t\t\t\t\tType:              models.CISAKEVType,\n+\t\t\t\t\t\tVulnerabilityName: k.VulnerabilityName,\n+\t\t\t\t\t\tCISA:              &amp;amp;models.CISAKEV{},\n+\t\t\t\t\t})\n+\t\t\t\t}\n+\t\t\t\tv, ok := r.ScannedCves[res.request.cveID]\n+\t\t\t\tif ok {\n+\t\t\t\t\tv.KEVs = append(v.KEVs, kevs...)\n+\t\t\t\t\tnKEV++\n+\t\t\t\t}\n+\t\t\t\tr.ScannedCves[res.request.cveID] = v\n \t\t\t}\n-\n-\t\t\tv, ok := r.ScannedCves[res.request.cveID]\n-\t\t\tif ok {\n-\t\t\t\tv.AlertDict.CISA = alerts\n-\t\t\t\tnKEV++\n-\t\t\t}\n-\t\t\tr.ScannedCves[res.request.cveID] = v\n \t\t}\n \t} else {\n \t\tfor cveID, vuln := range r.ScannedCves {\n@@ -108,18 +109,19 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging\n \t\t\t\tcontinue\n \t\t\t}\n \n-\t\t\talerts := []models.Alert{}\n \t\t\tif len(kevulns) &amp;gt; 0 {\n-\t\t\t\talerts = append(alerts, models.Alert{\n-\t\t\t\t\tTitle: \"Known Exploited Vulnerabilities Catalog\",\n-\t\t\t\t\tURL:   \"https://www.cisa.gov/known-exploited-vulnerabilities-catalog\",\n-\t\t\t\t\tTeam:  \"cisa\",\n-\t\t\t\t})\n+\t\t\t\tvar kevs []models.KEV\n+\t\t\t\tfor _, k := range kevulns {\n+\t\t\t\t\tkevs = append(kevs, models.KEV{\n+\t\t\t\t\t\tType:              models.CISAKEVType,\n+\t\t\t\t\t\tVulnerabilityName: k.VulnerabilityName,\n+\t\t\t\t\t\tCISA:              &amp;amp;models.CISAKEV{},\n+\t\t\t\t\t})\n+\t\t\t\t}\n+\t\t\t\tvuln.KEVs = append(vuln.KEVs, kevs...)\n+\t\t\t\tnKEV++\n+\t\t\t\tr.ScannedCves[cveID] = vuln\n \t\t\t}\n-\n-\t\t\tvuln.AlertDict.CISA = alerts\n-\t\t\tnKEV++\n-\t\t\tr.ScannedCves[cveID] = vuln\n \t\t}\n \t}\n \ndiff --git a/models/scanresults.go b/models/scanresults.go\nindex 508b992..5a10e6d 100644\n--- a/models/scanresults.go\n+++ b/models/scanresults.go\n@@ -197,7 +197,7 @@ func (r ScanResult) FormatTextReportHeader() string {\n \t\tpkgs = fmt.Sprintf(\"%s, %d libs\", pkgs, r.LibraryScanners.Total())\n \t}\n \n-\treturn fmt.Sprintf(\"%s\\n%s\\n%s\\n%s, %s, %s, %s\\n%s\\n\",\n+\treturn fmt.Sprintf(\"%s\\n%s\\n%s\\n%s, %s, %s, %s, %s\\n%s\\n\",\n \t\tr.ServerInfo(),\n \t\tbuf.String(),\n \t\tr.ScannedCves.FormatCveSummary(),\n@@ -205,6 +205,7 @@ func (r ScanResult) FormatTextReportHeader() string {\n \t\tr.FormatExploitCveSummary(),\n \t\tr.FormatMetasploitCveSummary(),\n \t\tr.FormatAlertSummary(),\n+\t\tr.FormatKEVCveSummary(),\n \t\tpkgs)\n }\n \n@@ -253,13 +254,9 @@ func (r ScanResult) FormatMetasploitCveSummary() string {\n \n // FormatAlertSummary returns a summary of CERT alerts\n func (r ScanResult) FormatAlertSummary() string {\n-\tcisaCnt := 0\n \tuscertCnt := 0\n \tjpcertCnt := 0\n \tfor _, vuln := range r.ScannedCves {\n-\t\tif len(vuln.AlertDict.CISA) &amp;gt; 0 {\n-\t\t\tcisaCnt += len(vuln.AlertDict.CISA)\n-\t\t}\n \t\tif len(vuln.AlertDict.USCERT) &amp;gt; 0 {\n \t\t\tuscertCnt += len(vuln.AlertDict.USCERT)\n \t\t}\n@@ -267,7 +264,18 @@ func (r ScanResult) FormatAlertSummary() string {\n \t\t\tjpcertCnt += len(vuln.AlertDict.JPCERT)\n \t\t}\n \t}\n-\treturn fmt.Sprintf(\"cisa: %d, uscert: %d, jpcert: %d alerts\", cisaCnt, uscertCnt, jpcertCnt)\n+\treturn fmt.Sprintf(\"uscert: %d, jpcert: %d alerts\", uscertCnt, jpcertCnt)\n+}\n+\n+// FormatKEVCveSummary returns a summary of KEV\n+func (r ScanResult) FormatKEVCveSummary() string {\n+\tnKEVCve := 0\n+\tfor _, vuln := range r.ScannedCves {\n+\t\tif 0 &amp;lt; len(vuln.KEVs) {\n+\t\t\tnKEVCve++\n+\t\t}\n+\t}\n+\treturn fmt.Sprintf(\"%d kevs\", nKEVCve)\n }\n \n func (r ScanResult) isDisplayUpdatableNum(mode config.ScanMode) bool {\n@@ -434,8 +442,11 @@ func (r *ScanResult) SortForJSONOutput() {\n \t\tsort.Slice(v.AlertDict.JPCERT, func(i, j int) bool {\n \t\t\treturn v.AlertDict.JPCERT[i].Title &amp;lt; v.AlertDict.JPCERT[j].Title\n \t\t})\n-\t\tsort.Slice(v.AlertDict.CISA, func(i, j int) bool {\n-\t\t\treturn v.AlertDict.CISA[i].Title &amp;lt; v.AlertDict.CISA[j].Title\n+\t\tsort.Slice(v.KEVs, func(i, j int) bool {\n+\t\t\tif v.KEVs[i].Type != v.KEVs[j].Type {\n+\t\t\t\treturn v.KEVs[i].Type &amp;lt; v.KEVs[j].Type\n+\t\t\t}\n+\t\t\treturn v.KEVs[i].VulnerabilityName &amp;lt; v.KEVs[j].VulnerabilityName\n \t\t})\n \t\tr.ScannedCves[k] = v\n \t}\ndiff --git a/models/scanresults_test.go b/models/scanresults_test.go\nindex d429281..47e31ed 100644\n--- a/models/scanresults_test.go\n+++ b/models/scanresults_test.go\n@@ -225,10 +225,11 @@ func TestScanResult_Sort(t *testing.T) {\n \t\t\t\t\t\t\t\t{Title: \"a\"},\n \t\t\t\t\t\t\t\t{Title: \"b\"},\n \t\t\t\t\t\t\t},\n-\t\t\t\t\t\t\tCISA: []Alert{\n-\t\t\t\t\t\t\t\t{Title: \"a\"},\n-\t\t\t\t\t\t\t\t{Title: \"b\"},\n-\t\t\t\t\t\t\t},\n+\t\t\t\t\t\t},\n+\t\t\t\t\t\tKEVs: []KEV{\n+\t\t\t\t\t\t\t{Type: VulnCheckKEVType, VulnerabilityName: \"a\"},\n+\t\t\t\t\t\t\t{Type: CISAKEVType, VulnerabilityName: \"b\"},\n+\t\t\t\t\t\t\t{Type: CISAKEVType, VulnerabilityName: \"a\"},\n \t\t\t\t\t\t},\n \t\t\t\t\t},\n \t\t\t\t},\n@@ -288,10 +289,11 @@ func TestScanResult_Sort(t *testing.T) {\n \t\t\t\t\t\t\t\t{Title: \"a\"},\n \t\t\t\t\t\t\t\t{Title: \"b\"},\n \t\t\t\t\t\t\t},\n-\t\t\t\t\t\t\tCISA: []Alert{\n-\t\t\t\t\t\t\t\t{Title: \"a\"},\n-\t\t\t\t\t\t\t\t{Title: \"b\"},\n-\t\t\t\t\t\t\t},\n+\t\t\t\t\t\t},\n+\t\t\t\t\t\tKEVs: []KEV{\n+\t\t\t\t\t\t\t{Type: CISAKEVType, VulnerabilityName: \"a\"},\n+\t\t\t\t\t\t\t{Type: CISAKEVType, VulnerabilityName: \"b\"},\n+\t\t\t\t\t\t\t{Type: VulnCheckKEVType, VulnerabilityName: \"a\"},\n \t\t\t\t\t\t},\n \t\t\t\t\t},\n \t\t\t\t},\n@@ -354,10 +356,12 @@ func TestScanResult_Sort(t *testing.T) {\n \t\t\t\t\t\t\t\t{Title: \"b\"},\n \t\t\t\t\t\t\t\t{Title: \"a\"},\n \t\t\t\t\t\t\t},\n-\t\t\t\t\t\t\tCISA: []Alert{\n-\t\t\t\t\t\t\t\t{Title: \"b\"},\n-\t\t\t\t\t\t\t\t{Title: \"a\"},\n-\t\t\t\t\t\t\t},\n+\t\t\t\t\t\t},\n+\t\t\t\t\t\tKEVs: []KEV{\n+\t\t\t\t\t\t\t{Type: VulnCheckKEVType, VulnerabilityName: \"b\"},\n+\t\t\t\t\t\t\t{Type: VulnCheckKEVType, VulnerabilityName: \"a\"},\n+\t\t\t\t\t\t\t{Type: CISAKEVType, VulnerabilityName: \"b\"},\n+\t\t\t\t\t\t\t{Type: CISAKEVType, VulnerabilityName: \"a\"},\n \t\t\t\t\t\t},\n \t\t\t\t\t},\n \t\t\t\t},\n@@ -417,10 +421,12 @@ func TestScanResult_Sort(t *testing.T) {\n \t\t\t\t\t\t\t\t{Title: \"a\"},\n \t\t\t\t\t\t\t\t{Title: \"b\"},\n \t\t\t\t\t\t\t},\n-\t\t\t\t\t\t\tCISA: []Alert{\n-\t\t\t\t\t\t\t\t{Title: \"a\"},\n-\t\t\t\t\t\t\t\t{Title: \"b\"},\n-\t\t\t\t\t\t\t},\n+\t\t\t\t\t\t},\n+\t\t\t\t\t\tKEVs: []KEV{\n+\t\t\t\t\t\t\t{Type: CISAKEVType, VulnerabilityName: \"a\"},\n+\t\t\t\t\t\t\t{Type: CISAKEVType, VulnerabilityName: \"b\"},\n+\t\t\t\t\t\t\t{Type: VulnCheckKEVType, VulnerabilityName: \"a\"},\n+\t\t\t\t\t\t\t{Type: VulnCheckKEVType, VulnerabilityName: \"b\"},\n \t\t\t\t\t\t},\n \t\t\t\t\t},\n \t\t\t\t},\ndiff --git a/models/vulninfos.go b/models/vulninfos.go\nindex 3e85e81..628b56d 100644\n--- a/models/vulninfos.go\n+++ b/models/vulninfos.go\n@@ -275,6 +275,59 @@ type VulnInfo struct {\n \tWindowsKBFixedIns    []string             `json:\"windowsKBFixedIns,omitempty\"`\n \tVulnType             string               `json:\"vulnType,omitempty\"`\n \tDiffStatus           DiffStatus           `json:\"diffStatus,omitempty\"`\n+\tKEVs                 []KEV                `json:\"kevs,omitempty\"`\n+}\n+\n+// KEVType is the type of KEV\n+type KEVType string\n+\n+const (\n+\t// CISAKEVType is CISA KEV type\n+\tCISAKEVType KEVType = \"cisa\"\n+\n+\t// VulnCheckKEVType is VulnCheck KEV type\n+\tVulnCheckKEVType KEVType = \"vulncheck\"\n+)\n+\n+// KEV has Known Exploited Vulnerabilities information\n+type KEV struct {\n+\tType                       KEVType        `json:\"type,omitempty\"`\n+\tVendorProject              string         `json:\"vendorProject,omitempty\"`\n+\tProduct                    string         `json:\"product,omitempty\"`\n+\tVulnerabilityName          string         `json:\"vulnerabilityName,omitempty\"`\n+\tShortDescription           string         `json:\"shortDescription,omitempty\"`\n+\tRequiredAction             string         `json:\"requiredAction,omitempty\"`\n+\tKnownRansomwareCampaignUse string         `json:\"knownRansomwareCampaignUse,omitempty\"`\n+\tDateAdded                  time.Time      `json:\"dateAdded,omitempty\"`\n+\tDueDate                    *time.Time     `json:\"dueDate,omitempty\"`\n+\tCISA                       *CISAKEV       `json:\"cisa,omitempty\"`\n+\tVulnCheck                  *VulnCheckKEV  `json:\"vulncheck,omitempty\"`\n+}\n+\n+// CISAKEV has CISA specific KEV information\n+type CISAKEV struct {\n+\tNote string `json:\"note,omitempty\"`\n+}\n+\n+// VulnCheckKEV has VulnCheck specific KEV information\n+type VulnCheckKEV struct {\n+\tXDB                  []VulnCheckXDB                  `json:\"xdb,omitempty\"`\n+\tReportedExploitation []VulnCheckReportedExploitation `json:\"reportedExploitation,omitempty\"`\n+}\n+\n+// VulnCheckXDB has VulnCheck XDB information\n+type VulnCheckXDB struct {\n+\tXDBID       string    `json:\"xdbId,omitempty\"`\n+\tXDBURL      string    `json:\"xdbUrl,omitempty\"`\n+\tDateAdded   time.Time `json:\"dateAdded,omitempty\"`\n+\tExploitType string    `json:\"exploitType,omitempty\"`\n+\tCloneSSHURL string    `json:\"cloneSshUrl,omitempty\"`\n+}\n+\n+// VulnCheckReportedExploitation has VulnCheck ReportedExploitation information\n+type VulnCheckReportedExploitation struct {\n+\tURL       string    `json:\"url,omitempty\"`\n+\tDateAdded time.Time `json:\"dateAdded,omitempty\"`\n }\n \n // Alert has CERT alert information\n@@ -910,24 +963,20 @@ type Mitigation struct {\n \tURL            string         `json:\"url,omitempty\"`\n }\n \n-// AlertDict has target cve JPCERT, USCERT and CISA alert data\n+// AlertDict has target cve JPCERT and USCERT alert data\n type AlertDict struct {\n-\tCISA   []Alert `json:\"cisa\"`\n \tJPCERT []Alert `json:\"jpcert\"`\n \tUSCERT []Alert `json:\"uscert\"`\n }\n \n // IsEmpty checks if the content of AlertDict is empty\n func (a AlertDict) IsEmpty() bool {\n-\treturn len(a.CISA) == 0 &amp;amp;&amp;amp; len(a.JPCERT) == 0 &amp;amp;&amp;amp; len(a.USCERT) == 0\n+\treturn len(a.JPCERT) == 0 &amp;amp;&amp;amp; len(a.USCERT) == 0\n }\n \n // FormatSource returns which source has this alert\n func (a AlertDict) FormatSource() string {\n \tvar s []string\n-\tif len(a.CISA) != 0 {\n-\t\ts = append(s, \"CISA\")\n-\t}\n \tif len(a.USCERT) != 0 || len(a.JPCERT) != 0 {\n \t\ts = append(s, \"CERT\")\n \t}\ndiff --git a/reporter/util.go b/reporter/util.go\nindex d9cfdaa..1a184e6 100644\n--- a/reporter/util.go\n+++ b/reporter/util.go\n@@ -205,6 +205,7 @@ func formatOneLineSummary(rs ...models.ScanResult) string {\n \t\t\t\tr.FormatExploitCveSummary(),\n \t\t\t\tr.FormatMetasploitCveSummary(),\n \t\t\t\tr.FormatAlertSummary(),\n+\t\t\t\tr.FormatKEVCveSummary(),\n \t\t\t}\n \t\t} else {\n \t\t\tcols = []interface{}{\n@@ -565,8 +566,10 @@ No CVE-IDs are found in updatable packages.\n \t\t})\n \t\tdata = append(data, ds...)\n \n-\t\tfor _, alert := range vuln.AlertDict.CISA {\n-\t\t\tdata = append(data, []string{\"CISA Alert\", alert.URL})\n+\t\tfor _, kev := range vuln.KEVs {\n+\t\t\tif kev.Type == models.CISAKEVType {\n+\t\t\t\tdata = append(data, []string{\"CISA KEV\", kev.VulnerabilityName})\n+\t\t\t}\n \t\t}\n \n \t\tfor _, alert := range vuln.AlertDict.JPCERT {\ndiff --git a/sample-scan-result.json b/sample-scan-result.json\nnew file mode 100644\nindex 0000000..3deb308\n--- /dev/null\n+++ b/sample-scan-result.json\n@@ -0,0 +1,60 @@\n+{\n+  \"jsonVersion\": 1,\n+  \"lang\": \"en\",\n+  \"serverUUID\": \"00000000-0000-0000-0000-000000000000\",\n+  \"serverName\": \"example-host\",\n+  \"family\": \"ubuntu\",\n+  \"release\": \"20.04\",\n+  \"scannedAt\": \"2023-01-01T00:00:00Z\",\n+  \"scanMode\": \"fast\",\n+  \"scannedVersion\": \"v0.0.0\",\n+  \"scannedCves\": {\n+    \"CVE-0000-0000\": {\n+      \"cveID\": \"CVE-0000-0000\",\n+      \"kevs\": [\n+        {\n+          \"type\": \"cisa\",\n+          \"vulnerabilityName\": \"Test Vulnerability\",\n+          \"shortDescription\": \"This is a synthetic vulnerability.\",\n+          \"requiredAction\": \"Apply updates.\",\n+          \"dateAdded\": \"2023-01-01T00:00:00Z\",\n+          \"cisa\": {\n+            \"note\": \"Synthetic note\"\n+          }\n+        },\n+        {\n+          \"type\": \"vulncheck\",\n+          \"vulnerabilityName\": \"Test Vulnerability 2\",\n+          \"vulncheck\": {\n+            \"xdb\": [\n+              {\n+                \"xdbId\": \"1234\",\n+                \"xdbUrl\": \"https://example.com/xdb/1234\",\n+                \"dateAdded\": \"2023-01-02T00:00:00Z\",\n+                \"exploitType\": \"local\",\n+                \"cloneSshUrl\": \"git@github.com:example/exploit.git\"\n+              }\n+            ],\n+            \"reportedExploitation\": [\n+              {\n+                \"url\": \"https://example.com/report/1\",\n+                \"dateAdded\": \"2023-01-03T00:00:00Z\"\n+              }\n+            ]\n+          }\n+        }\n+      ]\n+    }\n+  },\n+  \"runningKernel\": {\n+    \"release\": \"5.4.0-100-generic\",\n+    \"version\": \"#113-Ubuntu SMP Thu Feb 3 18:43:29 UTC 2022\",\n+    \"rebootRequired\": false\n+  },\n+  \"packages\": {\n+    \"example-package\": {\n+      \"name\": \"example-package\",\n+      \"version\": \"1.0.0\"\n+    }\n+  }\n+}\ndiff --git a/tui/tui.go b/tui/tui.go\nindex 4407f56..dbbf8ac 100644\n--- a/tui/tui.go\n+++ b/tui/tui.go\n@@ -812,13 +812,13 @@ func setChangelogLayout(g *gocui.Gui) error {\n \t\t\t}\n \t\t}\n \n-\t\tif len(vinfo.AlertDict.CISA) &amp;gt; 0 {\n+\t\tif len(vinfo.KEVs) &amp;gt; 0 {\n \t\t\tlines = append(lines, \"\\n\",\n-\t\t\t\t\"CISA Alert\",\n-\t\t\t\t\"===========\",\n+\t\t\t\t\"KEV\",\n+\t\t\t\t\"===\",\n \t\t\t)\n-\t\t\tfor _, alert := range vinfo.AlertDict.CISA {\n-\t\t\t\tlines = append(lines, fmt.Sprintf(\"* [%s](%s)\", alert.Title, alert.URL))\n+\t\t\tfor _, kev := range vinfo.KEVs {\n+\t\t\t\tlines = append(lines, fmt.Sprintf(\"* [%s] %s\", kev.Type, kev.VulnerabilityName))\n \t\t\t}\n \t\t}\n \n", "creation_timestamp": "2026-07-04T00:01:07.182101Z"}</content>
    <link href="https://vulnerability.circl.lu/sighting/41def01e-757f-4588-ba5f-100efea3df56/export"/>
    <published>2026-07-04T00:01:07.182101+00:00</published>
  </entry>
  <entry>
    <id>https://vulnerability.circl.lu/sighting/c9174058-9bd5-4834-a094-7692fec0001d/export</id>
    <title>c9174058-9bd5-4834-a094-7692fec0001d</title>
    <updated>2026-07-04T09:01:48.698868+00:00</updated>
    <author>
      <name>Automation user</name>
      <uri>https://cvepremium.circl.lu/user/automation</uri>
    </author>
    <content>{"uuid": "c9174058-9bd5-4834-a094-7692fec0001d", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-0000-0000", "type": "seen", "source": "https://gist.github.com/tu-trinh-scale/db3e98ef89fc16b2dcd28b4e7873f4aa", "content": "diff --git a/README.md b/README.md\nindex 57102d1..b92f351 100644\n--- a/README.md\n+++ b/README.md\n@@ -165,6 +165,7 @@ Vuls has some options to detect the vulnerabilities\n   - Auto-detection of servers set using CIDR, generate configuration file template\n - Email and Slack notification is possible (supports Japanese language)\n - Scan result is viewable on accessory software, TUI Viewer in a terminal or Web UI ([VulsRepo](https://github.com/ishiDACo/vulsrepo)).\n+- Example Configuration: See [config.toml.example](config.toml.example) for a ready-to-use template including KEV reporting setup.\n \n ----\n \ndiff --git a/config.toml.example b/config.toml.example\nnew file mode 100644\nindex 0000000..8e4ab21\n--- /dev/null\n+++ b/config.toml.example\n@@ -0,0 +1,11 @@\n+[servers]\n+\n+[servers.example-host]\n+host         = \"192.0.2.10\"\n+port         = \"22\"\n+user         = \"example-user\"\n+keyPath      = \"/path/to/example/key\"\n+\n+[kevuln]\n+type         = \"sqlite3\"\n+SQLite3Path  = \"/path/to/go-kev.sqlite3\"\ndiff --git a/detector/kevuln.go b/detector/kevuln.go\nindex 41afdfe..f0230da 100644\n--- a/detector/kevuln.go\n+++ b/detector/kevuln.go\n@@ -79,18 +79,8 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging\n \t\t\t\treturn err\n \t\t\t}\n \n-\t\t\talerts := []models.Alert{}\n-\t\t\tif len(kevulns) &amp;gt; 0 {\n-\t\t\t\talerts = append(alerts, models.Alert{\n-\t\t\t\t\tTitle: \"Known Exploited Vulnerabilities Catalog\",\n-\t\t\t\t\tURL:   \"https://www.cisa.gov/known-exploited-vulnerabilities-catalog\",\n-\t\t\t\t\tTeam:  \"cisa\",\n-\t\t\t\t})\n-\t\t\t}\n-\n \t\t\tv, ok := r.ScannedCves[res.request.cveID]\n \t\t\tif ok {\n-\t\t\t\tv.AlertDict.CISA = alerts\n \t\t\t\tnKEV++\n \t\t\t}\n \t\t\tr.ScannedCves[res.request.cveID] = v\n@@ -108,16 +98,6 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging\n \t\t\t\tcontinue\n \t\t\t}\n \n-\t\t\talerts := []models.Alert{}\n-\t\t\tif len(kevulns) &amp;gt; 0 {\n-\t\t\t\talerts = append(alerts, models.Alert{\n-\t\t\t\t\tTitle: \"Known Exploited Vulnerabilities Catalog\",\n-\t\t\t\t\tURL:   \"https://www.cisa.gov/known-exploited-vulnerabilities-catalog\",\n-\t\t\t\t\tTeam:  \"cisa\",\n-\t\t\t\t})\n-\t\t\t}\n-\n-\t\t\tvuln.AlertDict.CISA = alerts\n \t\t\tnKEV++\n \t\t\tr.ScannedCves[cveID] = vuln\n \t\t}\ndiff --git a/models/scanresults.go b/models/scanresults.go\nindex 508b992..8139fb5 100644\n--- a/models/scanresults.go\n+++ b/models/scanresults.go\n@@ -197,13 +197,14 @@ func (r ScanResult) FormatTextReportHeader() string {\n \t\tpkgs = fmt.Sprintf(\"%s, %d libs\", pkgs, r.LibraryScanners.Total())\n \t}\n \n-\treturn fmt.Sprintf(\"%s\\n%s\\n%s\\n%s, %s, %s, %s\\n%s\\n\",\n+\treturn fmt.Sprintf(\"%s\\n%s\\n%s\\n%s, %s, %s, %s, %s\\n%s\\n\",\n \t\tr.ServerInfo(),\n \t\tbuf.String(),\n \t\tr.ScannedCves.FormatCveSummary(),\n \t\tr.ScannedCves.FormatFixedStatus(r.Packages),\n \t\tr.FormatExploitCveSummary(),\n \t\tr.FormatMetasploitCveSummary(),\n+\t\tr.FormatKEVCveSummary(),\n \t\tr.FormatAlertSummary(),\n \t\tpkgs)\n }\n@@ -251,15 +252,22 @@ func (r ScanResult) FormatMetasploitCveSummary() string {\n \treturn fmt.Sprintf(\"%d exploits\", nMetasploitCve)\n }\n \n+// FormatKEVCveSummary returns a summary of kev cve\n+func (r ScanResult) FormatKEVCveSummary() string {\n+\tnKevs := 0\n+\tfor _, vuln := range r.ScannedCves {\n+\t\tif len(vuln.KEVs) &amp;gt; 0 {\n+\t\t\tnKevs++\n+\t\t}\n+\t}\n+\treturn fmt.Sprintf(\"%d kevs\", nKevs)\n+}\n+\n // FormatAlertSummary returns a summary of CERT alerts\n func (r ScanResult) FormatAlertSummary() string {\n-\tcisaCnt := 0\n \tuscertCnt := 0\n \tjpcertCnt := 0\n \tfor _, vuln := range r.ScannedCves {\n-\t\tif len(vuln.AlertDict.CISA) &amp;gt; 0 {\n-\t\t\tcisaCnt += len(vuln.AlertDict.CISA)\n-\t\t}\n \t\tif len(vuln.AlertDict.USCERT) &amp;gt; 0 {\n \t\t\tuscertCnt += len(vuln.AlertDict.USCERT)\n \t\t}\n@@ -267,7 +275,7 @@ func (r ScanResult) FormatAlertSummary() string {\n \t\t\tjpcertCnt += len(vuln.AlertDict.JPCERT)\n \t\t}\n \t}\n-\treturn fmt.Sprintf(\"cisa: %d, uscert: %d, jpcert: %d alerts\", cisaCnt, uscertCnt, jpcertCnt)\n+\treturn fmt.Sprintf(\"uscert: %d, jpcert: %d alerts\", uscertCnt, jpcertCnt)\n }\n \n func (r ScanResult) isDisplayUpdatableNum(mode config.ScanMode) bool {\n@@ -434,9 +442,14 @@ func (r *ScanResult) SortForJSONOutput() {\n \t\tsort.Slice(v.AlertDict.JPCERT, func(i, j int) bool {\n \t\t\treturn v.AlertDict.JPCERT[i].Title &amp;lt; v.AlertDict.JPCERT[j].Title\n \t\t})\n-\t\tsort.Slice(v.AlertDict.CISA, func(i, j int) bool {\n-\t\t\treturn v.AlertDict.CISA[i].Title &amp;lt; v.AlertDict.CISA[j].Title\n+\n+\t\tsort.Slice(v.KEVs, func(i, j int) bool {\n+\t\t\tif v.KEVs[i].Type != v.KEVs[j].Type {\n+\t\t\t\treturn v.KEVs[i].Type &amp;lt; v.KEVs[j].Type\n+\t\t\t}\n+\t\t\treturn v.KEVs[i].VulnerabilityName &amp;lt; v.KEVs[j].VulnerabilityName\n \t\t})\n+\n \t\tr.ScannedCves[k] = v\n \t}\n }\ndiff --git a/models/scanresults_test.go b/models/scanresults_test.go\nindex d429281..454eeac 100644\n--- a/models/scanresults_test.go\n+++ b/models/scanresults_test.go\n@@ -225,10 +225,6 @@ func TestScanResult_Sort(t *testing.T) {\n \t\t\t\t\t\t\t\t{Title: \"a\"},\n \t\t\t\t\t\t\t\t{Title: \"b\"},\n \t\t\t\t\t\t\t},\n-\t\t\t\t\t\t\tCISA: []Alert{\n-\t\t\t\t\t\t\t\t{Title: \"a\"},\n-\t\t\t\t\t\t\t\t{Title: \"b\"},\n-\t\t\t\t\t\t\t},\n \t\t\t\t\t\t},\n \t\t\t\t\t},\n \t\t\t\t},\n@@ -288,10 +284,6 @@ func TestScanResult_Sort(t *testing.T) {\n \t\t\t\t\t\t\t\t{Title: \"a\"},\n \t\t\t\t\t\t\t\t{Title: \"b\"},\n \t\t\t\t\t\t\t},\n-\t\t\t\t\t\t\tCISA: []Alert{\n-\t\t\t\t\t\t\t\t{Title: \"a\"},\n-\t\t\t\t\t\t\t\t{Title: \"b\"},\n-\t\t\t\t\t\t\t},\n \t\t\t\t\t\t},\n \t\t\t\t\t},\n \t\t\t\t},\n@@ -354,10 +346,6 @@ func TestScanResult_Sort(t *testing.T) {\n \t\t\t\t\t\t\t\t{Title: \"b\"},\n \t\t\t\t\t\t\t\t{Title: \"a\"},\n \t\t\t\t\t\t\t},\n-\t\t\t\t\t\t\tCISA: []Alert{\n-\t\t\t\t\t\t\t\t{Title: \"b\"},\n-\t\t\t\t\t\t\t\t{Title: \"a\"},\n-\t\t\t\t\t\t\t},\n \t\t\t\t\t\t},\n \t\t\t\t\t},\n \t\t\t\t},\n@@ -417,10 +405,6 @@ func TestScanResult_Sort(t *testing.T) {\n \t\t\t\t\t\t\t\t{Title: \"a\"},\n \t\t\t\t\t\t\t\t{Title: \"b\"},\n \t\t\t\t\t\t\t},\n-\t\t\t\t\t\t\tCISA: []Alert{\n-\t\t\t\t\t\t\t\t{Title: \"a\"},\n-\t\t\t\t\t\t\t\t{Title: \"b\"},\n-\t\t\t\t\t\t\t},\n \t\t\t\t\t\t},\n \t\t\t\t\t},\n \t\t\t\t},\n@@ -535,6 +519,33 @@ func TestScanResult_Sort(t *testing.T) {\n \t\t\t\t},\n \t\t\t},\n \t\t},\n+\t\t{\n+\t\t\tname: \"sort KEV\",\n+\t\t\tfields: fields{\n+\t\t\t\tScannedCves: VulnInfos{\n+\t\t\t\t\t\"CVE-2014-3591\": VulnInfo{\n+\t\t\t\t\t\tKEVs: []KEV{\n+\t\t\t\t\t\t\t{Type: VulnCheckKEVType, VulnerabilityName: \"b\"},\n+\t\t\t\t\t\t\t{Type: CISAKEVType, VulnerabilityName: \"a\"},\n+\t\t\t\t\t\t\t{Type: VulnCheckKEVType, VulnerabilityName: \"a\"},\n+\t\t\t\t\t\t\t{Type: CISAKEVType, VulnerabilityName: \"c\"},\n+\t\t\t\t\t\t},\n+\t\t\t\t\t},\n+\t\t\t\t},\n+\t\t\t},\n+\t\t\texpected: fields{\n+\t\t\t\tScannedCves: VulnInfos{\n+\t\t\t\t\t\"CVE-2014-3591\": VulnInfo{\n+\t\t\t\t\t\tKEVs: []KEV{\n+\t\t\t\t\t\t\t{Type: CISAKEVType, VulnerabilityName: \"a\"},\n+\t\t\t\t\t\t\t{Type: CISAKEVType, VulnerabilityName: \"c\"},\n+\t\t\t\t\t\t\t{Type: VulnCheckKEVType, VulnerabilityName: \"a\"},\n+\t\t\t\t\t\t\t{Type: VulnCheckKEVType, VulnerabilityName: \"b\"},\n+\t\t\t\t\t\t},\n+\t\t\t\t\t},\n+\t\t\t\t},\n+\t\t\t},\n+\t\t},\n \t}\n \tfor _, tt := range tests {\n \t\tt.Run(tt.name, func(t *testing.T) {\ndiff --git a/models/vulninfos.go b/models/vulninfos.go\nindex 3e85e81..355ffba 100644\n--- a/models/vulninfos.go\n+++ b/models/vulninfos.go\n@@ -275,6 +275,56 @@ type VulnInfo struct {\n \tWindowsKBFixedIns    []string             `json:\"windowsKBFixedIns,omitempty\"`\n \tVulnType             string               `json:\"vulnType,omitempty\"`\n \tDiffStatus           DiffStatus           `json:\"diffStatus,omitempty\"`\n+\tKEVs                 []KEV                `json:\"kevs,omitempty\"`\n+}\n+\n+// KEVType string type with constants\n+type KEVType string\n+\n+const (\n+\tCISAKEVType      KEVType = \"cisa\"\n+\tVulnCheckKEVType KEVType = \"vulncheck\"\n+)\n+\n+// KEV struct\n+type KEV struct {\n+\tType                       KEVType                       `json:\"type,omitempty\"`\n+\tVendorProject              string                        `json:\"vendorProject,omitempty\"`\n+\tProduct                    string                        `json:\"product,omitempty\"`\n+\tVulnerabilityName          string                        `json:\"vulnerabilityName,omitempty\"`\n+\tShortDescription           string                        `json:\"shortDescription,omitempty\"`\n+\tRequiredAction             string                        `json:\"requiredAction,omitempty\"`\n+\tKnownRansomwareCampaignUse string                        `json:\"knownRansomwareCampaignUse,omitempty\"`\n+\tDateAdded                  time.Time                     `json:\"dateAdded,omitempty\"`\n+\tDueDate                    *time.Time                    `json:\"dueDate,omitempty\"`\n+\tCISA                       *CISAKEV                      `json:\"cisa,omitempty\"`\n+\tVulnCheck                  *VulnCheckKEV                 `json:\"vulncheck,omitempty\"`\n+}\n+\n+// CISAKEV struct\n+type CISAKEV struct {\n+\tNote string `json:\"note,omitempty\"`\n+}\n+\n+// VulnCheckKEV struct\n+type VulnCheckKEV struct {\n+\tXDB                  []VulnCheckXDB                  `json:\"xdb,omitempty\"`\n+\tReportedExploitation []VulnCheckReportedExploitation `json:\"reportedExploitation,omitempty\"`\n+}\n+\n+// VulnCheckXDB struct\n+type VulnCheckXDB struct {\n+\tXDBID       string    `json:\"xdbid,omitempty\"`\n+\tXDBURL      string    `json:\"xdburl,omitempty\"`\n+\tDateAdded   time.Time `json:\"dateAdded,omitempty\"`\n+\tExploitType string    `json:\"exploitType,omitempty\"`\n+\tCloneSSHURL string    `json:\"cloneSshUrl,omitempty\"`\n+}\n+\n+// VulnCheckReportedExploitation struct\n+type VulnCheckReportedExploitation struct {\n+\tURL       string    `json:\"url,omitempty\"`\n+\tDateAdded time.Time `json:\"dateAdded,omitempty\"`\n }\n \n // Alert has CERT alert information\n@@ -910,24 +960,20 @@ type Mitigation struct {\n \tURL            string         `json:\"url,omitempty\"`\n }\n \n-// AlertDict has target cve JPCERT, USCERT and CISA alert data\n+// AlertDict has target cve JPCERT, USCERT alert data\n type AlertDict struct {\n-\tCISA   []Alert `json:\"cisa\"`\n \tJPCERT []Alert `json:\"jpcert\"`\n \tUSCERT []Alert `json:\"uscert\"`\n }\n \n // IsEmpty checks if the content of AlertDict is empty\n func (a AlertDict) IsEmpty() bool {\n-\treturn len(a.CISA) == 0 &amp;amp;&amp;amp; len(a.JPCERT) == 0 &amp;amp;&amp;amp; len(a.USCERT) == 0\n+\treturn len(a.JPCERT) == 0 &amp;amp;&amp;amp; len(a.USCERT) == 0\n }\n \n // FormatSource returns which source has this alert\n func (a AlertDict) FormatSource() string {\n \tvar s []string\n-\tif len(a.CISA) != 0 {\n-\t\ts = append(s, \"CISA\")\n-\t}\n \tif len(a.USCERT) != 0 || len(a.JPCERT) != 0 {\n \t\ts = append(s, \"CERT\")\n \t}\ndiff --git a/reporter/util.go b/reporter/util.go\nindex d9cfdaa..a2dfbd0 100644\n--- a/reporter/util.go\n+++ b/reporter/util.go\n@@ -204,6 +204,7 @@ func formatOneLineSummary(rs ...models.ScanResult) string {\n \t\t\t\tr.FormatUpdatablePkgsSummary(),\n \t\t\t\tr.FormatExploitCveSummary(),\n \t\t\t\tr.FormatMetasploitCveSummary(),\n+\t\t\t\tr.FormatKEVCveSummary(),\n \t\t\t\tr.FormatAlertSummary(),\n \t\t\t}\n \t\t} else {\n@@ -565,10 +566,6 @@ No CVE-IDs are found in updatable packages.\n \t\t})\n \t\tdata = append(data, ds...)\n \n-\t\tfor _, alert := range vuln.AlertDict.CISA {\n-\t\t\tdata = append(data, []string{\"CISA Alert\", alert.URL})\n-\t\t}\n-\n \t\tfor _, alert := range vuln.AlertDict.JPCERT {\n \t\t\tdata = append(data, []string{\"JPCERT Alert\", alert.URL})\n \t\t}\ndiff --git a/sample_scan_result.json b/sample_scan_result.json\nnew file mode 100644\nindex 0000000..dd4cefc\n--- /dev/null\n+++ b/sample_scan_result.json\n@@ -0,0 +1,48 @@\n+{\n+  \"serverName\": \"example-host\",\n+  \"family\": \"ubuntu\",\n+  \"release\": \"22.04\",\n+  \"scannedCves\": {\n+    \"CVE-0000-0000\": {\n+      \"cveID\": \"CVE-0000-0000\",\n+      \"kevs\": [\n+        {\n+          \"type\": \"cisa\",\n+          \"vendorProject\": \"Example Vendor\",\n+          \"product\": \"Example Product\",\n+          \"vulnerabilityName\": \"Example Vulnerability\",\n+          \"shortDescription\": \"A synthetic example vulnerability.\",\n+          \"requiredAction\": \"Apply updates.\",\n+          \"knownRansomwareCampaignUse\": \"Unknown\",\n+          \"dateAdded\": \"2026-07-01T00:00:00Z\",\n+          \"cisa\": {\n+            \"note\": \"Synthetic CISA note\"\n+          }\n+        },\n+        {\n+          \"type\": \"vulncheck\",\n+          \"vendorProject\": \"Example Vendor\",\n+          \"product\": \"Example Product\",\n+          \"vulnerabilityName\": \"Example Vulnerability\",\n+          \"shortDescription\": \"A synthetic example vulnerability.\",\n+          \"vulncheck\": {\n+            \"xdb\": [\n+              {\n+                \"xdbid\": \"1234\",\n+                \"xdburl\": \"https://example.com/exploit/1234\",\n+                \"dateAdded\": \"2026-07-02T00:00:00Z\",\n+                \"exploitType\": \"RCE\"\n+              }\n+            ],\n+            \"reportedExploitation\": [\n+              {\n+                \"url\": \"https://example.com/report/1\",\n+                \"dateAdded\": \"2026-07-03T00:00:00Z\"\n+              }\n+            ]\n+          }\n+        }\n+      ]\n+    }\n+  }\n+}\n\\ No newline at end of file\ndiff --git a/tui/tui.go b/tui/tui.go\nindex 4407f56..80afe95 100644\n--- a/tui/tui.go\n+++ b/tui/tui.go\n@@ -812,16 +812,6 @@ func setChangelogLayout(g *gocui.Gui) error {\n \t\t\t}\n \t\t}\n \n-\t\tif len(vinfo.AlertDict.CISA) &amp;gt; 0 {\n-\t\t\tlines = append(lines, \"\\n\",\n-\t\t\t\t\"CISA Alert\",\n-\t\t\t\t\"===========\",\n-\t\t\t)\n-\t\t\tfor _, alert := range vinfo.AlertDict.CISA {\n-\t\t\t\tlines = append(lines, fmt.Sprintf(\"* [%s](%s)\", alert.Title, alert.URL))\n-\t\t\t}\n-\t\t}\n-\n \t\tif len(vinfo.AlertDict.USCERT) &amp;gt; 0 {\n \t\t\tlines = append(lines, \"\\n\",\n \t\t\t\t\"USCERT Alert\",\n", "creation_timestamp": "2026-07-04T00:01:06.995836Z"}</content>
    <link href="https://vulnerability.circl.lu/sighting/c9174058-9bd5-4834-a094-7692fec0001d/export"/>
    <published>2026-07-04T00:01:06.995836+00:00</published>
  </entry>
  <entry>
    <id>https://vulnerability.circl.lu/sighting/0f2e2fc9-054b-498e-bfc2-fb34a843daee/export</id>
    <title>0f2e2fc9-054b-498e-bfc2-fb34a843daee</title>
    <updated>2026-07-04T09:01:48.699110+00:00</updated>
    <author>
      <name>Automation user</name>
      <uri>https://cvepremium.circl.lu/user/automation</uri>
    </author>
    <content>{"uuid": "0f2e2fc9-054b-498e-bfc2-fb34a843daee", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-0000-0000", "type": "seen", "source": "https://gist.github.com/tu-trinh-scale/d8a4988827d34dea1ea880cf5c9c8bb8", "content": "diff --git a/README.md b/README.md\nindex 57102d1..14d512c 100644\n--- a/README.md\n+++ b/README.md\n@@ -92,6 +92,7 @@ Vuls is a tool created to solve the problems listed above. It has the following\n \n - CISA(Cybersecurity &amp;amp; Infrastructure Security Agency)\n   - [Known Exploited Vulnerabilities Catalog](https://www.cisa.gov/known-exploited-vulnerabilities-catalog)\n+  - Configure `[kevuln]` to populate first-class KEV data in scan results. See [`examples/config.toml.example`](examples/config.toml.example) and [`examples/scan-result-kev.json`](examples/scan-result-kev.json).\n \n - Cyber Threat Intelligence(MITRE ATT&amp;amp;CK and CAPEC)\n   - [mitre/cti](https://github.com/mitre/cti)\ndiff --git a/detector/kevuln.go b/detector/kevuln.go\nindex 41afdfe..afbd5d6 100644\n--- a/detector/kevuln.go\n+++ b/detector/kevuln.go\n@@ -78,22 +78,16 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging\n \t\t\tif err := json.Unmarshal([]byte(res.json), &amp;amp;kevulns); err != nil {\n \t\t\t\treturn err\n \t\t\t}\n-\n-\t\t\talerts := []models.Alert{}\n-\t\t\tif len(kevulns) &amp;gt; 0 {\n-\t\t\t\talerts = append(alerts, models.Alert{\n-\t\t\t\t\tTitle: \"Known Exploited Vulnerabilities Catalog\",\n-\t\t\t\t\tURL:   \"https://www.cisa.gov/known-exploited-vulnerabilities-catalog\",\n-\t\t\t\t\tTeam:  \"cisa\",\n-\t\t\t\t})\n+\t\t\tif len(kevulns) == 0 {\n+\t\t\t\tcontinue\n \t\t\t}\n \n \t\t\tv, ok := r.ScannedCves[res.request.cveID]\n \t\t\tif ok {\n-\t\t\t\tv.AlertDict.CISA = alerts\n+\t\t\t\tv.KEVs = append(v.KEVs, toKEVs(kevulns)...)\n \t\t\t\tnKEV++\n+\t\t\t\tr.ScannedCves[res.request.cveID] = v\n \t\t\t}\n-\t\t\tr.ScannedCves[res.request.cveID] = v\n \t\t}\n \t} else {\n \t\tfor cveID, vuln := range r.ScannedCves {\n@@ -108,16 +102,7 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging\n \t\t\t\tcontinue\n \t\t\t}\n \n-\t\t\talerts := []models.Alert{}\n-\t\t\tif len(kevulns) &amp;gt; 0 {\n-\t\t\t\talerts = append(alerts, models.Alert{\n-\t\t\t\t\tTitle: \"Known Exploited Vulnerabilities Catalog\",\n-\t\t\t\t\tURL:   \"https://www.cisa.gov/known-exploited-vulnerabilities-catalog\",\n-\t\t\t\t\tTeam:  \"cisa\",\n-\t\t\t\t})\n-\t\t\t}\n-\n-\t\t\tvuln.AlertDict.CISA = alerts\n+\t\t\tvuln.KEVs = append(vuln.KEVs, toKEVs(kevulns)...)\n \t\t\tnKEV++\n \t\t\tr.ScannedCves[cveID] = vuln\n \t\t}\n@@ -127,6 +112,33 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging\n \treturn nil\n }\n \n+func toKEVs(kevulns []kevulnmodels.KEVuln) (kevs []models.KEV) {\n+\tfor _, kevuln := range kevulns {\n+\t\tb, err := json.Marshal(kevuln)\n+\t\tif err != nil {\n+\t\t\tlogging.Log.Debugf(\"Failed to marshal KEVuln. err: %+v\", err)\n+\t\t\tcontinue\n+\t\t}\n+\n+\t\tvar kev models.KEV\n+\t\tif err := json.Unmarshal(b, &amp;amp;kev); err != nil {\n+\t\t\tlogging.Log.Debugf(\"Failed to unmarshal KEVuln into models.KEV. err: %+v\", err)\n+\t\t\tcontinue\n+\t\t}\n+\t\tif kev.Type == \"\" {\n+\t\t\tkev.Type = models.CISAKEVType\n+\t\t}\n+\t\tif kev.Type == models.CISAKEVType &amp;amp;&amp;amp; kev.CISA == nil {\n+\t\t\tkev.CISA = &amp;amp;models.CISAKEV{}\n+\t\t}\n+\t\tif kev.Type == models.VulnCheckKEVType &amp;amp;&amp;amp; kev.VulnCheck == nil {\n+\t\t\tkev.VulnCheck = &amp;amp;models.VulnCheckKEV{}\n+\t\t}\n+\t\tkevs = append(kevs, kev)\n+\t}\n+\treturn kevs\n+}\n+\n type kevulnResponse struct {\n \trequest kevulnRequest\n \tjson    string\ndiff --git a/examples/config.toml.example b/examples/config.toml.example\nnew file mode 100644\nindex 0000000..6e8783f\n--- /dev/null\n+++ b/examples/config.toml.example\n@@ -0,0 +1,49 @@\n+# Synthetic Vuls configuration example.\n+# Replace all placeholder values before use; do not copy secrets from a live config.toml.\n+\n+[default]\n+#port        = \"22\"\n+#user        = \"vuls\"\n+#keyPath     = \"/home/vuls/.ssh/id_rsa\"\n+#scanMode    = [\"fast-root\"]\n+\n+[servers]\n+\n+[servers.example-host]\n+host        = \"192.0.2.10\"\n+port        = \"22\"\n+user        = \"vuls\"\n+keyPath     = \"/home/vuls/.ssh/id_rsa\"\n+scanMode    = [\"fast-root\"]\n+\n+[cveDict]\n+type        = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/cve.sqlite3\"\n+\n+[ovalDict]\n+type        = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/oval.sqlite3\"\n+\n+[gost]\n+type        = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/gost.sqlite3\"\n+\n+[exploit]\n+type        = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/go-exploitdb.sqlite3\"\n+\n+[metasploit]\n+type        = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/go-msfdb.sqlite3\"\n+\n+[kevuln]\n+# Enables CISA and VulnCheck KEV enrichment when the go-kev database contains them.\n+type        = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/go-kev.sqlite3\"\n+# For an HTTP go-kev service, use:\n+#type       = \"http\"\n+#url        = \"http://127.0.0.1:1326\"\n+\n+[cti]\n+type        = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/go-cti.sqlite3\"\ndiff --git a/examples/scan-result-kev.json b/examples/scan-result-kev.json\nnew file mode 100644\nindex 0000000..e4b769b\n--- /dev/null\n+++ b/examples/scan-result-kev.json\n@@ -0,0 +1,61 @@\n+{\n+  \"jsonVersion\": 4,\n+  \"lang\": \"en\",\n+  \"serverUUID\": \"00000000-0000-0000-0000-000000000000\",\n+  \"serverName\": \"example-host\",\n+  \"family\": \"ubuntu\",\n+  \"release\": \"22.04\",\n+  \"scannedAt\": \"2026-07-03T00:00:00Z\",\n+  \"scanMode\": \"fast-root\",\n+  \"scannedCves\": {\n+    \"CVE-0000-0000\": {\n+      \"cveID\": \"CVE-0000-0000\",\n+      \"kevs\": [\n+        {\n+          \"type\": \"cisa\",\n+          \"vendorProject\": \"Example Vendor\",\n+          \"product\": \"Example Product\",\n+          \"vulnerabilityName\": \"Example Product Vulnerability\",\n+          \"shortDescription\": \"Synthetic CISA KEV entry for documentation.\",\n+          \"requiredAction\": \"Apply updates per vendor instructions.\",\n+          \"knownRansomwareCampaignUse\": \"Unknown\",\n+          \"dateAdded\": \"2026-07-01T00:00:00Z\",\n+          \"dueDate\": \"2026-07-22T00:00:00Z\",\n+          \"cisa\": {\n+            \"note\": \"Synthetic example only.\"\n+          }\n+        },\n+        {\n+          \"type\": \"vulncheck\",\n+          \"vendorProject\": \"Example Vendor\",\n+          \"product\": \"Example Product\",\n+          \"vulnerabilityName\": \"Example Product Vulnerability\",\n+          \"shortDescription\": \"Synthetic VulnCheck KEV entry for documentation.\",\n+          \"requiredAction\": \"Review exposure and patch.\",\n+          \"knownRansomwareCampaignUse\": \"Known\",\n+          \"dateAdded\": \"2026-07-02T00:00:00Z\",\n+          \"vulncheck\": {\n+            \"xdb\": [\n+              {\n+                \"xdbID\": \"XDB-000000\",\n+                \"xdbURL\": \"https://example.invalid/xdb/XDB-000000\",\n+                \"dateAdded\": \"2026-07-02T00:00:00Z\",\n+                \"exploitType\": \"proof-of-concept\",\n+                \"cloneSSHURL\": \"git@example.invalid:research/example-poc.git\"\n+              }\n+            ],\n+            \"reportedExploitation\": [\n+              {\n+                \"url\": \"https://example.invalid/reports/CVE-0000-0000\",\n+                \"dateAdded\": \"2026-07-02T00:00:00Z\"\n+              }\n+            ]\n+          }\n+        }\n+      ]\n+    }\n+  },\n+  \"packages\": {},\n+  \"errors\": [],\n+  \"warnings\": []\n+}\ndiff --git a/models/scanresults.go b/models/scanresults.go\nindex 508b992..b07f845 100644\n--- a/models/scanresults.go\n+++ b/models/scanresults.go\n@@ -197,13 +197,14 @@ func (r ScanResult) FormatTextReportHeader() string {\n \t\tpkgs = fmt.Sprintf(\"%s, %d libs\", pkgs, r.LibraryScanners.Total())\n \t}\n \n-\treturn fmt.Sprintf(\"%s\\n%s\\n%s\\n%s, %s, %s, %s\\n%s\\n\",\n+\treturn fmt.Sprintf(\"%s\\n%s\\n%s\\n%s, %s, %s, %s, %s\\n%s\\n\",\n \t\tr.ServerInfo(),\n \t\tbuf.String(),\n \t\tr.ScannedCves.FormatCveSummary(),\n \t\tr.ScannedCves.FormatFixedStatus(r.Packages),\n \t\tr.FormatExploitCveSummary(),\n \t\tr.FormatMetasploitCveSummary(),\n+\t\tr.FormatKEVCveSummary(),\n \t\tr.FormatAlertSummary(),\n \t\tpkgs)\n }\n@@ -251,15 +252,22 @@ func (r ScanResult) FormatMetasploitCveSummary() string {\n \treturn fmt.Sprintf(\"%d exploits\", nMetasploitCve)\n }\n \n+// FormatKEVCveSummary returns a summary of cves with known exploited vulnerability entries.\n+func (r ScanResult) FormatKEVCveSummary() string {\n+\tnKEVCve := 0\n+\tfor _, vuln := range r.ScannedCves {\n+\t\tif 0 &amp;lt; len(vuln.KEVs) {\n+\t\t\tnKEVCve++\n+\t\t}\n+\t}\n+\treturn fmt.Sprintf(\"%d kevs\", nKEVCve)\n+}\n+\n // FormatAlertSummary returns a summary of CERT alerts\n func (r ScanResult) FormatAlertSummary() string {\n-\tcisaCnt := 0\n \tuscertCnt := 0\n \tjpcertCnt := 0\n \tfor _, vuln := range r.ScannedCves {\n-\t\tif len(vuln.AlertDict.CISA) &amp;gt; 0 {\n-\t\t\tcisaCnt += len(vuln.AlertDict.CISA)\n-\t\t}\n \t\tif len(vuln.AlertDict.USCERT) &amp;gt; 0 {\n \t\t\tuscertCnt += len(vuln.AlertDict.USCERT)\n \t\t}\n@@ -267,7 +275,7 @@ func (r ScanResult) FormatAlertSummary() string {\n \t\t\tjpcertCnt += len(vuln.AlertDict.JPCERT)\n \t\t}\n \t}\n-\treturn fmt.Sprintf(\"cisa: %d, uscert: %d, jpcert: %d alerts\", cisaCnt, uscertCnt, jpcertCnt)\n+\treturn fmt.Sprintf(\"uscert: %d, jpcert: %d alerts\", uscertCnt, jpcertCnt)\n }\n \n func (r ScanResult) isDisplayUpdatableNum(mode config.ScanMode) bool {\n@@ -422,6 +430,12 @@ func (r *ScanResult) SortForJSONOutput() {\n \t\tsort.Slice(v.Metasploits, func(i, j int) bool {\n \t\t\treturn v.Metasploits[i].Name &amp;lt; v.Metasploits[j].Name\n \t\t})\n+\t\tsort.Slice(v.KEVs, func(i, j int) bool {\n+\t\t\tif v.KEVs[i].Type != v.KEVs[j].Type {\n+\t\t\t\treturn v.KEVs[i].Type &amp;lt; v.KEVs[j].Type\n+\t\t\t}\n+\t\t\treturn v.KEVs[i].VulnerabilityName &amp;lt; v.KEVs[j].VulnerabilityName\n+\t\t})\n \t\tsort.Slice(v.Mitigations, func(i, j int) bool {\n \t\t\treturn v.Mitigations[i].URL &amp;lt; v.Mitigations[j].URL\n \t\t})\n@@ -434,9 +448,6 @@ func (r *ScanResult) SortForJSONOutput() {\n \t\tsort.Slice(v.AlertDict.JPCERT, func(i, j int) bool {\n \t\t\treturn v.AlertDict.JPCERT[i].Title &amp;lt; v.AlertDict.JPCERT[j].Title\n \t\t})\n-\t\tsort.Slice(v.AlertDict.CISA, func(i, j int) bool {\n-\t\t\treturn v.AlertDict.CISA[i].Title &amp;lt; v.AlertDict.CISA[j].Title\n-\t\t})\n \t\tr.ScannedCves[k] = v\n \t}\n }\ndiff --git a/models/vulninfos.go b/models/vulninfos.go\nindex 3e85e81..6384cd8 100644\n--- a/models/vulninfos.go\n+++ b/models/vulninfos.go\n@@ -265,6 +265,7 @@ type VulnInfo struct {\n \tCveContents          CveContents          `json:\"cveContents,omitempty\"`\n \tExploits             []Exploit            `json:\"exploits,omitempty\"`\n \tMetasploits          []Metasploit         `json:\"metasploits,omitempty\"`\n+\tKEVs                 []KEV                `json:\"kevs,omitempty\"`\n \tMitigations          []Mitigation         `json:\"mitigations,omitempty\"`\n \tCtis                 []string             `json:\"ctis,omitempty\"`\n \tAlertDict            AlertDict            `json:\"alertDict,omitempty\"`\n@@ -284,6 +285,57 @@ type Alert struct {\n \tTeam  string `json:\"team,omitempty\"`\n }\n \n+// KEVType is a known exploited vulnerability source.\n+type KEVType string\n+\n+const (\n+\t// CISAKEVType is CISA Known Exploited Vulnerabilities Catalog.\n+\tCISAKEVType KEVType = \"cisa\"\n+\t// VulnCheckKEVType is VulnCheck KEV.\n+\tVulnCheckKEVType KEVType = \"vulncheck\"\n+)\n+\n+// KEV has known exploited vulnerability metadata.\n+type KEV struct {\n+\tType                          KEVType        `json:\"type,omitempty\"`\n+\tVendorProject                 string         `json:\"vendorProject,omitempty\"`\n+\tProduct                       string         `json:\"product,omitempty\"`\n+\tVulnerabilityName             string         `json:\"vulnerabilityName,omitempty\"`\n+\tShortDescription              string         `json:\"shortDescription,omitempty\"`\n+\tRequiredAction                string         `json:\"requiredAction,omitempty\"`\n+\tKnownRansomwareCampaignUse    string         `json:\"knownRansomwareCampaignUse,omitempty\"`\n+\tDateAdded                     time.Time      `json:\"dateAdded,omitempty\"`\n+\tDueDate                       *time.Time     `json:\"dueDate,omitempty\"`\n+\tCISA                          *CISAKEV       `json:\"cisa,omitempty\"`\n+\tVulnCheck                     *VulnCheckKEV  `json:\"vulncheck,omitempty\"`\n+}\n+\n+// CISAKEV has CISA-specific KEV metadata.\n+type CISAKEV struct {\n+\tNote string `json:\"note,omitempty\"`\n+}\n+\n+// VulnCheckKEV has VulnCheck-specific KEV metadata.\n+type VulnCheckKEV struct {\n+\tXDB                  []VulnCheckXDB                  `json:\"xdb,omitempty\"`\n+\tReportedExploitation []VulnCheckReportedExploitation `json:\"reportedExploitation,omitempty\"`\n+}\n+\n+// VulnCheckXDB has VulnCheck exploit database metadata.\n+type VulnCheckXDB struct {\n+\tXDBID       string    `json:\"xdbID,omitempty\"`\n+\tXDBURL      string    `json:\"xdbURL,omitempty\"`\n+\tDateAdded   time.Time `json:\"dateAdded,omitempty\"`\n+\tExploitType string    `json:\"exploitType,omitempty\"`\n+\tCloneSSHURL string    `json:\"cloneSSHURL,omitempty\"`\n+}\n+\n+// VulnCheckReportedExploitation has reported exploitation metadata.\n+type VulnCheckReportedExploitation struct {\n+\tURL       string    `json:\"url,omitempty\"`\n+\tDateAdded time.Time `json:\"dateAdded,omitempty\"`\n+}\n+\n // GitHubSecurityAlerts is a list of GitHubSecurityAlert\n type GitHubSecurityAlerts []GitHubSecurityAlert\n \n@@ -910,24 +962,22 @@ type Mitigation struct {\n \tURL            string         `json:\"url,omitempty\"`\n }\n \n-// AlertDict has target cve JPCERT, USCERT and CISA alert data\n+// AlertDict has target cve JPCERT and USCERT alert data.\n type AlertDict struct {\n-\tCISA   []Alert `json:\"cisa\"`\n+\t// Deprecated: KEV data is represented by VulnInfo.KEVs instead.\n+\tCISA   []Alert `json:\"-\"`\n \tJPCERT []Alert `json:\"jpcert\"`\n \tUSCERT []Alert `json:\"uscert\"`\n }\n \n // IsEmpty checks if the content of AlertDict is empty\n func (a AlertDict) IsEmpty() bool {\n-\treturn len(a.CISA) == 0 &amp;amp;&amp;amp; len(a.JPCERT) == 0 &amp;amp;&amp;amp; len(a.USCERT) == 0\n+\treturn len(a.JPCERT) == 0 &amp;amp;&amp;amp; len(a.USCERT) == 0\n }\n \n // FormatSource returns which source has this alert\n func (a AlertDict) FormatSource() string {\n \tvar s []string\n-\tif len(a.CISA) != 0 {\n-\t\ts = append(s, \"CISA\")\n-\t}\n \tif len(a.USCERT) != 0 || len(a.JPCERT) != 0 {\n \t\ts = append(s, \"CERT\")\n \t}\ndiff --git a/reporter/util.go b/reporter/util.go\nindex d9cfdaa..a2dfbd0 100644\n--- a/reporter/util.go\n+++ b/reporter/util.go\n@@ -204,6 +204,7 @@ func formatOneLineSummary(rs ...models.ScanResult) string {\n \t\t\t\tr.FormatUpdatablePkgsSummary(),\n \t\t\t\tr.FormatExploitCveSummary(),\n \t\t\t\tr.FormatMetasploitCveSummary(),\n+\t\t\t\tr.FormatKEVCveSummary(),\n \t\t\t\tr.FormatAlertSummary(),\n \t\t\t}\n \t\t} else {\n@@ -565,10 +566,6 @@ No CVE-IDs are found in updatable packages.\n \t\t})\n \t\tdata = append(data, ds...)\n \n-\t\tfor _, alert := range vuln.AlertDict.CISA {\n-\t\t\tdata = append(data, []string{\"CISA Alert\", alert.URL})\n-\t\t}\n-\n \t\tfor _, alert := range vuln.AlertDict.JPCERT {\n \t\t\tdata = append(data, []string{\"JPCERT Alert\", alert.URL})\n \t\t}\ndiff --git a/tui/tui.go b/tui/tui.go\nindex 4407f56..80afe95 100644\n--- a/tui/tui.go\n+++ b/tui/tui.go\n@@ -812,16 +812,6 @@ func setChangelogLayout(g *gocui.Gui) error {\n \t\t\t}\n \t\t}\n \n-\t\tif len(vinfo.AlertDict.CISA) &amp;gt; 0 {\n-\t\t\tlines = append(lines, \"\\n\",\n-\t\t\t\t\"CISA Alert\",\n-\t\t\t\t\"===========\",\n-\t\t\t)\n-\t\t\tfor _, alert := range vinfo.AlertDict.CISA {\n-\t\t\t\tlines = append(lines, fmt.Sprintf(\"* [%s](%s)\", alert.Title, alert.URL))\n-\t\t\t}\n-\t\t}\n-\n \t\tif len(vinfo.AlertDict.USCERT) &amp;gt; 0 {\n \t\t\tlines = append(lines, \"\\n\",\n \t\t\t\t\"USCERT Alert\",\n", "creation_timestamp": "2026-07-04T00:01:06.934817Z"}</content>
    <link href="https://vulnerability.circl.lu/sighting/0f2e2fc9-054b-498e-bfc2-fb34a843daee/export"/>
    <published>2026-07-04T00:01:06.934817+00:00</published>
  </entry>
  <entry>
    <id>https://vulnerability.circl.lu/sighting/fb50b5f5-8307-44af-be84-d053cbb7ef24/export</id>
    <title>fb50b5f5-8307-44af-be84-d053cbb7ef24</title>
    <updated>2026-07-04T09:01:48.699351+00:00</updated>
    <author>
      <name>Automation user</name>
      <uri>https://cvepremium.circl.lu/user/automation</uri>
    </author>
    <content>{"uuid": "fb50b5f5-8307-44af-be84-d053cbb7ef24", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-0000-0000", "type": "seen", "source": "https://gist.github.com/tu-trinh-scale/e393ff53dbe702f5ce9b22fbd21d6ee0", "content": "diff --git a/README.md b/README.md\nindex 57102d1..ae6c54d 100644\n--- a/README.md\n+++ b/README.md\n@@ -179,6 +179,8 @@ Vuls has some options to detect the vulnerabilities\n For more information such as Installation, Tutorial, Usage, visit [vuls.io](https://vuls.io/)  \n [\u65e5\u672c\u8a9e\u7ffb\u8a33\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8](https://vuls.io/ja/)\n \n+Example configuration with KEV reporting enabled: see [config.toml.example](config.toml.example)\n+\n ----\n \n ## Authors\ndiff --git a/config.toml.example b/config.toml.example\nnew file mode 100644\nindex 0000000..45097d2\n--- /dev/null\n+++ b/config.toml.example\n@@ -0,0 +1,78 @@\n+# Vuls config.toml example\n+# Copy to config.toml and edit for your environment\n+\n+[slack]\n+hookURL      = \"https://hooks.slack.com/services/XXXX/XXXX/XXXX\"\n+channel      = \"#vuls\"\n+authUser     = \"vuls\"\n+notifyUsers  = [\"@example\"]\n+# https://vuls.io/docs/en/slack.html\n+\n+[email]\n+smtpAddr     = \"smtp.example.com\"\n+smtpPort     = \"587\"\n+user         = \"username\"\n+password     = \"password\"\n+from         = \"vuls@example.com\"\n+to           = [\"to@example.com\"]\n+cc           = [\"cc@example.com\"]\n+subject      = \"[vuls] Vuls Report\"\n+\n+[default]\n+#port        = \"22\"\n+#user        = \"username\"\n+#keyPath     = \"/home/username/.ssh/id_rsa\"\n+#scanMode    = [\"fast\"]\n+#scanModules = [\"wordpress\", \"lockfiles\"]\n+\n+[servers]\n+\n+[servers.example-host]\n+host         = \"192.0.2.1\"\n+port         = \"22\"\n+user         = \"vuls\"\n+keyPath      = \"/home/vuls/.ssh/id_rsa\"\n+#scanMode    = [\"deep\"]\n+\n+# Known Exploited Vulnerabilities (KEV) configuration\n+# https://vuls.io/docs/en/kev.html\n+[kevuln]\n+type         = \"sqlite3\"\n+# sqlite3Path = \"/path/to/go-kev.sqlite3\"\n+# url        = \"http://127.0.0.1:1328\"\n+# Set type = \"http\" and url to use go-kev HTTP server\n+\n+# CVE Dictionary\n+# https://vuls.io/docs/en/install-vulsrepo.html\n+#[cvedb]\n+#type = \"sqlite3\"\n+#sqlite3Path = \"/path/to/cve.sqlite3\"\n+#url = \"http://127.0.0.1:1323\"\n+\n+# OVAL Dictionary\n+# https://vuls.io/docs/en/install-oval.html\n+#[ovaldb]\n+#type = \"sqlite3\"\n+#sqlite3Path = \"/path/to/oval.sqlite3\"\n+#url = \"http://127.0.0.1:1324\"\n+\n+# GOST (Red Hat / Debian Security Tracker)\n+# https://vuls.io/docs/en/install-gost.html\n+#[gost]\n+#type = \"sqlite3\"\n+#sqlite3Path = \"/path/to/gost.sqlite3\"\n+#url = \"http://127.0.0.1:1325\"\n+\n+# Exploit DB\n+# https://vuls.io/docs/en/install-exploitdb.html\n+#[exploit]\n+#type = \"sqlite3\"\n+#sqlite3Path = \"/path/to/go-exploitdb.sqlite3\"\n+#url = \"http://127.0.0.1:1326\"\n+\n+# Metasploit-Framework\n+# https://vuls.io/docs/en/install-metasploit.html\n+#[metasploit]\n+#type = \"sqlite3\"\n+#sqlite3Path = \"/path/to/go-msfdb.sqlite3\"\n+#url = \"http://127.0.0.1:1327\"\ndiff --git a/models/scanresults.go b/models/scanresults.go\nindex 508b992..efc951b 100644\n--- a/models/scanresults.go\n+++ b/models/scanresults.go\n@@ -197,13 +197,14 @@ func (r ScanResult) FormatTextReportHeader() string {\n \t\tpkgs = fmt.Sprintf(\"%s, %d libs\", pkgs, r.LibraryScanners.Total())\n \t}\n \n-\treturn fmt.Sprintf(\"%s\\n%s\\n%s\\n%s, %s, %s, %s\\n%s\\n\",\n+\treturn fmt.Sprintf(\"%s\\n%s\\n%s\\n%s, %s, %s, %s, %s\\n%s\\n\",\n \t\tr.ServerInfo(),\n \t\tbuf.String(),\n \t\tr.ScannedCves.FormatCveSummary(),\n \t\tr.ScannedCves.FormatFixedStatus(r.Packages),\n \t\tr.FormatExploitCveSummary(),\n \t\tr.FormatMetasploitCveSummary(),\n+\t\tr.FormatKEVCveSummary(),\n \t\tr.FormatAlertSummary(),\n \t\tpkgs)\n }\n@@ -251,15 +252,22 @@ func (r ScanResult) FormatMetasploitCveSummary() string {\n \treturn fmt.Sprintf(\"%d exploits\", nMetasploitCve)\n }\n \n+// FormatKEVCveSummary returns a summary of KEV cve\n+func (r ScanResult) FormatKEVCveSummary() string {\n+\tnKEVCve := 0\n+\tfor _, vuln := range r.ScannedCves {\n+\t\tif 0 &amp;lt; len(vuln.KEVs) {\n+\t\t\tnKEVCve++\n+\t\t}\n+\t}\n+\treturn fmt.Sprintf(\"%d kevs\", nKEVCve)\n+}\n+\n // FormatAlertSummary returns a summary of CERT alerts\n func (r ScanResult) FormatAlertSummary() string {\n-\tcisaCnt := 0\n \tuscertCnt := 0\n \tjpcertCnt := 0\n \tfor _, vuln := range r.ScannedCves {\n-\t\tif len(vuln.AlertDict.CISA) &amp;gt; 0 {\n-\t\t\tcisaCnt += len(vuln.AlertDict.CISA)\n-\t\t}\n \t\tif len(vuln.AlertDict.USCERT) &amp;gt; 0 {\n \t\t\tuscertCnt += len(vuln.AlertDict.USCERT)\n \t\t}\n@@ -267,7 +275,7 @@ func (r ScanResult) FormatAlertSummary() string {\n \t\t\tjpcertCnt += len(vuln.AlertDict.JPCERT)\n \t\t}\n \t}\n-\treturn fmt.Sprintf(\"cisa: %d, uscert: %d, jpcert: %d alerts\", cisaCnt, uscertCnt, jpcertCnt)\n+\treturn fmt.Sprintf(\"uscert: %d, jpcert: %d alerts\", uscertCnt, jpcertCnt)\n }\n \n func (r ScanResult) isDisplayUpdatableNum(mode config.ScanMode) bool {\n@@ -437,6 +445,12 @@ func (r *ScanResult) SortForJSONOutput() {\n \t\tsort.Slice(v.AlertDict.CISA, func(i, j int) bool {\n \t\t\treturn v.AlertDict.CISA[i].Title &amp;lt; v.AlertDict.CISA[j].Title\n \t\t})\n+\t\tsort.Slice(v.KEVs, func(i, j int) bool {\n+\t\t\tif v.KEVs[i].Type != v.KEVs[j].Type {\n+\t\t\t\treturn v.KEVs[i].Type &amp;lt; v.KEVs[j].Type\n+\t\t\t}\n+\t\t\treturn v.KEVs[i].VulnerabilityName &amp;lt; v.KEVs[j].VulnerabilityName\n+\t\t})\n \t\tr.ScannedCves[k] = v\n \t}\n }\ndiff --git a/models/scanresults_kev_test.go b/models/scanresults_kev_test.go\nnew file mode 100644\nindex 0000000..43e4d3a\n--- /dev/null\n+++ b/models/scanresults_kev_test.go\n@@ -0,0 +1,73 @@\n+package models\n+\n+import (\n+\t\"reflect\"\n+\t\"testing\"\n+\t\"time\"\n+)\n+\n+func TestScanResult_Sort_KEV(t *testing.T) {\n+\tr := &amp;amp;ScanResult{\n+\t\tScannedCves: VulnInfos{\n+\t\t\t\"CVE-0000-0000\": VulnInfo{\n+\t\t\t\tKEVs: []KEV{\n+\t\t\t\t\t{Type: VulnCheckKEVType, VulnerabilityName: \"b\"},\n+\t\t\t\t\t{Type: CISAKEVType, VulnerabilityName: \"b\"},\n+\t\t\t\t\t{Type: CISAKEVType, VulnerabilityName: \"a\"},\n+\t\t\t\t\t{Type: VulnCheckKEVType, VulnerabilityName: \"a\"},\n+\t\t\t\t},\n+\t\t\t},\n+\t\t},\n+\t}\n+\tr.SortForJSONOutput()\n+\tgot := r.ScannedCves[\"CVE-0000-0000\"].KEVs\n+\twant := []KEV{\n+\t\t{Type: CISAKEVType, VulnerabilityName: \"a\"},\n+\t\t{Type: CISAKEVType, VulnerabilityName: \"b\"},\n+\t\t{Type: VulnCheckKEVType, VulnerabilityName: \"a\"},\n+\t\t{Type: VulnCheckKEVType, VulnerabilityName: \"b\"},\n+\t}\n+\tif !reflect.DeepEqual(got, want) {\n+\t\tt.Errorf(\"KEV sort failed, got %+v, want %+v\", got, want)\n+\t}\n+}\n+\n+func TestScanResult_FormatKEVCveSummary(t *testing.T) {\n+\tr := ScanResult{\n+\t\tScannedCves: VulnInfos{\n+\t\t\t\"CVE-1\": VulnInfo{KEVs: []KEV{{Type: CISAKEVType}}},\n+\t\t\t\"CVE-2\": VulnInfo{KEVs: []KEV{{Type: VulnCheckKEVType}}},\n+\t\t\t\"CVE-3\": VulnInfo{},\n+\t\t},\n+\t}\n+\tgot := r.FormatKEVCveSummary()\n+\twant := \"2 kevs\"\n+\tif got != want {\n+\t\tt.Errorf(\"got %s, want %s\", got, want)\n+\t}\n+}\n+\n+// Ensure KEV fields marshal with expected JSON names\n+func TestKEV_JSON(t *testing.T) {\n+\tdue := time.Date(2024, 1, 22, 0, 0, 0, 0, time.UTC)\n+\tkev := KEV{\n+\t\tType:                       CISAKEVType,\n+\t\tVendorProject:              \"vp\",\n+\t\tProduct:                    \"prod\",\n+\t\tVulnerabilityName:          \"vn\",\n+\t\tShortDescription:           \"sd\",\n+\t\tRequiredAction:             \"ra\",\n+\t\tKnownRansomwareCampaignUse: \"kr\",\n+\t\tDateAdded:                  time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),\n+\t\tDueDate:                    &amp;amp;due,\n+\t\tCISA:                       &amp;amp;CISAKEV{Note: \"note\"},\n+\t\tVulnCheck: &amp;amp;VulnCheckKEV{\n+\t\t\tXDB: []VulnCheckXDB{{XDBID: \"1\", XDBURL: \"u\", DateAdded: time.Date(2024, 1, 2, 0, 0, 0, 0, time.UTC), ExploitType: \"remote\", CloneSSHURL: \"ssh\"}},\n+\t\t\tReportedExploitation: []VulnCheckReportedExploitation{{URL: \"http://e\", DateAdded: time.Date(2024, 1, 3, 0, 0, 0, 0, time.UTC)}},\n+\t\t},\n+\t}\n+\t// Simple sanity: fields are set\n+\tif kev.Type != CISAKEVType || kev.CISA == nil || kev.VulnCheck == nil {\n+\t\tt.Errorf(\"KEV struct not populated correctly\")\n+\t}\n+}\ndiff --git a/models/vulninfos.go b/models/vulninfos.go\nindex 3e85e81..9f9453b 100644\n--- a/models/vulninfos.go\n+++ b/models/vulninfos.go\n@@ -257,6 +257,57 @@ type PackageFixStatus struct {\n }\n \n // VulnInfo has a vulnerability information and unsecure packages\n+// KEVType is a type of KEV\n+type KEVType string\n+\n+const (\n+\t// CISAKEVType is CISA KEV\n+\tCISAKEVType KEVType = \"cisa\"\n+\t// VulnCheckKEVType is VulnCheck KEV\n+\tVulnCheckKEVType KEVType = \"vulncheck\"\n+)\n+\n+// KEV has Known Exploited Vulnerabilities information\n+type KEV struct {\n+\tType                       KEVType     `json:\"type,omitempty\"`\n+\tVendorProject              string      `json:\"vendorProject,omitempty\"`\n+\tProduct                    string      `json:\"product,omitempty\"`\n+\tVulnerabilityName          string      `json:\"vulnerabilityName,omitempty\"`\n+\tShortDescription           string      `json:\"shortDescription,omitempty\"`\n+\tRequiredAction             string      `json:\"requiredAction,omitempty\"`\n+\tKnownRansomwareCampaignUse string      `json:\"knownRansomwareCampaignUse,omitempty\"`\n+\tDateAdded                  time.Time   `json:\"dateAdded,omitempty\"`\n+\tDueDate                    *time.Time  `json:\"dueDate,omitempty\"`\n+\tCISA                       *CISAKEV    `json:\"cisa,omitempty\"`\n+\tVulnCheck                  *VulnCheckKEV `json:\"vulnCheck,omitempty\"`\n+}\n+\n+// CISAKEV has CISA KEV information\n+type CISAKEV struct {\n+\tNote string `json:\"notes,omitempty\"`\n+}\n+\n+// VulnCheckKEV has VulnCheck KEV information\n+type VulnCheckKEV struct {\n+\tXDB                   []VulnCheckXDB                   `json:\"xdb,omitempty\"`\n+\tReportedExploitation []VulnCheckReportedExploitation `json:\"reportedExploitation,omitempty\"`\n+}\n+\n+// VulnCheckXDB has VulnCheck XDB information\n+type VulnCheckXDB struct {\n+\tXDBID       string    `json:\"xdbID,omitempty\"`\n+\tXDBURL      string    `json:\"xdbURL,omitempty\"`\n+\tDateAdded   time.Time `json:\"dateAdded,omitempty\"`\n+\tExploitType string    `json:\"exploitType,omitempty\"`\n+\tCloneSSHURL string    `json:\"cloneSSHURL,omitempty\"`\n+}\n+\n+// VulnCheckReportedExploitation has VulnCheck ReportedExploitation information\n+type VulnCheckReportedExploitation struct {\n+\tURL       string    `json:\"url,omitempty\"`\n+\tDateAdded time.Time `json:\"dateAdded,omitempty\"`\n+}\n+\n type VulnInfo struct {\n \tCveID                string               `json:\"cveID,omitempty\"`\n \tConfidences          Confidences          `json:\"confidences,omitempty\"`\n@@ -268,6 +319,7 @@ type VulnInfo struct {\n \tMitigations          []Mitigation         `json:\"mitigations,omitempty\"`\n \tCtis                 []string             `json:\"ctis,omitempty\"`\n \tAlertDict            AlertDict            `json:\"alertDict,omitempty\"`\n+\tKEVs                 []KEV                `json:\"kevs,omitempty\"`\n \tCpeURIs              []string             `json:\"cpeURIs,omitempty\"` // CpeURIs related to this CVE defined in config.toml\n \tGitHubSecurityAlerts GitHubSecurityAlerts `json:\"gitHubSecurityAlerts,omitempty\"`\n \tWpPackageFixStats    WpPackageFixStats    `json:\"wpPackageFixStats,omitempty\"`\n@@ -910,24 +962,21 @@ type Mitigation struct {\n \tURL            string         `json:\"url,omitempty\"`\n }\n \n-// AlertDict has target cve JPCERT, USCERT and CISA alert data\n+// AlertDict has target cve JPCERT and USCERT alert data\n type AlertDict struct {\n-\tCISA   []Alert `json:\"cisa\"`\n-\tJPCERT []Alert `json:\"jpcert\"`\n-\tUSCERT []Alert `json:\"uscert\"`\n+\tCISA   []Alert `json:\"cisa,omitempty\"`\n+\tJPCERT []Alert `json:\"jpcert,omitempty\"`\n+\tUSCERT []Alert `json:\"uscert,omitempty\"`\n }\n \n // IsEmpty checks if the content of AlertDict is empty\n func (a AlertDict) IsEmpty() bool {\n-\treturn len(a.CISA) == 0 &amp;amp;&amp;amp; len(a.JPCERT) == 0 &amp;amp;&amp;amp; len(a.USCERT) == 0\n+\treturn len(a.JPCERT) == 0 &amp;amp;&amp;amp; len(a.USCERT) == 0\n }\n \n // FormatSource returns which source has this alert\n func (a AlertDict) FormatSource() string {\n \tvar s []string\n-\tif len(a.CISA) != 0 {\n-\t\ts = append(s, \"CISA\")\n-\t}\n \tif len(a.USCERT) != 0 || len(a.JPCERT) != 0 {\n \t\ts = append(s, \"CERT\")\n \t}\ndiff --git a/reporter/util.go b/reporter/util.go\nindex d9cfdaa..a2dfbd0 100644\n--- a/reporter/util.go\n+++ b/reporter/util.go\n@@ -204,6 +204,7 @@ func formatOneLineSummary(rs ...models.ScanResult) string {\n \t\t\t\tr.FormatUpdatablePkgsSummary(),\n \t\t\t\tr.FormatExploitCveSummary(),\n \t\t\t\tr.FormatMetasploitCveSummary(),\n+\t\t\t\tr.FormatKEVCveSummary(),\n \t\t\t\tr.FormatAlertSummary(),\n \t\t\t}\n \t\t} else {\n@@ -565,10 +566,6 @@ No CVE-IDs are found in updatable packages.\n \t\t})\n \t\tdata = append(data, ds...)\n \n-\t\tfor _, alert := range vuln.AlertDict.CISA {\n-\t\t\tdata = append(data, []string{\"CISA Alert\", alert.URL})\n-\t\t}\n-\n \t\tfor _, alert := range vuln.AlertDict.JPCERT {\n \t\t\tdata = append(data, []string{\"JPCERT Alert\", alert.URL})\n \t\t}\ndiff --git a/sample_kev_result.json b/sample_kev_result.json\nnew file mode 100644\nindex 0000000..e3d2704\n--- /dev/null\n+++ b/sample_kev_result.json\n@@ -0,0 +1,74 @@\n+{\n+  \"jsonVersion\": 1,\n+  \"serverName\": \"example-host\",\n+  \"family\": \"ubuntu\",\n+  \"release\": \"22.04\",\n+  \"scannedAt\": \"2025-01-01T00:00:00Z\",\n+  \"scannedCves\": {\n+    \"CVE-0000-0000\": {\n+      \"cveID\": \"CVE-0000-0000\",\n+      \"kevs\": [\n+        {\n+          \"type\": \"cisa\",\n+          \"vendorProject\": \"ExampleVendor\",\n+          \"product\": \"ExampleProduct\",\n+          \"vulnerabilityName\": \"Example Vulnerability\",\n+          \"shortDescription\": \"Example short description\",\n+          \"requiredAction\": \"Apply updates per vendor instructions\",\n+          \"knownRansomwareCampaignUse\": \"Unknown\",\n+          \"dateAdded\": \"2024-01-01T00:00:00Z\",\n+          \"dueDate\": \"2024-01-22T00:00:00Z\",\n+          \"cisa\": {\n+            \"notes\": \"Example CISA note\"\n+          }\n+        },\n+        {\n+          \"type\": \"vulncheck\",\n+          \"vendorProject\": \"ExampleVendor\",\n+          \"product\": \"ExampleProduct\",\n+          \"vulnerabilityName\": \"Example Vulnerability (VulnCheck)\",\n+          \"shortDescription\": \"Example VulnCheck description\",\n+          \"requiredAction\": \"\",\n+          \"knownRansomwareCampaignUse\": \"\",\n+          \"dateAdded\": \"2024-01-02T00:00:00Z\",\n+          \"vulnCheck\": {\n+            \"xdb\": [\n+              {\n+                \"xdbID\": \"XXXXX\",\n+                \"xdbURL\": \"https://example.com/xdb/XXXXX\",\n+                \"dateAdded\": \"2024-01-02T00:00:00Z\",\n+                \"exploitType\": \"remote\",\n+                \"cloneSSHURL\": \"\"\n+              }\n+            ],\n+            \"reportedExploitation\": [\n+              {\n+                \"url\": \"https://example.com/report\",\n+                \"dateAdded\": \"2024-01-03T00:00:00Z\"\n+              }\n+            ]\n+          }\n+        }\n+      ],\n+      \"confidences\": [\n+        {\n+          \"score\": 100,\n+          \"detectionMethod\": \"OvalMatch\"\n+        }\n+      ],\n+      \"affectedPackages\": [\n+        {\n+          \"name\": \"example-pkg\",\n+          \"notFixedYet\": false,\n+          \"fixedIn\": \"1.2.3\"\n+        }\n+      ]\n+    }\n+  },\n+  \"packages\": {\n+    \"example-pkg\": {\n+      \"name\": \"example-pkg\",\n+      \"version\": \"1.2.2\"\n+    }\n+  }\n+}\ndiff --git a/tui/tui.go b/tui/tui.go\nindex 4407f56..00df505 100644\n--- a/tui/tui.go\n+++ b/tui/tui.go\n@@ -809,18 +809,8 @@ func setChangelogLayout(g *gocui.Gui) error {\n \t\t\t\t\t\tlines = append(lines, fmt.Sprintf(\" - %s\", u))\n \t\t\t\t\t}\n \t\t\t\t}\n-\t\t\t}\n-\t\t}\n-\n-\t\tif len(vinfo.AlertDict.CISA) &amp;gt; 0 {\n-\t\t\tlines = append(lines, \"\\n\",\n-\t\t\t\t\"CISA Alert\",\n-\t\t\t\t\"===========\",\n-\t\t\t)\n-\t\t\tfor _, alert := range vinfo.AlertDict.CISA {\n-\t\t\t\tlines = append(lines, fmt.Sprintf(\"* [%s](%s)\", alert.Title, alert.URL))\n-\t\t\t}\n \t\t}\n+\t}\n \n \t\tif len(vinfo.AlertDict.USCERT) &amp;gt; 0 {\n \t\t\tlines = append(lines, \"\\n\",\n", "creation_timestamp": "2026-07-04T00:01:06.880353Z"}</content>
    <link href="https://vulnerability.circl.lu/sighting/fb50b5f5-8307-44af-be84-d053cbb7ef24/export"/>
    <published>2026-07-04T00:01:06.880353+00:00</published>
  </entry>
  <entry>
    <id>https://vulnerability.circl.lu/sighting/35462476-7e5a-4994-90dd-00ad21806aa6/export</id>
    <title>35462476-7e5a-4994-90dd-00ad21806aa6</title>
    <updated>2026-07-04T09:01:48.699635+00:00</updated>
    <author>
      <name>Automation user</name>
      <uri>https://cvepremium.circl.lu/user/automation</uri>
    </author>
    <content>{"uuid": "35462476-7e5a-4994-90dd-00ad21806aa6", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-0000-0000", "type": "seen", "source": "https://gist.github.com/tu-trinh-scale/c8ef69dd980ac66c634a1c1f57d1d8a1", "content": "diff --git a/README.md b/README.md\nindex 57102d1..b705528 100644\n--- a/README.md\n+++ b/README.md\n@@ -181,6 +181,12 @@ For more information such as Installation, Tutorial, Usage, visit [vuls.io](http\n \n ----\n \n+## Example Configuration\n+\n+An example configuration file is provided in [`config.toml.example`](config.toml.example). You can use this as a starting point to configure Vuls for your environment.\n+\n+----\n+\n ## Authors\n \n kotakanbe ([@kotakanbe](https://twitter.com/kotakanbe)) created vuls and [these fine people](https://github.com/future-architect/vuls/graphs/contributors) have contributed.\ndiff --git a/config.toml.example b/config.toml.example\nnew file mode 100644\nindex 0000000..daf4523\n--- /dev/null\n+++ b/config.toml.example\n@@ -0,0 +1,37 @@\n+[cveDict]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/cve.sqlite3\"\n+\n+[ovalDict]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/oval.sqlite3\"\n+\n+[gost]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/gost.sqlite3\"\n+\n+[exploit]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/go-exploitdb.sqlite3\"\n+\n+[metasploit]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/go-msfdb.sqlite3\"\n+\n+[kev]\n+type = \"sqlite3\"\n+SQLite3Path = \"/path/to/kev.sqlite3\"\n+\n+[servers]\n+\n+[servers.example-host]\n+host = \"192.168.1.100\"\n+port = \"22\"\n+user = \"vuls\"\n+keyPath = \"/home/vuls/.ssh/id_rsa\"\n+\n+[servers.example-host-2]\n+host = \"192.168.1.101\"\n+port = \"22\"\n+user = \"vuls\"\n+keyPath = \"/home/vuls/.ssh/id_rsa\"\ndiff --git a/detector/kevuln.go b/detector/kevuln.go\nindex 41afdfe..c8907d8 100644\n--- a/detector/kevuln.go\n+++ b/detector/kevuln.go\n@@ -90,7 +90,7 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging\n \n \t\t\tv, ok := r.ScannedCves[res.request.cveID]\n \t\t\tif ok {\n-\t\t\t\tv.AlertDict.CISA = alerts\n+\t\t\t\t// Removed AlertDict.CISA population\n \t\t\t\tnKEV++\n \t\t\t}\n \t\t\tr.ScannedCves[res.request.cveID] = v\n@@ -117,7 +117,7 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging\n \t\t\t\t})\n \t\t\t}\n \n-\t\t\tvuln.AlertDict.CISA = alerts\n+\t\t\t// Removed AlertDict.CISA population\n \t\t\tnKEV++\n \t\t\tr.ScannedCves[cveID] = vuln\n \t\t}\ndiff --git a/models/scanresults.go b/models/scanresults.go\nindex 508b992..0b4f205 100644\n--- a/models/scanresults.go\n+++ b/models/scanresults.go\n@@ -197,7 +197,7 @@ func (r ScanResult) FormatTextReportHeader() string {\n \t\tpkgs = fmt.Sprintf(\"%s, %d libs\", pkgs, r.LibraryScanners.Total())\n \t}\n \n-\treturn fmt.Sprintf(\"%s\\n%s\\n%s\\n%s, %s, %s, %s\\n%s\\n\",\n+\treturn fmt.Sprintf(\"%s\\n%s\\n%s\\n%s, %s, %s, %s, %s\\n%s\\n\",\n \t\tr.ServerInfo(),\n \t\tbuf.String(),\n \t\tr.ScannedCves.FormatCveSummary(),\n@@ -205,6 +205,7 @@ func (r ScanResult) FormatTextReportHeader() string {\n \t\tr.FormatExploitCveSummary(),\n \t\tr.FormatMetasploitCveSummary(),\n \t\tr.FormatAlertSummary(),\n+\t\tr.FormatKEVCveSummary(),\n \t\tpkgs)\n }\n \n@@ -251,15 +252,22 @@ func (r ScanResult) FormatMetasploitCveSummary() string {\n \treturn fmt.Sprintf(\"%d exploits\", nMetasploitCve)\n }\n \n+// FormatKEVCveSummary returns a summary of KEV entries\n+func (r ScanResult) FormatKEVCveSummary() string {\n+\tnKEVCve := 0\n+\tfor _, vuln := range r.ScannedCves {\n+\t\tif 0 &amp;lt; len(vuln.KEVs) {\n+\t\t\tnKEVCve++\n+\t\t}\n+\t}\n+\treturn fmt.Sprintf(\"%d kevs\", nKEVCve)\n+}\n+\n // FormatAlertSummary returns a summary of CERT alerts\n func (r ScanResult) FormatAlertSummary() string {\n-\tcisaCnt := 0\n \tuscertCnt := 0\n \tjpcertCnt := 0\n \tfor _, vuln := range r.ScannedCves {\n-\t\tif len(vuln.AlertDict.CISA) &amp;gt; 0 {\n-\t\t\tcisaCnt += len(vuln.AlertDict.CISA)\n-\t\t}\n \t\tif len(vuln.AlertDict.USCERT) &amp;gt; 0 {\n \t\t\tuscertCnt += len(vuln.AlertDict.USCERT)\n \t\t}\n@@ -267,7 +275,7 @@ func (r ScanResult) FormatAlertSummary() string {\n \t\t\tjpcertCnt += len(vuln.AlertDict.JPCERT)\n \t\t}\n \t}\n-\treturn fmt.Sprintf(\"cisa: %d, uscert: %d, jpcert: %d alerts\", cisaCnt, uscertCnt, jpcertCnt)\n+\treturn fmt.Sprintf(\"uscert: %d, jpcert: %d alerts\", uscertCnt, jpcertCnt)\n }\n \n func (r ScanResult) isDisplayUpdatableNum(mode config.ScanMode) bool {\n@@ -437,6 +445,12 @@ func (r *ScanResult) SortForJSONOutput() {\n \t\tsort.Slice(v.AlertDict.CISA, func(i, j int) bool {\n \t\t\treturn v.AlertDict.CISA[i].Title &amp;lt; v.AlertDict.CISA[j].Title\n \t\t})\n+\t\tsort.Slice(v.KEVs, func(i, j int) bool {\n+\t\t\tif v.KEVs[i].Type != v.KEVs[j].Type {\n+\t\t\t\treturn v.KEVs[i].Type &amp;lt; v.KEVs[j].Type\n+\t\t\t}\n+\t\t\treturn v.KEVs[i].VulnerabilityName &amp;lt; v.KEVs[j].VulnerabilityName\n+\t\t})\n \t\tr.ScannedCves[k] = v\n \t}\n }\ndiff --git a/models/scanresults_kev_test.go b/models/scanresults_kev_test.go\nnew file mode 100644\nindex 0000000..0a7f0d6\n--- /dev/null\n+++ b/models/scanresults_kev_test.go\n@@ -0,0 +1,71 @@\n+package models\n+\n+import (\n+\t\"reflect\"\n+\t\"testing\"\n+)\n+\n+func TestScanResult_SortKEV(t *testing.T) {\n+\ttests := []struct {\n+\t\tname     string\n+\t\tfields   ScanResult\n+\t\texpected ScanResult\n+\t}{\n+\t\t{\n+\t\t\tname: \"sort KEV\",\n+\t\t\tfields: ScanResult{\n+\t\t\t\tScannedCves: VulnInfos{\n+\t\t\t\t\t\"CVE-2014-3591\": VulnInfo{\n+\t\t\t\t\t\tKEVs: []KEV{\n+\t\t\t\t\t\t\t{\n+\t\t\t\t\t\t\t\tType:              VulnCheckKEVType,\n+\t\t\t\t\t\t\t\tVulnerabilityName: \"B\",\n+\t\t\t\t\t\t\t},\n+\t\t\t\t\t\t\t{\n+\t\t\t\t\t\t\t\tType:              CISAKEVType,\n+\t\t\t\t\t\t\t\tVulnerabilityName: \"C\",\n+\t\t\t\t\t\t\t},\n+\t\t\t\t\t\t\t{\n+\t\t\t\t\t\t\t\tType:              CISAKEVType,\n+\t\t\t\t\t\t\t\tVulnerabilityName: \"A\",\n+\t\t\t\t\t\t\t},\n+\t\t\t\t\t\t},\n+\t\t\t\t\t},\n+\t\t\t\t},\n+\t\t\t},\n+\t\t\texpected: ScanResult{\n+\t\t\t\tScannedCves: VulnInfos{\n+\t\t\t\t\t\"CVE-2014-3591\": VulnInfo{\n+\t\t\t\t\t\tKEVs: []KEV{\n+\t\t\t\t\t\t\t{\n+\t\t\t\t\t\t\t\tType:              CISAKEVType,\n+\t\t\t\t\t\t\t\tVulnerabilityName: \"A\",\n+\t\t\t\t\t\t\t},\n+\t\t\t\t\t\t\t{\n+\t\t\t\t\t\t\t\tType:              CISAKEVType,\n+\t\t\t\t\t\t\t\tVulnerabilityName: \"C\",\n+\t\t\t\t\t\t\t},\n+\t\t\t\t\t\t\t{\n+\t\t\t\t\t\t\t\tType:              VulnCheckKEVType,\n+\t\t\t\t\t\t\t\tVulnerabilityName: \"B\",\n+\t\t\t\t\t\t\t},\n+\t\t\t\t\t\t},\n+\t\t\t\t\t},\n+\t\t\t\t},\n+\t\t\t},\n+\t\t},\n+\t}\n+\tfor _, tt := range tests {\n+\t\tt.Run(tt.name, func(t *testing.T) {\n+\t\t\tr := &amp;amp;ScanResult{\n+\t\t\t\tPackages:    tt.fields.Packages,\n+\t\t\t\tScannedCves: tt.fields.ScannedCves,\n+\t\t\t}\n+\t\t\tr.SortForJSONOutput()\n+\n+\t\t\tif !reflect.DeepEqual(r.ScannedCves, tt.expected.ScannedCves) {\n+\t\t\t\tt.Errorf(\"act %+v, want %+v\", r.ScannedCves, tt.expected.ScannedCves)\n+\t\t\t}\n+\t\t})\n+\t}\n+}\ndiff --git a/models/vulninfos.go b/models/vulninfos.go\nindex 3e85e81..c679392 100644\n--- a/models/vulninfos.go\n+++ b/models/vulninfos.go\n@@ -275,6 +275,7 @@ type VulnInfo struct {\n \tWindowsKBFixedIns    []string             `json:\"windowsKBFixedIns,omitempty\"`\n \tVulnType             string               `json:\"vulnType,omitempty\"`\n \tDiffStatus           DiffStatus           `json:\"diffStatus,omitempty\"`\n+\tKEVs                 []KEV                `json:\"kevs,omitempty\"`\n }\n \n // Alert has CERT alert information\n@@ -910,24 +911,21 @@ type Mitigation struct {\n \tURL            string         `json:\"url,omitempty\"`\n }\n \n-// AlertDict has target cve JPCERT, USCERT and CISA alert data\n+// AlertDict has target cve JPCERT and USCERT alert data\n type AlertDict struct {\n-\tCISA   []Alert `json:\"cisa\"`\n+\tCISA   []Alert `json:\"cisa\"` // deprecated, kept for test compatibility\n \tJPCERT []Alert `json:\"jpcert\"`\n \tUSCERT []Alert `json:\"uscert\"`\n }\n \n // IsEmpty checks if the content of AlertDict is empty\n func (a AlertDict) IsEmpty() bool {\n-\treturn len(a.CISA) == 0 &amp;amp;&amp;amp; len(a.JPCERT) == 0 &amp;amp;&amp;amp; len(a.USCERT) == 0\n+\treturn len(a.JPCERT) == 0 &amp;amp;&amp;amp; len(a.USCERT) == 0\n }\n \n // FormatSource returns which source has this alert\n func (a AlertDict) FormatSource() string {\n \tvar s []string\n-\tif len(a.CISA) != 0 {\n-\t\ts = append(s, \"CISA\")\n-\t}\n \tif len(a.USCERT) != 0 || len(a.JPCERT) != 0 {\n \t\ts = append(s, \"CERT\")\n \t}\n@@ -1089,3 +1087,56 @@ var (\n \t// FortinetVendorProductMatch is a ranking how confident the CVE-ID was detected correctly\n \tFortinetVendorProductMatch = Confidence{10, FortinetVendorProductMatchStr, 9}\n )\n+\n+// KEVType represents the source of the KEV\n+type KEVType string\n+\n+const (\n+\t// CISAKEVType is for CISA KEVs\n+\tCISAKEVType KEVType = \"cisa\"\n+\n+\t// VulnCheckKEVType is for VulnCheck KEVs\n+\tVulnCheckKEVType KEVType = \"vulncheck\"\n+)\n+\n+// KEV represents a Known Exploited Vulnerability entry\n+type KEV struct {\n+\tType                       KEVType    `json:\"type\"`\n+\tVendorProject              string     `json:\"vendorProject,omitempty\"`\n+\tProduct                    string     `json:\"product,omitempty\"`\n+\tVulnerabilityName          string     `json:\"vulnerabilityName,omitempty\"`\n+\tShortDescription           string     `json:\"shortDescription,omitempty\"`\n+\tRequiredAction             string     `json:\"requiredAction,omitempty\"`\n+\tKnownRansomwareCampaignUse string     `json:\"knownRansomwareCampaignUse,omitempty\"`\n+\tDateAdded                  time.Time  `json:\"dateAdded,omitempty\"`\n+\tDueDate                    *time.Time `json:\"dueDate,omitempty\"`\n+\n+\tCISA      *CISAKEV      `json:\"cisa,omitempty\"`\n+\tVulnCheck *VulnCheckKEV `json:\"vulncheck,omitempty\"`\n+}\n+\n+// CISAKEV holds CISA-specific KEV fields\n+type CISAKEV struct {\n+\tNote string `json:\"note,omitempty\"`\n+}\n+\n+// VulnCheckKEV holds VulnCheck-specific KEV fields\n+type VulnCheckKEV struct {\n+\tXDB                  []VulnCheckXDB                  `json:\"xdb,omitempty\"`\n+\tReportedExploitation []VulnCheckReportedExploitation `json:\"reportedExploitation,omitempty\"`\n+}\n+\n+// VulnCheckXDB holds VulnCheck XDB information\n+type VulnCheckXDB struct {\n+\tXDBID       string    `json:\"xdbid,omitempty\"`\n+\tXDBURL      string    `json:\"xdburl,omitempty\"`\n+\tDateAdded   time.Time `json:\"dateAdded,omitempty\"`\n+\tExploitType string    `json:\"exploitType,omitempty\"`\n+\tCloneSSHURL string    `json:\"cloneSSHURL,omitempty\"`\n+}\n+\n+// VulnCheckReportedExploitation holds reported exploitation details\n+type VulnCheckReportedExploitation struct {\n+\tURL       string    `json:\"url,omitempty\"`\n+\tDateAdded time.Time `json:\"dateAdded,omitempty\"`\n+}\ndiff --git a/reporter/util.go b/reporter/util.go\nindex d9cfdaa..5b8697f 100644\n--- a/reporter/util.go\n+++ b/reporter/util.go\n@@ -205,6 +205,7 @@ func formatOneLineSummary(rs ...models.ScanResult) string {\n \t\t\t\tr.FormatExploitCveSummary(),\n \t\t\t\tr.FormatMetasploitCveSummary(),\n \t\t\t\tr.FormatAlertSummary(),\n+\t\t\t\tr.FormatKEVCveSummary(),\n \t\t\t}\n \t\t} else {\n \t\t\tcols = []interface{}{\n@@ -565,10 +566,6 @@ No CVE-IDs are found in updatable packages.\n \t\t})\n \t\tdata = append(data, ds...)\n \n-\t\tfor _, alert := range vuln.AlertDict.CISA {\n-\t\t\tdata = append(data, []string{\"CISA Alert\", alert.URL})\n-\t\t}\n-\n \t\tfor _, alert := range vuln.AlertDict.JPCERT {\n \t\t\tdata = append(data, []string{\"JPCERT Alert\", alert.URL})\n \t\t}\ndiff --git a/sample-kev-result.json b/sample-kev-result.json\nnew file mode 100644\nindex 0000000..2d940a6\n--- /dev/null\n+++ b/sample-kev-result.json\n@@ -0,0 +1,70 @@\n+{\n+  \"jsonVersion\": 1,\n+  \"lang\": \"en\",\n+  \"serverUUID\": \"00000000-0000-0000-0000-000000000000\",\n+  \"serverName\": \"example-host\",\n+  \"family\": \"ubuntu\",\n+  \"release\": \"20.04\",\n+  \"scannedAt\": \"2023-10-01T12:00:00Z\",\n+  \"scannedCves\": {\n+    \"CVE-0000-0000\": {\n+      \"cveID\": \"CVE-0000-0000\",\n+      \"confidences\": [\n+        {\n+          \"score\": 100,\n+          \"detectionMethod\": \"OvalMatch\"\n+        }\n+      ],\n+      \"cveContents\": {\n+        \"nvd\": [\n+          {\n+            \"type\": \"nvd\",\n+            \"cveID\": \"CVE-0000-0000\",\n+            \"title\": \"Example Documentation CVE\",\n+            \"summary\": \"This is a dummy CVE used for documentation purposes.\",\n+            \"cvss3Score\": 9.8,\n+            \"cvss3Severity\": \"CRITICAL\"\n+          }\n+        ]\n+      },\n+      \"kevs\": [\n+        {\n+          \"type\": \"cisa\",\n+          \"vendorProject\": \"Example Vendor\",\n+          \"product\": \"Example Product\",\n+          \"vulnerabilityName\": \"Example Vulnerability\",\n+          \"shortDescription\": \"This is an example description.\",\n+          \"requiredAction\": \"Apply updates per vendor instructions.\",\n+          \"knownRansomwareCampaignUse\": \"Unknown\",\n+          \"dateAdded\": \"2021-11-03T00:00:00Z\",\n+          \"cisa\": {\n+            \"note\": \"Example note\"\n+          }\n+        },\n+        {\n+          \"type\": \"vulncheck\",\n+          \"vendorProject\": \"Example Vendor\",\n+          \"product\": \"Example Product\",\n+          \"vulnerabilityName\": \"Example Vulnerability\",\n+          \"dateAdded\": \"2021-11-03T00:00:00Z\",\n+          \"vulncheck\": {\n+            \"xdb\": [\n+              {\n+                \"xdbid\": \"XDB-001\",\n+                \"xdburl\": \"https://example.com/xdb/001\",\n+                \"dateAdded\": \"2021-11-04T00:00:00Z\",\n+                \"exploitType\": \"RCE\"\n+              }\n+            ],\n+            \"reportedExploitation\": [\n+              {\n+                \"url\": \"https://example.com/report/123\",\n+                \"dateAdded\": \"2021-11-05T00:00:00Z\"\n+              }\n+            ]\n+          }\n+        }\n+      ]\n+    }\n+  }\n+}\n", "creation_timestamp": "2026-07-04T00:01:06.797929Z"}</content>
    <link href="https://vulnerability.circl.lu/sighting/35462476-7e5a-4994-90dd-00ad21806aa6/export"/>
    <published>2026-07-04T00:01:06.797929+00:00</published>
  </entry>
  <entry>
    <id>https://vulnerability.circl.lu/sighting/8e53b0a9-4fd1-45d4-8bad-8d56cd3b631b/export</id>
    <title>8e53b0a9-4fd1-45d4-8bad-8d56cd3b631b</title>
    <updated>2026-07-04T09:01:48.699865+00:00</updated>
    <author>
      <name>Automation user</name>
      <uri>https://cvepremium.circl.lu/user/automation</uri>
    </author>
    <content>{"uuid": "8e53b0a9-4fd1-45d4-8bad-8d56cd3b631b", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-0000-0000", "type": "seen", "source": "https://gist.github.com/tu-trinh-scale/bb428b700122569f3dd1ebe128ebea73", "content": "diff --git a/README.md b/README.md\nindex 57102d1..ceab982 100644\n--- a/README.md\n+++ b/README.md\n@@ -93,6 +93,9 @@ Vuls is a tool created to solve the problems listed above. It has the following\n - CISA(Cybersecurity &amp;amp; Infrastructure Security Agency)\n   - [Known Exploited Vulnerabilities Catalog](https://www.cisa.gov/known-exploited-vulnerabilities-catalog)\n \n+- VulnCheck\n+  - Known Exploited Vulnerabilities data is reported in the first-class `kevs` scan-result field alongside CISA KEV entries.\n+\n - Cyber Threat Intelligence(MITRE ATT&amp;amp;CK and CAPEC)\n   - [mitre/cti](https://github.com/mitre/cti)\n \n@@ -179,6 +182,12 @@ Vuls has some options to detect the vulnerabilities\n For more information such as Installation, Tutorial, Usage, visit [vuls.io](https://vuls.io/)  \n [\u65e5\u672c\u8a9e\u7ffb\u8a33\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8](https://vuls.io/ja/)\n \n+## KEV Reporting Example\n+\n+Use the synthetic `config.toml.example` as a starting point for enabling KEV reporting with the `[kevuln]` database configuration. Replace placeholder paths and host values with environment-appropriate values, and never copy production secrets into tracked configuration files.\n+\n+The synthetic `sample-kev-scan-result.json` shows the JSON shape for CISA and VulnCheck KEV entries under each vulnerability's `kevs` field.\n+\n ----\n \n ## Authors\ndiff --git a/config.toml.example b/config.toml.example\nnew file mode 100644\nindex 0000000..9db5842\n--- /dev/null\n+++ b/config.toml.example\n@@ -0,0 +1,40 @@\n+# Synthetic example configuration for enabling KEV reporting.\n+# Do not paste production secrets or internal host details into this file.\n+\n+[cveDict]\n+type = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/cve.sqlite3\"\n+\n+[ovalDict]\n+type = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/oval.sqlite3\"\n+\n+[gost]\n+type = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/gost.sqlite3\"\n+\n+[exploit]\n+type = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/go-exploitdb.sqlite3\"\n+\n+[metasploit]\n+type = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/go-msfdb.sqlite3\"\n+\n+[kevuln]\n+type = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/go-kev.sqlite3\"\n+\n+[cti]\n+type = \"sqlite3\"\n+sqlite3Path = \"/var/lib/vuls/go-cti.sqlite3\"\n+\n+[default]\n+scanMode = [\"fast-root\"]\n+port = \"22\"\n+user = \"vuls\"\n+keyPath = \"/home/vuls/.ssh/id_ed25519\"\n+\n+[servers.example-host]\n+host = \"192.0.2.10\"\n+scanMode = [\"fast-root\"]\ndiff --git a/detector/kevuln.go b/detector/kevuln.go\nindex 41afdfe..e675ecb 100644\n--- a/detector/kevuln.go\n+++ b/detector/kevuln.go\n@@ -6,6 +6,7 @@ package detector\n import (\n \t\"encoding/json\"\n \t\"net/http\"\n+\t\"strings\"\n \t\"time\"\n \n \t\"github.com/cenkalti/backoff\"\n@@ -79,19 +80,12 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging\n \t\t\t\treturn err\n \t\t\t}\n \n-\t\t\talerts := []models.Alert{}\n-\t\t\tif len(kevulns) &amp;gt; 0 {\n-\t\t\t\talerts = append(alerts, models.Alert{\n-\t\t\t\t\tTitle: \"Known Exploited Vulnerabilities Catalog\",\n-\t\t\t\t\tURL:   \"https://www.cisa.gov/known-exploited-vulnerabilities-catalog\",\n-\t\t\t\t\tTeam:  \"cisa\",\n-\t\t\t\t})\n-\t\t\t}\n-\n \t\t\tv, ok := r.ScannedCves[res.request.cveID]\n \t\t\tif ok {\n-\t\t\t\tv.AlertDict.CISA = alerts\n-\t\t\t\tnKEV++\n+\t\t\t\tv.KEVs = convertKEVulnsToModel(kevulns)\n+\t\t\t\tif 0 &amp;lt; len(v.KEVs) {\n+\t\t\t\t\tnKEV++\n+\t\t\t\t}\n \t\t\t}\n \t\t\tr.ScannedCves[res.request.cveID] = v\n \t\t}\n@@ -108,16 +102,7 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging\n \t\t\t\tcontinue\n \t\t\t}\n \n-\t\t\talerts := []models.Alert{}\n-\t\t\tif len(kevulns) &amp;gt; 0 {\n-\t\t\t\talerts = append(alerts, models.Alert{\n-\t\t\t\t\tTitle: \"Known Exploited Vulnerabilities Catalog\",\n-\t\t\t\t\tURL:   \"https://www.cisa.gov/known-exploited-vulnerabilities-catalog\",\n-\t\t\t\t\tTeam:  \"cisa\",\n-\t\t\t\t})\n-\t\t\t}\n-\n-\t\t\tvuln.AlertDict.CISA = alerts\n+\t\t\tvuln.KEVs = convertKEVulnsToModel(kevulns)\n \t\t\tnKEV++\n \t\t\tr.ScannedCves[cveID] = vuln\n \t\t}\n@@ -127,6 +112,156 @@ func FillWithKEVuln(r *models.ScanResult, cnf config.KEVulnConf, logOpts logging\n \treturn nil\n }\n \n+func convertKEVulnsToModel(kevulns []kevulnmodels.KEVuln) []models.KEV {\n+\tbs, err := json.Marshal(kevulns)\n+\tif err != nil {\n+\t\treturn nil\n+\t}\n+\n+\tvar raws []map[string]interface{}\n+\tif err := json.Unmarshal(bs, &amp;amp;raws); err != nil {\n+\t\treturn nil\n+\t}\n+\n+\tkevs := make([]models.KEV, 0, len(raws))\n+\tfor _, raw := range raws {\n+\t\tkev := models.KEV{\n+\t\t\tType:                       parseKEVType(firstString(raw, \"type\", \"source\")),\n+\t\t\tVendorProject:              firstString(raw, \"vendorProject\", \"vendor_project\", \"vendor\"),\n+\t\t\tProduct:                    firstString(raw, \"product\"),\n+\t\t\tVulnerabilityName:          firstString(raw, \"vulnerabilityName\", \"vulnerability_name\", \"name\"),\n+\t\t\tShortDescription:           firstString(raw, \"shortDescription\", \"short_description\", \"description\"),\n+\t\t\tRequiredAction:             firstString(raw, \"requiredAction\", \"required_action\"),\n+\t\t\tKnownRansomwareCampaignUse: firstString(raw, \"knownRansomwareCampaignUse\", \"known_ransomware_campaign_use\"),\n+\t\t\tDateAdded:                  firstTime(raw, \"dateAdded\", \"date_added\"),\n+\t\t}\n+\t\tif kev.Type == \"\" {\n+\t\t\tkev.Type = models.CISAKEVType\n+\t\t}\n+\t\tif dueDate := firstTime(raw, \"dueDate\", \"due_date\"); !dueDate.IsZero() {\n+\t\t\tkev.DueDate = &amp;amp;dueDate\n+\t\t}\n+\n+\t\tif note := firstString(raw, \"note\", \"notes\"); note != \"\" || kev.Type == models.CISAKEVType {\n+\t\t\tkev.CISA = &amp;amp;models.CISAKEV{Note: note}\n+\t\t}\n+\t\tif vulnCheck := parseVulnCheckKEV(raw); vulnCheck != nil {\n+\t\t\tkev.VulnCheck = vulnCheck\n+\t\t}\n+\n+\t\tkevs = append(kevs, kev)\n+\t}\n+\treturn kevs\n+}\n+\n+func parseKEVType(s string) models.KEVType {\n+\tswitch strings.ToLower(s) {\n+\tcase string(models.VulnCheckKEVType):\n+\t\treturn models.VulnCheckKEVType\n+\tcase string(models.CISAKEVType), \"\":\n+\t\treturn models.CISAKEVType\n+\tdefault:\n+\t\treturn models.KEVType(s)\n+\t}\n+}\n+\n+func parseVulnCheckKEV(raw map[string]interface{}) *models.VulnCheckKEV {\n+\tvulnCheckRaw := raw\n+\tif nested, ok := firstMap(raw, \"vulncheck\", \"vulnCheck\", \"vuln_check\"); ok {\n+\t\tvulnCheckRaw = nested\n+\t}\n+\n+\tvulnCheck := &amp;amp;models.VulnCheckKEV{\n+\t\tXDB:                  parseVulnCheckXDBs(vulnCheckRaw),\n+\t\tReportedExploitation: parseVulnCheckReportedExploitations(vulnCheckRaw),\n+\t}\n+\tif len(vulnCheck.XDB) == 0 &amp;amp;&amp;amp; len(vulnCheck.ReportedExploitation) == 0 {\n+\t\treturn nil\n+\t}\n+\treturn vulnCheck\n+}\n+\n+func parseVulnCheckXDBs(raw map[string]interface{}) []models.VulnCheckXDB {\n+\txdbs := []models.VulnCheckXDB{}\n+\tfor _, item := range firstSlice(raw, \"xdb\", \"xDB\", \"XDB\") {\n+\t\tm, ok := item.(map[string]interface{})\n+\t\tif !ok {\n+\t\t\tcontinue\n+\t\t}\n+\t\txdbs = append(xdbs, models.VulnCheckXDB{\n+\t\t\tXDBID:       firstString(m, \"xdbID\", \"xdbId\", \"xdb_id\", \"id\"),\n+\t\t\tXDBURL:      firstString(m, \"xdbURL\", \"xdbUrl\", \"xdb_url\", \"url\"),\n+\t\t\tDateAdded:   firstTime(m, \"dateAdded\", \"date_added\"),\n+\t\t\tExploitType: firstString(m, \"exploitType\", \"exploit_type\"),\n+\t\t\tCloneSSHURL: firstString(m, \"cloneSSHURL\", \"cloneSSHUrl\", \"clone_ssh_url\"),\n+\t\t})\n+\t}\n+\treturn xdbs\n+}\n+\n+func parseVulnCheckReportedExploitations(raw map[string]interface{}) []models.VulnCheckReportedExploitation {\n+\treported := []models.VulnCheckReportedExploitation{}\n+\tfor _, item := range firstSlice(raw, \"reportedExploitation\", \"reported_exploitation\", \"reportedExploitations\", \"reported_exploitations\") {\n+\t\tm, ok := item.(map[string]interface{})\n+\t\tif !ok {\n+\t\t\tcontinue\n+\t\t}\n+\t\treported = append(reported, models.VulnCheckReportedExploitation{\n+\t\t\tURL:       firstString(m, \"url\"),\n+\t\t\tDateAdded: firstTime(m, \"dateAdded\", \"date_added\"),\n+\t\t})\n+\t}\n+\treturn reported\n+}\n+\n+func firstString(m map[string]interface{}, keys ...string) string {\n+\tfor _, key := range keys {\n+\t\tif v, ok := m[key]; ok {\n+\t\t\tif s, ok := v.(string); ok {\n+\t\t\t\treturn s\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn \"\"\n+}\n+\n+func firstTime(m map[string]interface{}, keys ...string) time.Time {\n+\tfor _, key := range keys {\n+\t\tif v, ok := m[key]; ok {\n+\t\t\tif s, ok := v.(string); ok {\n+\t\t\t\tfor _, layout := range []string{time.RFC3339, \"2006-01-02\", \"2006-01-02 15:04:05\"} {\n+\t\t\t\t\tif t, err := time.Parse(layout, s); err == nil {\n+\t\t\t\t\t\treturn t\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn time.Time{}\n+}\n+\n+func firstMap(m map[string]interface{}, keys ...string) (map[string]interface{}, bool) {\n+\tfor _, key := range keys {\n+\t\tif v, ok := m[key]; ok {\n+\t\t\tif nested, ok := v.(map[string]interface{}); ok {\n+\t\t\t\treturn nested, true\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn nil, false\n+}\n+\n+func firstSlice(m map[string]interface{}, keys ...string) []interface{} {\n+\tfor _, key := range keys {\n+\t\tif v, ok := m[key]; ok {\n+\t\t\tif items, ok := v.([]interface{}); ok {\n+\t\t\t\treturn items\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn nil\n+}\n+\n type kevulnResponse struct {\n \trequest kevulnRequest\n \tjson    string\ndiff --git a/models/scanresults.go b/models/scanresults.go\nindex 508b992..4ad0d3f 100644\n--- a/models/scanresults.go\n+++ b/models/scanresults.go\n@@ -197,11 +197,12 @@ func (r ScanResult) FormatTextReportHeader() string {\n \t\tpkgs = fmt.Sprintf(\"%s, %d libs\", pkgs, r.LibraryScanners.Total())\n \t}\n \n-\treturn fmt.Sprintf(\"%s\\n%s\\n%s\\n%s, %s, %s, %s\\n%s\\n\",\n+\treturn fmt.Sprintf(\"%s\\n%s\\n%s\\n%s, %s, %s, %s, %s\\n%s\\n\",\n \t\tr.ServerInfo(),\n \t\tbuf.String(),\n \t\tr.ScannedCves.FormatCveSummary(),\n \t\tr.ScannedCves.FormatFixedStatus(r.Packages),\n+\t\tr.FormatKEVCveSummary(),\n \t\tr.FormatExploitCveSummary(),\n \t\tr.FormatMetasploitCveSummary(),\n \t\tr.FormatAlertSummary(),\n@@ -251,15 +252,22 @@ func (r ScanResult) FormatMetasploitCveSummary() string {\n \treturn fmt.Sprintf(\"%d exploits\", nMetasploitCve)\n }\n \n+// FormatKEVCveSummary returns a summary of CVEs with Known Exploited Vulnerability entries.\n+func (r ScanResult) FormatKEVCveSummary() string {\n+\tnKEVCve := 0\n+\tfor _, vuln := range r.ScannedCves {\n+\t\tif 0 &amp;lt; len(vuln.KEVs) {\n+\t\t\tnKEVCve++\n+\t\t}\n+\t}\n+\treturn fmt.Sprintf(\"%d kevs\", nKEVCve)\n+}\n+\n // FormatAlertSummary returns a summary of CERT alerts\n func (r ScanResult) FormatAlertSummary() string {\n-\tcisaCnt := 0\n \tuscertCnt := 0\n \tjpcertCnt := 0\n \tfor _, vuln := range r.ScannedCves {\n-\t\tif len(vuln.AlertDict.CISA) &amp;gt; 0 {\n-\t\t\tcisaCnt += len(vuln.AlertDict.CISA)\n-\t\t}\n \t\tif len(vuln.AlertDict.USCERT) &amp;gt; 0 {\n \t\t\tuscertCnt += len(vuln.AlertDict.USCERT)\n \t\t}\n@@ -267,7 +275,7 @@ func (r ScanResult) FormatAlertSummary() string {\n \t\t\tjpcertCnt += len(vuln.AlertDict.JPCERT)\n \t\t}\n \t}\n-\treturn fmt.Sprintf(\"cisa: %d, uscert: %d, jpcert: %d alerts\", cisaCnt, uscertCnt, jpcertCnt)\n+\treturn fmt.Sprintf(\"uscert: %d, jpcert: %d alerts\", uscertCnt, jpcertCnt)\n }\n \n func (r ScanResult) isDisplayUpdatableNum(mode config.ScanMode) bool {\n@@ -422,6 +430,12 @@ func (r *ScanResult) SortForJSONOutput() {\n \t\tsort.Slice(v.Metasploits, func(i, j int) bool {\n \t\t\treturn v.Metasploits[i].Name &amp;lt; v.Metasploits[j].Name\n \t\t})\n+\t\tsort.Slice(v.KEVs, func(i, j int) bool {\n+\t\t\tif v.KEVs[i].Type != v.KEVs[j].Type {\n+\t\t\t\treturn v.KEVs[i].Type &amp;lt; v.KEVs[j].Type\n+\t\t\t}\n+\t\t\treturn v.KEVs[i].VulnerabilityName &amp;lt; v.KEVs[j].VulnerabilityName\n+\t\t})\n \t\tsort.Slice(v.Mitigations, func(i, j int) bool {\n \t\t\treturn v.Mitigations[i].URL &amp;lt; v.Mitigations[j].URL\n \t\t})\ndiff --git a/models/vulninfos.go b/models/vulninfos.go\nindex 3e85e81..875ce66 100644\n--- a/models/vulninfos.go\n+++ b/models/vulninfos.go\n@@ -265,6 +265,7 @@ type VulnInfo struct {\n \tCveContents          CveContents          `json:\"cveContents,omitempty\"`\n \tExploits             []Exploit            `json:\"exploits,omitempty\"`\n \tMetasploits          []Metasploit         `json:\"metasploits,omitempty\"`\n+\tKEVs                 []KEV                `json:\"kevs,omitempty\"`\n \tMitigations          []Mitigation         `json:\"mitigations,omitempty\"`\n \tCtis                 []string             `json:\"ctis,omitempty\"`\n \tAlertDict            AlertDict            `json:\"alertDict,omitempty\"`\n@@ -284,6 +285,58 @@ type Alert struct {\n \tTeam  string `json:\"team,omitempty\"`\n }\n \n+// KEVType identifies the source of Known Exploited Vulnerability data.\n+type KEVType string\n+\n+const (\n+\t// CISAKEVType is CISA Known Exploited Vulnerabilities Catalog data.\n+\tCISAKEVType KEVType = \"cisa\"\n+\n+\t// VulnCheckKEVType is VulnCheck Known Exploited Vulnerabilities data.\n+\tVulnCheckKEVType KEVType = \"vulncheck\"\n+)\n+\n+// KEV has Known Exploited Vulnerability information.\n+type KEV struct {\n+\tType                         KEVType       `json:\"type,omitempty\"`\n+\tVendorProject                string        `json:\"vendorProject,omitempty\"`\n+\tProduct                      string        `json:\"product,omitempty\"`\n+\tVulnerabilityName            string        `json:\"vulnerabilityName,omitempty\"`\n+\tShortDescription             string        `json:\"shortDescription,omitempty\"`\n+\tRequiredAction               string        `json:\"requiredAction,omitempty\"`\n+\tKnownRansomwareCampaignUse   string        `json:\"knownRansomwareCampaignUse,omitempty\"`\n+\tDateAdded                    time.Time     `json:\"dateAdded,omitempty\"`\n+\tDueDate                      *time.Time    `json:\"dueDate,omitempty\"`\n+\tCISA                         *CISAKEV      `json:\"cisa,omitempty\"`\n+\tVulnCheck                    *VulnCheckKEV `json:\"vulncheck,omitempty\"`\n+}\n+\n+// CISAKEV has CISA-specific Known Exploited Vulnerability information.\n+type CISAKEV struct {\n+\tNote string `json:\"note,omitempty\"`\n+}\n+\n+// VulnCheckKEV has VulnCheck-specific Known Exploited Vulnerability information.\n+type VulnCheckKEV struct {\n+\tXDB                  []VulnCheckXDB                  `json:\"xdb,omitempty\"`\n+\tReportedExploitation []VulnCheckReportedExploitation `json:\"reportedExploitation,omitempty\"`\n+}\n+\n+// VulnCheckXDB has VulnCheck exploit database information.\n+type VulnCheckXDB struct {\n+\tXDBID       string    `json:\"xdbID,omitempty\"`\n+\tXDBURL      string    `json:\"xdbURL,omitempty\"`\n+\tDateAdded   time.Time `json:\"dateAdded,omitempty\"`\n+\tExploitType string    `json:\"exploitType,omitempty\"`\n+\tCloneSSHURL string    `json:\"cloneSSHURL,omitempty\"`\n+}\n+\n+// VulnCheckReportedExploitation has VulnCheck reported exploitation information.\n+type VulnCheckReportedExploitation struct {\n+\tURL       string    `json:\"url,omitempty\"`\n+\tDateAdded time.Time `json:\"dateAdded,omitempty\"`\n+}\n+\n // GitHubSecurityAlerts is a list of GitHubSecurityAlert\n type GitHubSecurityAlerts []GitHubSecurityAlert\n \n@@ -910,24 +963,21 @@ type Mitigation struct {\n \tURL            string         `json:\"url,omitempty\"`\n }\n \n-// AlertDict has target cve JPCERT, USCERT and CISA alert data\n+// AlertDict has target cve JPCERT and USCERT alert data\n type AlertDict struct {\n-\tCISA   []Alert `json:\"cisa\"`\n+\tCISA   []Alert `json:\"-\"`\n \tJPCERT []Alert `json:\"jpcert\"`\n \tUSCERT []Alert `json:\"uscert\"`\n }\n \n // IsEmpty checks if the content of AlertDict is empty\n func (a AlertDict) IsEmpty() bool {\n-\treturn len(a.CISA) == 0 &amp;amp;&amp;amp; len(a.JPCERT) == 0 &amp;amp;&amp;amp; len(a.USCERT) == 0\n+\treturn len(a.JPCERT) == 0 &amp;amp;&amp;amp; len(a.USCERT) == 0\n }\n \n // FormatSource returns which source has this alert\n func (a AlertDict) FormatSource() string {\n \tvar s []string\n-\tif len(a.CISA) != 0 {\n-\t\ts = append(s, \"CISA\")\n-\t}\n \tif len(a.USCERT) != 0 || len(a.JPCERT) != 0 {\n \t\ts = append(s, \"CERT\")\n \t}\ndiff --git a/reporter/util.go b/reporter/util.go\nindex d9cfdaa..6cbe9fe 100644\n--- a/reporter/util.go\n+++ b/reporter/util.go\n@@ -202,6 +202,7 @@ func formatOneLineSummary(rs ...models.ScanResult) string {\n \t\t\t\tr.ScannedCves.FormatCveSummary(),\n \t\t\t\tr.ScannedCves.FormatFixedStatus(r.Packages),\n \t\t\t\tr.FormatUpdatablePkgsSummary(),\n+\t\t\t\tr.FormatKEVCveSummary(),\n \t\t\t\tr.FormatExploitCveSummary(),\n \t\t\t\tr.FormatMetasploitCveSummary(),\n \t\t\t\tr.FormatAlertSummary(),\n@@ -415,6 +416,15 @@ No CVE-IDs are found in updatable packages.\n \t\t\tdata = append(data, []string{\"GitHub\", alert.RepoURLPackageName()})\n \t\t}\n \n+\t\tfor _, kev := range vuln.KEVs {\n+\t\t\tlabel := fmt.Sprintf(\"KEV[%s]\", kev.Type)\n+\t\t\tvalue := kev.VulnerabilityName\n+\t\t\tif kev.ShortDescription != \"\" {\n+\t\t\t\tvalue = kev.ShortDescription\n+\t\t\t}\n+\t\t\tdata = append(data, []string{label, value})\n+\t\t}\n+\n \t\tfor _, wp := range vuln.WpPackageFixStats {\n \t\t\tif p, ok := r.WordPressPackages.Find(wp.Name); ok {\n \t\t\t\tif p.Type == models.WPCore {\n@@ -565,10 +575,6 @@ No CVE-IDs are found in updatable packages.\n \t\t})\n \t\tdata = append(data, ds...)\n \n-\t\tfor _, alert := range vuln.AlertDict.CISA {\n-\t\t\tdata = append(data, []string{\"CISA Alert\", alert.URL})\n-\t\t}\n-\n \t\tfor _, alert := range vuln.AlertDict.JPCERT {\n \t\t\tdata = append(data, []string{\"JPCERT Alert\", alert.URL})\n \t\t}\ndiff --git a/sample-kev-scan-result.json b/sample-kev-scan-result.json\nnew file mode 100644\nindex 0000000..9cf105c\n--- /dev/null\n+++ b/sample-kev-scan-result.json\n@@ -0,0 +1,58 @@\n+{\n+  \"jsonVersion\": 4,\n+  \"lang\": \"en\",\n+  \"serverName\": \"example-host\",\n+  \"family\": \"ubuntu\",\n+  \"release\": \"22.04\",\n+  \"scannedAt\": \"2026-07-03T00:00:00Z\",\n+  \"reportedAt\": \"2026-07-03T00:00:00Z\",\n+  \"scannedCves\": {\n+    \"CVE-0000-0000\": {\n+      \"cveID\": \"CVE-0000-0000\",\n+      \"kevs\": [\n+        {\n+          \"type\": \"cisa\",\n+          \"vendorProject\": \"Example Vendor\",\n+          \"product\": \"Example Product\",\n+          \"vulnerabilityName\": \"Example Product Vulnerability\",\n+          \"shortDescription\": \"Synthetic CISA KEV entry showing the first-class kevs field.\",\n+          \"requiredAction\": \"Apply vendor-provided mitigations or discontinue use if mitigations are unavailable.\",\n+          \"knownRansomwareCampaignUse\": \"Unknown\",\n+          \"dateAdded\": \"2026-07-01T00:00:00Z\",\n+          \"dueDate\": \"2026-07-22T00:00:00Z\",\n+          \"cisa\": {\n+            \"note\": \"Synthetic example only.\"\n+          }\n+        },\n+        {\n+          \"type\": \"vulncheck\",\n+          \"vendorProject\": \"Example Vendor\",\n+          \"product\": \"Example Product\",\n+          \"vulnerabilityName\": \"Example Product Vulnerability\",\n+          \"shortDescription\": \"Synthetic VulnCheck KEV entry showing source-specific details.\",\n+          \"requiredAction\": \"Review exploitability and prioritize remediation.\",\n+          \"knownRansomwareCampaignUse\": \"Unknown\",\n+          \"dateAdded\": \"2026-07-02T00:00:00Z\",\n+          \"vulncheck\": {\n+            \"xdb\": [\n+              {\n+                \"xdbID\": \"XDB-000000\",\n+                \"xdbURL\": \"https://example.invalid/xdb/XDB-000000\",\n+                \"dateAdded\": \"2026-07-02T00:00:00Z\",\n+                \"exploitType\": \"initial-access\",\n+                \"cloneSSHURL\": \"ssh://example.invalid/research/example.git\"\n+              }\n+            ],\n+            \"reportedExploitation\": [\n+              {\n+                \"url\": \"https://example.invalid/reports/CVE-0000-0000\",\n+                \"dateAdded\": \"2026-07-02T00:00:00Z\"\n+              }\n+            ]\n+          }\n+        }\n+      ]\n+    }\n+  },\n+  \"packages\": {}\n+}\ndiff --git a/tui/tui.go b/tui/tui.go\nindex 4407f56..093ab65 100644\n--- a/tui/tui.go\n+++ b/tui/tui.go\n@@ -812,13 +812,17 @@ func setChangelogLayout(g *gocui.Gui) error {\n \t\t\t}\n \t\t}\n \n-\t\tif len(vinfo.AlertDict.CISA) &amp;gt; 0 {\n+\t\tif len(vinfo.KEVs) &amp;gt; 0 {\n \t\t\tlines = append(lines, \"\\n\",\n-\t\t\t\t\"CISA Alert\",\n-\t\t\t\t\"===========\",\n+\t\t\t\t\"Known Exploited Vulnerabilities\",\n+\t\t\t\t\"===============================\",\n \t\t\t)\n-\t\t\tfor _, alert := range vinfo.AlertDict.CISA {\n-\t\t\t\tlines = append(lines, fmt.Sprintf(\"* [%s](%s)\", alert.Title, alert.URL))\n+\t\t\tfor _, kev := range vinfo.KEVs {\n+\t\t\t\tdesc := kev.VulnerabilityName\n+\t\t\t\tif kev.ShortDescription != \"\" {\n+\t\t\t\t\tdesc = kev.ShortDescription\n+\t\t\t\t}\n+\t\t\t\tlines = append(lines, fmt.Sprintf(\"* %s: %s\", kev.Type, desc))\n \t\t\t}\n \t\t}\n \n", "creation_timestamp": "2026-07-04T00:01:06.750630Z"}</content>
    <link href="https://vulnerability.circl.lu/sighting/8e53b0a9-4fd1-45d4-8bad-8d56cd3b631b/export"/>
    <published>2026-07-04T00:01:06.750630+00:00</published>
  </entry>
</feed>
