ndaal Information Advisory: ndaal_public_nvulnlookup vl-web — JSON-API full-text search (/api/v1/vulnerabilities/search) returned 503/empty without Meilisearch and rejected the limit query parameter; embedded redb fallback + limit parsing restore parity with the HTML /search page
Metadata
| Tracking ID | ndaal-sa-2026-082 |
|---|---|
| Category | csaf_informational_advisory |
| CSAF Version | 2.1 |
| Status | final |
| Version | 1.0.0 |
| Publisher | ndaal Gesellschaft fuer Sicherheit in der Informationstechnik mbH & Co KG |
| TLP | CLEAR |
| Initial Release | 2026-05-30T00:00:00.000Z |
| Current Release | 2026-05-30T00:00:00.000Z |
Vulnerabilities (0)
No vulnerabilities.
Raw JSON
{
"$schema": "https://docs.oasis-open.org/csaf/csaf/v2.1/schema/csaf.json",
"document": {
"category": "csaf_informational_advisory",
"csaf_version": "2.1",
"distribution": {
"tlp": {
"label": "CLEAR"
}
},
"lang": "en",
"notes": [
{
"category": "summary",
"text": "The JSON full-text search endpoint GET /api/v1/vulnerabilities/search returned no results on every real instance. Two independent defects: (1) when Meilisearch was not configured the handler short-circuited with 503 {\"error\":\"Full-text search is not configured\"}, and even when Meilisearch WAS configured it never fell back when a query returned zero hits. Because the Meilisearch indexes are not populated by the feeder write path, the endpoint was effectively dead even though the always-available embedded redb store (and the HTML /search page) held the data. (2) The limit query parameter — typed Option<usize> against a query layer that stores every value as a JSON string — failed to deserialize, so any request with ?q=...&limit=N fell back to a fully-empty query and returned 400 \"Query parameter 'q' is required\"."
},
{
"category": "description",
"text": "Defect 1 — no embedded fallback. The HTML /search route already falls back to VulnStorage::embedded_search (the always-populated redb store) when Meilisearch is absent or returns zero hits; the JSON API did not. The handler was reworked so that when search_client() is None OR Meilisearch yields zero hits, it falls back to embedded_search and maps the redb rows into the same {id, source, title, description} JSON shape (commit c7fb0f3c3, with a log4j integration test pinning that ?q=log4j surfaces the Log4Shell advisories via the fallback).\n\nDefect 2 — limit parameter rejected. router::parse_query stores every query value as a JSON string, so serde cannot coerce \"5\" into a usize; the strongly-typed Option<usize> limit field therefore failed the whole struct deserialization, the parse fell back to its default (q: None), and the empty-query guard returned 400. The pre-existing single-vulnerability endpoint documents this exact pitfall and uses Option<String>; the search endpoint was fixed to accept the limit value the same way (commit 7c902934a).\n\nNet effect after both fixes: ?q=<term> and ?q=<term>&limit=<N> both return results from the embedded store on an instance with no Meilisearch, matching the long-working HTML /search behaviour."
},
{
"category": "details",
"text": "Reproduction (pre-fix), Meilisearch not configured:\n GET /api/v1/vulnerabilities/search?q=log4j -> 503 \"Full-text search is not configured\"\n GET /api/v1/vulnerabilities/search?q=log4j&limit=5 -> 400 \"Query parameter 'q' is required\"\nwhile GET /search?q=log4j (HTML) returned the Log4Shell advisories.\n\nPost-fix: both JSON requests return the redb-backed results. Note: relevance ranking is still substring-based until the Meilisearch index write path is wired (the feeder write path does not yet call SearchClient::index_document; a bulk re-index aggregator that does was added in the same dev cycle but indexing remains best-effort and non-fatal)."
},
{
"category": "general",
"text": "Recommended action: upgrade to the next tagged release carrying commits c7fb0f3c3 and 7c902934a. No configuration change is required — the embedded redb fallback is always available, so the JSON search endpoint works with or without a configured Meilisearch."
},
{
"category": "other",
"text": "Severity classification: functional defect on a documented API endpoint. No data loss, no security exposure — the endpoint returned an error or empty set rather than wrong data. Impact: programmatic consumers of /api/v1/vulnerabilities/search received no results on every instance that does not run a populated Meilisearch (i.e. the default posture). Residual risk after the fix: low. Status: final."
},
{
"category": "legal_disclaimer",
"text": "THIS DOCUMENT IS PROVIDED ON AN 'AS IS' BASIS AND DOES NOT IMPLY ANY KIND OF GUARANTEE OR WARRANTY. ndaal Gesellschaft fuer Sicherheit in der Informationstechnik mbH & Co KG DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS INFORMATION INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS."
}
],
"publisher": {
"category": "vendor",
"contact_details": "security@ndaal.eu",
"issuing_authority": "ndaal Gesellschaft fuer Sicherheit in der Informationstechnik mbH & Co KG Security Team",
"name": "ndaal Gesellschaft fuer Sicherheit in der Informationstechnik mbH & Co KG",
"namespace": "https://ndaal.eu/csaf"
},
"references": [
{
"category": "self",
"summary": "This advisory in CSAF 2.1 format",
"url": "https://gitlab.com/vPierre/ndaal_public_nvulnlookup/-/raw/main/csaf/2026/082/ndaal-sa-2026-082.json"
},
{
"category": "external",
"summary": "ndaal_public_nvulnlookup repository",
"url": "https://gitlab.com/vPierre/ndaal_public_nvulnlookup"
},
{
"category": "external",
"summary": "JSON-API search handler (vl-web)",
"url": "https://gitlab.com/vPierre/ndaal_public_nvulnlookup/-/blob/main/vulnerability-lookup-rs/crates/vl-web/src/api/v1/vulnerability.rs"
},
{
"category": "external",
"summary": "Apache Log4Shell (CVE-2021-44228)",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2021-44228"
}
],
"title": "ndaal Information Advisory: ndaal_public_nvulnlookup vl-web — JSON-API full-text search (/api/v1/vulnerabilities/search) returned 503/empty without Meilisearch and rejected the limit query parameter; embedded redb fallback + limit parsing restore parity with the HTML /search page",
"tracking": {
"current_release_date": "2026-05-30T00:00:00.000Z",
"generator": {
"engine": {
"name": "ndaal CSAF Generator",
"version": "1.0.0"
}
},
"id": "ndaal-sa-2026-082",
"initial_release_date": "2026-05-30T00:00:00.000Z",
"revision_history": [
{
"date": "2026-05-30T00:00:00.000Z",
"number": "1.0.0",
"summary": "Initial informational advisory documenting two functional defects in the JSON full-text search endpoint GET /api/v1/vulnerabilities/search: (1) it returned 503 \"Full-text search is not configured\" when Meilisearch was absent and never fell back on zero hits, so it yielded no results on every instance whose Meilisearch indexes are unpopulated (the default), even though the embedded redb store and HTML /search held the data; (2) the limit query parameter failed to deserialize (Option<usize> against a string-valued query layer), making any ?q=...&limit=N request return 400. Fixes: embedded redb fallback (commit c7fb0f3c3) and limit parsing (commit 7c902934a). Functional defect, no security exposure. Status: final."
}
],
"status": "final",
"version": "1.0.0"
}
},
"product_tree": {
"branches": [
{
"category": "vendor",
"name": "ndaal Gesellschaft fuer Sicherheit in der Informationstechnik mbH & Co KG",
"branches": [
{
"category": "product_name",
"name": "ndaal_public_nvulnlookup",
"branches": [
{
"category": "product_version_range",
"name": "vers:cargo/<=0.1.49",
"product": {
"name": "ndaal_public_nvulnlookup vl-web at 0.1.49 and earlier — GET /api/v1/vulnerabilities/search returns 503 when Meilisearch is unconfigured (and never falls back on zero hits), and rejects the limit query parameter (returns 400), so the JSON search endpoint yields no results on a default instance.",
"product_id": "CSAFPID-0001"
}
},
{
"category": "product_version",
"name": "0.1.49-dev",
"product": {
"name": "ndaal_public_nvulnlookup vl-web development branch (commits c7fb0f3c3 + 7c902934a) — the JSON search endpoint falls back to the embedded redb store and accepts the limit parameter, matching the HTML /search page.",
"product_id": "CSAFPID-0002"
}
}
]
}
]
}
]
}
}