Problem/Motivation

Originally, from #560740-7: "Escape all HTML" filter does not escape any HTML
Then we thought #2348925: Uninstalling a filter plugin removes text formats fixed this.
But, we still need to resolve the behaviour in the following case:

1. A text format is configured to use a plugin

2. The plugin disappears.

There are now many less instances where a plugin can disappear, but not quite all of them:

1. The plugin could be removed or renamed in a contrib module with no upgrade path
2. The contrib module is removed from the system entirely without running uninstall

Neither of those cases are 'allowed', but is worth warning people about it. Even in these unsupported edge cases, Drupal should remain safe.

Proposed resolution

In either of those cases, when trying to run a text format, it should cause both an error and refuse to render the string.

Throw any kind of fatal error due to the class not existing or similar - as long as we don't fail completely silently as if nothing happened. - Simple. And puts the burden on those doing the crazy things: modules changing filters without upgrade path or developers removing code without letting Drupal know.

Beta phase evaluation

Reference: https://www.drupal.org/core/beta-changes
Issue category Bug because it should not be failing silently.
Issue priority Major because (@catch said so in #152) of the security implications.
Prioritized changes The main goal of this issue is security.
Disruption ??Disruptive for core/contributed and custom modules/themes because it will require a BC break/deprecation/internal refactoring/widespread changes...

This is allowed in the beta because it is a major bug dealing with a priority change: security and the impact is greater than the disruption.

Remaining tasks

User interface changes

API changes

Original report by @sun

#560740-7: "Escape all HTML" filter does not escape any HTML

First pass.

Not sufficient.

CommentFileSizeAuthor
#129 drupal.filter-fail.129.patch14.48 KBsun
#124 drupal.filter-fail.124.patch14.42 KBsun
#117 drupal.filter-fail.117.patch13.09 KBsun
#113 drupal.filter-fail.113.patch20.14 KBsun
#102 filter-module-disabled.png21.52 KBDamien Tournoud
#102 filter-status-report.png24.13 KBDamien Tournoud
#102 filter-text-format.png69.63 KBDamien Tournoud
#100 562694-filter-vanish-security.patch17.77 KBDamien Tournoud
#92 562694-92-filter-vanish-security.patch23.87 KBcarlos8f
#88 562694-filter-varnish-security.patch23.14 KBDamien Tournoud
#86 562694-filter-varnish-security.patch23.52 KBDamien Tournoud
#84 drupal.filter-fail.84-reroll-D6.patch20.5 KBsun
#84 drupal.filter-fail.84.patch34.43 KBsun
#71 disabled_filters.patch14.63 KBcatch
#67 Modules | Site-Install_1269789543096.png5.04 KBcatch
#67 disabled_filters.patch12.81 KBcatch
#65 drupal.filter-fail.65.patch20.42 KBsun
#60 drupal.filter-fail.60.patch20.54 KBsun
#59 drupal.filter-fail.59.patch20.42 KBsun
#52 drupal.filter-fail.52.patch20.01 KBsun
#50 drupal.filter-fail.50.patch16.6 KBsun
#46 drupal.filter-fail.46.patch16.94 KBsun
#45 drupal.filter-fail.45.patch16.93 KBsun
#43 drupal.filter-fail.43.patch16.95 KBsun
#41 drupal.filter-fail.41.patch16.96 KBsun
#38 drupal.filter-fail.37.patch16.95 KBsun
#35 drupal.filter-fail.35.patch17.04 KBsun
#22 drupal.filter-fail.22.patch21.69 KBsun
#19 drupal.filter-fail.19.patch21.77 KBsun
#18 drupal.filter-fail.18.patch19.51 KBsun
#16 drupal.filter-fail.16.patch18.85 KBsun
#12 drupal.filter-fail.12.patch18.86 KBsun
#10 drupal.filter-fail.patch12.27 KBsun
#9 562694-Filters-formats-must-fail-if-they-vanish-9.patch3.29 KBdropcube
#5 module-hooks-filter.patch3.32 KBdropcube
drupal.filter-fail.patch1.79 KBsun
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

sun’s picture

Status: Active » Needs review

1) hook_modules_disabled() needs to disable filters provided by disabled module.

2) foreach is still based on $filter_info(), which possibly won't contain the vanished filter. Copy filter info into the DB, so filter_format_list() gets it?

David_Rothstein’s picture

Subscribing.

We probably also want to provide some warning to the administrator when they disable a module whose filters are actually in use, so they understand what will happen.

This might also solve #161354: Disabling a module which provides a text format leaves the text format behind at the same time...

sun’s picture

Status: Needs review » Needs work

Are you on the security list? I'm not sure whether I'm allowed to attach a patch here.

David_Rothstein’s picture

To clarify for anyone else who is reading this, @sun unpublished the issue in order to bring it to the attention of the security team.

After some discussion, the conclusion was that this qualifies as a definite bug and a security hardening patch, but one that can be posted publicly because it is essentially about helping close off an easy avenue for people to configure their text formats insecurely, rather than a directly exploitable flaw per se.

So, the issue is now published again, and a fix can be worked on here.

dropcube’s picture

Status: Needs work » Needs review
FileSize
3.32 KB

The following patch implements hook_modules_disabled() and hook_modules_uninstalled() in filter module. When modules that provide filters are disabled, the filters are disabled in all the text formats, but not removed from the database, to preserve the settings.

When modules are uninstalled, then all the filters are removed from the database.

I think the warning message is not so clear, need help on that!

Also, we need this fixed #562908: {filter}.module is not saved, in order to get this working as expected.

hass’s picture

+

sun’s picture

Title: Filters must fail if they vanish » Filters and formats must fail if they vanish

To slightly broaden the scope: Text formats also need to fail (return an empty string) in case they vanish.

Status: Needs review » Needs work

The last submitted patch failed testing.

dropcube’s picture

Status: Needs work » Needs review
FileSize
3.29 KB

Rerolled against HEAD.

sun’s picture

Assigned: Unassigned » sun
FileSize
12.27 KB

Hm. Sorry, but we need to re-think this. I briefly talked to Heine about this issue in Paris.

Text formats and filters need to fail when they vanish for any reason. "fail" means to return an empty text instead of skipping a filter.

The same applies to disabling a module that provides a filter, which may be enabled or not (doesn't matter). We will NOT auto-disable any filters upon disabling/uninstalling modules. However, we can and should certainly output a message about text formats that need to be updated (to make them work again).

We always need to iterate over $format->filters, not $filter_info, to be able to identify vanished filters.

All of this has a trivial reason: The list of filters applied as well as the order of filters being applied is critical. We can't simply disable or skip a filter, because that will *always* break the intended filtering logic and process in a text format.

As long as the site administrator does not update/fix a text format, the text format has to fail. The most secure way to fail is to empty the text.

We can additionally implement hook_requirements() to output an error message and bug the administrator until all text formats have been fixed.

The format overview page may additionally colorize those text formats that need to be updated. Which also means we want to implement a helper function that returns a list of broken formats.

Completely revamped patch attached.

sun’s picture

Issue tags: +API clean-up

Tagging.

sun’s picture

FileSize
18.86 KB

This one is going to rock your socks.

1 assertion commented out. Depends on #562908: {filter}.module is not saved.

sun’s picture

Issue tags: +Release blocker

This is the replacement fix (and security hardening) for SA-2008-073, which was not applied to D7.

Thus, tagging as release blocker.

#562908: {filter}.module is not saved is RTBC now, so this one can go in right afterwards (removing the commented out assertion). We really need to move forward, because it's absolutely minimal time left to properly deal with the remaining FilterSystemRevamp issues.

sun’s picture

Issue tags: +D7 API clean-up

Tagging absolutely critical clean-ups for D7. Do not touch this tag.

Status: Needs review » Needs work

The last submitted patch failed testing.

sun’s picture

Status: Needs work » Needs review
FileSize
18.85 KB

Re-rolled against latest HEAD.

sun’s picture

+++ modules/filter/filter.admin.inc	9 Oct 2009 19:17:27 -0000
@@ -16,9 +16,15 @@ function filter_admin_overview($form) {
+  $broken_formats = _filter_get_broken_filters();
+  if ($broken_formats) {
+    drupal_set_message(format_plural(count($broken_formats), 'The highlighted text format needs to be re-configured, because it is using filters that no longer exist.', 'The highlighted text formats need to be re-configured, because they are using filters that no longer exist.'), 'error');
+  }

Weird variable name here ;)

+++ modules/filter/filter.module	9 Oct 2009 19:19:45 -0000
@@ -191,6 +191,16 @@ function filter_format_save(&$format) {
+  // When a text format is saved, we need to assume that its filter configuration
+  // has been verified and we are safe to remove any pointers to vanished filters.

Slightly exceeds 80 chars.

+++ modules/filter/filter.module	9 Oct 2009 19:19:45 -0000
@@ -670,6 +677,50 @@ function filter_list_format($format, $in
+ *   A list of assigned filters that no longer exist, keyed by text text format.
...
+function _filter_get_broken_filters($format_id = NULL, $modules = NULL) {

Double "text" in here.

+++ modules/filter/filter.module	9 Oct 2009 19:19:45 -0000
@@ -696,9 +747,15 @@ function check_markup($text, $format = N
+  // If the requested text format does not exist, we return an empty text for
+  // security reasons. The text stays empty until the assigned format has been
+  // fixed/updated.

s@fixed/updated@re-configured@

+++ modules/filter/filter.module	9 Oct 2009 19:19:45 -0000
@@ -708,25 +765,33 @@ function check_markup($text, $format = N
+    // If a filter ought to run, but no longer exists, return an empty text.
+    else {
+      return '';
     }

Perhaps also mention that we skip caching here (in case $cache is TRUE)?

+++ modules/filter/filter.module	9 Oct 2009 19:19:45 -0000
@@ -1169,3 +1234,38 @@ function _filter_html_escape($text) {
+function filter_modules_disabled($modules) {
+  if ($broken_formats = _filter_get_broken_filters(NULL, $modules)) {
...
+function filter_modules_uninstalled($modules) {
+  if (_filter_get_broken_filters(NULL, $modules)) {

Seems like both do the same, and none of both requires the return value, so we can remove the variable assignment.

+++ modules/filter/filter.test	9 Oct 2009 19:17:27 -0000
@@ -537,6 +537,63 @@ class FilterNoFormatTestCase extends Dru
 }
 
+class FilterBrokenTestCase extends DrupalWebTestCase {

Missing PHPDoc.

+++ modules/filter/filter.test	9 Oct 2009 19:17:27 -0000
@@ -537,6 +537,63 @@ class FilterNoFormatTestCase extends Dru
+  function testDisableFilterModule() {

Would be nice to squeeze some blank lines in here for better readability.

+++ modules/filter/filter.test	9 Oct 2009 19:17:27 -0000
@@ -537,6 +537,63 @@ class FilterNoFormatTestCase extends Dru
+    // @todo Please commit http://drupal.org/node/562908
+    #$this->assertRaw(t('At least one text format contains a filter that was provided by a module that has been disabled. All content using one of these text formats will be hidden until <a href="@text-format-url">text formats have been re-configured</a>.', array('@text-format-url' => url('admin/config/content/formats'))), t('Error message about disabled filter module displayed.'));

Is that committed already?

+++ modules/simpletest/tests/filter_test.module	9 Oct 2009 19:17:27 -0000
@@ -7,6 +7,44 @@
+function filter_test_system_info_alter(&$info, $file) {
+  if ($file->name == 'filter_test') {
+    $info['hidden'] = FALSE;
+  }
+}

What's happening here?

+++ modules/simpletest/tests/filter_test.module	9 Oct 2009 19:17:27 -0000
@@ -7,6 +7,44 @@
+ * Replaces any text with filter and text format information.

+ "entirely"

I'm on crack. Are you, too?

sun’s picture

FileSize
19.51 KB

Fixed all of those issues.

sun’s picture

FileSize
21.77 KB

Discussed with chx in IRC that we additionally want:

1) hook_requirements()

2) watchdog()

sun’s picture

This patch is required for #582378: Filter System clean-up

Status: Needs review » Needs work

The last submitted patch failed testing.

sun’s picture

Status: Needs work » Needs review
FileSize
21.69 KB

Re-rolled against HEAD.

Dries’s picture

This patch seems to make sense.

However, I wonder if it is preferred to avoid people being able to disable a module until its filters have been reconfigured. It feels less damaging, and might be a better pattern to introduce and strive for.

Might also be a lot easier to code.

Just a thought.

sun’s picture

The patch also caters for the possible situation that one simply deletes a module from your module's directory, or, for some reason, the filter module's directory is chmod 000, or, you manually run UPDATE system SET status = 0 WHERE module = ..., or whatever. There are plenty of ways to let a module vanish.

All of these possible scenarios were contained in the original mail I sent to the security list about this issue.

I think that not allowing a module to be disabled is a slightly different issue, which we should target in a more general scope. For starters, I would have no idea on how to get an additional warning or help text into the module configuration list currently.

Dries’s picture

Deleting any module can have severe damaging effects, and it is not something we are currently protecting people against. If you delete node.module, your site will be broken too. It is well understood that physically deleting a module can break your site, and that is why people disable and uninstall their modules first.

In other words, this patch introduces a new babysitting pattern. I'm not necessarily against that, because it is good for security, but it is a new babysitting pattern, and one that introduces some inconsistency and bloat.

Secondly, usability experts will tell you that a pro-active approach (i.e. prevent damage to begin with) is nicer than a retro-active approach (i.e. help people recover after they have done damage).

So while I'm cool with this "clean-up", I'd like to get some feedback from other core developers about this. Let's get some extra people to comment on this issue.

David_Rothstein’s picture

I had been meaning to take a look at this patch but hadn't had a chance yet. No time for a real review now, but a couple quick comments:

  • I agree with Dries that we shouldn't bother worrying about cases where stuff gets randomly changed in the filesystem.... that seems beyond the scope of what Drupal should code for. However, Drupal has an API for disabling modules, and I do think we want to code for the situation where a filter module is disabled via that method. So blocking people from disabling modules via the UI doesn't seem like enough - we still would want to detect something after that, as this patch is doing.
  • I definitely like the idea that this patch is detecting broken formats and warning about them in the UI. I'm less convinced about the part where it "zeroes out" all content in that format until the problem is fixed. It's tricky; for some filters, removing them definitely makes the content meaningless or even a security risk, but for others, this seems like huge overkill. If I disable the Smileys module, I really just want my smiley graphics to disappear, rather than having all my content disappear :) I don't have a great solution for this off the top of my head, though.
  • I'm not entirely sure why the "missing text formats" thing is part of this patch. It seems like that might be better left to #358437: Filter system security fixes from SA-2008-073 not applied to Drupal 7.x - it's really a different problem. For one thing, when the user deletes a text format, we don't zero out existing content in that format but rather display it using the fallback format (e.g., plain text). It seems that whatever we do here should be consistent with that, since the most likely way that someone would try to filter text with a non-existent format is if the format was deleted but some module didn't update their database tables correctly.
David_Rothstein’s picture

Also note the relationship between this patch and #275811: Warn about potentially insecure filter configurations - there, we detect any text format that is configured insecurely. So, just thinking out loud.... perhaps (combining them) we could do something like "if the removal of the disabled module's filter changed the text format from being secure to insecure, that is the only time we zero out the content - otherwise just warn about it in the UI".

That wouldn't be perfect, of course - maybe the site admin is the only one who had permission to use that filter in the first place, so it doesn't matter. And it doesn't cover other, more rare types of security issues that we won't really be able to detect (e.g., exposed PHP code due to the removal of the PHP filter which reveals your company's secrets that were contained in the source code).

Overall, I'd suggest that we focus this patch on the "broken filter" API parts now and making sure they can be committed by October 15? It seems like the other stuff, including actually zeroing out the content, can wait a bit and leave more time to discuss it.

Dries’s picture

Personally, I don't think we should remove functional code from this patch. It's working, and it is an improvement over what we have.

The big question is: should it be proactive, reactive or both?

sun’s picture

Just for the record: I totally disagree with the applied "security fix" in SA-2008-073. From a security and technical perspective, it is utterly wrong. You simply can't just go ahead and display content that is supposed to be filtered without filtering or with an arbitrary other filtering. It does not matter at all, which of both happens. That "fix" completely hi-jacks the filtering system and does the opposite of its intention.

It is simply the wrong fix. And SA-CORE-2009-007 proves that, because user signatures had exactly the same issue. To be filtered content cannot be displayed by using an arbitrary filtering.

However, I understand why it has been done that way for D6 -- because we did not have a solid Filter API that would allow modules to properly react on changes to text formats or filters. But that was the past. We have this facility in D7 now, so there is no point in applying a completely wrong security fix.

David_Rothstein’s picture

Answer: Ideally it would be both proactive and reactive, but if choosing one, reactive...

Is any part of this patch on an October 15 deadline? It's a bug, of course, but I assumed it was on that deadline because of the "API clean-up" tags -- however, looking at the code, the API changes are tiny (and perhaps not even necessary), which would suggest that it is not. If it doesn't need to be done by October 15, then no need to remove any code now.

@sun: I don't disagree with you, but I was just pointing out that whatever we do needs to be consistent. When the administrator deletes a text format intentionally, it doesn't make a whole lot of sense to automatically replace it with some random text format either, but that's what we do in D7 now (and that would be tricky to try to remove). We can't assume that just because D7 has a hook for deleted text formats, every module had a chance to respond to it: For example, a module that stores some data might have been disabled at the time the text format was deleted, so its hook never got called. When that module is enabled again, its data should not be treated any differently than the data stored by modules that were enabled all along.

sun’s picture

Apparently, this issue seems to be blocked by nostalgia. I will merge the required API clean-ups into #582378: Filter System clean-up now, because that stuff absolutely needs to get in on 10/15.

sun’s picture

Issue tags: -API clean-up, -D7 API clean-up

Done. Removing tags.

Dries’s picture

It's not blocked by nostalgia. I'm just asking for the opinion of some more extra people. Nothing wrong with that.

Status: Needs review » Needs work

The last submitted patch failed testing.

sun’s picture

FileSize
17.04 KB

Re-rolled against HEAD.

See #29 for why this patch is required.

sun’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch failed testing.

sun’s picture

Status: Needs work » Needs review
FileSize
16.95 KB
sun’s picture

Note that this patch also fixes a security issue that can happen fairly often:

When a site administrator disables a module that provides a filter that is enabled in a text format, then there is currently no notice or whatsoever that makes the site administrator aware of the fact that he just made his site potentially vulnerable.

This is actually what the included test demonstrates.

sun’s picture

In addition to what I mentioned previously, this patch is also the generic implementation of http://api.drupal.org/api/function/php_disable/7

Hence, if you think that this is all new, then you're wrong. We already apply this very concept, but only to PHP module's automatically created text format, because we thought it's somewhat special (but it is not), and instead of protecting the site from exposing PHP code, we only inform the user that "there may be problems".

sun’s picture

Title: Filters and formats must fail if they vanish » Text formats expose sensitive information and arbitrary user input if filters vanish
FileSize
16.96 KB

Better title. Re-rolled against HEAD.

Status: Needs review » Needs work

The last submitted patch failed testing.

sun’s picture

Status: Needs work » Needs review
FileSize
16.95 KB

Eat this.

Status: Needs review » Needs work

The last submitted patch failed testing.

sun’s picture

Status: Needs work » Needs review
FileSize
16.93 KB

ah, 'administer site configuration' was split into 'administer modules' ;)

sun’s picture

FileSize
16.94 KB

Re-rolled against HEAD.

sun requested that failed test be re-tested.

Re-test of drupal.filter-fail.46.patch from comment #46 was requested by sun.

Status: Needs review » Needs work
Issue tags: +Release blocker, +FilterSystemRevamp

The last submitted patch, drupal.filter-fail.46.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
FileSize
16.6 KB

Re-rolled against HEAD.

Status: Needs review » Needs work

The last submitted patch, drupal.filter-fail.50.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
FileSize
20.01 KB

phew... moving filtered content into field cache bears a lot of issues. I'm very happy that the tests in this patch revealed some bugs.

David_Rothstein’s picture

Since #358437: Filter system security fixes from SA-2008-073 not applied to Drupal 7.x was marked a duplicate of this, for the time being I'm adding the "security.drupal.org" tag to keep track of it.

sun’s picture

And you are in luck - tests passed, this fixes the security issue in the way it should have been fixed, and so this patch is ready to fly. :)

As mentioned in #29 already, the "fix" of SA-2008-073 for Drupal 6 is entirely hi-jacking the filter system, but couldn't be solved properly in Drupal 6. The filter API was improved for D7, so we won't introduce the bug that was introduced with SA-2008-073 in D7.

sun’s picture

Issue tags: +API change

.

catch’s picture

Status: Needs review » Needs work

Please move the hook_filter_format_$op() implementations to text.module - that's where the caching is done, so that's where it should be cleared.

Hunks in this patch are a duplicate of #651086: Cache clearing is an ineffective mess in module_enable() and system_modules_submit(), needs sorting out.

#556022: When a text format is deleted, some modules (like text.module) don't update their data, even though we say they do was a duplicate of this issue.

I'm a bit concerned about watchdog flooding by the patch here - try to show ten text fields with broken formats and you're suddenly getting ten watchdog inserts per page, but then your site is going to look pretty broken too, so I guess it's OK.

sun.core’s picture

Status: Needs work » Needs review
Issue tags: -Release blocker, -Security Advisory follow-up, -FilterSystemRevamp, -API change

#52: drupal.filter-fail.52.patch queued for re-testing.

Status: Needs review » Needs work
Issue tags: +Release blocker, +Security Advisory follow-up, +FilterSystemRevamp, +API change

The last submitted patch, drupal.filter-fail.52.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
FileSize
20.42 KB

Incorporated catch's review issues (moving hook implementations to text module).

sun’s picture

FileSize
20.54 KB

Another patch went in that already added filter_modules_disabled()... didn't know of that.

David_Rothstein’s picture

Hm, if this patch fixes #556022: When a text format is deleted, some modules (like text.module) don't update their data, even though we say they do, it's not obvious to me why - we definitely need some code comments about this in text_filter_format_delete() explaining how it works :) We also should test this. Currently it looks like the filter module has tests for what happens when you delete the text format assigned to a custom block, but since the Field API part is so different (and currently is broken/nonexistent in HEAD), we need tests for that too.

Actually.... the tests that are added by this patch suggest that the expected behavior for deleting a text format is that the node body goes blank, but actually that's not what we usually want, right? Seems like we want to test two different things: If a text format is deleted by the site administrator, text.module properly acts to actually switch the format of the node body (like other modules do) to the new one, but in a different case (e.g., the node body somehow has a text format that never existed), that's when the node body should go blank. I had some old tests as part of the patch at #358437: Filter system security fixes from SA-2008-073 not applied to Drupal 7.x that might help here, although they are probably very out of date now.

In addition, I still don't understand how the "text format does not exist" part of this patch can work for all cases. As I commented in #30:

We can't assume that just because D7 has a hook for deleted text formats, every module had a chance to respond to it: For example, a module that stores some data might have been disabled at the time the text format was deleted, so its hook never got called. When that module is enabled again, its data should not be treated any differently than the data stored by modules that were enabled all along.

As I understand this patch, it would currently result in data loss in those cases (or at least "effective" data loss that requires you to manually resave a bunch of content to get it back) - and that is not acceptable. It would also result in data loss in the case of a module that does not properly implement hook_filter_format_delete(). Granted, that would be a bug in the other module, but it still seems like a tough price to make the site administrator pay for the bug in this way.

Maybe we can do something simple like store a record of deleted text formats somewhere (e.g., in a variable) so that we know what to do when we encounter one? Then, only content with an assigned text format that never existed on the site would get the "display an empty string" treatment, and all other cases would always have the behavior applied that the site administrator indicated when they deleted the format?

sun’s picture

1) #556022: When a text format is deleted, some modules (like text.module) don't update their data, even though we say they do: It's the text_filter_format_update() implementation that cleares the text field cache when a text format is updated. Does that really need explanation?

2) Testing field API: hum? The test that is added here already tests the node body, not a block. I don't understand that point.

3) Different cases for vanished formats: I don't see how this would be possible. If some content refers to format X and X no longer exists, there's only one action that can be taken to stay secure: No output.

4) Formats not updated/adjusted for disabled modules: There is nothing we can do about that. This shortcuts back into 3). To be filtered text in a certain format must not be "simply output" in a different format and different set of filters using a different order and different settings. That's what is not acceptible. Note that the content is not "lost", it just needs to be updated.

5) A variable tracking the mapping of re-assigned deleted formats sounds interesting. However, this variable would have to be used everywhere where text formats are loaded or used. In general, I'm not too sold on the underlying problem in the first place. If you disable a module that stores references to node ids or user ids, and you delete those referenced nodes or users while the module is disabled, then you'll face the very same situation. This really applies to everything. A proper solution would be different: Invoking certain Drupal hooks also for disabled (but installed) modules.

David_Rothstein’s picture

Well, regarding the text module stuff, something is not working right with the patch at least. Here are steps to reproduce:

1. Create an article with some content in the node body and Filtered HTML as the format.
2. Delete the filtered HTML format. The message on the screen says "If you have any content left in this text format, it will be switched to the Plain text text format."
3. Go back to the article you created. The content does not display as plain text - instead, it is completely gone.

Actually, why was #556022: When a text format is deleted, some modules (like text.module) don't update their data, even though we say they do closed as duplicate of this in the first place? I'm not sure why text.module's failure to implement these hooks has anything to do with the issue here - except that it's a useful test case for the rest of the patch :) If it's at all complicated (and it sounds like it might be), I'd suggest reopening that instead.

Re #5, I agree it's a more general problem. On the other hand, this seems way more serious than most of the other examples I can think of. If you're storing a reference to a deleted node or user, you probably don't expect it to display anyway, so it sounds like you'd just get a PHP warning if you tried to but otherwise no difference (and there are ways to code around that if you're careful). But here the content will not be displayed at all, even though the site administrator has every reason to expect it to.

sun’s picture

Wow. Quite some confusion :) I've re-opened #556022: When a text format is deleted, some modules (like text.module) don't update their data, even though we say they do, because it's not at all a duplicate. I was somehow mistaken in that I thought that field/text module would already handle the hook_filter_format_delete() situation. Which also means that the introduced hook implementation here makes no sense at all. Sorry for that.

That also explains why my manual test in #635212: Fallback format is not configurable failed. ;)

re 5): It totally depends on what your disabled module is doing. Sure, when just referencing a deleted node and therefore not loading those records ever again, nothing bad will happen. But if the module actually tries to load all of the records to display them or do whatever, you'll face the very same situation.

Another way to resolve this would be to use Schema API to find all database columns that have a foreign key referring to {filter_format}.format and update all of them in one shot. However, even that wouldn't work for Field API due to its pluggable storage model.

sun’s picture

FileSize
20.42 KB

Without text_filter_format_delete()

Status: Needs review » Needs work

The last submitted patch, drupal.filter-fail.65.patch, failed testing.

catch’s picture

Title: Text formats expose sensitive information and arbitrary user input if filters vanish » Prevent
Status: Needs work » Needs review
FileSize
12.81 KB
5.04 KB

I just looked through this again and I really dislike the idea of allowing people to do something unfortunate, then flashing big warnings at them and breaking all their site content once they've done it. I also fully agree with David and Dries that we shouldn't cater for people removing modules from their filesystem or disabling them with SQL, tough luck if you do that.

So here's an alternative approach, based on #23 from Dries:

However, I wonder if it is preferred to avoid people being able to disable a module until its filters have been reconfigured. It feels less damaging, and might be a better pattern to introduce and strive for.

Might also be a lot easier to code.

Yes, it's much easier - it's actually about 5 lines of real patch, the rest is indentation and another bug fix (see below...).

If a filter is enabled, filter module, in hook_system_info_alter() adds the providing module as a dependency. So if I enable the 'youtube_filter' module, add that filter to a text format, then go to the modules page, I can see that filter module now depends on 'youtube_filter', and that the checkbox is disabled. It's actually the case here that filter module depends on that module, because it has a text format which uses that's module's filter, so although it's a dynamic dependency, I believe it's a proper use of the dependency system and of this hook. It also happens to be the only way I can think of to do this since there's no other way to stop a module being disabled afaik.

There's one snag however. In system_modules() we unset() all required and hidden modules, in doing so, we also prevent dependency information from being shown for those modules, which breaks this patch. To fix that isn't hard, instead of unsetting those modules, just don't render them instead - this allows dependency information to be displayed properly.

However, there's an additional issue that "standard" install profile lists several optional core modules as dependencies, and suddenly these get greyed out checkboxes as well. See screenshot. I've fixed this in the patch by implementing hook_standard_system_info_alter() to reset all the optional modules it depends on to not be required (which will obviously only happen after install) - this way they can be disabled from the modules page again. standard.profile is a special case, since it doesn't really depend on these modules, just recommends them, so I think this is reasonable. I'm including these fixes here becuase they're quite small, and because this approach is fairly impossible without them.

I'm sure someone will complain about the magic dependency setting being a WTF, but compare this to the wtf of all your site content suddenly disappearing and having to go to admin/config/content/formats to get it back, I think it's a considerably smaller wtf.

catch’s picture

Title: Prevent » Prevent filter-providing modules from being disabled if a text format depends on them

Status: Needs review » Needs work

The last submitted patch, disabled_filters.patch, failed testing.

sun’s picture

Title: Prevent filter-providing modules from being disabled if a text format depends on them » Text formats expose sensitive information and arbitrary user input if filters vanish

How does this handle major upgrades of Drupal?

catch’s picture

Title: Text formats expose sensitive information and arbitrary user input if filters vanish » Prevent filter-providing modules from being disabled if a text format depends on them
Status: Needs work » Needs review
FileSize
14.63 KB

Fix that test.

sun’s picture

Title: Prevent filter-providing modules from being disabled if a text format depends on them » Text formats expose sensitive information and arbitrary user input if filters vanish

Additionally, this "new approach" does not resolve SA-2008-073, which, as mentioned earlier, was not applied to D7 in favor of this proper solution.

I'm not comfortable with and not convinced of the recent change in direction.

catch’s picture

A text format being deleted is a completely different issue to a filter-providing module being disabled (while the filter is being used by an active text format). If this needs to be two separate issues, that's fine with me.

sun’s picture

It doesn't matter in which way a text format or filter vanished. What matters is that we are not able to filter and output text that has to be filtered.

Dead simple.

I'd be ok to merge this new patch into the previous one. Thus, preventing users to disable filter-providing modules, but still ensure we never ever ever never ever never ever output a to be filtered text in a wrong, incomplete, or in any other way broken format, and warn about such broken configurations.

catch’s picture

edit: this was a crosspost with #74

Looks like we cross posted a couple of times. In the case of major upgrades of Drupal, you should ensure your contrib modules are also up to date. If you don't update modules which your site depends on, then that's much the same as just removing them from the filesystem, and I don't think we need to add a tonne of code to core to support that case.

sun’s picture

Again, I'm fine with merging both patches.

Another reason for the security issue is that we simply have to admit that D7's data storage became more fragile and unreliable. #556022: When a text format is deleted, some modules (like text.module) don't update their data, even though we say they do reveals that we cannot guarantee that even most simple data operations will actually happen. I'm not sure whether I really need to find more possible ways to invalidate and break the logic for to be filtered text.

The actual code added by the patch that is executed at regular run-time is minimal. Also, the argument is moot, compared to the security implications of displaying text in a wrong/incomplete/broken format.

catch’s picture

edit: and this was a crosspost with #75.

It doesn't matter in which way a text format or filter vanished. What matters is that we are not able to filter and output text that has to be filtered.

No, it does matter. If you completely bypass core documentation, UI and APIs, core doesn't protect you from your site being broken, whether that's potential security issues or a WSOD. I don't see any reason to change that pattern here.

There are potential security implications if you wipe a filter-providing module from your filesytem, or it might be that your text doesn't get filtered on pirate day any more. However in both cases there's not a reproducible exploit - it requires mis-administration to get into that state.

So while I fully agree we want to prevent people from getting doing this via the UI (especially without even a warning as it was prior to that SA going in), there's very little motivation to add code to core to support those not following best practices. That goes beyond a bit of babysitting to a full padded cell.

The one place I wouldn't mind that additional check is in filter_requirements() - we can easily check the contents of {filter} to see if there's an enabled filter for a disabled/missing module in there. That would be minimal runtime code and would warn sites upgrading from D6 with inconsistent data.

sun’s picture

So can you explain why SA-2008-073 contained a security fix for this?

This patch is not babysitting broken code. This patch ensures that the intended and expected and assumed filter processing logic is actually valid and can be applied.

Which other thing works with broken data? This is not about some silly field, that may still be displayed by falling back to the default formatter or whatever. This is about dependent and required information to properly execute the code in the first place.

If you want to remove the watchdog messages, requirement, and UI notice, then be it, fine by me, we can just leave users with the WTF.

But the essential part of this patch is not debatable:

To be filtered text cannot and must not be output in a wrong/incomplete/broken format.

sun’s picture

Also, your statements here and in http://drupal.org/node/556022#comment-2778216 are totally contradictory. Over there: "let's just not support it". Here: "it cannot happen". Ideal preconditions for breakage.

David_Rothstein’s picture

I took a look at the patch - overall the simplicity is very nice and it definitely hits the most common use case (a filter disappears because the module that provides it was disabled via the UI), but I'm not sure if it gets enough of them. What about if someone disables a module using Drush? What about someone playing around with http://drupal.org/project/flexifilter? Those are valid things for a site administrator to do and are not quite as edge-case-y as someone deleting files in the filesystem. So I think I'm somewhat in agreement with @sun that this isn't quite enough, but on the other hand, one could argue that it's up to those modules to handle those situations - or (as @catch eventually suggested) at least keep hook_requirements() around to inform the administrator about them.

Some other notes:

  • The patch is maybe not as smart as would be ideal - e.g., if I turn on the PHP module and immediately want to disable it 5 seconds later, it doesn't let me because the filter is already "in use" (in the text format that the PHP module just created). I think the earlier patches had this kind of problem too, but it was less in-your-face since it didn't prevent disabling the module, just displayed warnings.
  • The UI here is confusing since it doesn't indicate how you go about breaking the dependency - how do I know that I need to reconfigure my formats and remove all this module's filter before I can disable it?
  • Finally, I don't understand why standard_system_info_alter() is special here... wouldn't any install profile have to do this (since they all now are supposed to declare dependencies in their .info file)?

***

Rereading this issue, I'm not sure I've changed my opinion much since #26. It feels like we need to define a middle ground of how paranoid to be, and then design the behavior around that. And we have to think somehow about different types of filters. For some of the filters that were mentioned above (Smileys, Pirate, etc) it is painful that we'd be requiring administrators to go and manually remove them from all formats by hand before they could (a) have their site work (@sun's approach), or (b) disable the module (@catch's approach). I'd really like to just be able to disable the Smileys module and have my smileys automatically go away :)

Have we considered doing this with a confirm form tacked on the module submission page? So maybe if I disable a module whose filters are in use, I could just be given an extra screen that asks me if I'm sure I want to do that, because removing the filter will change the behavior of text formats X, Y, and Z? And then assume they know what they're doing if they go ahead submit the form? That plus hook_requirements() to catch the edge cases seems like it might not be too bad - or beyond hook_requirements(), we could even go further and drupal_set_message() a specific message onto every admin page like update.module does, since this is a potential security issue just like that one is. I think this might be enough to be able to avoid actually zeroing out any content.

David_Rothstein’s picture

Re #78:

So can you explain why SA-2008-073 contained a security fix for this?

The security fix in SA-2008-073 was because Drupal told site administrators one thing but actually did another. When they went to delete a text format, it told them that all content using that format would be replaced with the default format, but in fact, any content using that format that was stored by a non-core module would actually be displayed with no filtering at all - i.e., it was essentially treated as Full HTML no matter what.

catch’s picture

@sun. Please explain #79. I said here that we should not allow filter modules to be disabled which have filters in use. I said on the text format issue that we should not allow text formats to be deleted which are in use. These are entirely consistent positions. You may not like them, but they're consistent. In all cases there is no ideal behaviour, so I would prefer we did the simpler solution that doesn't involve completely breaking people's sites by either hiding all their content (here) or batch updating potentially millions of items (there) (especially when we have no way to differentiate between HTML escaping and smileys as David points out).

David is correct about the security issue. Core doesn't have to account for every possible eventually that could ever happen in relation to security, we still ship with PHP module ffs, it just has to not be completely misleading, and ideally make it somewhat difficult to end up in an insecure state.

@David. Given we've not prevented this in the past, hook_requirements() seems sensible. I'm less happy with drupal_set_message() on every admin page, update.module hacks that in via a hook_help()...

I also think it's fine to expect drush and/or flexifilter to sort this out themselves.

On PHP module being immediately a dependency issue when writing the patch. I'd prefer it if PHP module didn't automatically create its own text format, most filter modules don't do this - they allow you to apply their filter to existing text formats, I don't see why PHP module is any different. There are also modules which only show PHP textareas for certain functionality if PHP module is enabled, so it's possible to enable it without wanting the format at all.

# The UI here is confusing since it doesn't indicate how you go about breaking the dependency - how do I know that I need to reconfigure my formats and remove all this module's filter before I can disable it?

Currently you don't, and we have no mechanism for explain this. We could add to filter help text, if we have a hook_requirements(), we could also add a non-warning status showing the current filters in use. Not sure there's much else we can do within existing core APIs.

# Finally, I don't understand why standard_system_info_alter() is special here... wouldn't any install profile have to do this (since they all now are supposed to declare dependencies in their .info file)?

Well the question is whether they're actual dependencies or just suggestions, as far as I can see the only reason this works as it does currently is due to a bug. If you have a profile with hard dependencies, these are ignored due to the current bug. If you have a profile with suggestions/defaults, these will be enforced if the bug is fixed. Needs its own issue.

David_Rothstein’s picture

Well the question is whether they're actual dependencies or just suggestions, as far as I can see the only reason this works as it does currently is due to a bug. If you have a profile with hard dependencies, these are ignored due to the current bug. If you have a profile with suggestions/defaults, these will be enforced if the bug is fixed. Needs its own issue.

My understanding is that "dependencies" is the new way for a profile to list the modules it wants Drupal to automatically install... I think this was introduced in #509392: Introduce .info files for install profiles. It might be by design.

So in other words, "dependencies" sort of means different things for an install profile than it does a module :/ Profiles might not have any way to enforce a "hard" dependency except by altering in a 'required' flag on the module or some such.

sun’s picture

As mentioned before, we can merge both patches.

Status: Needs review » Needs work

The last submitted patch, drupal.filter-fail.84.patch, failed testing.

Damien Tournoud’s picture

Status: Needs work » Needs review
FileSize
23.52 KB

Rolled. This one should pass (the previous one was trying to disable the filter_test module that is required by the filter module... through the UI).

Status: Needs review » Needs work

The last submitted patch, 562694-filter-varnish-security.patch, failed testing.

Damien Tournoud’s picture

Status: Needs work » Needs review
FileSize
23.14 KB

Retrying.

YesCT’s picture

Status: Needs review » Needs work
Issue tags: +Release blocker, +Security Advisory follow-up, +FilterSystemRevamp, +API change

The last submitted patch, 562694-filter-varnish-security.patch, failed testing.

YesCT’s picture

Status: Needs work » Needs review

why did it say failed testing, looks like it passed testing?

carlos8f’s picture

+++ modules/system/system.module
@@ -2271,6 +2271,8 @@ function _system_rebuild_module_data() {
   // The install profile is required.
   $modules[$profile]->info['required'] = TRUE;
+  $type = 'profile';
+  drupal_alter('system_info', $modules[$profile]->info, $modules[$profile], $type);

The immediate thing I see here is that this is adding a second info alter on $modules[$profile], the first having type = 'module' (inside the foreach), the second with type = 'profile'. It seems redundant to process the profile as both.

If 'profile' is a possible value of $type, it's a minor API change and needs a hunk in system.api.php. However I think it points to an inconsistency though that we store {system}.type = 'module' for profiles but special case them like this, with $type being 'profile' here. Also, it looks like scope creep because erasing standard.profile's dependencies[] during runtime is pretty far from filter.module and the title of this issue.

I offer an alternative where $type is still 'module' but the filename is checked instead of $type inside the info alter implementation. Checking $file->filename extension is a convention we seem to have going for determining if a 'module' is a profile (see #808162: update.php fails when optional modules disabled). Hopefully in D8 we can find a better way.

lol @ filename of #86 patch, I admit that I read the same thing at first.

65 critical left. Go review some!

carlos8f’s picture

Hmm, and what if you're not using standard.profile? Instead of every profile having to info alter their dependencies[] out, can we just do it from system.module for the current profile?

matt2000’s picture

Priority: Normal » Critical
Issue tags: -Security improvements +Release blocker, +Security Advisory follow-up, +API change

I'm reviewing this, and the patch in it's current status seems to contradict itself. Why do we have the messages in hook_modules_disabled and hook_modules_uninstalled to warn about filter modules that have been disabled, when the patch tries to prevent module form being disabled? I.e., if the patch is working, we should never see those messages.

I agree with David that it's disconcerting to enable PHP module and then find that you can't disable it because it magically got a dependency, and doesn't tell me how to break that. In essences, if someone ignorantly turns on PHP module, we're making it harder for them to correct their mistake, so we're actually making Drupal MORE vulnerable to security errors in the hands of someone who doesn't know better. You can't argue that PHP module is too dangerous to ship with core on the one hand, and then make it impossible to turn it off on the other.

This also breaks convention, as I have never before seen a module gain a new dependency after being enabled. The hook_system_info_alter just feels like a hack. Perhaps we need a more general way to define a dependency for a module which is not another module (e.g., a setting/variable, or external library, etc), and let that dependency notice link to a relevant URL to guide the user in addressing the dependency.

I think the best compromise between user experience & security would be to present a confirmation form that says, 'Are you SURE you want to disable that, because you're going to break these text formats: PHP, etc,....' How difficult is that to do? Do any of the prior patch come close to that? (I only reviewed the newest patch.)

catch’s picture

Since #820054: Add support for recommends[] and fix install profile dependency special casing has been moved to D8 by webchick, we should look at a centralised hack for this yes. System module doing an alter would be easier to rip out later than checking the filename somewhere in system_modules().

Also there are hunks in the current merged patch which are unnecessary if we do filter_system_info_alter(), for example:

 function filter_modules_disabled($modules) {
   // Reset the static cache of module-provided filters, in case any of the
   // newly disabled modules defined or altered any filters.
   drupal_static_reset('filter_get_filters');
+
+  if (_filter_get_broken_filters(NULL, $modules)) {
+    drupal_set_message(t('At least one text format contains a filter that was provided by a module that has been disabled. All content using one of these text formats will be hidden until <a href="@text-format-url">text formats have been re-configured</a>.', array('@text-format-url' => url('admin/config/content/formats'))), 'error');

Which once again makes me ask the question that if we're not going to run into this when disabling modules, only when they're deleted from the file system, do we really need all that code for something which happens once in a blue moon, and which only if you're very unlucky is going to cause a problem in the first place, and with the incredibly confusing side-effect of all your content disappearing.

catch’s picture

Status: Needs review » Needs work

cross-posted with matt2000

We can easily fix the PHP module issue by stopping it from creating a format when it's enabled. That's 4.6-era behaviour that should just stop.

catch’s picture

Removing the PHP format is worth it in its own right, so opened #826550: PHP module should not create a text format.

grendzy’s picture

Big +1 for this approach. I'm using the htmlpurifier module instead of the core html filter, and have lost a bit of sleep wondering what will happen if some future admin carelessly disables the purifier module.

Regarding the hook_requirements message, I think it would be much more helpful if it named the modules instead of just a generic warning (to do so easily may require keying _filter_get_broken_filters differently).

YesCT’s picture

todo list?

I'm not sure what parts of #94 and #95 were reviews of the previous patch or descriptions of the new approach moving forward.

from #96:
We can easily fix the PHP module issue by stopping it from creating a format when it's enabled.

from #98:
Regarding the hook_requirements message, I think it would be much more helpful if it named the modules instead of just a generic warning (to do so easily may require keying _filter_get_broken_filters differently)

Damien Tournoud’s picture

Status: Needs work » Needs review
FileSize
17.77 KB

I'm pretty much -1 on the "add a dependency to the filter module for all the modules that provide a filter that is currently used". It's just:

  1. confusing,
  2. completely unnecessary: because we have other code in place that protects text formats from outputting sensitive information when on of the filter is not available anymore,
  3. insufficient anyway, because filters can disappear for other reasons (a module is removed from the filesystem, a module stop providing a filter, etc.)

I strongly prefer the soft approach here, so here is a reroll of the patch without the parts that mess up with dependencies.

Damien Tournoud’s picture

A summary of what this patch actually does:

  • Implements a way to detect "broken" text formats (formats that rely on filters that are not available)
  • Adds an entry to the status report when at least one text format is "broken"
  • Display a warning to the user when a module that provided a filter that is used on one or more text formats is disabled or uninstalled
  • Disable the output from any text format that is broken (ie. returns an empty string), to avoid exposing sensitive information
Damien Tournoud’s picture

Some screenshots:

When disabling a module that provides a filter that is currently in use:

The status report:

The text format page:

Damien Tournoud’s picture

Assigned: sun » Unassigned
Priority: Critical » Normal

We discussed that with catch today, and came to the conclusion that both this patch (which makes it more obvious when a filter vanishes) and the previous approach (adding a dependencies to the module that provide a filter) might be more harmful then the problem they are initially trying to solve.

Because I don't believe this is such an issue anyway, let's just downgrade this. If someone comes with a proper solution, let's keep consider this for Drupal 7, but this doesn't have any of the characteristics of a release blocker.

catch’s picture

Clarifying in the tags that this is a security improvement, not an SA followup.

catch’s picture

Issue tags: -Release blocker

Clarifying in the tags that this is a security improvement, not an SA followup.

chx’s picture

In short, fixing such a weird edge case while useful security hardening is not a critical issue and it takes an insane amount of fairly ugly code.

David_Rothstein’s picture

Priority: Normal » Critical

#358437: Filter system security fixes from SA-2008-073 not applied to Drupal 7.x was marked duplicate of this, so this can't be downgraded unless you also decide to "un-duplicficate" that one...

Damien Tournoud’s picture

Priority: Critical » Normal

No, we already have the pieces needed to cover SA-2008-073:

  • When a text format is deleted, all the content is changed to the fallback filter
  • If check_markup() is called with an unknown text format, it just returns ''
sun’s picture

Version: 7.x-dev » 8.x-dev

Hm. Not fully. As stated in #79,

1) We try to switch existing content to a fallback format, but we do not want to guarantee it, if I understood people correctly. Essentially #556022: When a text format is deleted, some modules (like text.module) don't update their data, even though we say they do

2) Not in HEAD yet. Has been part of this patch, but http://api.drupal.org/api/function/check_markup/7 is not failsafe.

The essential part of the SA was 2). This patch simply advanced on that logic only -- if an invalid text format can be a security issue, then an invalid input filter can be a security issue, too.

I think I'd also be comfortable to just fix the missing/invalid text format part for D7 and leave the additional security hardening for input filters for D8, so as to fix the SA in HEAD and find a proper solution for processing of data with dependent foreign configuration properly later, as I'm quite sure that we will face exactly the same problem space with Field API in the long run.

I'd suggest to re-open the other duplicate issue and extract the lines applying to text formats from this patch into a new one over there.

philbar’s picture

Issue tags: +beta blocker

tag

catch’s picture

Issue tags: -beta blocker

Removing tag, this is Drupal 8 now.

philbar’s picture

This is still posted on the initiative page:

http://drupal.org/community-initiatives/drupal-core

Can someone with permissions remove it?

sun’s picture

FileSize
20.14 KB

Re-rolled against HEAD.

nyirocsaba’s picture

#38: drupal.filter-fail.37.patch queued for re-testing.

mlncn’s picture

Version: 8.x-dev » 7.x-dev

Marking this Drupal 7, for the moment, in the hopes that this will make the test bot run, especially as #556022: When a text format is deleted, some modules (like text.module) don't update their data, even though we say they do might be simplified with this.

Status: Needs review » Needs work

The last submitted patch, drupal.filter-fail.113.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
Issue tags: -API change
FileSize
13.09 KB

Re-rolled against HEAD. This patch no longer contains an API change - all originally contained API changes have been committed through other patches in the meantime.

Instead, this patch can be summarized as follows in the meantime:

For security reasons, disabling a Filter module should (temporarily) disable all text formats that actively used the input filter the disabled module provided. An example for this is PHP module, which currently just warns the user upon disabling the module.

This patch removes that custom behavior of PHP module and replaces it with a generic implementation in Filter module, and additionally makes sure that the warning is only output if the filter has been actively used, and the warning keeps on appearing until all affected text formats have been properly updated. Unless a text format has been updated, we cannot use it for filtering, so an empty string is output.

The screenshots in #102 are still valid. I'd call this patch RTBC, if bot passes.

sun’s picture

Assigned: Unassigned » sun
mlncn’s picture

Status: Needs review » Reviewed & tested by the community

Tested with disabling the PHP module, nice alert message, and PHP text format content goes blank. All one has to do to bring the content back is save the text format, but, we've been warned, at least twice, by that point.

sun’s picture

#117: drupal.filter-fail.117.patch queued for re-testing.

catch’s picture

Status: Reviewed & tested by the community » Needs work
+    // When a text format is updated, we need to assume that its filter
+    // configuration has been verified and we are safe to remove any pointers
+    // to vanished filters.

Code comment should not use first person plural, just say what happens and why.

+        $filter->status = -1;

Should be a constant rather than a magic number.

function _filter_get_broken_filters()

+  // Compile a list of vanished filters.

Is the filter broken because it's missing, or would the text format the filter is assigned to be broken? Also did the filters really vanish? Or were they disabled intentionally? Both the code comments and function naming don't accurately convey what the potential issues are leading to this situation.

Also I still dislike strongly that we just let people get into a broken state here when it's a situation that could be more or less easily prevented, so would not want to see this go in without at least a Drupal 8 followup to fix the limitations on preventing modules being disabled in the first place (i.e. #67 onwards).

sun’s picture

Version: 7.x-dev » 8.x-dev
Priority: Critical » Normal
Issue tags: -Release blocker, -Security Advisory follow-up, -API change +Security improvements

Although badly needed, this is D8 material according to the rules (I had to learn today). It may be backported at a later point in time (though that's unlikely).

chx’s picture

Version: 8.x-dev » 7.x-dev

This is a bugfix. You do not help by pushing bugfixes to 8.x needlessly yet.

sun’s picture

Status: Needs work » Needs review
FileSize
14.42 KB

Incorporated #121.

sun’s picture

Any other reason to hold this off?

mlncn’s picture

Status: Needs review » Reviewed & tested by the community

Good to go!

sun’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Security improvements, +FilterSystemRevamp

The last submitted patch, drupal.filter-fail.124.patch, failed testing.

sun’s picture

Status: Needs work » Reviewed & tested by the community
FileSize
14.48 KB

Re-rolled against HEAD.

sun’s picture

sun’s picture

sun’s picture

Issue tags: +API addition

Patch introduces internal API functions, so tagging accordingly.

sun’s picture

#129: drupal.filter-fail.129.patch queued for re-testing.

webchick’s picture

Status: Reviewed & tested by the community » Needs review

I'd still like to see some reviews here from people like Damien and David Rothstein who were involved in earlier incarnations of this patch but haven't spoken up for the current one.

This feels like too much of a behaviour change to insert this late (and even in October when this was first marked RTBC) to me, but if the consensus is the security improvement is worth it, we can discuss, as long as we wrap this up by RC1.

David_Rothstein’s picture

Status: Needs review » Needs work

I re-read this issue and the latest patch. I'm mostly in favor (it is definitely a security improvement), but I don't think we've reached consensus on a couple issues yet.

  1. If we're going to render their site broken after disabling one of these modules, we really need to warn people before they disable the module (as several of us suggested above). It's not enough to put a red "by the way your site is now broken" message after the fact; people will have no idea what hit them. We need a confirm form on the modules page. If it's really hard to have filter.module alter that in, I assume it would be fine to have the code live in system.module for D7, and improve it in D8.
  2. I'm still suspicious of the part of this that has check_markup() refuse to display any content for the formats that have missing filters. That makes perfect sense for some filters, but no sense at all for others. It is still crazy that with this patch, disabling the Smileys or Pirate modules (the examples a couple of us brought up above) would potentially cause Drupal to stop displaying all of your site's content until you went through a ritual of clicking through a bunch of text format configuration forms. Maybe in D8 we can have a way to categorize filters that makes it possible to do this more granularly, but right now it's an extremely blunt tool to be applying to all of them.

    That said, I think I'm not totally opposed to it, provided we do the confirm form above - that would at least make it consistent with the behavior we now do when a text format is disabled/deleted (even if neither of those behaviors always make sense). However, I don't think we should be under the illusion that it really provides much security by itself. If they don't understand what's going on and simply re-save the text format without making any changes, they've then managed to convince check_markup() to start displaying their content again, but whatever security problem they might have had without this patch would start appearing right then anyway.

    It is more important that we educate people than try to directly protect them.

The patch itself has a couple issues, one major:

  1. It seems to be checking for broken filters even on disabled text formats! (I discovered that because somehow I wound up in a scenario where it showed me the warning message even though none of my text formats had any problems; it seems to be because I had disabled the PHP code text format with the filter still in it, then disabled the PHP module.) That needs to be fixed.
  2. Everywhere the patch uses "re-configured" it can be "reconfigured" instead.
  3. @@ -262,6 +283,7 @@ function filter_format_save($format) {
         cache_clear_all($format->format . ':', 'cache_filter', TRUE);
       }
     
    +  // Clear the filter cache whenever a text format is deleted.
       filter_formats_reset();
    

    That should say "saved", not "deleted"?

sun’s picture

  1. I understand the idea of warning a site administrator before performing the action, but I don't understand how it can be a requirement for this patch, since current HEAD does not warn you at all, neither before nor after performing the action.

    Since there is no such confirmation form facility for the enable/disable tasks yet, and even more so, there's not even a known concept for one module altering in a validation/confirmation step for a form of another module, I suspect that this additional protection cannot be solved for D7.

    It would be nice if we could understand the current patch as a major improvement compared to the current situation. There's always room for improvement.

    (That said, one of my goals for D8 already is to entirely revamp confirmation forms.)

  2. To harden security for certain filters only, we'd have to "categorize" filters in the first place. That's a giant challenge of its own, which I doubt we'll be able resolve anytime soon. D8 or D9 material.
  3. The notion of disabled text formats did not exist when this patch was written...

...and the current behavior of disabled text formats invalidates the entire current patch, or vice-versa, the intended behavior of this patch is not compatible with the behavior of disabled text formats that suddenly appeared. That is, because disabled text formats entirely disappear from Filter module's administration UI.

So while this issue and patch would probably a very good place to think once more through the actual behavior of disabled text formats in terms of security, there are more dedicated issues touching that topic in the queue already.

mattyoung’s picture

,~

David_Rothstein’s picture

1. There is already a confirm form on the modules page that warns you when something unexpected (e.g., enabling modules you didn't ask for) is going to be done. With this patch, Drupal would actively stop displaying your content when certain modules are disabled, so that should be confirmed also. Since filter is a required module in D7, it shouldn't be a big deal if it can't be altered in; I think _filter_get_missing_filters() should probably be turned into a public function anyway, in which case system.module could simply call it.

3. Why do you think disabled text formats invalidate the entire patch? I think you just need to make sure _filter_get_missing_filters() ignores them (or at least ignores them by default). In that case, they will remain pristine in the database, with the original set of filters they last had. If someone wants to write a module to allow them to be reenabled, they could either deal with this issue themselves, or just let it be, and then in that case the newly re-enabled format won't start working until it's been verified/saved by the site administrator, which seems to be what we would want.

sun’s picture

  1. Why? Disable an existing text format having a filter enabled. Disable the module providing that filter. Try to update your text formats in order to ensure they are still secure. (Disabled text formats are still actively used.)
David_Rothstein’s picture

Why? Disable an existing text format having a filter enabled. Disable the module providing that filter. Try to update your text formats in order to ensure they are still secure. (Disabled text formats are still actively used.)

I am not sure what you mean by "actively used"...

The bug I reported above is that currently the patch will report disabled text formats as insecure when they contain missing filters. Since disabled text formats can't be accessed via the the UI to reconfigure them, and are protected by check_markup() which already returns an empty string whenever they are used (regardless of what filters they contain), we should just need to make the patch ignore them, so it doesn't display bogus warning messages in the above situation. Isn't that all we need to do to fix that - make _filter_get_missing_filters() ignore them by default?

sun’s picture

Since disabled text formats can't be accessed via the the UI to reconfigure them, and are protected by check_markup() which already returns an empty string whenever they are used (regardless of what filters they contain), we should just need to make the patch ignore them, so it doesn't display bogus warning messages in the above situation.

No, that's the actual problem: Disabled text formats are no longer accessible in the admin UI. When disabling an input filter module that is enabled in a disabled text format, then check_markup() will return an empty string for content that is still using the disabled text format. But you cannot re-configure the disabled text format, because it doesn't appear in the admin UI. And we also cannot exclude disabled text formats from this security hardening.

The notion of disabled formats has been introduced only a couple of weeks ago. It's possible that we still need to introduce a "re-enable" facility, as proposed in #949220: Text format names can never be reused (Possible solution: Allow disabled text formats to be re-enabled), to resolve this.

sun’s picture

Version: 7.x-dev » 8.x-dev
jibran’s picture

Status: Needs work » Needs review
Issue tags: -Security improvements, -FilterSystemRevamp, -API addition

#129: drupal.filter-fail.129.patch queued for re-testing.

Status: Needs review » Needs work

The last submitted patch, drupal.filter-fail.129.patch, failed testing.

mgifford’s picture

Assigned: sun » Unassigned
Issue summary: View changes
YesCT’s picture

Issue tags: -Release blocker

Release blockers are by definition Critical.. so I dont think this is release blocking.

alexpott’s picture

Title: Text formats expose sensitive information and arbitrary user input if filters vanish » Text formats expose sensitive information and arbitrary user input if a filter plugin vanishes
Priority: Normal » Critical
Issue tags: +filter, +filter plugin, +text formats, +Security

Retitling. This is critical since modules that provide filter plugins that are used in filter format should be able to be uninstalled. See discussions in #2348925: Uninstalling a filter plugin removes text formats

catch’s picture

Wim Leers’s picture

AFAICT this problem from back in 2009 has indeed been resolved :)

catch’s picture

Status: Needs work » Closed (duplicate)
Related issues: +#2348925: Uninstalling a filter plugin removes text formats

@alexpott agreed with #149 and #150 in irc, I think we can mark this duplicate of #2348925: Uninstalling a filter plugin removes text formats.

catch’s picture

Title: Text formats expose sensitive information and arbitrary user input if a filter plugin vanishes » Text formats should throw an error and refuse to process if a plugin goes missing
Priority: Critical » Major
Status: Closed (duplicate) » Active

Actually we missed a bit from here in #2348925: Uninstalling a filter plugin removes text formats.

I think we still need to resolve the behaviour in the following case:

1. A text format is configured to use a plugin

2. The plugin disappears.

There are now many less instances where a plugin can disappear, but not quite all of them:

1. The plugin could be removed or renamed in a contrib module with no upgrade path
2. The contrib module is removed from the system entirely without running uninstall

In either of those cases, when trying to run a text format, it should cause both an error and refuse to render the string.

Neither of those cases are 'allowed', but still seems worth warning people about it.

Wim Leers’s picture

Neither of those cases are 'allowed', but still seems worth warning people about it.

I was going to say exactly that as a reason not do to this. But I think you're right; even in these unsupported edge cases, Drupal should remain safe.

So close! :)

catch’s picture

I'd also be OK if the current behaviour was to throw any kind of fatal error due to the class not existing or similar - as long as we don't fail completely silently as if nothing happened.

Wim Leers’s picture

I like that idea. Simple. And puts the burden on those doing the crazy things: modules changing filters without upgrade path or developers removing code without letting Drupal know.

YesCT’s picture

Issue summary: View changes

gave a stab at an issue summary.
Could probably use some tightening up by someone more familiar with the issue.

Also started on beta evaluation. What would the disruption be?

YesCT’s picture

Issue summary: View changes

html nit.

Berdir’s picture

We already have this in ProcessedText::preRenderText():

    // If the requested text format does not exist, the text cannot be filtered.
    /** @var \Drupal\filter\Entity\FilterFormat $format **/
    if (!$format = FilterFormat::load($format_id)) {
      static::logger('filter')->alert('Missing text format: %format.', array('%format' => $format_id));
      $element['#markup'] = '';
      return $element;
    }

What if we should simply use very similar pattern when a filter plugin is missing. Log, drop text, move on. Could possibly result in a huge amount of log entries, but we can't change that unless you really want to break the whole page, which could be dangerous as it might be hard to get to the settings page.

catch’s picture

Priority: Major » Normal

#158 is a good plan.

Now that this is 'illegal' via configuration dependencies etc., I'm going to downgrade this issue to normal.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

John Pitcairn’s picture

If a module has provided a filter plugin for some purpose during development, then wishes to remove that plugin entirely without uninstalling the module, is there any documentation on how to do so?

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.8.x-dev

Drupal 8.6.x will not receive any further development aside from security fixes. Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

geek-merlin’s picture

Wim Leers’s picture

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

catch’s picture

Status: Active » Closed (outdated)

Yes I think #2015313: Missing filters result in Exception when the format is used was even an improvement over a previous issue that did what this suggests. Closing as outdated.