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

Latest versions of DOMPurify 2.5.x block custom SVG elements when they are set via ADD_TAGS config. #954

Closed
kevinroast opened this issue May 9, 2024 · 6 comments

Comments

@kevinroast
Copy link

kevinroast commented May 9, 2024

Background & Context

Our use-case works with custom SVG tags (output by MS Visio export process). These tags are passed via the ADD_TAGS configuration during DOMPurify set-up.

We were using an older DOMPurify (2.0.8) and the custom tags supplied via ADD_TAGS config work as expected and survive the purify process. With latest DOMPurify (2.5.2) which we obviously want for security updates, the custom tags are stripped.

Bug

Latest versions of DOMPurify 2.5.x block custom SVG elements when they are set via ADD_TAGS config.

Input

Example SVG HTML input:

<!-- Generated by Microsoft Visio, SVG Export automation.svg Page-1 -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
		xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="5.65654in" height="0.711985in"
		viewBox="0 0 407.271 51.2629" xml:space="preserve" color-interpolation-filters="sRGB" class="st5">
	<v:documentProperties v:langID="2057" v:metric="true" v:viewMarkup="false"/>
	<g v:mID="0" v:index="1" v:groupContext="foregroundPage">
		<title>Page-1</title>
		<v:pageProperties v:drawingScale="0.0393701" v:pageScale="0.0393701" v:drawingUnits="24" v:shadowOffsetX="8.50393" v:shadowOffsetY="-8.50393"/>
		<g id="group1-1" transform="translate(0.25,-0.25)" v:mID="1" v:groupContext="group">
			<v:custProps>
				<v:cp v:nameU="Row_3" v:lbl="scomId" v:type="0" v:langID="1033" v:val="VT4(d706f0fd-61f1-4a87-277a-928ddb1a76b6)"/>
				<v:cp v:nameU="Row_4" v:lbl="scomName" v:type="0" v:langID="2057" v:val="VT4(IIS 10 Computer Group)"/>
				<v:cp v:nameU="Row_5" v:lbl="scomPath" v:type="0" v:langID="2057"/>
			</v:custProps>
		</g>
	</g>
</svg>

Code is something like this, we generate the list of custom tags from input, so you would expect 'customTags' to be something like:
0: "v:documentProperties"
1: "v:pageProperties"
2: "v:custProps"
3: "v:cp"

const config = { ADD_TAGS: customTags }; const output = DOMPurify.sanitize(html, config);

Given output

<svg class="st5" color-interpolation-filters="sRGB" xml:space="preserve" viewBox="0 0 407.271 51.2629" height="0.711985in" width="5.65654in" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg">
    <g v:groupcontext="foregroundPage" v:index="1" v:mid="0">
        <title>Page-1</title>
		<g v:groupcontext="group" v:mid="1" transform="translate(0.25,-0.25)" id="group1-1">
		</g>
	</g>
</svg>

Expected output

<svg class="st5" color-interpolation-filters="sRGB" xml:space="preserve" viewBox="0 0 407.271 51.2629" height="0.711985in" width="5.65654in" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg">
    <v:documentproperties v:viewmarkup="false" v:metric="true" v:langid="2057"></v:documentproperties>
    <g v:groupcontext="foregroundPage" v:index="1" v:mid="0">
		<title>Page-1</title>
		<v:pageproperties v:shadowoffsety="-8.50393" v:shadowoffsetx="8.50393" v:drawingunits="24" v:pagescale="0.0393701" v:drawingscale="0.0393701"></v:pageproperties>
		<g v:groupcontext="group" v:mid="1" transform="translate(0.25,-0.25)" id="group1-1">
			<v:custprops>
				<v:cp v:val="VT4(d706f0fd-61f1-4a87-277a-928ddb1a76b6)" v:langid="1033" v:lbl="scomId" v:nameu="Row_3"></v:cp>
				<v:cp v:val="VT4(IIS 10 Computer Group)" v:langid="2057" v:type="0" v:lbl="scomName" v:nameu="Row_4"></v:cp>
				<v:cp v:langid="2057" v:type="0" v:lbl="scomPath" v:nameu="Row_5"></v:cp>
			</v:custprops>
		</g>
    </g>
</svg>

Solution

I was able to solve the issue directly in purify.js source as follows:
image
Obviously I am not sure this is the correct solution as I don't know the code well enough to be sure, but it solved the issue for us as it appears that the ADD_TAGS config is no longer being respected for SVG element input.

@kevinroast kevinroast changed the title Latest versions of DOMPurify 2.5.x block custom SVG elements even when they are set via ADD_TAGS config. Latest versions of DOMPurify 2.5.x block custom SVG elements when they are set via ADD_TAGS config. May 9, 2024
@cure53
Copy link
Owner

cure53 commented May 11, 2024

Alright, I had a closer look and found the issue. The problem is that we at some point started limited the permitted namespaces, and in your case, the XML contains one that is not allow-listed.

I believe you can fix the issue elegantly with a config option:

var html = `<!-- Generated by Microsoft Visio, SVG Export automation.svg Page-1 -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
		xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="5.65654in" height="0.711985in"
		viewBox="0 0 407.271 51.2629" xml:space="preserve" color-interpolation-filters="sRGB" class="st5">
	<v:documentProperties v:langID="2057" v:metric="true" v:viewMarkup="false"/>
	<g v:mID="0" v:index="1" v:groupContext="foregroundPage">
		<title>Page-1</title>
		<v:pageProperties v:drawingScale="0.0393701" v:pageScale="0.0393701" v:drawingUnits="24" v:shadowOffsetX="8.50393" v:shadowOffsetY="-8.50393"/>
		<g id="group1-1" transform="translate(0.25,-0.25)" v:mID="1" v:groupContext="group">
			<v:custProps>
				<v:cp v:nameU="Row_3" v:lbl="scomId" v:type="0" v:langID="1033" v:val="VT4(d706f0fd-61f1-4a87-277a-928ddb1a76b6)"/>
				<v:cp v:nameU="Row_4" v:lbl="scomName" v:type="0" v:langID="2057" v:val="VT4(IIS 10 Computer Group)"/>
				<v:cp v:nameU="Row_5" v:lbl="scomPath" v:type="0" v:langID="2057"/>
			</v:custProps>
		</g>
	</g>
</svg>`;

var config = { 
  ADD_TAGS: ['v:documentProperties', 'v:pageProperties'], 
  PARSER_MEDIA_TYPE: 'application/xhtml+xml', 
  ALLOWED_NAMESPACES: ['http://www.w3.org/2000/svg','http://schemas.microsoft.com/visio/2003/SVGExtensions/'] 
}; 
var output = DOMPurify.sanitize(html, config);

Fot this example, I allow-listed two elements and added their namespace, that seems to do the trick 🙂

@cure53
Copy link
Owner

cure53 commented May 11, 2024

Closing for now, as I assume this is solved

@cure53 cure53 closed this as completed May 11, 2024
@kevinroast
Copy link
Author

Alright, I had a closer look and found the issue. The problem is that we at some point started limited the permitted namespaces, and in your case, the XML contains one that is not allow-listed.

I believe you can fix the issue elegantly with a config option:

Fot this example, I allow-listed two elements and added their namespace, that seems to do the trick 🙂

Thanks for taking the time to investigate the issue. I will give this config solution a try and let you know!

@kevinroast
Copy link
Author

kevinroast commented May 13, 2024

After some testing I can say that it is close but not quite the same as the previous processing.

With a more involved SVG example, it is removing CDATA blocks inside embedded style elements and also removing the important viewBox attribute from the root SVG element.

Example Input:

<!-- Generated by Microsoft Visio, SVG Export automation.svg Page-1 -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
		xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="5.65654in" height="0.711985in"
		viewBox="0 0 407.271 51.2629" xml:space="preserve" color-interpolation-filters="sRGB" class="st5">
	<v:documentProperties v:langID="2057" v:metric="true" v:viewMarkup="false"/>
	<style type="text/css">
	<![CDATA[
		.st5 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
	]]>
	</style>
	<g v:mID="0" v:index="1" v:groupContext="foregroundPage">
		<title>Page-1</title>
		<v:pageProperties v:drawingScale="0.0393701" v:pageScale="0.0393701" v:drawingUnits="24" v:shadowOffsetX="8.50393" v:shadowOffsetY="-8.50393"/>
		<g id="group1-1" transform="translate(0.25,-0.25)" v:mID="1" v:groupContext="group">
			<v:custProps>
				<v:cp v:nameU="Row_3" v:lbl="scomId" v:type="0" v:langID="1033" v:val="VT4(d706f0fd-61f1-4a87-277a-928ddb1a76b6)"/>
				<v:cp v:nameU="Row_4" v:lbl="scomName" v:type="0" v:langID="2057" v:val="VT4(IIS 10 Computer Group)"/>
				<v:cp v:nameU="Row_5" v:lbl="scomPath" v:type="0" v:langID="2057"/>
			</v:custProps>
		</g>
	</g>
</svg>

The STYLE element CDATA block and the SVG element viewBox attribute are missing from the output after processing the above input with the suggested config.

Is there a suggested configuration that can maintain those values? Thanks!

@kevinroast
Copy link
Author

Do you think this can be solved by config, if not I can raise another issue showing where the processing is now different...?

@cure53
Copy link
Owner

cure53 commented May 15, 2024

Yup, this should do the trick:

var config = { 
  ADD_TAGS: ['v:documentProperties', 'v:pageProperties'], // from former example, needs to be updated
  PARSER_MEDIA_TYPE: 'application/xhtml+xml', 
  ADD_ATTR: ['viewBox'], // newly added
  ADD_TAGS: ['#cdata-section'], // newly added
  ALLOWED_NAMESPACES: ['http://www.w3.org/2000/svg','http://schemas.microsoft.com/visio/2003/SVGExtensions/'] 
}; 

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

No branches or pull requests

2 participants