Skip to content

Reject NUL characters in scrobble imports with a 400 - #215

Merged
rowkav09 merged 1 commit into
mainfrom
reject-nul-in-scrobble-import
Oct 2, 2026
Merged

rowkav09 merged 1 commit into
mainfrom
reject-nul-in-scrobble-import

Conversation

@rowkav09

@rowkav09 rowkav09 commented Oct 2, 2026

Copy link
Copy Markdown
Member

A scrobble whose artistName, trackTitle or albumTitle contained a NUL character passed validation and then failed inside Postgres (text cannot hold 0x00), so the API answered 500. Login already guards this for the username. The scrobble schema now refuses NUL, so the request gets the existing 400 INVALID_REQUEST before any query runs. Test: three cases (one per field) answer 500 on main and 400 with the change, and assert the database is never called. Behaviour change: an invalid payload now returns 400 instead of 500.

@codecov

codecov Bot commented Oct 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@rowkav09

rowkav09 commented Oct 2, 2026

Copy link
Copy Markdown
Member Author

Reviewed head a641db7 (on main ac48ed1). Approved.

All 54 test files and 363 tests pass. The three new NUL tests (artistName, trackTitle, albumTitle) fail against main's contracts schema. I sent 12 payloads through the real route, with a mocked database:

  • NUL in artistName, trackTitle or albumTitle, a NUL-only name, a NUL padded with spaces, and a NUL in the second of two items. On main these reached the database. Here they return 400 INVALID_REQUEST with no query run.
  • A raw JSON body that carries the NUL as a unicode escape sequence is also a 400 with no query run. I did not capture its error code.
  • Valid payloads are unchanged: with and without an album, an empty album, unicode, other control characters and a lone surrogate. They pass validation and reach the import.

The refine is on the shared ScrobbleItemSchema, whose only use is this route. .trim() does not strip NUL, so the check sees it in padded input too.

Non-blocking: the 400 body is the route's generic "Provide a valid list of scrobbles" message, so the caller isn't told which field had the NUL.

CI at the time I checked: all nine checks completed successfully (worker, api, migrate and web container builds, TypeScript workspace, Compose config, CodeFactor, codecov/patch, ghostdeps).

@rowkav09
rowkav09 merged commit 8c5cdc8 into main Oct 2, 2026
9 checks passed
@rowkav09
rowkav09 deleted the reject-nul-in-scrobble-import branch October 2, 2026 12:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant