After updating to the latest version of google_tag, following code might break stuff when clearing the cache before running updb. This because the \Drupal::config('google_tag.settings')->get('uri') is still empty and will execute @file_unmanaged_delete_recursive('/');:
/**
* {@inheritdoc}
*/
public function createAllAssets() {
// Remove any stale files (e.g. module update or machine name change).
@file_unmanaged_delete_recursive(\Drupal::config('google_tag.settings')->get('uri') . '/');
This is the script we use to deploy our code:
simple 02-Apr-2019 08:32:36 # Cache rebuild
simple 02-Apr-2019 08:32:36 /opt/php/7.1/bin/drush cr
simple 02-Apr-2019 08:32:36
simple 02-Apr-2019 08:32:36 # Entity & database updates
simple 02-Apr-2019 08:32:36 /opt/php/7.1/bin/drush entity-updates -y
simple 02-Apr-2019 08:32:36 /opt/php/7.1/bin/drush updb -y
simple 02-Apr-2019 08:32:36
simple 02-Apr-2019 08:32:36 # Import config
simple 02-Apr-2019 08:32:36 /opt/php/7.1/bin/drush csex production -y
simple 02-Apr-2019 08:32:36 /opt/php/7.1/bin/drush cim -y
simple 02-Apr-2019 08:32:36
simple 02-Apr-2019 08:32:36 # Cache rebuild
simple 02-Apr-2019 08:32:36 /opt/php/7.1/bin/drush cr
Resulting in this error.
error 02-Apr-2019 08:32:36 PHP Fatal error: Uncaught Error: Call to a member function read() on boolean in /releases/release-1.1.x-20190402-1/web/core/includes/file.inc:930
error 02-Apr-2019 08:32:36 Stack trace:
error 02-Apr-2019 08:32:36 #0 /releases/release-1.1.x-20190402-1/web/core/includes/file.inc(935): file_unmanaged_delete_recursive('//etc/ssl/priva...', NULL)
error 02-Apr-2019 08:32:36 #1 /releases/release-1.1.x-20190402-1/web/core/includes/file.inc(935): file_unmanaged_delete_recursive('//etc/ssl', NULL)
error 02-Apr-2019 08:32:36 #2 /releases/release-1.1.x-20190402-1/web/core/includes/file.inc(935): file_unmanaged_delete_recursive('//etc', NULL)
error 02-Apr-2019 08:32:36 #3 /releases/release-1.1.x-20190402-1/web/modules/contrib/google_tag/src/Entity/ContainerManager.php(188): file_unmanaged_delete_recursive('/')
error 02-Apr-2019 08:32:36 #4 /var/www/platform in /releases/release-1.1.x-20190402-1/web/core/includes/file.inc on line 930
error 02-Apr-2019 08:32:36 Drush command terminated abnormally due to an unrecoverable error. [error]
error 02-Apr-2019 08:32:36 Error: Uncaught Error: Call to a member function read() on boolean in
error 02-Apr-2019 08:32:36 /releases/release-1.1.x-20190402-1/web/core/includes/file.inc:930
error 02-Apr-2019 08:32:36 Stack trace:
error 02-Apr-2019 08:32:36 #0
error 02-Apr-2019 08:32:36 /releases/release-1.1.x-20190402-1/web/core/includes/file.inc(935):
error 02-Apr-2019 08:32:36 file_unmanaged_delete_recursive('//etc/ssl/priva...', NULL)
error 02-Apr-2019 08:32:36 #1
error 02-Apr-2019 08:32:36 /releases/release-1.1.x-20190402-1/web/core/includes/file.inc(935):
error 02-Apr-2019 08:32:36 file_unmanaged_delete_recursive('//etc/ssl', NULL)
error 02-Apr-2019 08:32:36 #2
error 02-Apr-2019 08:32:36 /releases/release-1.1.x-20190402-1/web/core/includes/file.inc(935):
error 02-Apr-2019 08:32:36 file_unmanaged_delete_recursive('//etc', NULL)
error 02-Apr-2019 08:32:36 #3
error 02-Apr-2019 08:32:36 /releases/release-1.1.x-20190402-1/web/modules/contrib/google_tag/src/Entity/ContainerManager.php(188):
error 02-Apr-2019 08:32:36 file_unmanaged_delete_recursive('/')
Proposed solution is to check if the variable has been set before removing any files.
Comments
Comment #2
ismail cherri commentedBy the way, if someone tries to run drush cr while having root privileges, this error might delete the whole server!
Comment #3
fhey commentedI would also suggest not defaulting to / when it comes to deleting things. You should never assume that people who run a website have their permissions set up properly and as such are probably running things as root, with potentially devastating consequences as a result.
Comment #4
nick_vhSeems critical enough to me to even unpublish this release. Better be safe than sorry.
Comment #5
kevinquillen commentedIs this in dev only, or in the 1.1 release? I can't view the code on GitLab at the moment.
Comment #6
ismail cherri commented@kevinquillen Fortunately it is only in dev version
Comment #7
mark_gerarts commentedCan be easily fixed with an empty check before trying to delete the assets. Which makes sense, because we don't want to delete anything if the directory hasn't been set. Alternatively we could default to the public://google_tag folder.
Comment #8
ismail cherri commentedHere is a patch which checks for the presence of the folder before actually performing the recursive delete function
Comment #9
ismail cherri commented@mark_gerarts Sorry, it seems I've hidden your file while uploading mine!
Comment #10
berdirI think it should either do only the empty check or maybe better even both, calling is_dir('') is a bit strange.
Comment #11
bceyssensIf I'm not mistaking,
is_dir('/')returns TRUE and@file_unmanaged_delete_recursive('//')will still flush the entire partition. Is it really necessary the delete all files in the configured folder, why not only delete files written by the google_tag module?Comment #12
berdirThe config value is empty, not /, the / is only added afterwards. I assume The folder should only be used by google_tag, otherwise it can't know what it did write and what not.
Comment #13
bceyssens@Berdir, users could provide
/as the config uri. There's still room for error, which could end up catastrophical.Comment #14
ismail cherri commented@berdir
I added the is_dir() to check if the folder is actually there, otherwise do not run the command. I think I made the mistake to assume that it will convert the 'public://' on its own.
@bceyssens
If the user add '/' as config uri, I think the function should assume to look in the 'public://` folder.
Comment #15
ismail cherri commentedAnother patch which correctly check for the realpath of the public folder
Comment #16
berdiris_dir('public://something') should work fine, that's not point, my point is that is_dir('') is strange and we should also explicitly check for an empty string.
Comment #18
KevinGuancheDarias commentedIndeed @Berdir, as is_dir('/') will return true too, empty check should be mandatory, note realpath('/') will return '/'
Comment #19
berdirthe trailing slash is added after the is_dir() check, it would only be / if a user actually enters /, which should of course not be allowed and makes me wonder if this shouldn't be configuration at all or at least only within the public:// file system, which could be a separate setting.
Comment #20
kevinquillen commentedThe top directory should be forced ("//google_tag/") IMO, so the only disaster scenario in this case would be deleting google tag generated files which can be regenerated anyway. Someone may enter the wrong path by mistake and wind up deleting files in public or elsewhere that they do not intend to do.
Comment #21
dorficus commentedI see that there is a commit pushed to the 1.x-dev branch, but it's still marked as "Needs Review". Is this fixed yet or are there still kinks that need to be worked out from the patch?
Comment #22
solotandem commentedThe commit fixes the initial purpose of this issue, namely that the "code might break stuff when clearing the cache before running updb". In that regard I consider the issue fixed.
I did not change status as the comments were diverting to a discussion of whether to restrict the directory used to store the snippets. (Quite possibly this should be a separate issue). I would be fine with changing this to allow user to specify a stream wrapper (and possibly a directory path beneath that) and using a directory of "wrapper://path/google_tag". Thoughts?
Comment #23
finex commentedHi, is this problem fixed?
Comment #24
sandboxplSomething I've spotted when testing patch #15 , when module configuration is missing, following line:
Will return a NULL , then
returns docroot , and is_dir() still gives TRUE, this ends up with following:
Comment #25
solotandem commentedWhy are you focusing on the patch in #15 when the problem was solved by the commit in #17?
As mentioned in #22 this issue has been fixed. If a discussion of the snippet directory needs to happen that is the topic of a separate issue/
Comment #26
gigapatCame across this problem yesterday with version 1.2 and resulted in public files being deleted. All this simply because the configuration settings for google_tag were not current with the 1.2 version (or what it expects).
The problem is when there is a case where "uri" is not found or defined in configuration. Regardless of operator or other unforeseen error, there should still be a condition in place to prevent a recursive delete on root "/" if "url" setting is empty.
Comment #27
solotandem commented@pmuzyk The 1.2 release includes the commit in #17 which I believe solves this problem. Please explain how the code could get to the delete statement with the uri setting being undefined. What steps were taken (and in what order) on your site that could leave it in a state in which the delete statement would be executed with an undefined uri setting, e.g. cache rebuild, db update, code update, etc.?
By more recent commit than #17, the trailing slash was removed from $uri. With this change, even if $uri is NULL and delete_recursive() is called, the latter will do nothing as NULL is neither a directory nor a file. [But this is not relevant to your claim.]
Comment #28
phayes commentedHi there,
We also ran into this issue. We're running on 1.2 . The google_tag module tried to delete the root filesystem.
Re-opening the ticket.
Comment #29
solotandem commented@phayes Please answer the questions in #27. In order to flag something as a bug you need to demonstrate how this module causes the problem. Among other things that means list the steps to reproduce.
Also, try the dev release.
Comment #30
berdirSeems obvious enough as commented before? If url is somehow empty, then it deletes "/". I've seen a lot of people who messed up updating mdoules and reimport old configuration.
Just add a check on $uri and don't do anything if empty. Considering how destructive this is, it doesn't hurt to be a little bit defensive.
Comment #31
solotandem commented@Berdir No it is not obvious enough. In fact, noone has demonstrated how the claim can occur in the 1.2 release (which includes the commit in #17). Can you? I understand how it could occur before the aforementioned commit, but not after. Please list steps to reproduce.
Also, try the dev release.
Comment #32
berdirDo not try this at home :p
Given that you have a container.
drush config-set google_tag.settings uri ""
drush ev "_google_tag_assets_create()"
Yes, sure, url shouldn't be empty, but clearly it happens. I've had a ton of bug reports in pathauto too, mostly because people reimported their old configuration after running updates. And in pathauto, it was just missing slashes in their URL's, not deleting their whole fricking disk :)
Just add a if ($uri) wrapper around it.
Comment #33
solotandem commentedRegarding the 1.2 release
Implied in your test setup from "given that you have a container" is that you had run the database update on the 1.2 release. After this update, a container would be created AND the uri setting would NOT be empty but would match the default value of 'public://google_tag'. In which case, the code would NOT attempt to delete the root file system.
With the two commands you indicate, you concede that this is a user error. Surely you are not one to dispute the fact that in an out-of-context manner (as you have indicated) all sorts of code can break. This raises the interesting question of how much sanitization of inputs should be done in a large system. When do you leave it to the users to take responsibility for themselves?
What if you had set the uri to '/', 'public://' or 'public://[dir-with-latest-file-uploads-that-are-not-backed-up] or ...? What are we to do then? Surely your suggested 'fix' is of no value in these cases?
Regarding the latest dev release
Those two commands you suggest, go ahead and try this at home :p
In the guidelines for filing a bug, does it not indicate to verify the problem against HEAD? (And I have asked twice already.)
To further demonstrate the fallacy of your suggested fix..
With the latest release, what if you set the uri to '/', 'public://' or 'public://[dir-with-latest-file-uploads-that-are-not-backed-up] or ...? Now what? Even your 'just add a wrapper around it' suggestion will not save you. This module is not responsible for operator error.
Issue remains Fixed.
Comment #34
eelkeblokAnother case how this could go wrong. I ran into this problem on a production release. The cause, it turns out, is because the GTM settings are in a config split and the split version was not updated. Obviously user error. However, IMHO the size of the error does not really measure up to the size of the consequences. A simple check if the setting has any value seems reasonable. Break as hard as you like, but break before attempting to delete the entire file system of the server.
Comment #35
berdirOne last try. I'm just trying to help. As others have said, the impact of this is *severe* and results in massive data loss.
I had a look at the dev version, and there is no relevant change.
Config split is one example where something like this could happen, despite having executed updates. And yes, all cases where this could happen are user mistakes. But these mistake have happened multiple times and are fairly common.
All we're asking for is adding this:
to that function.
Comment #36
solotandem commented@Berdir If you were sincerely 'trying to help', then you would actually test the dev release and NOT just 'look at the dev version, and [conclude] there is no relevant change'. For the use case of this issue, namely the uri being empty (NULL, blank), then the code will delete NOTHING!!!
Read it again, boys. NOTHING!
Also, your solitary concern ONLY for the root directory seems rather short-sided. Did you miss the point about user error to set the uri to any directory that the web user should have write access to?
@Berdir, @eelkeblok This issue is resolved in the latest dev release.
Comment #37
solotandem commentedAdded feature request to recharacterize the uri setting and always append 'google_tag' for the snippet directory as mentioned in #20.
Comment #38
eelkeblokOK, just to prevent others from having to actually look up the code, the latest dev version does indeed do a check if the setting is empty and also includes #3088579: Treat the uri setting as the parent directory. This was not clear to me before, thanks for pointing that out clearly.
Comment #40
bceyssensBe careful when updating to 1.3, we just lost our public folder. Seems like an edge case that could result in severe data loss, read: https://www.drupal.org/project/google_tag/issues/3088579#comment-13399329.