Security
Headlines
HeadlinesLatestCVEs

Headline

CVE-2021-25976: Merge pull request #1742 from PiranhaCMS/features/manager-security-up… · PiranhaCMS/piranha.core@e42abac

In PiranhaCMS, versions 4.0.0-alpha1 to 9.2.0 are vulnerable to cross-site request forgery (CSRF) when performing various actions supported by the management system, such as deleting a user, deleting a role, editing a post, deleting a media folder etc., when an ID is known.

CVE
#csrf#microsoft#js#git

Permalink

Browse files

Merge pull request #1742 from PiranhaCMS/features/manager-security-up…

…date

Updated manager security. Fixes #1741

  • Loading branch information

Showing with 1,051 additions and 630 deletions.

  1. +2 −3 core/Piranha.Manager.LocalAuth/Areas/Manager/Pages/Login.cshtml.cs
  2. +1 −1 core/Piranha.Manager/Areas/Manager/Pages/Index.cshtml.cs
  3. +0 −1 core/Piranha.Manager/Areas/Manager/Shared/Partial/_ContentPickerModal.cshtml
  4. +0 −1 core/Piranha.Manager/Areas/Manager/Shared/Partial/_MediaPickerModal.cshtml
  5. +0 −1 core/Piranha.Manager/Areas/Manager/Shared/Partial/_PagePickerModal.cshtml
  6. +0 −1 core/Piranha.Manager/Areas/Manager/Shared/Partial/_PostPickerModal.cshtml
  7. +0 −1 core/Piranha.Manager/Areas/Manager/Shared/Partial/_PreviewModal.cshtml
  8. +6 −1 core/Piranha.Manager/Areas/Manager/Shared/_Layout.cshtml
  9. +1 −0 core/Piranha.Manager/Controllers/AliasApiController.cs
  10. +55 −0 core/Piranha.Manager/Controllers/AuthController.cs
  11. +21 −14 core/Piranha.Manager/Controllers/CommentApiController.cs
  12. +1 −0 core/Piranha.Manager/Controllers/ConfigApiController.cs
  13. +4 −3 core/Piranha.Manager/Controllers/ContentApiController.cs
  14. +51 −5 core/Piranha.Manager/Controllers/LanguageApiController.cs
  15. +5 −4 core/Piranha.Manager/Controllers/MediaApiController.cs
  16. +1 −0 core/Piranha.Manager/Controllers/ModuleApiController.cs
  17. +10 −9 core/Piranha.Manager/Controllers/PageApiController.cs
  18. +1 −0 core/Piranha.Manager/Controllers/PermissionApiController.cs
  19. +7 −6 core/Piranha.Manager/Controllers/PostApiController.cs
  20. +1 −0 core/Piranha.Manager/Controllers/SiteApiController.cs
  21. +166 −166 core/Piranha.Manager/{ → Extensions}/ManagerExtensions.cs
  22. +23 −6 core/Piranha.Manager/{ → Extensions}/ManagerStartupExtensions.cs
  23. +36 −0 core/Piranha.Manager/ManagerOptions.cs
  24. +5 −0 core/Piranha.Manager/Models/LanguageEditModel.cs
  25. +5 −5 core/Piranha.Manager/assets/dist/css/full.min.css
  26. +5 −5 core/Piranha.Manager/assets/dist/css/slim.min.css
  27. +43 −36 core/Piranha.Manager/assets/dist/js/piranha.alias.js
  28. +1 −1 core/Piranha.Manager/assets/dist/js/piranha.alias.min.js
  29. +53 −29 core/Piranha.Manager/assets/dist/js/piranha.comment.js
  30. +1 −1 core/Piranha.Manager/assets/dist/js/piranha.comment.min.js
  31. +5 −1 core/Piranha.Manager/assets/dist/js/piranha.components.js
  32. +1 −1 core/Piranha.Manager/assets/dist/js/piranha.components.min.js
  33. +8 −5 core/Piranha.Manager/assets/dist/js/piranha.config.js
  34. +1 −1 core/Piranha.Manager/assets/dist/js/piranha.config.min.js
  35. +6 −4 core/Piranha.Manager/assets/dist/js/piranha.contentedit.js
  36. +1 −1 core/Piranha.Manager/assets/dist/js/piranha.contentedit.min.js
  37. +5 −1 core/Piranha.Manager/assets/dist/js/piranha.contentlist.js
  38. +1 −1 core/Piranha.Manager/assets/dist/js/piranha.contentlist.min.js
  39. +61 −23 core/Piranha.Manager/assets/dist/js/piranha.js
  40. +52 −31 core/Piranha.Manager/assets/dist/js/piranha.media.js
  41. +1 −1 core/Piranha.Manager/assets/dist/js/piranha.media.min.js
  42. +1 −1 core/Piranha.Manager/assets/dist/js/piranha.min.js
  43. +30 −19 core/Piranha.Manager/assets/dist/js/piranha.pageedit.js
  44. +1 −1 core/Piranha.Manager/assets/dist/js/piranha.pageedit.min.js
  45. +6 −4 core/Piranha.Manager/assets/dist/js/piranha.pagelist.js
  46. +1 −1 core/Piranha.Manager/assets/dist/js/piranha.pagelist.min.js
  47. +29 −22 core/Piranha.Manager/assets/dist/js/piranha.postedit.js
  48. +1 −1 core/Piranha.Manager/assets/dist/js/piranha.postedit.min.js
  49. +10 −10 core/Piranha.Manager/assets/dist/js/piranha.siteedit.js
  50. +1 −1 core/Piranha.Manager/assets/dist/js/piranha.siteedit.min.js
  51. +5 −1 core/Piranha.Manager/assets/src/js/components/post-archive.vue
  52. +43 −36 core/Piranha.Manager/assets/src/js/piranha.alias.js
  53. +53 −29 core/Piranha.Manager/assets/src/js/piranha.comment.js
  54. +8 −5 core/Piranha.Manager/assets/src/js/piranha.config.js
  55. +6 −4 core/Piranha.Manager/assets/src/js/piranha.contentedit.js
  56. +5 −1 core/Piranha.Manager/assets/src/js/piranha.contentlist.js
  57. +1 −0 core/Piranha.Manager/assets/src/js/piranha.dropzone.js
  58. +23 −17 core/Piranha.Manager/assets/src/js/piranha.languageedit.js
  59. +52 −31 core/Piranha.Manager/assets/src/js/piranha.media.js
  60. +8 −5 core/Piranha.Manager/assets/src/js/piranha.mediapicker.js
  61. +7 −0 core/Piranha.Manager/assets/src/js/piranha.notifications.js
  62. +30 −19 core/Piranha.Manager/assets/src/js/piranha.pageedit.js
  63. +6 −4 core/Piranha.Manager/assets/src/js/piranha.pagelist.js
  64. +29 −22 core/Piranha.Manager/assets/src/js/piranha.postedit.js
  65. +10 −10 core/Piranha.Manager/assets/src/js/piranha.siteedit.js
  66. +22 −1 core/Piranha.Manager/assets/src/js/piranha.utils.js
  67. +3 −1 core/Piranha.Manager/assets/src/scss/inc/_actions.scss
  68. +1 −1 core/Piranha.Manager/assets/src/scss/inc/_notifications.scss
  69. +6 −3 identity/Piranha.AspNetCore.Identity/Areas/Manager/Views/Role/List.cshtml
  70. +2 −1 identity/Piranha.AspNetCore.Identity/Controllers/RoleController.cs
  71. +1 −0 identity/Piranha.AspNetCore.Identity/Controllers/UserController.cs
  72. +2 −6 identity/Piranha.AspNetCore.Identity/assets/piranha.useredit.js
  73. +1 −3 identity/Piranha.AspNetCore.Identity/assets/piranha.userlist.js

@@ -104,10 +104,9 @@ public async Task<IActionResult> OnPostAsync(string returnUrl = null)

if (!string.IsNullOrEmpty(returnUrl))

{

return LocalRedirect(returnUrl);

return LocalRedirect($"~/manager/login/auth?returnUrl={ returnUrl }");

}

return new RedirectToPageResult(“Index”);

return LocalRedirect(“~/manager/login/auth”);

}

}

}

@@ -24,7 +24,7 @@ public IndexModel(IAuthorizationService service)

{

_service = service;

}

public async Task<IActionResult> OnGet()

public async Task<IActionResult> OnGet(string returnUrl = null)

{

var items = await Menu.Items.GetForUser(HttpContext.User, _service);

@@ -1,6 +1,5 @@

@inject ManagerLocalizer Localizer

<!-- The Modal -->

<div class="modal modal-panel fade" id="contentpicker">

<div class="modal-dialog modal-lg">

<div class="modal-content">

@@ -1,7 +1,6 @@

@inject IAuthorizationService Auth

@inject ManagerLocalizer Localizer

<!-- The Modal -->

<div class="modal modal-panel fade" id="mediapicker">

<div class="modal-dialog modal-lg">

<div class="modal-content">

@@ -1,6 +1,5 @@

@inject ManagerLocalizer Localizer

<!-- The Modal -->

<div class="modal modal-panel fade" id="pagepicker">

<div class="modal-dialog modal-lg">

<div class="modal-content">

@@ -1,6 +1,5 @@

@inject ManagerLocalizer Localizer

<!-- The Modal -->

<div class="modal modal-panel fade" id="postpicker">

<div class="modal-dialog modal-lg">

<div class="modal-content">

@@ -1,7 +1,6 @@

@inject IAuthorizationService Auth

@inject ManagerLocalizer Localizer

<!-- The Modal -->

<div class="modal fade" id="previewModal">

<div class="modal-dialog modal-lg">

<div class="modal-content">

@@ -1,4 +1,5 @@

@{

@inject Microsoft.Extensions.Options.IOptions<ManagerOptions> Options

@{

var module = Piranha.App.Modules.Get<Piranha.Manager.Module>();

var prerelease = Piranha.Utils.IsPreRelease(typeof(Piranha.Manager.Module).Assembly) ? “pre-release” : "";

var isRightToLeft = @System.Globalization.CultureInfo.CurrentCulture.TextInfo.IsRightToLeft;

@@ -53,6 +54,10 @@

var piranha = {};

window.piranha = piranha;

piranha.baseUrl = "@Url.Content(“~/”)";

piranha.antiForgery = {

cookieName: "@Options.Value.XsrfCookieName",

headerName: “@Options.Value.XsrfHeaderName”

};

</script>

<partial name="~/Areas/Manager/Shared/Partial/_EditorConfig.cshtml" />

@@ -25,6 +25,7 @@ namespace Piranha.Manager.Controllers

[Route(“manager/api/alias”)]

[Authorize(Policy = Permission.Admin)]

[ApiController]

[AutoValidateAntiforgeryToken]

public class AliasApiController : Controller

{

private readonly IApi _api;

@@ -0,0 +1,55 @@

/*

* Copyright © .NET Foundation and Contributors

*

* This software may be modified and distributed under the terms

* of the MIT license. See the LICENSE file for details.

*

* https://github.com/piranhacms/piranha.core

*

*/

using Microsoft.AspNetCore.Antiforgery;

using Microsoft.AspNetCore.Authorization;

using Microsoft.AspNetCore.Http;

using Microsoft.AspNetCore.Mvc;

using Microsoft.Extensions.Options;

namespace Piranha.Manager.Controllers

{

[Area(“Manager”)]

[Route(“manager/login/auth”)]

[Authorize(Policy = Permission.Admin)]

[ApiController]

public sealed class AuthController : Controller

{

private readonly IAntiforgery _antiForgery;

private readonly ManagerOptions _options;

/// <summary>

/// Default constructor.

/// </summary>

/// <param name="antiforgery">The antiforgery service</param>

/// <param name="options">The manager options</param>

public AuthController(IAntiforgery antiforgery, IOptions<ManagerOptions> options)

{

_antiForgery = antiforgery;

_options = options.Value;

}

[Route(“{returnUrl?}”)]

[HttpGet]

public IActionResult SetAuthCookie(string returnUrl = null)

{

var tokens = _antiForgery.GetAndStoreTokens(HttpContext);

Response.Cookies.Append(_options.XsrfCookieName, tokens.RequestToken, new CookieOptions

{

HttpOnly = false

});

if (!string.IsNullOrEmpty(returnUrl))

{

return LocalRedirect(returnUrl);

}

return LocalRedirect(“~/manager”);

}

}

}

@@ -24,8 +24,15 @@ namespace Piranha.Manager.Controllers

[Route(“manager/api/comment”)]

[Authorize(Policy = Permission.Admin)]

[ApiController]

[AutoValidateAntiforgeryToken]

public class CommentApiController : Controller

{

public class ApprovalModel

{

public Guid Id { get; set; }

public Guid? ParentId { get; set; }

}

private readonly CommentService _service;

private readonly ManagerLocalizer _localizer;

@@ -50,14 +57,14 @@ public Task<CommentListModel> List(Guid? id = null)

return _service.Get(id);

}

[Route(“approve/{id}/{parentId?}”)]

[HttpGet]

[Route(“approve”)]

[HttpPost]

[Authorize(Policy = Permission.CommentsApprove)]

public async Task<CommentListModel> Approve(Guid id, Guid? parentId = null)

public async Task<CommentListModel> Approve(ApprovalModel model)

{

await _service.ApproveAsync(id);

await _service.ApproveAsync(model.Id).ConfigureAwait(false);

var result = await List(parentId);

var result = await List(model.ParentId).ConfigureAwait(false);

result.Status = new StatusMessage

{

@@ -67,14 +74,14 @@ public async Task<CommentListModel> Approve(Guid id, Guid? parentId = null)

return result;

}

[Route(“unapprove/{id}/{parentId?}”)]

[HttpGet]

[Route(“unapprove”)]

[HttpPost]

[Authorize(Policy = Permission.CommentsApprove)]

public async Task<CommentListModel> UnApprove(Guid id, Guid? parentId = null)

public async Task<CommentListModel> UnApprove(ApprovalModel model)

{

await _service.UnApproveAsync(id);

await _service.UnApproveAsync(model.Id).ConfigureAwait(false);

var result = await List(parentId);

var result = await List(model.ParentId).ConfigureAwait(false);

result.Status = new StatusMessage

{

@@ -84,12 +91,12 @@ public async Task<CommentListModel> UnApprove(Guid id, Guid? parentId = null)

return result;

}

[Route(“delete/{id}”)]

[HttpGet]

[Route(“delete”)]

[HttpDelete]

[Authorize(Policy = Permission.CommentsDelete)]

public async Task<StatusMessage> Delete(Guid id)

public async Task<StatusMessage> Delete([FromBody]Guid id)

{

await _service.DeleteAsync(id);

await _service.DeleteAsync(id).ConfigureAwait(false);

var result = new StatusMessage

{

@@ -22,6 +22,7 @@ namespace Piranha.Manager.Controllers

[Route(“manager/api/config”)]

[Authorize(Policy = Permission.Admin)]

[ApiController]

[AutoValidateAntiforgeryToken]

public class ConfigApiController : Controller

{

private readonly ConfigService _service;

@@ -25,6 +25,7 @@ namespace Piranha.Manager.Controllers

[Route(“manager/api/content”)]

[Authorize(Policy = Permission.Admin)]

[ApiController]

[AutoValidateAntiforgeryToken]

public class ContentApiController : Controller

{

private readonly IApi _api;

@@ -228,10 +229,10 @@ public async Task<ContentEditModel> Save(ContentEditModel model)

/// </summary>

/// <param name="id">The unique id</param>

/// <returns>The result of the operation</returns>

[Route(“delete/{id}”)]

[HttpGet]

[Route(“delete”)]

[HttpDelete]

[Authorize(Policy = Permission.ContentDelete)]

public async Task<StatusMessage> Delete(Guid id)

public async Task<StatusMessage> Delete([FromBody]Guid id)

{

try

{

@@ -14,7 +14,6 @@

using Microsoft.AspNetCore.Mvc;

using Piranha.Manager.Models;

using Piranha.Manager.Services;

using Piranha.Models;

namespace Piranha.Manager.Controllers

{

@@ -25,6 +24,7 @@ namespace Piranha.Manager.Controllers

[Route(“manager/api/language”)]

[Authorize(Policy = Permission.Admin)]

[ApiController]

[AutoValidateAntiforgeryToken]

public class LanguageApiController : Controller

{

private readonly LanguageService _service;

@@ -56,16 +56,62 @@ public async Task<LanguageEditModel> Get()

/// <param name="model">The model</param>

[Route(“”)]

[HttpPost]

public async Task<LanguageEditModel> Save(LanguageEditModel model)

public async Task<IActionResult> Save(LanguageEditModel model)

{

return await _service.Save(model);

try

{

var result = await _service.Save(model);

result.Status = new StatusMessage

{

Type = StatusMessage.Success,

Body = _localizer.Language[“The language was successfully saved”]

};

return Ok(result);

}

catch (Exception e)

{

var result = new LanguageEditModel();

result.Status = new StatusMessage

{

Type = StatusMessage.Error,

Body = e.Message

};

return BadRequest(result);

}

}

/// <summary>

/// Deletes the language with the given id.

/// </summary>

/// <param name="id">The unique id</param>

[Route(“{id}”)]

[HttpDelete]

public async Task<LanguageEditModel> Delete(Guid id)

public async Task<IActionResult> Delete(Guid id)

{

return await _service.Delete(id);

try

{

var result = await _service.Delete(id);

result.Status = new StatusMessage

{

Type = StatusMessage.Success,

Body = _localizer.Language[“The language was successfully deleted”]

};

return Ok(result);

}

catch (Exception e)

{

var result = new LanguageEditModel();

result.Status = new StatusMessage

{

Type = StatusMessage.Error,

Body = e.Message

};

return BadRequest(result);

}

}

}

}

@@ -28,6 +28,7 @@ namespace Piranha.Manager.Controllers

[Route(“manager/api/media”)]

[Authorize(Policy = Permission.Admin)]

[ApiController]

[AutoValidateAntiforgeryToken]

public class MediaApiController : Controller

{

private readonly MediaService _service;

@@ -153,10 +154,10 @@ public async Task<IActionResult> SaveFolder(MediaFolderModel model, MediaType? f

}

}

[Route(“folder/delete/{id:Guid}”)]

[HttpGet]

[Route(“folder/delete”)]

[HttpDelete]

[Authorize(Policy = Permission.MediaDeleteFolder)]

public async Task<IActionResult> DeleteFolder(Guid id)

public async Task<IActionResult> DeleteFolder([FromBody]Guid id)

{

try

{

@@ -299,7 +300,7 @@ public async Task<IActionResult> Move([FromBody] IEnumerable<Guid> items, Guid?

}

[Route(“delete”)]

[HttpPost]

[HttpDelete]

[Consumes(“application/json”)]

[Authorize(Policy = Permission.MediaDelete)]

public async Task<IActionResult> Delete([FromBody] IEnumerable<Guid> items)

@@ -23,6 +23,7 @@ namespace Piranha.Manager.Controllers

[Route(“manager/api/module”)]

[Authorize(Policy = Permission.Admin)]

[ApiController]

[AutoValidateAntiforgeryToken]

public class ModuleApiController : Controller

{

private readonly ModuleService _service;

CVE: Latest News

CVE-2023-50976: Transactions API Authorization by oleiman · Pull Request #14969 · redpanda-data/redpanda
CVE-2023-6905
CVE-2023-6903
CVE-2023-6904
CVE-2023-3907