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
+19 -5
View File
@@ -211,6 +211,7 @@ func (a *AliasManager) removeSourceLinesFromShells() error {
if os.IsNotExist(err) {
continue
}
log.Warnf("Warning: skipping %s (%s)", shell.Name(), err)
continue
}
@@ -218,12 +219,14 @@ func (a *AliasManager) removeSourceLinesFromShells() error {
// Get original file permissions
info, err := os.Stat(configPath)
if err != nil {
log.Warnf("Warning: skipping %s (%s)", shell.Name(), err)
continue
}
// Create temp file
tempFile, err := os.CreateTemp(filepath.Dir(configPath), ".tmp-"+filepath.Base(configPath))
if err != nil {
log.Warnf("Warning: failed to create temporary file for %s: %s", configPath, err)
continue
}
@@ -242,16 +245,27 @@ func (a *AliasManager) removeSourceLinesFromShells() error {
continue
}
writer.WriteString(line + "\n")
if _, err := writer.WriteString(line + "\n"); err != nil {
log.Warnf("Warning: failed to write to temporary file: %s", err)
}
}
writer.Flush()
tempFile.Close()
if err := writer.Flush(); err != nil {
log.Warnf("Warning: failed to flush temporary file: %s", err)
}
if err := tempFile.Close(); err != nil {
log.Warnf("Warning: failed to close temporary file: %s", err)
}
// Set permissions on temporary file to match original file.
if err := os.Chmod(tempPath, info.Mode()); err != nil {
log.Warnf("Warning: failed to set permissions on temporary file for %s: %s", configPath, err)
}
// Replace original file
os.Chmod(tempPath, info.Mode())
if err := os.Rename(tempPath, configPath); err != nil {
os.Remove(tempPath)
_ = os.Remove(tempPath)
log.Warnf("Warning: failed to update %s: %s", configPath, err)
}
}
+22 -7
View File
@@ -248,7 +248,9 @@ func (l *fileWithRotationLogger) Log(event Event) error {
// Flush to ensure data is written
if l.file != nil {
l.file.Sync()
if err := l.file.Sync(); err != nil {
return fmt.Errorf("failed to sync file: %w", err)
}
}
return nil
@@ -267,6 +269,7 @@ func (l *fileWithRotationLogger) Close() error {
if l.file != nil {
return l.file.Close()
}
return nil
}
@@ -303,7 +306,9 @@ func LogMalwareBlocked(packageName, version, ecosystem, reason string, details m
}
event.Details["reason"] = reason
LogEvent(event)
if err := LogEvent(event); err != nil {
log.Warnf("failed to log malware blocked event: %s", err)
}
}
// LogMalwareConfirmed logs when user confirms installation despite warning
@@ -316,7 +321,9 @@ func LogMalwareConfirmed(packageName, version, ecosystem string) {
Ecosystem: ecosystem,
}
LogEvent(event)
if err := LogEvent(event); err != nil {
log.Warnf("failed to log malware confirmed event: %s", err)
}
}
// LogInstallAllowed logs when an installation is allowed
@@ -332,7 +339,9 @@ func LogInstallAllowed(packageName, version, ecosystem string, packageCount int)
},
}
LogEvent(event)
if err := LogEvent(event); err != nil {
log.Warnf("failed to log install allowed event: %s", err)
}
}
// LogInstallTrustedAllowed logs when an installation is allowed for a trusted package
@@ -345,7 +354,9 @@ func LogInstallTrustedAllowed(packageName, version, ecosystem string) {
Ecosystem: ecosystem,
}
LogEvent(event)
if err := LogEvent(event); err != nil {
log.Warnf("failed to log install trusted allowed event: %s", err)
}
}
// LogInstallStarted logs when an installation starts
@@ -359,7 +370,9 @@ func LogInstallStarted(packageManager string, args []string) {
},
}
LogEvent(event)
if err := LogEvent(event); err != nil {
log.Warnf("failed to log install started event: %s", err)
}
}
// LogError logs an error event
@@ -372,7 +385,9 @@ func LogError(message string, err error) {
},
}
LogEvent(event)
if err := LogEvent(event); err != nil {
log.Warnf("failed to log error event: %s", err)
}
}
// Close closes the global logger
+21 -6
View File
@@ -27,7 +27,10 @@ func TestLoggerInitialization(t *testing.T) {
// Initialize logger
err := InitializeWithDir(logDir)
assert.NoError(t, err, "Failed to initialize logger")
defer Close()
defer func() {
err := Close()
assert.NoError(t, err)
}()
// Check that directory was created
_, err = os.Stat(logDir)
@@ -47,7 +50,10 @@ func TestLogEvent(t *testing.T) {
// Initialize logger
err := reinitializeForTest(logDir)
assert.NoError(t, err, "Failed to initialize logger")
defer Close()
defer func() {
err := Close()
assert.NoError(t, err)
}()
// Log an event
event := Event{
@@ -87,7 +93,10 @@ func TestLogMalwareBlocked(t *testing.T) {
// Initialize logger
err := reinitializeForTest(logDir)
assert.NoError(t, err, "Failed to initialize logger")
defer Close()
defer func() {
err := Close()
assert.NoError(t, err)
}()
// Log malware blocked event
LogMalwareBlocked("malicious-pkg", "2.0.0", "pypi", "Contains known malware", nil)
@@ -114,14 +123,17 @@ func TestInitializeWithFile(t *testing.T) {
// Initialize logger with custom file
err := reinitializeForTest("")
if err == nil {
Close()
_ = Close()
}
// Reset for custom file
once = sync.Once{}
err = InitializeWithFile(logFile)
assert.NoError(t, err, "Failed to initialize logger with file")
defer Close()
defer func() {
err := Close()
assert.NoError(t, err)
}()
// Log an event
event := Event{
@@ -177,7 +189,10 @@ func TestCleanupOldLogs(t *testing.T) {
err = logger.init(logDir)
assert.NoError(t, err, "Failed to initialize logger")
defer logger.Close()
defer func() {
err := logger.Close()
assert.NoError(t, err)
}()
// Give cleanup goroutine time to run
time.Sleep(100 * time.Millisecond)
+3 -1
View File
@@ -368,7 +368,9 @@ func (f *proxyFlow) executeWithProxy(
var wg sync.WaitGroup
wg.Go(func() {
io.Copy(outputRouter, sess.PtyReader())
if _, err := io.Copy(outputRouter, sess.PtyReader()); err != nil {
log.Errorf("failed to copy output: %v", err)
}
})
inputRouter, err := pty.NewInputRouter(sess.PtyWriter())
+4 -2
View File
@@ -101,8 +101,10 @@ func NewSession(ctx context.Context, cfg SessionConfig) (InteractiveSession, err
Env: cfg.Env,
})
if err != nil {
c.Restore(oldState)
c.Close()
// We are already in error state, restore and close is best effort.
_ = c.Restore(oldState)
_ = c.Close()
return nil, fmt.Errorf("failed to spawn: %w", err)
}