Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Allow for customization when sharing files using Essentials #27685

Open
mattleibow opened this issue Feb 10, 2025 · 2 comments
Open

Allow for customization when sharing files using Essentials #27685

mattleibow opened this issue Feb 10, 2025 · 2 comments
Assignees
Labels
area-essentials Essentials: Device, Display, Connectivity, Secure Storage, Sensors, App Info essentials-share s/triaged Issue has been reviewed t/enhancement ☀️ New feature or request

Comments

@mattleibow
Copy link
Member

Description

Right now, when you share a file via the launcher, email or share, if the file is not in a good place - such as in a private storage or not a real file - it gets copied into the app cache and shared via a FileProvider.

Our current mechanism is to copy to the cache and then make the whole cache available to the FileProvider. Google recommends against the whole cache and app data to avoid accidentally sharing a file you may not want to: https://developer.android.com/privacy-and-security/risks/file-providers#share_narrow_path_ranges

As of .NET 9, there is no automatic way to configure this, but it can be done by overwriting the microsoft_maui_essentials_fileprovider_file_paths.xml file in the app and making sure to put the files in that new location.

Solutions

Maybe for .NET 10 we can have some configuration on the file provider to specify where to put the cached files. So, a dev could override the locations in the xml file, and then set FileProvider.PreferredRoot = "my-sharing-root". We would then use that as the root instead of /.

Then, if the file was outside the designated folder, Android will throw an exception saying it is unable to generate the URI needed to share.

Problems

This is slightly harder today because we can detect if a file (say a large movie) is in the cache, then we can share directly. Using a sub folder will mean that the file would have to be copied from the root of the chance into a sub-folder. This may take time and also we have no real way to know when to clean up as sharing is a fire and forget operation. Using a sub folder we may lose performance.

However, if security is the primary concern, a slower app is better than an app that may share files accidentally.

Example

Sample code: OverwriteFileProviderPaths.zip

This is a 2-step process before sharing/emailing/launching:

  • Add a file provider file paths override file:
  • Add a file named microsoft_maui_essentials_fileprovider_file_paths.xml to the Platforms\Android\Resources\xml directory such that the full, relative name to the project is:
    Platforms\Android\Resources\xml\microsoft_maui_essentials_fileprovider_file_paths.xml
    Set the contents to be the paths you want:
 <?xml version="1.0" encoding="UTF-8" ?>
 <paths>
    <external-path name="external_files" path="sharing-root" />
    <cache-path name="internal_cache" path="sharing-root" />
    <external-cache-path name="external_cache" path="sharing-root" />  
 </paths>

Ensure that when sharing you first move/copy/write the file you want to share into the sharing-root folder in one of the locations:

 // write into the specific sub-directory
 var dir = Path.Combine(FileSystem.CacheDirectory, "sharing-root");  
 Directory.CreateDirectory(dir);
 var file = Path.Combine(dir, "secrets.txt");
 await File.WriteAllTextAsync(file, $"Secrets: {count}"); 

 // share the file
 await Launcher.OpenAsync(new OpenFileRequest
 {
     Title = "Accidental Sharing Of Secrets",
     File = new ReadOnlyFile(file),
 });

Troubleshooting:
You can verify that the file being shared is correctly if the shared URI excludes the sharing root directory. For example, if you share the file

<CacheDirectory>/sharing-root/secrets.txt
and the shared URI is
content://com.companyname.overwritefileproviderpaths.fileProvider/internal_cache/sharing-root/secrets.txt
then the file provider is not using the correct paths, but if it is
content://com.companyname.overwritefileproviderpaths.fileProvider/internal_cache/secrets.txt
then the file paths are correct. Notice the sharing-root is missing from the path.
If you get an exception when sharing the file, then it most likely means you are sharing a file that is outside the sharing-root :

Java.Lang.IllegalArgumentException: Failed to find configured root that contains /data/data/com.companyname.overwritefileproviderpaths/cache/some-non-sharing-path/secrets.txt

This is because the file is not under one of the specified paths, but outside and the resolver will throw.

@mattleibow mattleibow added area-essentials Essentials: Device, Display, Connectivity, Secure Storage, Sensors, App Info essentials-share t/enhancement ☀️ New feature or request labels Feb 10, 2025
@mattleibow mattleibow added this to the .NET 10 Planning milestone Feb 10, 2025
@dotnet-policy-service dotnet-policy-service bot added the s/triaged Issue has been reviewed label Feb 10, 2025
@mattleibow
Copy link
Member Author

@Redth @jonathanpeppers thoughts?

@jonathanpeppers
Copy link
Member

I don't know if you even need to make path="sharing-root" configurable by developers.

It seems like you could just pick a default (other than path=".") that appeases certain security scanners.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
area-essentials Essentials: Device, Display, Connectivity, Secure Storage, Sensors, App Info essentials-share s/triaged Issue has been reviewed t/enhancement ☀️ New feature or request
Projects
Status: Todo
Development

No branches or pull requests

3 participants