This PR is a focused security and quality pass. No new features — just making things safer, more reliable, and easier to maintain.
Fixed
- Log injection across the board — Anything user-controlled (file paths, search titles, download IDs, ASINs, URLs) now goes through
LogRedactionbefore hitting the logger. Prevents newline injection attacks that could corrupt or spoof structured log output. - SSRF in NZB and torrent downloaders —
NzbgetAdapterandTorrentFileDownloadernow validate outbound URLs viaOutboundRequestSecurity.TryValidateExternalHttpUrlbefore making any request. Every redirect hop is checked too, not just the initial URL. Private/LAN hosts are permitted since user-configured indexers typically run on Docker networks. - SQL injection in debug schema endpoints — Column and table names in raw
PRAGMA/SELECTcalls inLibraryControllerare now double-quoted and validated against a whitelist. Low risk in practice (internal endpoints), but still worth fixing. - Race condition in
LoginRateLimiter—BlockUntiland the failure counter are now protected by a per-entry lock. Previously two concurrent login attempts could both slip through the rate limit window at the same time. - Raw session token in
ClaimsPrincipal—SessionServicewas emitting the actual session token as a claim, which could leak through middleware or claim-inspection APIs. Removed. - Column injection in
StartupDbNormalizer— Column names resolved at startup are now whitelisted + regex-validated before being dropped intoALTER TABLESQL. - Bulk audiobook update wasn't transactional —
BulkUpdateAudiobookslooped over audiobooks and saved after each one, so a failure halfway through left a partial update in the database. It's now wrapped in a single transaction; everything commits or nothing does. FileMovercould delete the original before confirming the copy — Hardlinks and copies now go to a temp file f...
Automated canary build