feat: Add support for policy inheritence (#113)

* feat: Add support for policy inheritence

* fix: Linter fixes

* Update docs/sandbox.md

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

* fix: Handle boolean inheritence

* ci: Add linter

* Update sandbox/policy_test.go

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

* fix: Linter fixes

* fix: Linter fixes

* fix: Sandbox rule regex format

---------

Signed-off-by: Abhisek Datta <abhisek.datta@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Abhisek Datta
2026-01-14 10:50:39 +05:30
committed by GitHub
co-authored by Copilot
parent 9693428171
commit 2e1f5b1a36
17 changed files with 815 additions and 58 deletions
+12 -11
View File
@@ -9,6 +9,7 @@ import (
"path/filepath"
"strings"
"github.com/safedep/dry/utils"
"github.com/safedep/pmg/sandbox"
"github.com/safedep/pmg/sandbox/util"
)
@@ -138,7 +139,7 @@ func generateMoveBlockingRules(pathPatterns []string, logTag string) []string {
if util.ContainsGlob(pathPattern) {
// For glob patterns, use regex matching for precise pattern enforcement
regexPattern := util.GlobToRegex(pathPattern)
rules = append(rules, fmt.Sprintf("(deny file-write-unlink (regex \"%s\") (with message \"%s\"))", regexPattern, logTag))
rules = append(rules, fmt.Sprintf("(deny file-write-unlink (regex #\"%s\") (with message \"%s\"))", regexPattern, logTag))
// Also block moving the base directory to prevent bypass
baseDir := extractBaseDir(pathPattern)
@@ -358,7 +359,7 @@ func (t *seatbeltPolicyTranslator) translate(policy *sandbox.SandboxPolicy) (str
}
// PTY support (optional)
if policy.AllowPTY {
if utils.SafelyGetValue(policy.AllowPTY) {
sb.WriteString(";; Pseudo-terminal (PTY) support\n")
sb.WriteString("(allow pseudo-tty)\n")
sb.WriteString("(allow file-ioctl\n")
@@ -390,7 +391,7 @@ func (t *seatbeltPolicyTranslator) translateFilesystem(policy *sandbox.SandboxPo
globDoubleStarAutoAllowParentDirIfNeeded(sb, pattern, expanded, "file-read*")
regexPattern := util.GlobToRegex(expanded)
sb.WriteString(fmt.Sprintf("(allow file-read* (regex \"%s\"))\n", regexPattern))
sb.WriteString(fmt.Sprintf("(allow file-read* (regex #\"%s\"))\n", regexPattern))
} else {
sb.WriteString(fmt.Sprintf("(allow file-read* (subpath \"%s\"))\n", expanded))
}
@@ -424,7 +425,7 @@ func (t *seatbeltPolicyTranslator) translateFilesystem(policy *sandbox.SandboxPo
globDoubleStarAutoAllowParentDirIfNeeded(sb, pattern, expanded, "file-write*")
regexPattern := util.GlobToRegex(expanded)
sb.WriteString(fmt.Sprintf("(allow file-write* (regex \"%s\"))\n", regexPattern))
sb.WriteString(fmt.Sprintf("(allow file-write* (regex #\"%s\"))\n", regexPattern))
} else {
sb.WriteString(fmt.Sprintf("(allow file-write* (subpath \"%s\"))\n", expanded))
}
@@ -444,7 +445,7 @@ func (t *seatbeltPolicyTranslator) translateFilesystem(policy *sandbox.SandboxPo
// Use regex matching for glob patterns, subpath for literals
if util.ContainsGlob(expanded) {
regexPattern := util.GlobToRegex(expanded)
sb.WriteString(fmt.Sprintf("(deny file-read* (regex \"%s\") (with message \"%s\"))\n", regexPattern, t.logTag))
sb.WriteString(fmt.Sprintf("(deny file-read* (regex #\"%s\") (with message \"%s\"))\n", regexPattern, t.logTag))
} else {
sb.WriteString(fmt.Sprintf("(deny file-read* (subpath \"%s\") (with message \"%s\"))\n", expanded, t.logTag))
}
@@ -471,7 +472,7 @@ func (t *seatbeltPolicyTranslator) translateFilesystem(policy *sandbox.SandboxPo
// Use regex matching for glob patterns, subpath for literals
if util.ContainsGlob(expanded) {
regexPattern := util.GlobToRegex(expanded)
sb.WriteString(fmt.Sprintf("(deny file-write* (regex \"%s\") (with message \"%s\"))\n", regexPattern, t.logTag))
sb.WriteString(fmt.Sprintf("(deny file-write* (regex #\"%s\") (with message \"%s\"))\n", regexPattern, t.logTag))
} else {
sb.WriteString(fmt.Sprintf("(deny file-write* (subpath \"%s\") (with message \"%s\"))\n", expanded, t.logTag))
}
@@ -483,7 +484,7 @@ func (t *seatbeltPolicyTranslator) translateFilesystem(policy *sandbox.SandboxPo
if t.enableDangerousFileBlocking {
// Add mandatory deny patterns for security (credentials, git hooks, etc.)
sb.WriteString(";; Mandatory security denies (credentials, git hooks, etc.)\n")
mandatoryDenies := util.GetMandatoryDenyPatterns(policy.AllowGitConfig)
mandatoryDenies := util.GetMandatoryDenyPatterns(utils.SafelyGetValue(policy.AllowGitConfig))
for _, pattern := range mandatoryDenies {
// Expand variables if needed
expanded, err := util.ExpandVariables(pattern)
@@ -494,8 +495,8 @@ func (t *seatbeltPolicyTranslator) translateFilesystem(policy *sandbox.SandboxPo
// Use regex matching for glob patterns, subpath for literals
if util.ContainsGlob(expanded) {
regexPattern := util.GlobToRegex(expanded)
sb.WriteString(fmt.Sprintf("(deny file-write* (regex \"%s\") (with message \"%s\"))\n", regexPattern, t.logTag))
sb.WriteString(fmt.Sprintf("(deny file-read* (regex \"%s\") (with message \"%s\"))\n", regexPattern, t.logTag))
sb.WriteString(fmt.Sprintf("(deny file-write* (regex #\"%s\") (with message \"%s\"))\n", regexPattern, t.logTag))
sb.WriteString(fmt.Sprintf("(deny file-read* (regex #\"%s\") (with message \"%s\"))\n", regexPattern, t.logTag))
} else {
sb.WriteString(fmt.Sprintf("(deny file-write* (subpath \"%s\") (with message \"%s\"))\n", expanded, t.logTag))
sb.WriteString(fmt.Sprintf("(deny file-read* (subpath \"%s\") (with message \"%s\"))\n", expanded, t.logTag))
@@ -571,7 +572,7 @@ func (t *seatbeltPolicyTranslator) translateProcess(policy *sandbox.SandboxPolic
if util.ContainsGlob(expanded) {
// For glob patterns, use regex matching for precise control
regexPattern := util.GlobToRegex(expanded)
sb.WriteString(fmt.Sprintf("(allow process-exec* (regex \"%s\"))\n", regexPattern))
sb.WriteString(fmt.Sprintf("(allow process-exec* (regex #\"%s\"))\n", regexPattern))
} else {
sb.WriteString(fmt.Sprintf("(allow process-exec* (literal \"%s\"))\n", expanded))
}
@@ -589,7 +590,7 @@ func (t *seatbeltPolicyTranslator) translateProcess(policy *sandbox.SandboxPolic
if util.ContainsGlob(expanded) {
// For glob patterns, use regex matching for precise control
regexPattern := util.GlobToRegex(expanded)
sb.WriteString(fmt.Sprintf("(deny process-exec* (regex \"%s\") (with message \"%s\"))\n", regexPattern, t.logTag))
sb.WriteString(fmt.Sprintf("(deny process-exec* (regex #\"%s\") (with message \"%s\"))\n", regexPattern, t.logTag))
} else {
sb.WriteString(fmt.Sprintf("(deny process-exec* (literal \"%s\") (with message \"%s\"))\n", expanded, t.logTag))
}