Files
pmg/sandbox/util/glob.go
T
9693428171 feat: Experimental Sandbox Support (#101)
* feat: Sandbox implementation with seatbelt

* refactor: Remove concept of PM_CACHE

* fix: Misc fixes

* refactor: Sandbox for separation of boundaries

* fix: Apply API

* fix: Add support for sandbox cleanup

* test: Add variable interpolation test

* fix: Misc cleanup fixes

* chore: Cleanup sandbox registry

* chore: Cleanup sandbox policy

* chore: Cleanup sandbox

* fix: Misc cleanup fixes

* fix: Remove violation mode

* fix: Update config template

* chore: Go mod cleanup

* fix: Handle the case when package manager policy is explicitly disabled

* fix: Sandbox executor

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Abhisek Datta <abhisek.datta@gmail.com>

* test: Remove unused var

* test: Add test for seatbelt sandbox driver

* fix: Sandbox profile loader from file should use path for caching

* test: Add policy test

* feat: Add support for config templates

* fix: Seatbelt translator handle glob

* fix: Merge conflicts

* fix: Fix sandbox policy generator for MacOS min permissions

* fix: Sandbox path handling bugs

* fix: Deny read to dangerous directories

* fix: Deny read to dangerous directories

* add sandbox e2e (#112)

* fix: Sandbox E2E test

* fix: Code review fixes

* fix: Code review fixes

* doc: Add sandbox debugging guide

* doc: Update sandbox doc

* docs: Add sandbox usage doc

* fix: Use better error for sandbox without policy

* fix: Add sandbox for npx

* fix: Enable PTY for npm

---------

Signed-off-by: Abhisek Datta <abhisek.datta@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Sahil Bansal <bansalsahil315@gmail.com>
2026-01-13 14:52:02 +05:30

95 lines
3.4 KiB
Go

package util
import (
"regexp"
"strings"
)
// GlobToRegex converts a glob pattern to a Seatbelt-compatible regular expression.
//
// This implements gitignore-style pattern matching to match the behavior used
// in filesystem permission systems.
//
// Supported patterns:
// - * matches any characters except / (e.g., *.ts matches foo.ts but not foo/bar.ts)
// - ** matches any characters including / (e.g., src/**/*.ts matches all .ts files in src/)
// - ? matches any single character except / (e.g., file?.txt matches file1.txt)
// - [abc] matches any character in the set (e.g., file[0-9].txt matches file3.txt)
//
// Note: This is designed for macOS sandbox (regex ...) syntax. The resulting regex
// will be used in sandbox profiles like: (deny file-write* (regex "pattern"))
//
// Examples:
// - "/path/to/*.txt" -> "^/path/to/[^/]*\\.txt$"
// - "/path/**/file" -> "^/path/(.*/)?file$"
// - "/tmp/file?.log" -> "^/tmp/file[^/]\\.log$"
func GlobToRegex(globPattern string) string {
result := normalizeGlobPathSeparator(globPattern)
// Escape regex special characters (except glob chars * ? [ ])
// We need to escape: . ^ $ + { } ( ) | \
result = escapeRegexChars(result)
// Escape unclosed brackets (no matching ])
// This handles edge cases like "[abc" which should be treated literally
result = escapeUnclosedBrackets(result)
// Convert glob patterns to regex (order matters - ** before *)
// Use placeholders to avoid double-conversion
// 1. Handle **/ (globstar with slash)
result = strings.ReplaceAll(result, "**/", "__GLOBSTAR_SLASH__")
// 2. Handle ** (globstar standalone)
result = strings.ReplaceAll(result, "**", "__GLOBSTAR__")
// 3. Handle * (wildcard)
result = strings.ReplaceAll(result, "*", "[^/]*")
// 4. Handle ? (single char wildcard)
result = strings.ReplaceAll(result, "?", "[^/]")
// 5. Restore placeholders
result = strings.ReplaceAll(result, "__GLOBSTAR_SLASH__", "(.*/)?")
result = strings.ReplaceAll(result, "__GLOBSTAR__", ".*")
// Add anchors for exact matching
return "^" + result + "$"
}
// escapeRegexChars escapes regex special characters except glob wildcards.
// Escapes: . ^ $ + { } ( ) |
// Preserves: * ? [ ] \
// Note: We don't escape backslash because it shouldn't appear in file path glob patterns
func escapeRegexChars(s string) string {
// Characters that need escaping in regex (excluding glob chars)
// We don't include backslash here because:
// 1. File paths on Unix don't contain backslashes
// 2. We use backslash to escape regex chars, so escaping backslash would double them
specialChars := []string{".", "^", "$", "+", "{", "}", "(", ")", "|"}
result := s
for _, char := range specialChars {
result = strings.ReplaceAll(result, char, "\\"+char)
}
return result
}
// normalizeGlobPathSeparator normalizes the path separator for a glob pattern.
// This is to keep options open to normalize the path separator when we support Windows.
// Particularly for Windows, the path separator is '\\' instead of '/'. We will normalize
// the path separator to '/' for consistency.
func normalizeGlobPathSeparator(s string) string {
return s
}
var escapeUnclosedBracketsRegex = regexp.MustCompile(`\[([^\]]*?)$`)
// escapeUnclosedBrackets escapes bracket expressions that don't have a closing bracket.
// Example: "[abc" -> "\[abc"
func escapeUnclosedBrackets(s string) string {
// Find all opening brackets that don't have a closing bracket
return escapeUnclosedBracketsRegex.ReplaceAllString(s, `\[$1`)
}