diff --git a/jte/src/main/java/gg/jte/compiler/TemplateParser.java b/jte/src/main/java/gg/jte/compiler/TemplateParser.java index 50449ecb..23dd0aca 100644 --- a/jte/src/main/java/gg/jte/compiler/TemplateParser.java +++ b/jte/src/main/java/gg/jte/compiler/TemplateParser.java @@ -754,11 +754,22 @@ private void interceptHtmlTags() { visitor.onError("Unclosed tag <" + currentHtmlTag.name + ">, expected " + ", got ."); } tagClosed = true; - } else if (!currentHtmlTag.attributesProcessed && !Character.isWhitespace(currentChar) && currentChar != '/' && currentHtmlTag.isCurrentAttributeComplete()) { + } else if (!currentHtmlTag.attributesProcessed && !Character.isWhitespace(currentChar) && currentChar != '/' && currentChar != '=' && currentHtmlTag.isCurrentAttributeComplete()) { HtmlAttribute attribute = parseHtmlAttribute(); if (attribute != null) { htmlPolicy.validateHtmlAttribute(currentHtmlTag, attribute); + if (attribute.name.startsWith("$unsafe{")) { + i += "$unsafe".length() - 1; + outputPrevented = false; + return; + } + + if (attribute.name.startsWith("${")) { + outputPrevented = false; + return; + } + currentHtmlTag.attributes.add(attribute); if (attribute.isSmartAttribute()) { diff --git a/jte/src/test/java/gg/jte/TemplateEngine_HtmlOutputEscapingTest.java b/jte/src/test/java/gg/jte/TemplateEngine_HtmlOutputEscapingTest.java index 3bb24fd1..f6073ebe 100644 --- a/jte/src/test/java/gg/jte/TemplateEngine_HtmlOutputEscapingTest.java +++ b/jte/src/test/java/gg/jte/TemplateEngine_HtmlOutputEscapingTest.java @@ -1704,6 +1704,24 @@ void attributes_dynamicNameForHotwire_unsafe_worksWithDefaultPolicyToo() { assertThat(output.toString()).isEqualTo("
\n
"); } + + @Test + void attributes_dynamicAttributesForHtmx() { + codeResolver.givenCode("template.jte", "@param String htmx\n
"); + + templateEngine.render("template.jte", TemplateUtils.toMap("htmx", "hx-get=/helloWorld hx-swap=outerHTML"), output); + + assertThat(output.toString()).isEqualTo("
"); + } + + @Test + void attributes_dynamicAttributesForHtmx_unsafe() { + codeResolver.givenCode("template.jte", "@param String htmx\n
"); + + templateEngine.render("template.jte", TemplateUtils.toMap("htmx", "hx-get=/helloWorld hx-swap=outerHTML"), output); + + assertThat(output.toString()).isEqualTo("
"); + } } @SuppressWarnings("unused")