From fb024232870673d94456faca18c958e9c00b8f3f Mon Sep 17 00:00:00 2001 From: Alex Anderson <191496+alxndrsn@users.noreply.github.com> Date: Mon, 11 Nov 2024 20:11:14 +0300 Subject: [PATCH 01/10] Content-Security-Policy: allow images from data: URLs (#772) QR displayed in odk-central-frontend are displayed with src=data:... Closes #629 --- files/nginx/common-headers.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/nginx/common-headers.conf b/files/nginx/common-headers.conf index 7661bb01..7fa6eefa 100644 --- a/files/nginx/common-headers.conf +++ b/files/nginx/common-headers.conf @@ -6,7 +6,7 @@ # They are included here to ease interpretation of violation reports. # # N.B. a separate CSP is defined for Enketo in odk.conf.template -add_header Content-Security-Policy-Report-Only "default-src 'none'; connect-src 'self'; font-src 'self'; frame-src 'self' https://getodk.github.io/central/news.html; img-src *; manifest-src 'none'; media-src 'none'; object-src 'none'; script-src 'self'; style-src 'self'; style-src-attr 'unsafe-inline'; report-uri /csp-report"; +add_header Content-Security-Policy-Report-Only "default-src 'none'; connect-src 'self'; font-src 'self'; frame-src 'self' https://getodk.github.io/central/news.html; img-src * data:; manifest-src 'none'; media-src 'none'; object-src 'none'; script-src 'self'; style-src 'self'; style-src-attr 'unsafe-inline'; report-uri /csp-report"; # If changing these headers, please apply the same updates to enketo # location(s) in odk.conf.template From 2e5c4f7de60c6db39c3205320cedba95d49a9aa3 Mon Sep 17 00:00:00 2001 From: Alex Anderson <191496+alxndrsn@users.noreply.github.com> Date: Wed, 20 Nov 2024 08:48:02 +0300 Subject: [PATCH 02/10] service: remove --legacy-peer-deps from backend init (#791) Removed from backend codebase in https://github.com/getodk/central-backend/pull/1293 --- service.dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service.dockerfile b/service.dockerfile index a2ede997..82d5848f 100644 --- a/service.dockerfile +++ b/service.dockerfile @@ -54,7 +54,7 @@ RUN apt-get update \ postgresql-client-14 \ netcat-traditional \ && rm -rf /var/lib/apt/lists/* \ - && npm clean-install --omit=dev --legacy-peer-deps --no-audit \ + && npm clean-install --omit=dev --no-audit \ --fund=false --update-notifier=false COPY server/ ./ From 51a3e10fdf188473ce38b24313962c674188c9ff Mon Sep 17 00:00:00 2001 From: Alex Anderson <191496+alxndrsn@users.noreply.github.com> Date: Mon, 25 Nov 2024 13:12:01 +0300 Subject: [PATCH 03/10] enketo: relax Content-Security-Policy for inline style attributes (#771) --- files/nginx/odk.conf.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/nginx/odk.conf.template b/files/nginx/odk.conf.template index 663cb874..6362ab80 100644 --- a/files/nginx/odk.conf.template +++ b/files/nginx/odk.conf.template @@ -36,7 +36,7 @@ server { # More lax CSP for enketo-express: # Google Maps API: https://developers.google.com/maps/documentation/javascript/content-security-policy - add_header Content-Security-Policy-Report-Only "default-src 'none'; connect-src 'self' blob: https://maps.googleapis.com/maps/ https://maps.google.com/ https://maps.gstatic.com/mapfiles/ https://fonts.gstatic.com/ https://fonts.googleapis.com/; font-src 'self' https://fonts.gstatic.com/; frame-src 'none'; img-src data: blob: jr: 'self' https://maps.google.com/maps/ https://maps.gstatic.com/mapfiles/ https://maps.googleapis.com/maps/; manifest-src 'none'; media-src blob: jr: 'self'; object-src 'none'; script-src 'unsafe-inline' 'self' https://maps.googleapis.com/maps/api/js/ https://maps.google.com/maps/ https://maps.google.com/maps-api-v3/api/js/; style-src 'unsafe-inline' 'self' https://fonts.googleapis.com/css; style-src-attr 'none'; report-uri /csp-report"; + add_header Content-Security-Policy-Report-Only "default-src 'none'; connect-src 'self' blob: https://maps.googleapis.com/maps/ https://maps.google.com/ https://maps.gstatic.com/mapfiles/ https://fonts.gstatic.com/ https://fonts.googleapis.com/; font-src 'self' https://fonts.gstatic.com/; frame-src 'none'; img-src data: blob: jr: 'self' https://maps.google.com/maps/ https://maps.gstatic.com/mapfiles/ https://maps.googleapis.com/maps/; manifest-src 'none'; media-src blob: jr: 'self'; object-src 'none'; script-src 'unsafe-inline' 'self' https://maps.googleapis.com/maps/api/js/ https://maps.google.com/maps/ https://maps.google.com/maps-api-v3/api/js/; style-src 'unsafe-inline' 'self' https://fonts.googleapis.com/css; style-src-attr 'unsafe-inline'; report-uri /csp-report"; # # Rules set to 'none' here would fallback to default-src if excluded. # They are included here to ease interpretation of violation reports. From 5bb4def40dfc5af31ea5bae5f781476f54e21716 Mon Sep 17 00:00:00 2001 From: Alex Anderson <191496+alxndrsn@users.noreply.github.com> Date: Thu, 5 Dec 2024 08:02:15 +0300 Subject: [PATCH 04/10] nginx: skip frontend build in tests (#813) This significantly decreases test turnaround time. Local results (`./run-tests; time ./run-tests.sh`): * with this PR: 14 seconds * without this: 90 seconds Cons: * complicates build script (`build-frontend.sh`) --- files/prebuild/build-frontend.sh | 18 ++++++++++++++++-- nginx.dockerfile | 2 ++ test/nginx.test.docker-compose.yml | 2 ++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/files/prebuild/build-frontend.sh b/files/prebuild/build-frontend.sh index 6122c321..699d7d09 100755 --- a/files/prebuild/build-frontend.sh +++ b/files/prebuild/build-frontend.sh @@ -1,4 +1,18 @@ #!/bin/bash -eu + cd client -npm clean-install --no-audit --fund=false --update-notifier=false -npm run build + +if [[ ${SKIP_FRONTEND_BUILD-} != "" ]]; then + echo "[build-frontend] Skipping frontend build." + + # Create minimal fake frontend to allow tests to pass: + mkdir -p dist + echo > dist/blank.html + echo > dist/favicon.ico + echo > dist/index.html '
' + + exit +else + npm clean-install --no-audit --fund=false --update-notifier=false + npm run build +fi diff --git a/nginx.dockerfile b/nginx.dockerfile index 5129ac67..b2b94712 100644 --- a/nginx.dockerfile +++ b/nginx.dockerfile @@ -8,6 +8,8 @@ RUN apt-get update \ COPY ./ ./ RUN files/prebuild/write-version.sh + +ARG SKIP_FRONTEND_BUILD RUN files/prebuild/build-frontend.sh diff --git a/test/nginx.test.docker-compose.yml b/test/nginx.test.docker-compose.yml index f7af4ace..4a863054 100644 --- a/test/nginx.test.docker-compose.yml +++ b/test/nginx.test.docker-compose.yml @@ -17,6 +17,8 @@ services: build: context: .. dockerfile: nginx.dockerfile + args: + SKIP_FRONTEND_BUILD: true depends_on: - service - enketo From 357864d2c03b1f3140ce160a3574f906592b2724 Mon Sep 17 00:00:00 2001 From: brontolosone <177225737+brontolosone@users.noreply.github.com> Date: Fri, 6 Dec 2024 14:24:41 +0000 Subject: [PATCH 05/10] let's gzip JS --- files/nginx/odk.conf.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/nginx/odk.conf.template b/files/nginx/odk.conf.template index 6362ab80..9ecaa292 100644 --- a/files/nginx/odk.conf.template +++ b/files/nginx/odk.conf.template @@ -22,7 +22,7 @@ server { gzip_vary on; gzip_min_length 1280; gzip_http_version 1.1; - gzip_types text/plain text/css application/json application/x-javascript text/xml text/csv; + gzip_types text/plain text/css application/json application/x-javascript application/javascript text/xml text/csv; location = /robots.txt { add_header Content-Type text/plain; From 515bfb939f1f4060c63eed278db4b152933f19c2 Mon Sep 17 00:00:00 2001 From: Alex Anderson <191496+alxndrsn@users.noreply.github.com> Date: Sat, 7 Dec 2024 14:32:51 +0300 Subject: [PATCH 06/10] nginx: separate cert paths from server_name (#814) Working on #809 I noticed that the location of SSL certs is based either on the domain name, or on the method of supply of SSL certs. Cert provision approach should probably not affect the nginx "server_name" setting. Also, the old variable name `CNAME` (short for "certificate name?") is easily confused with the DNS concept of CNAME records ("canonical names"). --- files/nginx/odk.conf.template | 8 ++++---- files/nginx/setup-odk.sh | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/files/nginx/odk.conf.template b/files/nginx/odk.conf.template index 9ecaa292..5fbdaf08 100644 --- a/files/nginx/odk.conf.template +++ b/files/nginx/odk.conf.template @@ -1,10 +1,10 @@ server { listen 443 ssl; - server_name ${CNAME}; + server_name ${DOMAIN}; - ssl_certificate /etc/${SSL_TYPE}/live/${CNAME}/fullchain.pem; - ssl_certificate_key /etc/${SSL_TYPE}/live/${CNAME}/privkey.pem; - ssl_trusted_certificate /etc/${SSL_TYPE}/live/${CNAME}/fullchain.pem; + ssl_certificate /etc/${SSL_TYPE}/live/${CERT_DOMAIN}/fullchain.pem; + ssl_certificate_key /etc/${SSL_TYPE}/live/${CERT_DOMAIN}/privkey.pem; + ssl_trusted_certificate /etc/${SSL_TYPE}/live/${CERT_DOMAIN}/fullchain.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; diff --git a/files/nginx/setup-odk.sh b/files/nginx/setup-odk.sh index 85520dd5..db0f9356 100644 --- a/files/nginx/setup-odk.sh +++ b/files/nginx/setup-odk.sh @@ -30,8 +30,8 @@ echo "writing fresh nginx templates..." # redirector.conf gets deleted if using upstream SSL so copy it back cp /usr/share/odk/nginx/redirector.conf /etc/nginx/conf.d/redirector.conf -CNAME=$( [ "$SSL_TYPE" = "customssl" ] && echo "local" || echo "$DOMAIN") \ -envsubst '$SSL_TYPE $CNAME $SENTRY_ORG_SUBDOMAIN $SENTRY_KEY $SENTRY_PROJECT' \ +CERT_DOMAIN=$( [ "$SSL_TYPE" = "customssl" ] && echo "local" || echo "$DOMAIN") \ +envsubst '$SSL_TYPE $CERT_DOMAIN $DOMAIN $SENTRY_ORG_SUBDOMAIN $SENTRY_KEY $SENTRY_PROJECT' \ < /usr/share/odk/nginx/odk.conf.template \ > /etc/nginx/conf.d/odk.conf From 0f7bad611df1329b3de3305674903a44ee21d011 Mon Sep 17 00:00:00 2001 From: Alex Anderson <191496+alxndrsn@users.noreply.github.com> Date: Tue, 10 Dec 2024 07:10:38 +0300 Subject: [PATCH 07/10] nginx: reject requests with unexpected Host header (#809) Co-authored-by: alxndrsn --- .circleci/config.yml | 4 +- files/nginx/odk.conf.template | 9 ++ files/nginx/redirector.conf | 12 ++- files/nginx/setup-odk.sh | 13 ++- test/run-tests.sh | 2 +- test/test-nginx.js | 153 ++++++++++++++++++++++++++++++++-- 6 files changed, 180 insertions(+), 13 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 547304ae..01960b5e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -30,11 +30,11 @@ jobs: docker compose up -d CONTAINER_NAME=$(docker inspect -f '{{.Name}}' $(docker compose ps -q nginx) | cut -c2-) docker run --network container:$CONTAINER_NAME \ - appropriate/curl -4 --insecure --retry 30 --retry-delay 10 --retry-connrefused https://localhost/ \ + appropriate/curl -4 --insecure --retry 30 --retry-delay 10 --retry-connrefused https://localhost/ -H 'Host: local' \ | tee /dev/tty \ | grep -q 'ODK Central' docker run --network container:$CONTAINER_NAME \ - appropriate/curl -4 --insecure --retry 20 --retry-delay 2 --retry-connrefused https://localhost/v1/projects \ + appropriate/curl -4 --insecure --retry 20 --retry-delay 2 --retry-connrefused https://localhost/v1/projects -H 'Host: local' \ | tee /dev/tty \ | grep -q '\[\]' - run: diff --git a/files/nginx/odk.conf.template b/files/nginx/odk.conf.template index 5fbdaf08..8f268ae4 100644 --- a/files/nginx/odk.conf.template +++ b/files/nginx/odk.conf.template @@ -1,3 +1,12 @@ +server { + listen 443 default_server ssl; + + ssl_certificate /etc/nginx/ssl/nginx.default.crt; + ssl_certificate_key /etc/nginx/ssl/nginx.default.key; + + return 421; +} + server { listen 443 ssl; server_name ${DOMAIN}; diff --git a/files/nginx/redirector.conf b/files/nginx/redirector.conf index 08e33bd5..ba56723f 100644 --- a/files/nginx/redirector.conf +++ b/files/nginx/redirector.conf @@ -1,8 +1,9 @@ server { # Listen on plain old HTTP and catch all requests so they can be redirected # to HTTPS instead. - listen 80 default_server reuseport; - listen [::]:80 default_server reuseport; + listen 80 reuseport; + listen [::]:80 reuseport; + server_name ${DOMAIN}; # Anything requesting this particular URL should be served content from # Certbot's folder so the HTTP-01 ACME challenges can be completed for the @@ -18,3 +19,10 @@ server { return 301 https://$http_host$request_uri; } } + +server { + listen 80 default_server; + listen [::]:80 default_server; + + return 421; +} diff --git a/files/nginx/setup-odk.sh b/files/nginx/setup-odk.sh index db0f9356..87416c4b 100644 --- a/files/nginx/setup-odk.sh +++ b/files/nginx/setup-odk.sh @@ -9,6 +9,15 @@ fi envsubst < /usr/share/odk/nginx/client-config.json.template > /usr/share/nginx/html/client-config.json +# Generate self-signed keys for the incorrect (catch-all) HTTPS listener. This +# cert should never be seen by legitimate users, so it's not a big deal that +# it's self-signed and won't expire for 1,000 years. +mkdir -p /etc/nginx/ssl +openssl req -x509 -nodes -newkey rsa:2048 \ + -subj "/" \ + -keyout /etc/nginx/ssl/nginx.default.key \ + -out /etc/nginx/ssl/nginx.default.crt \ + -days 365000 DH_PATH=/etc/dh/nginx.pem if [ "$SSL_TYPE" != "upstream" ] && [ ! -s "$DH_PATH" ]; then @@ -28,7 +37,9 @@ fi # start from fresh templates in case ssl type has changed echo "writing fresh nginx templates..." # redirector.conf gets deleted if using upstream SSL so copy it back -cp /usr/share/odk/nginx/redirector.conf /etc/nginx/conf.d/redirector.conf +envsubst '$DOMAIN' \ + < /usr/share/odk/nginx/redirector.conf \ + > /etc/nginx/conf.d/redirector.conf CERT_DOMAIN=$( [ "$SSL_TYPE" = "customssl" ] && echo "local" || echo "$DOMAIN") \ envsubst '$SSL_TYPE $CERT_DOMAIN $DOMAIN $SENTRY_ORG_SUBDOMAIN $SENTRY_KEY $SENTRY_PROJECT' \ diff --git a/test/run-tests.sh b/test/run-tests.sh index f9ee7828..0c09276f 100755 --- a/test/run-tests.sh +++ b/test/run-tests.sh @@ -34,7 +34,7 @@ wait_for_http_response 5 localhost:8383/health 200 log "Waiting for mock enketo..." wait_for_http_response 5 localhost:8005/health 200 log "Waiting for nginx..." -wait_for_http_response 90 localhost:9000 301 +wait_for_http_response 90 localhost:9000 421 npm run test:nginx diff --git a/test/test-nginx.js b/test/test-nginx.js index 2125adc7..0c38aa6c 100644 --- a/test/test-nginx.js +++ b/test/test-nginx.js @@ -1,3 +1,5 @@ +const tls = require('node:tls'); +const { Readable } = require('stream'); const { assert } = require('chai'); describe('nginx config', () => { @@ -12,7 +14,16 @@ describe('nginx config', () => { // then assert.equal(res.status, 301); - assert.equal(res.headers.get('location'), 'https://localhost:9000/'); + assert.equal(res.headers.get('location'), 'https://odk-nginx.example.test/'); + }); + + it('should forward HTTP to HTTPS (IPv6)', async () => { + // when + const res = await fetchHttp6('/'); + + // then + assert.equal(res.status, 301); + assert.equal(res.headers.get('location'), 'https://odk-nginx.example.test/'); }); it('should serve generated client-config.json', async () => { @@ -25,6 +36,16 @@ describe('nginx config', () => { assert.equal(await res.headers.get('cache-control'), 'no-cache'); }); + it('should serve generated client-config.json (IPv6)', async () => { + // when + const res = await fetchHttps6('/client-config.json'); + + // then + assert.equal(res.status, 200); + assert.deepEqual(await res.json(), { oidcEnabled: false }); + assert.equal(await res.headers.get('cache-control'), 'no-cache'); + }); + [ [ '/index.html', /
<\/div>/ ], [ '/version.txt', /^versions:/ ], @@ -83,7 +104,7 @@ describe('nginx config', () => { it('should set x-forwarded-proto header to "https"', async () => { // when - const res = await fetch(`https://localhost:9001/v1/reflect-headers`); + const res = await fetchHttps('/v1/reflect-headers'); // then assert.equal(res.status, 200); @@ -95,7 +116,7 @@ describe('nginx config', () => { it('should override supplied x-forwarded-proto header', async () => { // when - const res = await fetch(`https://localhost:9001/v1/reflect-headers`, { + const res = await fetchHttps('/v1/reflect-headers', { headers: { 'x-forwarded-proto': 'http', }, @@ -108,16 +129,78 @@ describe('nginx config', () => { // then assert.equal(body['x-forwarded-proto'], 'https'); }); + + it('should reject HTTP requests with incorrect host header supplied', async () => { + // when + const res = await fetchHttp('/', { headers:{ host:'bad.example.com' } }); + + // then + assert.equal(res.status, 421); + }); + + it('should reject HTTP requests with incorrect host header supplied (IPv6)', async () => { + // when + const res = await fetchHttp6('/', { headers:{ host:'bad.example.com' } }); + + // then + assert.equal(res.status, 421); + }); + + it('should reject HTTPS requests with incorrect host header supplied', async () => { + // when + const res = await fetchHttps('/', { headers:{ host:'bad.example.com' } }); + + // then + assert.equal(res.status, 421); + }); + + it('should reject HTTPS requests with incorrect host header supplied (IPv6)', async () => { + // when + const res = await fetchHttps6('/', { headers:{ host:'bad.example.com' } }); + + // then + assert.equal(res.status, 421); + }); + + it('should serve long-lived certificate to HTTPS requests with incorrect host header', () => new Promise((resolve, reject) => { + const socket = tls.connect(9001, { host:'localhost', servername:'bad.example.com', rejectUnauthorized:false }, () => { + try { + const certificate = socket.getPeerCertificate(); + const validUntilRaw = certificate.valid_to; + + // Dates look like RFC-822 format - probably direct output of `openssl`. NodeJS Date.parse() + // seems to support this format. + const validUntil = new Date(validUntilRaw); + assert.isFalse(isNaN(validUntil), `Could not parse certificate's valid_to value as a date ('${validUntilRaw}')`); + assert.isAbove(validUntil.getFullYear(), 3000, 'The provided certificate expires too soon.'); + socket.end(); + } catch(err) { + socket.destroy(err); + } + }); + socket.on('end', resolve); + socket.on('error', reject); + })); }); function fetchHttp(path, options) { if(!path.startsWith('/')) throw new Error('Invalid path.'); - return fetch(`http://localhost:9000${path}`, { redirect:'manual', ...options }); + return request(`http://127.0.0.1:9000${path}`, options); +} + +function fetchHttp6(path, options) { + if(!path.startsWith('/')) throw new Error('Invalid path.'); + return request(`http://[::1]:9000${path}`, options); } function fetchHttps(path, options) { if(!path.startsWith('/')) throw new Error('Invalid path.'); - return fetch(`https://localhost:9001${path}`, { redirect:'manual', ...options }); + return request(`https://127.0.0.1:9001${path}`, options); +} + +function fetchHttps6(path, options) { + if(!path.startsWith('/')) throw new Error('Invalid path.'); + return request(`https://[::1]:9001${path}`, options); } function assertEnketoReceived(...expectedRequests) { @@ -129,7 +212,7 @@ function assertBackendReceived(...expectedRequests) { } async function assertMockHttpReceived(port, expectedRequests) { - const res = await fetch(`http://localhost:${port}/request-log`); + const res = await request(`http://localhost:${port}/request-log`); assert.isTrue(res.ok); assert.deepEqual(expectedRequests, await res.json()); } @@ -143,6 +226,62 @@ function resetBackendMock() { } async function resetMock(port) { - const res = await fetch(`http://localhost:${port}/reset`); + const res = await request(`http://localhost:${port}/reset`); assert.isTrue(res.ok); } + +// Similar to fetch() but: +// +// 1. do not follow redirects +// 2. allow overriding of fetch's "forbidden" headers: https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name +// 3. allow access to server SSL certificate +function request(url, { body, ...options }={}) { + if(!options.headers) options.headers = {}; + if(!options.headers.host) options.headers.host = 'odk-nginx.example.test'; + + return new Promise((resolve, reject) => { + try { + const req = getProtocolImplFrom(url).request(url, options, res => { + res.on('error', reject); + + const body = new Readable({ _read: () => {} }); + res.on('error', err => body.destroy(err)); + res.on('data', data => body.push(data)); + res.on('end', () => body.push(null)); + + const text = () => new Promise((resolve, reject) => { + const chunks = []; + body.on('error', reject); + body.on('data', data => chunks.push(data)) + body.on('end', () => resolve(Buffer.concat(chunks).toString('utf8'))); + }); + + const status = res.statusCode; + + resolve({ + status, + ok: status >= 200 && status < 300, + statusText: res.statusText, + body, + text, + json: async () => JSON.parse(await text()), + headers: new Headers(res.headers), + }); + }); + req.on('error', reject); + if(body !== undefined) req.write(body); + req.end(); + } catch(err) { + reject(err); + } + }); +} + +function getProtocolImplFrom(url) { + const { protocol } = new URL(url); + switch(protocol) { + case 'http:': return require('node:http'); + case 'https:': return require('node:https'); + default: throw new Error(`Unsupported protocol: ${protocol}`); + } +} From 801ab503c74eaa24b27a226622eceee9351d5db0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9l=C3=A8ne=20Martin?= Date: Mon, 9 Dec 2024 20:55:06 -0800 Subject: [PATCH 08/10] Upgrade dependencies for v2024.3. (#838) * Use node 22.12.0 Docker images * Upgrade Enketo * Fix dependency vulnerabilities * Use pyxform 3.0.0 --- .github/workflows/test-nginx.yml | 2 +- client | 2 +- docker-compose.yml | 2 +- enketo.dockerfile | 2 +- nginx.dockerfile | 2 +- secrets.dockerfile | 2 +- server | 2 +- service.dockerfile | 2 +- test/mock-http-server/package-lock.json | 26 ++++++++++++++----------- 9 files changed, 23 insertions(+), 19 deletions(-) diff --git a/.github/workflows/test-nginx.yml b/.github/workflows/test-nginx.yml index ee56a757..cd1349c8 100644 --- a/.github/workflows/test-nginx.yml +++ b/.github/workflows/test-nginx.yml @@ -16,7 +16,7 @@ jobs: submodules: recursive - uses: actions/setup-node@v4 with: - node-version: 20.17.0 + node-version: 22.12.0 - run: cd test && npm i - run: cd test && ./run-tests.sh diff --git a/client b/client index 99b11a8c..3fb0c22b 160000 --- a/client +++ b/client @@ -1 +1 @@ -Subproject commit 99b11a8cc7d30dbc03376f97ab345dc0d18c2a98 +Subproject commit 3fb0c22b1cbdc3a6004963afcc3847a82c09307d diff --git a/docker-compose.yml b/docker-compose.yml index 1d70fdfc..97b7ac65 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -108,7 +108,7 @@ services: options: max-file: "30" pyxform: - image: 'ghcr.io/getodk/pyxform-http:v2.1.1' + image: 'ghcr.io/getodk/pyxform-http:v3.0.0' restart: always secrets: volumes: diff --git a/enketo.dockerfile b/enketo.dockerfile index c4b03466..efd47403 100644 --- a/enketo.dockerfile +++ b/enketo.dockerfile @@ -1,4 +1,4 @@ -FROM ghcr.io/enketo/enketo:7.4.0 +FROM ghcr.io/enketo/enketo:7.5.0 ENV ENKETO_SRC_DIR=/srv/src/enketo/packages/enketo-express WORKDIR ${ENKETO_SRC_DIR} diff --git a/nginx.dockerfile b/nginx.dockerfile index b2b94712..fb66d63f 100644 --- a/nginx.dockerfile +++ b/nginx.dockerfile @@ -1,4 +1,4 @@ -FROM node:20.17.0-slim AS intermediate +FROM node:22.12.0-slim AS intermediate RUN apt-get update \ && apt-get install -y --no-install-recommends \ diff --git a/secrets.dockerfile b/secrets.dockerfile index aef60b73..0a53585a 100644 --- a/secrets.dockerfile +++ b/secrets.dockerfile @@ -1,3 +1,3 @@ -FROM node:20.17.0-slim +FROM node:22.12.0-slim COPY files/enketo/generate-secrets.sh ./ diff --git a/server b/server index 63ca7881..b4754cf5 160000 --- a/server +++ b/server @@ -1 +1 @@ -Subproject commit 63ca7881f6e6eb0b5c9051bf64448d802720f100 +Subproject commit b4754cf52bfa64b1ca841bc9ccb64a38726398e8 diff --git a/service.dockerfile b/service.dockerfile index 82d5848f..7b947419 100644 --- a/service.dockerfile +++ b/service.dockerfile @@ -1,4 +1,4 @@ -ARG node_version=20.17.0 +ARG node_version=22.12.0 diff --git a/test/mock-http-server/package-lock.json b/test/mock-http-server/package-lock.json index 75660bbe..7f68446c 100644 --- a/test/mock-http-server/package-lock.json +++ b/test/mock-http-server/package-lock.json @@ -95,9 +95,9 @@ } }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "engines": { "node": ">= 0.6" } @@ -194,16 +194,16 @@ } }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -217,7 +217,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -232,6 +232,10 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/finalhandler": { @@ -485,9 +489,9 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" }, "node_modules/proxy-addr": { "version": "2.0.7", From 20878c7ffe3b92a2b9f35bb253e6043e5ddba67b Mon Sep 17 00:00:00 2001 From: Alex Anderson <191496+alxndrsn@users.noreply.github.com> Date: Tue, 10 Dec 2024 09:26:31 +0300 Subject: [PATCH 09/10] test/nginx: remove misleading comment (#839) This comment was merged as part of #809, but was not correct by the time the PR was merged. --- test/test-nginx.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test-nginx.js b/test/test-nginx.js index 0c38aa6c..a367d7f5 100644 --- a/test/test-nginx.js +++ b/test/test-nginx.js @@ -234,7 +234,6 @@ async function resetMock(port) { // // 1. do not follow redirects // 2. allow overriding of fetch's "forbidden" headers: https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name -// 3. allow access to server SSL certificate function request(url, { body, ...options }={}) { if(!options.headers) options.headers = {}; if(!options.headers.host) options.headers.host = 'odk-nginx.example.test'; From 0b373353cfc736b0052cb7bbd9f4b091e484fba1 Mon Sep 17 00:00:00 2001 From: Matthew White Date: Tue, 17 Dec 2024 14:55:05 -0500 Subject: [PATCH 10/10] Update submodules for v2024.3 (#853) --- client | 2 +- server | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client b/client index 3fb0c22b..6ba4ef11 160000 --- a/client +++ b/client @@ -1 +1 @@ -Subproject commit 3fb0c22b1cbdc3a6004963afcc3847a82c09307d +Subproject commit 6ba4ef1183d2081e1da6f9921806c03df8483f97 diff --git a/server b/server index b4754cf5..92f1186a 160000 --- a/server +++ b/server @@ -1 +1 @@ -Subproject commit b4754cf52bfa64b1ca841bc9ccb64a38726398e8 +Subproject commit 92f1186a3257393ba46ea8ede3dc75803eb15c4d