0ae798d24f
* fix(installer): confine tarball extraction with os.Root Lexical containment of tar entry names is not enough on its own: an archive can ship a symlink whose target passes the check (`a` -> `.` resolves to destDir itself) and then write through it with a later entry (`a/x` -> `..`) that really lands outside destDir. Confirmed by extracting a crafted tarball — it wrote a file into destDir's parent. Anchor extraction at an os.Root opened on destDir so the OS-level path walk refuses to follow a link out of the tree, and keep the lexical checks for clear up-front errors. Adds a regression test for the two-hop symlink chain. Fixes CodeQL alert #19 (go/zipslip). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(installer): reject archive symlinks under symlinked parents Root.Symlink follows symlinked parent components, so an entry named "a/x" is created at "x" when "a" is a symlink to ".". The link still lands inside destDir, but one level shallower than its archive path — which changes what a relative target means. "a/x" -> ".." passes a check computed against parent "a" and then resolves to destDir's parent from its real home, leaving an escaping symlink in the install tree. Nothing can be written through it during extraction (os.Root refuses to follow it), but it persists for any later consumer that walks the tree with ordinary os calls. Require every parent component of a symlink entry to be a real directory, so an entry's archive path and its on-disk location agree — the assumption the existing target check already depended on. Reported by the Codex reviewer on #1934. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Kandev Agent <agent@kandev.dev> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>