Skip to content

Spread object prototype is leaked into and overrides props #16139

Open
@rChaoz

Description

@rChaoz

Describe the bug

<!-- Child.svelte -->
<script>
	let { toString } = $props()

	console.log(toString)
</script>


<!-- Parent.svelte -->
<script>
	import Component from "./Component.svelte"
</script>

<Component {...{}} />

Expected output: undefined
Actual output: function toString()

From the docs on the spread operator:

A single spread creates a shallow copy of the original object (but without non-enumerable properties and without copying the prototype)

In regular JS the object prototype (or any prototype for that matter) is never spread:

console.log({ toString: 123, ...{} })
// output: Object { toString: 123 }

The issue comes from this line:

if (typeof p === 'object' && p !== null && key in p) return p[key];

However this is not an isolated case, the in operator is used throughout the file above another 20+ times and is likely used many times in other places as well, causing similar issues.

In general, the in operator is bad and 99% of the times you don't want to check the prototype, so Object.hasOwn or Object.prototype.hasOwnProperty.call should always be preferred. Maybe even ban in using eslint.

Note this issue also causes legit props to be overridden/not accessible:

{@const obj= { somethingElse: 123 }}
<Component toString="legit" {...obj} />

<!-- In component, reading `toString` returns the function rather than "legit", even when it's typed as string -->

Reproduction

https://svelte.dev/playground/8d30600fe1c54c69a76146c602cd977d?version=5.34.1

Severity

serious

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions