Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1aae541
Remove Artillery test ownership
afoote-mitre Jul 22, 2026
a468db8
Add pull request quality workflow
afoote-mitre Jul 24, 2026
cfdbb65
Bound search queue and OpenSearch deadlines
afoote-mitre Jul 27, 2026
3f871fd
Add structured search telemetry and request logging
afoote-mitre Jul 27, 2026
0459724
Gate deprecated web search endpoint
afoote-mitre Jul 27, 2026
6629d98
Add fallback search rate limiting
afoote-mitre Jul 27, 2026
c77b297
Remediate owned dependency vulnerabilities
afoote-mitre Jul 27, 2026
a08497e
Merge pull request #109 from CVEProject/af/artillery-removal
jdaigneau5 Jul 29, 2026
0347312
fix: enforce CPE vulnerability and range semantics
afoote-mitre Jul 28, 2026
7415ffc
fix: harden search request and response handling
afoote-mitre Jul 28, 2026
71537e0
fix: bound search input complexity
afoote-mitre Jul 28, 2026
efbc94e
fix: cancel abandoned search work
afoote-mitre Jul 28, 2026
01a0c0a
feat: add OpenSearch readiness checks
afoote-mitre Jul 28, 2026
7e33edc
Merge pull request #115 from CVEProject/af/0.3.0-review
jdaigneau5 Aug 3, 2026
f6bc827
fix: align CPE NA range matching with NVD
afoote-mitre Jul 28, 2026
8523259
fix: order CPE version qualifiers semantically
afoote-mitre Jul 28, 2026
75d897e
fix: correlate all CPE name components
afoote-mitre Jul 28, 2026
ab6f0c2
fix: make CPE literal matching case-insensitive
afoote-mitre Jul 28, 2026
1fa490c
test: lock down NVD leading-zero version behavior
afoote-mitre Jul 28, 2026
0804d39
docs: remove NVD alignment language
afoote-mitre Jul 29, 2026
370b8c7
test: use direct CPE behavior descriptions
afoote-mitre Jul 29, 2026
175011b
Merge pull request #117 from CVEProject/af/cpe-filtering-fixes
jdaigneau5 Aug 3, 2026
5c7d0ed
feat: remove sourceIdentifier filter for 0.4.0-beta
afoote-mitre Jul 30, 2026
127d560
Merge pull request #118 from CVEProject/af/sourceIdentifier
jdaigneau5 Aug 3, 2026
3606706
Merge pull request #129 from CVEProject/dev
jdaigneau5 Aug 7, 2026
c2d5634
Merge pull request #130 from CVEProject/test
jdaigneau5 Aug 12, 2026
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
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,14 @@ SearchPostFilterCacheTtlMs=60000
SearchPostFilterCacheMaxEntries=100
SearchPostFilterColdSearchConcurrency=10
SearchPostFilterColdSearchMaxQueueDepth=500
SearchPostFilterColdSearchMaxWaitMs=10000
SearchOpenSearchRequestTimeoutMs=20000
SearchRequestDeadlineMs=30000
SearchReadinessTimeoutMs=5000
RequestLogSuccessSampleRate=0.1
RequestLogHealthChecks=false
WebSearchEnabled=true
SearchRateLimitEnabled=false
SearchRateLimitWindowMs=60000
SearchRateLimitMaxRequests=0
NPM_CA_CERT=
33 changes: 33 additions & 0 deletions .github/workflows/quality.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Search API Quality

on:
pull_request:

permissions:
contents: read

concurrency:
group: search-api-quality-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
quality:
name: Search API Quality
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 24.14.1
cache: npm

- name: Install dependencies
run: npm ci

- name: Run quality checks
run: npm run quality
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,6 @@ web_modules/
.env.test.local
.env.production.local
.env.local
test/artillery/.env
test/artillery/.env.*
!test/artillery/.env.example

# parcel-bundler cache (https://parceljs.org/)
.cache
Expand Down
119 changes: 72 additions & 47 deletions README.md

Large diffs are not rendered by default.

155 changes: 132 additions & 23 deletions api-docs/openapi.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"openapi": "3.0.2",
"info": {
"version": "0.3.0-beta",
"version": "0.4.0-beta",
"title": "CVE Search API",
"description": "CVE Search API to retrieve all matching CVE records after applying request inputs as filters.",
"contact": {
Expand All @@ -25,13 +25,61 @@
"operationId": "search",
"responses": {
"200": {
"description": "A list of matching CVE records"
"description": "A list of matching CVE records",
"headers": {
"X-Request-ID": {
"description": "Request correlation ID",
"schema": {
"type": "string"
}
}
}
},
"400": {
"description": "Bad Request"
"description": "Malformed JSON, invalid request body, parameter combination, or version range"
},
"404": {
"description": "Invalid cpeName value"
},
"415": {
"description": "A non-empty request body did not use the application/json media type with UTF-8 encoding"
},
"429": {
"description": "Application rate limit exceeded, or the cold CPE search queue is full or exceeded its maximum wait",
"headers": {
"Retry-After": {
"description": "Seconds until a rate-limited request can be retried",
"schema": {
"type": "integer"
}
},
"RateLimit": {
"description": "Current application rate-limit state when application limiting is enabled",
"schema": {
"type": "string"
}
},
"RateLimit-Policy": {
"description": "Application rate-limit policy when application limiting is enabled",
"schema": {
"type": "string"
}
}
}
},
"503": {
"description": "OpenSearch is unavailable",
"headers": {
"Retry-After": {
"description": "Seconds before retrying an unavailable OpenSearch dependency",
"schema": {
"type": "integer"
}
}
}
},
"504": {
"description": "Search exceeded its OpenSearch or overall processing deadline"
}
},
"requestBody": {
Expand Down Expand Up @@ -60,7 +108,6 @@
"cvssV3_0Metrics": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"cvssV3_1Metrics": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"cvssV4Metrics": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N",
"sourceIdentifier": "security@example.org",
"cpeName": "cpe:2.3:a:freetype:freetype:2.8.1:*:*:*:*:*:*:*",
"isVulnerable": false,
"lastModStartDate": "2024-01-01T00:00:00.000Z",
Expand Down Expand Up @@ -109,12 +156,57 @@
},
"/api/health-check": {
"get": {
"summary": "Health check for AWS monitoring",
"description": "Health check for AWS monitoring",
"summary": "Process liveness check",
"description": "Returns 200 when the API process is running. This endpoint does not check OpenSearch.",
"operationId": "healthCheck",
"responses": {
"200": {
"description": "OK"
"description": "OK",
"headers": {
"X-Request-ID": {
"description": "Request correlation ID",
"schema": {
"type": "string"
}
}
}
}
}
}
},
"/api/ready-check": {
"get": {
"summary": "OpenSearch readiness check",
"description": "Sends a minimal zero-result search to the configured CVE index. Returns 200 when the index is available with search permission, or a sanitized 503 response when configuration, connectivity, timeout, permission, or index availability checks fail.",
"operationId": "readinessCheck",
"responses": {
"200": {
"description": "API is ready to serve search requests",
"headers": {
"X-Request-ID": {
"description": "Request correlation ID",
"schema": {
"type": "string"
}
}
}
},
"503": {
"description": "Search service is not ready",
"headers": {
"Retry-After": {
"description": "Seconds before retrying the readiness check",
"schema": {
"type": "integer"
}
},
"X-Request-ID": {
"description": "Request correlation ID",
"schema": {
"type": "string"
}
}
}
}
}
}
Expand All @@ -124,15 +216,29 @@
"tags": [
"CVE Record"
],
"summary": "Retrieves all matching CVE records after applying the JSON request body",
"description": " <h2>Expected Behavior</h2> <p>Retrieves all matching CVE records after applying the JSON request body</p>",
"summary": "Deprecated raw query search",
"description": " <h2>Expected Behavior</h2> <p>Retrieves all matching CVE records after applying the JSON request body. This endpoint is deprecated and can be disabled by the service operator. Use POST /search instead.</p>",
"operationId": "webSearch",
"responses": {
"200": {
"description": "A list of matching CVE records"
"description": "A list of matching CVE records",
"headers": {
"X-Request-ID": {
"description": "Request correlation ID",
"schema": {
"type": "string"
}
}
}
},
"400": {
"description": "Bad Request"
},
"410": {
"description": "The deprecated endpoint is disabled; use POST /search"
},
"504": {
"description": "Search exceeded its OpenSearch request deadline"
}
},
"requestBody": {
Expand Down Expand Up @@ -183,7 +289,8 @@
}
}
}
}
},
"deprecated": true
}
}
},
Expand All @@ -196,8 +303,9 @@
"properties": {
"keywordSearch": {
"type": "string",
"maxLength": 512,
"example": "remote code execution",
"description": "Returns CVE records where a word or phrase is included in the record's description. Multiple keywords operate like an AND statement, returning records whose descriptions include every keyword."
"description": "Returns CVE records where a word or phrase is included in the record's description. Multiple keywords operate like an AND statement, returning records whose descriptions include every keyword. Limited to 512 characters and 64 whitespace-delimited terms."
},
"keywordExactMatch": {
"type": "boolean",
Expand All @@ -206,8 +314,9 @@
},
"cveIds": {
"type": "string",
"maxLength": 4096,
"example": "CVE-2024-1234,CVE-2024-5678",
"description": "Returns one or more CVE records identified by a comma-separated list of CVE IDs. Limited to 100 CVE IDs."
"description": "Returns one or more CVE records identified by a comma-separated list of CVE IDs. Limited to 100 CVE IDs, 64 characters per ID, and 4096 characters overall."
},
"noRejected": {
"type": "boolean",
Expand All @@ -216,6 +325,7 @@
},
"cweId": {
"type": "string",
"maxLength": 32,
"example": "CWE-79",
"description": "Returns CVE records that include the provided Common Weakness Enumeration ID."
},
Expand Down Expand Up @@ -298,20 +408,16 @@
"example": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N",
"description": "Returns CVE records that contain the provided CVSS v4.0 vector string. Values are limited to 256 characters, 32 slash-delimited metric tokens, and 32 characters per metric token."
},
"sourceIdentifier": {
"type": "string",
"example": "security@example.org",
"description": "Returns CVE records that contain the provided source identifier."
},
"cpeName": {
"type": "string",
"maxLength": 2048,
"example": "cpe:2.3:a:freetype:freetype:2.8.1:*:*:*:*:*:*:*",
"description": "Returns CVE records whose CPE Match Criteria apply to the provided CPE 2.3 asset name. The part, vendor, product, and version components must contain values other than *. The concrete version can match stored criteria with the same version or criteria with a wildcard version and version range fields. Invalid cpeName values return HTTP 404. Use virtualMatchString for partial CPE Match String searches."
"description": "Returns CVE records whose CPE Match Criteria apply to the provided CPE 2.3 asset name. CPE string literal comparisons are insensitive to lexical case. The part, vendor, product, and version components must contain values other than *. Every concrete non-version component must be compatible with the same CPE Match Criteria entry that satisfies the version and vulnerability checks; optional * components remain broad matches. The concrete version can match stored criteria with the same version or criteria with a wildcard version and version range fields. Numeric version segments in ranges ignore leading zeros; exact CPE Match Criteria preserve leading zeros. Version qualifiers use Maven-style ordering, with prerelease qualifiers before an unqualified release and sp or unknown qualifiers after it. The version - represents the CPE logical value NA; numeric range boundaries on wildcard-version criteria do not exclude it. Invalid cpeName values return HTTP 404. Use virtualMatchString for partial CPE Match String searches."
},
"isVulnerable": {
"type": "boolean",
"default": false,
"description": "When true, returns CVE records whose CPE Match Criteria match cpeName and are marked vulnerable. cpeName is required when isVulnerable is true. virtualMatchString cannot be provided with isVulnerable."
"description": "When true, returns CVE records where the same CPE Match Criteria entry both matches cpeName and is marked vulnerable. False or omitted does not apply a vulnerability restriction. cpeName is required when isVulnerable is true. virtualMatchString cannot be provided with isVulnerable."
},
"lastModStartDate": {
"type": "string",
Expand Down Expand Up @@ -354,8 +460,9 @@
},
"versionStart": {
"type": "string",
"maxLength": 256,
"example": "2.2",
"description": "Sets the starting version bound for a CPE Match String range search. versionStartType and virtualMatchString are required when versionStart is provided. virtualMatchString must not include a version component."
"description": "Sets the starting version bound for a CPE Match String range search. versionStartType and virtualMatchString are required when versionStart is provided. When both bounds are provided, versionStart must be less than versionEnd; equal values require both bounds to be including. Numeric version segments in ranges ignore leading zeros; exact CPE Match Criteria preserve leading zeros. Version qualifiers use Maven-style ordering, with prerelease qualifiers before an unqualified release and sp or unknown qualifiers after it. virtualMatchString must not include a version component."
},
"versionStartType": {
"type": "string",
Expand All @@ -367,8 +474,9 @@
},
"versionEnd": {
"type": "string",
"maxLength": 256,
"example": "2.6",
"description": "Sets the ending version bound for a CPE Match String range search. versionEndType and virtualMatchString are required when versionEnd is provided. virtualMatchString must not include a version component."
"description": "Sets the ending version bound for a CPE Match String range search. versionEndType and virtualMatchString are required when versionEnd is provided. When both bounds are provided, versionStart must be less than versionEnd; equal values require both bounds to be including. Numeric version segments in ranges ignore leading zeros; exact CPE Match Criteria preserve leading zeros. Version qualifiers use Maven-style ordering, with prerelease qualifiers before an unqualified release and sp or unknown qualifiers after it. virtualMatchString must not include a version component."
},
"versionEndType": {
"type": "string",
Expand All @@ -380,8 +488,9 @@
},
"virtualMatchString": {
"type": "string",
"maxLength": 2048,
"example": "cpe:2.3:o:microsoft:windows_10_1809",
"description": "Returns CVE records whose CPE Match Criteria are compatible with the provided CPE 2.3 match string. Omitted trailing components and * components act as broad matches. When used with versionStart or versionEnd, provide a partial CPE 2.3 match string without a version component."
"description": "Returns CVE records whose CPE Match Criteria are compatible with the provided CPE 2.3 match string. CPE string literal comparisons are insensitive to lexical case. Omitted trailing components and * components act as broad matches. When used with versionStart or versionEnd, provide a partial CPE 2.3 match string without a version component."
}
}
}
Expand Down
Loading
Loading