]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commit
fix(ui-server): post-#1891 access policy and WS upgrade hardening (#1898)
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Sun, 14 Jun 2026 20:07:08 +0000 (22:07 +0200)
committerGitHub <noreply@github.com>
Sun, 14 Jun 2026 20:07:08 +0000 (22:07 +0200)
commitbf4bc1b519edc264e29d56100778f32cf3240fc7
tree07f994071bcc0ba323266481b0f9b514016724d4
parent72bb3f08a9545d1d8eec1aeb9444f73b7ed1ae6c
fix(ui-server): post-#1891 access policy and WS upgrade hardening (#1898)

* fix(ui-server): reject ports in allowedHosts, fix host normalization edges

- ConfigurationSchema: reject allowedHosts entries that carry a port
  (e.g., 'gateway.example.com:8080', '[::1]:8080'). Runtime host
  matching strips ports, so a port-bearing entry was silently
  equivalent to its host-only form. Operators are now forced to
  spell out host-only entries at config load time, matching how
  matching actually works.
- UIServerNet: normalizeHost strips a trailing dot after the colon
  split rather than from the whole input, so a Host header like
  'localhost.:80' now resolves to 'localhost' instead of leaving
  the trailing dot intact and failing the allowlist match.
- UIServerAccessPolicy: nonEmpty treats whitespace-only forwarded
  values as absent, and hasForwardedHeaders consumes nonEmpty so
  the two helpers agree. A whitespace-only X-Forwarded-Proto no
  longer flips forwardedHeadersPresent to true and triggers an
  Ambiguous* denial.

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
* fix(ui-server): attach socket error listener before WS upgrade rejection writes

The onSocketError listener was previously attached only after the
prologue and bad-header guards had already written their rejection
responses, and was removed before the auth-failure rejection write.
A client TCP-reset between an upgrade event and the destroy callback
could fire 'error' on the Duplex socket with no listener, which Node
escalates to an unhandled exception and crashes the process.

The listener is now attached at the top of the upgrade handler and
only removed once webSocketServer.handleUpgrade takes ownership of
the socket. All three rejection paths (bad upgrade headers, prologue
failure, auth failure) keep the listener until socket.destroy(),
neutralising the DoS vector.

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
* refactor(ui-server): tighten access-policy schema messages and forwarded parsing

- Schema: split UIServerListenOptions validation so non-object values
  surface 'must be a non-array object' instead of the misleading
  'accessPolicy must be configured under uiServer' message.
- Forwarded parser: unescape RFC 7230 quoted-pair sequences inside
  double-quoted Forwarded parameter values.
- getForwardedClientAddress: propagate forwarded.kind === 'error' before
  the trustedProxy gate, matching getForwardedProtocol and
  getForwardedHost (no behavioral change today; closes a latent
  asymmetry if the upstream untrusted-peer gate is reordered).
- UIServerAccessCache: document identity-based cache invalidation for
  trustedProxies; reload flows must construct a new configuration.
- getForwardedProtocol / getForwardedHost: distinguish length === 0
  (new InvalidForwardedProtocol / InvalidForwardedHost reasons) from
  length > 1 (existing Ambiguous reasons), matching the existing
  InvalidForwardedClient / AmbiguousForwardedClient pattern.

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
---------

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
src/charging-station/ui-server/UIServerAccessPolicy.ts
src/charging-station/ui-server/UIServerNet.ts
src/charging-station/ui-server/UIWebSocketServer.ts
src/utils/ConfigurationSchema.ts
tests/charging-station/ui-server/UIServerAccessPolicy.test.ts
tests/charging-station/ui-server/UIServerNet.test.ts
tests/charging-station/ui-server/UIWebSocketServer.test.ts
tests/utils/ConfigurationSchema.test.ts