Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ public static class ContentSecurityPolicyHashes
/// <summary>
/// The hash of the inline script used on the check session endpoint.
/// </summary>
public const string CheckSessionScript = "sha256-fa5rxHhZ799izGRP38+h4ud5QXNT0SFaFlh4eqDumBI=";
public const string CheckSessionScript = "sha256-4Hj97GNFvt0k8A6DbSr2hoRb/RJmCCakAgE+4zuVeHs=";
}

public static class ProtocolRoutePaths
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// See LICENSE in the project root for license information.


using System.Text.RegularExpressions;
using Duende.IdentityModel;
using Duende.IdentityServer;
using Duende.IdentityServer.Configuration;
Expand Down Expand Up @@ -226,6 +227,32 @@ public async Task form_post_mode_should_pass_results_in_body()
html.ShouldContain("<input type='hidden' name='state' value='state' />");
}

[Fact]
public async Task csp_hash_should_match_inline_script()
{
_response.Request = new ValidatedAuthorizeRequest
{
ClientId = "client",
ResponseMode = OidcConstants.ResponseModes.FormPost,
RedirectUri = "http://client/callback",
State = "state"
};

await _subject.WriteHttpResponse(new AuthorizeResult(_response), _context);

_context.Response.StatusCode.ShouldBe(200);
_context.Response.ContentType.ShouldStartWith("text/html");
_context.Response.Body.Seek(0, SeekOrigin.Begin);
using var rdr = new StreamReader(_context.Response.Body);
var html = await rdr.ReadToEndAsync();

var match = Regex.Match(html, "<script>(.*?)</script>", RegexOptions.Singleline | RegexOptions.IgnoreCase);
match.Success.ShouldBeTrue();

var scriptSha256 = "sha256-" + match.Groups[1].Value.ToSha256();
IdentityServerConstants.ContentSecurityPolicyHashes.AuthorizeScript.ShouldContain(scriptSha256);
}

[Fact]
public async Task form_post_mode_should_add_unsafe_inline_for_csp_level_1()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// See LICENSE in the project root for license information.


using System.Text.RegularExpressions;
using Duende.IdentityModel;
using Duende.IdentityServer;
using Duende.IdentityServer.Configuration;
using Duende.IdentityServer.Endpoints.Results;
Expand Down Expand Up @@ -46,6 +48,24 @@ public async Task should_pass_results_in_body()
html.ShouldContain("<script id='cookie-name' type='application/json'>foobar</script>");
}

[Fact]
public async Task csp_hash_should_match_inline_script()
{
await _subject.WriteHttpResponse(new CheckSessionResult(), _context);

_context.Response.StatusCode.ShouldBe(200);
_context.Response.ContentType.ShouldStartWith("text/html");
_context.Response.Body.Seek(0, SeekOrigin.Begin);
using var rdr = new StreamReader(_context.Response.Body);
var html = await rdr.ReadToEndAsync();

var match = Regex.Match(html, "<script>(.*?)</script>", RegexOptions.Singleline | RegexOptions.IgnoreCase);
match.Success.ShouldBeTrue();

var scriptSha256 = "sha256-" + match.Groups[1].Value.ToSha256();
IdentityServerConstants.ContentSecurityPolicyHashes.CheckSessionScript.ShouldContain(scriptSha256);
}

[Fact]
public async Task form_post_mode_should_add_unsafe_inline_for_csp_level_1()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
// See LICENSE in the project root for license information.


using System.Text.RegularExpressions;
using Duende.IdentityModel;
using Duende.IdentityServer;
using Duende.IdentityServer.Configuration;
using Duende.IdentityServer.Endpoints.Results;
using Duende.IdentityServer.Models;
Expand Down Expand Up @@ -52,10 +55,10 @@ public async Task success_should_render_html_and_iframes()
_context.Response.Headers.CacheControl.First().ShouldContain("no-cache");
_context.Response.Headers.CacheControl.First().ShouldContain("max-age=0");
_context.Response.Headers.ContentSecurityPolicy.First().ShouldContain("default-src 'none';");
_context.Response.Headers.ContentSecurityPolicy.First().ShouldContain("style-src 'sha256-e6FQZewefmod2S/5T11pTXjzE2vn3/8GRwWOs917YE4=';");
_context.Response.Headers.ContentSecurityPolicy.First().ShouldContain($"style-src '{IdentityServerConstants.ContentSecurityPolicyHashes.EndSessionStyle}';");
_context.Response.Headers.ContentSecurityPolicy.First().ShouldContain("frame-src http://foo.com http://bar.com");
_context.Response.Headers["X-Content-Security-Policy"].First().ShouldContain("default-src 'none';");
_context.Response.Headers["X-Content-Security-Policy"].First().ShouldContain("style-src 'sha256-e6FQZewefmod2S/5T11pTXjzE2vn3/8GRwWOs917YE4=';");
_context.Response.Headers["X-Content-Security-Policy"].First().ShouldContain($"style-src '{IdentityServerConstants.ContentSecurityPolicyHashes.EndSessionStyle}';");
_context.Response.Headers["X-Content-Security-Policy"].First().ShouldContain("frame-src http://foo.com http://bar.com");
_context.Response.Body.Seek(0, SeekOrigin.Begin);
using var rdr = new StreamReader(_context.Response.Body);
Expand All @@ -64,6 +67,27 @@ public async Task success_should_render_html_and_iframes()
html.ShouldContain("<iframe loading='eager' allow='' src='http://bar.com'></iframe>");
}

[Fact]
public async Task csp_hash_should_match_inline_style()
{
_result.IsError = false;
_result.FrontChannelLogoutUrls = new string[] { "http://foo.com", "http://bar.com" };

await _subject.WriteHttpResponse(new EndSessionCallbackResult(_result), _context);

_context.Response.StatusCode.ShouldBe(200);
_context.Response.ContentType.ShouldStartWith("text/html");
_context.Response.Body.Seek(0, SeekOrigin.Begin);
using var rdr = new StreamReader(_context.Response.Body);
var html = await rdr.ReadToEndAsync();

var match = Regex.Match(html, "<style>(.*?)</style>", RegexOptions.Singleline | RegexOptions.IgnoreCase);
match.Success.ShouldBeTrue();

var styleSha256 = "sha256-" + match.Groups[1].Value.ToSha256();
IdentityServerConstants.ContentSecurityPolicyHashes.EndSessionStyle.ShouldContain(styleSha256);
}

[Fact]
public async Task fsuccess_should_add_unsafe_inline_for_csp_level_1()
{
Expand All @@ -73,8 +97,8 @@ public async Task fsuccess_should_add_unsafe_inline_for_csp_level_1()

await _subject.WriteHttpResponse(new EndSessionCallbackResult(_result), _context);

_context.Response.Headers.ContentSecurityPolicy.First().ShouldContain("style-src 'unsafe-inline' 'sha256-e6FQZewefmod2S/5T11pTXjzE2vn3/8GRwWOs917YE4='");
_context.Response.Headers["X-Content-Security-Policy"].First().ShouldContain("style-src 'unsafe-inline' 'sha256-e6FQZewefmod2S/5T11pTXjzE2vn3/8GRwWOs917YE4='");
_context.Response.Headers.ContentSecurityPolicy.First().ShouldContain($"style-src 'unsafe-inline' '{IdentityServerConstants.ContentSecurityPolicyHashes.EndSessionStyle}'");
_context.Response.Headers["X-Content-Security-Policy"].First().ShouldContain($"style-src 'unsafe-inline' '{IdentityServerConstants.ContentSecurityPolicyHashes.EndSessionStyle}'");
}

[Fact]
Expand All @@ -86,7 +110,7 @@ public async Task form_post_mode_should_not_add_deprecated_header_when_it_is_dis

await _subject.WriteHttpResponse(new EndSessionCallbackResult(_result), _context);

_context.Response.Headers.ContentSecurityPolicy.First().ShouldContain("style-src 'sha256-e6FQZewefmod2S/5T11pTXjzE2vn3/8GRwWOs917YE4='");
_context.Response.Headers.ContentSecurityPolicy.First().ShouldContain($"style-src '{IdentityServerConstants.ContentSecurityPolicyHashes.EndSessionStyle}'");
_context.Response.Headers["X-Content-Security-Policy"].ShouldBeEmpty();
}
}