VYPR
Moderate severityNVD Advisory· Published Feb 20, 2026· Updated Feb 25, 2026

Svelte SSR attribute spreading includes inherited properties from prototype chain

CVE-2026-27125

Description

svelte performance oriented web framework. Prior to 5.51.5, in server-side rendering, attribute spreading on elements (e.g. <div {...attrs}>) enumerates inherited properties from the object's prototype chain rather than only own properties. In environments where Object.prototype has already been polluted — a precondition outside of Svelte's control — this can cause unexpected attributes to appear in SSR output or cause SSR to throw errors. Client-side rendering is not affected. This vulnerability is fixed in 5.51.5.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
sveltenpm
< 5.51.55.51.5

Affected products

1

Patches

1
73098bb26c6f

Merge commit from fork

https://github.com/sveltejs/svelteElliott JohnsonFeb 18, 2026via ghsa
9 files changed · +79 6
  • .changeset/all-pandas-appear.md+5 0 added
    @@ -0,0 +1,5 @@
    +---
    +'svelte': patch
    +---
    +
    +fix: check to make sure `svelte:element` tags are valid during SSR
    
  • documentation/docs/98-reference/.generated/server-errors.md+8 0 modified
    @@ -16,6 +16,14 @@ Encountered asynchronous work while rendering synchronously.
     
     You (or the framework you're using) called [`render(...)`](svelte-server#render) with a component containing an `await` expression. Either `await` the result of `render` or wrap the `await` (or the component containing it) in a [`<svelte:boundary>`](svelte-boundary) with a `pending` snippet.
     
    +### dynamic_element_invalid_tag
    +
    +```
    +`<svelte:element this="%tag%">` is not a valid element name — the element will not be rendered
    +```
    +
    +The value passed to the `this` prop of `<svelte:element>` must be a valid HTML element, SVG element, MathML element, or custom element name. A value containing invalid characters (such as whitespace or special characters) was provided, which could be a security risk. Ensure only valid tag names are passed.
    +
     ### html_deprecated
     
     ```
    
  • packages/svelte/messages/server-errors/errors.md+6 0 modified
    @@ -10,6 +10,12 @@ Some platforms require configuration flags to enable this API. Consult your plat
     
     You (or the framework you're using) called [`render(...)`](svelte-server#render) with a component containing an `await` expression. Either `await` the result of `render` or wrap the `await` (or the component containing it) in a [`<svelte:boundary>`](svelte-boundary) with a `pending` snippet.
     
    +## dynamic_element_invalid_tag
    +
    +> `<svelte:element this="%tag%">` is not a valid element name — the element will not be rendered
    +
    +The value passed to the `this` prop of `<svelte:element>` must be a valid HTML element, SVG element, MathML element, or custom element name. A value containing invalid characters (such as whitespace or special characters) was provided, which could be a security risk. Ensure only valid tag names are passed.
    +
     ## html_deprecated
     
     > The `html` property of server render results has been deprecated. Use `body` instead.
    
  • packages/svelte/src/compiler/phases/1-parse/state/element.js+11 4 modified
    @@ -2,7 +2,7 @@
     /** @import { Location } from 'locate-character' */
     /** @import { AST } from '#compiler' */
     /** @import { Parser } from '../index.js' */
    -import { is_void } from '../../../../utils.js';
    +import { is_void, REGEX_VALID_TAG_NAME } from '../../../../utils.js';
     import read_expression from '../read/expression.js';
     import { read_script } from '../read/script.js';
     import read_style from '../read/style.js';
    @@ -24,8 +24,15 @@ const regex_whitespace_or_slash_or_closing_tag = /(\s|\/|>)/;
     const regex_token_ending_character = /[\s=/>"']/;
     const regex_starts_with_quote_characters = /^["']/;
     const regex_attribute_value = /^(?:"([^"]*)"|'([^'])*'|([^>\s]+))/;
    -const regex_valid_element_name =
    -	/^(?:![a-zA-Z]+|[a-zA-Z](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?|[a-zA-Z][a-zA-Z0-9]*:[a-zA-Z][a-zA-Z0-9-]*[a-zA-Z0-9])$/;
    +/** @param {string} name */
    +function is_valid_element_name(name) {
    +	// DOCTYPE (e.g. !DOCTYPE)
    +	if (/^![a-zA-Z]+$/.test(name)) return true;
    +	// svelte:* meta tags (e.g. svelte:element, svelte:head)
    +	if (/^[a-zA-Z][a-zA-Z0-9]*:[a-zA-Z][a-zA-Z0-9-]*[a-zA-Z0-9]$/.test(name)) return true;
    +	// standard HTML/SVG/MathML elements and custom elements
    +	return REGEX_VALID_TAG_NAME.test(name);
    +}
     export const regex_valid_component_name =
     	// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#identifiers adjusted for our needs
     	// (must start with uppercase letter if no dots, can contain dots)
    @@ -134,7 +141,7 @@ export default function element(parser) {
     		e.svelte_meta_invalid_tag(bounds, list(Array.from(meta_tags.keys())));
     	}
     
    -	if (!regex_valid_element_name.test(tag.name) && !regex_valid_component_name.test(tag.name)) {
    +	if (!is_valid_element_name(tag.name) && !regex_valid_component_name.test(tag.name)) {
     		// <div. -> in the middle of typing -> allow in loose mode
     		if (!parser.loose || !tag.name.endsWith('.')) {
     			const bounds = { start: start + 1, end: start + 1 + tag.name.length };
    
  • packages/svelte/src/internal/server/errors.js+13 0 modified
    @@ -14,6 +14,19 @@ export function async_local_storage_unavailable() {
     	throw error;
     }
     
    +/**
    + * `<svelte:element this="%tag%">` is not a valid element name — the element will not be rendered
    + * @param {string} tag
    + * @returns {never}
    + */
    +export function dynamic_element_invalid_tag(tag) {
    +	const error = new Error(`dynamic_element_invalid_tag\n\`<svelte:element this="${tag}">\` is not a valid element name — the element will not be rendered\nhttps://svelte.dev/e/dynamic_element_invalid_tag`);
    +
    +	error.name = 'Svelte error';
    +
    +	throw error;
    +}
    +
     /**
      * Encountered asynchronous work while rendering synchronously.
      * @returns {never}
    
  • packages/svelte/src/internal/server/index.js+10 2 modified
    @@ -13,9 +13,14 @@ import {
     } from '../../constants.js';
     import { escape_html } from '../../escaping.js';
     import { DEV } from 'esm-env';
    -import { EMPTY_COMMENT, BLOCK_CLOSE, BLOCK_OPEN, BLOCK_OPEN_ELSE } from './hydration.js';
    +import { EMPTY_COMMENT, BLOCK_OPEN, BLOCK_OPEN_ELSE } from './hydration.js';
     import { validate_store } from '../shared/validate.js';
    -import { is_boolean_attribute, is_raw_text_element, is_void } from '../../utils.js';
    +import {
    +	is_boolean_attribute,
    +	is_raw_text_element,
    +	is_void,
    +	REGEX_VALID_TAG_NAME
    +} from '../../utils.js';
     import { Renderer } from './renderer.js';
     import * as e from './errors.js';
     
    @@ -35,6 +40,9 @@ export function element(renderer, tag, attributes_fn = noop, children_fn = noop)
     	renderer.push('<!---->');
     
     	if (tag) {
    +		if (!REGEX_VALID_TAG_NAME.test(tag)) {
    +			e.dynamic_element_invalid_tag(tag);
    +		}
     		renderer.push(`<${tag}`);
     		attributes_fn();
     		renderer.push(`>`);
    
  • packages/svelte/src/utils.js+13 0 modified
    @@ -480,6 +480,19 @@ export function is_raw_text_element(name) {
     	return RAW_TEXT_ELEMENTS.includes(/** @type {typeof RAW_TEXT_ELEMENTS[number]} */ (name));
     }
     
    +// Matches valid HTML/SVG/MathML element names and custom element names.
    +// https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
    +//
    +// Standard elements: ASCII alpha start, followed by ASCII alphanumerics.
    +// Custom elements: ASCII alpha start, followed by any mix of PCENChar (which
    +// includes ASCII alphanumerics, `-`, `.`, `_`, and specified Unicode ranges),
    +// with at least one hyphen required somewhere after the first character.
    +//
    +// Rejects strings containing whitespace, quotes, angle brackets, slashes, equals,
    +// or other characters that could break out of a tag-name token and enable markup injection.
    +export const REGEX_VALID_TAG_NAME =
    +	/^[a-zA-Z][a-zA-Z0-9]*(-[a-zA-Z0-9.\-_\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u{10000}-\u{EFFFF}]+)*$/u;
    +
     /**
      * Prevent devtools trying to make `location` a clickable link by inserting a zero-width space
      * @template {string | undefined} T
    
  • packages/svelte/tests/server-side-rendering/samples/dynamic-element-xss-prevention/_config.js+8 0 added
    @@ -0,0 +1,8 @@
    +import { test } from '../../test';
    +
    +export default test({
    +	props: {
    +		tag: 'svg onload=alert(1)'
    +	},
    +	error: 'dynamic_element_invalid_tag'
    +});
    
  • packages/svelte/tests/server-side-rendering/samples/dynamic-element-xss-prevention/main.svelte+5 0 added
    @@ -0,0 +1,5 @@
    +<script>
    +	let { tag } = $props();
    +</script>
    +
    +<svelte:element this={tag}>ok</svelte:element>
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.