Problem/Motivation
In #3252126: Do not apply updates during cron if there are DB updates in the staging area, we introduced detection of new or changed update functions in staged modules. Currently, we just use simple hashing of the .install and .post_update.php functions to check for changes, but this can produce incorrect results if comments, whitespace, or runtime code like hook_requirements changes in those files. We figured that's good enough for MVP, but it might be nice to introduce something smarter.
We should solve this for stable in Drupal core.
Proposed resolution
Use low-level built-in PHP parsing (token_get_all()) to detect new update functions in the .install and .post_update.php files.
This will NOT detect removed or changed update functions. That's on purpose. Changed update functions are irrelevant -- updates are only ever run once, so they're not really something you ever change. And removed update functions are also not relevant to our purposes -- they are gone, so they don't affect anything. We only really need to detect new updates.
Issue fork automatic_updates-3253828
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
effulgentsia commentedI think adding
nikic/php-parserto core has other good use cases too, such as parsing annotations or PHP 8 attributes. If there isn't a core issue already for adding this library to core, let's open one.Comment #3
wim leersIsn't core already depending on
nikic/php-parser?This seems like it potentially is worthy to be tagged
core-mvp, since it has such a big impact?I think this is at minimum?
Can we convert this into a Plan and create multiple child Tasks?
Comment #4
tedbowJust to be clear
I believe all of these could only produce false positives, determining there is an update when there is not. Not false negative, determining there is not an update when there is.
I think this case is much less critical because:
if there weren't functional changes in those files
So this may be a reason for this not be major. I personally don't see why this would need to be core-mvp(unless everything we would ever want the system to be is core mvp😜). It doesn't seem changing this later would cause any API changes
Comment #5
wim leersI figured it was potentially
core-mvpbecause it has significant security implications. Not because we missed something, but because we would prevent a security update from getting installed precisely because this is overly cautious…Comment #6
tedbowIt seems to be in
composer/Metapackage/CoreRecommended/composer.jsonbut nowhere else core so I am not sure what this mean. If it is a direct core dependency shouldn't it be in `core/composer.json`.does that mean we should make a core issue to add the dependency to core before we put work into using in our module. Assigning to @Wim Leers because maybe you know how this works
Comment #7
wim leersTurns out it's a dev-only dependency for
phpunit. But this does seem like a low-risk external dependency.I do think that is still accurate based on #5. I still think that reducing false positives on this front is something with indirect security implications.
However, after inspecting the code (
\Drupal\package_manager\Validator\StagedDBUpdateValidator), I do see that we can be 99% confident that this would be an isolated change, without the need for API changes/BC breaks. Plus, it's marked@internal.So let's explicitly postpone this to a later phase?
Comment #8
wim leersSince @tedbow didn't respond after a week, explicitly assigning to him.
Comment #9
tedbowYes I think we can postpone this for reasons stated in #7
Comment #10
tedbowI think we could do this after the first core commit but maybe @Wim Leers disagrees
Comment #11
wim leersI agree 😊
So … is this a must-have for beta/stable, or a should-have for stable?
Comment #12
wim leersMarking to get @tedbow's response to #11.
Comment #13
tedbowI think for stable. Not sure how we should tag issues
core-stable-blockerandcore-beta-blocker?Comment #14
wim leersWhy not just
stable blockerandbeta blocker? The implication is that it's referring to core?Comment #15
wim leersOops! Was just testing it out :P
Comment #16
tedbowComment #17
tedbowComment #18
tedbowI noticed that 10.0.8 to 10.0.9 actually has a change to system_requirements that would flag has a DB update when it is not
Comment #19
wim leers#18 happened at #3351895-24: Add Drush command to allow running cron updates via console and by a separate user, for defense-in-depth.
Comment #21
tedbowcrediting @FizCS3 for finding in testing
Comment #22
tedbowComment #23
phenaproximaI dug into this a bit and I think it actually wouldn't be too tricky.
It would require us to create a node visitor (as documented) that detects functions in certain files, matching certain naming patterns, and simply compares the lists of found functions. Obviously this would only be done if nikic/php-parser were installed; otherwise, we'd fall back to hashing.
But generally I think this would be relatively simple.
Comment #25
tedbowComment #26
phenaproximaComment #27
wim leers@phenaproxima mentioned
token_get_all(). It looks REALLY promising: https://3v4l.org/SX0rTAny sequence like this:
we'd need the value of
T_STRINGfor, and … we'd have a complete list! 🥳Comment #28
phenaproximaComment #29
phenaproximaComment #30
tedbowre #18 we should manually test that this new method would not have the false positive for the DB updates that current method has
Comment #31
tedbowI few comments in the MR
Comment #32
phenaproximaComment #33
tedbowComment #34
phenaproximaComment #35
tedbowComment #36
phenaproximaComment #37
phenaproximaFor manual testing:
Drupal 10.0.9 changed some code in system.install, compared to 10.0.0. (The code in question is part of hook_requirements.) No update functions were added that I can see. So this is a perfect pairing of versions to prove that this static analysis is smarter than what we previously had.
Comment #38
phenaproximaTo manually test the status quo, I did the following:
As expected, this died with the error:
Comment #39
phenaproximaTo test this branch, I did the following:
This time, it died with the following:
However, this makes perfect sense: running
git diff 10.0.0:core/modules/user/user.post_update.php 10.0.9:core/modules/user/user.post_update.phpshows me that post-update functions were added to the User module!This is already a lot more promising! It detected actual update functions in User, but didn't complain about System.
Comment #40
tedbowThanks for testing!
Comment #41
phenaproximaComment #43
phenaproximaComment #44
wim leersThat looks amazing! 🤩
Comment #46
wim leersI am a bit curious if this is sufficiently fast for large codebases, but considering how little code is typically in
*.installand*.post_update.phpfiles, I'm not very concerned. Still, this might come back to haunt us at some point in the future in some extreme cases…