CVE-2022-0820: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Cross-site Scripting (XSS) - Stored in GitHub repository orchardcms/orchardcore prior to 1.3.0.
CWE description: 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.
CVSS v3 base score: 6.1
Published: 2022-03-11T00:15Z
Repository: https://github.com/orchardcms/orchardcore
Language: C#
Files changed: src/OrchardCore.Modules/OrchardCore.ContentFields/Controllers/LocalizationSetContentPickerAdminController.cs, src/OrchardCore.Modules/OrchardCore.Demo/Controllers/ContentController.cs, src/OrchardCore.Modules/OrchardCore.Features/Controllers/AdminController.cs, src/OrchardCore.Modules/OrchardCore.Roles/Controllers/AdminController.cs, src/OrchardCore.Modules/OrchardCore.Seo/Drivers/SeoContentDriver.cs, src/OrchardCore.Modules/OrchardCore.Shortcodes/Controllers/AdminController.cs, src/OrchardCore.Modules/OrchardCore.Tenants/Controllers/FeatureProfilesController.cs
Commit date: 2022-03-10 10:03:52 -0800
Commit message: Fix missing permission checks and encoding. (#11344)


Diff: diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Controllers/LocalizationSetContentPickerAdminController.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Controllers/LocalizationSetContentPickerAdminController.cs
index f0a7ca5d11..3818679bd7 100644
--- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Controllers/LocalizationSetContentPickerAdminController.cs
+++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Controllers/LocalizationSetContentPickerAdminController.cs
@@ -1,19 +1,22 @@
 using System.Collections.Generic;
 using System.Linq;
 using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
 using OrchardCore.Admin;
 using OrchardCore.ContentFields.Settings;
 using OrchardCore.ContentFields.ViewModels;
 using OrchardCore.ContentLocalization;
 using OrchardCore.ContentManagement;
 using OrchardCore.ContentManagement.Metadata;
 using OrchardCore.ContentManagement.Records;
+using OrchardCore.Contents;
 using OrchardCore.Modules;
 using YesSql;
 using YesSql.Services;
+using IHttpContextAccessor = Microsoft.AspNetCore.Http.IHttpContextAccessor;
 
 namespace OrchardCore.ContentFields.Controllers
 {
     [RequireFeatures("OrchardCore.ContentLocalization")]
     [Admin]
@@ -21,22 +24,27 @@ namespace OrchardCore.ContentFields.Controllers
     {
         private readonly IContentDefinitionManager _contentDefinitionManager;
         private readonly IContentLocalizationManager _contentLocalizationManager;
         private readonly IContentManager _contentManager;
         private readonly ISession _session;
+        private readonly IAuthorizationService _authorizationService;
+        private readonly IHttpContextAccessor _httpContextAccessor;
 
         public LocalizationSetContentPickerAdminController(
             IContentDefinitionManager contentDefinitionManager,
             IContentLocalizationManager contentLocalizationManager,
             IContentManager contentManager,
-            ISession session
-            )
+            ISession session,
+            IAuthorizationService authorizationService,
+            IHttpContextAccessor httpContextAccessor)
         {
             _contentDefinitionManager = contentDefinitionManager;
             _contentLocalizationManager = contentLocalizationManager;
             _contentManager = contentManager;
             _session = session;
+            _authorizationService = authorizationService;
+            _httpContextAccessor = httpContextAccessor;
         }
 
         [HttpGet]
         public async Task<IActionResult> SearchLocalizationSets(string part, string field, string query)
         {
@@ -69,16 +77,19 @@ namespace OrchardCore.ContentFields.Controllers
 
             var results = new List<VueMultiselectItemViewModel>();
 
             foreach (var contentItem in cleanedContentItems)
             {
-                results.Add(new VueMultiselectItemViewModel
+                if (await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.ViewContent, contentItem))
                 {
-                    Id = contentItem.Key, //localization set
-                    DisplayText = contentItem.Value.ToString(),
-                    HasPublished = await _contentManager.HasPublishedVersionAsync(contentItem.Value)
-                });
+                    results.Add(new VueMultiselectItemViewModel
+                    {
+                        Id = contentItem.Key, //localization set
+                        DisplayText = contentItem.Value.ToString(),
+                        HasPublished = await _contentManager.HasPublishedVersionAsync(contentItem.Value)
+                    });
+                }
             }
 
             return new ObjectResult(results);
         }
     }
diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/ContentController.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/ContentController.cs
index 047258b800..e82dfd0779 100644
--- a/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/ContentController.cs
+++ b/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/ContentController.cs
@@ -1,32 +1,41 @@
 using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
 using OrchardCore.Admin;
 using OrchardCore.ContentManagement;
 using OrchardCore.ContentManagement.Display;
+using OrchardCore.Contents;
 using OrchardCore.DisplayManagement.ModelBinding;
 using YesSql;
+using IHttpContextAccessor = Microsoft.AspNetCore.Http.IHttpContextAccessor;
 
 namespace OrchardCore.Demo.Controllers
 {
     public class ContentController : Controller
     {
         private readonly IContentItemDisplayManager _contentDisplay;
         private readonly IContentManager _contentManager;
         private readonly ISession _session;
         private readonly IUpdateModelAccessor _updateModelAccessor;
+        private readonly IAuthorizationService _authorizationService;
+        private readonly IHttpContextAccessor _httpContextAccessor;
 
         public ContentController(
             IContentManager contentManager,
             IContentItemDisplayManager contentDisplay,
             ISession session,
-            IUpdateModelAccessor updateModelAccessor)
+            IUpdateModelAccessor updateModelAccessor,
+            IAuthorizationService authorizationService,
+            IHttpContextAccessor httpContextAccessor)
         {
             _contentManager = contentManager;
             _contentDisplay = contentDisplay;
             _session = session;
             _updateModelAccessor = updateModelAccessor;
+            _authorizationService = authorizationService;
+            _httpContextAccessor = httpContextAccessor;
         }
 
         public async Task<ActionResult> Display(string contentItemId)
         {
             var contentItem = await _contentManager.GetAsync(contentItemId);
@@ -34,10 +43,15 @@ namespace OrchardCore.Demo.Controllers
             if (contentItem == null)
             {
                 return NotFound();
             }
 
+            if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.ViewContent, contentItem))
+            {
+                return Forbid();
+            }
+
             var shape = await _contentDisplay.BuildDisplayAsync(contentItem, _updateModelAccessor.ModelUpdater);
             return View(shape);
         }
 
         [Admin]
@@ -48,10 +62,15 @@ namespace OrchardCore.Demo.Controllers
             if (contentItem == null)
             {
                 return NotFound();
             }
 
+            if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.EditContent, contentItem))
+            {
+                return Forbid();
+            }
+
             var shape = await _contentDisplay.BuildEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, false);
             return View(shape);
         }
 
         [Admin, HttpPost, ActionName("Edit")]
@@ -62,10 +81,15 @@ namespace OrchardCore.Demo.Controllers
             if (contentItem == null)
             {
                 return NotFound();
             }
 
+            if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.EditContent, contentItem))
+            {
+                return Forbid();
+            }
+
             var shape = await _contentDisplay.UpdateEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, false);
 
             if (!ModelState.IsValid)
             {
                 await _session.CancelAsync();
diff --git a/src/OrchardCore.Modules/OrchardCore.Features/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Features/Controllers/AdminController.cs
index 6b811e56da..96e1244588 100644
--- a/src/OrchardCore.Modules/OrchardCore.Features/Controllers/AdminController.cs
+++ b/src/OrchardCore.Modules/OrchardCore.Features/Controllers/AdminController.cs
@@ -110,10 +110,15 @@ namespace OrchardCore.Features.Controllers
         }
 
         [HttpPost]
         public async Task<IActionResult> Disable(string id)
         {
+            if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageFeatures))
+            {
+                return Forbid();
+            }
+
             var feature = (await _shellFeaturesManager.GetAvailableFeaturesAsync())
                 .FirstOrDefault(f => !f.IsTheme() && f.Id == id);
 
             if (feature == null)
             {
@@ -132,10 +137,15 @@ namespace OrchardCore.Features.Controllers
         }
 
         [HttpPost]
         public async Task<IActionResult> Enable(string id)
         {
+            if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageFeatures))
+            {
+                return Forbid();
+            }
+
             var feature = (await _shellFeaturesManager.GetAvailableFeaturesAsync())
                 .FirstOrDefault(f => !f.IsTheme() && f.Id == id);
 
             if (feature == null)
             {
diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Roles/Controllers/AdminController.cs
index aae4ee9e9b..ad15b14882 100644
--- a/src/OrchardCore.Modules/OrchardCore.Roles/Controllers/AdminController.cs
+++ b/src/OrchardCore.Modules/OrchardCore.Roles/Controllers/AdminController.cs
@@ -83,10 +83,15 @@ namespace OrchardCore.Roles.Controllers
         }
 
         [HttpPost]
         public async Task<IActionResult> Create(CreateRoleViewModel model)
         {
+            if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRoles))
+            {
+                return Forbid();
+            }
+
             if (ModelState.IsValid)
             {
                 model.RoleName = model.RoleName.Trim();
 
                 if (model.RoleName.Contains('/'))
diff --git a/src/OrchardCore.Modules/OrchardCore.Seo/Drivers/SeoContentDriver.cs b/src/OrchardCore.Modules/OrchardCore.Seo/Drivers/SeoContentDriver.cs
index 940ee1c869..7197d857d4 100644
--- a/src/OrchardCore.Modules/OrchardCore.Seo/Drivers/SeoContentDriver.cs
+++ b/src/OrchardCore.Modules/OrchardCore.Seo/Drivers/SeoContentDriver.cs
@@ -1,6 +1,8 @@
 using System;
+using System.Text.Encodings.Web;
+using System.Text.Json;
 using System.Threading.Tasks;
 using Microsoft.AspNetCore.Html;
 using OrchardCore.ContentManagement;
 using OrchardCore.ContentManagement.Display.ContentDisplay;
 using OrchardCore.ContentManagement.Metadata.Settings;
@@ -19,24 +21,27 @@ namespace OrchardCore.Seo.Drivers
     {
         private readonly IContentManager _contentManager;
         private readonly IPageTitleBuilder _pageTitleBuilder;
         private readonly IResourceManager _resourceManager;
         private readonly IShortcodeService _shortcodeService;
+        private readonly HtmlEncoder _htmlEncoder;
 
         private bool _primaryContentRendered { get; set; }
 
         public SeoContentDriver(
             IContentManager contentManager,
             IPageTitleBuilder pageTitleBuilder,
             IResourceManager resourceManager,
-            IShortcodeService shortcodeService
+            IShortcodeService shortcodeService,
+            HtmlEncoder htmlEncoder
             )
         {
             _contentManager = contentManager;
             _pageTitleBuilder = pageTitleBuilder;
             _resourceManager = resourceManager;
             _shortcodeService = shortcodeService;
+            _htmlEncoder = htmlEncoder;
         }
 
         public override async Task<IDisplayResult> DisplayAsync(ContentItem contentItem, BuildDisplayContext context)
         {
             // We only apply this on the primary content item, which is considered the first call to BuildDisplay.
@@ -58,30 +63,35 @@ namespace OrchardCore.Seo.Drivers
             if (!aspect.Render)
             {
                 return null;
             }
 
+            var shortCodeContext = new Context
+            {
+                ["ContentItem"] = contentItem
+            };
+
             if (!String.IsNullOrEmpty(aspect.PageTitle))
             {
-                _pageTitleBuilder.SetFixedTitle(new HtmlString(await RenderAsync(aspect.PageTitle, contentItem)));
+                _pageTitleBuilder.SetFixedTitle(new HtmlString(_htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.PageTitle, shortCodeContext))));
             }
 
             if (!String.IsNullOrEmpty(aspect.MetaDescription))
             {
                 _resourceManager.RegisterMeta(new MetaEntry
                 {
                     Name = "description",
-                    Content = await RenderAsync(aspect.MetaDescription, contentItem)
+                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.MetaDescription, shortCodeContext))
                 });
             }
 
             if (!String.IsNullOrEmpty(aspect.MetaKeywords))
             {
                 _resourceManager.RegisterMeta(new MetaEntry
                 {
                     Name = "keywords",
-                    Content = await RenderAsync(aspect.MetaKeywords, contentItem)
+                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.MetaKeywords, shortCodeContext))
                 });
             }
 
             if (!String.IsNullOrEmpty(aspect.Canonical))
             {
@@ -95,191 +105,197 @@ namespace OrchardCore.Seo.Drivers
             if (!String.IsNullOrEmpty(aspect.MetaRobots))
             {
                 _resourceManager.RegisterMeta(new MetaEntry
                 {
                     Name = "robots",
-                    Content = await RenderAsync(aspect.MetaRobots, contentItem)
+                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.MetaRobots, shortCodeContext))
                 });
             }
 
             foreach (var customMetaTag in aspect.CustomMetaTags)
             {
                 // Generate a new meta entry as the builder is preopulated.
                 _resourceManager.RegisterMeta(new MetaEntry(
-                    await RenderAsync(customMetaTag.Name, contentItem),
-                    await RenderAsync(customMetaTag.Property, contentItem),
-                    await RenderAsync(customMetaTag.Content, contentItem),
-                    await RenderAsync(customMetaTag.HttpEquiv, contentItem),
-                    await RenderAsync(customMetaTag.Charset, contentItem)));
+                    _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.Name, shortCodeContext)),
+                    _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.Property, shortCodeContext)),
+                    _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.Content, shortCodeContext)),
+                    _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.HttpEquiv, shortCodeContext)),
+                    _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.Charset, shortCodeContext))));
             }
 
             // OpenGraph.
             if (!String.IsNullOrEmpty(aspect.OpenGraphType))
             {
                 _resourceManager.RegisterMeta(new MetaEntry
                 {
                     Property = "og:type",
-                    Content = await RenderAsync(aspect.OpenGraphType, contentItem)
+                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphType, shortCodeContext))
                 });
             }
 
             if (!String.IsNullOrEmpty(aspect.OpenGraphTitle))
             {
                 _resourceManager.RegisterMeta(new MetaEntry
                 {
                     Property = "og:title",
-                    Content = await RenderAsync(aspect.OpenGraphTitle, contentItem)
+                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphTitle, shortCodeContext))
                 });
             }
 
             if (!String.IsNullOrEmpty(aspect.OpenGraphDescription))
             {
                 _resourceManager.RegisterMeta(new MetaEntry
                 {
                     Property = "og:description",
-                    Content = await RenderAsync(aspect.OpenGraphDescription, contentItem)
+                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphDescription, shortCodeContext))
                 });
             }
 
             if (!String.IsNullOrEmpty(aspect.OpenGraphImage))
             {
                 _resourceManager.RegisterMeta(new MetaEntry
                 {
                     Property = "og:image",
-                    Content = await RenderAsync(aspect.OpenGraphImage, contentItem)
+                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphImage, shortCodeContext))
                 });
             }
 
             if (!String.IsNullOrEmpty(aspect.OpenGraphImageAlt))
             {
                 _resourceManager.RegisterMeta(new MetaEntry
                 {
                     Property = "og:image:alt",
-                    Content = await RenderAsync(aspect.OpenGraphImageAlt, contentItem)
+                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphImageAlt, shortCodeContext))
                 });
             }
 
             if (!String.IsNullOrEmpty(aspect.OpenGraphUrl))
             {
                 _resourceManager.RegisterMeta(new MetaEntry
                 {
                     Property = "og:url",
-                    Content = await RenderAsync(aspect.OpenGraphUrl, contentItem)
+                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphUrl, shortCodeContext))
                 });
             }
 
             if (!String.IsNullOrEmpty(aspect.OpenGraphSiteName))
             {
                 _resourceManager.RegisterMeta(new MetaEntry
                 {
                     Property = "og:site_name",
-                    Content = await RenderAsync(aspect.OpenGraphSiteName, contentItem)
+                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphSiteName, shortCodeContext))
                 });
             }
 
             if (!String.IsNullOrEmpty(aspect.OpenGraphAppId))
             {
                 _resourceManager.RegisterMeta(new MetaEntry
                 {
                     Property = "fb:app_id",
-                    Content = await RenderAsync(aspect.OpenGraphAppId, contentItem)
+                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphAppId, shortCodeContext))
                 });
             }
 
             if (!String.IsNullOrEmpty(aspect.OpenGraphLocale))
             {
                 _resourceManager.RegisterMeta(new MetaEntry
                 {
                     Property = "og:locale",
-                    Content = await RenderAsync(aspect.OpenGraphLocale, contentItem)
+                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphLocale, shortCodeContext))
                 });
             }
 
             // Twitter.
             if (!String.IsNullOrEmpty(aspect.TwitterCard))
             {
                 _resourceManager.RegisterMeta(new MetaEntry
                 {
                     Property = "twitter:card",
-                    Content = await RenderAsync(aspect.TwitterCard, contentItem)
+                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterCard, shortCodeContext))
                 });
             }
 
             if (!String.IsNullOrEmpty(aspect.TwitterSite))
             {
                 _resourceManager.RegisterMeta(new MetaEntry
                 {
                     Property = "twitter:site",
-                    Content = await RenderAsync(aspect.TwitterSite, contentItem)
+                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterSite, shortCodeContext))
                 });
             }
 
             if (!String.IsNullOrEmpty(aspect.TwitterTitle))
             {
                 _resourceManager.RegisterMeta(new MetaEntry
                 {
                     Name = "twitter:title",
-                    Content = await RenderAsync(aspect.TwitterTitle, contentItem)
+                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterTitle, shortCodeContext))
                 });
             }
 
             if (!String.IsNullOrEmpty(aspect.TwitterDescription))
             {
                 _resourceManager.RegisterMeta(new MetaEntry
                 {
                     Name = "twitter:description",
-                    Content = await RenderAsync(aspect.TwitterDescription, contentItem)
+                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterDescription, shortCodeContext))
                 });
             }
 
             if (!String.IsNullOrEmpty(aspect.TwitterImage))
             {
                 _resourceManager.RegisterMeta(new MetaEntry
                 {
                     Name = "twitter:image",
-                    Content = await RenderAsync(aspect.TwitterImage, contentItem)
+                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterImage, shortCodeContext))
                 });
             }
 
             if (!String.IsNullOrEmpty(aspect.TwitterImageAlt))
             {
                 _resourceManager.RegisterMeta(new MetaEntry
                 {
                     Name = "twitter:image:alt",
-                    Content = await RenderAsync(aspect.TwitterImageAlt, contentItem)
+                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterImageAlt, shortCodeContext))
                 });
             }
 
             if (!String.IsNullOrEmpty(aspect.TwitterCreator))
             {
                 _resourceManager.RegisterMeta(new MetaEntry
                 {
                     Name = "twitter:creator",
-                    Content = await RenderAsync(aspect.TwitterCreator, contentItem)
+                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterCreator, shortCodeContext))
                 });
             }
 
             if (!String.IsNullOrEmpty(aspect.TwitterUrl))
             {
                 _resourceManager.RegisterMeta(new MetaEntry
                 {
                     Name = "twitter:url",
-                    Content = await RenderAsync(aspect.TwitterUrl, contentItem)
+                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterUrl, shortCodeContext))
                 });
             }
 
             if (!String.IsNullOrEmpty(aspect.GoogleSchema))
             {
-                _resourceManager.RegisterHeadScript(new HtmlString($"<script type=\"application/ld+json\">\n{aspect.GoogleSchema}\n</script>"));
+                var json = await _shortcodeService.ProcessAsync(aspect.GoogleSchema, shortCodeContext);
+
+                try
+                {
+                    // Validate json format
+                    JsonDocument.Parse(json);
+                }
+                catch
+                {
+                    json = "{ \"error\": \"Invalid JSON content in SEO settings\" }";
+                }
+
+                _resourceManager.RegisterHeadScript(new HtmlString($"<script type=\"application/ld+json\">\n{json}\n</script>"));
+
             }
 
             return null;
         }
-
-        private ValueTask<string> RenderAsync(string template, ContentItem contentItem)
-            => _shortcodeService.ProcessAsync(template,
-                    new Context
-                    {
-                        ["ContentItem"] = contentItem
-                    });
     }
 }
diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Controllers/AdminController.cs
index dc52159673..b72d6795bf 100644
--- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Controllers/AdminController.cs
+++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Controllers/AdminController.cs
@@ -9,10 +9,11 @@ using Microsoft.AspNetCore.Mvc.Rendering;
 using Microsoft.AspNetCore.Routing;
 using Microsoft.Extensions.Localization;
 using Newtonsoft.Json;
 using OrchardCore.DisplayManagement;
 using OrchardCore.DisplayManagement.Notify;
+using OrchardCore.Infrastructure.Html;
 using OrchardCore.Liquid;
 using OrchardCore.Modules;
 using OrchardCore.Navigation;
 using OrchardCore.Routing;
 using OrchardCore.Settings;
@@ -31,31 +32,34 @@ namespace OrchardCore.Shortcodes.Controllers
         private readonly ILiquidTemplateManager _liquidTemplateManager;
         private readonly ISiteService _siteService;
         private readonly INotifier _notifier;
         private readonly IStringLocalizer S;
         private readonly IHtmlLocalizer H;
+        private readonly IHtmlSanitizerService _htmlSanitizerService;
         private readonly dynamic New;
 
         public AdminController(
             IAuthorizationService authorizationService,
             ShortcodeTemplatesManager shortcodeTemplatesManager,
             ILiquidTemplateManager liquidTemplateManager,
             ISiteService siteService,
             INotifier notifier,
             IShapeFactory shapeFactory,
             IStringLocalizer<AdminController> stringLocalizer,
-            IHtmlLocalizer<AdminController> htmlLocalizer
+            IHtmlLocalizer<AdminController> htmlLocalizer,
+            IHtmlSanitizerService htmlSanitizerService
             )
         {
             _authorizationService = authorizationService;
             _shortcodeTemplatesManager = shortcodeTemplatesManager;
             _liquidTemplateManager = liquidTemplateManager;
             _siteService = siteService;
             _notifier = notifier;
             New = shapeFactory;
             S = stringLocalizer;
             H = htmlLocalizer;
+            _htmlSanitizerService = htmlSanitizerService;
         }
 
         public async Task<IActionResult> Index(ContentOptions options, PagerParameters pagerParameters)
         {
             if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageShortcodeTemplates))
@@ -157,11 +161,11 @@ namespace OrchardCore.Shortcodes.Controllers
             {
                 var template = new ShortcodeTemplate
                 {
                     Content = model.Content,
                     Hint = model.Hint,
-                    Usage = model.Usage,
+                    Usage = _htmlSanitizerService.Sanitize(model.Usage),
                     DefaultValue = model.DefaultValue,
                     Categories = JsonConvert.DeserializeObject<string[]>(model.SelectedCategories)
                 };
 
                 await _shortcodeTemplatesManager.UpdateShortcodeTemplateAsync(model.Name, template);
@@ -254,11 +258,11 @@ namespace OrchardCore.Shortcodes.Controllers
             {
                 var template = new ShortcodeTemplate
                 {
                     Content = model.Content,
                     Hint = model.Hint,
-                    Usage = model.Usage,
+                    Usage = _htmlSanitizerService.Sanitize(model.Usage),
                     DefaultValue = model.DefaultValue,
                     Categories = JsonConvert.DeserializeObject<string[]>(model.SelectedCategories)
                 };
 
                 await _shortcodeTemplatesManager.RemoveShortcodeTemplateAsync(sourceName);
diff --git a/src/OrchardCore.Modules/OrchardCore.Tenants/Controllers/FeatureProfilesController.cs b/src/OrchardCore.Modules/OrchardCore.Tenants/Controllers/FeatureProfilesController.cs
index 147ff6c33e..c66e14cb2e 100644
--- a/src/OrchardCore.Modules/OrchardCore.Tenants/Controllers/FeatureProfilesController.cs
+++ b/src/OrchardCore.Modules/OrchardCore.Tenants/Controllers/FeatureProfilesController.cs
@@ -38,12 +38,12 @@ namespace OrchardCore.Tenants.Controllers
             IAuthorizationService authorizationService,
             FeatureProfilesManager featueProfilesManager,
             ISiteService siteService,
             INotifier notifier,
             IShapeFactory shapeFactory,
-            IStringLocalizer<AdminController> stringLocalizer,
-            IHtmlLocalizer<AdminController> htmlLocalizer
+            IStringLocalizer<FeatureProfilesController> stringLocalizer,
+            IHtmlLocalizer<FeatureProfilesController> htmlLocalizer
             )
         {
             _authorizationService = authorizationService;
             _featureProfilesManager = featueProfilesManager;
             _siteService = siteService;

Vulnerable code:             ISession session
            )
                results.Add(new VueMultiselectItemViewModel
                    Id = contentItem.Key, //localization set
                    DisplayText = contentItem.Value.ToString(),
                    HasPublished = await _contentManager.HasPublishedVersionAsync(contentItem.Value)
                });
            IUpdateModelAccessor updateModelAccessor)
            IShortcodeService shortcodeService
                _pageTitleBuilder.SetFixedTitle(new HtmlString(await RenderAsync(aspect.PageTitle, contentItem)));
                    Content = await RenderAsync(aspect.MetaDescription, contentItem)
                    Content = await RenderAsync(aspect.MetaKeywords, contentItem)
                    Content = await RenderAsync(aspect.MetaRobots, contentItem)
                    await RenderAsync(customMetaTag.Name, contentItem),
                    await RenderAsync(customMetaTag.Property, contentItem),
                    await RenderAsync(customMetaTag.Content, contentItem),
                    await RenderAsync(customMetaTag.HttpEquiv, contentItem),
                    await RenderAsync(customMetaTag.Charset, contentItem)));
                    Content = await RenderAsync(aspect.OpenGraphType, contentItem)
                    Content = await RenderAsync(aspect.OpenGraphTitle, contentItem)
                    Content = await RenderAsync(aspect.OpenGraphDescription, contentItem)
                    Content = await RenderAsync(aspect.OpenGraphImage, contentItem)
                    Content = await RenderAsync(aspect.OpenGraphImageAlt, contentItem)
                    Content = await RenderAsync(aspect.OpenGraphUrl, contentItem)
                    Content = await RenderAsync(aspect.OpenGraphSiteName, contentItem)
                    Content = await RenderAsync(aspect.OpenGraphAppId, contentItem)
                    Content = await RenderAsync(aspect.OpenGraphLocale, contentItem)
                    Content = await RenderAsync(aspect.TwitterCard, contentItem)
                    Content = await RenderAsync(aspect.TwitterSite, contentItem)
                    Content = await RenderAsync(aspect.TwitterTitle, contentItem)
                    Content = await RenderAsync(aspect.TwitterDescription, contentItem)
                    Content = await RenderAsync(aspect.TwitterImage, contentItem)
                    Content = await RenderAsync(aspect.TwitterImageAlt, contentItem)
                    Content = await RenderAsync(aspect.TwitterCreator, contentItem)
                    Content = await RenderAsync(aspect.TwitterUrl, contentItem)
                _resourceManager.RegisterHeadScript(new HtmlString($"<script type=\"application/ld+json\">\n{aspect.GoogleSchema}\n</script>"));

        private ValueTask<string> RenderAsync(string template, ContentItem contentItem)
            => _shortcodeService.ProcessAsync(template,
                    new Context
                    {
                        ["ContentItem"] = contentItem
                    });
            IHtmlLocalizer<AdminController> htmlLocalizer
                    Usage = model.Usage,
                    Usage = model.Usage,
            IStringLocalizer<AdminController> stringLocalizer,
            IHtmlLocalizer<AdminController> htmlLocalizer
Fixed code: using Microsoft.AspNetCore.Authorization;
using OrchardCore.Contents;
using IHttpContextAccessor = Microsoft.AspNetCore.Http.IHttpContextAccessor;
        private readonly IAuthorizationService _authorizationService;
        private readonly IHttpContextAccessor _httpContextAccessor;
            ISession session,
            IAuthorizationService authorizationService,
            IHttpContextAccessor httpContextAccessor)
            _authorizationService = authorizationService;
            _httpContextAccessor = httpContextAccessor;
                if (await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.ViewContent, contentItem))
                    results.Add(new VueMultiselectItemViewModel
                    {
                        Id = contentItem.Key, //localization set
                        DisplayText = contentItem.Value.ToString(),
                        HasPublished = await _contentManager.HasPublishedVersionAsync(contentItem.Value)
                    });
                }
using Microsoft.AspNetCore.Authorization;
using OrchardCore.Contents;
using IHttpContextAccessor = Microsoft.AspNetCore.Http.IHttpContextAccessor;
        private readonly IAuthorizationService _authorizationService;
        private readonly IHttpContextAccessor _httpContextAccessor;
            IUpdateModelAccessor updateModelAccessor,
            IAuthorizationService authorizationService,
            IHttpContextAccessor httpContextAccessor)
            _authorizationService = authorizationService;
            _httpContextAccessor = httpContextAccessor;
            if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.ViewContent, contentItem))
            {
                return Forbid();
            }

            if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.EditContent, contentItem))
            {
                return Forbid();
            }

            if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.EditContent, contentItem))
            {
                return Forbid();
            }

            if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageFeatures))
            {
                return Forbid();
            }

            if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageFeatures))
            {
                return Forbid();
            }

            if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRoles))
            {
                return Forbid();
            }

using System.Text.Encodings.Web;
using System.Text.Json;
        private readonly HtmlEncoder _htmlEncoder;
            IShortcodeService shortcodeService,
            HtmlEncoder htmlEncoder
            _htmlEncoder = htmlEncoder;
            var shortCodeContext = new Context
            {
                ["ContentItem"] = contentItem
            };

                _pageTitleBuilder.SetFixedTitle(new HtmlString(_htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.PageTitle, shortCodeContext))));
                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.MetaDescription, shortCodeContext))
                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.MetaKeywords, shortCodeContext))
                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.MetaRobots, shortCodeContext))
                    _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.Name, shortCodeContext)),
                    _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.Property, shortCodeContext)),
                    _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.Content, shortCodeContext)),
                    _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.HttpEquiv, shortCodeContext)),
                    _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.Charset, shortCodeContext))));
                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphType, shortCodeContext))
                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphTitle, shortCodeContext))
                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphDescription, shortCodeContext))
                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphImage, shortCodeContext))
                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphImageAlt, shortCodeContext))
                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphUrl, shortCodeContext))
                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphSiteName, shortCodeContext))
                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphAppId, shortCodeContext))
                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphLocale, shortCodeContext))
                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterCard, shortCodeContext))
                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterSite, shortCodeContext))
                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterTitle, shortCodeContext))
                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterDescription, shortCodeContext))
                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterImage, shortCodeContext))
                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterImageAlt, shortCodeContext))
                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterCreator, shortCodeContext))
                    Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterUrl, shortCodeContext))
                var json = await _shortcodeService.ProcessAsync(aspect.GoogleSchema, shortCodeContext);

                try
                {
                    // Validate json format
                    JsonDocument.Parse(json);
                }
                catch
                {
                    json = "{ \"error\": \"Invalid JSON content in SEO settings\" }";
                }

                _resourceManager.RegisterHeadScript(new HtmlString($"<script type=\"application/ld+json\">\n{json}\n</script>"));

using OrchardCore.Infrastructure.Html;
        private readonly IHtmlSanitizerService _htmlSanitizerService;
            IHtmlLocalizer<AdminController> htmlLocalizer,
            IHtmlSanitizerService htmlSanitizerService
            _htmlSanitizerService = htmlSanitizerService;
                    Usage = _htmlSanitizerService.Sanitize(model.Usage),
                    Usage = _htmlSanitizerService.Sanitize(model.Usage),
            IStringLocalizer<FeatureProfilesController> stringLocalizer,
            IHtmlLocalizer<FeatureProfilesController> htmlLocalizer