Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Follow-up to #2280965: [meta] Remove every SafeMarkup::set() call
Problem/Motivation
SafeMarkup::set() is mostly for internal use only. For the most part, existing APIs like t()
, String::checkPlain()
, XSS::filter()
, drupal_render()
, etc. should be marking the things they sanitize, and markup in general should be moved into templates wherever possible so the themer has control of it.
Proposed resolution
Remove the call to SafeMarkup::set() in RestExport - at this point #markup
is the result of a render therefore it is marked safe already.
Comment | File | Size | Author |
---|---|---|---|
#14 | Screen Shot 2015-08-20 at 19.25.28.png | 249.43 KB | alexpott |
#7 | interdiff.txt | 1.91 KB | Wim Leers |
#7 | 2553953-7.patch | 1.17 KB | Wim Leers |
#4 | 2553953.4.patch | 2.14 KB | alexpott |
#4 | 2-4-interdiff.txt | 1.07 KB | alexpott |
Comments
Comment #2
alexpottNo point calling SafeMarkup::set() on something already wrapped in SafeStringInterface.
Comment #4
alexpottAh okay so...
from RestExport is classic views interrupting the render pipeline to do it's own thing. We have a class for this! Let's use it.
Comment #5
Gábor Hojtsy@alexpott: so you said
So I tried to understand how this works. But executeInRenderContext() documents that it returns whatever was passed in, and neither the style plugin renderer neither the ViewsRenderPipelineSafeString seem to explain what they would return/how.
So its hard to ensure based on my review how this is going to be safe :) I mean the class name is promising and all, but:
StylePluginBase::render() says
so that will return something... Then ViewsRenderPipelineSafeString wraps it.
Then executeInRenderContext() returns "The callable's return value.". So it will just be a string after all, and that's it. How does that ensure anything about safeness?
Comment #6
Wim LeersYou give
RendererInterface::executeInRenderContext()
acallable
. The return value of that callable is returned:EDIT: I saw too late that you already figured this out, in the bottom of your comment — oops.
Yeah, the docs aren't the best, but it always returns a
string
(a plain string, not aSafeStringInterface
string).So, to then get to the key part of your question:
. The simple part of the answer is: "because REST export only renders to non-HTML formats, so we don't want additional filtering or escaping to happen just because Views happens to use part of the regular rendering pipeline — which is specifically designed for HTML — otherwise we'd end up sending HTML-escaped JSON, HAL, or whatever other format".BUT! I think you're actually right that the changes in this patch are wrong. Because the removed code
was only marking the markup as safe for non-HTML formats. This means that in HEAD, in case of a non-HTML format,
#markup
gets the default safety strategy treatment: admin XSS tag filtering. But because this patch now effectively is always marking the rendered markup as safe… that means that filtering no longer happens.This is a very complex intersection of different problems:
RestExport
could output HTML?!There are only two possible explanations AFAICT:
RestExport
is being used to output HTML.RestExport
, which means the quoted if-statement is actually wrong.Comment #7
Wim LeersThe patch is simpler than the interdiff :)
Follow-up filed: #2554819: RestExport detects if the content type is HTML, but it can never *be* HTML.
Comment #8
Gábor HojtsyYay, now I understand it better too :) Looks good.
Comment #9
xjmHmm, so this issue is now not doing what it says in the title. So we should at least re-scope it. Also I'd like to make sure @alexpott is on board with the current patch.
The last time I looked into this (admittedly, several months ago) I believe I found that the rest data is output as HTML when it is used in the View preview, which is why it's necessary to support HTML.
Comment #10
xjmSorry, I misread the updated diff. Disregard #9. Still would like @alexpott's feedback though.
Comment #11
alexpottTotally on board with this patch. We could change the RestExport::preview() method to not call the render() method - but the escaping is what we want for preview - exactly the same as Feed::preview().
Comment #12
alexpottImproved the title.
Comment #13
xjmEdit: This was regarding:
This was originally added in #2273925: Ensure #markup is XSS escaped in Renderer::doRender(). In #163 of that issue, @larowlan proposed returning a Response object directly inline there, but @dawehner pointed out there would be a conflict with #2477157: rest_export Views display plugin does not set necessary cache metadata.
@effulgentsia added the conditional and the long comment in #2273925-215: Ensure #markup is XSS escaped in Renderer::doRender().
I'd also like @dawehner to take a look here.
Edit: crossposted with @alexpott.
Comment #14
alexpottTested the rest export preview with this patch applied and without... looks the same and html inside the output is correctly escaped:
Comment #15
alexpottComment #16
Wim Leers#13: this does not change the existing behavior at all. It's identical.
I agree this is all very confusing and weird. But fixing that is a different problem. For that, I opened #2554819: RestExport detects if the content type is HTML, but it can never *be* HTML in #7.
Comment #17
dawehnerSo yeah HTML is just another representation of output.
I'm wondering we should set the entire output as ViewsRenderPipelineSafe, not just the one inside the !== 'html' case? I mean even for html, its semantically that we know its safe, either it
got safe already via the normal render workflow, check plained for the preview.
Comment #18
Wim LeersHow do we know this?
StylePluginBase::render()
returns a plain string, and style plugins can do any sort of rendering they want (even pure string concatenation).Comment #19
xjmAlright, sounds to me like that would need more discussion. So let's go ahead with this patch as it is, and then sort the rest of this out in #2554819: RestExport detects if the content type is HTML, but it can never *be* HTML.
Thanks for the reviews and the manual testing!
Comment #21
dawehnerIt can be a pure string, or a render array.
Comment #22
Wim LeersExactly, so how do we know that a pure string is safe markup?
Comment #23
joelpittet@Wim Leers SafeMarkup::isSafe($string) still works at the moment. Does that answer the question?
Comment #24
Wim Leers@joelpittet: Maybe :) I don't think Views style plugins are actually marking the output as safe, but I could very well be wrong. E.g. the
\Drupal\rest\Plugin\views\style\Serializer::render()
Views style plugin just returns:So I don't see how that ends up in
SafeMarkup
's list of safe strings.Comment #25
dawehnerThis assumption is wrong. Its not end up there, but rather its marked as safe by creating the new object in this issue, see
$string instanceOf SafeStringInterface
in\Drupal\Component\Utility\SafeMarkup::isSafe