# Symlink-following privileged file operations

Privileged read/write/delete paths that follow symlinks or skip containment before acting

_Area: Web access control and IDOR. Seen in 2 disclosed reports; latest 2026-07-20. Updated 2026-10-30._

## How it works
A privileged operation (root cron, upload handler, renderer) builds a file path from user-influenced input and acts on it without resolving or rejecting symlinks, or without re-checking containment. The assumption that breaks: that a normalized or "scoped" path string still points where the caller intended. A symlink, or a path that failed an earlier containment check, redirects the operation to a file the actor should not touch.

## Where it shows up
- Path built by string concatenation into a user-owned directory, then passed to a helper that only normalizes slashes [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-mq5v-pxpm-8jw2.md)
- Containment wrapper where some methods call a guard and others (Remove/RemoveAll) do not [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-fmm7-x4gx-8jhr.md)
- Error-handling fall-through: a path that failed a containment check still reaches a cleanup/delete call [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-fmm7-x4gx-8jhr.md)
- Config attributes "locked" by a framework but writable through alternate code paths (counters, substitutions, includes) [E1](https://bugbountylibrary.com/r/h1%3A1098793.md)
- Privileged append/chown/write into `~/.ssh` or similar user-controlled locations [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-mq5v-pxpm-8jw2.md)

## How to review
The invariant: every privileged file operation must act on a path whose real, resolved target is inside the intended boundary, and the check must be enforced at the operation itself, not only earlier. Concretely: does the sanitizer call realpath/lstat and reject symlinks, or only normalize the string [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-mq5v-pxpm-8jw2.md)? Does every method of the containment wrapper invoke the guard [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-fmm7-x4gx-8jhr.md)? Can any error path reach a delete/write without passing the guard [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-fmm7-x4gx-8jhr.md)? Are locked attributes truly immutable across all writers [E1](https://bugbountylibrary.com/r/h1%3A1098793.md)?

## Variants seen
- Root cron appends SSH keys through a customer symlink into `/root/.ssh/authorized_keys` [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-mq5v-pxpm-8jw2.md)
- Create-only user deletes out-of-scope files via unguarded RemoveAll following a symlinked ancestor [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-fmm7-x4gx-8jhr.md)
- Renderer reads/writes arbitrary files because a locked attribute was overridden via a counter directive [E1](https://bugbountylibrary.com/r/h1%3A1098793.md)

## Why fixes fail
E2 is an incomplete fix of an earlier CVE: the containment wrapper was added but Remove/RemoveAll were left without the guard, and the upload handler's failure-cleanup still reached RemoveAll. Fixes that guard one entry point but not every method or fall-through path leave the hole open.

## Impact and severity
Root SSH access [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-mq5v-pxpm-8jw2.md), out-of-scope file deletion by a low-privilege user [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-fmm7-x4gx-8jhr.md), arbitrary file read/write [E1](https://bugbountylibrary.com/r/h1%3A1098793.md). Severity rises when the operation runs as root or the actor holds only a narrow permission (Create-only), since the boundary crossed is large relative to the granted role.

## Evidence
- E1: https://bugbountylibrary.com/r/h1%3A1098793.md
- E2: https://bugbountylibrary.com/r/ghsa%3AGHSA-fmm7-x4gx-8jhr.md
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-mq5v-pxpm-8jw2.md
