Files
pmg/sandbox/platform/glob_linux.go
T
Abhisek DattaandGitHub 4c42ceca0e feat: Add support for Landlock based Sandbox for Linux (#238)
* feat: Initial implementation of landlock based sandbox driver

* fix: Handle seccom probe failure

* fix: Remove unnecessary seccomp probe

* fix: Use file based policy load

* fix: Keep bpf filter in memory

* fix: Use TSYNC for seccom filter

* fix: Use TSYNC for seccom filter

* fix: Update landlock translator

* fix: Landlock sandbox implementation

* fix: Landlock + seccomp based sandboxing on Linux

* fix: Misc fixes

* fix: Cleanup sandbox files

* fix: Handle mandatory deny API change post merge

* fix: Landlock write access translation

* chore: Fix linter issues

* ci: Use /tmp for npm cache for landlock
2026-05-07 12:42:28 +05:30

111 lines
3.2 KiB
Go

//go:build linux
package platform
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/safedep/dry/log"
"github.com/safedep/pmg/sandbox/util"
)
// expandGlobstarPattern expands patterns containing ** (recursive glob),
// which filepath.Glob does not support. Splits the pattern at ** into a base
// path and a suffix, walks the base with a depth limit, and collects entries
// whose path ends with the suffix.
//
// If the base path does not yet exist, returns []string{basePath} so callers
// can still grant coverage to the parent directory (matters for fresh
// node_modules / pnpm caches that haven't been created yet).
func expandGlobstarPattern(pattern string, maxDepth, maxPaths int) ([]string, error) {
parts := strings.Split(pattern, "**")
if len(parts) != 2 {
return nil, fmt.Errorf("only one ** globstar supported per pattern")
}
basePath := strings.TrimSuffix(parts[0], "/")
suffix := strings.TrimPrefix(parts[1], "/")
if basePath == "" {
log.Debugf("Skipping globstar pattern '%s' with empty base path (would walk from root)", pattern)
return []string{}, nil
}
expandedBase, err := util.ExpandVariables(basePath)
if err != nil {
return nil, fmt.Errorf("failed to expand base path: %w", err)
}
basePath = expandedBase
if _, err := os.Stat(basePath); os.IsNotExist(err) {
return []string{basePath}, nil
}
matches := []string{}
if err := walkGlobWithDepthLimit(basePath, suffix, maxDepth, maxPaths, &matches); err != nil {
return nil, fmt.Errorf("failed to walk directory tree: %w", err)
}
return matches, nil
}
// walkGlobWithDepthLimit walks a directory tree from root, appending paths
// whose suffix matches `suffix`. Stops at maxDepth levels (when > 0) and
// after collecting maxPaths entries.
func walkGlobWithDepthLimit(root, suffix string, maxDepth, maxPaths int, matches *[]string) error {
return filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return nil
}
relPath, err := filepath.Rel(root, path)
if err != nil {
return nil
}
depth := 0
if relPath != "." {
depth = len(strings.Split(relPath, string(filepath.Separator)))
}
if maxDepth > 0 && depth > maxDepth {
if info.IsDir() {
return filepath.SkipDir
}
return nil
}
if suffix == "" || strings.HasSuffix(path, suffix) {
*matches = append(*matches, path)
if len(*matches) >= maxPaths {
return filepath.SkipAll
}
}
return nil
})
}
// extractGlobParentDir extracts the parent directory of a glob pattern. Used
// for coarse-grained fallback when expansion yields too many paths or the
// target tree is unsuitable for fine-grained rules.
//
// Examples:
// - ${CWD}/node_modules/** → ${CWD}/node_modules
// - ${HOME}/.cache/pnpm/** → ${HOME}/.cache/pnpm
// - /tmp/*.txt → /tmp
// - /usr/lib/**/*.so → /usr/lib
func extractGlobParentDir(pattern string) string {
pattern = strings.TrimSuffix(pattern, "/**")
pattern = strings.TrimSuffix(pattern, "/*")
idx := strings.IndexAny(pattern, "*?[")
if idx >= 0 {
pattern = pattern[:idx]
pattern = filepath.Dir(pattern)
}
pattern = strings.TrimSuffix(pattern, string(filepath.Separator))
if pattern == "" || pattern == string(filepath.Separator) {
return "."
}
return pattern
}