-
Notifications
You must be signed in to change notification settings - Fork 10.5k
InputFile follow-up #25248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
InputFile follow-up #25248
Conversation
| { | ||
| get | ||
| { | ||
| ValidateFiles(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we do this in InputFile.NotifyChange? That way the component is responsible for the code and this is a POCO type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pranavkm How would the developer catch this exception? NotifyChange is initiated from a JS callback.
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework> | ||
| <Description>A collection of Blazor components for the web.</Description> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| <Description>A collection of Blazor components for the web.</Description> | |
| <Description>Provides functionality for storing protected data using the browser's localStorage and sessionStorage APIs.</Description> |
| /// Opens the stream for reading the uploaded file. | ||
| /// </summary> | ||
| /// <param name="maxAllowedSize"> | ||
| /// The maximum size that can be read from the Stream. Defaults to 500kb. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// The maximum size that can be read from the Stream. Defaults to 500kb. | |
| /// The maximum number of bytes that can be supplied by the Stream. Defaults to 500 KB. |
| /// The maximum size that can be read from the Stream. Defaults to 500kb. | ||
| /// <para> | ||
| /// Calling <see cref="OpenReadStream(long, CancellationToken)"/> | ||
| /// will throw if the uploaded file size, as specified by <see cref="Size"/> is larger than |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// will throw if the uploaded file size, as specified by <see cref="Size"/> is larger than | |
| /// will throw if the file's size, as specified by <see cref="Size"/> is larger than |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recommend we avoid using the term "upload" anywhere, since (1) the file hasn't actually been transferred by the time this method runs, and (2) in the wasm case, the file doesn't necessarily leave the user's computer at all.
People have been confused into thinking this is about HTTP uploads, but it's not - it's about making file data readable by .NET code.
| /// <para> | ||
| /// Calling <see cref="OpenReadStream(long, CancellationToken)"/> | ||
| /// will throw if the uploaded file size, as specified by <see cref="Size"/> is larger than | ||
| /// <paramref name="maxAllowedSize"/>. By default, uploading a file larger than 500kb will result in an exception. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// <paramref name="maxAllowedSize"/>. By default, uploading a file larger than 500kb will result in an exception. | |
| /// <paramref name="maxAllowedSize"/>. By default, if the user supplies a file larger than 500 KB, this method will throw an exception. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switching to more active voice to clarify who are the actors that supply files and throw exceptions.
| using System.Runtime.Versioning; | ||
|
|
||
| [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Components.ProtectedBrowserStorage.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] | ||
| [assembly: UnsupportedOSPlatform("browser")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is super neat - I love it
| /// When specified, it is important to choose a value based on the largest size the calling code is expected to handle. | ||
| /// Specifying an arbitrary large value may by a vector for resource exhaustion attacks in particular in Blazor Server. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest avoiding the term "vector" as it's a bit jargony outside security circles. Here's an attempt to phrase it more approachably for beginners, but please feel free to use your own description if you prefer!
| /// When specified, it is important to choose a value based on the largest size the calling code is expected to handle. | |
| /// Specifying an arbitrary large value may by a vector for resource exhaustion attacks in particular in Blazor Server. | |
| /// It is valuable to choose a size limit that corresponds to your use case. If you allow excessively large files, this | |
| /// may result in excessive consumption of memory or disk/database space, depending on what your code does | |
| /// with the supplied Stream. | |
| /// </para> | |
| /// <para> | |
| /// For Blazor Server in particular, beware of reading the entire stream into a memory buffer unless you have | |
| /// passed a suitably low size limit, since you will be consuming that memory on the server. |
| /// </param> | ||
| /// <param name="cancellationToken">A cancellation token to signal the cancellation of streaming file data.</param> | ||
| Stream OpenReadStream(CancellationToken cancellationToken = default); | ||
| /// <exception cref="IOException">Thrown if the Stream</exception> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// <exception cref="IOException">Thrown if the Stream</exception> | |
| /// <exception cref="IOException">Thrown if the file's length exceeds the <paramref name="maxAllowedSize"/> value.</exception> |
| /// Gets or sets the maximum allowed file size in bytes. Defaults to 15 MB. | ||
| /// </summary> | ||
| [Parameter] | ||
| public long MaxFileSize { get; set; } = 1024 * 1024 * 15; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pranavkm Are you removing this now the check is done on OpenReadStream?
| /// Gets or sets the maximum allowed number of files. Defaults to 3. | ||
| /// </summary> | ||
| [Parameter] | ||
| public int MaxAllowedFiles { get; set; } = 3; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
3 is an awkward default because a developer might realistically test uploading 2 or 3 files and believe that they support an arbitrary number. Personally I think that either:
- We should change this to a much higher value just to catch extreme edge cases, rather than interfering with typical use cases. For example, to 100. Then we reason that typical change-handlers are going to process the files in series, not in parallel, so it's not increasing the total amount of memory used if you're reading files into memory sequentially. It just affects total storage space used if you are dumping these to disk/DB, but that's the same as for any other case where a user can write stuff to disk/DB - they can typically do it arbitrarily many times anyway, unless the developer writes logic to impose limits (which is the same here).
- Or we should change it to 1, so developers who intend to support multiple file uploads will certainly hit it in their most basic local verification, and then know they have to pick a value themselves.
| public static class BrowserFileExtensions | ||
| { | ||
| /// <summary> | ||
| /// Converts the current image file to a new one of the specified file type and maximum file dimensions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// Converts the current image file to a new one of the specified file type and maximum file dimensions. | |
| /// Attempts to convert the current image file to a new one of the specified image format and maximum dimensions. | |
| /// Caution: there is no guarantee that the file will be converted, or will even be a valid image file at all, either | |
| /// before or after conversion. The conversion is requested within the browser before it is transferred to .NET | |
| /// code. The resulting data should be treated as untrusted. |
ea9df71 to
2f92e84
Compare
|
Hey @pranavkm, sorry for the scope creep, but @javiercn and I discussed this earlier today and considered the
If you want, I'm happy to implement that myself. Or if you want to do it, or if you disagree with the design, those are fine too. If we do this, I'd recommend we don't require the
I know this is quite scope-creepy. If you don't feel this is a good direction for us (or if you just want to punt it to RC2, maybe for someone else to implement) please say so! |
|
The alternate approach would be replacing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this really happen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been trying to work that out for the last little while, and it seems like no. I thought the user might be able to do some obscure gesture like focusing and pressing delete to clear the file input, but no gesture I've tried so far has that effect. The HTML spec doesn't seem to address this question directly.
Since there's no indication from my research that it's possible, I'll change this case to throw on the grounds that it's not meant to be possible. If somehow it does become possible in the future it would be awkward for us though - either we'd need a breaking change, or our API here would be kind of messy (you'd have to check the file count before accessing the File property).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GetFiles? This feels like a weird name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Counter offer: GetMultipleFiles
I'm just trying to make it clear that you're only meant to use this if your scenario involves multiple.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds reasonable.
|
Note: the reason I'm going with the |
SteveSandersonMS
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm approving everyone else's changes in this PR. @pranavkm, can you also post your approval here if you're happy with my changes?
* Move ProtectedBrowserStorage to it's own package * Mark Components.Web.Extensions as non-shipping until we can move it over * Allow InputFile.OpenReadStreamAsync to specify an expected file size.
3e4d040 to
1abc8a6
Compare
| public IBrowserFile File => _files.Count switch | ||
| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
|
Hello human! Please make sure you've included the Shiproom Template in a comment or (preferably) the PR description. Also, make sure this PR is not marked as a draft and is ready-to-merge. |
src/Components/Web.Extensions/src/Microsoft.AspNetCore.Components.Web.Extensions.csproj
Show resolved
Hide resolved
|
Thanks - approved for RC1, pending CI completion. |
This PR addresses feedback from the API and security reviews for #24640.
Also moved
InputFilefromM.A.Components.Web.ExtensionstoM.A.Components.Forms.Ask-mode template
Description
This PR addresses feedback from the API and security reviews for #24640.
Customer Impact
If we don't do this, we'll ship APIs in RC1 that we immediately intend to break in RC2. Additionally, not doing it means we won't have the desired security characteristics.
Regression?
No, this is a new feature
Risk
This change reduces risk because it reflects security/API review outcomes.