Skip to content

fix: handle prototype-colliding external event names - #384

Merged
wangbill (YunchuWang) merged 1 commit into
mainfrom
yunchuwang-event-name-routing
Oct 8, 2026
Merged

wangbill (YunchuWang) merged 1 commit into
mainfrom
yunchuwang-event-name-routing

Conversation

@YunchuWang

@YunchuWang wangbill (YunchuWang) commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

Summary

Fix external-event routing for valid names that collide with JavaScript object properties, including constructor and __proto__.

What changed?

  • Initialize the received-event and pending-listener dictionaries without a prototype, while retaining their existing typed record contracts and queue operations.
  • Add public in-memory client/worker regressions for listener-first and event-first delivery, mixed casing, FIFO ordering, falsy payloads, and unprocessed-event carryover through continue-as-new and replay.
  • Add a core SDK changelog note.

Why is this change needed?

  • Plain-object lookups could return inherited properties instead of event queues. Waiting for constructor failed before event delivery; an early constructor or __proto__ event failed the orchestration instead of being buffered.
  • Non-empty event names remain accepted without a reserved-name blacklist. Case-insensitive matching, per-name ordering, payload serialization, and replay behavior are preserved.

Issues / work items

  • Resolves: N/A; no linked issue.
  • Related: N/A.

Project checklist

  • Release notes are not required for the next release
    • Otherwise: Notes added to CHANGELOG.md
  • Backport is not required
    • Otherwise: Backport tracked by issue/PR (N/A)
  • All required tests have been added/updated (unit tests, E2E tests)
  • Breaking change?
    • If yes:
      • Impact: No public API or protocol change.
      • Migration guidance: None required for this fix.

AI-assisted code disclosure (required)

Was an AI tool used? (select one)

  • No
  • Yes, AI helped write parts of this PR (e.g., GitHub Copilot)
  • Yes, an AI agent generated most of this PR

If AI was used:

  • Tool(s): GitHub Copilot.
  • AI-assisted areas/files: Runtime event dictionary initialization, external-event-routing.spec.ts, CHANGELOG.md, and this description.
  • What you changed after AI output: No human edits recorded; human review is pending.

AI verification (required if AI was used):

  • I understand the code and can explain it
  • I verified referenced APIs/types exist and are correct
  • I reviewed edge cases/failure paths (timeouts, retries, cancellation, exceptions)
  • I reviewed concurrency/async behavior
  • I checked for unintended breaking or behavior changes

Testing

Automated tests

  • Result: Passed after the fix. Before the production change, 10 of the 19 new regressions failed with the expected public TypeError failure details; the other 9 new cases and 13 existing controls passed. The same focused selection then passed all 32 tests.
  • npm run test:unit -w @microsoft/durabletask-js -- --silent - passed: 86 suites, 1,715 tests, including all 19 new regressions and existing empty-name/null-payload controls.
  • npm run build:core - passed.
  • .\node_modules\.bin\eslint.cmd packages\durabletask-js\src\worker\runtime-orchestration-context.ts packages\durabletask-js\test\external-event-routing.spec.ts - passed.
  • git diff --check origin/main...HEAD - passed.
  • Prettier: the complete new test and changelog content and the edited runtime hunk pass a check against the repository configuration after normalizing Windows checkout line endings. Unrelated pre-existing runtime formatting differences were left unchanged.
  • Hosted CI has not run locally. Full-workspace aggregates, sidecar/DTS emulator E2E, Azure E2E, and sample validation were not run.

Manual validation (only if runtime/behavior changed)

  • Environment (OS, Node.js version, components): Windows, Node.js v24.14.0, freshly built local core SDK with the public TestOrchestrationClient, TestOrchestrationWorker, and in-memory backend; no external service.
  • Steps + observed results:
    1. Run ordinary async generator orchestrators for both arrival orders with constructor, Constructor, __proto__, and normal/mixed-case controls. All completed with the exact raised payload; the original constructor and early-event failures are gone.
    2. Fence event application in committed history, exercise FIFO with [false, 0], and carry events into a new execution before a fresh event. Results preserve order and once-only consumption: [false, 0] and [false, 0, "fresh"].
    3. Complete all 19 compiled-runtime cases, stop the worker/client, and reset the backend. No pending work or active timers remained, and the process exited naturally.
  • Evidence (optional): Local proof artifacts record the committed source/module provenance, public terminal states, event ordering, and cleanup.

Notes for reviewers

  • The production change is limited to the two event dictionaries. No dependency, generated protobuf, entity registry/dispatch, or language-adapter changes.
  • The simple __proto__ listener-first case already worked before the fix and remains covered; the failing __proto__ case was event-before-listener routing.
  • Event names are protobuf string fields. The documented failure-properties protobuf map-key limitation is unrelated.

Initialize received and pending event dictionaries without inherited
properties so constructor and __proto__ route to event queues.

Add public in-memory client/worker regressions for both arrival orders,
case-insensitive matching, FIFO, falsy payloads, and continue-as-new replay.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b5bc4dcd-bd3b-4767-92b5-12338c9f1ceb
Copilot AI balanced review requested due to automatic review settings October 8, 2026 18:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The focused implementation correctly addresses prototype collisions and is backed by comprehensive tests.

0 open findings

What changed in this PR

Fixes external-event routing for names that collide with JavaScript object properties.

Changes:

  • Uses prototype-free event dictionaries.
  • Adds routing, FIFO, falsy-payload, replay, and continue-as-new regressions.
  • Documents the fix in the changelog.
File Description
runtime-orchestration-context.ts Creates prototype-free event dictionaries.
external-event-routing.spec.ts Adds comprehensive regression coverage.
CHANGELOG.md Records the external-event fix.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@YunchuWang
wangbill (YunchuWang) merged commit 218503e into main Oct 8, 2026
31 checks passed
@YunchuWang
wangbill (YunchuWang) deleted the yunchuwang-event-name-routing branch October 8, 2026 20:38
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.

4 participants