Refuse an archive or a module directory that leads out of the repository through a link in the tree, so a repository cannot make the build pack files it does not hold (issue 1609)

This commit is contained in:
2026-10-21 09:22:14 +00:00
parent 0205b8f708
commit fcca6ba9e0
2 changed files with 146 additions and 1 deletions
+42 -1
View File
@@ -943,6 +943,23 @@ func inside(tree, path string) (string, error) {
return "", fmt.Errorf(
"%q leaves the repository, and a build reads only its own tree", path)
}
// A link in the fetched tree must not lead the module out of it either (issue 1609): compare where
// both really are. A module directory that does not exist is left to the read of its manifest.
realTree, err := filepath.EvalSymlinks(tree)
if err != nil {
return "", err
}
realWithin, err := filepath.EvalSymlinks(within)
if errors.Is(err, os.ErrNotExist) {
return within, nil
}
if err != nil {
return "", err
}
if rel, err := filepath.Rel(realTree, realWithin); err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
return "", fmt.Errorf("%q leads out of the repository through a link: it resolves to %s, and the repository is %s",
path, realWithin, realTree)
}
return within, nil
}
@@ -1326,7 +1343,10 @@ func one(ctx context.Context, run Runner, publish Publisher,
}
} else {
src.ownHas(cleanEntry(a.From) + "/**")
body, err = pack(filepath.Join(tree, a.From))
var root string
if root, err = archiveRoot(tree, a); err == nil {
body, err = pack(root)
}
}
if err != nil {
return catalogue.Built{}, fmt.Errorf("%s: packing %s failed: %w", module, a.Name, err)
@@ -1343,6 +1363,27 @@ func one(ctx context.Context, run Runner, publish Publisher,
module, a.Name, a.Kind)
}
// archiveRoot is the directory an archive artifact is packed from, refused when a link in the fetched
// tree leads out of the module. The repository is somebody else's: pack follows a link in a parent
// component of `from`, so without this a committed link makes the build pack, register and serve any
// directory the build account can read (issue 1609). Both are compared where they really are.
func archiveRoot(tree string, a catalogue.Artifact) (string, error) {
root := filepath.Join(tree, a.From)
realTree, err := filepath.EvalSymlinks(tree)
if err != nil {
return "", err
}
realRoot, err := filepath.EvalSymlinks(root)
if err != nil {
return "", err
}
if rel, err := filepath.Rel(realTree, realRoot); err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
return "", fmt.Errorf("the archive %s is taken from %q, which lies outside the module: it resolves to %s, and the module is %s",
a.Name, a.From, realRoot, realTree)
}
return root, nil
}
// pack tars and gzips a directory.
//
// **Deterministically**: entries sorted, and no timestamps, uid, gid or original names carried