pull/18629/merge
Luan Taraschi 5 days ago committed by GitHub
commit bb3d3573d7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: keep an own `__proto__` key in `$state.snapshot`

@ -1,7 +1,7 @@
/** @import { Snapshot } from './types' */
import { DEV } from 'esm-env';
import * as w from './warnings.js';
import { get_prototype_of, is_array, object_prototype } from './utils.js';
import { define_property, get_prototype_of, is_array, object_prototype } from './utils.js';
/**
* In dev, we keep track of which properties could not be cloned. In prod
@ -90,7 +90,7 @@ function clone(value, cloned, path, paths, original = null, no_tojson = false) {
}
for (var key of Object.keys(value)) {
copy[key] = clone(
var cloned_value = clone(
// @ts-expect-error
value[key],
cloned,
@ -99,6 +99,22 @@ function clone(value, cloned, path, paths, original = null, no_tojson = false) {
null,
no_tojson
);
if (key === '__proto__') {
// Assigning `__proto__` runs the setter inherited from `Object.prototype`
// rather than creating a property, so the key would be dropped and an
// object value would become the copy's prototype, leaving the snapshot
// inheriting fields that were data. `structuredClone`, which this
// function falls back to below, keeps it as an own property.
define_property(copy, key, {
value: cloned_value,
writable: true,
enumerable: true,
configurable: true
});
} else {
copy[key] = cloned_value;
}
}
return copy;

@ -0,0 +1,5 @@
import { test } from '../../test';
export default test({
html: `<div>["__proto__","b"]</div><div>true</div><div></div>`
});

@ -0,0 +1,11 @@
<script>
// An own `__proto__` key comes from JSON, which is where state hydrated
// from a response, from storage, or from a query string comes from.
let state = $state(JSON.parse('{"__proto__":{"admin":true},"b":2}'));
const snapshot = $state.snapshot(state);
</script>
<div>{JSON.stringify(Object.keys(snapshot))}</div>
<div>{Object.getPrototypeOf(snapshot) === Object.prototype}</div>
<div>{snapshot.admin}</div>
Loading…
Cancel
Save