Skip to content

refactor(validators): centralize endpoint URL policy and allow HTTP in development#11

Merged
voyvodka merged 2 commits into
mainfrom
feature/dev-http-endpoint-validator
May 4, 2026
Merged

refactor(validators): centralize endpoint URL policy and allow HTTP in development#11
voyvodka merged 2 commits into
mainfrom
feature/dev-http-endpoint-validator

Conversation

@voyvodka
Copy link
Copy Markdown
Owner

@voyvodka voyvodka commented May 4, 2026

Why

Local end-to-end testing of the JMESPath transformation pipeline (and the upcoming sample receivers / Docker compose flows) needs to point endpoints at a host receiver on `http://host.docker.internal:9999\` or similar. The existing validator hard-codes HTTPS-only, which is correct for production but blocks local verification without TLS plumbing.

What changed

  • Extracted the four duplicated copies of `BeValidHttpsUrl` into a single `EndpointUrlPolicy` service.
  • Production behavior is unchanged: HTTPS-only.
  • Development environment (`IHostEnvironment.IsDevelopment()`): HTTP is also accepted.
  • The relaxation is gated on the host environment, not a feature flag — production deployments cannot accidentally opt in.
  • Validation message adapts to the active rule.

Test plan

  • `dotnet build WebhookEngine.sln --configuration Release` (0 errors)
  • `dotnet test WebhookEngine.sln` — all 142 tests pass under the default Testing environment (HTTPS still required there)
  • CI (Backend / Frontend / Docker / CodeQL) green on this branch
  • Local Docker e2e: create endpoint with `http://host.docker.internal:9999\` succeeds in Development env

voyvodka added 2 commits May 5, 2026 01:26
…n development

The four endpoint validators (CreateEndpointRequestValidator, UpdateEndpointRequestValidator, and the dashboard equivalents) each carried their own copy of a private static BeValidHttpsUrl helper. Extracted the rule into a single EndpointUrlPolicy service injected via the DI container; AddValidatorsFromAssemblyContaining<Program>() resolves it for every endpoint validator without further wiring.

Production behavior is unchanged: HTTPS remains the only accepted scheme. In Development environments (IHostEnvironment.IsDevelopment() == true), HTTP is also accepted so local end-to-end testing — Docker compose with a host receiver, sample receivers, ngrok-free flows — works without TLS plumbing. The relaxation is gated on the host environment rather than a feature flag, so production deployments cannot accidentally opt in. The validation message adapts ("HTTPS or HTTP" vs "HTTPS") so callers see the rule that applies in their environment.

All 142 existing tests still pass — they run under the default Testing environment with no Development override and continue to expect HTTPS.
CI runners hit the 100ms production default on JmesPath.Net's first JIT-warmed evaluation, intermittently flipping the size and reshape tests into the timeout fail-open path. Test fixtures now use a 5000ms ceiling by default; the production default in TransformationOptions stays at 100ms. Tests that specifically exercise the timeout still pass an explicit short value.
@voyvodka voyvodka merged commit 5cfc059 into main May 4, 2026
7 checks passed
@voyvodka voyvodka deleted the feature/dev-http-endpoint-validator branch May 4, 2026 22:33
voyvodka added a commit that referenced this pull request May 11, 2026
…le mock-fetch (#100)

CodeQL alert #11 (HIGH severity, js/incomplete-url-substring-sanitization) on samples/portal-host/src/mock-fetch.ts:117. The previous shape was:

  if (!url.startsWith(BASE)) return originalFetch(input, init);

with BASE = 'https://hooks.example.com'. The startsWith check correctly catches paths like https://hooks.example.com/api/v1/portal/endpoints, but it ALSO matches https://hooks.example.com.attacker.com/api/v1/portal/endpoints — the BASE prefix can be followed by an arbitrary host suffix. This is fine for the sample's mock semantics in isolation (the worst case is the mock answers a request that should have gone to the real network), but the kind of pattern that absolutely should not get copy-pasted into production code.

Fixed by parsing the URL and comparing protocol + host explicitly via WHATWG URL. Same intent, no substring trap. Also flipped path = url.slice(BASE.length).split('?')[0] to path = parsed.pathname which is the same value via a safer route. The route handlers downstream are unchanged.

Closes the CodeQL alert. No test changes required — the mock continues to serve the same routes for the same inputs the sample emits.
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.

1 participant