]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commit
fix(simulator): validate template supervision URLs with the shared endpoint policy...
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Fri, 6 Nov 2026 15:52:33 +0000 (16:52 +0100)
committerGitHub <noreply@github.com>
Fri, 6 Nov 2026 15:52:33 +0000 (16:52 +0100)
commit36365546c6a49c6d3f4184cc0f5af8eba97a46f0
treed51d3b6200482f4b137152474cc6a85e4e8ea4ba
parent978b1721e18e39db87928e478aca9142e2f7b0bb
fix(simulator): validate template supervision URLs with the shared endpoint policy (#2185)

* fix(simulator): validate template supervision URLs with the shared endpoint policy

Templates accepted arbitrary strings in supervisionUrls: a value the
supervision transport cannot dial (e.g. ftp://, a fragment, a query)
passed template validation and only failed at WebSocket connection time
with a bare protocol SyntaxError naming neither the field nor the
template.

Narrow BaseTemplateSchema.supervisionUrls (string or string[]) to the
shared SupervisionUrlSchema - the same policy as configuration files
(ConfigurationSchema) and runtime UI payloads
(ChargingStationConfigurationSchema) - and bump the template schema
version to v2 with an identity migration: the narrowing is a validation
concern, and a v1 template carrying a value the transport cannot dial
has no migrated form, so it is rejected with a field-level diagnostic.
All shipped station templates are bumped to v2.

Rejected values never reach a live station: template loading fails
before construction, and the consumer-side policy (#2181) is unchanged.

Tests cover the shared sub-schema (schemes, query/fragment, ws+unix
form, whitespace trimming), the v1 -> v2 identity hop, v1 template
rejection (field naming, migration note) and migration of a valid v1
template, shipped-template round-trips, and real-station coverage of
the template and OCPP-key seeding paths plus setSupervisionUrl.

* fix(simulator): validate seeded OCPP supervision key at template load

A template seeding the active supervision OCPP key (Configuration.configurationKey entry matching supervisionUrlOcppKey) with a value the transport cannot dial now fails at template load, naming the key, instead of constructing a station that fails at first connection. Empty values remain inert (the consumer ignores them).

The shared policy error text is aligned to the 'supervision URL' terminology used across the codebase, and the README cell harmonizes URIs to URLs.

* fix(simulator): apply OCPP 2.0.x key resolution to the seeded supervision key check

The consumer resolves supervisionUrlOcppKey before matching configuration entries (OCPP 2.0.x stations store the mapped 1.6 standard keys under their 2.0.1 namespaced variable name). The template-load check now applies the same resolution via the extracted resolveOcppParameterKey, closing the fail-fast gap for namespaced entries and removing a false positive for un-resolved spellings.

The README supervisionUrlOcppKey row documents the seeded-value constraint, and isOCPP20x accepts a plain version string.

* [autofix.ci] apply automated fixes

* fix(simulator): validate only the first matching entry of the seeded supervision OCPP key

The consumer dials only the first configuration key entry matching the supervision key (later duplicates are ignored, and an empty first value falls back to the configured supervision URL). The template-load check now mirrors that lookup exactly instead of validating every match, which rejected templates the station would have dialed correctly. Suggested by the repository PR-bot review comment on TemplateSchema.

* fix(simulator): extend supervision URL policy to the effective key and 2.0.1 writes

- Validate the effective OCPP key at template load: an omitted
  supervisionUrlOcppKey falls back to the default ConnectionUrl, so
  the seeded value for that key is checked like any other active key.
- Reject SetVariables writes that point the seeded supervision OCPP
  key at a value the transport cannot dial, mirroring the OCPP 1.6
  ChangeConfiguration guard; isSupportedSupervisionUrl moves to
  ConfigurationKeyUtils so both services share one predicate.
- README: align the seeded-value clause with the consumer semantics
  (first matching entry; empty values are ignored).

* [autofix.ci] apply automated fixes

* fix(simulator): apply the raw-value supervision guard to the seeded OCPP key

SupervisionUrlSchema normalizes whitespace and control characters, but the
OCPP key stores the value as sent and the consumer dials it raw. The seeded
check now applies the shared isSupportedSupervisionUrl predicate before schema
parsing so a padded or control-character value is rejected like the equivalent
runtime write (ChangeConfiguration / SetVariables), naming the raw-character
rule when the schema itself would accept the normalized form.

* test(simulator): pin supervision guard coverage boundaries and correct its documentation

- Correct the raw-value guard documentation: the consumer trims the base
  before appending the station identity, so the raw check is deliberate
  input hygiene stricter than the dial path, not a dial constraint.
- Document the 2.0.1 guard coverage boundary: unmapped vendor keys and the
  BootNotification heartbeat-interval write are inert by design; the
  fail-closed dial-time parse remains the net for both.
- Tests: pin the guard's inert branches (empty key, unmapped vendor key),
  restore dial-time coverage for a non-dialable value from a non-template
  source (persisted file / manual edit), and assert the template file name
  in the load error.

* fix(charging-station): unify supervision OCPP key active check and correct audit findings

Add the isSupervisionOcppKeyActive predicate and migrate the five
call sites that each rewrote the active-key condition (consumer
getter, setSupervisionUrl, constructor re-seed, 1.6 ChangeConfiguration
guard, 2.0.1 SetVariables guard). The 1.6 guard now also requires a
non-empty key, aligning it with the consumer, the 2.0.1 guard and the
template-load check: a station whose supervision key is empty never
dials the key, so a write to it is inert instead of rejected.

Correct the audit findings in the written artifacts: resolveOcppParameterKey
maps 1.6 keys (standard and vendor) so drop the imprecise 'standard'
qualification and reference the resolver instead of re-explaining it;
state the 2.0.1 guard matching condition exactly (a key matches a
component-scoped write key only when its resolved spelling is namespaced);
widen the isSupportedSupervisionUrl JSDoc scope (it guards runtime
writes as well as the seeded value); reference the raw-value guard
rationale from its authoritative JSDoc instead of re-explaining it in
three places; use the canonical 'supervision' terminology in the README
supervisionUrls row and the two 'shared supervision endpoint policy'
mention sites; rename the padded-value test case to match what it
exercises.

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
34 files changed:
README.md
cspell.config.yaml
src/assets/station-templates/abb-atg.station-template.json
src/assets/station-templates/abb.station-template.json
src/assets/station-templates/chargex.station-template.json
src/assets/station-templates/evlink.station-template.json
src/assets/station-templates/keba-ocpp2-signed.station-template.json
src/assets/station-templates/keba-ocpp2.station-template.json
src/assets/station-templates/keba.station-template.json
src/assets/station-templates/schneider-evses.station-template.json
src/assets/station-templates/schneider-imredd.station-template.json
src/assets/station-templates/schneider.station-template.json
src/assets/station-templates/siemens.station-template.json
src/assets/station-templates/virtual-simple-atg.station-template.json
src/assets/station-templates/virtual-simple-signed.station-template.json
src/assets/station-templates/virtual-simple.station-template.json
src/assets/station-templates/virtual.station-template.json
src/charging-station/ChargingStation.ts
src/charging-station/ConfigurationKeyUtils.ts
src/charging-station/TemplateMigrations.ts
src/charging-station/TemplateSchema.ts
src/charging-station/index.ts
src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts
src/charging-station/ocpp/2.0/OCPP20VariableManager.ts
src/utils/ConfigurationSchema.ts
src/utils/Utils.ts
tests/charging-station/ChargingStation-SupervisionUrl.test.ts
tests/charging-station/ConfigurationKeyUtils.test.ts
tests/charging-station/TemplateMigrations.test.ts
tests/charging-station/TemplateSchema.test.ts
tests/charging-station/TemplateValidation.test.ts
tests/charging-station/ocpp/1.6/OCPP16IncomingRequestService-Configuration.test.ts
tests/charging-station/ocpp/2.0/OCPP20VariableManager.test.ts
tests/charging-station/ui-server/ui-services/UIServiceRequestPayloadSchemas.test.ts