mirror of
https://github.com/penpot/penpot.git
synced 2026-03-20 01:13:41 +00:00
🐛 Fix "Cannot assign to read only property toString" error in plugins runtime
The error "Cannot assign to read only property 'toString' of function" occurs during React's commit phase after a plugin is loaded. The root cause is an initialization ordering issue in the SES (Secure EcmaScript) lockdown sequence. When loadPlugin() is called, ses.harden(context) runs first, which transitively freezes everything reachable from the context object — including Function.prototype and Object.prototype — via prototype chain traversal of getter functions. Later, createSandbox() calls ses.hardenIntrinsics(), which attempts to run enablePropertyOverrides() to convert frozen data properties (like Function.prototype.toString) into accessor pairs that work around JavaScript's "override mistake". However, enablePropertyOverrides checks "if (configurable)" before converting, and since Function.prototype is already frozen (all properties have configurable: false), the override taming is silently skipped. This leaves Function.prototype.toString as a frozen non-writable data property, causing any subsequent code that assigns .toString to a function instance in strict mode to throw a TypeError. The fix calls ses.hardenIntrinsics() before ses.harden(context) in loadPlugin(), ensuring override taming installs the accessor pairs on prototype properties before they get frozen. The existing hardenIntrinsics() call in createSandbox() becomes a harmless no-op thanks to the idempotency guard. Signed-off-by: Andrey Antukh <niwi@niwi.nz>
This commit is contained in:
committed by
Alonso Torres
parent
27a934dcfd
commit
f796f7ccb9
@@ -22,6 +22,7 @@ vi.mock('./create-plugin', () => ({
|
||||
vi.mock('./ses.js', () => ({
|
||||
ses: {
|
||||
harden: vi.fn().mockImplementation((obj) => obj),
|
||||
hardenIntrinsics: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
@@ -52,6 +52,16 @@ export const loadPlugin = async function (
|
||||
|
||||
closeAllPlugins();
|
||||
|
||||
// hardenIntrinsics must be called BEFORE harden() to ensure that
|
||||
// override taming (enablePropertyOverrides) converts prototype
|
||||
// properties like Function.prototype.toString into accessor pairs.
|
||||
// Without this, harden() would freeze Function.prototype with plain
|
||||
// data properties, making them non-configurable, which causes
|
||||
// enablePropertyOverrides to silently skip them when hardenIntrinsics
|
||||
// runs later — resulting in "Cannot assign to read only property
|
||||
// 'toString'" errors.
|
||||
ses.hardenIntrinsics();
|
||||
|
||||
const plugin = await createPlugin(
|
||||
ses.harden(context) as Context,
|
||||
manifest,
|
||||
|
||||
Reference in New Issue
Block a user