Follow-up to #2545972: Remove all code usages SafeMarkup::checkPlain() and rely more on Twig autoescaping
Problem/Motivation
In Drupal 8 we enabled twig autoescape but we also created SafeMarkup::checkPlain() to escape text and then mark it as safe to ensure that Twig does not double escape it.
Proposed resolution
In #2545972: Remove all code usages SafeMarkup::checkPlain() and rely more on Twig autoescaping we remove all usages in core. We should rely on Twig auto escape and completely remove SafeMarkup::checkPlain(). SafeMarkup::checkPlain() is dangerous because if the resulting string is modified in any way, for example nl2br(), the result will no longer be marked safe. In places where explicit escaping is needed, Html::escape() should be used.
We should deprecate SafeMarkup::checkPlain() and decide if we want to remove it.
Remaining tasks
- Improve the change record discussion of checkPlain() replacements (see #32).
- Review all change records that contain references to check_plain and checkPlain and ensure they reflect current best practice. List the specific change records below once it has been confirmed they are up to date.
User interface changes
None
API changes
Deprecate SafeMarkup::checkPlain()
Data model changes
None
Beta phase evaluation
| Issue category | Task because this is to decide whether we want to deprecate |
|---|---|
| Issue priority | Major because SafeMarkup::checkPlain() is hard to use correctly and we should be using auto escape |
| Disruption | Disruptive for core/contributed and custom modules/themes because it will require a BC break as SafeMarkup::checkPlain() no longer exists. However, as shown by the patch most cases will be fixed by just removing it. I think the disruption is worth it for a less complex and entangled SafeMarkup class. |
| Comment | File | Size | Author |
|---|---|---|---|
| #18 | interdiff-15-17.txt | 1.07 KB | stefan.r |
| #17 | 2555473-17.patch | 873 bytes | stefan.r |
| #15 | deprecate-2555473-15.patch | 848 bytes | cilefen |
| #15 | diffstuff.txt | 1000 bytes | cilefen |
| #14 | 2555473.14.patch | 860 bytes | alexpott |
Comments
Comment #2
alexpottComment #3
mpdonadioThe biggest advantage to removing it is that it will force people to use the new methods. It it remains, I forsee a lot of SA-CONTRIB going out because contrib maintainers will just convert check_plain() usages into SafeMarkup::checkPlain(). Given all of the XSS SA that have gone out this year, I think this is a legitimate concern. I also think that we have enough examples to pull from from the current purge to fill out https://www.drupal.org/node/28984 nicely.
Comment #4
wim leers#3: Nicely put :)
Comment #5
xjmDiscussed with @effulgentsia, @alexpott, @catch, and @webchick. We agreed to deprecate
checkPlain()and that it is a critical part of minimizing SafeMarkup use. We will initially deprecate it for removal before 8.0.0.Removing
checkPlain()entirely or removing its core usages is not critical, but we can continue that work up to RC.Comment #6
alexpottComment #7
catchJust to clarify what I think the plan is:
Deprecate for 8.0.0 now, without removing all usages yet, to send a clear message via change notices etc. that contrib should remove their own usages asap.
If we're able to remove all core usages before a release candidate, try to remove it.
If we're not able to remove all core usages before a release candidate, deprecate for 9.x instead and/or mark @internal and possibly try to make it so neither do anything at all with the safe list.
Comment #8
catchTagging this as beta target since it's a deliberate API change that we want to expose to contrib as soon as possible.
Comment #9
stefan.r commentedCould we do something like this, on both methods:
@deprecated Will be removed before Drupal 8.0.0. If possible, rely on Twig's auto-escaping feature, or use the '#plain_text' key when constructing a render array that contains plain text in order to use the renderer's auto-escaping feature. If neither of these are possible, Html::escape() can be used in places where explicit escaping is needed....whenever (and if) #2555931: Add #plain_text to escape text in render arrays goes in?
Comment #10
cilefen commentedA novice critical? A novice can create the patch.
Comment #11
plachComment #12
plachSomething like this?
Comment #13
catchJust committed #2555931: Add #plain_text to escape text in render arrays which was soft-blocking this.
Comment #14
alexpottActually I think looking at #2549393: Remove SafeMarkup::escape() and rely more on Twig autoescaping we only need to deal with SafeMarkup::checkPlain here...
Comment #15
cilefen commentedI think this wording is a little more direct. To me, "possibly" used twice sounds like an invitation to do whatever I want.
Comment #16
dawehnerWhat about pointing to the corresponding docs (https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Render%21...) ? I think you can use @link for that, not sure about the syntax though
Comment #17
stefan.r commentedAdding in that link and removing a superfluous comma
Comment #18
stefan.r commentedComment #19
lauriiiIMO this comment should not be written only for the Twig because we still have to support PHP templating engine. At least we shouldn't make assumptions that everyone is using Twig.
Comment #20
lauriiiSo it doesn't actually leave out users of other templating engines but instead the structure was a little hard to process for me. Anyway I think the structure is good as it is because now it very explicitly tells in the first sentence that if you use Twig, there shouldn't be need to care about escaping manually.
Comment #21
cilefen commented#20 - The confusing sentence structure was the intention of the "superfluous" comma.
Comment #22
dawehnerI agree that
, or,was confusing to read, at least in english.Does this issue needs its own dedicated change record, or should we prepare one big one for all the issues?
Comment #23
alexpottI think we should have one CR that reflects the 8.x changes wrt to SafeMarkup (https://www.drupal.org/node/2549395) and one change record that details Twig auto-escape (https://www.drupal.org/node/2296163). If we must have other CRs they should be very short and just point to one of these two.
Comment #24
stefan.r commentedUpdated the CR at https://www.drupal.org/node/2549395 with a bullet point about checkPlain
Comment #25
alexpottI think this ready. We've got the CR and consensus. My contribution was a removal.
Comment #26
catchCommitted/pushed to 8.0.x, thanks!
Comment #28
xjmIn #2545972-95: Remove all code usages SafeMarkup::checkPlain() and rely more on Twig autoescaping I listed out a bunch of CRs that also needed to be updated for this change (others are/were mentioned in #2545972-84: Remove all code usages SafeMarkup::checkPlain() and rely more on Twig autoescaping). It might be worth reopening this for that, because it's not just a simple replacement; we want to dissuade people from escaping inappropriately.
Comment #29
xjmAlso, I think that the section on
checkPlain()in https://www.drupal.org/node/2549395 needs more work to better clarify whencheckPlain()should be replaced and with what. For example, it says "SafeMarkup::checkPlain() - If the string will end up in a Twig template, rely on Twig's auto-escaping feature." This is pretty vague and confusing -- one way or another, most things will "end up" in Twig templates. If modules remove their escaping incorrectly, it will still open XSS vulnerabilities. It might be better to say thatHtml::escape()is the most direct replacement forSafeMarkup::checkPlain()so people who don't pay attention to the details start there, and then to go into more detail about other strategies, with more examples. We should also emphasize that the developer should test carefully when converting code to rely on Twig's autoescape.Also, I think it's important to emphasize that non-HTML output must still be sanitized, either by using
Html::escape()when (e.g.) constructing the response, or by escaping in (e.g.) JavaScript à la #2503963: XSS in Quick Edit: entity title is not safely encoded.Reopening for that as well as #28. Thanks everyone!
Comment #30
xjmDraft for the related g.d.o/core announcement: https://docs.google.com/document/d/1OM33gYwcIXUB-QP2uiF-M-H54QyZdGyJHTj4...
Comment #31
xjmSorry, forgot to update the title. Also, the doc in #30 now has public comment access, but the post should go out in a bit anyway. :)
Comment #32
xjmAlright, @alexpott, @effulgentsia, and I reworked the main change record for the method removals in SafeMarkup methods are removed. There are still a couple outstanding things that I think should be improved (copying and expanding notes from our google docs draft):
This is clearer now, but as a reader of this I still would not entirely understand "is used in a template". First of all, there are ways to use strings unsafely in a template. Do we have a Twig resource we can reference about how output ends up in templates and how it is used safely? Secondly, there are many cases where a developer is creating output that will be used in an existing template without the developer knowing anything about it. (Maybe we need a chart somewhere indicating which strings will be escaped, which strings will be filtered, and which will not be sanitized. That doesn't need to block this though.)
We should clarify when they can't be removed. For example, if the string is XSS-filtered, this will be more permissive than escaping and bypass it because the resulting string will be safe.
This example is helpful. A code snippet might make it more so. However, some render array elements, like
#prefix,#suffix,#value, and#markupare not escaped (only admin filtered) so this sentence is somewhat too vague.Comment #33
xjmI updated some of the references to
check_plainorcheckPlainyesterday, and it looks like someone worked on them today as well (thanks!). Some remain; adding links to search to the summary as well as the remaining task for the main CR.Comment #34
xjmComment #35
xjmComment #36
chx commentedHere, I edited the summary for you to help with the decision.
Comment #37
alexpottobviously https://www.drupal.org/node/2549395 and https://www.drupal.org/node/2296163 are good
Going through the rest of the CRs from point 2 of the issue summary.
I think we might be done.
Comment #38
xjm#37 looks good to me. Thanks @alexpott.
#32 has not been addressed entirely, but I don't think that's critical at this point, so moving that to the scope of #2494297: [no patch] Consolidate change records relating to safe markup and filtering/escaping to ensure cross references exist.
Comment #39
mustanggb commentedIt's looking like #2545972: Remove all code usages SafeMarkup::checkPlain() and rely more on Twig autoescaping might land before RC, so shouldn't this issue be demoted from critical and postponed on that one, in-case removal rather than deprecation is possible?
Comment #40
catchWe need to deprecate before we remove, and ideally give as much notice as possible so people can update.
Also it was committed already.