Merge release/2.2 -> master
Fixes integrity validation in Identity UI V3
This commit is contained in:
commit
7421ba8087
|
|
@ -67,17 +67,17 @@
|
||||||
asp-fallback-src="~/Identity/lib/jquery/dist/jquery.min.js"
|
asp-fallback-src="~/Identity/lib/jquery/dist/jquery.min.js"
|
||||||
asp-fallback-test="window.jQuery"
|
asp-fallback-test="window.jQuery"
|
||||||
crossorigin="anonymous"
|
crossorigin="anonymous"
|
||||||
integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT">
|
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=">
|
||||||
</script>
|
</script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"
|
||||||
asp-fallback-src="~/Identity/lib/bootstrap/dist/js/bootstrap.min.js"
|
asp-fallback-src="~/Identity/lib/bootstrap/dist/js/bootstrap.min.js"
|
||||||
asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
|
asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
|
||||||
crossorigin="anonymous"
|
crossorigin="anonymous"
|
||||||
integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa">
|
integrity="sha256-U5ZEeKfGNOja007MMD3YBI0A3OSZOQbeG6z2f2Y0hu8=">
|
||||||
</script>
|
</script>
|
||||||
<script src="~/Identity/js/site.js" asp-append-version="true"></script>
|
<script src="~/Identity/js/site.js" asp-append-version="true"></script>
|
||||||
</environment>
|
</environment>
|
||||||
|
|
||||||
@RenderSection("Scripts", required: false)
|
@RenderSection("Scripts", required: false)
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -9,6 +9,7 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
|
|
@ -23,7 +24,7 @@ namespace Microsoft.AspNetCore.Identity.Test
|
||||||
public IdentityUIScriptsTest(ITestOutputHelper output)
|
public IdentityUIScriptsTest(ITestOutputHelper output)
|
||||||
{
|
{
|
||||||
_output = output;
|
_output = output;
|
||||||
_httpClient = new HttpClient();
|
_httpClient = new HttpClient(new RetryHandler(new HttpClientHandler() { }));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<object[]> ScriptWithIntegrityData
|
public static IEnumerable<object[]> ScriptWithIntegrityData
|
||||||
|
|
@ -40,15 +41,18 @@ namespace Microsoft.AspNetCore.Identity.Test
|
||||||
[MemberData(nameof(ScriptWithIntegrityData))]
|
[MemberData(nameof(ScriptWithIntegrityData))]
|
||||||
public async Task IdentityUI_ScriptTags_SubresourceIntegrityCheck(ScriptTag scriptTag)
|
public async Task IdentityUI_ScriptTags_SubresourceIntegrityCheck(ScriptTag scriptTag)
|
||||||
{
|
{
|
||||||
string expectedIntegrity;
|
var sha256Integrity = await GetShaIntegrity(scriptTag, SHA256.Create(), "sha256");
|
||||||
|
Assert.Equal(scriptTag.Integrity, sha256Integrity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> GetShaIntegrity(ScriptTag scriptTag, HashAlgorithm algorithm, string prefix)
|
||||||
|
{
|
||||||
using (var respStream = await _httpClient.GetStreamAsync(scriptTag.Src))
|
using (var respStream = await _httpClient.GetStreamAsync(scriptTag.Src))
|
||||||
using (var alg = SHA256.Create())
|
using (var alg = SHA256.Create())
|
||||||
{
|
{
|
||||||
var hash = alg.ComputeHash(respStream);
|
var hash = alg.ComputeHash(respStream);
|
||||||
expectedIntegrity = "sha256-" + Convert.ToBase64String(hash);
|
return $"{prefix}-" + Convert.ToBase64String(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.Equal(expectedIntegrity, scriptTag.Integrity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<object[]> ScriptWithFallbackSrcData
|
public static IEnumerable<object[]> ScriptWithFallbackSrcData
|
||||||
|
|
@ -66,7 +70,7 @@ namespace Microsoft.AspNetCore.Identity.Test
|
||||||
public async Task IdentityUI_ScriptTags_FallbackSourceContent_Matches_CDNContent(ScriptTag scriptTag)
|
public async Task IdentityUI_ScriptTags_FallbackSourceContent_Matches_CDNContent(ScriptTag scriptTag)
|
||||||
{
|
{
|
||||||
var slnDir = GetSolutionDir();
|
var slnDir = GetSolutionDir();
|
||||||
var wwwrootDir = Path.Combine(slnDir, "src", "UI", "wwwroot", "V4");
|
var wwwrootDir = Path.Combine(slnDir, "src", "UI", "wwwroot", scriptTag.Version);
|
||||||
|
|
||||||
var cdnContent = await _httpClient.GetStringAsync(scriptTag.Src);
|
var cdnContent = await _httpClient.GetStringAsync(scriptTag.Src);
|
||||||
var fallbackSrcContent = File.ReadAllText(
|
var fallbackSrcContent = File.ReadAllText(
|
||||||
|
|
@ -77,6 +81,7 @@ namespace Microsoft.AspNetCore.Identity.Test
|
||||||
|
|
||||||
public struct ScriptTag
|
public struct ScriptTag
|
||||||
{
|
{
|
||||||
|
public string Version;
|
||||||
public string Src;
|
public string Src;
|
||||||
public string Integrity;
|
public string Integrity;
|
||||||
public string FallbackSrc;
|
public string FallbackSrc;
|
||||||
|
|
@ -91,8 +96,9 @@ namespace Microsoft.AspNetCore.Identity.Test
|
||||||
private static List<ScriptTag> GetScriptTags()
|
private static List<ScriptTag> GetScriptTags()
|
||||||
{
|
{
|
||||||
var slnDir = GetSolutionDir();
|
var slnDir = GetSolutionDir();
|
||||||
var uiDir = Path.Combine(slnDir, "src", "UI", "Areas", "Identity", "Pages", "V4");
|
var uiDirV3 = Path.Combine(slnDir, "src", "UI", "Areas", "Identity", "Pages", "V3");
|
||||||
var cshtmlFiles = Directory.GetFiles(uiDir, "*.cshtml", SearchOption.AllDirectories);
|
var uiDirV4 = Path.Combine(slnDir, "src", "UI", "Areas", "Identity", "Pages", "V4");
|
||||||
|
var cshtmlFiles = GetRazorFiles(uiDirV3).Concat(GetRazorFiles(uiDirV4));
|
||||||
|
|
||||||
var scriptTags = new List<ScriptTag>();
|
var scriptTags = new List<ScriptTag>();
|
||||||
foreach (var cshtmlFile in cshtmlFiles)
|
foreach (var cshtmlFile in cshtmlFiles)
|
||||||
|
|
@ -104,6 +110,8 @@ namespace Microsoft.AspNetCore.Identity.Test
|
||||||
Assert.NotEmpty(scriptTags);
|
Assert.NotEmpty(scriptTags);
|
||||||
|
|
||||||
return scriptTags;
|
return scriptTags;
|
||||||
|
|
||||||
|
IEnumerable<string> GetRazorFiles(string dir) => Directory.GetFiles(dir, "*.cshtml", SearchOption.AllDirectories);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<ScriptTag> GetScriptTags(string cshtmlFile)
|
private static List<ScriptTag> GetScriptTags(string cshtmlFile)
|
||||||
|
|
@ -123,6 +131,7 @@ namespace Microsoft.AspNetCore.Identity.Test
|
||||||
|
|
||||||
scriptTags.Add(new ScriptTag
|
scriptTags.Add(new ScriptTag
|
||||||
{
|
{
|
||||||
|
Version = cshtmlFile.Contains("V3") ? "V3" : "V4",
|
||||||
Src = scriptElement.Source,
|
Src = scriptElement.Source,
|
||||||
Integrity = scriptElement.Integrity,
|
Integrity = scriptElement.Integrity,
|
||||||
FallbackSrc = fallbackSrcAttribute?.Value,
|
FallbackSrc = fallbackSrcAttribute?.Value,
|
||||||
|
|
@ -155,5 +164,25 @@ namespace Microsoft.AspNetCore.Identity.Test
|
||||||
{
|
{
|
||||||
_httpClient.Dispose();
|
_httpClient.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RetryHandler : DelegatingHandler
|
||||||
|
{
|
||||||
|
public RetryHandler(HttpMessageHandler innerHandler) : base(innerHandler) { }
|
||||||
|
|
||||||
|
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
HttpResponseMessage result = null;
|
||||||
|
for (var i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
result = await base.SendAsync(request, cancellationToken);
|
||||||
|
if (result.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
await Task.Delay(1000);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue