From 6da57c5443054ff0482e35aff485dea6bbf628a6 Mon Sep 17 00:00:00 2001 From: Adrian Niculescu <15037449+adrian-niculescu@users.noreply.github.com> Date: Tue, 6 Oct 2026 20:46:19 +0300 Subject: [PATCH] fix(worker): throw when an environment-data key cannot be converted to a string setEnvironmentData and getEnvironmentData converted the key with a helper that swallows a throwing toString() and returns an empty string, so such a key silently read, overwrote or deleted the unrelated empty-string entry. The conversion is checked now and the exception reaches the caller. --- NativeScript/runtime/Messaging.cpp | 22 ++++++++++++++++++-- TestRunner/app/tests/MessagingTests.js | 28 ++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/NativeScript/runtime/Messaging.cpp b/NativeScript/runtime/Messaging.cpp index a1750620..136ccaed 100644 --- a/NativeScript/runtime/Messaging.cpp +++ b/NativeScript/runtime/Messaging.cpp @@ -958,13 +958,28 @@ void SetEmitMessageCallback(const FunctionCallbackInfo& info) { state->emitMessage.Reset(isolate, info[0].As()); } +// The key as the string the store files it under. A key whose conversion throws +// leaves that exception pending for the caller rather than standing in for "". +static bool EnvironmentDataKey(Isolate* isolate, Local value, + std::string& key) { + Local str; + if (!value->ToString(isolate->GetCurrentContext()).ToLocal(&str)) { + return false; + } + key = tns::ToString(isolate, str); + return true; +} + void SetEnvironmentDataCallback(const FunctionCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); if (info.Length() < 1) { return; } Local context = isolate->GetCurrentContext(); - std::string key = tns::ToString(isolate, info[0]); + std::string key; + if (!EnvironmentDataKey(isolate, info[0], key)) { + return; + } if (info.Length() < 2 || info[1]->IsUndefined()) { std::lock_guard lock(g_environmentDataMutex); g_environmentData.erase(key); @@ -988,7 +1003,10 @@ void GetEnvironmentDataCallback(const FunctionCallbackInfo& info) { if (info.Length() < 1) { return; } - std::string key = tns::ToString(isolate, info[0]); + std::string key; + if (!EnvironmentDataKey(isolate, info[0], key)) { + return; + } std::shared_ptr stored; { std::lock_guard lock(g_environmentDataMutex); diff --git a/TestRunner/app/tests/MessagingTests.js b/TestRunner/app/tests/MessagingTests.js index f6695984..308dc807 100644 --- a/TestRunner/app/tests/MessagingTests.js +++ b/TestRunner/app/tests/MessagingTests.js @@ -295,6 +295,34 @@ describe("Messaging runtime edges", function () { }); }); + describe("environment data keys", function () { + function thrownMessage(fn) { + try { + fn(); + } catch (e) { + return e && e.message; + } + return "nothing thrown"; + } + + it("throws what a key's toString throws instead of using the empty-string key", function () { + var wt = require("node:worker_threads"); + var key = { toString: function () { throw new Error("key toString failed"); } }; + wt.setEnvironmentData("", "empty"); + try { + expect(thrownMessage(function () { wt.setEnvironmentData(key, "other"); })) + .toBe("key toString failed"); + expect(thrownMessage(function () { wt.getEnvironmentData(key); })) + .toBe("key toString failed"); + expect(thrownMessage(function () { wt.setEnvironmentData(key); })) + .toBe("key toString failed"); + expect(wt.getEnvironmentData("")).toBe("empty"); + } finally { + wt.setEnvironmentData(""); + } + }); + }); + describe("worker error reporting", function () { // A worker boots on its own thread, so the first error arrives whenever // the runner gets to it; specs wait for it and only then settle for