File: src\Shared\ResultsHelpers\SharedUrlHelper.cs
Web Access
Project: src\src\Http\Http.Results\src\Microsoft.AspNetCore.Http.Results.csproj (Microsoft.AspNetCore.Http.Results)
// 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.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Http;
 
namespace Microsoft.AspNetCore.Internal;
 
internal static class SharedUrlHelper
{
    [return: NotNullIfNotNull("contentPath")]
    internal static string? Content(HttpContext httpContext, string? contentPath)
    {
        if (string.IsNullOrEmpty(contentPath))
        {
            return null;
        }
        else if (contentPath[0] == '~')
        {
            var segment = new PathString(contentPath.Substring(1));
            var applicationPath = httpContext.Request.PathBase;
 
            var path = applicationPath.Add(segment);
            Debug.Assert(path.HasValue);
            return path.Value;
        }
 
        return contentPath;
    }
 
    internal static bool IsLocalUrl([NotNullWhen(true)] string? url)
    {
        if (string.IsNullOrEmpty(url))
        {
            return false;
        }
 
        // Allows "/" or "/foo" but not "//" or "/\".
        if (url[0] == '/')
        {
            // url is exactly "/"
            if (url.Length == 1)
            {
                return true;
            }
 
            // url doesn't start with "//" or "/\"
            if (url[1] != '/' && url[1] != '\\')
            {
                return !HasControlCharacter(url.AsSpan(1));
            }
 
            return false;
        }
 
        // Allows "~/" or "~/foo" but not "~//" or "~/\".
        if (url[0] == '~' && url.Length > 1 && url[1] == '/')
        {
            // url is exactly "~/"
            if (url.Length == 2)
            {
                return true;
            }
 
            // url doesn't start with "~//" or "~/\"
            if (url[2] != '/' && url[2] != '\\')
            {
                return !HasControlCharacter(url.AsSpan(2));
            }
 
            return false;
        }
 
        return false;
 
        static bool HasControlCharacter(ReadOnlySpan<char> readOnlySpan)
        {
            // URLs may not contain ASCII control characters.
            for (var i = 0; i < readOnlySpan.Length; i++)
            {
                if (char.IsControl(readOnlySpan[i]))
                {
                    return true;
                }
            }
 
            return false;
        }
    }
}