Files
pmg/sandbox/executor/diagnostics.go
T
Abhisek DattaandGitHub 219d743b80 chore: Standardise Error Codes (#286)
* fix: Misc error handling fixes

* fix: Sandbox error translation
2026-05-24 12:46:06 +05:30

81 lines
2.3 KiB
Go

package executor
import (
"fmt"
"github.com/safedep/dry/log"
"github.com/safedep/dry/usefulerror"
"github.com/safedep/pmg/config"
"github.com/safedep/pmg/errcodes"
"github.com/safedep/pmg/sandbox"
)
// WrapCommandExecutionError converts a package manager execution error into a
// user-facing error. It never attributes the failure to the sandbox: causation
// cannot be inferred from EPERM/EACCES returns alone, and a security tool
// should not make best-effort claims. Any observed sandbox denials are
// persisted to the violation cache for forensic review via
// `pmg sandbox violations list` and `pmg sandbox explain`.
func WrapCommandExecutionError(err error, result *sandbox.ExecutionResult, exitCode int) error {
if err == nil {
return nil
}
observed := observeAndPersistViolations(result, err)
humanError := "Failed to execute package manager command"
if exitCode >= 0 {
humanError = fmt.Sprintf("Package manager command exited with code: %d", exitCode)
}
help := "Check the package manager command and its arguments"
builder := usefulerror.NewUsefulError().
WithCode(errcodes.PackageManagerExecutionFailed).
WithHumanError(humanError).
WithHelp(help)
if observed > 0 {
builder = builder.WithAdditionalHelp(fmt.Sprintf(
"Sandbox observed %d denied operation(s) during this run. Run `pmg sandbox violations list` to investigate.",
observed,
))
}
return builder.Wrap(err)
}
// observeAndPersistViolations collects any sandbox violation report associated
// with the run and writes it to the violation cache. Returns the number of
// violations observed. Failures are logged and swallowed; observability MUST
// NOT affect command exit.
func observeAndPersistViolations(result *sandbox.ExecutionResult, runErr error) int {
if result == nil {
return 0
}
report, diagErr := result.BestEffortViolation(runErr)
if diagErr != nil {
log.Warnf("failed to collect sandbox diagnostics: %v", diagErr)
return 0
}
if report == nil || len(report.Violations) == 0 {
return 0
}
cfg := config.Get()
if cfg == nil {
return len(report.Violations)
}
dir := cfg.SandboxViolationCacheDir()
if dir == "" {
return len(report.Violations)
}
if _, err := sandbox.NewViolationCache(dir).Write(report); err != nil {
log.Warnf("failed to persist sandbox violation report: %v", err)
}
return len(report.Violations)
}