From c1633e504c23ae13d352929ab16b53c38e6b3eb8 Mon Sep 17 00:00:00 2001 From: Andrew Foote Date: Wed, 22 Jul 2026 15:14:34 -0400 Subject: [PATCH 1/3] build: use distroless runtime image --- api-docs/openapi.json | 2 +- docker/Dockerfile | 73 ++++++++++++++++++++++++++----------------- package-lock.json | 50 +++++++++++++++++++++++++++-- package.json | 6 ++-- src/scripts/start.js | 28 +++++++++++++++++ src/swagger.js | 2 +- 6 files changed, 125 insertions(+), 36 deletions(-) create mode 100644 src/scripts/start.js diff --git a/api-docs/openapi.json b/api-docs/openapi.json index f9fbbb5bd..9d8d9e074 100644 --- a/api-docs/openapi.json +++ b/api-docs/openapi.json @@ -11,7 +11,7 @@ }, "servers": [ { - "url": "https://cveawg-dev.mitre.org/api" + "url": "/api" } ], "paths": { diff --git a/docker/Dockerfile b/docker/Dockerfile index f2f477f74..0a9ac5cd3 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,41 +1,58 @@ -FROM node:24-alpine3.22 +FROM node:24-trixie AS build LABEL \ mitre.name=cveawg \ - mitre.project=cveawg + mitre.project=cveawg -ENV PIP_BREAK_SYSTEM_PACKAGES=1 -# Run an optional pre-flight script for host-dependent reqs such as CA certs -# Use --build-arg: -# docker compose --build-arg CVE_PREFLIGHT="wget -q -O - --no-check-certificate http://pki.local/install_certs.sh | sh" +# Run an optional pre-flight script for host-dependent requirements such as +# corporate CA certificates before package installation performs network I/O. ARG CVE_PREFLIGHT -RUN sh -c "${CVE_PREFLIGHT:-exit 0}" +RUN /bin/sh -c "${CVE_PREFLIGHT:-exit 0}" -# Install python/pip (required for argon2 build from source) -ENV PYTHONUNBUFFERED=1 -RUN apk add --update --no-cache python3 py3-pip -RUN pip3 install --no-cache --upgrade pip setuptools +# Python and build essentials are required when argon2 builds from source. +RUN apt-get update \ + && apt-get install --yes --no-install-recommends python3 make g++ ca-certificates \ + && rm -rf /var/lib/apt/lists/* -# Install build essentials (also required for argon2) -RUN apk add --update --no-cache build-base +WORKDIR /app -# Set up directory to run as node user rather than root -ADD . /home/node/app -RUN rm -Rf /home/node/app/.git # we don't need this -RUN chown -R node:node /home/node +COPY package.json package-lock.json ./ +RUN npm ci -WORKDIR /home/node/app +COPY src ./src +COPY schemas ./schemas +COPY api-docs ./api-docs +COPY config ./config +COPY docker/default.json-docker ./config/default.json +RUN for environment in development staging integration production test; do \ + printf '{}\n' > "./config/${environment}.json"; \ + done -RUN npm install --production -COPY --chown=node:node docker/entrypoint.sh /home/node/app/entrypoint.sh -RUN echo '{}' > /home/node/app/config/dev.json -RUN echo '{}' > /home/node/app/config/test.json -RUN echo '{}' > /home/node/app/config/staging.json +# Generate the OpenAPI artifact while build-only dependencies are available, +# then remove them before copying node_modules into the runtime image. +RUN node src/swagger.js \ + && npm prune --omit=dev \ + && npm cache clean --force -# Change db hostname from localhost to docdb for use inside docker -COPY docker/default.json-docker /home/node/app/config/default.json +FROM gcr.io/distroless/nodejs24-debian13:nonroot AS runtime + +LABEL \ + mitre.name=cveawg \ + mitre.project=cveawg + +ENV NODE_ENV=production + +WORKDIR /app + +# Preserve any custom CA certificates installed by CVE_PREFLIGHT in the build +# stage. The application otherwise runs with a read-only application tree. +COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt +COPY --from=build --chown=65532:65532 /app/node_modules ./node_modules +COPY --from=build --chown=65532:65532 /app/package.json ./package.json +COPY --from=build --chown=65532:65532 /app/src ./src +COPY --from=build --chown=65532:65532 /app/schemas ./schemas +COPY --from=build --chown=65532:65532 /app/api-docs ./api-docs +COPY --from=build --chown=65532:65532 /app/config ./config -# Run as the node user rather than root -USER node EXPOSE 3000 -ENTRYPOINT '/home/node/app/entrypoint.sh' +CMD ["src/scripts/start.js"] diff --git a/package-lock.json b/package-lock.json index 0fe7e1a0b..133a9a732 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,12 +31,9 @@ "mongoose": "^8.9.5", "mongoose-aggregate-paginate-v2": "1.0.6", "morgan": "^1.11.0", - "node-dev": "^7.4.3", "packageurl-js": "^2.0.1", "prompt-sync": "^4.2.0", - "replace-in-file": "6.3.5", "replace-json-property": "^1.8.0", - "swagger-autogen": "^2.19.0", "swagger-ui-express": "^4.3.0", "uuid": "^14.0.0", "validator": ">=13.7.0", @@ -61,9 +58,12 @@ "eslint-plugin-promise": "^4.2.1", "eslint-plugin-standard": "^4.0.1", "mocha": "^10.8.2", + "node-dev": "^7.4.3", "nyc": "^15.1.0", + "replace-in-file": "6.3.5", "sinon": "^15.0.4", "standard": "^16.0.3", + "swagger-autogen": "^2.19.0", "xlsx": "^0.18.5" } }, @@ -1115,6 +1115,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -2229,6 +2230,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "dev": true, "license": "MIT", "engines": { "node": "*" @@ -2238,6 +2240,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", + "dev": true, "license": "MIT" }, "node_modules/debug": { @@ -2291,6 +2294,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -2493,6 +2497,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", "integrity": "sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==", + "dev": true, "license": "MIT", "dependencies": { "xtend": "^4.0.0" @@ -2515,6 +2520,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, "license": "MIT" }, "node_modules/enabled": { @@ -2726,6 +2732,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -3502,6 +3509,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/filewatcher/-/filewatcher-3.0.1.tgz", "integrity": "sha512-Fro8py2B8EJupSP37Kyd4kjKZLr+5ksFq7Vbw8A392Z15Unq8016SPUDvO/AsDj5V6bbPk98PTAinpc5YhPbJw==", + "dev": true, "license": "MIT", "dependencies": { "debounce": "^1.0.0" @@ -3846,6 +3854,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -3889,6 +3898,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=8.0.0" @@ -4106,6 +4116,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==", + "dev": true, "license": "MIT" }, "node_modules/has": { @@ -4527,6 +4538,7 @@ "version": "2.16.2", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, "license": "MIT", "dependencies": { "hasown": "^2.0.3" @@ -4577,6 +4589,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, "license": "MIT", "bin": { "is-docker": "cli.js" @@ -4618,6 +4631,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4933,6 +4947,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, "license": "MIT", "dependencies": { "is-docker": "^2.0.0" @@ -4952,6 +4967,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, "license": "ISC" }, "node_modules/istanbul-lib-coverage": { @@ -5610,6 +5626,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6008,6 +6025,7 @@ "version": "7.4.3", "resolved": "https://registry.npmjs.org/node-dev/-/node-dev-7.4.3.tgz", "integrity": "sha512-o8aYipN28xY+WEunMHHiNc3hpPSkGG8ulHyYBapNbkg4dQxohmhx6jiRbiFhTF6zy+5IwljUGv1EcuxsaWI4Bw==", + "dev": true, "license": "MIT", "dependencies": { "dateformat": "^3.0.3", @@ -6070,6 +6088,7 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.2.tgz", "integrity": "sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==", + "dev": true, "license": "MIT", "dependencies": { "growly": "^1.3.0", @@ -6084,6 +6103,7 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, "license": "MIT", "bin": { "uuid": "dist/bin/uuid" @@ -6754,6 +6774,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, "license": "MIT" }, "node_modules/path-to-regexp": { @@ -7539,6 +7560,7 @@ "version": "6.3.5", "resolved": "https://registry.npmjs.org/replace-in-file/-/replace-in-file-6.3.5.tgz", "integrity": "sha512-arB9d3ENdKva2fxRnSjwBEXfK1npgyci7ZZuwysgAp7ORjHSyxz6oqIjTEv8R0Ydl4Ll7uOAZXL4vbkhGIizCg==", + "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.1.2", @@ -7556,6 +7578,7 @@ "version": "1.1.13", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -7566,6 +7589,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -7581,6 +7605,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -7601,6 +7626,7 @@ "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -7613,6 +7639,7 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -7631,6 +7658,7 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -7655,6 +7683,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -7687,6 +7716,7 @@ "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, "license": "MIT", "dependencies": { "is-core-module": "^2.16.1", @@ -7919,6 +7949,7 @@ "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -8087,6 +8118,7 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true, "license": "MIT" }, "node_modules/side-channel": { @@ -8926,6 +8958,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -9027,6 +9060,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -9110,6 +9144,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -9122,6 +9157,7 @@ "version": "2.23.7", "resolved": "https://registry.npmjs.org/swagger-autogen/-/swagger-autogen-2.23.7.tgz", "integrity": "sha512-vr7uRmuV0DCxWc0wokLJAwX3GwQFJ0jwN+AWk0hKxre2EZwusnkGSGdVFd82u7fQLgwSTnbWkxUL7HXuz5LTZQ==", + "dev": true, "license": "MIT", "dependencies": { "acorn": "^7.4.1", @@ -9134,6 +9170,7 @@ "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -9146,6 +9183,7 @@ "version": "1.1.13", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -9157,6 +9195,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -9177,6 +9216,7 @@ "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -9699,6 +9739,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -9883,6 +9924,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -9951,6 +9993,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.4" @@ -9960,6 +10003,7 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, "license": "ISC", "engines": { "node": ">=10" diff --git a/package.json b/package.json index b67f9e094..13c660dcf 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,12 @@ "eslint-plugin-promise": "^4.2.1", "eslint-plugin-standard": "^4.0.1", "mocha": "^10.8.2", + "node-dev": "^7.4.3", "nyc": "^15.1.0", + "replace-in-file": "6.3.5", "sinon": "^15.0.4", "standard": "^16.0.3", + "swagger-autogen": "^2.19.0", "xlsx": "^0.18.5" }, "dependencies": { @@ -49,12 +52,9 @@ "mongoose": "^8.9.5", "mongoose-aggregate-paginate-v2": "1.0.6", "morgan": "^1.11.0", - "node-dev": "^7.4.3", "packageurl-js": "^2.0.1", "prompt-sync": "^4.2.0", - "replace-in-file": "6.3.5", "replace-json-property": "^1.8.0", - "swagger-autogen": "^2.19.0", "swagger-ui-express": "^4.3.0", "uuid": "^14.0.0", "validator": ">=13.7.0", diff --git a/src/scripts/start.js b/src/scripts/start.js new file mode 100644 index 000000000..1fc578582 --- /dev/null +++ b/src/scripts/start.js @@ -0,0 +1,28 @@ +const environmentAliases = { + 'prod-staging': 'production', + 'adp-test': 'production' +} + +const supportedEnvironments = new Set([ + 'development', + 'staging', + 'integration', + 'prod-staging', + 'adp-test', + 'production' +]) + +const requestedEnvironment = process.env.NODE_ENV + +if (!supportedEnvironments.has(requestedEnvironment)) { + console.error(`NODE_ENV '${requestedEnvironment}' does not correspond with an application environment.`) + process.exit(1) +} + +process.env.NODE_ENV = environmentAliases[requestedEnvironment] || requestedEnvironment + +console.log(`> NODE_ENV=${process.env.NODE_ENV}`) +console.log(`> MONGO_HOST=${process.env.MONGO_HOST}`) +console.log(`> MONGO_PORT=${process.env.MONGO_PORT}`) + +require('../index') diff --git a/src/swagger.js b/src/swagger.js index 6f491125f..2aa674301 100644 --- a/src/swagger.js +++ b/src/swagger.js @@ -48,7 +48,7 @@ const doc = { }, servers: [ { - url: 'urlplaceholder' + url: '/api' } ], basePath: '/api', From b80b92025291748a8daa3dc04c5865fe8b9f7474 Mon Sep 17 00:00:00 2001 From: Andrew Foote Date: Wed, 22 Jul 2026 15:28:04 -0400 Subject: [PATCH 2/3] fix: avoid starting app during coverage discovery --- src/scripts/start.js | 32 ++++++++++++++++++++-------- test/unit-tests/scripts/startTest.js | 22 +++++++++++++++++++ 2 files changed, 45 insertions(+), 9 deletions(-) create mode 100644 test/unit-tests/scripts/startTest.js diff --git a/src/scripts/start.js b/src/scripts/start.js index 1fc578582..f5eae5789 100644 --- a/src/scripts/start.js +++ b/src/scripts/start.js @@ -12,17 +12,31 @@ const supportedEnvironments = new Set([ 'production' ]) -const requestedEnvironment = process.env.NODE_ENV +function resolveEnvironment (requestedEnvironment) { + if (!supportedEnvironments.has(requestedEnvironment)) { + throw new Error(`NODE_ENV '${requestedEnvironment}' does not correspond with an application environment.`) + } -if (!supportedEnvironments.has(requestedEnvironment)) { - console.error(`NODE_ENV '${requestedEnvironment}' does not correspond with an application environment.`) - process.exit(1) + return environmentAliases[requestedEnvironment] || requestedEnvironment } -process.env.NODE_ENV = environmentAliases[requestedEnvironment] || requestedEnvironment +function start () { + try { + process.env.NODE_ENV = resolveEnvironment(process.env.NODE_ENV) + } catch (err) { + console.error(err.message) + process.exit(1) + } -console.log(`> NODE_ENV=${process.env.NODE_ENV}`) -console.log(`> MONGO_HOST=${process.env.MONGO_HOST}`) -console.log(`> MONGO_PORT=${process.env.MONGO_PORT}`) + console.log(`> NODE_ENV=${process.env.NODE_ENV}`) + console.log(`> MONGO_HOST=${process.env.MONGO_HOST}`) + console.log(`> MONGO_PORT=${process.env.MONGO_PORT}`) -require('../index') + require('../index') +} + +if (require.main === module) { + start() +} + +module.exports = { resolveEnvironment } diff --git a/test/unit-tests/scripts/startTest.js b/test/unit-tests/scripts/startTest.js new file mode 100644 index 000000000..aee41965e --- /dev/null +++ b/test/unit-tests/scripts/startTest.js @@ -0,0 +1,22 @@ +const chai = require('chai') +const expect = chai.expect + +const { resolveEnvironment } = require('../../../src/scripts/start') + +describe('Distroless startup environment resolution', () => { + it('preserves supported application environments', () => { + for (const environment of ['development', 'staging', 'integration', 'production']) { + expect(resolveEnvironment(environment)).to.equal(environment) + } + }) + + it('maps deployment aliases to production', () => { + expect(resolveEnvironment('prod-staging')).to.equal('production') + expect(resolveEnvironment('adp-test')).to.equal('production') + }) + + it('rejects unsupported environments', () => { + expect(() => resolveEnvironment('test')) + .to.throw("NODE_ENV 'test' does not correspond with an application environment.") + }) +}) From fdec3f4012c4e73a5281308e20dd81772874c0e6 Mon Sep 17 00:00:00 2001 From: Andrew Foote Date: Thu, 23 Jul 2026 14:36:49 -0400 Subject: [PATCH 3/3] build: pin Node and distroless images --- docker/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 0a9ac5cd3..d8dec8317 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM node:24-trixie AS build +FROM node:24.18.0-trixie-slim@sha256:ae91dcc111a68c9d2d81ff2a17bda61be126426176fde6fe7d08ab13b7f50573 AS build LABEL \ mitre.name=cveawg \ @@ -34,7 +34,7 @@ RUN node src/swagger.js \ && npm prune --omit=dev \ && npm cache clean --force -FROM gcr.io/distroless/nodejs24-debian13:nonroot AS runtime +FROM gcr.io/distroless/nodejs24-debian13:nonroot@sha256:af85d11ce7ef10172855a6e3649e3e8125b1b9e3ca41849ec2918036f05cb212 AS runtime LABEL \ mitre.name=cveawg \