Problem/Motivation
Let's assume the following scenario:
- Site is on beta 12
- Site code is upated to beta 13
- Sites update.php is executed
- Problem: The aggregated JS file does not have yet #2510104: Convert drupalSettings from JavaScript to JSON, to allow for CSP in the future applied
- Now you cannot run update.php due to some JS error on the batch process
Detailed report:
- Go to update.php already triggers:
Uncaught ReferenceError: drupalSettings is not defined - Later
Uncaught DrupalBehaviorError: attach ; batch: Cannot read property 'batch' of undefinedis triggered.
In addition, the logic around maintenance mode in DbUpdateController is broken and seems to be trying to use config that's never set. We need to fix that also if we want to rely on it.
As a bonus, defined('MAINTENANCE_MODE') seems to only be true during the running of install.php now.
Proposed resolution
Disable the JS aggregation on update.php by disabling it whenever in maintenance mode. Fix the logic in the update controller so we actually go into in maintenance mode while update.php is running.
Fix the Cache ID so that JS aggregation uses the correct cache value in AssetResolver::getJsAssets()
Remaining tasks
review
User interface changes
none
API changes
none
Data model changes
none
Beta phase evaluation
| Issue category | Bug because JS can be broken during updates, and JS asset caching does not work correctly |
|---|---|
| Issue priority | Major because it blocks a security enhancement, and can break running update.php in the UI |
| Prioritized changes | The main goal of this issue is security becuase it enables removal of inline JS settings which enables CSP |
| Disruption | Not disruptive for core/contributed and custom modules/themes |
| Comment | File | Size | Author |
|---|---|---|---|
| #44 | increment.txt | 3.13 KB | pwolanin |
| #44 | 2538274-update-js-44.patch | 11.41 KB | pwolanin |
| #42 | increment.txt | 5.69 KB | pwolanin |
| #42 | 2538274-42-UpdateScriptTest-only.patch | 5.36 KB | pwolanin |
| #42 | 2538274-42-SiteMaintenanceTest-only.patch | 2.11 KB | pwolanin |
Comments
Comment #1
wim leersSeems major at least? :)
Comment #2
dawehnerComment #3
wim leersThe actual problem here is that
update.phpshould NEVER use aggregated CSS & JS. It's only by accident that we haven't encountered this problem in the past.#2510104: Convert drupalSettings from JavaScript to JSON, to allow for CSP in the future just happens to be the first one to trigger this problem.
Comment #4
wim leersComment #5
dawehner.
Comment #6
wim leersI think we should make
BareHtmlPageRendererInterface-rendered HTML responses never aggregate CSS & JS. The installer and updater don't need better front-end performance (which is what aggregation is for), they need to be reliable.Comment #7
dawehnerI'm curious whether care about fast maintenance pages if you are offline
Comment #8
wim leersThis is the code retrieving aggregated CSS/JS:
(In
\Drupal\Core\Render\HtmlResponseAttachmentsProcessor::processAssetLibraries().)So, the easy fix is to have
BareHtmlPageRendererdodefine('MAINTENANCE_MODE', TRUE). But we kind of want to get rid of that global, so…Comment #9
wim leersMeaning we should have aggregation on the maintenance page?
I was thinking that too, but… your maintenance page should be super lightweight in the first place (i.e. very little CSS, and probably no JS); so I don't think it really makes sense to want to enable CSS/JS aggregation on the maintenance page.
Comment #10
dawehnerBesides the discussion that defining the constant might do it, I would strongly argue that this is just a workaround. There should be a proper API for that.
Comment #11
wim leersActually,
install.phpdoes setMAINTENANCE_MODE. Looks like the migration ofupdate.phpto a route + controller (DbUpdateController) lost that?And, in fact, looks like this breaks existing code:
Those things no longer run because
DbUpdateControllerdoesn't setconst MAINTENANCE_MODE = 'update';. Onlyauthorize.phpstill does.Looks like
DbUpdateControllershould too?Comment #12
wim leersAgreed in principle, but attachments, including assets, are only handled AFTER we have a
HtmlResponseobject. So whatever non-global-state API we provide, would need to make its way intoHtmlResponsetoo; i.e.HtmlResponseneeds to carry the configuration.(Not saying that's bad, just pointing it out.)
Comment #13
wim leersSo
DbUpdateControllersets a value in state rather than the constant. That's why #11 points out broken code. Here's a patch with the quick'n'dirty fix.This could be a solution that's equally hacky as the current mess. But agreed with #10 in principle that we want a proper API. Just not sure that's worth it, because the API simply is… the configuration. The only places where you want to disable aggregation are places where the site's deployed state is being modified.
Actually, perhaps this could be implemented using a config override?
Comment #14
catchOpened #2538292: Remove atavistic references to MAINTENANCE_MODE == 'update' and supporting code.
Comment #15
wim leersAlternatively, here's a config override-based approach. Untested.
Comment #16
dawehnerMHH, this would add an overhead on every page, which is sad.
\Drupal\Core\Config\Config::setSettingsOverrideis public, so it could be called from the controller, couldn't it?Comment #17
wim leersComment #18
pwolanin commented#13 looks reasonable if it resolves the bug. What's wrong with that apporach?
Comment #19
dawehnerRight, but its simply adding an overhead on every request. Let's not start to do bad things.
Comment #20
pwolanin commentedLooks like that need to merge with Wims patch?
Comment #21
pwolanin commentedAre the 2 extra method calls that much of a worry?
Comment #22
dawehnerMh, no?
Are you really sure we talk about just 2? For example cacheable metadata needs to be created, then merged.
I bet you easily come to 50, and well, the approach from wim is more difficult.
Comment #23
fabianx commentedWe will need some tests - but #19 looks fine on its own.
Comment #24
dawehnerMh, that is too late to be actually able to change it, mh...
Comment #25
dawehnerI would vote for adding a flag on the route to be able to opt out.
Comment #26
pwolanin commentedCan we even just disable JS and not CSS here? Especially with the plan to break CSS into many more small files that may break worse on some browsers, and generally the CSS isn't functional for update.php
Comment #27
dawehneryeah I don't care about CSS here.
Comment #28
wim leersI prefer consistency and prudence, but don't have a strong opinion.
Peter, can you link to the issue where we are splitting up CSS in many small files?
Comment #29
serg2 commentedI think #26 was referring to #2395853: Split system.module.css and system.theme.css files into SMACSS style components
Comment #30
pwolanin commentedYes, that's the one davidhernandez was working on recently.
Comment #31
pwolanin commentedTesting this locally, I don't see that the JS files are non-aggregated with this patch applied:
Am I missing something about how this should work?
Comment #32
dawehnerYeah this is why we need maybe a flag on the route itself. It is kinda great that you cannot just change global state as you like it to be.
Comment #33
wim leersRight.
What do you propose, @dawehner?
Comment #34
pwolanin commentedWow, the logic in DbUpdateController seems to be 2x broken.
This is a new patch.
Comment #35
wim leersIf we go this way after all, can we then at least keep the two "is maintenance mode?"-checks together?
All we need now, is test coverage.
Comment #36
pwolanin commentedTrying to write the test, I found a serious bug in \Drupal\Core\Asset\AssetResolver::getJsAssets() in that is was not including the $optimize flag in the cache ID, so was serving incorrect cached assets.
So, this includes that fix plus a small test change to verify that the JS aggregation is off when maintenance mode is enabled.
Comment #37
pwolanin commentedComment #38
dawehnerWill try to manually reproduce my previous failures.
Do we need this fix here? #2435135: Maintenance Mode after Database Update seems to tackle the same problem but also takes care of providing a test
Comment #39
dawehnerPeter asked me to manually check out the patch:
Comment #40
pwolanin commented@dawehner, the other issue doesn't seem to have a real fix?
Comment #41
pwolanin commentedApplying patch from https://www.drupal.org/node/2435135#comment-10165198
credit to wuinfo for that
Comment #42
pwolanin commentedExpanding the test so we check running both running updates starting from out of or in maintenance mode and making sure that's preserved.
Also, 2 test only patches to show the fails during update.php and the fails around aggregation.
Comment #43
wim leersThis looks great! Pretty much only nits. RTBC once they are fixed. Especially considering #39.
We should update this code comment too, to remain in sync.
Nit: 80 cols.
Nit: "bring the site offline" sounds a bit strange. Why not just "put in maintenance mode"?
s/Should/should/
Missing
\n.Comment #44
pwolanin commented@Wim Leers -
43.1: the CSS code above doesn't mention the $optimize flag, even though it includes it already. I don't think that needs a comment, since it's obvious why it's needed.
Fixed other comment issues.
Comment #47
dawehnerLooks alright for me now
Comment #48
wim leers+1
Comment #49
alexpottCommitted fbde5c4 and pushed to 8.0.x. Thanks!
Thanks for adding the beta evaluation to the issue summary.
Not a huge fan of just tacking a 0 or 1 on to the end of the key but this is exactly the same as the CSS version so okay.
Fixed Model -> Mode on commit.