Skip to content

Commit 80c9335

Browse files
Merge pull request #83 from Kentico/copilot/fix-html-comments-injection
Fix HTML comments incorrectly receiving data-snippet-id attribute injection
2 parents 4dd8435 + 8837fec commit 80c9335

File tree

2 files changed

+186
-1
lines changed

2 files changed

+186
-1
lines changed

src/Kentico.Xperience.TagManager/Rendering/DefaultChannelCodeSnippetsService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,5 +146,5 @@ private static CodeSnippetDto AdjustCustomCodeSnippet(CodeSnippetDto codeSnippet
146146
};
147147

148148
private static string AddSnippetIds(int codeSnippetId, string codeSnippet) =>
149-
Regex.Replace(codeSnippet, "<([^\\/]*?)>", $"""<$1 data-snippet-id="{codeSnippetId}">""");
149+
Regex.Replace(codeSnippet, "<(?!!--|\\/|!)([^>]*?)(/?)>", $"""<$1 data-snippet-id="{codeSnippetId}"$2>""");
150150
}
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
using System.Reflection;
2+
using System.Text.RegularExpressions;
3+
4+
using NUnit.Framework;
5+
6+
namespace Kentico.Xperience.TagManager.Tests;
7+
8+
[TestFixture]
9+
public class AddSnippetIdsTests
10+
{
11+
// Using reflection to test the private static method
12+
private static string InvokeAddSnippetIds(int snippetId, string codeSnippet)
13+
{
14+
var assembly = Assembly.Load("Kentico.Xperience.TagManager");
15+
var type = assembly.GetType("Kentico.Xperience.TagManager.Rendering.DefaultChannelCodeSnippetsService");
16+
var method = type!.GetMethod("AddSnippetIds", BindingFlags.NonPublic | BindingFlags.Static);
17+
return (string)method!.Invoke(null, [snippetId, codeSnippet])!;
18+
}
19+
20+
[Test]
21+
public void AddSnippetIds_ShouldNotModifyHtmlComments()
22+
{
23+
// Arrange
24+
int snippetId = 6;
25+
string input = "<!-- Google Tag Manager -->\n<!-- End Google Tag Manager -->";
26+
string expected = "<!-- Google Tag Manager -->\n<!-- End Google Tag Manager -->";
27+
28+
// Act
29+
string result = InvokeAddSnippetIds(snippetId, input);
30+
31+
// Assert
32+
Assert.That(result, Is.EqualTo(expected));
33+
}
34+
35+
[Test]
36+
public void AddSnippetIds_ShouldAddAttributeToRegularTags()
37+
{
38+
// Arrange
39+
int snippetId = 6;
40+
string input = "<script>alert('test');</script>";
41+
string expected = "<script data-snippet-id=\"6\">alert('test');</script>";
42+
43+
// Act
44+
string result = InvokeAddSnippetIds(snippetId, input);
45+
46+
// Assert
47+
Assert.That(result, Is.EqualTo(expected));
48+
}
49+
50+
[Test]
51+
public void AddSnippetIds_ShouldAddAttributeToDiv()
52+
{
53+
// Arrange
54+
int snippetId = 6;
55+
string input = "<div class=\"container\"></div>";
56+
string expected = "<div class=\"container\" data-snippet-id=\"6\"></div>";
57+
58+
// Act
59+
string result = InvokeAddSnippetIds(snippetId, input);
60+
61+
// Assert
62+
Assert.That(result, Is.EqualTo(expected));
63+
}
64+
65+
[Test]
66+
public void AddSnippetIds_ShouldHandleMixedContent()
67+
{
68+
// Arrange
69+
int snippetId = 6;
70+
string input = @"<!-- Google Tag Manager -->
71+
<script>
72+
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
73+
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
74+
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
75+
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
76+
})(window,document,'script','dataLayer','GTM-XXXX');
77+
</script>
78+
<!-- End Google Tag Manager -->";
79+
80+
// Act
81+
string result = InvokeAddSnippetIds(snippetId, input);
82+
83+
// Assert
84+
Assert.That(result, Does.Contain("<!-- Google Tag Manager -->"));
85+
Assert.That(result, Does.Contain("<!-- End Google Tag Manager -->"));
86+
Assert.That(result, Does.Not.Contain("<!-- Google Tag Manager -- data-snippet-id"));
87+
Assert.That(result, Does.Not.Contain("<!-- End Google Tag Manager -- data-snippet-id"));
88+
Assert.That(result, Does.Contain("<script data-snippet-id=\"6\">"));
89+
}
90+
91+
[Test]
92+
public void AddSnippetIds_ShouldNotModifyClosingTags()
93+
{
94+
// Arrange
95+
int snippetId = 6;
96+
string input = "<div></div>";
97+
string expected = "<div data-snippet-id=\"6\"></div>";
98+
99+
// Act
100+
string result = InvokeAddSnippetIds(snippetId, input);
101+
102+
// Assert
103+
Assert.That(result, Is.EqualTo(expected));
104+
}
105+
106+
[Test]
107+
public void AddSnippetIds_ShouldHandleSelfClosingTags()
108+
{
109+
// Arrange
110+
int snippetId = 6;
111+
string input = "<img src=\"test.jpg\" />";
112+
string expected = "<img src=\"test.jpg\" data-snippet-id=\"6\"/>";
113+
114+
// Act
115+
string result = InvokeAddSnippetIds(snippetId, input);
116+
117+
// Assert
118+
Assert.That(result, Is.EqualTo(expected));
119+
}
120+
121+
[Test]
122+
public void AddSnippetIds_ShouldHandleMultilineComments()
123+
{
124+
// Arrange
125+
int snippetId = 6;
126+
string input = @"<!-- This is a
127+
multiline comment
128+
that should not be modified -->";
129+
string expected = @"<!-- This is a
130+
multiline comment
131+
that should not be modified -->";
132+
133+
// Act
134+
string result = InvokeAddSnippetIds(snippetId, input);
135+
136+
// Assert
137+
Assert.That(result, Is.EqualTo(expected));
138+
}
139+
140+
[Test]
141+
public void AddSnippetIds_ShouldHandleNestedComments()
142+
{
143+
// Arrange
144+
int snippetId = 6;
145+
string input = "<!-- Comment 1 --><div><!-- Comment 2 --></div><!-- Comment 3 -->";
146+
string expected = "<!-- Comment 1 --><div data-snippet-id=\"6\"><!-- Comment 2 --></div><!-- Comment 3 -->";
147+
148+
// Act
149+
string result = InvokeAddSnippetIds(snippetId, input);
150+
151+
// Assert
152+
Assert.That(result, Is.EqualTo(expected));
153+
}
154+
155+
[Test]
156+
public void AddSnippetIds_ShouldHandleDoctype()
157+
{
158+
// Arrange
159+
int snippetId = 6;
160+
string input = "<!DOCTYPE html>";
161+
string expected = "<!DOCTYPE html>";
162+
163+
// Act
164+
string result = InvokeAddSnippetIds(snippetId, input);
165+
166+
// Assert
167+
Assert.That(result, Is.EqualTo(expected));
168+
}
169+
170+
[Test]
171+
public void AddSnippetIds_ShouldHandleConditionalComments()
172+
{
173+
// Arrange
174+
int snippetId = 6;
175+
string input = "<!--[if IE]><script src=\"ie.js\"></script><![endif]-->";
176+
// The comment wrapper is preserved but the script tag inside gets the attribute
177+
string expected = "<!--[if IE]><script src=\"ie.js\" data-snippet-id=\"6\"></script><![endif]-->";
178+
179+
// Act
180+
string result = InvokeAddSnippetIds(snippetId, input);
181+
182+
// Assert
183+
Assert.That(result, Is.EqualTo(expected));
184+
}
185+
}

0 commit comments

Comments
 (0)