microsoftgraph / msgraph-sdk-dotnet

Microsoft Graph Client Library for .NET!
https://graph.microsoft.com
Other
695 stars 246 forks source link

Unable to upload a file whose name contains a URL-encoded slash character #2565

Open mfeingol opened 3 months ago

mfeingol commented 3 months ago

Describe the bug

For reasons of my own, I'm trying to upload a file with the following name: A%2FB%2BC+D.zip. This is a URL-encoded form of the string "A/B+C D.zip"

With version 4.54.0 I get a new directory named A and a file inside that directory named B+C+D.zip. This is obviously incorrect behavior.

With version 5.56.0, I get the following Microsoft.Graph.Models.ODataErrors.ODataError exception when calling CreateUploadSession:

"Name specified in the path doesn't match the name specified in the request body."
Code = "invalidRequest"

This too is incorrect behavior.

Update: if my filename contains an encoded + (i.e. "%2B") that also causes CreateUploadSession to throw the same exception. Clearly something is attempting to decode the name improperly.

Expected behavior

I'd expect the file to be uploaded with precisely the specified name, with no alterations performed by the underlying SDK or server side. None of the characters in the name are illegal, to the best of my understanding.

How to reproduce

5.56.0 code:

GraphServiceClient graph = ...;
string driveId = ...;
string folderId = ...;
string name = "A%2FB%2BC+D.zip";

Microsoft.Graph.Drives.Item.Items.Item.CreateUploadSession.CreateUploadSessionPostRequestBody body = new()
{
    Item = new() { Name = name }
};

UploadSession upload = await graph.Drives[driveId].Items[folderId].ItemWithPath(name).CreateUploadSession.PostAsync(body, cancellationToken: cancellationToken);

SDK Version

5.56.0

Latest version known to work for scenario above?

No response

Known Workarounds

No response

Debug output

No response

Configuration

Windows 11, x64, probably not specific to this configuration.

Other information

No response

andrueastman commented 3 months ago

Thanks for raising this @mfeingol

I believe this is related to https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/2541 as the name in the url should not need to be encoded.

Any chance it works for you if you change the code to

string name = "A/B+C D.zip"; // remove an encoding
Microsoft.Graph.Drives.Item.Items.Item.CreateUploadSession.CreateUploadSessionPostRequestBody body = new()
{
    Item = new() { Name = name }
};

UploadSession upload = await graph.Drives[driveId].Items[folderId].ItemWithPath(name).CreateUploadSession.PostAsync(body, cancellationToken: cancellationToken);
mfeingol commented 3 months ago

I tried uploading the following file name: d90eec56-abe2-4c71-9694-1abd2ce3c063;2024-07-03;9;A/+/+B;1.zip

Same Microsoft.Graph.Models.ODataErrors.ODataError exception - "Name specified in the path doesn't match the name specified in the request body."

the name in the url should not need to be encoded.

But either way, if I'm specifying a valid string for a file name, encoded or not, that string should reach the OneDrive filesystem untouched and shouldn't be decoded anywhere along the way.

andrueastman commented 2 months ago

Are you able to make a successful request on the graph explorer? Or by specifying the name property in the url but not in the request body (or vice versa)?

mfeingol commented 2 months ago

@andrueastman:

Are you able to make a successful request on the graph explorer?

I'm not sure how to upload a file from the Graph Explorer. I just see a text field for the PUT body; is there a way to provide file contents in there?

Or by specifying the name property in the url but not in the request body (or vice versa)?

If I don't specify the Name property on the item, the upload succeeds but my url-encoded name is cracked and mayhem ensues. E.g. a url-encoded / + / + /;1.zip file name becomes two nested subdirectories named "+++" with a file named ";1.zip" - i.e. +++\+++\;1.zip

andrueastman commented 2 months ago

Just to confirm. Are you able to specify this in the body but not in the path?

mfeingol commented 2 months ago

@andrueastman: I'm not sure how to do that programmatically. See above for the code I'm using.

andrueastman commented 2 months ago

@mfeingol Taking a look at the one drive documentation, it looks like the API won't accept \ as part of a file or folder name. But used as a path separator.

https://learn.microsoft.com/en-us/onedrive/developer/rest-api/concepts/addressing-driveitems?view=odsp-graph-online#onedrive-reserved-characters

Any chance you can confirm if it's possible to make the request directly using the rest API?

mfeingol commented 2 months ago

@andrueastman: the filename I'm providing is url-encoded. There is no \ in the name itself. Something in the code path is decoding my url-encoded name, which it shouldn't be doing.

Any chance you can confirm if it's possible to make the request directly using the rest API?

I absolutely can, if you can give me a hint on how to do this correctly. E.g. some C# code I can run?

andrueastman commented 2 months ago

I absolutely can, if you can give me a hint on how to do this correctly. E.g. some C# code I can run?

Any chance you can try using a raw requests with curl, Postman etc...?

@mfeingol Taking a look at the one drive documentation, it looks like the API won't accept \ as part of a file or folder name. But used as a path separator.

What I mean here is that the API won't accept the characters as part of the file name even if they are url encoded. The API doesn't allow them and will throw an error for them...

mfeingol commented 1 month ago

If you can give me an example REST request, I can do that. I'm not super familiar with the OD API.

Reading that documentation you linked, I see no prohibition on url-encoding any character, even slashes or backslashes. Can you quote the specific language you're referring to?

mfeingol commented 1 month ago

I captured a REST request using Fiddler and was able to reproduce the createUploadSession in a tool (Imsomnia).

With no % in the file name and in the body, the request returns 200. With a % in the file name and in the body, the request returns 400. With a % in the file name and an empty body, the request returns 200, but uploading content results in the server side interpreting the %2F as a directory separator (!)

Conclusion: the server side is kind of broken here. However... I'm able to create a file named a2%fb.text in my OneDrive folder. So ... how do I do that programmatically, if the obvious answer (escaping) doesn't work?