|
| 1 | +File Type Policy API |
| 2 | +=== |
| 3 | + |
| 4 | +# Background |
| 5 | +When saving a file with the standard SaveFilePicker (window.showSaveFilePicker), |
| 6 | +the user might receive a security alert, as the second prompt to confirm the unsafe file type. |
| 7 | +It's because the browser applies the [file type policies](https://learn.microsoft.com/en-us/deployedge/microsoft-edge-security-downloads-interruptions#file-types-requiring-a-gesture) |
| 8 | +to protect end users. However, in an app that is using WebView2, when end users try |
| 9 | +to save a file with a certain file extension, they usually can trust the |
| 10 | +host App, domain and file extension. So, we provide the App developers the |
| 11 | +File Type Policy API to manage the file type policies dynamically. |
| 12 | + |
| 13 | +We'd appreciate your feedback. |
| 14 | + |
| 15 | +# Description |
| 16 | + |
| 17 | +We proposed the `CoreWebView2.SaveFileSecurityCheckStarting` event. As a developer, you can register a handler on |
| 18 | +this event to get the file path, file extension and document origin URI information. Then you can apply your own |
| 19 | +rules to allow save the file without a default file type policy security warning UI; |
| 20 | +to cancel the saving; and even to create your own UI to manage runtime |
| 21 | +file type policies. |
| 22 | + |
| 23 | +# Examples |
| 24 | +## Win32 C++ |
| 25 | +This example will register the event with two custom rules. |
| 26 | +- Suppressing file type policy, security dialog, and |
| 27 | +allows saving ".eml" files directly; when the URI is trusted. |
| 28 | +- Showing customized warning UI when saving ".iso" files. |
| 29 | +It allows to block the saving directly. |
| 30 | +```c++ |
| 31 | +void ScenarioFileTypePolicy::AddCustomFileTypePolicies() |
| 32 | +{ |
| 33 | + wil::com_ptr<ICoreWebView2> m_webview; |
| 34 | + EventRegistrationToken m_saveFileSecurityCheckStartingToken = {}; |
| 35 | + auto m_webview2_23 = m_webView.try_query<ICoreWebView2_23>(); |
| 36 | + // Register a handler for the `SaveFileSecurityCheckStarting` event. |
| 37 | + m_webView2_23->add_saveFileSecurityCheckStarting( |
| 38 | + Callback<ICoreWebView2SaveFileSecurityCheckStartingEventHandler>( |
| 39 | + [this]( |
| 40 | + ICoreWebView2* sender, |
| 41 | + ICoreWebView2SaveFileSecurityCheckStartingEventArgs* args) -> HRESULT |
| 42 | + { |
| 43 | + // Get the file extension for file to be saved. |
| 44 | + wil::unique_cotaskmem_string extension; |
| 45 | + CHECK_FAILURE(args->get_FileExtension(&extension)); |
| 46 | + // Get the document origin URI of file to be saved. |
| 47 | + wil::unique_cotaskmem_string uri; |
| 48 | + CHECK_FAILURE(args->get_DocumentOriginUri(&uri)); |
| 49 | + // Convert the file extension value to lower case for |
| 50 | + // the case-insensitive comparison. |
| 51 | + std::wstring extension_lower = extension.get(); |
| 52 | + std::transform( |
| 53 | + extension_lower.begin(), extension_lower.end(), |
| 54 | + extension_lower.begin(), ::towlower); |
| 55 | + // Set the `SuppressDefaultPolicy` property to skip the |
| 56 | + // default file type policy checking and a possible security |
| 57 | + // alert dialog for ".eml" file, when it's from a trusted URI. |
| 58 | + // This will consent to save the file. |
| 59 | + // |
| 60 | + // 'IsTrustedUri' should be your own helper method |
| 61 | + // to determine whether the URI is trusted. |
| 62 | + if (wcscmp(extension_lower.c_str(), L".eml") == 0 && IsTrustedUri(uri)) |
| 63 | + { |
| 64 | + CHECK_FAILURE(args->put_SuppressDefaultPolicy(TRUE)); |
| 65 | + } |
| 66 | + // Show customized warning UI for ".iso" file with |
| 67 | + // the deferral. |
| 68 | + if (wcscmp(extension_lower.c_str(), L".iso") == 0) |
| 69 | + { |
| 70 | + wil::com_ptr<ICoreWebView2Deferral> deferral; |
| 71 | + CHECK_FAILURE(args->GetDeferral(&deferral)); |
| 72 | + |
| 73 | + // 'm_appWindow' should be your main app window. |
| 74 | + m_appWindow->RunAsync( |
| 75 | + [args = wil::make_com_ptr(args), deferral]() |
| 76 | + { |
| 77 | + // Set the `CancelSave` property to cancel the saving |
| 78 | + // for ".iso" file directly. Save action will be aborted. |
| 79 | + // |
| 80 | + // You can let end users make decision on their save |
| 81 | + // action with your customized warning UI. |
| 82 | + // 'IsCancelledFromCustomizedWarningUI' should be |
| 83 | + // your helper method that retrieves result from the UI. |
| 84 | + if (IsCancelledFromCustomizedWarningUI()) |
| 85 | + { |
| 86 | + CHECK_FAILURE(args->put_CancelSave(TRUE)); |
| 87 | + } |
| 88 | + CHECK_FAILURE(deferral->Complete()); |
| 89 | + }); |
| 90 | + } |
| 91 | + return S_OK; |
| 92 | + }) |
| 93 | + .Get(), |
| 94 | + &m_saveFileSecurityCheckStartingToken); |
| 95 | +} |
| 96 | +``` |
| 97 | + |
| 98 | +## .Net/ WinRT |
| 99 | +This example will register the event with two custom rules. |
| 100 | +- Suppressing file type policy, security dialog, and |
| 101 | +allows saving ".eml" files directly; when the URI is trusted. |
| 102 | +- Showing customized warning UI when saving ".iso" files. |
| 103 | +It allows to block the saving directly. |
| 104 | +```c# |
| 105 | +void AddCustomFileTypePolicies() |
| 106 | +{ |
| 107 | + // Register a handler for the `SaveFileSecurityCheckStarting` event. |
| 108 | + webView.CoreWebView2.SaveFileSecurityCheckStarting += (sender, args) => |
| 109 | + { |
| 110 | + if (string.Equals(args.FileExtension, ".eml", StringComparison.OrdinalIgnoreCase) |
| 111 | + && IsTrustedUri(args.DocumentOriginUri)) |
| 112 | + { |
| 113 | + // Set the `SuppressDefaultPolicy` property to skip the |
| 114 | + // default file type policy checking and a possible security |
| 115 | + // alert dialog for ".eml" file, when it's from a trusted URI. |
| 116 | + // This will consent to save the file. |
| 117 | + // |
| 118 | + // 'IsTrustedUri' should be your own helper method |
| 119 | + // to determine whether the URI is trusted. |
| 120 | + args.SuppressDefaultPolicy = true; |
| 121 | + } |
| 122 | + if (string.Equals(args.FileExtension, ".iso", StringComparison.OrdinalIgnoreCase)) |
| 123 | + { |
| 124 | + CoreWebView2Deferral deferral = args.GetDeferral(); |
| 125 | + System.Threading.SynchronizationContext.Current.Post((_) => |
| 126 | + { |
| 127 | + using (deferral) |
| 128 | + { |
| 129 | + if (IsCancelledFromCustomizedWarningUI()) |
| 130 | + { |
| 131 | + // Set the `CancelSave` property to cancel the saving |
| 132 | + // for ".iso" file directly. Save action will be aborted. |
| 133 | + // |
| 134 | + // You can let end users make decision on their save |
| 135 | + // action with your customized warning UI. |
| 136 | + // 'IsCancelledFromCustomizedWarningUI' should be |
| 137 | + // your helper method that retrieves result from the UI. |
| 138 | + args.CancelSave = true; |
| 139 | + } |
| 140 | + } |
| 141 | + }, null); |
| 142 | + } |
| 143 | + }; |
| 144 | +} |
| 145 | +``` |
| 146 | + |
| 147 | +# API Details |
| 148 | +## Win32 C++ |
| 149 | +```c++ |
| 150 | +interface ICoreWebView2_23 : ICoreWebView2_22 { |
| 151 | + /// Adds an event handler for the `SaveFileSecurityCheckStarting` event. |
| 152 | + /// If the default save file picker is used to save a file, for |
| 153 | + /// example, client using the File System API `showSaveFilePicker`; |
| 154 | + /// this event will be raised during system FileTypePolicy |
| 155 | + /// checking the dangerous file extension list. |
| 156 | + /// |
| 157 | + /// Developers can specify their own logic for determining whether |
| 158 | + /// to allow a particular type of file to be saved from the document origin URI. |
| 159 | + /// Developers can also determine the save decision based on other criteria. |
| 160 | + /// Here are two properties in `ICoreWebView2SaveFileSecurityCheckStartingEventArgs` |
| 161 | + /// to manage the decision, `CancelSave` and `SuppressDefaultPolicy`. |
| 162 | + /// |
| 163 | + /// Table of Properties' value and result: |
| 164 | + /// |
| 165 | + /// | CancelSave | SuppressDefaultPolicy | Result |
| 166 | + /// | ---------- | ------ | --------------------- |
| 167 | + /// | False | False | Perform the default policy check. It may show the |
| 168 | + /// | | | security warning UI if the file extension is |
| 169 | + /// | | | dangerous. |
| 170 | + /// | ---------- | ------ | --------------------- |
| 171 | + /// | False | True | Skip the default policy check and the possible |
| 172 | + /// | | | security warning. Start saving or downloading. |
| 173 | + /// | ---------- | ------ | --------------------- |
| 174 | + /// | True | Any | Skip the default policy check and the possible |
| 175 | + /// | | | security warning. Abort save or download. |
| 176 | + HRESULT add_SaveFileSecurityCheckStarting( |
| 177 | + [in] ICoreWebView2SaveFileSecurityCheckStartingEventHandler* eventHandler, |
| 178 | + [out] EventRegistrationToken* token); |
| 179 | + |
| 180 | + /// Removes an event handler previously added with `add_SaveFileSecurityCheckStarting`. |
| 181 | + HRESULT remove_SaveFileSecurityCheckStarting( |
| 182 | + [in] EventRegistrationToken token); |
| 183 | +} |
| 184 | + |
| 185 | + |
| 186 | +/// The event args for `SaveFileSecurityCheckStarting` event. |
| 187 | +interface ICoreWebView2SaveFileSecurityCheckStartingEventArgs : IUnknown { |
| 188 | + /// Gets the `CancelSave` property. |
| 189 | + [propget] HRESULT CancelSave([out, retval] BOOL* value); |
| 190 | + |
| 191 | + /// Set whether to cancel the upcoming save/download. `TRUE` means the action |
| 192 | + /// will be cancelled before validations in default policy. |
| 193 | + /// |
| 194 | + /// The default value is `FALSE`. |
| 195 | + [propput] HRESULT CancelSave([in] BOOL value); |
| 196 | + |
| 197 | + /// Get the extension of file to be saved. |
| 198 | + /// |
| 199 | + /// The file extension is the extension portion of the FilePath, |
| 200 | + /// preserving original case. |
| 201 | + /// |
| 202 | + /// Only final extension with period "." will be provided. For example, |
| 203 | + /// "*.tar.gz" is a double extension, where the ".gz" will be its |
| 204 | + /// final extension. |
| 205 | + /// |
| 206 | + /// File extension can be empty, if the file name has no extension |
| 207 | + /// at all. |
| 208 | + [propget] HRESULT FileExtension([out, retval] LPWSTR* value); |
| 209 | + |
| 210 | + /// Get the full path of file to be saved. This includes the |
| 211 | + /// file name and extension. |
| 212 | + /// |
| 213 | + /// This method doesn't provide path validation, the returned |
| 214 | + /// string may longer than MAX_PATH. |
| 215 | + [propget] HRESULT FilePath([out, retval] LPWSTR* value); |
| 216 | + |
| 217 | + /// Gets the `SuppressDefaultPolicy` property. |
| 218 | + [propget] HRESULT SuppressDefaultPolicy([out, retval] BOOL* value); |
| 219 | + |
| 220 | + /// Set if the default policy checking and security warning will be |
| 221 | + /// suppressed. `TRUE` means it will be suppressed. |
| 222 | + /// |
| 223 | + /// The default value is `FALSE`. |
| 224 | + [propput] HRESULT SuppressDefaultPolicy([in] BOOL value); |
| 225 | + |
| 226 | + /// The document origin URI of this file save operation. |
| 227 | + [propget] HRESULT DocumentOriginUri([out, retval] LPWSTR* value); |
| 228 | + |
| 229 | + /// Returns an `ICoreWebView2Deferral` object. Use this operation to complete |
| 230 | + /// the SaveFileSecurityCheckStartingEvent. |
| 231 | + /// |
| 232 | + /// The default policy checking and any default UI will be blocked temporarily, |
| 233 | + /// saving file to local won't start, until the deferral is completed. |
| 234 | + HRESULT GetDeferral( |
| 235 | + [out, retval] ICoreWebView2Deferral** value |
| 236 | + ); |
| 237 | +} |
| 238 | + |
| 239 | +/// Receives `SaveFileSecurityCheckStarting` events. |
| 240 | +interface ICoreWebView2SaveFileSecurityCheckStartingEventHandler : IUnknown { |
| 241 | + /// Provides the event args for the corresponding event. |
| 242 | + HRESULT Invoke( |
| 243 | + [in] ICoreWebView2* sender, |
| 244 | + [in] ICoreWebView2SaveFileSecurityCheckStartingEventArgs* args); |
| 245 | +} |
| 246 | +``` |
| 247 | + |
| 248 | +## .Net/ WinRT |
| 249 | +```c# (but really MIDL3) |
| 250 | +namespace Microsoft.Web.WebView2.Core |
| 251 | +{ |
| 252 | + |
| 253 | + runtimeclass CoreWebView2SaveFileSecurityCheckStartingEventArgs; |
| 254 | + runtimeclass CoreWebView2; |
| 255 | + |
| 256 | + runtimeclass CoreWebView2SaveFileSecurityCheckStartingEventArgs |
| 257 | + { |
| 258 | + Boolean CancelSave { get; set; }; |
| 259 | + String FileExtension { get; }; |
| 260 | + String FilePath { get; }; |
| 261 | + Boolean SuppressDefaultPolicy { get; set; }; |
| 262 | + String DocumentOriginUri { get; }; |
| 263 | + Windows.Foundation.Deferral GetDeferral(); |
| 264 | + }; |
| 265 | + |
| 266 | + runtimeclass CoreWebView2 |
| 267 | + { |
| 268 | + // ... |
| 269 | + |
| 270 | + [interface_name("Microsoft.Web.WebView2.Core.ICoreWebView2_23")] |
| 271 | + { |
| 272 | + event Windows.Foundation.TypedEventHandler |
| 273 | + <CoreWebView2, CoreWebView2SaveFileSecurityCheckStartingEventArgs> SaveFileSecurityCheckStarting; |
| 274 | + } |
| 275 | + }; |
| 276 | +} |
| 277 | +``` |
0 commit comments