diff --git a/Controllers/QuoteController.cs b/Controllers/QuoteController.cs index 17d2e2b..89e4719 100644 --- a/Controllers/QuoteController.cs +++ b/Controllers/QuoteController.cs @@ -158,32 +158,47 @@ public class QuotesController : ControllerBase /// [AUTHED] Add a new quote /// /// Newly created quote's id + /// + /// Note: + /// User-provided image URLs are validated by checking + /// if they start with "https://", "http://" or "/". + /// This is rather a naive solution. + /// /// Form data containing required quote information /// Returned on valid request /// Returned when any of the categories does not exist /// Returned when user's id does not match the creator's id + /// Returned when image url is invalid (does not start with "https://", "http://", or "/") [HttpPost("new")] [Authorize] [EnableCors] [ProducesResponseType(201)] [ProducesResponseType(typeof(ErrorDTO), 400)] [ProducesResponseType(typeof(ErrorDTO), 403)] + [ProducesResponseType(typeof(ErrorDTO), 406)] public async Task CreateQuote([FromBody] CreateQuoteDTO request) { // Get user ID from claims - var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (userIdClaim == null || !int.TryParse(userIdClaim, out int userId)) // https://stackoverflow.com/a/47708867 return StatusCode(403, new ErrorDTO { Status = "error", Error_msg = "Invalid user ID" }); - // Find or create image + // Try to find the image inside the DB Image? image = null; if (!string.IsNullOrEmpty(request.ImageUrl)) { image = await _db.Images.FirstOrDefaultAsync(i => i.Url == request.ImageUrl); + + // Failed? Just insert it yourself if (image == null) { + // Simple (naive) sanity check for image URLs + if ( !request.ImageUrl.StartsWith("http://") + && !request.ImageUrl.StartsWith("https://") + && !request.ImageUrl.StartsWith("/")) + return StatusCode(406, new ErrorDTO { Status = "error", Error_msg = "Image URLs should point to http/https url or a local resource" }); + image = new Image { Url = request.ImageUrl }; _db.Images.Add(image); await _db.SaveChangesAsync(); @@ -347,13 +362,19 @@ public class QuotesController : ControllerBase /// While "categories = null" will not alter the quote's categories, /// "categories = []" will (and in turn, empty each and every present category)!
/// Be careful when handling user-provided categories! + ///

+ /// Note: + /// User-provided image URLs are validated by checking + /// if they start with "https://", "http://" or "/". + /// This is rather a naive solution. /// /// Newly modified quote as a DTO /// Quote to be modified - /// Updated quote form data + /// Updated quote form data. Id is ignored. /// Returned on valid request /// Returned when request text or author is empty (or whitespace) /// Returned when no such quote exists + /// Returned when image url is invalid (does not start with "https://", "http://", or "/") [HttpPatch("{id}")] [Authorize] [EnableCors] @@ -385,9 +406,16 @@ public class QuotesController : ControllerBase if (!string.IsNullOrEmpty(updatedQuote.ImageUrl)) { image = await _db.Images.FirstOrDefaultAsync(i => i.Url == updatedQuote.ImageUrl); + // Failed? Just insert it yourself if (image == null) { + // Simple (naive) sanity check for image URLs + if ( !updatedQuote.ImageUrl.StartsWith("http://") + && !updatedQuote.ImageUrl.StartsWith("https://") + && !updatedQuote.ImageUrl.StartsWith("/")) + return StatusCode(406, new ErrorDTO { Status = "error", Error_msg = "Image URLs should point to http/https url or a local resource" }); + image = new Image { Url = updatedQuote.ImageUrl }; _db.Images.Add(image); await _db.SaveChangesAsync();