DOKIMASecurity review report

Overview

Subtitle conversion endpoint fully parses and buffers files in memory with no pre-read size cap (resource-exhaustion DoS)

low Possibly Valid medium confidence

DOK-100012-FILEBROWSER-SUBTITLE-CONVERSIO-SUBTITLE-ENDPOINT · filebrowser · subtitle-conversion

Status

Possibly Valid

Missing size cap before an in-memory parse+convert is confirmed; the DoS magnitude hinges on astisub's memory behavior (dependency behavior for crafted input) which cannot be settled from this repo, so possibly_valid.

Repository / Component

Repository
filebrowser
Component
subtitle-conversion

Plain-English Description

When someone requests a subtitle file, the server reads the whole file into memory and builds the converted output entirely in memory before sending anything, with no limit on file size. Large or crafted subtitle files, requested repeatedly, can drive up memory use and strain the server.

Description of the Underlying Issue

subtitleFileHandler (http/subtitle.go:37-80) opens the target subtitle file (http/subtitle.go:43-47) with no prior size check. For .srt/.ass/.ssa inputs it calls astisub.ReadFromSRT / ReadFromSSA (http/subtitle.go:51-55), which parse the entire file into an in-memory astisub.Subtitles structure. The converted WebVTT output is then written into an in-memory bytes.Buffer (http/subtitle.go:73-78) and served via bytes.NewReader before any streaming occurs. There is no stat-based cap, http.MaxBytesReader, or io.LimitReader guarding this path, unlike detectType elsewhere which caps text handling at 10MB. Compounding this, the HTTP server is configured with only ReadHeaderTimeout and no Read/Write/Idle timeouts (cmd/root.go:246-249), so slow or large in-memory conversions are not bounded by a server-side deadline. Each request therefore holds both the fully-parsed subtitle model and the fully-buffered output resident in memory simultaneously.

Potential Attack

A user with Perm.Download uploads (into their own storage) a large or structurally crafted .srt/.ass/.ssa file, then issues many concurrent GET /api/subtitle/<path> requests. Each request opens the file, parses it fully into memory via astisub, and buffers the entire WebVTT conversion in memory before responding. With no size cap and no request timeouts, concurrent requests multiply the per-request memory footprint.

Outcomes of Potential Attack

Concurrent conversions of large/crafted subtitle files inflate server memory consumption and CPU for parsing/conversion, degrading responsiveness for other users and potentially reaching out-of-memory conditions on constrained hosts. The outcome is availability degradation / resource exhaustion rather than data disclosure or code execution.

Affected Scope

subtitleFileHandler (http/subtitle.go:43-78)

Suggested Fix (plain english)

Check the subtitle file's size before opening and parsing it, and reject or refuse to convert files above a sane maximum.

Suggested Fix (detailed)

In subtitleFileHandler, obtain the file size (via the already-available files.FileInfo / an Fs stat) and reject files above a conservative maximum before calling file.Fs.Open and astisub.ReadFrom* (http/subtitle.go:43-55). Alternatively wrap the reader in an io.LimitReader sized to that maximum so parsing cannot consume unbounded memory. Consider streaming or bounding the WebVTT output rather than buffering the whole document (http/subtitle.go:73-78). Independently, set ReadTimeout/WriteTimeout/IdleTimeout on the http.Server (cmd/root.go:246-249) so no single conversion can hold resources indefinitely. Verify by requesting an oversized subtitle and confirming it is rejected before parsing, and that memory stays bounded under concurrent requests. Enforce a maximum file-size guard (stat/LimitReader) before opening/parsing in subtitleFileHandler.

Validation

Missing size cap before an in-memory parse+convert is confirmed; the DoS magnitude hinges on astisub's memory behavior (dependency behavior for crafted input) which cannot be settled from this repo, so possibly_valid.

first_opinion

Full Evidence

http/subtitle.go:43-47 file opened with no size check
http/subtitle.go:51-55 astisub.ReadFromSRT/ReadFromSSA parses the entire file into memory
http/subtitle.go:73-78 full WebVTT output buffered in an in-memory bytes.Buffer before serving
cmd/root.go:246-249 http.Server sets only ReadHeaderTimeout; no Read/Write/Idle timeouts
grep of http/ shows no MaxBytesReader/LimitReader guarding this path
Proven fact: There is no pre-read size cap before the subtitle file is fully parsed and converted in memory, unlike detectType which caps text handling at 10MB
Unvalidated fact: The real memory-amplification factor for crafted SRT/SSA in astisub is not determinable from filebrowser's source alone
Unvalidated fact: Reaching a meaningful DoS requires Perm.Download, an attacker-uploaded large/crafted subtitle (own storage), and concurrent requests
http/subtitle.go:43-47 — file.Fs.Open(file.Path) with no preceding size/stat check
http/subtitle.go:51-55 — astisub.ReadFromSRT / ReadFromSSA parse the entire file into an in-memory Subtitles structure
http/subtitle.go:73-78 — full WebVTT output written to an in-memory bytes.Buffer, then served via bytes.NewReader
cmd/root.go:246-249 — http.Server sets only ReadHeaderTimeout; no ReadTimeout/WriteTimeout/IdleTimeout
grep of http/ shows no MaxBytesReader/LimitReader guarding the subtitle path, unlike detectType's 10MB cap
Proven fact: No pre-read size cap exists before the subtitle file is fully parsed and converted in memory in subtitleFileHandler.
Proven fact: Both the parsed subtitle model and the buffered WebVTT output are held fully in memory per request before any bytes are served.
Proven fact: The HTTP server sets only ReadHeaderTimeout, leaving read/write/idle durations unbounded.
Unvalidated fact: The real memory-amplification factor for crafted SRT/SSA input is a property of the astisub dependency parser and cannot be determined from filebrowser's source alone.
Unvalidated fact: Reaching a meaningful DoS requires Perm.Download, an attacker-supplied large/crafted subtitle in reachable storage, and sufficient request concurrency relative to host memory.
http/subtitle.go:50-55 astisub parses entire file into memory
http/subtitle.go:73-78 full WebVTT output buffered in-memory before serving