Problem/Motivation
The template preprocess functions in form.inc for fieldsset and form element label do prepare the title variable by using '#markup', but the template preprocess funciton for details passes the title variable as is, which leads to different behavior of fieldsets, form elements and detaills, as an inline html inside a title string of a details element will be escaped and not rendered in the browser, but an inline html inside title of any other form element will not be escaped and rendered correctly in the browser.
For example, untranslatable fields are extended with a "all translations" hint to make sure the editor understands the affect of changing it:

Proposed resolution
Use #markup for titles of details elements.
Remaining tasks
None
User interface changes
None
API changes
None
Data model changes
None
| Comment | File | Size | Author |
|---|---|---|---|
| #67 | 2652850-67.patch | 6.46 KB | idebr |
| #67 | 2652850-67-test-only.patch | 4.91 KB | idebr |
| #67 | all-languages-element-after.png | 31.51 KB | idebr |
| #67 | all-languages-element-before.png | 35.44 KB | idebr |
| #66 | 2652850-66.patch | 6.46 KB | tstoeckler |
Comments
Comment #2
hchonovComment #3
mkalkbrennerI agree, but maybe the formatting should be similar to the two other preproccess functions:
Comment #4
hchonovNow it is consistent with the two other preprocess functions.
Comment #5
hchonovComment #6
star-szrI think to do this in 8.0.x we may need to add some BC code to Stable to maintain the previous behaviour. Doing this in 8.1.x makes it easier to justify this type of change. Consistency is good.
Comment #8
hchonovComment #10
merilainen commentedWorks for me.
Comment #11
mpdonadioMerging with work from #2819069: HTML is escaped in "all languages" in Datetime Field UI after SafeMarkup change; didn't address anything else yet. Please make sure @sylus gets credit on this issue.
Comment #12
lauriiiLets keep the if statement unchanged and use the if (!empty()). This is unnecessary change.
Comment #13
joelpittet@lauriii with that we can't use '0' in a title, which is rare but we shouldn't be excluding that.
Does need tests though.
Comment #14
lauriii@joelpittet TBH that doesn't seem to be in scope of this issue :)
Comment #15
joelpittetAh yeah, your probably right.
Also what's wrong with the details title being escaped? This issue needs a use-case in the issue summary.
Comment #16
mpdonadioUpdated IS w/ example from #2819069: HTML is escaped in "all languages" in Datetime Field UI after SafeMarkup change.
Comment #17
tstoecklerUpdating patch. I guess still needs some tests.
Comment #18
pguillard commented@tstoeckler, this patch is fine.
I came across the problem with the date field of the date range module only, and reproduced that on head of 8.2.x,
BEFORE :

AFTER:

I do not pass this one to RTBC because I didn't reproduced that on other fields yet.
Comment #20
jwilson3I can also confirm that this patch works both with the $node->created field in the node edit screen sidebar, as well as a multiple cardinality file field on a node.
Node Created Date
Before

After

File Field w/ Multiple Cardinality
Not updating status to RTBC because apparently still needs tests.
Comment #21
hchonovComment #22
anthony.bouch commentedI just came across this, and the patch works fine. Should this issue not be moved to 8.4 dev?
Comment #23
anthony.bouch commentedComment #24
mpdonadioThis will need test coverage with explicit XSS tests.
Comment #25
sanderjp commented#17 works for me
Comment #26
adelossantos commentedComment #27
lauriii@adelossantos it seems like your patch doesn't contain any new changes. In case you haven't made any changes for the latest patch, there is no need to re-upload the patch. If you tested the latest patch manually, you can write your review in the comment and if you have to make any remarks, you can use screenshots to help the communication.
Leaving this to "Needs Work" because we still need automated test coverage as pointed on #24.
Comment #28
adelossantos commented@lauriii noted sorry about that
Comment #30
mbovan commentedStumbled upon this problem and created #2901250: HTML of the (All languages) suffix is not escaped in summary. Closed it in favour of this issue.
Here is the test coverage for the use-case I faced based on the patch #17. The tests can probably be improved now as I wasn't aware of all the places where this problem can appear when I was writing tests.
Comment #32
j-leeCame across #2833031: Field label shows HTML if field is marked as not translatable which I closed as duplicated.
The test case looks good for me. Never seen this issue in other cases.
Patch works.
Comment #33
a.hover commentedThis patch works great for me, thanks @mbovan. My use case is a custom entity with an embedded entity browser (which appears in a details element).
Comment #34
sgurlt commentedWhich patch to use for 8.3.x ?
Comment #35
jonathan1055 commentedHi sgurit,
The patches in #17 (which was at 8.3.x) and #26 (when moved to 8.4.x) are identical, and the actual code change is still the same in #30 (after moving to 8.5.x) so I would guess that #17 or #26 should still apply OK at 8.3.x
Comment #36
mpdonadioThese are deprecated. Should probably just do one assertion with $this->assertSession()->assertNoEscaped() instead, or at least $this->assertSession()->responseNotContains() and $this->assertSession()->responseContains() instead.
I did some manual testing w/ some hacks, and think this is XSS safe, but we really need an explicit test here to catch any regressions here. Using the string
<script>alert(String.fromCharCode(88,83,83))</script>is handy since you don't have to worry about the quotation marks.NW for the XSS assertion.
Comment #37
ainarend commentedTo build further on the issue, not escaped title is also used in form validation. Using the patch from #30 and Drupal 8.3.6.
I created a custom node type and added two fields. One which is a regular text field and other that is a datetime field. After that i made the node type translatable, but kept the fields as not translatable. The patch made the title escape the all languages span, but when validating the form it still is not escaped.
Added a screenshot in the attachments. I tried to make the element title a formattable markup in core module content_translaton class ContentTranslationHandler in the method entityFormSharedElements after the translatability clue is added, but to no help.
Comment #38
handkerchiefWith drupal 8.4.0
the effect also occurs with some other labels.
Comment #39
espurnesThe problem persist in 8.4.2
But it is solved after applying the patch #30.
BEFORE

AFTER

Comment #40
tstoecklerThis fixes the things mentioned in #36. I added a generic test that outputs a hacky XSS title for all elements in core where I thought it made sense.
Comment #42
tstoecklerSo this breaks the Migrate Drupal UI form that was updated in #2905227: Migrate UI: Improve 'Review Upgrade' page UX. That now contains this:
(and a similar thing for available migrations, as well)
This is broken by this patch. Not sure what to do about that.
Comment #43
tstoecklerHere's a patch that uses a workaround and adds a @todo for #2930407: Allow specifying summary attributes for details elements. That would be a sensible way forward, in my opinion, but I'm neither a markup nor a migrate expert, so feel free to disagree.
Comment #45
tstoecklerComment #47
tstoecklerComment #50
mohit_aghera commentedThere are few more elements like title which are causing similar issues.
I found one similar in class that provides link element render implementation.
Attached patch for it.
Comment #52
mohit_aghera commentedFixing issue for use cases where $element['#title'] is string
For some cases like mentioned below, when we render this inside form, title is coming as string, instead of markup array or translatable markup object.
So creating array when there is direct string as $element['#title']
Comment #53
tstoeckler@mohit_aghera: I'm not exactly sure what use-case you are trying to fix with your patch in #50 / #52. But more importantly, are you sure this needs to be part of this issue, or could we split that into a separate issue instead?
Comment #55
berdirAgreed, that seems like a separate issue to me :)
What if only wrap into a render array if it is a string, like the last patch did with the link? Then the existing use case should continue to work for the migrate form?
Comment #56
tstoecklerHmm... interesting proposal in #55. That would certainly be preferable in terms of backwards-compatibility.
What I think speaks against that is the fact that we then have an inconsistent behavior between details elements and all other form elements, where the former supports a render array as title and the latter does not. So I could also see an argument for keeping it consistent with
template_preprocess_form_element_label().(Although strictly speaking we should change it to
if (isset($element['#title']) && $element['#title'] !== '') {to be consistent with that, then.)Not sure.
Comment #57
mohit_aghera commented@tstoeckler
I was trying to add a link inside form. I used following snippet and added via form alter.
Now when i enable translations, and i see the link in the form it is displayed like this (i.e. button title)
"Copy component <span class="translation-entity-all-languages">(' . t('all languages') . ')</span>"Above patch in #52 fixes that issue.
However as you and @Berdir mentioned, it will be better to split it up and create different issue for this.
Comment #58
anybody#52 also fixed the issue for me, but has some failing tests. Are these related or unrelated? Or do the tests have to be changed accordingly?
Comment #59
rodrigoaguileraAttached is a reroll of #43 against 8.6.x
Comment #60
rodrigoaguileraforgot the patch...
Comment #61
dravenkI tested path #60. It's fixed bug.It also fixes problems with the third-party module address.Thanks.
Comment #62
anybodyConfirming #61. Looking forward to have this in 8.6! :)
Comment #63
alexpottThis we should still set the #title to an empty string.
I think we should only convert to an array if it is not already an array.
This change should be reverted and is a sign the approach is not quite right.
I think we should add a note that it is XSS filtered here. Because otherwise this sounds scary.
Let's make the string
$type <script>alert('XSS')</script>is XSS filtered!because that makes the responseContains assertion better.
Let's hard code the result of the filter rather than relying on the same code in both the system-under-test and assertion.
Comment #64
tstoecklerPatch no longer applied because #2930407: Allow specifying summary attributes for details elements landed in the meantime. Luckily this allows us to remove the hunk from Migrate Drupal UI. I "rerolled" the patch simply by removing that from the patch file.
Also removing the "Quick fix" tag... ;-)
Also fixed #63:
$variables['title']even if$element['#title']is not set so that other preprocess functions can rely on it.'0'as a titleso I think should cover all bases. Added the same condition to
template_preprocess_datetime_wrapper(), as well.This makes the code a bit more verbose than previously but I couldn't come up with a nicer solution that satisfies all the above conditions. Feel free to suggest improvements!
Comment #66
tstoecklerOK, that wasn't so smart, as I wasn't setting the title variable at all for e.g. markup objects.
This one then tries to go for the minimally invasive fix. It reverts the fix to allow
'0'titles as that's a separate issue as I now realize that was discussed above already. Let's see...Comment #67
idebr commentedDid a manual test with a element to confirm the patch works as expected.
Before:
After:
Also added a 'tests-only' patch to confirm the state in HEAD is incorrect. Actual patch content is unchanged.
Comment #70
catchCommitted 252f7ff and pushed to 8.6.x. Thanks!
Comment #71
tstoecklerYay, thanks!
Since this is a bugfix, could we get this into 8.5, as well? Not a big deal if not, but wanted to ask...
Comment #72
damienmckennaThanks for fixing this!
I'll second the request for this to be backported to 8.5.x, it has been causing problems for sites using Metatag.
Comment #73
tstoecklerOK, let's do this, then.
Comment #75
tstoecklerComment #76
eloivaqueWorks for me on 8.5.5
Comment #77
miroslavbanov commentedSame as above - works on 8.5.5
Comment #78
alexpott8.5.x is now in critical bugfix only mode ¯\_(ツ)_/¯
Comment #79
tstoecklerOK, fair enough. Fixing version, though.