1+ using Duende . IdentityServer . Configuration ;
2+ using Duende . IdentityServer . Extensions ;
3+ using Duende . IdentityServer . Services ;
4+ using Duende . IdentityServer . Validation ;
5+ using FluentAssertions ;
6+ using Microsoft . Extensions . Logging ;
7+ using UnitTests . Common ;
8+ using UnitTests . Endpoints . Authorize ;
9+ using Xunit ;
10+
11+ public class IsLocalUrlTests
12+ {
13+ private const string queryParameters = "?client_id=mvc.code" +
14+ "&redirect_uri=https%3A%2F%2Flocalhost%3A44302%2Fsignin-oidc" +
15+ "&response_type=code" +
16+ "&scope=openid%20profile%20email%20custom.profile%20resource1.scope1%20resource2.scope1%20offline_access" +
17+ "&code_challenge=LcJN1shWmezC0J5EU7QOi7N_amBuvMDb6PcTY0sB2YY" +
18+ "&code_challenge_method=S256" +
19+ "&response_mode=form_post" +
20+ "&nonce=nonce" +
21+ "&state=state" ;
22+ private const string ExternalWithControlCharacters =
23+ "/ /evil.com/connect/authorize/callback" + // Note tab character between slashes
24+ queryParameters ;
25+ private const string ExternalWithoutControlCharacters =
26+ "//evil.com/"
27+ + queryParameters ;
28+ private const string Local =
29+ "/connect/authorize/callback"
30+ + queryParameters ;
31+
32+ [ Fact ]
33+ public void IsLocalUrl ( )
34+ {
35+ Local . IsLocalUrl ( ) . Should ( ) . BeTrue ( ) ;
36+ ExternalWithoutControlCharacters . IsLocalUrl ( ) . Should ( ) . BeFalse ( ) ;
37+ ExternalWithControlCharacters . IsLocalUrl ( ) . Should ( ) . BeFalse ( ) ;
38+ }
39+
40+ [ Fact ]
41+ public void GetIdentityServerRelativeUrl ( )
42+ {
43+ var serverUrls = new MockServerUrls
44+ {
45+ Origin = "https://localhost:5001" ,
46+ BasePath = "/"
47+ } ;
48+
49+ serverUrls . GetIdentityServerRelativeUrl ( Local ) . Should ( ) . NotBeNull ( ) ;
50+ serverUrls . GetIdentityServerRelativeUrl ( ExternalWithoutControlCharacters ) . Should ( ) . BeNull ( ) ;
51+ serverUrls . GetIdentityServerRelativeUrl ( ExternalWithControlCharacters ) . Should ( ) . BeNull ( ) ;
52+ }
53+
54+ [ Fact ]
55+ public async void OidcReturnUrlParser ( )
56+ {
57+ var oidcParser = GetOidcParser ( ) ;
58+
59+ ( await oidcParser . ParseAsync ( Local ) ) . Should ( ) . NotBeNull ( ) ;
60+ oidcParser . IsValidReturnUrl ( Local ) . Should ( ) . BeTrue ( ) ;
61+ ( await oidcParser . ParseAsync ( ExternalWithoutControlCharacters ) ) . Should ( ) . BeNull ( ) ;
62+ oidcParser . IsValidReturnUrl ( ExternalWithoutControlCharacters ) . Should ( ) . BeFalse ( ) ;
63+ ( await oidcParser . ParseAsync ( ExternalWithControlCharacters ) ) . Should ( ) . BeNull ( ) ;
64+ oidcParser . IsValidReturnUrl ( ExternalWithControlCharacters ) . Should ( ) . BeFalse ( ) ;
65+ }
66+
67+ [ Fact ]
68+ public async void ReturnUrlParser ( )
69+ {
70+ var oidcParser = GetOidcParser ( ) ;
71+ var parser = new ReturnUrlParser ( [ oidcParser ] ) ;
72+
73+ ( await parser . ParseAsync ( Local ) ) . Should ( ) . NotBeNull ( ) ;
74+ parser . IsValidReturnUrl ( Local ) . Should ( ) . BeTrue ( ) ;
75+ ( await parser . ParseAsync ( ExternalWithoutControlCharacters ) ) . Should ( ) . BeNull ( ) ;
76+ parser . IsValidReturnUrl ( ExternalWithoutControlCharacters ) . Should ( ) . BeFalse ( ) ;
77+ ( await parser . ParseAsync ( ExternalWithControlCharacters ) ) . Should ( ) . BeNull ( ) ;
78+ parser . IsValidReturnUrl ( ExternalWithControlCharacters ) . Should ( ) . BeFalse ( ) ;
79+ }
80+
81+ private static OidcReturnUrlParser GetOidcParser ( )
82+ {
83+ return new OidcReturnUrlParser (
84+ new IdentityServerOptions ( ) ,
85+ new StubAuthorizeRequestValidator
86+ {
87+ Result = new AuthorizeRequestValidationResult
88+ (
89+ new ValidatedAuthorizeRequest ( )
90+ )
91+ } ,
92+ new MockUserSession ( ) ,
93+ new MockServerUrls ( ) ,
94+ new LoggerFactory ( ) . CreateLogger < OidcReturnUrlParser > ( ) ) ;
95+ }
96+ }
0 commit comments