File size: 2,555 Bytes
d2507b5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | package compactscan
import (
"errors"
"io/fs"
"os"
"path/filepath"
)
// preflightMaxFiles mirrors the whole-bundle scanner's discovery bound. It
// counts SKILL.md as well as siblings, so an accepted tree always fits within
// the scanner without producing a partial-scan note.
const preflightMaxFiles = 4096
// preflightPackage validates the complete package tree without reading member
// content. In particular, this runs before bundle.Scan, whose analysis path is
// intentionally willing to inspect symlink targets. Compact scoring is
// stricter: no link or special file may be opened at all.
func preflightPackage(rootPath string) error {
rootInfo, err := os.Lstat(rootPath)
if err != nil {
return errors.New("inspect package root")
}
if rootInfo.Mode()&os.ModeSymlink != 0 || !rootInfo.IsDir() {
return errors.New("package root is not an unlinked directory")
}
root, err := os.OpenRoot(rootPath)
if err != nil {
return errors.New("open package root")
}
defer root.Close()
fileCount := 0
err = filepath.WalkDir(rootPath, func(path string, entry fs.DirEntry, walkErr error) error {
if walkErr != nil {
return errors.New("walk package tree")
}
if path == rootPath {
return nil
}
rel, err := filepath.Rel(rootPath, path)
if err != nil || rel == "." || filepath.IsAbs(rel) || hasTraversal(rel) {
return errors.New("package tree contains a non-canonical member")
}
info, err := root.Lstat(rel)
if err != nil {
return errors.New("package member changed during preflight")
}
if info.Mode()&os.ModeSymlink != 0 {
return errors.New("package tree contains a symlink")
}
if info.IsDir() {
return nil
}
if !info.Mode().IsRegular() {
return errors.New("package tree contains a non-regular file")
}
fileCount++
if fileCount > preflightMaxFiles {
return errors.New("package tree exceeds the file limit")
}
file, err := openPreflightFile(root, rel)
if err != nil {
return errors.New("open package member during preflight")
}
opened, statErr := file.Stat()
if statErr != nil {
_ = file.Close()
return errors.New("stat package member during preflight")
}
links, linkErr := openedHardLinkCount(file, opened)
closeErr := file.Close()
if linkErr != nil || closeErr != nil || !opened.Mode().IsRegular() || !os.SameFile(info, opened) {
return errors.New("package member changed during preflight")
}
if links != 1 {
return errors.New("package tree contains a hard-linked file")
}
return nil
})
if err != nil {
return err
}
return nil
}
|