Follow-up to #2297711: Fix HTML escaping due to Twig autoescape
Problem/Motivation
The DX for fixing HTML escaped values from the render API is currently a pain.
We don't want to open up security holes but we don't want to also make it a pain to put #descriptions on fields with HTML tags in them for example. So the mid point between those two problems seems to be Xss::filterAdmin() on certain render array keys.
Example issue for reference: @see #2309929: HTML double-escaping in field forms
Proposed resolution
@larowlan and @chx came up with a great idea to deal with the DX and safe markup work necessary for a good chunk of what's left HTML escaped through keys in the Render API.
Proposing the following keys be run through Xss::filterAdmin() in the render API:
- #description
- #field_prefix
- #field_suffix
- #prefix
- #suffix
Remaining tasks
Decide which keys should be XSS filtered.
User interface changes
n/a
API changes
Certain keys will be automatically run through Xss::filterAdmin().
Comment | File | Size | Author |
---|---|---|---|
#155 | interdiff-153-155.txt | 2.33 KB | joelpittet |
#155 | fix_common_html_escaped-2324371-155.patch | 16.95 KB | joelpittet |
#155 | interdiff-153.txt | 3.09 KB | joelpittet |
#153 | 2324371-153.patch | 15.66 KB | joelpittet |
#150 | 2324371_150.patch | 15.42 KB | webflo |
Comments
Comment #1
chx CreditAttribution: chx commentedI think this is the solution to #2273925: Ensure #markup is XSS escaped in Renderer::doRender() as well: add a #safe_markup which can be used to add anything and filter_xss #markup as well.
Comment #2
larowlan+1 for the five keys in the OP
Comment #3
joelpittet@chx not sure exactly where to put that for #markup but here's a quick patch to show it works. Grabbed the tests from #2309929-27: HTML double-escaping in field forms And applied to the keys.
Comment #7
aneek CreditAttribution: aneek commented+ 1 for this issue fixing. :)
Comment #8
aneek CreditAttribution: aneek commented@joelpittet,
After applying your patch one thing caught my eye. There is some dots in the manage field pages. See the attached screenshot.
I had a little discussion with @chx in IRC and he as of that discussion,
'#description', '#field_prefix', '#field_suffix', '#prefix', '#suffix'
elements should expect string always. But seeing at the error inDrupal\locale\Tests\LocaleExportTest
file asstrlen() expects parameter 1 to be string, array given
, there may be any places present where these attributes have array values instead string.If I am able to backtrace the error then I'll let you know.
Comment #9
joelpittetI've my doubts now whether this is viable.
Those bullets are there, but get's filtered out of the allowed tags and that's why they show up.
Seven's local task override has this:
Which is ridiculous I know, and maybe because we haven't converted those to twig yet that this may be an outlier... BUT I can see people wanting to put a button tag, and maybe other filtered out tags in Xss::AdminFilter() into a prefix/description/suffix etc. Also if those keys are on other hook_theme() implementations they will be filtered automatically and be potentially a bug WTF(especially #description).
Hmm, I'm at a bit of a loss here, help! I kind of just want to SafeMarkup::set() all the #description values that are passing through markup... though not sure if that is viable either.
Comment #10
larowlanAlthough we could whitelist button for these uses cases, https://html5sec.org/ says that button w/ form attribute and one of onformchange or formaction attributes can be used to sniff/snoop on form-submissions via XSS. So I think we shouldn't do that.
So I think the option is fix this mess and use a for-real theme hook, this concatenation feels puke.
And a button in #description seems wrong - for https://html5sec.org/ reasons alone.
Comment #11
joelpittet@larowlan thanks, yeah now that I think about it, it's strange to but a button in description. More thinking the other ones, like putting a button in #field_suffix may be a totally reasonable thing?
Here's where we are actually cleaning up that mess for theme_menu_blah hooks #1898478: menu.inc - Convert theme_ functions to Twig
Comment #12
chx CreditAttribution: chx commentedOK, OK but what are those arrays?
Comment #13
herom CreditAttribution: herom commentedThe arrays are from
modules/locale/src/Form/ImportForm.php::BuildForm()
(the#description
key).I found this using
grep
. I think this is the only array, sinceTwigTransTest
is also loading the locale import form.Comment #14
larowlanSo vote 1 for ignoring arrays, i.e. wrap the code in a check that it's scalar first.
Comment #15
chx CreditAttribution: chx commentedThat is absolutely horrible. But yes, I vote so too.
Comment #16
aneek CreditAttribution: aneek commented@joelpittet & @chx,
By checking with
is_scalar
andis_null
(since is_scalar don't check for null values) won't solve the purpose for those markups which uses #theme to style their output. Since we can't use theme_*() any more #2173655: Refactor theme() to _theme(); make it a private API to discourage module developers from circumventing the renderable build system we need to find a proper way to make the theme implementation to supply string mainly for markup elements.So do we have to call
drupal_render()
again?Regarding cross site scripting, is it possible to check the values inside any attributes of a HTML tag? Like,
If any malicious attribute value is found then strip it off? So the above becomes,
<form id="test"></form><button form="test" formaction="">X</button>
OR
<form id="test"></form><button form="test">X</button>
It's a big list of my queries. :-)
Please let me know your thoughts.
Thanks!
Comment #17
chx CreditAttribution: chx commentedIf you use a render array then that's secure. Solving #markup is perhaps not in scope for this, so I just vote in a !is_array check for the sake of simplicity.
> If any malicious attribute value is found then strip it off
Yes, Xss::filterAdmin removes any attribute with malicious values.
Comment #18
aneek CreditAttribution: aneek commented@chx, okay so I'll change the code in
modules/locale/src/Form/ImportForm.php::BuildForm()
and check if any test cases fail. Also, in the patch file I'll change and allow "button" tag in Xss::filterAdmin().Just to be sure I will also check with the string
<form id="test"></form><button form="test" formaction="javascript:alert(1)">X</button>
for any possible XSS attack.Will let you know about my observations.
Thanks!
Comment #19
aneek CreditAttribution: aneek commented@chx & @joelpittet,
Here is another patch with checks on array values and also changed the #theme declaration in
modules/locale/src/Form/ImportForm.php::BuildForm()
. This one is not finished though and I've checked with some tests and it failed. Seems that we have a long way to go. Making it for review so you all can have a look where it failed.Please review and provide comments.
Comment #20
aneek CreditAttribution: aneek commentedComment #22
joelpittetThis would nullify arrays that are passed in. Is this what we want to do? Likely, but just checking...
Anyways that fixes the exceptions:) Thanks @aneek!
I will dig deeper later in the week when I get some time, though anybody can feel free to jump in!
Comment #23
aneek CreditAttribution: aneek commentedThanks @joelpittet.
Settings arrays to null is what I thought at that moment. May be someone can come up with a better idea. Also, I thought of one thing. Instead of using
$elements[$key] = Xss::filterAdmin($elements[$key]);
we could use$elements[$key] = Xss::filter($string, $adminExtendedTags);
. Where,$adminExtendedTags
will consist of the defaultXss::$adminTags
variable and added with some more HTML tags likebutton
.Then I will check for XSS as per the site https://html5sec.org/.
I'll check these tomorrow. In the mean time if you have any suggestions please let me know.
Happy to work on this issue. :-)
Comment #24
aneek CreditAttribution: aneek commentedGuys,
I think #2273277: Figure out a solution for the problematic interaction between the render system and the theme system when using #pre_render fixes the issues with the keys mentioned in the issue. So do we need this issue anymore? and also #2309929: HTML double-escaping in field forms is fixed I checked with the last committed code.
Comment #25
chx CreditAttribution: chx commentedDo we have tests for these to make sure? Edit: just a few new asserts I hope.
Comment #26
andypost@Wim Leers any idea why
#markup
and#description
by-passes autoescape now? is that secure?Comment #27
andypostIt does not works anymore after reinstall
Comment #28
joelpittetHere is a different approach... not sure if it's going to fly but the #description and field_prefix/suffix are FormElement keys so I delegated the responsibility of filtering them to that class. Unfortunately I can't think of a good way to add pre_render without NestedArray::mergeDeep, so I manually added it to where it needed to be or unioned it off of FormElement.
Comment #29
joelpittetwrong interdiff.
Comment #31
chx CreditAttribution: chx commentedThat's fine, I guess, although #prefix and #suffix still need to be escaped in drupal_render.
Comment #32
joelpittetOk I went back to the global fix, not totally happy with that, but my idea of using the FormElement preRender is rife with merging issues or lots of duplicate code (error prone).
I did change up the logic a bit so that if it's scalar, it will do this, and if not it will leave it alone. If you have a renderable array or an object with __toString(), you have likely dealt with the issue already. May need to make that isSafe() or something...
Bloating up drupal_render feels wrong.
Comment #33
joelpittetMy guess is more fails this time. But also I moved the code up before preRender so the JS errors may be fixed.
Comment #35
joelpittetYup, JavaScriptTest is fixed. Though some of the tests are looking for prefix
<foo>
which isn't safe.If I'm generating XML, this may not be ideal. Hmmm.
Comment #36
joelpittetFTR, I don't think #prefix/#suffix and their ilk should exist in the first place. The promote bad habits of putting markup in PHP.
I'm wondering if really this blanket approach is hurting us in the long run?
Comment #37
larowlanComment #38
rteijeiro CreditAttribution: rteijeiro commentedLet's drunkroll!
Comment #39
rteijeiro CreditAttribution: rteijeiro commentedGo bot, go!!
Comment #41
lauriiiComment #42
lauriiiComment #43
alqahtani CreditAttribution: alqahtani commentedI tested the patch provided by lauriii on comment #42 and it worked fine for me
Comment #44
lauriiiComment #45
marcus7777 CreditAttribution: marcus7777 commentedreviewing
Comment #46
chx CreditAttribution: chx commented@laurii thanks so much for fixing this! I wonder though, is the field ui and rdf fixes belong to this issue ...?
Comment #47
lauriii@chx: I guess we have to fix them to make tests pass
Comment #48
killerpoke CreditAttribution: killerpoke commentedI'm not surre if this belongs to this issue, but on the install screen there is one message double escaped. I don't know, where this issue is coming from, since only one message is double escaped not all of them.
Comment #49
lauriii#2317281: Double escaping of install errors
Comment #50
chx CreditAttribution: chx commented@killerpoke that belongs to another issue as @laurii points out. Please remove the embedding of such a huge image it makes an already long issue too long. Thanks.
Comment #51
Fabianx CreditAttribution: Fabianx commentedAs much as I love the idea and the plan laid out in the issue summary, unfortunately the patch has some issues:
a) #prefix, #suffix can still be changed in #pre_render, so the check should be done before its used (e.g. directly above the concating of that strings.
b) #description and possibly (?) #field_prefix, _suffix are Form API properties, so should also be run through filterXssAdmin shortly before usage.
Thanks for the hard work @all!
This is awesome!
Comment #52
lauriiiComment #53
Fabianx CreditAttribution: Fabianx commentedOkay, so here is the point:
#description is used somewhere, where it is automatically escaped.
#description is a FAPI element, which has nothing to do with drupal_render, so we need to fix it:
a) near where #description is actually output, to ensure no code can change it afterwards again and break it again
b) somewhere within Form API - as that is where the property is defined and documented
Is that more clear now?
Thanks for working on this very important issue!
Comment #54
lauriiiNow we set markup as safe when we've XSS filtered them. Had to fix some elements in render tests since
Xss::filterAdmin
removes all unknown elements such as<foo>
. I added filter todoBuildForm()
which should be pretty close to printing.Comment #55
Fabianx CreditAttribution: Fabianx commentedRemove those three here and move down to below (commented there, where #prefix / #suffix is used.
Using SafeMarkup::set is wrong here. Xss::filterAdmin already returns safe markup ...
Here is where we should add the code, but I think I would prefer UX like:
// Performance check - critical path
if (!empty($elements[$key])) {
$elements[$key] = SafeMarkup::checkAdminXss($elements[$key]);
}
That would be way better UX in my opinion and also then do the remaining if checks etc.
Remove those two and use the nicer UX function on SafeMarkup class as its a common function / we might people to use with their own admin FAPI properties.
This change looks good to me.
Comment #57
lauriiiFixed most of the points I guess. Tests are still most likely failing, just making sure that more stuff isnt breaking.
Comment #59
lauriiiComment #60
chx CreditAttribution: chx commentedThanks for working on this but look
You are running checkAdminXss after #prefix / #suffix is already used. Which also means that #prefix / #suffix is not tested :)
You could do:
and be done with it.
Comment #61
lauriiiDont know what I was thinking..
Comment #63
Fabianx CreditAttribution: Fabianx commentedThis declaration is no longer used.
This however needs to use:
use XSS?
This XSS can be removed now.
Comment #64
joelpittetThanks everyone and especially @lauriii for helping out, this is going in a much better direction now with all your help.
Glad the form stuff has been moved to it's domain and not poluting drupal_render().
There are a lot of links in the #description key especially but as long as they translate them or mark them as safe a head of time this should be ok.
The DX issue that would be most confusing is if a contrib module updates a description by appending more info, they'd have to re set the concatenated string in total as safe and not just their own addition unfortunately.
Comment #65
Fabianx CreditAttribution: Fabianx commentedIf that is the case, we are still doing it wrong. We should then probably do it in preprocess. The latest possible point before it's rendered that is always executed.
Comment #66
lauriiiComment #68
aneek CreditAttribution: aneek commentedThe test is failing continuously due to a JavaScript test in
/core/modules/system/src/Tests/Common/JavaScriptTest.php
. This file contains a test casetestBrowserConditionalComments
where Conditional IE based script is tested. With the patch no applied the test generates,But with the patch applied the test generates,
So it's clear that in
common.inc
SafeMarkup::checkAdminXss($elements['#prefix'])
andSafeMarkup::checkAdminXss($elements['#suffix'])
are stripping the tags. But this feature of conditional JS or CSS add is a feature that is necessary.We can mitigate this by checking why
<!--
&<!
is not getting passed viaXss::filter()
method.Any thoughts?
Comment #69
Fabianx CreditAttribution: Fabianx commentedNope, this is correct.
Whatever is setting this script tag, need to run it via SafeMarkup::set(), because this is a case of:
Yes, I want to output script tags and _yes_ I know what I am doing.
The autoescape is exactly for cases like this to prevent them - so it needs to be specifically marked.
Comment #70
Fabianx CreditAttribution: Fabianx commentedAh, I re-read the comment and it seems we even allow certain script tags, but we don't allow comments ...
Interesting ...
I overall think its an edge case, where calling SafeMarkup::set is actually okay, but I will need to take a look at the test itself.
So there is a work-around using #inline_template and even using JS escaping strategy if needed.
I think that test would be a good precedent to show how to use #inline_template.
Comment #71
aneek CreditAttribution: aneek commented@Fabianx,
So you are saying that we need to change the test case?
As per the code,
$attached['#attached']['js']
is attaching theJavaScript and then therender()
function callsdrupal_render()
where the tags are getting stripped off due to the application ofSafeMarkup::checkAdminXss()
I do agree with you @Fabianx as to use inline_template.
Also, what do you think if we move this bit of code,
in
$element['#after_build']
. Because indoBuildForm()
method if we apply this to each element then there is (I think) no chance any one can alter it afterwards. So the logic is this #after_build callback will be defined after all modules defines there #after_build functions.Comment #72
Fabianx CreditAttribution: Fabianx commentedI created an interdiff that should fix the problem, it is attached.
Comment #73
aneek CreditAttribution: aneek commented@Fabianx, thanks. Trying once again.
Comment #74
Fabianx CreditAttribution: Fabianx commented#71 We don't need to fix the test, but the #pre_render.
We can do two things:
- Keep the suffix / prefix approach and use SafeMarkup::set()
OR
Do something like:
Regarding the location where the form is changed:
Yes, this should be in #after_build or even afterwards.
Latest possible point before those properties are output.
Comment #75
aneek CreditAttribution: aneek commented@Fabianx, I applied your interdiff and "JAVASCRIPTTEST" ran fine. Just changing the location of the
SafeMarkup::checkAdminXss
to #after_build.Comment #76
aneek CreditAttribution: aneek commentedUploading one patch. Needs manual and automated review both.
Comment #77
aneek CreditAttribution: aneek commentedComment #79
aneek CreditAttribution: aneek commentedI'll check this one tonight. Again failed. :-(
Comment #80
Fabianx CreditAttribution: Fabianx commentedMaybe lets try that in template_preprocess_form_element() or whatever is called for all FAPI elements again?
If its not possible in #after_build might need to do it in doBuild() - which should be fine, too.
Comment #81
aneek CreditAttribution: aneek commented@Fabianx,
Yes I think this time it "may" pass. Yup its possible in #after_build. Just the function needed to be a static method rather than a private or protected one. Uploading a patch with your interdiff added.
Needs automated and manual review both.
Comment #82
aneek CreditAttribution: aneek commentedComment #83
Fabianx CreditAttribution: Fabianx commentedYou will need to change that, too:
FormBuilder::formSafeCheck
Comment #84
aneek CreditAttribution: aneek commented@Fabianx, yes you are right. I keep forgetting it's OOP now. Anyway, I'll add
self::formSafeCheck
Comment #85
aneek CreditAttribution: aneek commentedCreated a new one with @Fabianx's inputs. Guys please review it.
Thanks!
Comment #86
chx CreditAttribution: chx commentedThanks much for moving this forward rapidly!
Very interesting approach. At first I thought I don't like it because of #after_build but since you want to fire some code just after #after_build it actually does make sense :) However,
Thinking aloud about this code, it seems to me that -- whether realistic or not but I suspect yes -- there's a chance of recursion/infinite loop here based on the presence of #after_build_done. After this code the
isset($element['#after_build'])
is always true. All put together I suggest:this change. Also,
array(get_class($this), 'self::formSafeCheck');
I am surprised this even works. o_O Behold this: http://3v4l.org/CfFch vs http://3v4l.org/k6KTB Very nice PHPWTF, thanks. http://www.phpwtf.org/variable-function-calls written here.The proper syntax is
array($this, 'formSafeCheck');
simply, please use it.Comment #87
aneek CreditAttribution: aneek commented@chx,
Thanks for your feedback. One thing though as per comment http://php.net/manual/en/function.call-user-func-array.php#93744 in PHP (5.3 >=) we can use
call_user_func_array('parent::func', $args);
What are your thoughts on this?
Comment #88
chx CreditAttribution: chx commentedLook http://php.net/manual/en/language.types.callable.php here
vs
but we have a very fine instantiated object:
$this
andformSafeCheck
is not static.Comment #89
Fabianx CreditAttribution: Fabianx commentedShould we just call this statically after invoking all other after builds?
Hm, looking at that again, I think the best place is to do invoke the method right before returning the $element at the end of the function.
Because this is usually before its rendered.
Comment #90
aneek CreditAttribution: aneek commented@Fabianx, just before the return we can call this section. But what do you think to introduce a new attribute kind of "#secure_build" for each field element and also for the form itself. Idea is the same as "#after_build". So other modules can assign their own callback methods to implement security based on their different needs? - Just a thought.
But it this is not desired then instead of calling in "#after_build" we can also have the code block as you said just before the return.
Comment #91
Fabianx CreditAttribution: Fabianx commentedI don't really think we need to solve the #secure_build in this issue, after all there is methods how to mark the #description keys, etc. secure.
This is mainly for the most used properties, a convenience thing.
Comment #92
aneek CreditAttribution: aneek commented@Fabianx, I do agree. As I said before it's just a thought.
I do like @chx's idea to place the #after_build in a condition. But then again the idea is, there should be at least one #after_build and it's callback is self::formSafeCheck.
So even if we add the isset check, for each element the code will still go to the logic where
is written. So if there is any recursive infinite loop how does this condition based logic prevents it?
I'm a bit confused here. :-(
Then @Fabianx, with your thought, placing the check in just before the
return $element;
section will solve the problem surely but don't we have any other way(s)?Comment #93
Fabianx CreditAttribution: Fabianx commentedThere is no point in adding an #after_build that always runs, or is there?
And no, putting it before the return is the right way, lets not overcomplicate this, please.
And we can run the filterXSS again and again and probably should do so.
This is for cases where we rebuild the form.
Comment #94
aneek CreditAttribution: aneek commented@Fabianx, So lets put this bit of code just before the return statement. So do you think its a good idea to keep
this bit of code in a method or just as is? Also if we make this as method what if to define a protected variable to store $markup_keys instead of local variables and use it as
$this->markup_keys
?Comment #95
Fabianx CreditAttribution: Fabianx commentedIt is definitely cleaner to keep this is in a protected method.
I don't think we need to move the keys to a variable for now as its unlikely someone will override this class.
Comment #96
aneek CreditAttribution: aneek commented@Fabianx, yup very less chance of overriding the class methods or variables. So lets keep it simple and will use the method that I created "formSafeCheck". I'll post a patch tomorrow for review.
Thanks!
Comment #97
aneek CreditAttribution: aneek commentedNew patch to review, based on @Fabianx suggestions.
Comment #98
aneek CreditAttribution: aneek commentedComment #99
hass CreditAttribution: hass commentedI can confirm that #97 patch fixes the issues I have found in Google Analytics module. As I'm not deep enough in this patch I stay away from setting it to RTBC.
Comment #100
hass CreditAttribution: hass commentedJust a thought, shouldn't
#title
not be part of the list?Comment #101
joelpittetThis is the only line I could find myself that seemed a bit on the iffy side of things.
I'm guessing that this is done so that the prefix doesn't get checkAdminXss filtered?
Kind of depends on what is inside there I guess...
@hass re #100 have you seen any issues arise that #title was commonly getting escaped tags in it? Usually I'd expect, for UI consistency, that field doesn't get tag much. Also, if there are any
<em>
tags they are likely coming from t(). I could be wrong though...Otherwise nice work and thank you for helping make this much better than what I'd started, I was very skeptical but if someone can let me know on the
SafeMarkup::set()
what is up there. I'd be fine with RTBCing this puppy.Comment #102
chx CreditAttribution: chx commented@hass #2348851: Regression: Allow HTML tags inside detail summary has a use case for title but it's so rare vs the title being so commonly used it's better not to slow this whole thing down even further. Do you know of any other cases?
Comment #103
hass CreditAttribution: hass commentedWhat I tried to use from time to time SUP and SUB in title. I also have an open bug in views for table header about this.
Comment #104
Fabianx CreditAttribution: Fabianx commentedI agree with #101, we definitely need to call SafeMarkup::checkAdminXss() on the prefix and suffix in rdf module before calling SafeMarkup::set() - similar to how we solved it in the #pre_render case for the scripts.
Comment #105
aneek CreditAttribution: aneek commented@joelpittet & @Fabianx, I haven't checked that part very minutely before. But I think I've to check that part too. I'll have an update on this soon.
Thanks for the feedbacks :-)
Comment #106
hass CreditAttribution: hass commentedJust a quick note. I tried to use
<sup>1</sup>
in a #title and this works well. There is nothing to do here about #title.Comment #107
aneek CreditAttribution: aneek commentedAddressed comments by @joelpittet & @Fabianx.
Thanks!
Comment #108
aneek CreditAttribution: aneek commentedComment #109
Fabianx CreditAttribution: Fabianx commentedI hate to play pedantic code style, but this can be written easier as:
Could we please fix this?
Besides that, this is RTBC. Thanks!
Comment #110
aneek CreditAttribution: aneek commented@Fabianx, indeed this can be fixed. I tried to change the minimal of the codes in the module. However, this can easily be done. :-)
Thanks!
Comment #111
aneek CreditAttribution: aneek commentedNew patch.
Comment #112
Fabianx CreditAttribution: Fabianx commentedRTBC if tests pass. Thanks!
Comment #113
alexpottI think we can remove the SafeMarkup::set() calls from editor.admin.inc and (maybe) Drupal\Core\Render\Element\MachineName
Comment #114
chx CreditAttribution: chx commentedDone.
Comment #115
chx CreditAttribution: chx commentedComment #116
chx CreditAttribution: chx commentedOpsie.
Comment #117
Fabianx CreditAttribution: Fabianx commentedRe-reviewed: Back to RTBC - provided tests still pass.
Comment #118
Fabianx CreditAttribution: Fabianx commentedComment #124
Fabianx CreditAttribution: Fabianx commentedComment #125
lauriiiComment #127
lauriiiThese SafeMarkup:sets cannot be removed since they are not going through the form apis sanitation.
Comment #128
chx CreditAttribution: chx commentedWell then we need to think on how the patch is architected because those properties are supposed to be safe from double escape?
Comment #129
aneek CreditAttribution: aneek commented@chx,
Just to get an idea,
Isn't that was the initial idea of this issue to check whether other modules or core sends proper data in
#prefix
,#suffix
etc. before sending it to twig and to remove the double escape. In that case why the modules need to send data wrapped inSafeMarkup::set()
method? What if the system checks for the data and then decides to send it viaSafeMarkup::set()
orcheckAdminXss()
?@lauriii, as you said,
Can't we just let the modules/core forms send what ever data (ideal case they will sanitize the data) possible and check in our form or theme layer?
Comment #130
Fabianx CreditAttribution: Fabianx commented#129 You are right.
It was me who derailed this issue and pushed to move that to Form API.
We will need to track the execution stack from drupal_render($element) down to the actual twig template, to see where we can be inject if not at FAPI level.
Edit:
It might be that the place to add this is before the actual _theme() call in drupal_render(), but it might also be there is something else the Form API elements have in common.
Comment #131
aneek CreditAttribution: aneek commented@Fabianx, then I think we need to re-structure this one and have to work from scratch. Rather than taking the FAPI approach I'd suggest to work this on the theme layer. What if to work on
#post_render
callback?The last level of code execution on theme level - Any suggestions? @chx, @lauriii, @alexpott, @joelpittet any thoughts?
Basic changes that are expected,
At the end execution phase of form render or theme render, need to check the values in the #key elements are safe or not. If not safe then use XSS check else if safe then use SafeMarkup.
Please feel free to modify the implementation logic mentioned above. And any ideas or thoughts are welcome.
EDIT #1
The issue #2273925: Ensure #markup is XSS escaped in Renderer::doRender() is also needs some attention (I think). I came across this while going via
drupal_render()
function.EDIT #2
There are few more findings about
drupal_render()
&_theme()
functions.@Fabianx, I tried to drill down
drupal_render()
function and the following points can be assumed,#theme_wrappers
.#render_children
is only to be used internally and only set via_theme()
function so no other module will add it in FAPI.Being said that if we can implement the sanitization of form elements in
_theme()
function just before,where it is decided where the theme get created, via functions or via twig template then, it may solve one section of this issue. But if
#theme_wrappers
are supplied by any modules this may fail as to prevent infinite loopdrupal_render()
takes some measures.So guys, please share your thoughts and correct me if I am in a wrong direction.
Thanks!
Comment #132
aneek CreditAttribution: aneek commentedComment #133
aneek CreditAttribution: aneek commentedA patch based on my thoughts in comment #132. Probably it will fail but I want to be sure that which sections fail.
Future plans-
After fixing failing sections and addressing review comments if this one is feasible enough then will add test cases that will validate this one.
Comment #134
aneek CreditAttribution: aneek commentedComment #136
aneek CreditAttribution: aneek commentedIt seems "#post_render_cache" form attribute has some issues with this patch. :-)
Any ideas please..
Comment #137
aneek CreditAttribution: aneek commentedBased on the discussion with @chx and @Fabianx, this patch is based on patch #116.
Review review :-)
Comment #138
aneek CreditAttribution: aneek commentedComment #140
chx CreditAttribution: chx commentedComment #141
aneek CreditAttribution: aneek commented@chx, thanks for the fix :-)
So any reviews guys? Or can we make this RTBC?
Comment #142
chx CreditAttribution: chx commentedComment #143
Fabianx CreditAttribution: Fabianx commentedNow we are almost back at the original patch, but 100 comments further, we are finally back to RTBC :).
But now we can at least say:
We tried all possible locations and this one is perfect.
Great job! Thanks for all that contributed so far!
Comment #144
joelpittetCan't say we didn't try on this one.
RTBC+1
Comment #145
alexpottIt'd be great to have more explicit testing of this in RenderTest - but that can be a followup.
I don't get why we need to do this.
Leaving at rtbc to get an answer to the second question.
Comment #146
aneek CreditAttribution: aneek commented@Fabianx, now as all are in place do we need the following?
Do we still need to check the #prefix here? Clearly the prefix will go via drupal_render and gets checked.
Is it really necessary now to mark the content as safe here?
Comment #147
chx CreditAttribution: chx commentedComment #148
Fabianx CreditAttribution: Fabianx commented#145: Here is my reasoning:
My reasoning, was that anyone could do something strange like setting the expression to some user input data, which clearly is an edge case.
On the other hand there is the "rule" to never put something in SafeMarkup::set that you are not 100% sure is safe.
Therefore would like to keep the check for consistency, but maybe improve the comment?
#147: I just _love_ that solution. Very clever usage of drupal_render() here and one less SafeMarkup::set(). Win!
Comment #149
alexpott#147 looks awesome.
It'd be good to get a better comment for the
$browsers['IE']
case. I think somewhere we agreed to document every unexpected usage of SafeMarkup::set() really well so contrib does not copy unnecessarily - we should have the same rule for SafeMarkup::checkAdminXss().Comment #150
webflo CreditAttribution: webflo commentedImproved the comment in
HtmlTag::preRenderConditionalComments
.Comment #151
Fabianx CreditAttribution: Fabianx commentedThe comment looks good to me. I hope it passes Alex' review as well.
Lets get this in!
Comment #152
tim.plunkettExtra space in front.
@param string $string
Indented too far, and it should be
@see \Drupal\Component\Utility\Xss::filterAdmin()
Please don't remove this for no reason.
These comments need a little finessing, they don't read perfectly in English. Also, they should be rewrapped to 80 characters.
Comment #153
joelpittetThanks for the review @tim.plunkett these should address the comments in #152.
Comment #154
tim.plunkettIs "SafeString" supposed to be this class? If so, it can be
self::set()
, otherwise it needs to be fully qualified, and still needs ()you missed the :: and the () here.
Interdiffs++
Comment #155
joelpittet@tim.plunkett whoops sorry, just before I was kicked out of the sprint.
Here is the interdiff for #153 and this one.
Comment #156
aneek CreditAttribution: aneek commented[++]Commit to core :-)
Comment #157
Fabianx CreditAttribution: Fabianx commentedThanks @Tim Plunkett for a great review. I did go through the patch again very carefully, but could not find further occurrences of what Tim described.
Therefore back to RTBC.
Comment #158
alexpottCommitted 56af688 and pushed to 8.0.x. Thanks!
Reclassifying as a bug since this will solve a load.
Fixed on commit.
Comment #160
mbrett5062 CreditAttribution: mbrett5062 commentedFound some inconsistency in the code markup. Please see below.
I believe the conditional comment markup in this line and the following lines is incorrect and inconsistent. They should always begin with
<!--
and end with-->
Can the issue be re-opened to fix this.
Comment #161
chx CreditAttribution: chx commentedPlease file a followup and link here. Thanks!
Comment #162
mbrett5062 CreditAttribution: mbrett5062 commentedFollow up to fix conditional comment expressions in HtmlTag.php has been raised.
#2364357: Markup in HtmlTag.php incorrect.Issue closed, everything is as it should be.
Comment #163
star-szrGreat work everyone! Happy to see this in.
Edit: Moving discussion to follow-up issue.
Comment #164
jibran