mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
95 lines
3.4 KiB
Go
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`)
|
||
|
|
}
|