@@ -13,6 +13,16 @@ import (
1313 "strings"
1414)
1515
16+ // isPathWithinBase checks if absPath (absolute, resolved) is under baseAbs (absolute, resolved)
17+ func isPathWithinBase (baseAbs , absPath string ) bool {
18+ rel , err := filepath .Rel (baseAbs , absPath )
19+ if err != nil {
20+ return false
21+ }
22+ relClean := filepath .Clean (rel )
23+ return relClean != ".." && ! strings .HasPrefix (relClean , ".." + string (filepath .Separator ))
24+ }
25+
1626// Policy defines extraction limits and behavior.
1727type Policy struct {
1828 MaxFiles int // maximum number of files allowed
@@ -181,6 +191,28 @@ func Unzip(srcZip, destDir string, p Policy) (err error) {
181191 // Normalize a bit (keep relative)
182192 // If symlink target escapes on resolution at runtime, parent check above still blocks via EvalSymlinks
183193 _ = os .Remove (targetAbs ) // best-effort replace
194+
195+ // -- Fix: Check resolved destination and symlink target before creating symlink --
196+ // 1. Resolve parent directory's symlinks (already extracted so far).
197+ parentResolved , err := filepath .EvalSymlinks (filepath .Dir (targetAbs ))
198+ if err != nil {
199+ if ! os .IsNotExist (err ) {
200+ return fmt .Errorf ("symlink parent resolve error: %w" , err )
201+ }
202+ // If parent doesn't exist, mkdirall above does it, so we fallback to intendeed parent
203+ parentResolved = filepath .Dir (targetAbs )
204+ }
205+ linkAbs := filepath .Join (parentResolved , filepath .Base (targetAbs ))
206+ if ! isPathWithinBase (destAbs , linkAbs ) {
207+ return fmt .Errorf ("symlink destination escapes extraction root: %q" , linkAbs )
208+ }
209+ // 2. Where would the symlink, if created, point to? (Relative to resolved parent.)
210+ targetCandidate := filepath .Join (parentResolved , linkTarget )
211+ // We can't EvalSymlinks on the new symlink yet, but check that the _synthetic resolution_ is within destAbs.
212+ if ! isPathWithinBase (destAbs , targetCandidate ) {
213+ return fmt .Errorf ("symlink target escapes extraction root: %q -> %q" , f .Name , linkTarget )
214+ }
215+
184216 if err := os .Symlink (linkTarget , targetAbs ); err != nil {
185217 return fmt .Errorf ("create symlink: %w" , err )
186218 }
0 commit comments