Closed casid closed 1 year ago
Patch coverage: 100.00%
and project coverage change: +0.01%
:tada:
Comparison is base (
3e4c084
) 90.97% compared to head (66dbec0
) 90.98%.
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
Been a few days away from keyboard. Ran things this morning.
Seems to work pretty well. I'm usually wary about single boolean switched on contructors, but looking at the code I don't see anything obvious that looks better than what you've done there.
I was also exploring the ergonomics of what happens when we don't have params for it, to try and avoid having to include if/else conditional chunks in the templates. My understanding was that attributes without values would get no-oped by JTE, but I think I'm misunderstanding.
What should happen here? I haven't changed the assert from the base test, but wasn't expecting the attributes to render at all.
@Test
void attributes_dynamicNameForHotwireWithEmptyDefaultsNoParams() {
codeResolver.givenCode("template.kte", "@param controller:String = \"\"\n@param target:String=\"\"\n<div data-controller=\"${controller}\">" +
"\n<input data-${controller}-target=\"${target}\"/></div>");
templateEngine.render("template.kte", TemplateUtils.toMap(), output);
//assertThat(output.toString()).isEqualTo("<div data-controller=\"hello\">\n<input data-hello-target=\"name\"/></div>");
assertThat(output.toString()).isEqualTo("<div>\n<input/></div>");
}
This result
org.opentest4j.AssertionFailedError:
expected:
"<div>
<input/></div>"
but was:
"<div data-controller="">
<input data--target=""/></div>"
Rereading https://github.com/casid/jte/blob/main/DOCUMENTATION.md#smart-attributes
If I made the default values null
instead of ""
would it work? Will test
Edit: Almost works, but the value needs to be quoted and then renders as ""
@Test
void attributes_dynamicNameForHotwireWithEmptyDefaultsNoParams() {
codeResolver.givenCode("template.kte", "@param controller:String?=null\n@param target:String?=null\n<div data-controller=\"${controller}\">" +
"<input data-${controller}-target=\"${target}\"/></div>");
templateEngine.render("template.kte", TemplateUtils.toMap(), output);
//assertThat(output.toString()).isEqualTo("<div data-controller=\"hello\">\n<input data-hello-target=\"name\"/></div>");
assertThat(output.toString()).isEqualTo("<div><input/></div>");
}
org.opentest4j.AssertionFailedError:
expected: "<div><input/></div>"
but was: "<div><input data--target=""/></div>"
Expected :"<div><input/></div>"
Actual :"<div><input data--target=""/></div>"
I can't do this
<input data-${controller}-target=${target}/>
Because it triggers
gg.jte.TemplateException: Failed to compile template.kte, error at line 3: Unquoted HTML attribute values are not allowed: data-${controller}-target
I know we could do if/else
wrapping and render 2 different div blocks, but still thinking that gets a little awkward if the templates get busy
@wyaeld nope, smart attributes are not working atm when attributes names are dynamic.
This is because smart attributes currently check at compile time if the attribute is a boolean or regular html attribute. In case of boolean it generates different code. That's not possible when the attribute name is not present at compile time.
What's your usecase exactly? Is the controller variable expected to be null, or the target variable?
Just exploring different ways to organise our templates, and whether we need to have hotwire and non-hotwire variants of common controls, like inputs, selects etc.
What you've done with this PR gives us good building blocks though, its a big improvement.
Okay, I'll then merge this branch so that it is available in the next bugfix release.
We can still try to add more features later, if needed.
For hotwire development it is required to have dynamic attribute names in certain cases. See issue #254
This is prevented by
OwaspHtmlPolicy
, or more specifically the policyPreventOutputInTagsAndAttributes
. This is because attribute names are not a safe slot to put untrusted user input into. There are various ways to escape the attribute name.This PR fixes a bug in jte, so that correct output is generated, if this policy is disabled by the user and dynamic output is used in an attribute name.
There are currently two ways to achieve this:
PreventOutputInTagsAndAttributes
policy$unsafe
for the dynamic output in the attribute name@wyaeld during implementation I noticed that my initial recommendation to disable the
PreventOutputInTagsAndAttributes
policy entirely does remove a few more policies that are quite useful and I think that most users will want to have, such as preventing dynamic tag names and preventing control structures and content blocks in attribute names.That's why I've parameterized the
PreventOutputInTagsAndAttributes
, to make it possible to only disable the check that you need, like this: https://github.com/casid/jte/blob/66dbec00f9be247e06a200e531b2a3ecdd2a0c08/jte-kotlin/src/test/java/gg/jte/kotlin/TemplateEngine_HtmlOutputEscapingTest.java#L1419As an alternative, you could also use
$unsafe
for these parts and stick with the defaultOwaspHtmlPolicy
, like here: https://github.com/casid/jte/blob/66dbec00f9be247e06a200e531b2a3ecdd2a0c08/jte-kotlin/src/test/java/gg/jte/kotlin/TemplateEngine_HtmlOutputEscapingTest.java#L1448