Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
36cd876
Fail closed on incomplete OpenSearch responses
afoote-mitre Jul 30, 2026
e19399d
Harden structured search filter validation
afoote-mitre Jul 30, 2026
a7be791
Correct search pagination and response contracts
afoote-mitre Jul 30, 2026
d16f762
Harden semantic search resource controls
afoote-mitre Jul 30, 2026
4440cb7
Add graceful runtime shutdown handling
afoote-mitre Jul 30, 2026
e0388f4
Expand CI runtime validation coverage
afoote-mitre Jul 30, 2026
200538a
Remove stale search maintenance paths
afoote-mitre Jul 30, 2026
b5f5901
Fail closed on malformed search candidates
afoote-mitre Jul 30, 2026
fa2eabd
Handle oversized JSON requests explicitly
afoote-mitre Jul 30, 2026
c0c077b
Prevent readiness success during shutdown
afoote-mitre Jul 30, 2026
9bae613
Remove legacy search fallback paths
afoote-mitre Jul 30, 2026
4cbb92f
Validate semantic search candidates incrementally
afoote-mitre Jul 30, 2026
26e3c54
Tighten CVE and virtual match validation
afoote-mitre Jul 30, 2026
3444581
Clarify CPE applicability entry semantics
afoote-mitre Jul 30, 2026
6e2b477
Centralize runtime configuration validation
afoote-mitre Jul 30, 2026
113f858
Bound search resource usage
afoote-mitre Jul 30, 2026
63959b4
Harden delivery and OpenSearch contracts
afoote-mitre Jul 30, 2026
0c5a2cc
Harden OpenSearch contract delivery checks
afoote-mitre Aug 3, 2026
269f79c
Fail closed on incomplete OpenSearch responses
afoote-mitre Aug 3, 2026
65fdf6d
Fail closed on invalid search construction
afoote-mitre Aug 3, 2026
7f9ae6c
Bound semantic CPE evaluation work
afoote-mitre Aug 3, 2026
7c51b03
Handle asynchronous startup failures
afoote-mitre Aug 3, 2026
bcc0b5f
Report rolling event loop telemetry
afoote-mitre Aug 3, 2026
b844b14
refactor: remove unused internal exports
afoote-mitre Aug 3, 2026
2ad4b7d
refactor: remove obsolete base URL config
afoote-mitre Aug 3, 2026
89500a7
refactor: simplify search validation
afoote-mitre Aug 3, 2026
aad8976
test: consolidate search validation coverage
afoote-mitre Aug 3, 2026
bf78e7f
docs: clarify semantic CPE result limits
afoote-mitre Aug 3, 2026
a43949a
test: streamline search controller coverage
afoote-mitre Aug 3, 2026
757f826
test: consolidate delivery policy coverage
afoote-mitre Aug 3, 2026
beee7c0
test: organize OpenAPI contract coverage
afoote-mitre Aug 3, 2026
0b499e7
docs: align documentation with current API behavior
afoote-mitre Aug 3, 2026
832a67c
Harden search validation and runtime safeguards
afoote-mitre Aug 3, 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
8 changes: 8 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
NODE_CONFIG_ENV=devel
PORT=3000
OpenSearchCveIndex=cve-index-local
OpenSearchAllowUnknownSslCerts=false
Expand All @@ -8,11 +9,18 @@ SearchPostFilterCacheMaxEntries=100
SearchPostFilterColdSearchConcurrency=10
SearchPostFilterColdSearchMaxQueueDepth=500
SearchPostFilterColdSearchMaxWaitMs=10000
SearchInFlightMaxWaitersPerOperation=500
SearchPostFilterEvaluationChunkSize=250
SearchOpenSearchRequestTimeoutMs=20000
SearchRequestDeadlineMs=30000
SearchReadinessTimeoutMs=5000
SearchMaxResponseBytes=10485760
RequestLogSuccessSampleRate=0.1
RequestLogHealthChecks=false
ShutdownTimeoutMs=35000
HttpHeadersTimeoutMs=10000
HttpRequestTimeoutMs=15000
HttpKeepAliveTimeoutMs=5000
WebSearchEnabled=true
SearchRateLimitEnabled=false
SearchRateLimitWindowMs=60000
Expand Down
22 changes: 22 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
version: 2
updates:
- package-ecosystem: npm
directory: /
target-branch: dev
schedule:
interval: weekly
open-pull-requests-limit: 5

- package-ecosystem: docker
directory: /
target-branch: dev
schedule:
interval: weekly
open-pull-requests-limit: 5

- package-ecosystem: github-actions
directory: /
target-branch: dev
schedule:
interval: weekly
open-pull-requests-limit: 5
48 changes: 48 additions & 0 deletions .github/workflows/opensearch-contract.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: OpenSearch Contract

on:
workflow_dispatch:
inputs:
environment:
description: Protected GitHub environment containing the OpenSearch contract-test secrets
required: true
type: environment

permissions:
contents: read

concurrency:
group: search-api-opensearch-contract-${{ inputs.environment }}
cancel-in-progress: false

jobs:
contract:
name: Read-only OpenSearch Contract
if: github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
timeout-minutes: 15
environment: ${{ inputs.environment }}

env:
NODE_ENV: production
NODE_CONFIG_ENV: prod

steps:
- name: Check out repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

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

- name: Install production dependencies
run: npm ci --omit=dev

- name: Run read-only contract checks
run: npm run test:opensearch-contract
env:
OpenSearchDomainEndpoint: ${{ secrets.OPENSEARCH_DOMAIN_ENDPOINT }}
OpenSearchCveIndex: ${{ secrets.OPENSEARCH_CVE_INDEX }}
OpenSearchAllowUnknownSslCerts: ${{ vars.OPENSEARCH_ALLOW_UNKNOWN_SSL_CERTS || 'false' }}
104 changes: 101 additions & 3 deletions .github/workflows/quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,47 @@ name: Search API Quality

on:
pull_request:
push:
branches:
- dev
- main
merge_group:

permissions:
contents: read

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

jobs:
dependency-review:
name: Dependency Review
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 10

steps:
- name: Check out repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- name: Review dependency changes
uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
with:
fail-on-severity: high
fail-on-scopes: runtime, development

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

steps:
- name: Check out repository
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- name: Set up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 24.14.1
cache: npm
Expand All @@ -31,3 +52,80 @@ jobs:

- name: Run quality checks
run: npm run quality

container:
name: Runtime Container
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- name: Check out repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- name: Build production image
run: docker build --tag cve-search-api:pr .

- name: Report all high and critical image vulnerabilities
continue-on-error: true
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
scan-type: image
image-ref: cve-search-api:pr
format: table
exit-code: 0
ignore-unfixed: false
severity: CRITICAL,HIGH
scanners: vuln
trivyignores: /dev/null
version: v0.69.3

- name: Enforce fixable high and critical image vulnerabilities
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
scan-type: image
image-ref: cve-search-api:pr
format: table
exit-code: 1
ignore-unfixed: true
severity: CRITICAL,HIGH
scanners: vuln
trivyignores: .trivyignore
version: v0.69.3

- name: Start production image
run: |
docker run --detach \
--name cve-search-api-pr \
--publish 4100:3000 \
--env NODE_CONFIG_ENV=prod \
--env OpenSearchDomainEndpoint=https://search.example.com \
--env OpenSearchCveIndex=cves \
cve-search-api:pr

- name: Validate runtime image
run: |
for attempt in $(seq 1 30); do
if curl --fail --silent --show-error http://127.0.0.1:4100/api/health-check; then
break
fi
if [ "$attempt" -eq 30 ]; then
exit 1
fi
sleep 1
done

curl --fail --silent --show-error http://127.0.0.1:4100/api-docs/openapi.json \
| jq --exit-status \
'.openapi == "3.0.2" and .info.title == "CVE Search API"' \
> /dev/null

test "$(docker image inspect cve-search-api:pr --format '{{.Config.User}}')" = "65532:65532"
test "$(docker image inspect cve-search-api:pr --format '{{json .Config.Cmd}}')" = '["server.js"]'

- name: Capture container logs
if: always()
run: docker logs cve-search-api-pr || true

- name: Remove test container
if: always()
run: docker rm --force cve-search-api-pr || true
3 changes: 3 additions & 0 deletions .trivyignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# package=adm-zip owner=cve-core fixed=0.6.0 review-by=2026-10-31
# cve-core transitively pins adm-zip below 0.6.0. Remediation belongs in cve-core.
CVE-2026-39244
13 changes: 6 additions & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
# syntax=docker/dockerfile:1

ARG NODE_VERSION=24.14.1
ARG DISTROLESS_NODE_IMAGE=gcr.io/distroless/nodejs24-debian13:nonroot
FROM node:24.14.1@sha256:80fc934952c8f1b2b4d39907af7211f8a9fff1a4c2cf673fb49099292c251cec AS deps

FROM node:${NODE_VERSION} AS deps

ENV NODE_ENV=production
ENV NODE_ENV=production \
NODE_CONFIG_ENV=prod

WORKDIR /usr/src/app

Expand All @@ -16,9 +14,10 @@ COPY package.json package-lock.json ./
RUN npm ci --omit=dev \
&& npm cache clean --force

FROM ${DISTROLESS_NODE_IMAGE} AS runtime
FROM gcr.io/distroless/nodejs24-debian13:nonroot@sha256:af85d11ce7ef10172855a6e3649e3e8125b1b9e3ca41849ec2918036f05cb212 AS runtime

ENV NODE_ENV=production
ENV NODE_ENV=production \
NODE_CONFIG_ENV=prod

WORKDIR /usr/src/app

Expand Down
13 changes: 6 additions & 7 deletions Dockerfile.local
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
# syntax=docker/dockerfile:1

ARG NODE_VERSION=24.14.1
ARG DISTROLESS_NODE_IMAGE=gcr.io/distroless/nodejs24-debian13:nonroot
FROM node:24.14.1@sha256:80fc934952c8f1b2b4d39907af7211f8a9fff1a4c2cf673fb49099292c251cec AS deps

FROM node:${NODE_VERSION} AS deps

ENV NODE_ENV=production
ENV NODE_ENV=production \
NODE_CONFIG_ENV=prod

WORKDIR /usr/src/app

Expand All @@ -21,9 +19,10 @@ RUN --mount=type=secret,id=npm_ca,required=false \
fi \
&& npm cache clean --force

FROM ${DISTROLESS_NODE_IMAGE} AS runtime
FROM gcr.io/distroless/nodejs24-debian13:nonroot@sha256:af85d11ce7ef10172855a6e3649e3e8125b1b9e3ca41849ec2918036f05cb212 AS runtime

ENV NODE_ENV=production
ENV NODE_ENV=production \
NODE_CONFIG_ENV=prod

WORKDIR /usr/src/app

Expand Down
Loading
Loading