Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 35 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,31 @@ An AWS load balancer can use `/api/ready-check` to stop routing search traffic t

## API Input Validation

`POST /search` accepts an optional JSON object request body and validates supported fields, boolean parameters, pagination values, CVSS severity values, required parameter combinations, CPE Name requirements, and date-time formats. A non-empty body must use the `application/json` media type with UTF-8 encoding; unsupported media types or encodings return HTTP 415, and malformed JSON returns HTTP 400. If OpenSearch is unavailable, the endpoint returns HTTP 503 with a `Retry-After` header.
`POST /search` accepts an optional JSON object request body and validates supported fields, boolean parameters, pagination values, CVSS severity values, required parameter combinations, CPE Name requirements, and date-time formats. Enum-valued fields are accepted case-insensitively and normalized to their documented canonical values. A non-empty body must use the `application/json` media type with UTF-8 encoding; unsupported media types or encodings return HTTP 415, and malformed JSON returns HTTP 400. If OpenSearch is unavailable, the endpoint returns HTTP 503 with a `Retry-After` header.

Use the optional `scope` field to control which parts of each record participate in matching. Values are case-insensitive: `FULL` (the default) searches base record fields, CNA data, and every ADP provider; `CNA_ONLY` searches base record fields and CNA data; and `ADP_ONLY` searches base record fields and every ADP provider. Scope changes matching only, so successful responses always contain the complete CVE record. CPE applicability filters use CNA data and return HTTP 400 when combined with `ADP_ONLY`.

Scope applies to filter matching as follows:

| Filters | Data used for matching |
| -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| `cveIds`, `pubStartDate`, `pubEndDate`, `lastModStartDate`, `lastModEndDate` | Base record metadata in every scope |
| `noRejected` | Base record state in every scope, plus the legacy CNA state when CNA data is selected |
| `keywordSearch`, `keywordExactMatch` | Description text in the selected CNA and/or ADP containers |
| `cweId`, all CVSS severity and metric filters | Problem types or metrics in the selected CNA and/or ADP containers |
| All SSVC filters, `knownExploited`, KEV date filters, `referenceURL`, `disputed` | Structured metrics, references, or tags in the selected CNA and/or ADP containers |
| `cpeName`, `isVulnerable`, `virtualMatchString`, and version range filters | CNA CPE applicability data; incompatible with `ADP_ONLY` |
| `resultsPerPage`, `pageNumber` | Response controls; they do not select record data |

If the selected containers do not contain the requested data, the request is valid and returns zero matches. The API does not remove unselected containers from a returned record.

SSVC metrics can be filtered with `ssvcExploitation` (`NONE`, `POC`, or `ACTIVE`), `ssvcAutomatable` (`YES` or `NO`), `ssvcTechnicalImpact` (`PARTIAL` or `TOTAL`), `ssvcRole`, `ssvcTimestampStartDate`, and `ssvcTimestampEndDate`. Enum and role comparisons are case-insensitive, timestamps use the API's full UTC timestamp format with inclusive boundaries, and all supplied SSVC conditions must match the same structured SSVC metric entry in a container selected by `scope`.

Use `knownExploited` to include or exclude records with a structured KEV metric in containers selected by `scope`. The optional `kevDateAddedStartDate` and `kevDateAddedEndDate` boundaries are inclusive, require `knownExploited: true`, and use the API's full UTC timestamp format. Stored KEV `dateAdded` values are interpreted as midnight UTC.

Use `referenceURL` for a case-insensitive literal substring search over reference URLs in containers selected by `scope`. The filter accepts one string; user-supplied wildcard syntax is treated literally.

Use the strict boolean `disputed` filter to require (`true`) or exclude (`false`) the exact `disputed` tag in containers selected by `scope`. This does not provide a general-purpose record tag filter.

String inputs are bounded before query construction:

Expand All @@ -107,6 +131,8 @@ String inputs are bounded before query construction:
| `cpeName` | 2,048 characters |
| `virtualMatchString` | 2,048 characters |
| `versionStart`, `versionEnd` | 256 characters each |
| `ssvcRole` | 256 characters |
| `referenceURL` | 2,048 characters |
| CVSS metric fields | 256 characters, 32 slash-delimited tokens, and 32 characters per token |

Requests that exceed a limit return HTTP 400 before contacting OpenSearch. These bounds limit input size and query complexity without changing the documented CPE matching semantics.
Expand Down Expand Up @@ -216,13 +242,15 @@ While enabled, `POST /webSearch` requires a non-empty JSON `query` string. It al

## Swagger API docs

Swagger API docs are automatically generated on `docker compose` or `npm run dev`, and can be regenerated manually using:
Swagger API docs are regenerated by `npm run dev` and can be regenerated manually using:

```bash
npm run swagger-autogen
```

The Swagger API docs will be served at `localhost:3000/api-docs` while the CVE Search API is running via Docker or npm.
Docker builds copy the committed `api-docs/openapi.json` into the runtime image; they do not regenerate it. Regenerate and commit that file whenever the route or request schema changes.

The Swagger API docs will be served at `http://localhost:3000/api-docs` while the CVE Search API is running via Docker or npm with the default port.

## Testing

Expand Down Expand Up @@ -261,6 +289,10 @@ The fallback limiter uses one in-memory counter per Node.js process. Set `Search

This is a process-wide safety limit, not a precise distributed quota. Each ECS task has an independent counter, so total permitted traffic scales with the number of tasks and load-balancer routing. It deliberately does not use caller IP addresses or forwarded headers until the ALB proxy chain and Express `trust proxy` setting are confirmed. AWS WAF or another infrastructure-level distributed limit remains the preferred public-facing control.

### Semantic Post-Filtering

SSVC filters and date-bounded positive `knownExploited` searches use semantic post-filtering because OpenSearch ordinary-object arrays cannot guarantee that multiple conditions matched the same structured metric entry. These searches retrieve up to 10,000 lightweight candidates, apply same-entry checks in Node.js, and fetch the requested page as complete CVE records. The reported total and pagination are complete only within that bounded candidate set; qualifying records beyond the first 10,000 OpenSearch candidates may not be represented. This preserves the existing bounded post-filter behavior until PIT-based pagination is implemented. SSVC and KEV candidates do not use the temporary CPE cache or cold-search queue. A `knownExploited` filter without date boundaries uses a direct OpenSearch query and retains ordinary exact-total behavior.

### CPE Post-Filter Cache

Searches that use `cpeName`, `virtualMatchString`, or `virtualMatchString` with version range parameters use a semantic CPE post-filter. The API first asks OpenSearch for up to 10,000 lightweight candidate records, filters those candidates in Node.js, then fetches the requested page of full CVE records by CVE ID.
Expand Down
Loading
Loading