Repository: naturalcrit/homebrewery (js)

Issue:
IDs and Styles leak from internal elements when Mustache Style Injectors are used
### Your idea:

When using an inline or block style injector, attributes like style or ID from internal elements leak to their parents.

Reproduction:
```
{{test
# H1
}}
{}
```

Even though there is nothing to inject into the wrapping block, we receive the following HTML output:
```
<div class="block test" id="h1">
<h1 id="h1">h1</h1>
</div>
```

This is most noticeable when using the image masks; some users have been adding styling like so:
```
{{imageMaskEdge2,--offset:0%,--rotation:180
  ![](https://i.imgur.com/GZfjDWV.png){height:100%}
  {--test:true}
}}
```

which is resulting in:
```
<div class="block imageMaskEdge2" style="--offset:0%; --rotation:180;">
<p style="height:100%; --test:true;" src="https://i.imgur.com/GZfjDWV.png" alt="">
  <img style="height:100%;" src="https://i.imgur.com/GZfjDWV.png" alt="">
</p>
</div>
```

The `--test:true` is correctly applied to the `<p>`, but the `<height:100%>` has been incorrectly applied from the internal `<img>` styling.

Pull request:
Limit htmlString to the first element ONLY
This PR resolves #3559.

This PR limits the `htmlString` to be processed by the `extractHTMLStyleTags` function in `markdown.js` to only the content of the first element - that is, from the beginning of the string to the location of the first `>`. This prevents any attributes from elements deeper in the DOM tree from incorrectly being applied to the parent element.

Reproduction brew: https://homebrewery.naturalcrit.com/share/fodf7wAEOrbC

Patch:
diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js
index 52912983..39939f30 100644
--- a/shared/naturalcrit/markdown.js
+++ b/shared/naturalcrit/markdown.js
@@ -787,11 +787,13 @@ const processStyleTags = (string)=>{
 	};
 };
 
+//Given a string representing an HTML element, extract all of its properties (id, class, style, and other attributes)
 const extractHTMLStyleTags = (htmlString)=>{
-	const id         = htmlString.match(/id="([^"]*)"/)?.[1]    || null;
-	const classes    = htmlString.match(/class="([^"]*)"/)?.[1] || null;
-	const styles     = htmlString.match(/style="([^"]*)"/)?.[1] || null;
-	const attributes = htmlString.match(/[a-zA-Z]+="[^"]*"/g)
+	const firstElementOnly = htmlString.split('>')[0];
+	const id         = firstElementOnly.match(/id="([^"]*)"/)?.[1]    || null;
+	const classes    = firstElementOnly.match(/class="([^"]*)"/)?.[1] || null;
+	const styles     = firstElementOnly.match(/style="([^"]*)"/)?.[1] || null;
+	const attributes = firstElementOnly.match(/[a-zA-Z]+="[^"]*"/g)
 		?.filter((attr)=>!attr.startsWith('class="') && !attr.startsWith('style="') && !attr.startsWith('id="'))
 		.reduce((obj, attr)=>{
 			const index = attr.indexOf('=');


Test patch:
diff --git a/tests/markdown/mustache-syntax.test.js b/tests/markdown/mustache-syntax.test.js
index 7b0115ca..3f7f2529 100644
--- a/tests/markdown/mustache-syntax.test.js
+++ b/tests/markdown/mustache-syntax.test.js
@@ -333,6 +333,13 @@ describe('Injection: When an injection tag follows an element', ()=>{
 			expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p><span class="inline-block" style="color:red;">text</span>{background:blue}</p>');
 		});
 
+		it('Renders an parent and child element, each modified by an injector', function() {
+			const source = dedent`**bolded text**{color:red}
+														{color:blue}`;
+			const rendered = Markdown.render(source).trimReturns();
+			expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<p style="color:blue;"><strong style="color:red;">bolded text</strong></p>');
+		});
+
 		it('Renders an image with added attributes', function() {
 			const source = `![homebrew mug](https://i.imgur.com/hMna6G0.png) {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e}`;
 			const rendered = Markdown.render(source).trimReturns();