commit be7ee69398cc6aef5989dcf92340e7fc323517a6
parent d750f9f7aab43a912871d96eadb2ebd5254e09c4
Author: mtmn <miro@haravara.org>
Date: Mon, 1 Jun 2026 20:50:22 +0200
fix: replace `unsafeRegex` clauses
Diffstat:
3 files changed, 43 insertions(+), 11 deletions(-)
diff --git a/src/Cover.purs b/src/Cover.purs
@@ -13,13 +13,13 @@ import Control.Alt ((<|>))
import Config (UserConfig, s3ConfigFromUser)
import S3 (existsInS3, getS3Url, uploadToS3)
import Data.Array ((!!), find)
-import Data.Either (Either(..))
+import Data.Either (Either(..), fromRight')
import Data.Foldable (foldM)
import Data.Map as Map
import Data.Maybe (Maybe(..), fromMaybe)
import Data.String.CaseInsensitive (CaseInsensitiveString(..))
-import Data.String.Regex (replace, parseFlags)
-import Data.String.Regex.Unsafe (unsafeRegex)
+import Data.String.Regex (regex, replace, parseFlags)
+import Partial.Unsafe (unsafeCrashWith)
import Effect (Effect)
import Effect.Aff (Aff, forkAff, try)
import Effect.Class (liftEffect)
@@ -52,8 +52,8 @@ type CoverSource =
sanitizeKey :: String -> String
sanitizeKey = replace re1 "_" >>> replace re2 "_"
where
- re1 = unsafeRegex "[^a-z0-9.-]" (parseFlags "gi")
- re2 = unsafeRegex "_{2,}" (parseFlags "g")
+ re1 = fromRight' (\_ -> unsafeCrashWith "invalid regex re1") $ regex "[^a-z0-9.-]" (parseFlags "gi")
+ re2 = fromRight' (\_ -> unsafeCrashWith "invalid regex re2") $ regex "_{2,}" (parseFlags "g")
getQueryParam :: String -> URL -> Maybe String
getQueryParam key url = URLSearchParams.get key (URL.searchParams url)
diff --git a/src/Main.purs b/src/Main.purs
@@ -8,13 +8,13 @@ import Cosine (serveSimilar)
import Data.Argonaut (decodeJson, encodeJson, parseJson, stringify)
import Data.Array (find, length)
import Data.Array as Data.Array
-import Data.Either (Either(..))
+import Data.Either (Either(..), fromRight')
import Data.Foldable (for_, traverse_)
import Data.Int (fromString, toNumber)
import Data.Maybe (Maybe(..), fromMaybe)
import Data.String (Pattern(..), stripPrefix)
-import Data.String.Regex (replace, parseFlags)
-import Data.String.Regex.Unsafe (unsafeRegex)
+import Data.String.Regex (regex, replace, parseFlags)
+import Partial.Unsafe (unsafeCrashWith)
import Data.Traversable (traverse)
import Db (Connection, FilterField(..), backupDb, connect, getOrCreateToken, getTokenUser, getScrobbles, getStats, initDb, initReleaseMetadata, ping, upsertScrobble, withTransaction)
import Effect (Effect)
@@ -245,13 +245,16 @@ serveProxy corsOrigin db url res = do
void $ writeString w UTF8 responseBody
end w
+sanitizeDate :: String -> String
+sanitizeDate = replace (fromRight' (\_ -> unsafeCrashWith "invalid regex") $ regex "[^0-9\\-]" (parseFlags "g")) ""
+
serveStats :: Connection -> URL -> Response -> Effect Unit
serveStats db url res = do
setHeader "Content-Type" "application/json" (toOutgoingMessage res)
launchAff_ do
let period = getQueryParam "period" url
let section = getQueryParam "section" url
- let safeDate = map (replace (unsafeRegex "[^0-9\\-]" (parseFlags "g")) "")
+ let safeDate = map sanitizeDate
let mFrom = safeDate (getQueryParam "from" url)
let mTo = safeDate (getQueryParam "to" url)
stats <- getStats db period mFrom mTo section
diff --git a/test/Main.purs b/test/Main.purs
@@ -5,18 +5,19 @@ import Prelude
import Unsafe.Coerce (unsafeCoerce)
import Data.Argonaut (decodeJson, encodeJson, parseJson)
import Data.Array (length)
-import Data.Either (Either(..))
+import Data.Either (Either(..), isRight)
import Data.Maybe (Maybe(..))
import Effect (Effect)
import Test.Spec (describe, it)
import Test.Spec.Assertions (shouldEqual, fail)
+import Data.String.Regex (regex, parseFlags)
import Test.Spec.Reporter.Console (consoleReporter)
import Test.Spec.Runner.Node (runSpecAndExitProcess)
import Types (Listen(..), ListenBrainzResponse(..), MbidMapping(..), Payload(..), Stats(..), StatsEntry(..), TrackMetadata(..), ListenBrainzSubmitPayload(..), ListenBrainzSubmitListen(..), ListenBrainzSubmitTrackMetadata(..), ListenBrainzAdditionalInfo(..))
import Db (FilterField(..), connect, initDb, checkExists, upsertScrobble, getScrobbles, initReleaseMetadata, upsertReleaseMetadata, getStats, dbBaseName, getOldestTs, getUnenrichedMbids, getEmptyGenreMbids, getArtistReleasesByMbids, touchGenreCheckedAt, getOrCreateToken, getTokenUser)
import Data.Argonaut.Core (Json)
import Foreign.Object as Object
-import Main (parseFilterField, submitListenToListen, findUserByToken)
+import Main (parseFilterField, submitListenToListen, findUserByToken, sanitizeDate)
import Cover (sanitizeKey)
import Sync (listenBrainzUrl, lastfmTrackToListen, parseLastfmResponse)
import S3 (getS3Url)
@@ -27,11 +28,27 @@ main = runSpecAndExitProcess [consoleReporter] do
it "should build ListenBrainz URLs correctly" do
listenBrainzUrl "user1" `shouldEqual` "https://api.listenbrainz.org/1/user/user1/listens"
+ it "regex patterns should compile successfully" do
+ let re1 = regex "[^a-z0-9.-]" (parseFlags "gi")
+ let re2 = regex "_{2,}" (parseFlags "g")
+ let re3 = regex "[^0-9\\-]" (parseFlags "g")
+ isRight re1 `shouldEqual` true
+ isRight re2 `shouldEqual` true
+ isRight re3 `shouldEqual` true
+
it "should sanitize S3 keys correctly" do
sanitizeKey "hello world!" `shouldEqual` "hello_world_"
sanitizeKey "T.est-123" `shouldEqual` "T.est-123"
sanitizeKey "multiple spaces" `shouldEqual` "multiple_spaces"
+ it "should sanitize S3 keys - edge cases" do
+ sanitizeKey "" `shouldEqual` ""
+ sanitizeKey "already-clean" `shouldEqual` "already-clean"
+ sanitizeKey "a@#b$c%d" `shouldEqual` "a_b_c_d"
+ sanitizeKey "___" `shouldEqual` "_"
+ sanitizeKey "a...b" `shouldEqual` "a...b"
+ sanitizeKey "UPPER lower" `shouldEqual` "UPPER_lower"
+
describe "parseFilterField" do
it "maps all valid field names" do
parseFilterField "artist" `shouldEqual` Just FilterArtist
@@ -44,6 +61,18 @@ main = runSpecAndExitProcess [consoleReporter] do
parseFilterField "unknown" `shouldEqual` Nothing
parseFilterField "" `shouldEqual` Nothing
+ describe "sanitizeDate" do
+ it "strips non-numeric characters except hyphens" do
+ sanitizeDate "2024-01-15" `shouldEqual` "2024-01-15"
+ sanitizeDate "2024/01/15" `shouldEqual` "20240115"
+ sanitizeDate "Jan 15, 2024" `shouldEqual` "152024"
+ sanitizeDate "2024-01-15T10:30:00" `shouldEqual` "2024-01-15103000"
+
+ it "handles edge cases" do
+ sanitizeDate "" `shouldEqual` ""
+ sanitizeDate "12345" `shouldEqual` "12345"
+ sanitizeDate "abc" `shouldEqual` ""
+
describe "ListenBrainz Submission" do
it "should decode a ListenBrainz submission payload" do
let jsonStr = """