From fdf902319d6fbb5381a270e2ee77617630fd24e5 Mon Sep 17 00:00:00 2001 From: Taimoor Zaeem Date: Sun, 20 Apr 2025 21:57:07 +0500 Subject: [PATCH] fix: regression with parameter charset=utf-8 in mediatype --- CHANGELOG.md | 1 + src/PostgREST/MediaType.hs | 8 ++++---- test/spec/Feature/Query/CustomMediaSpec.hs | 21 +++++++++++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 358e4f41c..3bb2cf82a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - #3697, #3602, Handle queries on non-existing table gracefully - @taimoorzaeem - #3600, #3926, Improve JWT errors - @taimoorzaeem - #3013, Fix `order=` with POST, PATCH, PUT and DELETE requests - @taimoorzaeem + - #4030, Fix regression with parameter `charset=utf-8` in mediatype - @taimoorzaeem ### Changed diff --git a/src/PostgREST/MediaType.hs b/src/PostgREST/MediaType.hs index ba79c3a51..d0055f3bb 100644 --- a/src/PostgREST/MediaType.hs +++ b/src/PostgREST/MediaType.hs @@ -183,14 +183,14 @@ decodeMediaType mt = decodeMediaType' $ decodeLatin1 mt -- >>> P.parse tokenizeMediaType "" "application/vnd.pgrst.plan+text; for=\"text/xml\"; options=analyze|verbose|settings|buffers|wal" -- Right ("application","vnd.pgrst.plan+text",[("for","text/xml"),("options","analyze|verbose|settings|buffers|wal")]) +-- TODO: Improve mediatype parser as per RFC 2045 https://datatracker.ietf.org/doc/html/rfc2045#section-5.1 tokenizeMediaType :: P.Parser (Text, Text, [(Text, Text)]) tokenizeMediaType = do mainType <- P.many1 (P.alphaNum <|> P.oneOf ".*") P.char '/' subType <- P.many1 (P.alphaNum <|> P.oneOf ".*+-") params <- P.many pSemicolonSeparatedKeyVals - P.optional $ P.try $ P.spaces *> P.char ';' -- ending semicolon - P.eof + P.optional $ P.try $ P.spaces *> P.char ';' -- ending semicolon, discard input after that because it has already failed or we have hit EOF return (T.pack mainType, T.pack subType, params) where pSemicolonSeparatedKeyVals :: P.Parser (Text, Text) @@ -198,12 +198,12 @@ tokenizeMediaType = do where pKeyVal :: P.Parser (Text, Text) pKeyVal = do - key <- P.many1 P.alphaNum + key <- P.many1 (P.alphaNum <|> P.oneOf "-") P.spaces P.char '=' P.spaces val <- P.try pQuoted <|> P.try pUnQuoted return (T.pack key, T.pack val) where - pUnQuoted = P.many1 (P.alphaNum <|> P.oneOf "|") + pUnQuoted = P.many1 (P.alphaNum <|> P.oneOf "|-") pQuoted = P.char '\"' *> P.manyTill P.anyChar (P.char '\"') diff --git a/test/spec/Feature/Query/CustomMediaSpec.hs b/test/spec/Feature/Query/CustomMediaSpec.hs index 219047f28..1459c55ab 100644 --- a/test/spec/Feature/Query/CustomMediaSpec.hs +++ b/test/spec/Feature/Query/CustomMediaSpec.hs @@ -382,3 +382,24 @@ spec = describe "custom media types" $ do `shouldRespondWith` [json| {"code":"PGRST107","details":null,"hint":null,"message":"None of these media types are available: undefined"} |] { matchStatus = 406 } + + context "media type parser allowed characters" $ do + it "regression test allowing charset=utf-8" $ + request methodPost "/rpc/overloaded_default" + [("Content-Type", "application/json; charset=utf-8")] + [json|{"must_param":1}|] + `shouldRespondWith` + [json|{"val":1}|] + { matchStatus = 200 + , matchHeaders = ["Content-Type" <:> "application/json; charset=utf-8"] + } + + it "handle unrecognized parameters leniently" $ do + request methodPost "/rpc/overloaded_default" + [("Content-Type", "application/json; $$ unrecognized-chars=ignored $$")] + [json|{"must_param":1}|] + `shouldRespondWith` + [json|{"val":1}|] + { matchStatus = 200 + , matchHeaders = ["Content-Type" <:> "application/json; charset=utf-8"] + }