Problem/Motivation
In the BigPipe D8.1 core issue, @effulgentsia said this at #2469431-258: BigPipe for auth users: first send+render the cheap parts of the page, then the expensive parts.6:
+++ b/core/modules/big_pipe/src/Render/BigPipe.php @@ -0,0 +1,431 @@ + list($pre_body, $post_body) = explode('</body>', $content, 2);This can catch an earlier occurrence of
</body>than the one we want. For example, such a string within a CDATA section or a JS string value within inline JS. I didn't check if other places where we do similar string parsing is similarly brittle.
The complete set of places with such brittle parsing:
- In
\Drupal\big_pipe\Render\BigPipe::sendContent():
list($pre_body, $post_body) = explode('</body>', $content, 2); - In
\Drupal\big_pipe\Render\BigPipe::getPlaceholderOrder():
$fragments = explode('<div data-big-pipe-placeholder-id="', $html); … $t = explode('"></div>', $fragment, 2); - In
\Drupal\big_pipe\Render\BigPipe::sendPreBody:
list($pre_scripts_bottom, $scripts_bottom, $post_scripts_bottom) = explode('<drupal-big-pipe-scripts-bottom-marker>', $pre_body, 3); -
in
\Drupal\big_pipe\Render\BigPipe::sendNoJsPlaceholders():// Split the HTML on every no-JS placeholder string. $prepare_for_preg_split = function ($placeholder_string) { return '(' . preg_quote($placeholder_string, '/') . ')'; }; $preg_placeholder_strings = array_map($prepare_for_preg_split, array_keys($no_js_placeholders)); $fragments = preg_split('/' . implode('|', $preg_placeholder_strings) . '/', $html, NULL, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
Proposed resolution
Per #16, only fix the most probable (yet still quite improbable) edge case: a second closing </body> tag. The others would have to be intentionally constructed HTML, and if somebody can insert that, they can just as well insert malicious HTML.
Remaining tasks
None.
User interface changes
None.
API changes
None.
Data model changes
None.
| Comment | File | Size | Author |
|---|---|---|---|
| #27 | 2678662-27.patch | 5.86 KB | wim leers |
Comments
Comment #2
wim leersComment #3
wim leersComment #4
wim leersOther occurrences of brittle parsing:
\Drupal\big_pipe\Render\BigPipe::getPlaceholderOrder():\Drupal\big_pipe\Render\BigPipe::sendPreBody:IS updated.
Comment #5
wim leersOh and one more, in
\Drupal\big_pipe\Render\BigPipe::sendNoJsPlaceholders():Comment #6
wim leersSo, working towards a proposed resolution…
\DOMDocument-based parsing. I.e. DOM parsing, not string parsing. That'd work for cases 1–3, but not case 4, because no-JS placeholders may be embedded inside attribute values or even text nodes. That could theoretically be done using DOM parsing, but would be extremely slow.\DOMDocumentdoesn't support HTML5. It only supports XHTML. #2429363: Add HTML5-lib to Drupal 8 core for the filter system and for the testing system added a library to support HTML5 parsing. But that means the parsing would be done in PHP, not in C, and hence be even slower.Given all that, I'm not sure how we can solve this.
Comment #7
wim leersSince November 19, when BigPipe 8.x-1.0-beta1 for Drupal 8.0.x was tagged (https://www.drupal.org/node/2619070), not a single person has run into this problem. That's 3.5 months.
Therefore I think it's fair to say this is minor.
Even more so because
CDATAis really XML, not HTML5, even though HTML5 still allows it.Comment #9
wim leersComment #10
mondrakeI'm not sure this is the case, but using BigPipe with the Devel module and Kint I get some JS rendered as text when inspecting an entity through the Devel tab, and cannot expand the Kint debug
without BigPipe it all works fine
Comment #11
Tim Banks commentedI am getting the same behaviour as mondrake. A fix is pretty important for install profiles that already use bigpipe as you cant just turn it off... Any chance a fix might get looked at again?
Comment #12
wim leers#11: also using Kint?
Comment #13
wim leersReproduced the problems with Kint.
Wow, Kint seems very poorly integrated:
kint($page)inquickedit_page_attachments()for example, it will completely break Drupal while BigPipe is enabled. You literally won't get any Drupal HTML, just Kint's<script>tag plusThe website encountered an unexpected error. Please try again later.<br />.document.write()(gasp!) to loadThis makes sense from the POV that Kint wants to be self-contained and robust, regardless of where it is used. But I think we need to more carefully integrate the module into Drupal to prevent these kinds of problems.
Comment #14
podarok#10 confirmed on my end with kint + devel
Comment #15
wim leersTriaged by @Fabianx and I at DrupalCon New Orleans.
We agree this needs to be fixed, but we both think it is in fact minor: it can be fixed at any time, will not require BC breaks. The Kint solution is tangentially related, but not exactly, so we moved that to a separate issue: #2723709: Investigate Kint + BigPipe compatibility.
Comment #16
wim leersDiscussed with @Fabianx.
So, this issue is going to solve the most-probable (yet still quite improbable) edge case in a simple, sane way. If we encounter these other edge cases in the real world, we'll deal with them then. Hopefully PHP will by then have a working HTML5 parser.
Assigning to me because I'll be working on this in the next few days.
Comment #17
wim leersTo start, here's regression test coverage showing that a second
</body>tag is a problem. This test should fail.Comment #18
wim leersAnd here's the fix.
Comment #20
fabianx commentedRTBC - Looks great to me!
Comment #22
fabianx commentedBack to RTBC, random test failure
Comment #23
alexpottI wonder if this could be better written as:
Comment #24
wim leersThat'd work too. It's probably easier to understand.
Fabian, do you agree?
Comment #25
alexpottThe performance looks quite similar... https://3v4l.org/4Wnhp/perf#tabs vs https://3v4l.org/CfDGB/perf#tabs
I think #23 is much simpler to understand than all the reversing and more maintainable - so setting to needs work for that.
Comment #26
wim leers+1
Comment #27
wim leersDone.
Comment #29
fabianx commentedBack to RTBC, still looks like a random test fail to me.
Comment #30
alexpottCommitted and pushed f95fc93c84b93e1cafe9892b8942d1846b773e0f to 8.2.x and 518e32b to 8.1.x. Thanks!
Comment #33
mondrakeChecked post commit, and no longer see #10 happening. Thanks!
Comment #34
wim leersGreat!