From e7cebc32a4b6c7e6fc0d1f6b87688b361163f307 Mon Sep 17 00:00:00 2001
From: eee4 <41441600+eee4@users.noreply.github.com>
Date: Tue, 22 Jul 2025 13:09:13 +0200
Subject: [PATCH] feat: naive sanity check for image URLs
---
Controllers/QuoteController.cs | 34 +++++++++++++++++++++++++++++++---
1 file changed, 31 insertions(+), 3 deletions(-)
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();