From 8dbabee422256e9263e147b5f69e2dd803a1269c Mon Sep 17 00:00:00 2001 From: Ved Thakar Date: Tue, 7 Apr 2026 21:49:03 -0400 Subject: [PATCH] feat: clarify API 404 behavior when querying alias CVE IDs --- docs/api/get-v1-vulns.md | 10 +++++++++- docs/api/post-v1-query.md | 8 +++++++- docs/faq.md | 16 ++++++++++++++++ gcp/api/integration_tests.py | 14 +++++++++++++- gcp/api/server.py | 7 +++++-- 5 files changed, 50 insertions(+), 5 deletions(-) diff --git a/docs/api/get-v1-vulns.md b/docs/api/get-v1-vulns.md index 47f5b18c98e..6fa45ba5d7f 100644 --- a/docs/api/get-v1-vulns.md +++ b/docs/api/get-v1-vulns.md @@ -78,4 +78,12 @@ curl "https://api.osv.dev/v1/vulns/OSV-2020-111" ], "schema_version": "1.4.0" } -``` \ No newline at end of file +``` + +## Error responses + +| HTTP status | `code` | `message` | Meaning | +|---|---|---|---| +| 404 | 5 | `Bug not found.` | No record exists for this ID and it is not known as an alias. | +| 404 | 5 | `Bug not found, but the following aliases were: ` | The ID is an alias; use one of the listed first-class IDs instead. See the [FAQ](/faq#api-404). | +| 400 | 3 | `ID too long` | The supplied ID exceeds 100 characters. | diff --git a/docs/api/post-v1-query.md b/docs/api/post-v1-query.md index 516603aa1c2..d718778e8ac 100644 --- a/docs/api/post-v1-query.md +++ b/docs/api/post-v1-query.md @@ -184,4 +184,10 @@ curl -d \ The API has a response size limit of 32MiB when using HTTP/1.1. There is **no limit** when using HTTP/2. We recommend using HTTP/2 for queries that may result in large responses. {: .note } -In rare cases, the response might contain **only** the `next_page_token`. In those cases, there might be more data that can be retrieved, but were not found within the time limit, please keep querying with the `next_page_token` until either results are returned, or no more page tokens are returned. \ No newline at end of file +In rare cases, the response might contain **only** the `next_page_token`. In those cases, there might be more data that can be retrieved, but were not found within the time limit, please keep querying with the `next_page_token` until either results are returned, or no more page tokens are returned. + +## Aliases {#aliases} + +OSV.dev records may reference external IDs (CVEs, GHSAs, etc.) as *aliases*. These aliases +do not have their own top-level records; `GET /v1/vulns/{id}` will return a 404 for them. +To search by package across all aliases, use `POST /v1/query` with the `package` field. diff --git a/docs/faq.md b/docs/faq.md index 6affa545bbd..6e385fff9e6 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -101,6 +101,22 @@ for the vulnerability will state this. You should raise this with the home database responsible for the record. +### Why does the API return "Bug not found" for a CVE I can see on the website? {#api-404} + +OSV.dev distinguishes between **first-class records** (vulnerabilities with a primary OSV +entry) and **aliases** (external IDs such as CVEs that are cross-referenced by one or more +OSV records but do not have their own entry). + +The `GET /v1/vulns/{id}` endpoint only returns a full record for a first-class OSV ID. If you +query a CVE that is listed as an alias on an OSV record, you will receive a 404 response +containing the IDs of the records that reference it as an alias. + +To find vulnerabilities by a CVE alias, use the +[`POST /v1/query`](/post-v1-query/) endpoint with the `package` field, or look up the +first-class ID from the alias list in the error response and query that directly. + +See also [Querying by first-class IDs vs. aliases](/post-v1-query/#aliases). + ### I've found something wrong with the data Data quality is very important to us. Please remember that OSV.dev is an diff --git a/gcp/api/integration_tests.py b/gcp/api/integration_tests.py index 567742e7a72..df6a087d20b 100644 --- a/gcp/api/integration_tests.py +++ b/gcp/api/integration_tests.py @@ -695,7 +695,19 @@ def test_get_vuln_by_alias_not_in_db(self): { 'code': 5, 'message': 'Bug not found, but the following aliases were: ' - 'MAL-2024-2291' + 'MAL-2024-2291. ' + 'See https://osv.dev/faq#api-404 for more information.' + }, response.json()) + + def test_get_vuln_not_found_no_alias(self): + """Test that a completely unknown ID returns Bug not found with FAQ link.""" + response = requests.get( + _api() + '/v1/vulns/CVE-0000-00000', timeout=_TIMEOUT) + self.assert_results_equal( + { + 'code': 5, + 'message': 'Bug not found. ' + 'See https://osv.dev/faq#api-404 for more information.' }, response.json()) def test_query_batch(self): diff --git a/gcp/api/server.py b/gcp/api/server.py index 1f25d0c9f59..a2d63ee4985 100644 --- a/gcp/api/server.py +++ b/gcp/api/server.py @@ -183,9 +183,12 @@ def GetVulnById(self, request, context: grpc.ServicerContext): ]) context.abort( grpc.StatusCode.NOT_FOUND, - f'Bug not found, but the following aliases were: {alias_string}') + f'Bug not found, but the following aliases were: {alias_string}. ' + 'See https://osv.dev/faq#api-404 for more information.') return None - context.abort(grpc.StatusCode.NOT_FOUND, 'Bug not found.') + context.abort( + grpc.StatusCode.NOT_FOUND, + 'Bug not found. See https://osv.dev/faq#api-404 for more information.') return None @ndb_context