[Bug] High: CLI -f follows symlinked output path and overwrites target file
Author: parasol-aserCreated Apr 19, 2026Updated Apr 19, 2026
Priority: High
Affected component: brotli(1) CLI output file creation in c/tools/brotli.c (OpenOutputFile())
Root cause:
OpenOutputFile() opens the output path with open(output_path, O_CREAT | (force ? 0 : O_EXCL) | O_WRONLY | O_TRUNC, ...). When -f / --force is used, O_EXCL is removed and the code still does not set O_NOFOLLOW. A pre-existing symlink at the output path is therefore followed, and the symlink target is truncated and rewritten with attacker-controlled decompressed content.
Exact reproduction steps:
- Use the current
masterHEAD that was verified locally (ab685df) and build the CLI (build-asan/brotliwas used for verification). - Run the deterministic repro helper from the verification artifacts:
timeout 180s tests/regression/t02/repro_symlink_overwrite.sh build-asan/brotli- Observe the success output:
T-02 OK: 21-byte seed overwrote symlink target with crafted plaintext- The helper creates
source.txt -> target.txt, then runsbrotli -d -f source.txt.br. After the run,target.txtcontainsATTACKER_CONTENT, showing that the CLI followed the symlink and overwrote the target file instead of refusing the path.
Relevant file paths:
c/tools/brotli.c:795-816(OpenOutputFile())- In particular
c/tools/brotli.c:803-804(output opened withoutO_NOFOLLOW;-fremovesO_EXCL) - Verification repro assets:
tests/regression/t02/repro_symlink_overwrite.sh,tests/regression/t02/source.txt.br,tests/regression/t02/source.txt,tests/regression/t02/target.txt
Suggested fix direction:
- Add
O_NOFOLLOWto the outputopen()flags unconditionally. - Keep
-fscoped to overwriting an existing regular file; it should not implicitly allow following symlinks. - If
open()fails withELOOP, emit a clear diagnostic so the refusal is obvious to users. - On platforms without
O_NOFOLLOW, use a final-component symlink check before opening as defense in depth.
Source: google/brotli