Skip to content

System.Uri does not compress paths when an authority is not present #123750

@stfnw

Description

@stfnw

Description

During playing around with property based testing of the .NET URI parsing implementation I noticed the following violations of expected properties:

Reproduction Steps

  • URI normalization / canonicalization is not idempotent.
    (normalizing twice is not the same as normalizing once).

    let uri = "https://microsoft.com/%20"
    let normalized1 = System.Uri(uri, System.UriKind.Absolute).ToString()
    let normalized2 = System.Uri(normalized1, System.UriKind.Absolute).ToString()
    printfn "%A" normalized1                    // "https://microsoft.com/ "
    printfn "%A" normalized2                    // "https://microsoft.com/"
    printfn "%A" (normalized1 = normalized2)    // false
  • URI normalization / canonicalization is not idempotent.
    (normalizing twice is not the same as normalizing once).

    let uri = "aaaa:b%7c/"
    let normalized1 = System.Uri(uri, System.UriKind.Absolute).ToString()
    let normalized2 = System.Uri(normalized1, System.UriKind.Absolute).ToString()
    printfn "%A" normalized1                    // "aaaa:b|/"
    printfn "%A" normalized2                    // "aaaa:b:/"
    printfn "%A" (normalized1 = normalized2)    // false
  • URI normalization / canonicalization results in path with unresolved/not removed dot segments.
    Compare https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4 Remove Dot Segments
    (May be somewhat related to [Uri] System.Uri does not compact empty path segments #31300)

    let uri = "xmgz:/.///b?y"
    let normalized = System.Uri(System.Uri( uri, System.UriKind.Absolute).ToString(), System.UriKind.Absolute)
    printfn "uri: %A" (normalized.ToString())               // "xmgz:/.///b?y"
    printfn "path: %A" normalized.AbsolutePath              // "/.///b"
    printfn "path segments: %A" normalized.Segments         // [|"/"; "./"; "/"; "/"; "b"|]

It could be that some of these examples are false positives and that I misunderstood the desired behavior.
Even if they are true positives, I don't think they are security-relevant.

The examples are fully self-contained; each given example can be run with dotnet fsi <inputfile.fsx> (where <inputfile.fsx> simply contains exactly the code from the respective snippet).

For background information on generating examples compare also https://github.com/stfnw/talk-introduction-to-property-based-testing/blob/4afa26d76f9df193e2ec57c9bee5a9c02a060b82/code/uri-parsing.fsx.

Expected behavior

The stated properties should intuitively hold.

Actual behavior

The properties don't hold.

Regression?

N/A

Known Workarounds

N/A

Configuration

# dotnet fsi --version
Microsoft (R) F# Interactive version 14.0.102.0 for F# 10.0

# uname -a
Linux 16e9db91a078 6.12.63+deb13-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.63-1 (2025-12-30) x86_64 GNU/Linux

# # installed from Microsoft repos https://packages.microsoft.com/debian/13/prod trixie/main amd64 Packages

# dpkg -l  dotnet-*-10.0
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name                       Version      Architecture Description
+++-==========================-============-============-============================================
ii  dotnet-apphost-pack-10.0   10.0.2       amd64        Microsoft.NETCore.App.Host 10.0.2
ii  dotnet-hostfxr-10.0        10.0.2       amd64        Microsoft .NET Host FX Resolver - 10.0.2
ii  dotnet-runtime-10.0        10.0.2       amd64        Microsoft.NETCore.App.Runtime.CoreCLR 10.0.2
ii  dotnet-runtime-deps-10.0   10.0.2       amd64        dotnet-runtime-deps-debian 10.0.2
ii  dotnet-sdk-10.0            10.0.102     amd64        Microsoft .NET SDK 10.0.102
ii  dotnet-targeting-pack-10.0 10.0.2       amd64        Microsoft.NETCore.App.Ref 10.0.2

# dpkg -s dotnet-sdk-10.0
Package: dotnet-sdk-10.0
Status: install ok installed
Priority: standard
Section: libs
Installed-Size: 411370
Maintainer: .NET Team <dotnetpackages@dotnetfoundation.org>
Architecture: amd64
Version: 10.0.102
Depends: dotnet-runtime-10.0 (>= 10.0.2), dotnet-targeting-pack-10.0 (>= 10.0.2), dotnet-apphost-pack-10.0 (>= 10.0.2), aspnetcore-runtime-10.0 (>= 10.0.2), aspnetcore-targeting-pack-10.0 (>= 10.0.2)
Description: Microsoft .NET SDK 10.0.102
   .NET is a development platform that you can use to build command-line applications, microservices and modern websites. It is open source, cross-platform and is supported by Microsoft. We hope you enjoy using it! If you do, please consider joining the active community of developers that are contributing to the project on GitHub (https://github.com/dotnet/core). We happily accept issues and PRs.
Homepage: https://github.com/dotnet/core

# dpkg -s dotnet-runtime-10.0
Package: dotnet-runtime-10.0
Status: install ok installed
Priority: standard
Section: libs
Installed-Size: 80278
Maintainer: .NET Team <dotnetpackages@dotnetfoundation.org>
Architecture: amd64
Version: 10.0.2
Depends: dotnet-hostfxr-10.0 (>= 10.0.2), dotnet-runtime-deps-10.0 (>= 10.0.2)
Description: Microsoft.NETCore.App.Runtime.CoreCLR 10.0.2
   .NET is a development platform that you can use to build command-line applications, microservices and modern websites. It is open source, cross-platform and is supported by Microsoft. We hope you enjoy using it! If you do, please consider joining the active community of developers that are contributing to the project on GitHub (https://github.com/dotnet/core). We happily accept issues and PRs.
Homepage: https://github.com/dotnet/core

Other information

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions