feat(13-06): content adapter — ContentItem to Film/Song/Podcast, close the streamUrl JWT leak
- archyContentAdapter.ts: hand-written adaptContentItems mapping (D-12), fixture-pinned at the adjacency, empty, ordering and paid-lock edges named in AIUI-03; classifyByMime covers the m4a/aac/opus/wma extension gap ShareModal.vue's mime map leaves today; buildMediaUrl never puts a credential in a query string (T-13-32). - filebrowser-client.ts: streamUrl now returns a query-free same-origin raw-file URL, relying on the path=/ cookie login() already sets instead of also putting the JWT in the URL (T-13-39 — closes the pre-existing leak CONTEXT.md names, rather than merely not repeating it). - filebrowserStreamUrl.test.ts: regression pin for the fix, including a traversal case confirming sanitizePath behavior is unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
6520cffc95
commit
f7691fd1bb
@@ -165,15 +165,25 @@ class FileBrowserClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a direct streaming URL with auth token in query string.
|
||||
* Use for video/audio <src> where browser needs to stream (range requests).
|
||||
* The token is a short-lived JWT so exposure in URL is acceptable.
|
||||
* Get a direct streaming URL for video/audio `<src>` where the browser
|
||||
* needs to make Range requests.
|
||||
*
|
||||
* Carries NO credential in the query string (T-13-39, fixed 2026-08-03 —
|
||||
* this was "the known leak to fix rather than propagate", per
|
||||
* 13-CONTEXT.md). `login()` already sets the filebrowser JWT as a
|
||||
* `path=/` cookie on this page's own origin, `baseUrl` is that same
|
||||
* origin, and the browser attaches the cookie to the same-origin media
|
||||
* subresource request automatically — the same mechanism filebrowser's
|
||||
* own web UI relies on. Putting the token in the URL too was redundant,
|
||||
* and it reached browser history, `Referer` headers and any access log on
|
||||
* the path. The cookie itself is unchanged by this fix: it is still a
|
||||
* 24-hour JWT, now confined to the cookie jar rather than also appearing
|
||||
* in the URL.
|
||||
*/
|
||||
async streamUrl(path: string): Promise<string> {
|
||||
await this.ensureAuth()
|
||||
const token = this.getAuthCookie()
|
||||
const safePath = sanitizePath(path)
|
||||
return `${this.baseUrl}/api/raw${safePath}?auth=${token}`
|
||||
return `${this.baseUrl}/api/raw${safePath}`
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user