Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Snapshots can be mutated by adding or removing properties #749

Closed
1 of 2 tasks
pastelmind opened this issue Jul 9, 2023 · 2 comments · Fixed by #752
Closed
1 of 2 tasks

Snapshots can be mutated by adding or removing properties #749

pastelmind opened this issue Jul 9, 2023 · 2 comments · Fixed by #752

Comments

@pastelmind
Copy link
Contributor

pastelmind commented Jul 9, 2023

Summary

Adding or removing properties from a snapshot object or array does not throw, even in strict mode.

'use strict' // Must be enabled in browser scripts / Node CommonJS

const state = proxy([5, 10, 15])
const snap = snapshot(state)

// snap[0] = 100 // Throws
snap[10] = 100 // Does not throw
snap.push(100) // Does not throw
snap.pop() // Does not throw

const state2 = proxy({ foo: 1 })
const snap2 = snapshot(state2)

// snap2.foo = 100 // Throws
snap2.bar = 2 // Does not throw

Although it appears that such invalid operations do not affect the proxy store itself, silently swallowing allowing these errors is undesirable, since the snapshot (correctly) errors on other types of mutations.

Edit: The snapshot can be mutated:

const p = proxy({ foo: 1 });
const s = snapshot(p);
console.log(s); // { foo: 1 }
s.bar = 2;
console.log(s); // { foo: 1, bar: 2 }
delete s.foo;
console.log(s); // { bar: 2 }

This seems to be a correctness issue.

Link to reproduction

https://codesandbox.io/s/sad-gauss-w364gf

Check List

Please do not ask questions in issues.

  • I've already opened a discussion before opening this issue, or already discussed in other media.

Please include a minimal reproduction.

@pastelmind
Copy link
Contributor Author

pastelmind commented Jul 9, 2023

Preventing the addition of new properties should be trivial. We can call Object.preventExtensions() on the snapshot object here, just before returning it here:

diff --git a/src/vanilla.ts b/src/vanilla.ts
index 7844994..1e169a0 100644
--- a/src/vanilla.ts
+++ b/src/vanilla.ts
@@ -146,6 +146,7 @@ const buildProxyFunction = (
       }
       Object.defineProperty(snap, key, desc)
     })
+    Object.preventExtensions(snap)
     return snap
   },

I tested this locally and found no breaking tests. I can submit a PR if you want.

However, preventing deletion would be a bit more involved. It's hard to grok the details, but I believe snapshot properties were made configurable in ba53bf6 (#664) due to performance issues. Would it be too difficult to revert some of the changes?

If we want to avoid reverting the above, I suppose we might be able to wrap the snapshot object in its own Proxy with a deleteProperty handler that throws a TypeError. This sounds too complicated and risky, though.

@pastelmind pastelmind changed the title Extending or removing properties from snapshot does not throw Snapshots can be mutated by adding or removing properties Jul 9, 2023
@dai-shi
Copy link
Member

dai-shi commented Jul 10, 2023

We used to deep-freeze snapshot object. But, there's performance downside, because we had to deep-copy the object in proxy-compare. So, it means we took performance over correctness.

Object.preventExtensions() sounds good. If it doesn't break existing tests and it doesn't drop performance (but, we don't have tests for it), we can merge it.

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 a pull request may close this issue.

2 participants