Skip to content

test(server): prove in-process HTTPS over real TLS + document edge-termination posture#252

Merged
jsugg merged 3 commits into
mainfrom
docs/tls-edge-termination
Jul 9, 2026
Merged

test(server): prove in-process HTTPS over real TLS + document edge-termination posture#252
jsugg merged 3 commits into
mainfrom
docs/tls-edge-termination

Conversation

@jsugg

@jsugg jsugg commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Why

Two gaps surfaced while reviewing the HTTPS posture after the Node 24 cutover:

  1. No test actually exercised the in-process HTTPS listener. createHttpsServer (https.createServer({key,cert}, app)) was only ever mocked in startApplicationRuntime.test.js; no test performed a real TLS handshake. The "HTTPS-first" claim had unit coverage for cert-loading and HTTP→HTTPS redirect enforcement, but nothing proving the listener itself serves TLS.
  2. The edge-termination decision wasn't documented. The managed Render deploy runs TLS_ENABLED=false (HTTP-only on $PORT) — that needs a written rationale so it doesn't read as a security downgrade.

What

  • tests/integration/httpsListener.test.js — loads dev credentials via loadTlsCredentials(), stands up the real createHttpsServer, starts it on an ephemeral port, and issues a genuine https request. Asserts 200, the app sees req.secure === true, and — decisively — socket.encrypted === true with a TLSv1.x protocol. Runs in the CI test:ci composed lane.
  • DEVELOPMENT.md:
    • New "Inbound TLS posture: HTTPS-first vs. edge termination" section: the project is HTTPS-first by default (in-process listener, self-signed dev certs, HTTP→HTTPS redirect keyed on X-Forwarded-Proto, HSTS via helmet); the managed deploy runs TLS_ENABLED=false because TLS terminates at the platform edge; public traffic stays HTTPS end-to-end and enforcement still holds behind the edge.
    • Documents the TLS_ENABLED env var and marks TLS_KEY/TLS_CERT/TLS_PORT as not required when it's off.
    • Expands TRUST_PROXY_HOPS: must equal the ingress hop count, governs req.secure / redirect / secure cookies / HSTS behind the edge, set it explicitly on the service, and raise it behind a further proxy/CDN.
    • Adds the edge-termination + trust-proxy notes to the Render Deployment Contract.

…ument edge-termination posture

Adds tests/integration/httpsListener.test.js: loads dev credentials, stands up
createHttpsServer, and issues a genuine TLS request asserting HTTP 200 and
socket.encrypted === true. Until now createHttpsServer was only ever mocked, so
the HTTPS-first claim had no end-to-end coverage.

Documents in DEVELOPMENT.md why the managed deployment runs with TLS_ENABLED=false
(TLS terminates at the platform edge) while the project stays HTTPS-first by
default, adds the TLS_ENABLED env var, and expands TRUST_PROXY_HOPS guidance
(must match ingress hops; governs req.secure / redirect / secure cookies / HSTS
behind the edge; raise it behind a further proxy or CDN).
Comment thread tests/integration/httpsListener.test.js Fixed
…ation

Trust the server's own self-signed certificate as the CA and connect by its SAN
hostname, so the handshake is fully verified. Removes the rejectUnauthorized:false
bypass CodeQL flagged (js/disabling-certificate-validation) and makes the test
stricter.
Comment thread tests/integration/httpsListener.test.js Fixed
Generate the self-signed localhost cert with selfsigned directly instead of the
file-capable loader, so the trusted CA never originates from disk. Clears the
CodeQL 'file data in outbound network request' finding while keeping full TLS
validation; loadTlsCredentials keeps its own unit coverage.
@jsugg jsugg merged commit a063eb0 into main Jul 9, 2026
21 checks passed
@jsugg jsugg deleted the docs/tls-edge-termination branch July 9, 2026 01:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants