Problem/Motivation

We used to remove unused files in D7. With [#9204215] we changed this in D8. Unused files will stay in the system and eventually be removed during cron runs based on a configuration variable. This introduces a potential security issue. Unused temporary files will generally be accessible for download to any user (incl. anonymous). This is not desired.

Proposed resolution

Block download of unused temporary files. Allow contrib modules to take ownership of this files. By taking ownership modules also take responsibility for secure treatment of those.

This closes the gap from Drupal 7 and makes the behavior from a security standpoint essentially the same.

If a module relies on this behavior, it must either:

- add a usage count

or

- make the file permanent

Given the file is no longer immediately deleted and having temporary files with no usage being "permanent" feels very very wrong, we deem that an acceptable compromise in getting Drupal 8 releasable.

Also the file_entity use case is mainly dealing with public files.

User interface changes

- None.

API changes

- No user except the owner has access to a private temporary file with usage count of 0.

Original report by @David_Rothstein

In Drupal, hook_file_download() is used to control access to private files. It operates on the principle that if one module wants to grant access and another module wants to deny access, the module which wants to deny access wins.

The File module implements this hook to control access to file attachments. It denies access to a file whenever all the entities that the file is attached to are not accessible by the current user.

In Drupal 7, a key part of this system is that when a file is no longer being used it is immediately deleted. This means that even after the File module stops managing a private file, it still ensures that users who did not have access to the file before will not suddenly get access to it.

In Drupal 8, this behavior was removed (I think in #1401558: Remove the usage handling logic from file_delete()) which introduces a security issue. The security issue is not present in Drupal 7, but can be reproduced if using the core patch at #1399846-263: Make unused file 'cleanup' configurable since that proposes backporting a similar change to Drupal 7. The instructions below will work for either Drupal 8 or Drupal 7 with the above patch applied; given that a contrib module is required to reproduce this, it is a little easier to understand with the Drupal 7 version.

To reproduce from a fresh installation:

  1. Configure image fields on articles to be private files.
  2. Ensure that a module is installed which will grant anonymous users access to these files. In Drupal 7, this could be done most simply with something like https://www.drupal.org/project/file_entity and granting the "View private files" permission to anonymous users. In Drupal 8, the attached patch (https://www.drupal.org/files/issues/2461845-demo-do-not-test.patch) can be applied to Drupal core for testing purposes.
  3. Create an unpublished article node with an image attached. Because the article is unpublished and anonymous users can't view unpublished content, they should not have access to the image either. You can verify that this works; if anonymous users access the image file directly by URL, they will not have access to it.

    The intention of this setup is as follows: The site owner wants to make sure anonymous users can view a whole class of private files by default. But if the private file is associated with restricted content, it should inherit those restrictions and not be viewable by everyone. (For example, the image might be a top secret diagram that is not ready for the public to view.)

  4. Now remove the file from the node, or delete the node (either should work). If you try to view the file as an anonymous user, you will now suddenly have access to it. This is a security issue. It will only be accessible for 6 hours by default, but because of #1399846: Make unused file 'cleanup' configurable the window can be configured to be longer than that, including infinitely long (via the "Delete orphaned files after" setting at admin/config/media/file-system).

    An unpatched Drupal 7 site deletes the file right away, so does not experience this security issue.

I am not sure how best to fix this. Rolling back the changes from #1401558: Remove the usage handling logic from file_delete() would probably do it but doesn't sound like a great idea. Maybe there needs to be code to more aggressively block access to files that are not attached to any entity anymore?

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

David_Rothstein’s picture

Issue summary: View changes
FileSize
636 bytes
xjm’s picture

Issue tags: +D8 upgrade path

Yowch. Seems like something we need to fix before the upgrade path is supported since I imagine this would be an SA if it did impact D7 core.

Berdir’s picture

In file_file_download(), if a file has no references left, it doesn't explicitly check access and allows other modules to chime in. That's why this happens.

Hm. I understand that the change for D7 might be a problem, but I'm not sure that it's just by design for D8.

As you said, you need a module that actually grants access, like "View *private* files". Which is defined as an admin permission:

    'view private files' => array(
      'title' => t('View private files'),
      'restrict access' => TRUE,
    ),

I would actually consider it to be strange/a bug that I can't access private files despite having that permission? What's the point of that permission then?

And your hook explicitly grants access to *all* files. Again, not very surprising?

That said, I do see that point that is unexpected when access was denied before and then it no longer is. But as mentioned above, in those scenarios, I'm not actually 100% sure that' desired? It might be a logical next step to make files their own thing and not just attachments to other entities that have no existence beyond that, to not deny access anymore? So maybe instead of trying to find workarounds to force denying access to those fields, maybe we should instead make a step forward and remove the -1. That means other modules need to consider private files properly, but again, access code always need to be correct or it is an issue.

David_Rothstein’s picture

Granting access to all files to anonymous users was just the easiest way to test this - it doesn't have to be that broad of a permission. All you really need is a hook that grants access to the one particular file in question.

Maybe a more realistic way to reproduce this in practice would be:

  1. Anonymous users have no access to download files at all (hence the site needs to use the private file system).
  2. Authenticated users are supposed to be able to download a certain class of files (let's say PDF files) that are not necessarily attached to other entities. It might be a library of browsable, donwloadable documents, for example. You need some code that will explicitly grant them that access, because otherwise nothing else will give them access in the case of an unattached file.
  3. But if an administrator separately attaches a PDF file to an unpublished node somewhere else on the site, authenticated users should not be able to download that.

It is not far from a use case I saw on an actual site a year or two ago (although that one was even more complicated).

It might be a logical next step to make files their own thing and not just attachments to other entities that have no existence beyond that, to not deny access anymore? So maybe instead of trying to find workarounds to force denying access to those fields, maybe we should instead make a step forward and remove the -1. That means other modules need to consider private files properly, but again, access code always need to be correct or it is an issue.

So if we did it that way, I'm not sure how the above situation would get resolved.

Personally I think Drupal should ideally distinguish between files that are intended to be "only attachments" and files that are intended to be able to "stand on their own". (See my comment at #1399846-74: Make unused file 'cleanup' configurable, last paragraph.) It was rightly pointed out that would be a big change and probably Drupal 9 material, but I think it addresses the heart of the problem. Because then File module would know which files it should be checking access for (only files in the first category), and for those files only it could also go back to deleting immediately when they are no longer attached to anything, like Drupal 7 does.

quicksketch’s picture

Authenticated users are supposed to be able to download a certain class of files (let's say PDF files) that are not necessarily attached to other entities. It might be a library of browsable, donwloadable documents, for example. You need some code that will explicitly grant them that access, because otherwise nothing else will give them access in the case of an unattached file.

Any module that is granting access to all private files that is by extension or any other criteria other than registered records in the database would likely open up security vulnerabilities (as described a couple of different ways above) intentionally is granting access to a file with limited knowledge of the file usage (which generally is a bad idea). If the file is a "stand-alone", a file_usage record should still be registered by the module that provides the stand-alone capability (e.g. File Entity in D7).

Personally I think Drupal should ideally distinguish between files that are intended to be "only attachments" and files that are intended to be able to "stand on their own".

I think this might be a good idea as well, but in the mean time, any file that stands on its own should simply point a file_usage record at the file entity, ensuring the file never is deleted because it's always "in-use" by its own entity (unless that file entity itself is deleted).

So far from all the examples given, they all seem like any problems would be derived from a module implementing hook_file_download() and returning access when they didn't have enough information to be making such a judgement.

I think @Berdir is correct that the thing we're missing here is consistency in granting access to a file. File module should probably *not* return a -1 access for files that may have been attached to a node but to which the user does not have access. If another module wants to grant access to that file (because it may have originally been uploaded through a file browser or some other means), then that module should be allowed to grant access.

EDIT: Amended my statement about causing security vulnerabilities (it doesn't by itself).

plach’s picture

Gábor Hojtsy’s picture

As per hook_file_download:

 * @return
 *   If the user does not have permission to access the file, return -1. If the
 *   user has permission, return an array with the appropriate headers. If the
 *   file is not controlled by the current module, the return value should be
 *   NULL.

So is the idea that file_file_download() would

  if (!$file->access('download')) {
    return NULL;
  }

Instead of -1? That would mandate that all other file download hooks would need to do this access check themselves then?

Gábor Hojtsy’s picture

Not sure how would that resolve the problem, given the "problem" is with the lines above that return:

  // Find out which (if any) fields of this type contain the file.
  $references = file_get_file_references($file, NULL, EntityStorageInterface::FIELD_LOAD_CURRENT, NULL);

  // Stop processing if there are no references in order to avoid returning
  // headers for files controlled by other modules. Make an exception for
  // temporary files where the host entity has not yet been saved (for example,
  // an image preview on a node/add form) in which case, allow download by the
  // file's owner.
  if (empty($references) && ($file->isPermanent() || $file->getOwnerId() != \Drupal::currentUser()->id())) {
    return;
  }

Whatever is returned in the following lines, this will already return if there are no references left. While its true that a contrib module that manages a file directory should maintain usage records for the files, core already allows in Drupal 8 to keep all files (without usage records). And I don't think we want to keep a usage record for each file referencing itself, because then no file would be ever deleted. Maybe we can keep a usage record for each file if the site is set to keep all files globally, but then we need to update all files when that setting is changed, so the setting change actually results in files removed later. And then you still have this window of the file being accessible until actually deleted which is part of the report too.

Berdir’s picture

I'm not sure how adding a dummy file usage would actually help, that would then still either always allow access or never. So we're back to the question what we should do with those files. Also, how would we remove files then that actually have no usage left and should be deleted?

The thing is that we return a different value for those two cases.

* If there is no file usage, we return NULL, so we don't grant access but we also don't explicitly deny. (access is still denied, unless someone else allows access)
* If there is file usage, but the user doesn't have access, we explicitly deny access.

I think we should be consistent. In which direction, I'm not completely sure, but I'm definitely leaning towards returning NULL in both cases, and allow other hooks to still grant access. That is consistent with how entity access works. Why is it so bad here? If you're implementing file access hooks, then it is your responsibility to handle it correctly.

Also, entity access has three states now. So we actually could differentiate between neutral and explicitly deny there as well, in that case, we would only explicitly deny if access('download') explicitly denies?

Berdir’s picture

Status: Active » Needs review
FileSize
2.46 KB

My suggestion would look like this. Completely untested, want to see what the test output is.

Status: Needs review » Needs work

The last submitted patch, 10: file-download-forbid-neutral-2461845-10.patch, failed testing.

Berdir’s picture

Uhm, that's what I get for not testing...

Gábor Hojtsy’s picture

@Berdir: Are the removed conditions (owner, permanent) handled in the access method directly? Looking at \Drupal\file\Entity\File it does not seem like.

Berdir’s picture

Yes, they are in FileAccessControlHandler.

The check there didn't actually check access, it just allowed the download access implementation to run which then again checked access. We're still doing that.

slashrsm’s picture

I must confess that I am a bit confused and not completely sure about the intent of this ticket. It appears to me that we're trying to solve a security issue that *might* be caused by a poor code or administrator giving out permissions to wrong people.

However, patch in #12 looks good to me. It makes thing more consistent and easier to understand. +1 to get it in.

larowlan’s picture

Issue tags: +Needs tests

working on a test

larowlan’s picture

So here's a test based on the OP.
It fails but doesn't pass with the patch from #12
So either the test is wrong, the steps in the OP are wrong or the patch doesn't fix the issue.
I'm tipping it's one of the first two options.

Hoping someone else can shed some light on it.

Berdir’s picture

My patch isn't supposed to fix the described problem, because I think that is not a bug, it's by design. If I have the "View private files" permission (which has restrict access => TRUE), then I expect to have access to *all* private files. Right now, I only have access as long as they are either not used anywhere or I do have access.

Imagine this: You upload a file with file_entity as a private file, and you have access to it. Until someone decides to use that file for a node that you don't have access to (e.g., the node is not yet published). Now you can't see your file anymore?

+++ b/core/modules/file/tests/file_test/file_test.module
@@ -150,6 +150,11 @@ function file_test_file_validate(File $file) {
 function file_test_file_download($uri) {
+  if (\Drupal::state()->get('file_test.allow_all', FALSE)) {
+    $files = entity_load_multiple_by_properties('file', array('uri' => $uri));
+    $file = reset($files);
+    return file_get_content_headers($file);
+  }
   _file_test_log_call('download', array($uri));

So yes, if you implement the hook like this, you have a problem. But that's the same for hook_node_access(), if you return Access::isAllowed() from yourmodule_node_access(), then your site is wide open too. That's not a bug?

My patch is just making the behavior consistent and behaving the same way, so that it doesn't make a difference if there is a file usage without access or not file usage. We don't grant but also don't explicitly deny access. Unless a field/entity access explicitly returns forbid(), then we do forbid access.

So, a test for my patch would test the difference between a field or entity access returning an explicit forbidden and neutral. neutral is identical to no file usages. I'm not 100% sure that is correct (what if a file is used on two fields and one explicitly forbids access? I guess you still want access). Which would actually simplify the patch even more, we'd just have an else/if and FileAccessControlHandler doesn't need to be changed.

larowlan’s picture

Tagging for issue summary update, as OP is wrong

Status: Needs review » Needs work

The last submitted patch, 17: some-file-thingo-2461845.doesnt-pass-wa-wa.patch, failed testing.

The last submitted patch, 17: some-file-thingo-2461845.fail_.patch, failed testing.

David_Rothstein’s picture

Title: Private files that are no longer attached to an entity are not properly prevented from being downloaded » Private files that are no longer attached to an entity should not suddenly become accessible to people who couldn't see them before

I'm retitling this a bit since I agree that the security issue here really is mostly about the before vs. after inconsistency.

I think the current design is clearly that the File module intends to "own" any file that is used as an attachment (regardless of where else it might be used) and thereby ensure that it's inaccessible based on its attachment status. The scenario I wrote up is based on the current design.

Changing the design to be the opposite is an interesting proposal, though... I agree that it would improve some things and would make file access more like other types of entity access.

Where I still get stuck, though, is that if the design is changed, people here are basically saying that no module should ever grant unprivileged users blanket access to a group of files.

Comparing that to the node access system, wouldn't this be the equivalent of removing the core behavior where users with "access content" permission can see all published nodes by default, and then decreeing that no contrib node access module should attempt to add anything like that permission on their own? How would that work?

In the node access system, we certainly do expect that you can give the "access content" permission freely to unprivileged users without giving up on the idea of implementing node access on your site...

Gábor Hojtsy’s picture

I looked at least how file and node access is documented, not sure that gives a helping hand in understanding this problem:

So for files:

If one or more modules returned headers the download will start with the returned headers. If a module returns -1 an AccessDeniedHttpException will be thrown.

For entities:

Each implementation may explicitly allow, explicitly deny, or ignore the access request. If at least one module says to deny the request, it will be rejected. If no modules deny the request and at least one says to allow it, the request will be permitted.

So the docs on file access are contradicting, it both says that if at least one grants access (returns headers) it is downloaded and also says if someone returns -1 it will not.

Berdir’s picture

Not sure I see why they are contradicting?

They are really the same IMHO:

By default, access is denied.

headers == allow
Not returning anything == neutral
-1 == forbidden

Comparing that to the node access system, wouldn't this be the equivalent of removing the core behavior where users with "access content" permission can see all published nodes by default, and then decreeing that no contrib node access module should attempt to add anything like that permission on their own? How would that work?

In the node access system, we certainly do expect that you can give the "access content" permission freely to unprivileged users without giving up on the idea of implementing node access on your site...

Hm, not a full answer, just some thoughts:
* Private files are already a subset of all files.
* If you want files to be available by default, then don't make them private files.
* access content is a special permission, because without it, we won't even check anything else. There is no equivalent in file access and also no generic entity access concept for that, that's why it is checked in access(), before even calling any hooks
* Even if all your files are private files, as long as they are attached to nodes that are available to anyone anyway.

I still don't see any use cases that would make sense to have that would break..

And what you are saying also completely depends on the core way of using private files, that they are uploaded to nodes and start with having file usages. When you use file_entity/media, then you can upload files without already having usages, and when you create them as private files then, then they would also be visible if such code would exist.

Berdir’s picture

Status: Needs work » Needs review

Setting back to needs review. I still think that this is not a security issue but only a non-critical inconsistency.

I'm not sure if the patch in #12 is OK, or if we should remove the neutral/forbidden behavior. The question to answer is this:

Assuming that a file is attached to two different nodes (1 and 2), what is the expected behavior:

N1 | N2 | Result
A   | A   | A
A   | N   | A
A   | F   | ?
N   | N  | F
N   | F   | F
F    | F   | F

Right now, you have access for the case A / F, and it probably has to stay like this, otherwise you can view the node, but the file won't be visible. Which I doubt is the expected case.

webchick’s picture

Tentatively tagging.

toamit’s picture

Last week I created another issue for core that can potentially handle these and more complex cases well.
Please take a look at https://www.drupal.org/node/2485807

Fabianx’s picture

Assigned: Unassigned » Fabianx
Issue tags: -Needs technical direction

So I think there is a much much simpler fix:

- Deny access to temporary private files with usage 0 by default.

  /**
   * Implements Drupal\file\FileUsage\FileUsageInterface::delete().
   */
  public function delete(FileInterface $file, $module, $type = NULL, $id = NULL, $count = 1) {
    // If there are no more remaining usages of this file, mark it as temporary,
    // which result in a delete through system_cron().
    $usage = \Drupal::service('file.usage')->listUsage($file);
    if (empty($usage)) {
      $file->setTemporary();
      $file->save();
    }
  }

And we talk about downloading, so that should be okay - if it is no longer attached anywhere ...

As Access + Deny == Access contrib is fine, too.

I am getting a patch for that going ...

Fabianx’s picture

The other part of the problem (and why file_entity is a really bad example) is that file_entity in D7 never checks hook_file_download_access() and it also uses the 'view' permission for the entity instead of the 'download' permission. (I just had the pleasure to circumvent that for a client ...)

So the real problem is the broken/strange API around this, but that problem is pre-existing in D7 and hence not a release blocker - unless we allow someone to access 'deleted' files in the race after it was deleted till cron runs.

That API is broken because:

- hook_file_download() can just allow access by sending headers.
- Only file_file_download() calls file_download_access_alter(), but works only on usage by entities

TL;DR: This is pre-existing strange in D7, lets fix the critical issue of the race when the file was deleted in D7 and when it is deleted lazily in D8.

Fabianx’s picture

And fixed the tests and added a proper fix.

That puts behavior almost back to where it was in D7.

It just means that if some module wants to grant access to temporary files, they need to make them either:

- permanent

or

- add file usage

Given that system_cron could run at any time (race conditions anyone? ... follow-up though), that is a good thing in general though.

And to have the owner of the file still being able to access the file should be okay ...

Status: Needs review » Needs work

The last submitted patch, 30: private_files_that_are-2461845-30.patch, failed testing.

Fabianx’s picture

Status: Needs work » Needs review
FileSize
4.74 KB
768 bytes

Fixed the test failures, the test depended on the old behavior, which nothing except tests would do like this.

Also the file was created for uid 1 by an anon user, which does not really much sense ( in a real world usage) ...

Status: Needs review » Needs work

The last submitted patch, 32: private_files_that_are-2461845-31.patch, failed testing.

Fabianx’s picture

Status: Needs work » Needs review
benjy’s picture

+++ b/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -599,8 +599,16 @@ function file_file_download($uri) {

hook_file_download() isn't document in the file api file. I was looking specifically to see if the return -1 was meant to be a constant or such. I see FileDownloadController::download() specifically checks for -1 though, so all good.

The rest of the patch looks pretty great.

Berdir’s picture

Hm.

Deleting temporary files is actually optional now. I'm wondering if that has any impact on how this works, not sure.

I still think it would be better to go in the other direction but if that's what it takes to get rid of this issue, then so be it.

David_Rothstein’s picture

This is interesting, and I think it might work. It is sort of what I was talking about in the issue summary originally ("Maybe there needs to be code to more aggressively block access to files that are not attached to any entity anymore") but for some reason I didn't think of doing that only for temporary files, which definitely does make it a lot simpler.

As Access + Deny == Access contrib is fine, too.

For hook_file_download(), Access + Deny => Deny actually (see FileDownloadController::download). So what that means is that by returning -1 as introduced in this patch, the File module essentially prevents any other module from granting access to a temporary file.

Are there scenarios where that's a problem? I'm not sure I can think of any. It does sort of reintroduce the requirement (at least for private files) that modules which want to keep an unused file around, e.g. in a "media library" that multiple admins need access to, must add a file usage record for it, so that it always stays permanent. Removing that requirement was sort of the main goal of #1399846: Make unused file 'cleanup' configurable (see the issue summary there)... I am not sure it was a totally valid goal, but it needs some consideration.

But overall I think this makes sense to me, and this patch might be the quickest (even if not the most ideal) way to close this issue.

David_Rothstein’s picture

+    // Deny access to temporary files without usage that are not owned by the
+    // same user as they will be deleted by system_cron() later anyway.

This comment is not quite right - the code is checking references (not usage), and system_cron() will not necessarily ever delete the file; it depends on how temporary file cleanup is configured on the site.

Probably better to have the code comment explain the security rationale instead?... i.e., a temporary file might be temporary because it no longer has references, so we deny access in case those references would have denied access if they still existed.

Yeah, um, that's pretty confusing to explain :)

---

I'd still love to see if what I mentioned in #4 is possible instead:

Personally I think Drupal should ideally distinguish between files that are intended to be "only attachments" and files that are intended to be able to "stand on their own".

To the extent that it's just adding a new boolean flag to files, maybe it's not really Drupal 9 material after all? But I won't have time to look into it right now.

dawehner’s picture

So could we fix the criticality of that issue and maybe be too harsh about that for now and then follow up on it later?

Fabianx’s picture

Status: Needs review » Needs work

Are there scenarios where that's a problem? I'm not sure I can think of any. It does sort of reintroduce the requirement (at least for private files) that modules which want to keep an unused file around, e.g. in a "media library" that multiple admins need access to, must add a file usage record for it, so that it always stays permanent. Removing that requirement was sort of the main goal of #1399846: Make unused file 'cleanup' configurable (see the issue summary there)... I am not sure it was a totally valid goal, but it needs some consideration.

Well, they should not make it temporary then. Or at least put it back to permanent afterwards, but that is only a major bug - if at all.

Temporary files with no references / no usage should not be around forever. That completely subverts what 'temporary' means.

Because the file is now not deleted directly, they can still make it "permanent" again. Not totally pretty, but workable.

---

This comment is not quite right - the code is checking references (not usage), and system_cron() will not necessarily ever delete the file; it depends on how temporary file cleanup is configured on the site.

That is a very good point. Lets check for usage() instead first and then deny if usage == 0 && temporary (and not the owner of the file).

Then keep the references code intact, even smaller patch. And then the comment is correct :D.

David_Rothstein’s picture

Hm, yes, I think checking usage makes sense and would be safer.

Some people from #1399846: Make unused file 'cleanup' configurable could still complain about all this, but if so it's fixable - and they were mostly interested in public files anyway (which is not affected by this patch one way or the other).

One more problem with the code comment:

+    // Deny access to temporary files without usage that are not owned by the
+    // same user as they will be deleted by system_cron() later anyway.

It's actually file_cron() in Drupal 8.

Fabianx’s picture

Status: Needs work » Needs review
FileSize
4.98 KB
2.75 KB

And here is the patch as promised, which is now even more explicit about things and also improved the documentation.

Yes, file_cron() is also fixed in this patch. Thanks! (Could file a follow-up for BaseFileBase::delete() as that still refers to system_cron(), but out of scope here.)

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

Its good that we have a pretty good explanation of what is going on.

slashrsm’s picture

Issue summary seems to be pretty outdated. Do we need an update?

catch’s picture

Status: Reviewed & tested by the community » Needs review

Yes that'd be useful - both for committers and for anyone who finds this issue one day via git blame.

slashrsm’s picture

Issue summary: View changes
Status: Needs review » Reviewed & tested by the community

Updated issue summary. Moving back to RTBC. While working on this also noticed a minor issue in documentation (#2489606: hook_file_download() docblock refers to a non-existing function file_download()).

Fabianx’s picture

Issue summary: View changes

Added some to the summary I had already written.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

The patch attached closes the security hole and behaviour change from Drupal 7 detailed in the issue summary. If contrib wants to change the behaviour it can.

Committed dd0712a and pushed to 8.0.x. Thanks!

  • alexpott committed dd0712a on 8.0.x
    Issue #2461845 by Fabianx, Berdir, larowlan, David_Rothstein, Gábor...
YesCT’s picture

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.