Problem/Motivation
If you uninstall a module that provides filters to text formats, Drupal removes the text formats that used the filters, but should not.
Steps to Reproduce the Bug
- Enable a module that provides a text filter plugin.
- Enable the filter on a text format.
- Uninstall the module.
You will find that the text format has been removed.
Proposed resolution
- Prevent uninstallation of modules that provide filter plugins that are enabled in text formats.
- Implement FilterFormat::onDependencyRemoval(). Remove disabled filters from the text formats when called.
Remaining tasks
- Code a solution.
- Write tests.
User interface changes
None.
API changes
Task | Novice task? | Contributor instructions | Complete? |
---|---|---|---|
Manually test the patch | Novice | Instructions | |
Review patch to ensure that it fixes the issue, stays within scope, is properly documented, and follows coding standards | Instructions |
Comment | File | Size | Author |
---|---|---|---|
#50 | cant-enable-fitler-module-2348925-50.patch | 794 bytes | Garrett Albright |
#46 | 2348925.46.patch | 8.25 KB | alexpott |
#46 | 41-46-interdiff.txt | 2.56 KB | alexpott |
#41 | 2348925.40.patch | 7.99 KB | alexpott |
#40 | 34-40-interdiff.txt | 669 bytes | alexpott |
Comments
Comment #1
cilefen CreditAttribution: cilefen commentedComment #2
cilefen CreditAttribution: cilefen commentedDrupal warns you:
Comment #3
BerdirWe have an API for this now, see ConfigEntityInterface::onDependencyRemoval() and existing implemetations for it, most notably in EntityDisplayBase.
Fully agreed that this is a major if not critical bug, it should actually never be possible to delete a filter format.
Comment #4
alexpottDefinitely critical. Nice find.
Comment #5
cilefen CreditAttribution: cilefen commentedThis is fine except it doesn't work. ;-)
Comment #6
ashish_nirmohi CreditAttribution: ashish_nirmohi commentedComment #7
BerdirDoes not work how, does it still remove the filter?
Filter format uses EntityWithPluginCollectionInterface, so the dependencies are calculated based on getPluginCollections() -> filters(), which has a cache. I'm not sure if we re-load the objects or not.
I also don't know if those methods even care about the status?
Comment #8
cilefen CreditAttribution: cilefen commentedThis still does not work. removeFilter() is called in this situation but the code to remove the filter is wrong.
Comment #9
cilefen CreditAttribution: cilefen commentedComment #10
cilefen CreditAttribution: cilefen commentedComment #11
cilefen CreditAttribution: cilefen commentedI added a test that should not pass because the patch doesn't work. But, it passes
Comment #12
alexpottSo the
KernelTestBase::uninstallModule()
does not actually fire module uninstall code - it is a special flower.Also filter formats are a special flower too since the filter plugin collection will always return all available filter plugins since disabled configuration is preserved - unless it matches the default state - see \Drupal\filter\FilterPluginCollection::getConfiguration().
Comment #14
cilefen CreditAttribution: cilefen commented@alexpott Thank you. Manual testing with #12 works for me. +1 for RTBC
Comment #15
Wim LeersWOW!!!!!!
+1
Can be chained?
I'd think that this would happen automatically, since uninstalling modules should invalidate the
extension
cache tag. But I'm guessing this is for the static cache, which doesn't use cache tags?Missing trailing newline.
Comment #16
alexpottTnaks for the review @Wim Leers - re #15
Comment #17
Wim LeersAdding comments to clarify further.
Comment #18
cilefen CreditAttribution: cilefen commentedComment #19
tstoecklerSorry, if I'm missing something, but at the very least this was not discussed here, so...
I don't see how this is even remotely viable. The specific configuration and order of filters in a text format is highly security relevant and it is in no case whatsoever safe to just willy-nilly delete some of them without explicit user confirmation.
If you uninstall PHP filter module for example, you will potentially reveal highly sensitive information to the world.
Surely the current solution of deleting the formats isn't good, but at least it hides the text wholesale, because check_markup() can't find the format. This patch makes things dramatically worse.
The correct solution is to disallow uninstalling any module which provides a filter to an existing text format.
Comment #20
Wim LeersThis was my first thought also! :)
But consider this: you just installed a text filter. You gave it a try. You dislike it. You want to remove it. With what you propose, you can't.
Why is this warning not sufficient:
?
Comment #21
tstoecklerThat is not sufficient because quite simply we are putting the site in a potentially insecure state. And the user might be aware of that but she or he might not. A warning like that is nothing more than an excuse, when it comes to security. And it's a poor one too, because it is not at all explicit. Maybe I am completely aware of the security implications but I think that Drupal will simply delete all content which uses the given format, so I just go ahead. Who knows what
means?!
Comment #22
alexpottSo I think @tstoeckler is correct. We need to implement hook_system_info_alter in the filter module to not allow a module to be uninstalled if it provides a filter format plugin that is enabled in any filter format configuration entity.
Comment #23
cilefen CreditAttribution: cilefen commentedThis is not quite it (it makes all filter-providing modules required), but it's a start.
Comment #24
cilefen CreditAttribution: cilefen commentedComment #26
alexpottHang with #23 and #24. I was wrong. #21 now thinking about it - surely this is an existing issue. In Drupal 7 you can uninstall a module that provides the equivalent of a filter format plugin so this is an existing issue. I think the patch in #17 is good to go. We can fix this issue in the old issue created for it #562694: Text formats should throw an error and refuse to process if a plugin goes missing (which I have a patch for).
Comment #27
alexpottUploading the patch in #17 again to make it the latest and bring the issue back to being in scope.
Comment #29
tstoecklerRe #26: Ok, I agree that that issue is the one we need to fix properly. I still agree with this going in in the meantime, though. As stated above, the current behavior of deleting text formats is pretty bad, too, but at least it makes sure that text never gets filtered incorrectly, because check_markup() bails out in that case. I don't see why we should be purposely making things worse and introducing a known Security flaw just because we have another issue to fix that properly.
Comment #30
cilefen CreditAttribution: cilefen commented@tstoeckler This is fixing a regression. Without #27, you have a broken site. #27 is making things better, not worse. Let's focus attention on #562694: Text formats should throw an error and refuse to process if a plugin goes missing after this goes in.
Comment #31
tstoeckler@cilefen: Well it might be broken, but at least it is not insecure. In that tradeoff we should always choose security and I would consider it a terrible decision to commit this without #562694: Text formats should throw an error and refuse to process if a plugin goes missing but @alexpott is right that D7 suffers from similar behavior, so... In any case it's not up to me :-)
Comment #32
alexpottThis patch does not make anything less secure. Also I think we're confusing filter plugins with filter formats. Filter plugins are provided be modules and can be configured to be part of a filter format. HEAD's current behaviour is totally ridiculous. If a module provides a filter plugin all filter formats will depend it on. When that module is uninstalled ALL filter formats will be deleted. This fixes that.
Separately we should address the issue that if a module provides a filter plugin and it is used in any filter format then we need to prevent that module being uninstalled. That is exactly what #562694: Text formats should throw an error and refuse to process if a plugin goes missing is (I've made this critical). I've made critical and will have a patch the moment this one is committed. There is a separate issue around what occurs when a whole filter format is removed and that WORKS - a missing filter format results in no output.
Given @tstoeckler says:
I'm re-rtbcing.
Comment #33
tstoecklerSorry, that was a really weird typo. I meant disagree.
And just want to clarify that this does make things less secure as explained above.
Comment #34
alexpottOkey-dokey I'm not going to hold this up anymore especially since I've already done the work. Here is a patch that prevents uninstalling a module that provides a filter plugin that is currently used in any filter format configuration entity.
Comment #35
catchfwiw I completely agree with tstoeckler here.
One case where we could delete the filter format entirely is when it's not used at all in any content, but that doesn't really seem compatible with the hook_system_info_alter() approach.
Comment #36
catchComment #37
chx CreditAttribution: chx commentedI really don't like all these issues being hanged from hook_system_info_alter ; it will make any rebuild quite slow and brittle . We are working on an uninstall in #2278017: When a content entity type providing module is uninstalled, the entities are not fully deleted, leaving broken reference
Comment #38
catchIf we can add a validation step to module uninstall and present the information then that would save doing it in hook_system_info_alter() - is worth a side issue to add that. I don't want to hold any of these issues up on that though - we can convert later using the same information and it'll be an API addition rather than change.
We'll need to make sure at least drush also respects this though (as it does required modules).
Comment #39
alexpottModuleHandler::uninstall()
doesn't check if a module is required - if it did and we implement this extra step there then Drush would haven't to do anything too special. +1 to not holding this up.Comment #40
alexpottPatch fixes test for new
module_installer
service.Comment #41
alexpottNow for the patch
Comment #42
alexpottTagging with the upgrade path as the current HEAD could get a site into an inconsistent state.
Comment #43
Wim LeersReviewed the patch, tested it manually, stepped through the test coverage, tried to remove parts that I didn't understand at first sight/that didn't seem to be doing anything, hence gaining a full understanding. I couldn't break it. RTBC.
Theoretically we'd want to exclude the
filter
module from getting this treatment, because that'd imply a nonsensical recursive dependency. However, it also has real-world — though rare — repercussions: it makes it impossible to uninstall the Filter module as long as any text format (FilterFormat
) config entity isstill using any filter plugin. To uninstall the Filter module, you'd have to disable every filter plugin on every text format.
I'd almost say that this is a feature: it makes it exceedingly annoying/painful to remove the Filter system, and that's a good thing :)
Nitpick: clearer comment would be "Remove disabled filters, so that this FilterFormat config entity can continue to exist."
Comment #44
tstoecklerAwesome, thanks @alexpott for pushing this. This is a great solution to a non-trivial problem!
Comment #45
catchI think the issue has diverged from the summary a bit, from the patch we seem to be making two changes:
1. Mark modules that provide plugins required if the filter plugin is used in a filter format.
2. Remove filter plugins from formats if the plugin provider is uninstalled, but only if the filter plugin is disabled.
I'm confused why if we do #1 how we'd ever get to #2 though?
Minor nits below:
Minor and can fix on commit but missing space after foreach x 2.
Not sure this comment describes what the test does enough.
What about:
"Ensure that modules providing filter plugins are required when the plugin is in use, and that only disabled plugins are removed from format configuration entities rather than the configuration entities being deleted."
Although if that's the correct description I'm still not sure if the behaviour is right.
Comment #46
alexpottUpdated issue summary.
This is because disabled filters are still stored in text formats if they have different settings from their defaults.
re #43
re #45
Tentatively setting back to rtbc since the changes are very small.
Comment #48
catchOK that makes sense. Not storing anything for disabled filters is I think for #2312379: Bring filter format plugin configuration into line with other systems, I'd forgotten we'd not resolved that completely yet.
Comment #49
Garrett Albright CreditAttribution: Garrett Albright commentedCommit 8b6c343 has made it so that one gets a PluginNotFoundException when enabling a module which provides a filter. Try the D8 branch of Pathologic, for example. (Despite the exception when the module is enabled, if you later go to the text format configuration form, the filter is available…)
I'm trying to look into it to discover why, but might not succeed before my bedtime.
Comment #50
Garrett Albright CreditAttribution: Garrett Albright commentedLooks like filter_system_info_alter() is trying to get info about a plugin which doesn't fully exist yet when a module is enabled.
Comment #52
Wim LeersGarrett: please open a new issue and link to this one. It's no longer accepted in core to reopen already committed issues. It's much easier for everyone to have a new issue. It's fine to link to the new one from the fixed one though!
Comment #53
Wim LeersComment #55
slashrsm CreditAttribution: slashrsm commentedI seems this introduced another bug: #2397393: Fatal if installing a module that defined a filter plugin
Comment #56
geek-merlin