CVE-2022-0243 (C#, published 2022-01-19T20:15Z)
Description: value: Cross-site Scripting (XSS) - Stored in NuGet OrchardCore.Application.Cms.Targets prior to 1.2.2.
Severity: LOW (CVSS v3 base score: 5.4)
Weakness: CWE-79 \u2014 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
Repository: https://github.com/orchardcms/orchardcore
Files changed:
src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/LinkFieldDisplayDriver.cs
src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Controllers/AuditTrailContentController.cs
src/OrchardCore.Modules/OrchardCore.Menu/Drivers/HtmlMenuItemPartDisplayDriver.cs
src/OrchardCore.Modules/OrchardCore.Menu/Drivers/LinkMenuItemPartDisplayDriver.cs
src/OrchardCore.Modules/OrchardCore.Users/Workflows/Activities/RegisterUserTask.cs
src/OrchardCore.Modules/OrchardCore.Workflows/Activities/NotifyTask.cs
src/OrchardCore.Modules/OrchardCore.Workflows/Views/Items/UserTaskEvent.Fields.Design.cshtml
src/OrchardCore.Modules/OrchardCore.Workflows/Views/Workflow/Details.cshtml
src/OrchardCore.Modules/OrchardCore.Workflows/Views/Workflow/Index.cshtml
src/OrchardCore/OrchardCore.Workflows.Abstractions/Helpers/ActivityExtensions.cs
Commit message: Fix localization and sanitization usages (#11034)



Vulnerable code:
            IStringLocalizer<LinkFieldDisplayDriver> localizer)

                // Run this through a sanitizer in case someone puts html in it.
                // No settings.
                    await _notifier.WarningAsync(new LocalizedHtmlString(error.ErrorMessage, error.ErrorMessage));
        public HtmlMenuItemPartDisplayDriver(IHtmlSanitizerService htmlSanitizerService)
                    var localizedSubject = new LocalizedString(nameof(RegisterUserTask), subject);
                    var localizedBody = new LocalizedHtmlString(nameof(RegisterUserTask), body);
                        Subject = localizedSubject.ResourceNotFound ? subject : localizedSubject.Value,
                        Body = localizedBody.IsResourceNotFound ? body : localizedBody.Value,
    <span>@T["Required roles: <em>{0}</em>", Model.Activity.Roles.Any() ? new LocalizedHtmlString("RequiredRoles", string.Join(", ", Model.Activity.Roles)) : T["Any"]]</span>
                        <span>@Model.Workflow.Status.GetLocalizedStatus(T)</span>
                                        @entry.Workflow.Status.GetLocalizedStatus(T)
        private static IHtmlLocalizer H;

            return !string.IsNullOrEmpty(title) ? new LocalizedHtmlString(title, title) : defaultTitle();
        public static LocalizedHtmlString GetLocalizedStatus(this WorkflowStatus status, IHtmlLocalizer localizer)
            // Field for PoExtractor compatibility
            H = localizer;

                _ => new LocalizedHtmlString(status.ToString(), status.ToString()),

Fixed code:
using System.Text.Encodings.Web;
using OrchardCore.Infrastructure.Html;
        private readonly IHtmlSanitizerService _htmlSanitizerService;
        private readonly HtmlEncoder _htmlencoder;
            IStringLocalizer<LinkFieldDisplayDriver> localizer,
            IHtmlSanitizerService htmlSanitizerService,
            HtmlEncoder htmlencoder)
            _htmlSanitizerService = htmlSanitizerService;
            _htmlencoder = htmlencoder;
                else
                {
                    var link = $"<a href=\"{_htmlencoder.Encode(urlToValidate)}\"></a>";

                    if (!String.Equals(link, _htmlSanitizerService.Sanitize(link), StringComparison.OrdinalIgnoreCase))
                    {
                        updater.ModelState.AddModelError(Prefix, nameof(field.Url), S["{0} is an invalid url.", field.Url]);
                    }
                }


                    // Pass ErrorMessage as an argument to ensure it is encoded
                    await _notifier.WarningAsync(new LocalizedHtmlString(nameof(AuditTrailContentController.Restore), "{0}", false, error.ErrorMessage));
using System;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.Extensions.Localization;
        private readonly IUrlHelperFactory _urlHelperFactory;
        private readonly IActionContextAccessor _actionContextAccessor;
        private readonly HtmlEncoder _htmlencoder;
        private readonly IStringLocalizer S;
        public HtmlMenuItemPartDisplayDriver(
            IUrlHelperFactory urlHelperFactory,
            IActionContextAccessor actionContextAccessor,
            IStringLocalizer<HtmlMenuItemPartDisplayDriver> localizer,
            IHtmlSanitizerService htmlSanitizerService,
            HtmlEncoder htmlencoder
            )
            _urlHelperFactory = urlHelperFactory;
            _actionContextAccessor = actionContextAccessor;
            _htmlencoder = htmlencoder;
            S = localizer;

                var urlToValidate = part.Url;

                if (!String.IsNullOrEmpty(urlToValidate))
                {
                    urlToValidate = urlToValidate.Split('#', 2)[0];

                    if (urlToValidate.StartsWith("~/", StringComparison.Ordinal))
                    {
                        var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext);
                        urlToValidate = urlHelper.Content(urlToValidate);
                    }

                    urlToValidate = urlToValidate.ToUriComponents();

                    if (!Uri.IsWellFormedUriString(urlToValidate, UriKind.RelativeOrAbsolute))
                    {
                        updater.ModelState.AddModelError(nameof(part.Url), S["{0} is an invalid url.", part.Url]);
                    }
                    else
                    {
                        var link = $"<a href=\"{_htmlencoder.Encode(urlToValidate)}\"></a>";

                        if (!String.Equals(link, _htmlSanitizerService.Sanitize(link), StringComparison.OrdinalIgnoreCase))
                        {
                            updater.ModelState.AddModelError(nameof(part.Url), S["{0} is an invalid url.", part.Url]);
                        }
                    }
                }
using System;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.Extensions.Localization;
using OrchardCore.Infrastructure.Html;
        private readonly IUrlHelperFactory _urlHelperFactory;
        private readonly IActionContextAccessor _actionContextAccessor;
        private readonly IHtmlSanitizerService _htmlSanitizerService;
        private readonly HtmlEncoder _htmlencoder;
        private readonly IStringLocalizer S;

        public LinkMenuItemPartDisplayDriver(
            IUrlHelperFactory urlHelperFactory,
            IActionContextAccessor actionContextAccessor,
            IStringLocalizer<LinkMenuItemPartDisplayDriver> localizer,
            IHtmlSanitizerService htmlSanitizerService,
            HtmlEncoder htmlencoder
            )
        {
            _urlHelperFactory = urlHelperFactory;
            _actionContextAccessor = actionContextAccessor; 
            _htmlSanitizerService = htmlSanitizerService;
            _htmlencoder = htmlencoder;
            S = localizer;
        }


                var urlToValidate = part.Url;

                if (!String.IsNullOrEmpty(urlToValidate))
                {
                    urlToValidate = urlToValidate.Split('#', 2)[0];

                    if (urlToValidate.StartsWith("~/", StringComparison.Ordinal))
                    {
                        var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext);
                        urlToValidate = urlHelper.Content(urlToValidate);
                    }

                    urlToValidate = urlToValidate.ToUriComponents();

                    if (!Uri.IsWellFormedUriString(urlToValidate, UriKind.RelativeOrAbsolute))
                    {
                        updater.ModelState.AddModelError(nameof(part.Url), S["{0} is an invalid url.", part.Url]);
                    }
                    else
                    {
                        var link = $"<a href=\"{_htmlencoder.Encode(urlToValidate)}\"></a>";

                        if (!String.Equals(link, _htmlSanitizerService.Sanitize(link), StringComparison.OrdinalIgnoreCase))
                        {
                            updater.ModelState.AddModelError(nameof(part.Url), S["{0} is an invalid url.", part.Url]);
                        }
                    }
                }


                        Subject = subject,
                        Body = body,

            // The notification message can contain HTML by design
    <span>@T["Required roles: <em>{0}</em>", Model.Activity.Roles.Any() ? Html.Raw(Html.Encode(string.Join(", ", Model.Activity.Roles))) : T["Any"]]</span>
                        <span>@T.GetLocalizedStatus(Model.Workflow.Status)</span>
                                        @T.GetLocalizedStatus(entry.Workflow.Status)

            // A string used in LocalizedHtmlString won't be encoded so it needs to be pre-encoded.
            // Passing the title as an argument so it uses the HtmlEncoder when rendered
            // Another options would be to use new LocalizedHtmlString(Html.Encode(title)) but it's not available in the current context

            return !string.IsNullOrEmpty(title) ? new LocalizedHtmlString(nameof(ActivityExtensions.GetTitleOrDefault), "{0}", false, title) : defaultTitle();
        public static LocalizedHtmlString GetLocalizedStatus(this IHtmlLocalizer H, WorkflowStatus status)
                _ => throw new NotSupportedException(),