Skip to content

render.Writer.Write chmods through a symlinked destination before the symlink policy is consulted #248

Description

@spxrogers

With AGENTSYNC_ALLOW_SYMLINK_DEST unset, apply is documented as refusing to write through a symlinked destination. The refusal lives in iox.AtomicWrite (resolveSymlinkDest), which render.Writer.Write reaches only when the content differs. Before that, Write's convergence arm handles the content-identical case itself: it reads through the link, and when op.Mode != 0 and the permission bits differ it calls os.Chmod(op.Path, want). os.Chmod follows symlinks, so the TARGET's mode is rewritten through a link the user never opted into.

Measured on 7f2d4ad (probe in a throwaway clone): link → regular file with identical content, target 0600, op.Mode 0644, env unset → Write returns nil, the link is preserved, the target is now 0644. No ErrSymlinkDest.

Where. internal/render/writer.go, the convergence branch (os.ReadFile(op.Path) then the op.Mode != 0 chmod) runs before AtomicWrite; resolveSymlinkDest is never consulted on that path.

Why it matters now. #247 makes every read-side surface obey the switch, and reconcile's [o]verride re-applies through Writer.Write. [o] is therefore withheld on the symlink refusal arm in #247, and the SECURITY/architecture prose there names this issue as the one write path that still follows a link with the switch unset. The same convergence read is also the unguarded read behind #241 (a FIFO at a rendered destination hangs apply), so a fix that routes the convergence arm through the same shape and symlink gates would close both.

Expected. With the switch unset, Write refuses a symlinked destination on the mode arm exactly as AtomicWrite refuses it on the content arm (or skips the chmod and reports it), so "refuses to write through a link" is true of every write.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions