--inplace follows symlinks when updating
GHSA-c3cc-5w96-mw9r · CVE assignment pending
Summary
When writing with -i / --inplace, yq resolves and follows symlinks instead of replacing them. A symlink planted in the tree being processed (e.g. in a PR, a shared config repo, or a downloaded project) transfers the in-place write to any file reachable by the yq user — the victim file gets overwritten with the attacker-controlled processed document. This diverges from the ecosystem default (sed -i replaces the symlink), is not opt-out, and is not documented anywhere.
Impact
- Clobber via planted symlink — overwrite of operator files with attacker-controlled content at any path reachable by the yq user.
- Attack vector: local — a malicious
.yml/.yamlsymlink inside a processed repository or config tree; no privilege boundary crossed (the write runs with the yq user's own privileges, but the file choice and content are attacker-controlled). - Escalation: overwriting dotfiles, scripts, or workflow definitions (
~/.bashrc,~/.ssh/authorized_keys,.github/workflows/*.yml, crontab) leads to code execution on the next run; critical files can also be corrupted.
Root Cause
pkg/yqlib/file_utils.go:10-17 (tryRenameFile): the symlink branch is intentional — when writing in-place, yq resolves and follows the symlink, writing the processed content through it. There is no opt-out flag, no warning in the flag help, and no documentation of the risk.
// pkg/yqlib/file_utils.go:10-17 — tryRenameFile
// Symlink branch: yq follows the link instead of replacing it
// (sed -i replaces the symlink; yq writes through it)
Behavioral divergence: sed -i replaces the symlink with a new regular file (breaking the link, protecting the victim file); sponge and ex follow symlinks like yq — but those tools' behaviors are known and documented, whereas yq neither documents nor guards its choice. The advisory also lists CWE-22 and CWE-73 (external control of file name or path) as related weaknesses.
Proof of Concept
Tested against yq v4.54.1 (commit 504fc38780cc46be8444ea1b72fb55919fc0bfb0), binary built from the tagged source. 3 screenshots attached to the advisory.
# Clobber through a symlink (working dir /tmp/yq-vn044/)
$ ls -la
[lrwxrwxrwx link.yml -> victim.txt
-rw-rw-r-- victim.txt]
$ cat victim.txt
[app: production
key: SECRET_VALUE]
$ ./yq -i '.key = "PWNED_BY_YQ"' link.yml
[exit 0]
$ ls -la
[lrwxrwxrwx link.yml -> victim.txt] ← SYMLINK STILL A SYMLINK (yq followed it)
$ cat victim.txt
[app: production
key: PWNED_BY_YQ] ← REAL FILE OVERWRITTEN THROUGH THE SYMLINK
# Planted symlink pointing outside the processed tree
$ ln -s ../outside2/important.yml repo/ci-config.yml
$ ./yq -i '.db_password = "PWNED"' repo/ci-config.yml
[exit 0]
$ cat outside2/important.yml
[db_password: PWNED] ← OVERWRITTEN OUTSIDE THE REPO TREE
# Contrast with sed -i (ecosystem default: replace, don't follow)
$ sed -i 's/SECRET_VALUE/PWNED_BY_SED/' link2.yml
$ ls -la link2.yml victim2.txt
[-rw-rw-r-- link2.yml] ← SYMLINK DESTROYED, REPLACED BY A REGULAR FILE
$ cat victim2.txt
[app: production
key: SECRET_VALUE] ← VICTIM INTACT
Disclosure Timeline
| Date | Event |
|---|---|
| 2026-10-23 | Reported as Issue B of the combined advisory GHSA-c3cc-5w96-mw9r (together with the --split-exp issue); accepted by the maintainer the same day |
| 2026-11-05 | Issue A moved to the standalone GHSA-9fvq-9qj8-6qhx; this advisory scoped to --inplace only |
| 2026-11-09 | Advisory published; fix released in yq v4.54.3 |
| Pending | CVE assignment (request due now that a fix is public) |
Fix
yq v4.54.3 replaces symlinks instead of writing through them:
- Fixed `--inplace` to reject direct updates to read-only files and replace
symbolic links instead of modifying their targets. (#2973)
The advisory also suggests a default-safe design (replace symlinks as sed -i does) or an explicit --preserve-symlinks opt-in flag with a warning in the help and docs. Workaround on vulnerable versions: do not run yq -i over trees containing untrusted symlinks (process a copy, or pre-resolve/remove symlinks).
Fixed in yq v4.54.3 (2026-11-09). Advisory published 2026-11-09; CVE assignment pending.