Repository navigation
fix: serve media/art for filenames containing a literal % - #126
Conversation
route-pattern already percent-decodes wildcard params before handlers run, so decoding them again with decodeURIComponent threw on filenames that contain a literal % (400 Invalid path encoding) and silently resolved names like %41.mp3 to A.mp3. Use the router-decoded param directly in the media, art, admin artwork/media/stream/analytics/metadata, and MCP widget routes, drop the now-unused decodePathParam helper, and replace the handler-level malformed-encoding tests with router-level coverage. Fixes #125 Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (16)
💤 Files with no reviewable changes (7)
Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughRoutes now pass URL path parameters directly to media-path parsers. New tests cover filenames with reserved characters, malformed encodings, and paths that would resolve only after double decoding. ChangesPath parameter handling
Priority: ➖ Normal Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Bug fix · Severity of issue fixed: Medium Merge Risk: ⚪ Minimal · up to The change fixes literal-percent filenames without introducing an identified path-handling regression. It is mergeable subject to normal checks. Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to The fix preserves public token and feed-access checks while preventing filenames from being interpreted twice. No newly weakened security control was identified. Protection of administrative endpoints remains unverified, and their existing filesystem resolver lacks an explicit root-containment check. Retained concerns Security review detailsSecurity Blast Radius
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
Fixes #125.
route-patternalready percent-decodes wildcard params before a handler runs. Eight path handlers then randecodeURIComponentagain throughdecodePathParam, which caused two problems:Episode #1 + 50%.mp3, encoded in the RSS feed as50%25, reached the handler as50%, so the second decode threw. The route returned400 Invalid path encoding, and the episode could not be played in podcast clients.%XXsequence were silently resolved to a different file. For example, a request for%41.mp3servedA.mp3.This PR removes the second decode from every route with a
*pathwildcard, not only the three named in the issue:/media/:token/*path/art/:token/*path/mcp/widget/:token/*path/admin/api/artwork/*path/admin/api/media/*path/admin/api/media/*path/metadata/admin/api/media-stream/*path/admin/api/media-analytics/*pathdecodePathParamis now unused and has been deleted.Malformed encodings such as
%E0%A4%Anever reach these handlers, because the router's strict canonicalization rejects them with a 404 first. The old handler-level "Invalid path encoding" tests fed raw, still-encoded params straight to the handlers, a state that can't happen in production. They are replaced with router-level tests.The MCP resource template in
app/mcp/resources.tsis intentionally left alone. The SDK'sUriTemplate.matchdoes not decode, so decoding once there is still correct.Test Plan
app/routes/path-param-decoding.test.tsdrives the realrouter.fetchwith URLs built the way the app builds them:encodeURIComponentas in the RSS media URLs,encodeRelativePathas in the MCP tools, andbuildItemPodcastArtUrlfor artwork. It covers:Episode #1 + 50%.mp3andSeason 100%/Episode %41 & 50%25.mp3served by/media, by/art(both legacy and.jpg-decorated URLs), and by admin artwork and media-stream.%41.mp3must not serveA.mp3.main, 5 of the 6 new tests fail with400(and one200where404is expected). On this branch all of them pass.npm run validatepasses on Node 24, with 271 tests.Checklist
Screenshots
N/A (server-side routing fix)
Summary by CodeRabbit
#, and+more consistently.