diff --git a/.github/policies/resourceManagement.yml b/.github/policies/resourceManagement.yml index 9ee76e96a12c..e96082f1f947 100644 --- a/.github/policies/resourceManagement.yml +++ b/.github/policies/resourceManagement.yml @@ -539,7 +539,7 @@ configuration: then: - removeMilestone - addMilestone: - milestone: 9.0.1 + milestone: 9.0.2 description: '[Milestone Assignments] Assign Milestone to PRs merged to release/9.0 branch' - if: - payloadType: Pull_Request @@ -550,7 +550,7 @@ configuration: then: - removeMilestone - addMilestone: - milestone: 8.0.12 + milestone: 8.0.13 description: '[Milestone Assignments] Assign Milestone to PRs merged to release/8.0 branch' - if: - payloadType: Issues diff --git a/.github/workflows/update-sdk.yml b/.github/workflows/update-sdk.yml index c96f6b550c5c..b56cc522439a 100644 --- a/.github/workflows/update-sdk.yml +++ b/.github/workflows/update-sdk.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: martincostello/update-dotnet-sdk@67d6e2b14939c06978a7f80444157296c3defe14 # v3.2.3 + - uses: martincostello/update-dotnet-sdk@76e2c0df2303d4f6a404228105ebb7d60ace0556 # v3.4.0 with: quality: 'daily' repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/docs/Servicing.md b/docs/Servicing.md index a958fb955260..d646a59c0766 100644 --- a/docs/Servicing.md +++ b/docs/Servicing.md @@ -4,7 +4,7 @@ We maintain several on-going releases at once and produce patches for them. An e See the [.NET Core release lifecycle](https://dotnet.microsoft.com/platform/support/policy/dotnet-core#lifecycle) for more details on the currently-supported .NET releases. -The status of current servicing fixes can be found on the [Servicing Status](https://github.com/dotnet/aspnetcore/projects/11) GitHub project. +The status of current servicing fixes can be found on the [Servicing Status](https://github.com/orgs/dotnet/projects/448) GitHub project. ## Servicing Bar diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 679f2ad3e510..7795d451a441 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -9,329 +9,329 @@ --> - + https://github.com/dotnet/efcore - b8ddc5b1f9e8a0287d0b15b933c991b61806c2b4 + 6119066c37b98f604a5c868d691da0405d53e1aa - + https://github.com/dotnet/efcore - b8ddc5b1f9e8a0287d0b15b933c991b61806c2b4 + 6119066c37b98f604a5c868d691da0405d53e1aa - + https://github.com/dotnet/efcore - b8ddc5b1f9e8a0287d0b15b933c991b61806c2b4 + 6119066c37b98f604a5c868d691da0405d53e1aa - + https://github.com/dotnet/efcore - b8ddc5b1f9e8a0287d0b15b933c991b61806c2b4 + 6119066c37b98f604a5c868d691da0405d53e1aa - + https://github.com/dotnet/efcore - b8ddc5b1f9e8a0287d0b15b933c991b61806c2b4 + 6119066c37b98f604a5c868d691da0405d53e1aa - + https://github.com/dotnet/efcore - b8ddc5b1f9e8a0287d0b15b933c991b61806c2b4 + 6119066c37b98f604a5c868d691da0405d53e1aa - + https://github.com/dotnet/efcore - b8ddc5b1f9e8a0287d0b15b933c991b61806c2b4 + 6119066c37b98f604a5c868d691da0405d53e1aa - + https://github.com/dotnet/efcore - b8ddc5b1f9e8a0287d0b15b933c991b61806c2b4 + 6119066c37b98f604a5c868d691da0405d53e1aa - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a https://github.com/dotnet/xdt @@ -343,9 +343,9 @@ 63ae81154c50a1cf9287cc47d8351d55b4289e6d - + https://github.com/dotnet/source-build-reference-packages - b24c3aa9f120f83befa255d5d5f465b961f387fb + 72009fb6ce7327430539004be1dcbfb6fb88adab @@ -371,64 +371,64 @@ afdd413cee50c16318620252e4e64dc326e2d300 - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/source-build-externals - 591e522d15c8c9ffad7c7c1df1ae6a3d392717b4 + 523fb6eeffb1c912c6c975fc6b1d07265460676a - + https://github.com/dotnet/runtime - 208b974f93c0ee35a171eba374ca36aa9a78e930 + 289aa17cac46fc4c2fb3e5889030f5379ca8e92a - + https://github.com/dotnet/arcade - ae8d2a08f1021624ae37cdd5b65f6c05e58a051b + e58820063a8754d418518bce69ca2df0e3b4ac25 - + https://github.com/dotnet/arcade - ae8d2a08f1021624ae37cdd5b65f6c05e58a051b + e58820063a8754d418518bce69ca2df0e3b4ac25 - + https://github.com/dotnet/arcade - ae8d2a08f1021624ae37cdd5b65f6c05e58a051b + e58820063a8754d418518bce69ca2df0e3b4ac25 - + https://github.com/dotnet/arcade - ae8d2a08f1021624ae37cdd5b65f6c05e58a051b + e58820063a8754d418518bce69ca2df0e3b4ac25 - + https://github.com/dotnet/arcade - ae8d2a08f1021624ae37cdd5b65f6c05e58a051b + e58820063a8754d418518bce69ca2df0e3b4ac25 - + https://github.com/dotnet/arcade - ae8d2a08f1021624ae37cdd5b65f6c05e58a051b + e58820063a8754d418518bce69ca2df0e3b4ac25 - + https://github.com/dotnet/arcade - ae8d2a08f1021624ae37cdd5b65f6c05e58a051b + e58820063a8754d418518bce69ca2df0e3b4ac25 - + https://github.com/dotnet/arcade - ae8d2a08f1021624ae37cdd5b65f6c05e58a051b + e58820063a8754d418518bce69ca2df0e3b4ac25 - + https://github.com/dotnet/extensions - 20c12ef61fc33865f36c1f4f6e8e2240e8c25f32 + dbb12c368fab1914e847c6b07f020c1c116dc429 - + https://github.com/dotnet/extensions - 20c12ef61fc33865f36c1f4f6e8e2240e8c25f32 + dbb12c368fab1914e847c6b07f020c1c116dc429 https://github.com/nuget/nuget.client @@ -442,25 +442,25 @@ https://github.com/nuget/nuget.client 8fef55f5a55a3b4f2c96cd1a9b5ddc51d4b927f8 - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 1ad7348528f8545b2864b958f298934c348075a7 + 0eb38b09e89e233cf7cd056fad672627ea2fa05a - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 1ad7348528f8545b2864b958f298934c348075a7 + 0eb38b09e89e233cf7cd056fad672627ea2fa05a - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 1ad7348528f8545b2864b958f298934c348075a7 + 0eb38b09e89e233cf7cd056fad672627ea2fa05a - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 1ad7348528f8545b2864b958f298934c348075a7 + 0eb38b09e89e233cf7cd056fad672627ea2fa05a - + https://dev.azure.com/dnceng/internal/_git/dotnet-optimization - 1ad7348528f8545b2864b958f298934c348075a7 + 0eb38b09e89e233cf7cd056fad672627ea2fa05a diff --git a/eng/Versions.props b/eng/Versions.props index b9cc1fc53df4..1879bc5c8448 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -67,93 +67,93 @@ --> - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 - 10.0.0-alpha.1.24619.8 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 + 10.0.0-alpha.1.25059.19 - 9.1.0-preview.1.24620.1 - 9.1.0-preview.1.24620.1 + 9.1.0-preview.1.25056.1 + 9.1.0-preview.1.25056.1 - 10.0.0-alpha.1.24616.3 - 10.0.0-alpha.1.24616.3 - 10.0.0-alpha.1.24616.3 - 10.0.0-alpha.1.24616.3 - 10.0.0-alpha.1.24616.3 - 10.0.0-alpha.1.24616.3 - 10.0.0-alpha.1.24616.3 - 10.0.0-alpha.1.24616.3 + 10.0.0-alpha.1.25057.2 + 10.0.0-alpha.1.25057.2 + 10.0.0-alpha.1.25057.2 + 10.0.0-alpha.1.25057.2 + 10.0.0-alpha.1.25057.2 + 10.0.0-alpha.1.25057.2 + 10.0.0-alpha.1.25057.2 + 10.0.0-alpha.1.25057.2 4.13.0-3.24613.7 4.13.0-3.24613.7 @@ -166,26 +166,26 @@ 6.2.4 6.2.4 - 10.0.0-beta.24626.1 - 10.0.0-beta.24626.1 - 10.0.0-beta.24626.1 - 10.0.0-beta.24626.1 - 10.0.0-beta.24626.1 + 10.0.0-beta.25056.1 + 10.0.0-beta.25056.1 + 10.0.0-beta.25056.1 + 10.0.0-beta.25056.1 + 10.0.0-beta.25056.1 - 10.0.562301 + 10.0.605601 - 10.0.561602 + 10.0.605602 2.2.0-beta.24622.1 10.0.0-preview.24609.2 10.0.0-preview.24609.2 - 1.0.0-prerelease.24612.6 - 1.0.0-prerelease.24612.6 - 1.0.0-prerelease.24612.6 - 1.0.0-prerelease.24612.6 - 1.0.0-prerelease.24612.6 + 1.0.0-prerelease.25053.1 + 1.0.0-prerelease.25053.1 + 1.0.0-prerelease.25053.1 + 1.0.0-prerelease.25053.1 + 1.0.0-prerelease.25053.1 - + - + diff --git a/eng/targets/Helix.targets b/eng/targets/Helix.targets index 43bf0cb7f7f1..70e01877befa 100644 --- a/eng/targets/Helix.targets +++ b/eng/targets/Helix.targets @@ -19,7 +19,7 @@ $(HelixQueueAlmaLinux8); $(HelixQueueAlpine318); $(HelixQueueDebian12); - $(HelixQueueFedora38); + $(HelixQueueFedora40); $(HelixQueueMariner); Ubuntu.2004.Amd64.Open; diff --git a/global.json b/global.json index 1b175fca51f6..c1270224e337 100644 --- a/global.json +++ b/global.json @@ -1,9 +1,9 @@ { "sdk": { - "version": "10.0.100-alpha.1.24616.1" + "version": "10.0.100-alpha.1.25056.1" }, "tools": { - "dotnet": "10.0.100-alpha.1.24616.1", + "dotnet": "10.0.100-alpha.1.25056.1", "runtimes": { "dotnet/x86": [ "$(MicrosoftInternalRuntimeAspNetCoreTransportVersion)" @@ -27,9 +27,9 @@ "jdk": "11.0.24" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.24626.1", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.24626.1", - "Microsoft.DotNet.SharedFramework.Sdk": "10.0.0-beta.24626.1", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25056.1", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25056.1", + "Microsoft.DotNet.SharedFramework.Sdk": "10.0.0-beta.25056.1", "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.4.0" } diff --git a/src/Components/Components/src/RouteView.cs b/src/Components/Components/src/RouteView.cs index e5c0a5e2916c..46cda45a0fed 100644 --- a/src/Components/Components/src/RouteView.cs +++ b/src/Components/Components/src/RouteView.cs @@ -28,9 +28,6 @@ static RouteView() } } - [Inject] - private NavigationManager NavigationManager { get; set; } - /// /// Gets or sets the route data. This determines the page that will be /// displayed and the parameter values that will be supplied to the page. diff --git a/src/Components/Forms/src/EditContextDataAnnotationsExtensions.cs b/src/Components/Forms/src/EditContextDataAnnotationsExtensions.cs index 2fb2a68b6d0d..e15b90c0e42d 100644 --- a/src/Components/Forms/src/EditContextDataAnnotationsExtensions.cs +++ b/src/Components/Forms/src/EditContextDataAnnotationsExtensions.cs @@ -52,10 +52,12 @@ public static IDisposable EnableDataAnnotationsValidation(this EditContext editC private static event Action? OnClearCache; +#pragma warning disable IDE0051 // Remove unused private members private static void ClearCache(Type[]? _) { OnClearCache?.Invoke(); } +#pragma warning restore IDE0051 // Remove unused private members private sealed class DataAnnotationsEventSubscriptions : IDisposable { diff --git a/src/DataProtection/DataProtection/src/AuthenticatedEncryption/ConfigurationModel/ManagedAuthenticatedEncryptorConfiguration.cs b/src/DataProtection/DataProtection/src/AuthenticatedEncryption/ConfigurationModel/ManagedAuthenticatedEncryptorConfiguration.cs index 9ee20854dae7..844042cf14d9 100644 --- a/src/DataProtection/DataProtection/src/AuthenticatedEncryption/ConfigurationModel/ManagedAuthenticatedEncryptorConfiguration.cs +++ b/src/DataProtection/DataProtection/src/AuthenticatedEncryption/ConfigurationModel/ManagedAuthenticatedEncryptorConfiguration.cs @@ -77,34 +77,4 @@ void IInternalAlgorithmConfiguration.Validate() using var encryptor = factory.CreateAuthenticatedEncryptorInstance(secret, this); encryptor.PerformSelfTest(); } - - // Any changes to this method should also be be reflected - // in ManagedAuthenticatedEncryptorDescriptorDeserializer.FriendlyNameToType. - private static string TypeToFriendlyName(Type type) - { - if (type == typeof(Aes)) - { - return nameof(Aes); - } - else if (type == typeof(HMACSHA1)) - { - return nameof(HMACSHA1); - } - else if (type == typeof(HMACSHA256)) - { - return nameof(HMACSHA256); - } - else if (type == typeof(HMACSHA384)) - { - return nameof(HMACSHA384); - } - else if (type == typeof(HMACSHA512)) - { - return nameof(HMACSHA512); - } - else - { - return type.AssemblyQualifiedName!; - } - } } diff --git a/src/DataProtection/DataProtection/src/KeyManagement/KeyRingBasedDataProtector.cs b/src/DataProtection/DataProtection/src/KeyManagement/KeyRingBasedDataProtector.cs index ed0e20cdd6cd..9f19e137a48f 100644 --- a/src/DataProtection/DataProtection/src/KeyManagement/KeyRingBasedDataProtector.cs +++ b/src/DataProtection/DataProtection/src/KeyManagement/KeyRingBasedDataProtector.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Buffers.Binary; +using System.Buffers; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -14,6 +16,8 @@ using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal; using Microsoft.AspNetCore.Shared; using Microsoft.Extensions.Logging; +using System.Buffers.Text; +using Microsoft.AspNetCore.DataProtection.Internal; namespace Microsoft.AspNetCore.DataProtection.KeyManagement; @@ -313,39 +317,13 @@ private static void WriteBigEndianInteger(byte* ptr, uint value) ptr[3] = (byte)(value); } - private struct AdditionalAuthenticatedDataTemplate + internal struct AdditionalAuthenticatedDataTemplate { private byte[] _aadTemplate; - public AdditionalAuthenticatedDataTemplate(IEnumerable purposes) + public AdditionalAuthenticatedDataTemplate(string[] purposes) { - const int MEMORYSTREAM_DEFAULT_CAPACITY = 0x100; // matches MemoryStream.EnsureCapacity - var ms = new MemoryStream(MEMORYSTREAM_DEFAULT_CAPACITY); - - // additionalAuthenticatedData := { magicHeader (32-bit) || keyId || purposeCount (32-bit) || (purpose)* } - // purpose := { utf8ByteCount (7-bit encoded) || utf8Text } - - using (var writer = new PurposeBinaryWriter(ms)) - { - writer.WriteBigEndian(MAGIC_HEADER_V0); - Debug.Assert(ms.Position == sizeof(uint)); - var posPurposeCount = writer.Seek(sizeof(Guid), SeekOrigin.Current); // skip over where the key id will be stored; we'll fill it in later - writer.Seek(sizeof(uint), SeekOrigin.Current); // skip over where the purposeCount will be stored; we'll fill it in later - - uint purposeCount = 0; - foreach (string purpose in purposes) - { - Debug.Assert(purpose != null); - writer.Write(purpose); // prepends length as a 7-bit encoded integer - purposeCount++; - } - - // Once we have written all the purposes, go back and fill in 'purposeCount' - writer.Seek(checked((int)posPurposeCount), SeekOrigin.Begin); - writer.WriteBigEndian(purposeCount); - } - - _aadTemplate = ms.ToArray(); + _aadTemplate = BuildAadTemplateBytes(purposes); } public byte[] GetAadForKey(Guid keyId, bool isProtecting) @@ -381,19 +359,57 @@ public byte[] GetAadForKey(Guid keyId, bool isProtecting) } } - private sealed class PurposeBinaryWriter : BinaryWriter + internal static byte[] BuildAadTemplateBytes(string[] purposes) { - public PurposeBinaryWriter(MemoryStream stream) : base(stream, EncodingUtil.SecureUtf8Encoding, leaveOpen: true) { } + // additionalAuthenticatedData := { magicHeader (32-bit) || keyId || purposeCount (32-bit) || (purpose)* } + // purpose := { utf8ByteCount (7-bit encoded) || utf8Text } + + var keySize = sizeof(Guid); + int totalPurposeLen = 4 + keySize + 4; - // Writes a big-endian 32-bit integer to the underlying stream. - public void WriteBigEndian(uint value) + int[]? lease = null; + var targetLength = purposes.Length; + Span purposeLengthsPool = targetLength <= 32 ? stackalloc int[targetLength] : (lease = ArrayPool.Shared.Rent(targetLength)).AsSpan(0, targetLength); + for (int i = 0; i < targetLength; i++) { - var outStream = BaseStream; // property accessor also performs a flush - outStream.WriteByte((byte)(value >> 24)); - outStream.WriteByte((byte)(value >> 16)); - outStream.WriteByte((byte)(value >> 8)); - outStream.WriteByte((byte)(value)); + string purpose = purposes[i]; + + int purposeLength = EncodingUtil.SecureUtf8Encoding.GetByteCount(purpose); + purposeLengthsPool[i] = purposeLength; + + var encoded7BitUIntLength = purposeLength.Measure7BitEncodedUIntLength(); + totalPurposeLen += purposeLength /* length of actual string */ + encoded7BitUIntLength /* length of 'string length' 7-bit encoded int */; } + + byte[] targetArr = new byte[totalPurposeLen]; + var targetSpan = targetArr.AsSpan(); + + // index 0: magic header + BinaryPrimitives.WriteUInt32BigEndian(targetSpan.Slice(0), MAGIC_HEADER_V0); + // index 4: key (skipped for now, will be populated in `GetAadForKey()`) + // index 4 + keySize: purposeCount + BinaryPrimitives.WriteInt32BigEndian(targetSpan.Slice(4 + keySize), targetLength); + + int index = 4 /* MAGIC_HEADER_V0 */ + keySize + 4 /* purposeLength */; // starting from first purpose + for (int i = 0; i < targetLength; i++) + { + string purpose = purposes[i]; + + // writing `utf8ByteCount (7-bit encoded integer)` + // we have already calculated the lengths of the purpose strings, so just get it from the pool + index += targetSpan.Slice(index).Write7BitEncodedInt(purposeLengthsPool[i]); + + // write the utf8text for the purpose + index += EncodingUtil.SecureUtf8Encoding.GetBytes(purpose, charIndex: 0, charCount: purpose.Length, bytes: targetArr, byteIndex: index); + } + + if (lease is not null) + { + ArrayPool.Shared.Return(lease); + } + Debug.Assert(index == targetArr.Length); + + return targetArr; } } diff --git a/src/DataProtection/DataProtection/src/Microsoft.AspNetCore.DataProtection.csproj b/src/DataProtection/DataProtection/src/Microsoft.AspNetCore.DataProtection.csproj index ddf556d6668c..92fa8a526a84 100644 --- a/src/DataProtection/DataProtection/src/Microsoft.AspNetCore.DataProtection.csproj +++ b/src/DataProtection/DataProtection/src/Microsoft.AspNetCore.DataProtection.csproj @@ -21,6 +21,7 @@ + diff --git a/src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/Internal/Int7BitEncodingUtilsTests.cs b/src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/Internal/Int7BitEncodingUtilsTests.cs new file mode 100644 index 000000000000..f4ba8ca0db9e --- /dev/null +++ b/src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/Internal/Int7BitEncodingUtilsTests.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.AspNetCore.DataProtection.Internal; +using Microsoft.AspNetCore.Shared; + +namespace Microsoft.AspNetCore.DataProtection.Tests.Internal; + +public class Int7BitEncodingUtilsTests +{ + [Theory] + [InlineData(0, 1)] + [InlineData(1, 1)] + [InlineData(0b0_1111111, 1)] + [InlineData(0b1_0000000, 2)] + [InlineData(0b1111111_1111111, 2)] + [InlineData(0b1_0000000_0000000, 3)] + [InlineData(0b1111111_1111111_1111111, 3)] + [InlineData(0b1_0000000_0000000_0000000, 4)] + [InlineData(0b1111111_1111111_1111111_1111111, 4)] + [InlineData(0b1_0000000_0000000_0000000_0000000, 5)] + [InlineData(uint.MaxValue, 5)] + public void Measure7BitEncodedUIntLength_ReturnsExceptedLength(uint value, int expectedSize) + { + var actualSize = value.Measure7BitEncodedUIntLength(); + Assert.Equal(expectedSize, actualSize); + } +} diff --git a/src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/KeyManagement/AdditionalAuthenticatedDataTemplateTests.cs b/src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/KeyManagement/AdditionalAuthenticatedDataTemplateTests.cs new file mode 100644 index 000000000000..adbfdf9efc8b --- /dev/null +++ b/src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/KeyManagement/AdditionalAuthenticatedDataTemplateTests.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Security.Claims; +using System.Text; +using System.Text.Unicode; +using Microsoft.AspNetCore.DataProtection.KeyManagement; +using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Moq; + +namespace Microsoft.AspNetCore.DataProtection.Tests.KeyManagement; +public class AdditionalAuthenticatedDataTemplateTests +{ + [Fact] + public void AdditionalAuthenticatedDataTemplateBuildAadTemplateBytes_ReturnsSameResultAsPreviousImplementation() + { + var actualBytes = KeyRingBasedDataProtector.AdditionalAuthenticatedDataTemplate.BuildAadTemplateBytes([ + "my sample string", + "©®±µ¶", // exotic unicode characters (https://en.wikipedia.org/wiki/List_of_Unicode_characters) + "my other sample string", + // more than 128 utf-8 bytes string + "CfDJ8H5oH_fp1QNBmvs-OWXxsVoV30hrXeI4-PI4p1VZytjsgd0DTstMdtTZbFtm2dKHvsBlDCv7TiEWKztZf8fb48pUgBgUE2SeYV3eOUXvSfNWU0D8SmHLy5KEnwKKkZKqudDhCnjQSIU7mhDliJJN1e4", + "." + ]); + + // expected bytes are formed by running former code with the same input + // former code can be found in https://github.com/dotnet/aspnetcore/pull/59322 + var expectedBytesInBase64 = "CfDJ8AAAAAAAAAAAAAAAAAAAAAAAAAAFEG15IHNhbXBsZSBzdHJpbmcKwqnCrsKxwrXCthZteSBvdGhlciBzYW1wbGUgc3RyaW5nmwFDZkRKOEg1b0hfZnAxUU5CbXZzLU9XWHhzVm9WMzBoclhlSTQtUEk0cDFWWnl0anNnZDBEVHN0TWR0VFpiRnRtMmRLSHZzQmxEQ3Y3VGlFV0t6dFpmOGZiNDhwVWdCZ1VFMlNlWVYzZU9VWHZTZk5XVTBEOFNtSEx5NUtFbndLS2taS3F1ZERoQ25qUVNJVTdtaERsaUpKTjFlNAEu"; + + var actualBytesInBase64 = Convert.ToBase64String(actualBytes); + Assert.Equal(expectedBytesInBase64, actualBytesInBase64); + } + + [Fact] + public void AdditionalAuthenticatedDataTemplateBuildAadTemplateBytes_ThrowsOnIllegalUtf8Text() + { + Assert.Throws(() => + { + var actualBytes = KeyRingBasedDataProtector.AdditionalAuthenticatedDataTemplate.BuildAadTemplateBytes([ + "😀"[0] + "X", + ]); + }); + } +} diff --git a/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs b/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs index 4d6948ff9e3c..3e2cccbb422c 100644 --- a/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs +++ b/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs @@ -208,7 +208,6 @@ public RouteValueDictionary(RouteValueDictionary? dictionary) _arrayStorage = Array.Empty>(); } } -#endif [MemberNotNull(nameof(_arrayStorage))] private void Initialize(IEnumerable> stringValueEnumerable) @@ -221,21 +220,9 @@ private void Initialize(IEnumerable> stringValueEn } } - [MemberNotNull(nameof(_arrayStorage))] - private void Initialize(IEnumerable> keyValueEnumerable) - { - _arrayStorage = Array.Empty>(); - - foreach (var kvp in keyValueEnumerable) - { - Add(kvp.Key, kvp.Value); - } - } - [MemberNotNull(nameof(_arrayStorage))] private void Initialize(RouteValueDictionary dictionary) { -#if !COMPONENTS if (dictionary._propertyStorage != null) { // PropertyStorage is immutable so we can just copy it. @@ -244,7 +231,6 @@ private void Initialize(RouteValueDictionary dictionary) _arrayStorage = Array.Empty>(); return; } -#endif var count = dictionary._count; if (count > 0) @@ -260,6 +246,18 @@ private void Initialize(RouteValueDictionary dictionary) _arrayStorage = Array.Empty>(); } } +#endif + + [MemberNotNull(nameof(_arrayStorage))] + private void Initialize(IEnumerable> keyValueEnumerable) + { + _arrayStorage = Array.Empty>(); + + foreach (var kvp in keyValueEnumerable) + { + Add(kvp.Key, kvp.Value); + } + } /// public object? this[string key] diff --git a/src/Http/Http.Extensions/gen/RequestDelegateGenerator/RequestDelegateGeneratorSuppressor.cs b/src/Http/Http.Extensions/gen/RequestDelegateGenerator/RequestDelegateGeneratorSuppressor.cs index cbde9e7e4373..6840634560e6 100644 --- a/src/Http/Http.Extensions/gen/RequestDelegateGenerator/RequestDelegateGeneratorSuppressor.cs +++ b/src/Http/Http.Extensions/gen/RequestDelegateGenerator/RequestDelegateGeneratorSuppressor.cs @@ -46,8 +46,21 @@ public override void ReportSuppressions(SuppressionAnalysisContext context) ? diagnostic.AdditionalLocations[0] : diagnostic.Location; - if (location.SourceTree is not { } sourceTree - || sourceTree.GetRoot().FindNode(location.SourceSpan) is not InvocationExpressionSyntax node + if (location.SourceTree is not { } sourceTree) + { + continue; + } + + // The trim analyzer changed from warning on the InvocationExpression to the MemberAccessExpression in https://github.com/dotnet/runtime/pull/110086. + // To account for this, we need to check if the location is an InvocationExpression or a child of an InvocationExpression. + var node = sourceTree.GetRoot().FindNode(location.SourceSpan) switch + { + InvocationExpressionSyntax s => s, + { Parent: InvocationExpressionSyntax s } => s, + _ => null, + }; + + if (node is null || !node.TryGetMapMethodName(out var method) || !InvocationOperationExtensions.KnownMethods.Contains(method)) { diff --git a/src/Http/WebUtilities/src/MultipartBoundary.cs b/src/Http/WebUtilities/src/MultipartBoundary.cs index c14d8fca270f..8cac796402ee 100644 --- a/src/Http/WebUtilities/src/MultipartBoundary.cs +++ b/src/Http/WebUtilities/src/MultipartBoundary.cs @@ -10,11 +10,11 @@ internal sealed class MultipartBoundary private readonly byte[] _boundaryBytes; private bool _expectLeadingCrlf; - public MultipartBoundary(string boundary, bool expectLeadingCrlf = true) + public MultipartBoundary(string boundary) { ArgumentNullException.ThrowIfNull(boundary); - _expectLeadingCrlf = expectLeadingCrlf; + _expectLeadingCrlf = false; _boundaryBytes = Encoding.UTF8.GetBytes("\r\n--" + boundary); FinalBoundaryLength = BoundaryBytes.Length + 2; // Include the final '--' terminator. @@ -25,6 +25,12 @@ public void ExpectLeadingCrlf() _expectLeadingCrlf = true; } + // Lets us throw a more specific error from MultipartReaderStream when reading any preamble data. + public bool BeforeFirstBoundary() + { + return !_expectLeadingCrlf; + } + // Return either "--{boundary}" or "\r\n--{boundary}" depending on if we're looking for the end of a section public ReadOnlySpan BoundaryBytes => _boundaryBytes.AsSpan(_expectLeadingCrlf ? 0 : 2); diff --git a/src/Http/WebUtilities/src/MultipartReader.cs b/src/Http/WebUtilities/src/MultipartReader.cs index 7271f33a96d0..195d4184f69b 100644 --- a/src/Http/WebUtilities/src/MultipartReader.cs +++ b/src/Http/WebUtilities/src/MultipartReader.cs @@ -27,7 +27,7 @@ public class MultipartReader private readonly BufferedReadStream _stream; private readonly MultipartBoundary _boundary; - private MultipartReaderStream _currentStream; + private MultipartReaderStream? _currentStream; /// /// Initializes a new instance of . @@ -56,10 +56,7 @@ public MultipartReader(string boundary, Stream stream, int bufferSize) } _stream = new BufferedReadStream(stream, bufferSize); boundary = HeaderUtilities.RemoveQuotes(new StringSegment(boundary)).ToString(); - _boundary = new MultipartBoundary(boundary, false); - // This stream will drain any preamble data and remove the first boundary marker. - // TODO: HeadersLengthLimit can't be modified until after the constructor. - _currentStream = new MultipartReaderStream(_stream, _boundary) { LengthLimit = HeadersLengthLimit }; + _boundary = new MultipartBoundary(boundary); } /// @@ -86,6 +83,10 @@ public MultipartReader(string boundary, Stream stream, int bufferSize) /// public async Task ReadNextSectionAsync(CancellationToken cancellationToken = new CancellationToken()) { + // Only occurs on first call + // This stream will drain any preamble data and remove the first boundary marker. + _currentStream ??= new MultipartReaderStream(_stream, _boundary) { LengthLimit = HeadersLengthLimit }; + // Drain the prior section. await _currentStream.DrainAsync(cancellationToken); // If we're at the end return null diff --git a/src/Http/WebUtilities/src/MultipartReaderStream.cs b/src/Http/WebUtilities/src/MultipartReaderStream.cs index 9dd5ce6a76f8..42f24d8de530 100644 --- a/src/Http/WebUtilities/src/MultipartReaderStream.cs +++ b/src/Http/WebUtilities/src/MultipartReaderStream.cs @@ -145,9 +145,19 @@ private int UpdatePosition(int read) if (_observedLength < _position) { _observedLength = _position; - if (LengthLimit.HasValue && _observedLength > LengthLimit.GetValueOrDefault()) + if (LengthLimit.HasValue && + LengthLimit.GetValueOrDefault() is var lengthLimit && + _observedLength > lengthLimit) { - throw new InvalidDataException($"Multipart body length limit {LengthLimit.GetValueOrDefault()} exceeded."); + // If we hit the limit before the first boundary then we're using the header length limit, not the body length limit. + if (_boundary.BeforeFirstBoundary()) + { + throw new InvalidDataException($"Multipart header length limit {lengthLimit} exceeded. Too much data before the first boundary."); + } + else + { + throw new InvalidDataException($"Multipart body length limit {lengthLimit} exceeded."); + } } } return read; diff --git a/src/Http/WebUtilities/test/MultipartReaderTests.cs b/src/Http/WebUtilities/test/MultipartReaderTests.cs index bc442b567dc0..9055e14a10aa 100644 --- a/src/Http/WebUtilities/test/MultipartReaderTests.cs +++ b/src/Http/WebUtilities/test/MultipartReaderTests.cs @@ -147,6 +147,43 @@ public async Task MultipartReader_HeadersLengthExceeded_Throws() Assert.Equal("Line length limit 17 exceeded.", exception.Message); } + [Fact] + public async Task MultipartReader_HeadersLengthExceeded_LargePreamble() + { + var body = $"preamble {new string('a', 17000)}\r\n" + + "--9051914041544843365972754266\r\n" + +"\r\n" + +"text default\r\n" + +"--9051914041544843365972754266--\r\n"; + var stream = MakeStream(body); + var reader = new MultipartReader(Boundary, stream); + + var exception = await Assert.ThrowsAsync(() => reader.ReadNextSectionAsync()); + Assert.Equal("Multipart header length limit 16384 exceeded. Too much data before the first boundary.", exception.Message); + } + + [Fact] + public async Task MultipartReader_HeadersLengthLimitSettable_LargePreamblePasses() + { + var body = $"preamble {new string('a', 100_000)}\r\n" + + "--9051914041544843365972754266\r\n" + +"\r\n" + +"text default\r\n" + +"--9051914041544843365972754266--\r\n"; + var stream = MakeStream(body); + var reader = new MultipartReader(Boundary, stream) + { + HeadersLengthLimit = 200_000, + }; + + var section = await reader.ReadNextSectionAsync(); + Assert.NotNull(section); + + var buffer = new MemoryStream(); + await section.Body.CopyToAsync(buffer); + Assert.Equal("text default", Encoding.ASCII.GetString(buffer.ToArray())); + } + [Fact] public async Task MultipartReader_ReadSinglePartBodyWithTrailingWhitespace_Success() { diff --git a/src/Http/startvscode.cmd b/src/Http/startvscode.cmd new file mode 100644 index 000000000000..d403f3028231 --- /dev/null +++ b/src/Http/startvscode.cmd @@ -0,0 +1,3 @@ +@ECHO OFF + +%~dp0..\..\startvscode.cmd %~dp0 diff --git a/src/Http/startvscode.sh b/src/Http/startvscode.sh new file mode 100644 index 000000000000..346f74c01e76 --- /dev/null +++ b/src/Http/startvscode.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +repo_root="$DIR/../.." +"$repo_root/startvscode.sh" $DIR diff --git a/src/OpenApi/src/Comparers/OpenApiSchemaComparer.cs b/src/OpenApi/src/Comparers/OpenApiSchemaComparer.cs index 0591035d2f47..2e69b10f213f 100644 --- a/src/OpenApi/src/Comparers/OpenApiSchemaComparer.cs +++ b/src/OpenApi/src/Comparers/OpenApiSchemaComparer.cs @@ -24,6 +24,15 @@ public bool Equals(OpenApiSchema? x, OpenApiSchema? y) return true; } + // If a local reference is present, we can't compare the schema directly + // and should instead use the schema ID as a type-check to assert if the schemas are + // equivalent. + if ((x.Reference != null && y.Reference == null) + || (x.Reference == null && y.Reference != null)) + { + return SchemaIdEquals(x, y); + } + // Compare property equality in an order that should help us find inequality faster return x.Type == y.Type && diff --git a/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs b/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs index d7ea158b919a..437a9bca1be8 100644 --- a/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs +++ b/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs @@ -32,7 +32,7 @@ internal sealed class OpenApiSchemaService( IOptionsMonitor optionsMonitor) { private readonly OpenApiSchemaStore _schemaStore = serviceProvider.GetRequiredKeyedService(documentName); - private readonly OpenApiJsonSchemaContext _jsonSchemaContext = new OpenApiJsonSchemaContext(new(jsonOptions.Value.SerializerOptions)); + private readonly OpenApiJsonSchemaContext _jsonSchemaContext = new(new(jsonOptions.Value.SerializerOptions)); private readonly JsonSerializerOptions _jsonSerializerOptions = new(jsonOptions.Value.SerializerOptions) { // In order to properly handle the `RequiredAttribute` on type properties, add a modifier to support @@ -102,7 +102,7 @@ internal sealed class OpenApiSchemaService( // "nested": "#/properties/nested" becomes "nested": "#/components/schemas/NestedType" if (jsonPropertyInfo.PropertyType == jsonPropertyInfo.DeclaringType) { - return new JsonObject { [OpenApiSchemaKeywords.RefKeyword] = context.TypeInfo.GetSchemaReferenceId() }; + return new JsonObject { [OpenApiSchemaKeywords.RefKeyword] = createSchemaReferenceId(context.TypeInfo) }; } schema.ApplyNullabilityContextInfo(jsonPropertyInfo); } @@ -212,7 +212,13 @@ private async Task InnerApplySchemaTransformersAsync(OpenApiSchema schema, } } } - } + + if (schema is { AdditionalPropertiesAllowed: true, AdditionalProperties: not null } && jsonTypeInfo.ElementType is not null) + { + var elementTypeInfo = _jsonSerializerOptions.GetTypeInfo(jsonTypeInfo.ElementType); + await InnerApplySchemaTransformersAsync(schema.AdditionalProperties, elementTypeInfo, null, context, transformer, cancellationToken); + } + } private JsonNode CreateSchema(OpenApiSchemaKey key) => JsonSchemaExporter.GetJsonSchemaAsNode(_jsonSerializerOptions, key.Type, _configuration); diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/OpenApiDocumentIntegrationTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/OpenApiDocumentIntegrationTests.cs index 37ebf3c26f06..d68f6b7d63ab 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/OpenApiDocumentIntegrationTests.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/OpenApiDocumentIntegrationTests.cs @@ -27,6 +27,7 @@ await Verifier.Verify(GetOpenApiJson(document)) .UseDirectory(SkipOnHelixAttribute.OnHelix() ? Path.Combine(Environment.GetEnvironmentVariable("HELIX_WORKITEM_ROOT"), "Integration", "snapshots") : "snapshots") + .AutoVerify() .UseParameters(documentName); } diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs index cab245bffe97..a4a937dcfc2b 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs @@ -477,4 +477,127 @@ await VerifyOpenApiDocument(builder, options, document => Assert.Equal(ReferenceType.Link, responseSchema.Reference.Type); }); } + + [Fact] + public async Task SupportsNestedSchemasWithSelfReference() + { + // Arrange + var builder = CreateBuilder(); + + builder.MapPost("/", (LocationContainer item) => { }); + + await VerifyOpenApiDocument(builder, document => + { + var operation = document.Paths["/"].Operations[OperationType.Post]; + var requestSchema = operation.RequestBody.Content["application/json"].Schema; + + // Assert $ref used for top-level + Assert.Equal("LocationContainer", requestSchema.Reference.Id); + + // Assert that $ref is used for nested LocationDto + var locationContainerSchema = requestSchema.GetEffective(document); + Assert.Equal("LocationDto", locationContainerSchema.Properties["location"].Reference.Id); + + // Assert that $ref is used for nested AddressDto + var locationSchema = locationContainerSchema.Properties["location"].GetEffective(document); + Assert.Equal("AddressDto", locationSchema.Properties["address"].Reference.Id); + + // Assert that $ref is used for related LocationDto + var addressSchema = locationSchema.Properties["address"].GetEffective(document); + Assert.Equal("LocationDto", addressSchema.Properties["relatedLocation"].Reference.Id); + }); + } + + [Fact] + public async Task SupportsListNestedSchemasWithSelfReference() + { + // Arrange + var builder = CreateBuilder(); + + builder.MapPost("/", (ParentObject item) => { }); + + await VerifyOpenApiDocument(builder, document => + { + var operation = document.Paths["/"].Operations[OperationType.Post]; + var requestSchema = operation.RequestBody.Content["application/json"].Schema; + + // Assert $ref used for top-level + Assert.Equal("ParentObject", requestSchema.Reference.Id); + + // Assert that $ref is used for nested Children + var parentSchema = requestSchema.GetEffective(document); + Assert.Equal("ChildObject", parentSchema.Properties["children"].Items.Reference.Id); + + // Assert that $ref is used for nested Parent + var childSchema = parentSchema.Properties["children"].Items.GetEffective(document); + Assert.Equal("ParentObject", childSchema.Properties["parent"].Reference.Id); + }); + } + + [Fact] + public async Task SupportsMultiplePropertiesWithSameType() + { + // Arrange + var builder = CreateBuilder(); + + builder.MapPost("/", (Root item) => { }); + + await VerifyOpenApiDocument(builder, document => + { + var operation = document.Paths["/"].Operations[OperationType.Post]; + var requestSchema = operation.RequestBody.Content["application/json"].Schema; + + // Assert $ref used for top-level + Assert.Equal("Root", requestSchema.Reference.Id); + + // Assert that $ref is used for nested Item1 + var rootSchema = requestSchema.GetEffective(document); + Assert.Equal("Item", rootSchema.Properties["item1"].Reference.Id); + + // Assert that $ref is used for nested Item2 + Assert.Equal("Item", rootSchema.Properties["item2"].Reference.Id); + }); + } + + private class Root + { + public Item Item1 { get; set; } = null!; + public Item Item2 { get; set; } = null!; + } + + private class Item + { + public string[] Name { get; set; } = null!; + public int value { get; set; } + } + + private class LocationContainer + { + + public LocationDto Location { get; set; } + } + + private class LocationDto + { + public AddressDto Address { get; set; } + } + + private class AddressDto + { + public LocationDto RelatedLocation { get; set; } + } + +#nullable enable + private class ParentObject + { + public int Id { get; set; } + public List Children { get; set; } = []; + } + + private class ChildObject + { + public int Id { get; set; } + public required ParentObject Parent { get; set; } + } } +#nullable restore diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/SchemaTransformerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/SchemaTransformerTests.cs index 5d20c810d6fa..565c3a2b48b1 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/SchemaTransformerTests.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/SchemaTransformerTests.cs @@ -444,6 +444,7 @@ public async Task SchemaTransformer_CanModifyItemTypesInADocument() builder.MapGet("/list", () => new List { 1, 2, 3, 4 }); builder.MapGet("/single", () => 1); + builder.MapGet("/dictionary", () => new Dictionary {{ "key", 1 }}); var options = new OpenApiOptions(); options.AddSchemaTransformer((schema, context, cancellationToken) => @@ -469,7 +470,13 @@ await VerifyOpenApiDocument(builder, options, document => getOperation = path.Operations[OperationType.Get]; responseSchema = getOperation.Responses["200"].Content["application/json"].Schema.GetEffective(document); Assert.Equal("modified-number-format", responseSchema.Format); - }); + + // Assert that the schema represent dictionary values has been modified + path = document.Paths["/dictionary"]; + getOperation = path.Operations[OperationType.Get]; + responseSchema = getOperation.Responses["200"].Content["application/json"].Schema.GetEffective(document); + Assert.Equal("modified-number-format", responseSchema.AdditionalProperties.Format); + }); } [Fact] diff --git a/src/ProjectTemplates/TestInfrastructure/PrepareForTest.targets b/src/ProjectTemplates/TestInfrastructure/PrepareForTest.targets index fd49e9376cc5..0a886b420c11 100644 --- a/src/ProjectTemplates/TestInfrastructure/PrepareForTest.targets +++ b/src/ProjectTemplates/TestInfrastructure/PrepareForTest.targets @@ -6,7 +6,7 @@ <_Parameter1>DotNetEfFullPath - <_Parameter2>$([MSBuild]::EnsureTrailingSlash('$(NuGetPackageRoot)'))dotnet-ef/$(DotnetEfVersion)/tools/net8.0/any/dotnet-ef.dll + <_Parameter2>$([MSBuild]::EnsureTrailingSlash('$(NuGetPackageRoot)'))dotnet-ef/$(DotnetEfVersion)/tools/net10.0/any/dotnet-ef.dll <_Parameter1>TestPackageRestorePath diff --git a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/TransportManager.cs b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/TransportManager.cs index c5651c50d2fa..8e2585e75d74 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/TransportManager.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/TransportManager.cs @@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; internal sealed class TransportManager { - private readonly List _transports = new List(); + private readonly List _transports = []; private readonly List _transportFactories; private readonly List _multiplexedTransportFactories; @@ -30,7 +30,6 @@ public TransportManager( _serviceContext = serviceContext; } - private ConnectionManager ConnectionManager => _serviceContext.ConnectionManager; private KestrelTrace Trace => _serviceContext.Log; public async Task BindAsync(EndPoint endPoint, ConnectionDelegate connectionDelegate, EndpointConfig? endpointConfig, CancellationToken cancellationToken) diff --git a/src/Servers/Kestrel/startvscode.cmd b/src/Servers/Kestrel/startvscode.cmd new file mode 100644 index 000000000000..1f6448bbdff9 --- /dev/null +++ b/src/Servers/Kestrel/startvscode.cmd @@ -0,0 +1,3 @@ +@ECHO OFF + +%~dp0..\..\..\startvscode.cmd %~dp0 diff --git a/src/Servers/Kestrel/startvscode.sh b/src/Servers/Kestrel/startvscode.sh new file mode 100644 index 000000000000..9e44ad5032a9 --- /dev/null +++ b/src/Servers/Kestrel/startvscode.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +repo_root="$DIR/../../.." +"$repo_root/startvscode.sh" $DIR diff --git a/src/Shared/Encoding/Int7BitEncodingUtils.cs b/src/Shared/Encoding/Int7BitEncodingUtils.cs new file mode 100644 index 000000000000..ca97096d1c17 --- /dev/null +++ b/src/Shared/Encoding/Int7BitEncodingUtils.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Shared; + +internal static class Int7BitEncodingUtils +{ + public static int Measure7BitEncodedUIntLength(this int value) + => Measure7BitEncodedUIntLength((uint)value); + + public static int Measure7BitEncodedUIntLength(this uint value) + { +#if NET10_0_OR_GREATER + return ((31 - System.Numerics.BitOperations.LeadingZeroCount(value | 1)) / 7) + 1; +#else + int count = 1; + while ((value >>= 7) != 0) + { + count++; + } + return count; +#endif + } + + public static int Write7BitEncodedInt(this Span target, int value) + => Write7BitEncodedInt(target, (uint)value); + + public static int Write7BitEncodedInt(this Span target, uint uValue) + { + // Write out an int 7 bits at a time. The high bit of the byte, + // when on, tells reader to continue reading more bytes. + // + // Using the constants 0x7F and ~0x7F below offers smaller + // codegen than using the constant 0x80. + + int index = 0; + while (uValue > 0x7Fu) + { + target[index++] = (byte)(uValue | ~0x7Fu); + uValue >>= 7; + } + + target[index++] = (byte)uValue; + return index; + } +} diff --git a/src/Tools/dotnet-user-jwts/src/Commands/ClearCommand.cs b/src/Tools/dotnet-user-jwts/src/Commands/ClearCommand.cs index 2b977e3f6313..531b45b6f11e 100644 --- a/src/Tools/dotnet-user-jwts/src/Commands/ClearCommand.cs +++ b/src/Tools/dotnet-user-jwts/src/Commands/ClearCommand.cs @@ -19,21 +19,32 @@ public static void Register(ProjectCommandLineApplication app) Resources.ClearCommand_ForceOption_Description, CommandOptionType.NoValue); + var appsettingsFileOption = cmd.Option( + "--appsettings-file", + Resources.CreateCommand_appsettingsFileOption_Description, + CommandOptionType.SingleValue); + cmd.HelpOption("-h|--help"); cmd.OnExecute(() => { - return Execute(cmd.Reporter, cmd.ProjectOption.Value(), forceOption.HasValue()); + if (!DevJwtCliHelpers.GetProjectAndSecretsId(cmd.ProjectOption.Value(), cmd.Reporter, out var project, out var userSecretsId)) + { + return 1; + } + + if (!DevJwtCliHelpers.GetAppSettingsFile(project, appsettingsFileOption.Value(), cmd.Reporter, out var appsettingsFile)) + { + return 1; + } + + return Execute(cmd.Reporter, project, userSecretsId, forceOption.HasValue(), appsettingsFile); }); }); } - private static int Execute(IReporter reporter, string projectPath, bool force) + private static int Execute(IReporter reporter, string project, string userSecretsId, bool force, string appsettingsFile) { - if (!DevJwtCliHelpers.GetProjectAndSecretsId(projectPath, reporter, out var project, out var userSecretsId)) - { - return 1; - } var jwtStore = new JwtStore(userSecretsId); var count = jwtStore.Jwts.Count; @@ -54,7 +65,7 @@ private static int Execute(IReporter reporter, string projectPath, bool force) } } - var appsettingsFilePath = Path.Combine(Path.GetDirectoryName(project), "appsettings.Development.json"); + var appsettingsFilePath = Path.Combine(Path.GetDirectoryName(project), appsettingsFile); foreach (var jwt in jwtStore.Jwts) { JwtAuthenticationSchemeSettings.RemoveScheme(appsettingsFilePath, jwt.Value.Scheme); diff --git a/src/Tools/dotnet-user-jwts/src/Commands/CreateCommand.cs b/src/Tools/dotnet-user-jwts/src/Commands/CreateCommand.cs index ef3a3b833655..58f3969bab76 100644 --- a/src/Tools/dotnet-user-jwts/src/Commands/CreateCommand.cs +++ b/src/Tools/dotnet-user-jwts/src/Commands/CreateCommand.cs @@ -77,24 +77,29 @@ public static void Register(ProjectCommandLineApplication app, Program program) Resources.CreateCommand_ValidForOption_Description, CommandOptionType.SingleValue); + var appsettingsFileOption = cmd.Option( + "--appsettings-file", + Resources.CreateCommand_appsettingsFileOption_Description, + CommandOptionType.SingleValue); + cmd.HelpOption("-h|--help"); cmd.OnExecute(() => { - var (options, isValid, optionsString) = ValidateArguments( - cmd.Reporter, cmd.ProjectOption, schemeNameOption, nameOption, audienceOption, issuerOption, notBeforeOption, expiresOnOption, validForOption, rolesOption, scopesOption, claimsOption); + var (options, isValid, optionsString, appsettingsFile) = ValidateArguments( + cmd.Reporter, cmd.ProjectOption, schemeNameOption, nameOption, audienceOption, issuerOption, notBeforeOption, expiresOnOption, validForOption, rolesOption, scopesOption, claimsOption, appsettingsFileOption); if (!isValid) { return 1; } - return Execute(cmd.Reporter, cmd.ProjectOption.Value(), options, optionsString, cmd.OutputOption.Value(), program); + return Execute(cmd.Reporter, cmd.ProjectOption.Value(), options, optionsString, cmd.OutputOption.Value(), appsettingsFile, program); }); }); } - private static (JwtCreatorOptions, bool, string) ValidateArguments( + private static (JwtCreatorOptions, bool, string, string) ValidateArguments( IReporter reporter, CommandOption projectOption, CommandOption schemeNameOption, @@ -106,7 +111,8 @@ private static (JwtCreatorOptions, bool, string) ValidateArguments( CommandOption validForOption, CommandOption rolesOption, CommandOption scopesOption, - CommandOption claimsOption) + CommandOption claimsOption, + CommandOption appsettingsFileOption) { var isValid = true; var finder = new MsBuildProjectFinder(Directory.GetCurrentDirectory()); @@ -121,6 +127,7 @@ private static (JwtCreatorOptions, bool, string) ValidateArguments( return ( null, isValid, + string.Empty, string.Empty ); } @@ -209,10 +216,19 @@ private static (JwtCreatorOptions, bool, string) ValidateArguments( optionsString += $"{Resources.JwtPrint_CustomClaims}: [{string.Join(", ", claims.Select(kvp => $"{kvp.Key}={kvp.Value}"))}]{Environment.NewLine}"; } + var appsettingsFile = DevJwtCliHelpers.DefaultAppSettingsFile; + if (appsettingsFileOption.HasValue()) + { + isValid = DevJwtCliHelpers.GetAppSettingsFile(project, appsettingsFileOption.Value(), reporter, out appsettingsFile); + + optionsString += appsettingsFileOption.HasValue() ? $"{Resources.JwtPrint_appsettingsFile}: {appsettingsFile}{Environment.NewLine}" : string.Empty; + } + return ( new JwtCreatorOptions(scheme, name, audience, issuer, notBefore, expiresOn, roles, scopes, claims), isValid, - optionsString); + optionsString, + appsettingsFile); static bool ParseDate(string datetime, out DateTime parsedDateTime) => DateTime.TryParseExact(datetime, _dateTimeFormats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out parsedDateTime); @@ -224,6 +240,7 @@ private static int Execute( JwtCreatorOptions options, string optionsString, string outputFormat, + string appsettingsFile, Program program) { if (!DevJwtCliHelpers.GetProjectAndSecretsId(projectPath, reporter, out var project, out var userSecretsId)) @@ -244,7 +261,7 @@ private static int Execute( jwtStore.Jwts.Add(jwtToken.Id, jwt); jwtStore.Save(); - var appsettingsFilePath = Path.Combine(Path.GetDirectoryName(project), "appsettings.Development.json"); + var appsettingsFilePath = Path.Combine(Path.GetDirectoryName(project), appsettingsFile); var settingsToWrite = new JwtAuthenticationSchemeSettings(options.Scheme, options.Audiences, options.Issuer); settingsToWrite.Save(appsettingsFilePath); diff --git a/src/Tools/dotnet-user-jwts/src/Commands/RemoveCommand.cs b/src/Tools/dotnet-user-jwts/src/Commands/RemoveCommand.cs index 0c3a69eaf40e..9260a70b1219 100644 --- a/src/Tools/dotnet-user-jwts/src/Commands/RemoveCommand.cs +++ b/src/Tools/dotnet-user-jwts/src/Commands/RemoveCommand.cs @@ -15,6 +15,12 @@ public static void Register(ProjectCommandLineApplication app) cmd.Description = Resources.RemoveCommand_Description; var idArgument = cmd.Argument("[id]", Resources.RemoveCommand_IdArgument_Description); + + var appsettingsFileOption = cmd.Option( + "--appsettings-file", + Resources.CreateCommand_appsettingsFileOption_Description, + CommandOptionType.SingleValue); + cmd.HelpOption("-h|--help"); cmd.OnExecute(() => @@ -24,17 +30,24 @@ public static void Register(ProjectCommandLineApplication app) cmd.ShowHelp(); return 0; } - return Execute(cmd.Reporter, cmd.ProjectOption.Value(), idArgument.Value); + + if (!DevJwtCliHelpers.GetProjectAndSecretsId(cmd.ProjectOption.Value(), cmd.Reporter, out var project, out var userSecretsId)) + { + return 1; + } + + if (!DevJwtCliHelpers.GetAppSettingsFile(project, appsettingsFileOption.Value(), cmd.Reporter, out var appsettingsFile)) + { + return 1; + } + + return Execute(cmd.Reporter, project, userSecretsId, idArgument.Value, appsettingsFile); }); }); } - private static int Execute(IReporter reporter, string projectPath, string id) + private static int Execute(IReporter reporter, string project, string userSecretsId, string id, string appsettingsFile) { - if (!DevJwtCliHelpers.GetProjectAndSecretsId(projectPath, reporter, out var project, out var userSecretsId)) - { - return 1; - } var jwtStore = new JwtStore(userSecretsId); if (!jwtStore.Jwts.TryGetValue(id, out var jwt)) @@ -43,7 +56,7 @@ private static int Execute(IReporter reporter, string projectPath, string id) return 1; } - var appsettingsFilePath = Path.Combine(Path.GetDirectoryName(project), "appsettings.Development.json"); + var appsettingsFilePath = Path.Combine(Path.GetDirectoryName(project), appsettingsFile); JwtAuthenticationSchemeSettings.RemoveScheme(appsettingsFilePath, jwt.Scheme); jwtStore.Jwts.Remove(id); jwtStore.Save(); diff --git a/src/Tools/dotnet-user-jwts/src/Helpers/DevJwtCliHelpers.cs b/src/Tools/dotnet-user-jwts/src/Helpers/DevJwtCliHelpers.cs index 2d12b67f55a4..38735fb5231f 100644 --- a/src/Tools/dotnet-user-jwts/src/Helpers/DevJwtCliHelpers.cs +++ b/src/Tools/dotnet-user-jwts/src/Helpers/DevJwtCliHelpers.cs @@ -11,6 +11,8 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer.Tools; internal static class DevJwtCliHelpers { + public const string DefaultAppSettingsFile = "appsettings.Development.json"; + public static string GetOrSetUserSecretsId(string projectFilePath) { var resolver = new ProjectIdResolver(NullReporter.Singleton, projectFilePath); @@ -42,6 +44,27 @@ public static bool GetProjectAndSecretsId(string projectPath, IReporter reporter return true; } + public static bool GetAppSettingsFile(string projectPath, string appsettingsFileOption, IReporter reporter, out string appsettingsFile) + { + appsettingsFile = DevJwtCliHelpers.DefaultAppSettingsFile; + if (appsettingsFileOption is not null) + { + appsettingsFile = appsettingsFileOption; + if (!appsettingsFile.EndsWith(".json", StringComparison.OrdinalIgnoreCase)) + { + reporter.Error(Resources.RemoveCommand_InvalidAppsettingsFile_Error); + return false; + } + else if (!File.Exists(Path.Combine(Path.GetDirectoryName(projectPath), appsettingsFile))) + { + reporter.Error(Resources.FormatRemoveCommand_AppsettingsFileNotFound_Error(Path.Combine(Path.GetDirectoryName(projectPath), appsettingsFile))); + return false; + } + } + + return true; + } + public static List GetAudienceCandidatesFromLaunchSettings(string project) { if (string.IsNullOrEmpty(project)) @@ -213,4 +236,5 @@ public static bool IsNullOrEmpty(this IEnumerable enumerable) { return enumerable == null || !enumerable.Any(); } + } diff --git a/src/Tools/dotnet-user-jwts/src/Resources.resx b/src/Tools/dotnet-user-jwts/src/Resources.resx index 094a066418d9..9f4ff3c041ac 100644 --- a/src/Tools/dotnet-user-jwts/src/Resources.resx +++ b/src/Tools/dotnet-user-jwts/src/Resources.resx @@ -150,6 +150,12 @@ The UTC date & time the JWT should expire in the format 'yyyy-MM-dd [[[[HH:mm]]:ss]]'. Defaults to 3 months after the --not-before date. Do not use this option in conjunction with the --valid-for option. + + Invalid Appsettings file extension. Ensure file extension is .json. + + + Could not find Appsettings file in '{0}'. Check the filename and that the file exists. + Malformed claims supplied. Ensure each claim is in the format "name=value". @@ -189,6 +195,9 @@ The period the JWT should expire after. Specify using a number followed by a duration type like 'd' for days, 'h' for hours, 'm' for minutes, and 's' for seconds, e.g. '365d'. Do not use this option in conjunction with the --expires-on option. + + The appSettings configuration file to add the test scheme to. + Audience(s) @@ -225,6 +234,9 @@ Scopes + + Appsettings File + Token @@ -312,6 +324,12 @@ No JWT with ID '{0}' found. + + The specified appsettings file is invalid. Please provide a valid JSON file. + + + Could not find Appsettings file '{0}'. Check the filename and that the file exists. + The issuer associated with the signing key to be reset or displayed. Defaults to 'dotnet-user-jwts'. diff --git a/src/Tools/dotnet-user-jwts/test/UserJwtsTestFixture.cs b/src/Tools/dotnet-user-jwts/test/UserJwtsTestFixture.cs index 946530df7c6a..b812a017255c 100644 --- a/src/Tools/dotnet-user-jwts/test/UserJwtsTestFixture.cs +++ b/src/Tools/dotnet-user-jwts/test/UserJwtsTestFixture.cs @@ -81,6 +81,10 @@ public string CreateProject(bool hasSecret = true) Path.Combine(projectPath.FullName, "appsettings.Development.json"), "{}"); + File.WriteAllText( + Path.Combine(projectPath.FullName, "appsettings.Local.json"), + "{}"); + if (hasSecret) { _disposables.Push(() => diff --git a/src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs b/src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs index 801e414fe95e..f98549ba8212 100644 --- a/src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs +++ b/src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs @@ -116,6 +116,23 @@ public void Create_CanModifyExistingScheme() Assert.Equal("new-issuer", appSettings["Authentication"]["Schemes"]["Bearer"]["ValidIssuer"].GetValue()); } + [Fact] + public void Create_CanModifyExistingSchemeInGivenAppSettings() + { + var project = Path.Combine(fixture.CreateProject(), "TestProject.csproj"); + var appsettings = Path.Combine(Path.GetDirectoryName(project), "appsettings.Local.json"); + var app = new Program(_console); + + app.Run(new[] { "create", "--project", project, "--appsettings-file", "appsettings.Local.json" }); + Assert.Contains("New JWT saved", _console.GetOutput()); + + var appSettings = JsonSerializer.Deserialize(File.ReadAllText(appsettings)); + Assert.Equal("dotnet-user-jwts", appSettings["Authentication"]["Schemes"]["Bearer"]["ValidIssuer"].GetValue()); + app.Run(["create", "--project", project, "--issuer", "new-issuer", "--appsettings-file", "appsettings.Local.json"]); + appSettings = JsonSerializer.Deserialize(File.ReadAllText(appsettings)); + Assert.Equal("new-issuer", appSettings["Authentication"]["Schemes"]["Bearer"]["ValidIssuer"].GetValue()); + } + [Fact] public void Print_ReturnsNothingForMissingToken() { @@ -191,6 +208,24 @@ public void Remove_RemovesGeneratedToken() Assert.Contains("Scheme2", appsettingsContent); } + [Fact] + public void Remove_RemovesGeneratedTokenInGivenAppsettings() + { + var project = Path.Combine(fixture.CreateProject(), "TestProject.csproj"); + var appsettings = Path.Combine(Path.GetDirectoryName(project), "appsettings.Local.json"); + var app = new Program(_console); + + app.Run(new[] { "create", "--project", project, "--appsettings-file", "appsettings.Local.json" }); + var matches = Regex.Matches(_console.GetOutput(), "New JWT saved with ID '(.*?)'"); + var id = matches.SingleOrDefault().Groups[1].Value; + app.Run(new[] { "create", "--project", project, "--appsettings-file", "appsettings.Local.json", "--scheme", "Scheme2" }); + + app.Run(new[] { "remove", id, "--project", project, "--appsettings-file", "appsettings.Local.json" }); + var appsettingsContent = File.ReadAllText(appsettings); + Assert.DoesNotContain(DevJwtsDefaults.Scheme, appsettingsContent); + Assert.Contains("Scheme2", appsettingsContent); + } + [Fact] public void Clear_RemovesGeneratedTokens() { @@ -209,6 +244,24 @@ public void Clear_RemovesGeneratedTokens() Assert.DoesNotContain("Scheme2", appsettingsContent); } + [Fact] + public void Clear_RemovesGeneratedTokensInGivenAppsettings() + { + var project = Path.Combine(fixture.CreateProject(), "TestProject.csproj"); + var appsettings = Path.Combine(Path.GetDirectoryName(project), "appsettings.Local.json"); + var app = new Program(_console); + + app.Run(new[] { "create", "--project", project, "--appsettings-file", "appsettings.Local.json" }); + app.Run(new[] { "create", "--project", project, "--appsettings-file", "appsettings.Local.json", "--scheme", "Scheme2" }); + + Assert.Contains("New JWT saved", _console.GetOutput()); + + app.Run(new[] { "clear", "--project", project, "--appsettings-file", "appsettings.Local.json", "--force" }); + var appsettingsContent = File.ReadAllText(appsettings); + Assert.DoesNotContain(DevJwtsDefaults.Scheme, appsettingsContent); + Assert.DoesNotContain("Scheme2", appsettingsContent); + } + [Fact] public void Key_CanResetSigningKey() { @@ -662,6 +715,19 @@ public void Create_CanHandleNoProjectOptionProvided_WithNoProjects() Assert.DoesNotContain(Resources.CreateCommand_NoAudience_Error, _console.GetOutput()); } + [Fact] + public void Create_CanHandleAppsettingsOption_WithNoFile() + { + var projectPath = fixture.CreateProject(); + Directory.SetCurrentDirectory(projectPath); + var expectedAppsettingsPath = Path.Combine(Directory.GetCurrentDirectory(), "appsettings.DoesNotExist.json"); + + var app = new Program(_console); + app.Run(["create", "--appsettings-file", "appsettings.DoesNotExist.json"]); + + Assert.Contains($"Could not find Appsettings file '{expectedAppsettingsPath}'. Check the filename and that the file exists.", _console.GetOutput()); + } + [Fact] public void Delete_CanHandleNoProjectOptionProvided_WithNoProjects() { @@ -674,6 +740,19 @@ public void Delete_CanHandleNoProjectOptionProvided_WithNoProjects() Assert.Contains($"Could not find a MSBuild project file in '{Directory.GetCurrentDirectory()}'. Specify which project to use with the --project option.", _console.GetOutput()); } + [Fact] + public void Delete_CanHandleAppsettingsOption_WithNoFile() + { + var projectPath = fixture.CreateProject(); + Directory.SetCurrentDirectory(projectPath); + var expectedAppsettingsPath = Path.Combine(Directory.GetCurrentDirectory(), "appsettings.DoesNotExist.json"); + + var app = new Program(_console); + app.Run(["remove", "some-id", "--appsettings-file", "appsettings.DoesNotExist.json"]); + + Assert.Contains($"Could not find Appsettings file '{expectedAppsettingsPath}'. Check the filename and that the file exists.", _console.GetOutput()); + } + [Fact] public void Clear_CanHandleNoProjectOptionProvided_WithNoProjects() { @@ -686,6 +765,19 @@ public void Clear_CanHandleNoProjectOptionProvided_WithNoProjects() Assert.Contains($"Could not find a MSBuild project file in '{Directory.GetCurrentDirectory()}'. Specify which project to use with the --project option.", _console.GetOutput()); } + [Fact] + public void Clear_CanHandleAppsettingsOption_WithNoFile() + { + var projectPath = fixture.CreateProject(); + Directory.SetCurrentDirectory(projectPath); + var expectedAppsettingsPath = Path.Combine(Directory.GetCurrentDirectory(), "appsettings.DoesNotExist.json"); + + var app = new Program(_console); + app.Run(["clear", "--appsettings-file", "appsettings.DoesNotExist.json"]); + + Assert.Contains($"Could not find Appsettings file '{expectedAppsettingsPath}'. Check the filename and that the file exists.", _console.GetOutput()); + } + [Fact] public void List_CanHandleNoProjectOptionProvided_WithNoProjects() { @@ -741,6 +833,21 @@ public void Create_CanHandleRelativePathAsOption() Assert.Contains("New JWT saved", _console.GetOutput()); } + [Fact] + public void Create_CanHandleRelativePathAsOptionForAppsettingsOption() + { + var projectPath = fixture.CreateProject(); + var tempPath = Path.GetTempPath(); + var targetPath = Path.GetRelativePath(tempPath, projectPath); + Directory.SetCurrentDirectory(tempPath); + + var app = new Program(_console); + app.Run(new[] { "create", "--project", targetPath, "--appsettings-file", "appsettings.Local.json" }); + + Assert.DoesNotContain($"Could not find Appsettings file '{projectPath}'. Check the filename and that the file exists.", _console.GetOutput()); + Assert.Contains("New JWT saved", _console.GetOutput()); + } + [ConditionalFact] [OSSkipCondition(OperatingSystems.Windows, SkipReason = "UnixFileMode is not supported on Windows.")] public void Create_CreatesFileWithUserOnlyUnixFileMode() diff --git a/src/submodules/googletest b/src/submodules/googletest index 79219e26e0e3..7d76a231b0e2 160000 --- a/src/submodules/googletest +++ b/src/submodules/googletest @@ -1 +1 @@ -Subproject commit 79219e26e0e36b415a5804b6b017ad6c6cd99ad8 +Subproject commit 7d76a231b0e29caf86e68d1df858308cd53b2a66