cve-search: a local CVE and CPE database behind the CIRCL public service
cve-search - a tool to perform local searches for known vulnerabilities
At a glance
- What is it?
- A Flask and MongoDB tool that imports CVE, CPE, CWE, CAPEC and EPSS data into a local database for fast searching, with a CLI, a web interface, a REST API and the ranking tables that let an organisation track its own critical software.
- Who is it for?
- cve-search earns its place in one specific situation: repeated lookups where you do not want to hit a public API, either for speed or because the queries reveal your inventory. The local database plus CLI covers that well, and the ranking tables are the feature no public service can offer you, since they encode which CPE names matter to your own department.
- Can I use it commercially?
- Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 40 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Why a local copy of the CVE database
The stated purpose is narrow and clear: import CVE and CPE data into MongoDB so searches run locally. The README's reasoning is operational rather than ideological. Direct lookups against the public CVE databases are slow, and sensitive queries leak through the network, so an organisation that runs repeated product searches wants its own copy.
Four components follow from that. There is a back end that stores vulnerabilities and related data, a web interface for searching and managing them, a set of command line tools for querying, and a web API. The project has a notable pedigree: the README says the public CVE services run by CIRCL were powered by cve-search, before being superseded by Vulnerability-Lookup.
The scope has grown past raw CVE entries. The update sources the admin endpoint exposes include CPE, CVE, CWE, CAPEC, VIA4 and EPSS, so the database carries weakness taxonomies, attack patterns and exploit prediction scores alongside the vulnerability records. The v6.0.1 release added EPSS metrics to the search interface, which is the sort of addition that changes what a tool is for: with EPSS attached you can sort by likelihood of exploitation rather than by severity score alone.
The repository is a Python project on AGPL-3.0 with roughly 2,650 stars and 619 forks, no open issues, and a last push on 2026-09-02. The tree shows the structure clearly: `bin/` for user commands, `sbin/` for administration scripts, `web/` for the Flask application, `lib/` for the core library, `test/`, `docs/` and several `requirements` files split into prod, dev and system variants.
The search CLI and the flags that decide how strict it is
`bin/search.py` is the tool most people will use, and its help text in the README is worth reading because the matching flags change results more than most users expect. A product search takes a CPE-style string:
./bin/search.py -p cisco:ios:12.4
./bin/search.py -p cisco:ios:12.4 -o json
./bin/search.py -f nagios -n
./bin/search.py -p microsoft:windows_7 -o htmlThe important one is `--only-if-vulnerable`. Without it, a search for `windows_7` will also return vulnerabilities that merely list Windows 7 as an affected operating system, for example something in Adobe Reader. With it, only records directly assigned to that product come back. That single flag is the difference between a usable product inventory and a pile of irrelevant advisories.
`--strict_vendor_product` requires the vendor and product to match rather than just the product string, and `--lax` disables strict version comparison entirely, which the README warns will likely produce false positives for versions that were not yet vulnerable. Output formats cover csv, html, json, xml and a bare cveid listing. `-f` is free text over the summary, `-c` takes specific CVE IDs, `-s` searches summary text, `-t` and `-T` limit to the last n days by publication date and by modification date respectively, and `-i` caps the number of results.
Two flags add derived data rather than filtering: `-n` looks up the complete CPE name for a vulnerable configuration, and `-r` returns the ranking of that configuration. `-a` looks up CAPEC entries for related CWE weaknesses. That combination, ranking plus CPE plus CAPEC, is how you go from an identifier to an attack pattern in one command.
Ranking tables for knowing what matters to your organisation
The ranking database is the feature that makes this more than a search box. You assign CPE names a rating within a group you choose, and that group can be a department, an organisation, or anything meaningful to you. The README's example is apt: `sap:netweaver` scored as critical for an accounting team.
Administration is a single script:
./sbin/db_ranking.py -c "sap:netweaver" -g "accounting" -r 3and the lookup side reuses the same flag on the search tool:
./bin/search.py -c CVE-2012-4341 -r -nNothing on the public CVE services can do this, because the ranking is local knowledge. A vulnerability in a niche line-of-business product matters more to one team than a widely reported one does, and encoding that as data in the database beats maintaining a separate spreadsheet that nobody updates.
The README also shows the tool being composed with ordinary Unix tools, which is a good sign about the output format. The example asks who uses the term unknown for their vulnerabilities, then pipes the fulltext search through `jq`, `cut`, `sort` and `uniq -c`:
python3 bin/search_fulltext.py -q unknown -f \
| jq -c '. | .vulnerable_configuration[0]' \
| cut -f5 -d: | sort | uniq -c | sort -nr | head -10A second example feeds JSON output into `jq` and then R for summary statistics, comparing the CVSS distribution of `oracle:java` against `sun:jre`. That the output survives this kind of piping without a custom client is the point: json output plus flat records means you can analyse the data with whatever you already use.
Fulltext indexing and keyword visualisation
Search by identifier only gets you so far, so there is a Whoosh-based fulltext index over the CVE collection. Building it is one script, and `-l 0` means index everything rather than a recent window:
./sbin/db_fulltext.py -l 0Querying is separate from searching, and the distinction is useful. Without `-f` you get a list of matching CVE IDs, and with it you get the full JSON object per record:
./bin/search_fulltext.py -q NFS -q Linux
./bin/search_fulltext.py -q NFS -q Linux -fMultiple `-q` terms combine, so you can narrow a broad term like Linux with a specific one like NFS.
There is also a visualisation mode, which the README describes as generating JSON data for the most common keywords across CVE entries. It uses NLTK to lemmatize words into base forms and to filter out common English stopwords, so plurals and inflections collapse together. The practical value is finding which vulnerability terms cluster: it tells you which weakness categories dominate your data and whether the distribution looks plausible for your industry.
For recency, `bin/dump_last.py` dumps the last entries in RSS or Atom format, which is how the project feeds a reader the newest CVE records without anyone polling a page. That is a small feature, and it is the one most likely to be wired into a team channel.
The web layer and a published vulnerability in it
The web side is Flask with a good deal of structure around it. The dependency list gives the shape: Flask 2.2.5, Flask-RESTX for the API with Swagger docs, Flask-Login for sessions, Flask-WTF and WTForms for forms, Flask-Bootstrap4 and Flask-Breadcrumbs for navigation, flask-menu, Jinja2, Werkzeug, gunicorn for serving, oauthlib for authorisation flows, redis 4.5.4 for caching or sessions, Whoosh 2.7.4 for the search index, nltk 3.10.3 for the keyword work, dnspython, and `cvexplore` at 0.3.41 pinned, which is the data import layer.
The database side was modernised in v6.0.0, released on 2025-12-09. The notes describe refactoring for full PyMongo 4.x compatibility, tested against MongoDB 4.4, 6.0 and 8.0, plus lazy-loading of database connections so multi-process environments such as gunicorn behave. That same release added manual source selection to the `/updatedb` admin endpoint, so you can choose which of CPE, CVE, CWE, CAPEC, VIA4 or EPSS to refresh, and added update locking with an admin option to clear a stale lock.
Then there is v6.0.1 from 2026-07-05, which is worth reading in full because it is an admission. The release fixes an unauthenticated NoSQL injection and information disclosure in the `POST /fetch_cve_data` endpoint, tracked as CVE-2026-59509 and as GCVE-1-2026-20055. The endpoint had accepted attacker-controlled parameters for MongoDB collection selection, field projection, filtering, sorting and pagination, so a remote unauthenticated attacker could reach unintended application data including administrative usernames and password hashes when local authentication was enabled. The fix restricts the endpoint to the intended CVE dataset, allowlists projected and sortable fields, rejects invalid column definitions with a 400, and bounds page sizes.
A project that assigns its own advisory a CVE and publishes the detail is behaving correctly, but the practical takeaway is concrete: run the web interface with authentication enabled and keep it off untrusted networks, and upgrade past 6.0.1.
Release cadence and the loose ends in the dependency set
Releases are infrequent and substantive. v5.2.0 on 2025-03-17 is a long generated changelog, and reading it tells you what gets attention: CVSS v4 support added to the web interface with filtering, sorting and a Swagger update, a CWE array maintained in the web UI and the API, a `-T` flag on `search.py` to filter by days since modified as distinct from days since published, and a pass of table improvements in the WebUI. There are also routine dependency bumps by dependabot, including an NLTK update.
That CVSS v4 work is the substantive part. The scoring system changed, so a tool that only understood v3 would misreport the severity of anything scored under v4, and the v5.2.0 changes cover display, filtering, sorting, table cell sizing and the API documentation together.
The dependency set carries one oddity worth verifying before you install. `requirements.txt` pins Flask 2.2.5 while Werkzeug is at 3.1.6, and those two versions have had a documented incompatibility around URL quoting and some form handling. A project with its own test suite and continuous integration badges would presumably catch it, but if you hit strange behaviour in form parsing or routing, check that pair first. There is also a raw feedformatter URL pinned directly from a master branch archive rather than a released version, which means that dependency can change without a version bump.
Documentation lives in a `docs/` folder with a published site at cve-search.github.io, and the README is deliberately a starting point rather than a manual. Between them, what is not written down is the operational procedure that matters most in practice: how often to refresh each source, how large the database gets, and how long a full CPE import takes on your hardware.
Editorial conclusion
cve-search earns its place in one specific situation: repeated lookups where you do not want to hit a public API, either for speed or because the queries reveal your inventory. The local database plus CLI covers that well, and the ranking tables are the feature no public service can offer you, since they encode which CPE names matter to your own department. The costs are real. Someone published a CVE against this project in July 2026, so the web layer deserves authentication and a firewall even on an internal network, and the pinned Flask 2.2.5 alongside Werkzeug 3.1.6 is a combination worth checking against your own environment. Start with `cvexplore` to populate a database, then use `bin/search.py` for scripted lookups and the ranking scripts once you know which products matter.
Frequently asked questions
What is CVE used for, and what does cve-search do with it?
CVE, the Common Vulnerabilities and Exposures system, assigns public identifiers to publicly disclosed vulnerabilities so systems can refer to them consistently. cve-search imports those records along with CPE names, CWE weaknesses, CAPEC patterns and EPSS scores into a local MongoDB, so you can search product inventories without querying a public API for every lookup.
Can you give me an example of a CVE search with cve-search?
A product lookup takes a CPE-style string, for example `./bin/search.py -p cisco:ios:12.4`, and you can add `--only-if-vulnerable` so results are limited to records directly assigned to that product rather than ones that merely mention it as an affected OS. A single record can be dumped with `./bin/search.py -c CVE-2010-3333 -o json`.
What security advisory applies to cve-search itself?
CVE-2026-59509, also tracked as GCVE-1-2026-20055, covers an unauthenticated NoSQL injection and information disclosure in the `POST /fetch_cve_data` endpoint. It was fixed in v6.0.1, released on 2026-07-05, which restricts the endpoint to the CVE dataset and validates projected, sortable and pagination parameters. The README's own tool can look it up locally.
How do you keep the local vulnerability database up to date?
The admin interface exposes an `/updatedb` endpoint that can refresh the database from selected sources, including CPE, CVE, CWE, CAPEC, VIA4 and EPSS. Since v6.0.0 that endpoint lets you choose the sources manually and applies an update lock so two refreshes cannot run at once, with an admin option to clear a stale lock.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/cve-search-cve-search)