Repository navigation
Conversation
SkyZeroZx
force-pushed
the
fix/style-xss
branch
5 times, most recently
from
July 24, 2026 21:07
d09dd18 to
1a88dea
Compare
SkyZeroZx
marked this pull request as ready for review
July 24, 2026 21:39
Author
|
@alan-agius4 Could you please check this? |
SkyZeroZx
force-pushed
the
fix/style-xss
branch
from
August 4, 2026 16:23
1a88dea to
bd63244
Compare
Author
|
@alan-agius4 Sorry to bother you, but I think this should be included in a patch this week as it would be considered a security vulnerability. |
SkyZeroZx
force-pushed
the
fix/style-xss
branch
2 times, most recently
from
October 31, 2026 04:16
183c02b to
88ac836
Compare
Author
|
@alan-agius4 This PR is ready for review ,. Could you take a look at it? |
A parser assigns namespaces from the markup, not from our tree, so the text of an HTML raw text element is escaped wherever a parser re-reading the output may process its start tag in SVG or MathML content, and stays raw everywhere else, including at HTML integration points. The HTML loop of serializeChildren() stays as it was. svg and math subtrees go through serializeForeignContent(), which tracks top-down how a parser reads them: integration points only count in their own namespace, and template contents and fallback raw text are covered. Raw text stays escaped once the parser can leave the tree inside svg or math: when an end tag closes a same-named foreign ancestor of an integration point, when a breakout tag pops a nested svg or math element, when raw text is ended early by its own end tag, after a void element, which is not void in foreign content, and at a table start tag in an integration point of an svg or math element foster parented by a table, where the table insertion mode pops past the integration point. A style, script, xmp or plaintext element that may be read as foreign content gets all of its markup escaped instead of the end tags that match it, since those are real end tags of nested elements when it is read as foreign content. Fixes angular/angular#69900
SkyZeroZx
force-pushed
the
fix/style-xss
branch
from
November 12, 2026 18:03
88ac836 to
e262097
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When a
<style>created through DOM APIs ends up inside an<svg>or<math>(content projection,Renderer2,NgComponentOutlet...), domino writes its text as raw text. But the browser parsing the SSR output sees that<style>inside SVG, so it becomes an SVG<style>and the text is parsed as markup:You don't even need a
</style>for this, the<img>alone breaks out of the SVG. The same happens withscript,xmp,noembedand the other raw text elements.With this change the text gets escaped whenever a parser reading our output could see the start tag inside SVG or MathML, and stays raw everywhere else, so CSS inside a
<foreignObject>keeps working as before.The HTML loop in
serializeChildren()is the same as before, onlysvg/mathsubtrees go through a newserializeForeignContent()that keeps track of how the parser reads each element while serializing (integration points only in their own namespace, breakout tags, template contents with the same_hosthop asfallbackRawContentTags()). Some trees built through DOM APIs don't survive a round trip through the parser, e.g. a stray end tag closing a foreign ancestor, a void element that isn't void in SVG, or a<table>inside a foster parented<svg>. When that can happen I keep escaping until the outermost<svg>/<math>is closed.I also had to change how a
<style>that can be read as SVG is escaped: escaping only its</stylealso broke the end tag of a nested<style>, and the parser stayed inside the SVG after the</svg>. Now all its markup gets escaped in that case.I tested it with trees built through DOM APIs in Chrome, Firefox and WebKit, plus the Angular cases from the issue, and nothing got through. On regular HTML the overhead is around 2%, a bit more on pages with a lot of SVG.
The only downside I found is that with some invalid nesting (like an
<svg>directly inside a<table>) a<style>can end up escaped even if the browser would read it as CSS. I think that's fine atm since it's always on the safe side.Fixes angular/angular#69900
More context issuetracker.google.com/u/1/issues/537033759