2026-01-07 13:22:08 +05:30
|
|
|
package interceptors
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
|
|
|
|
|
"github.com/safedep/dry/log"
|
|
|
|
|
"github.com/safedep/pmg/analyzer"
|
|
|
|
|
"github.com/safedep/pmg/guard"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// ConfirmationRequest represents a request for user confirmation on a suspicious package
|
|
|
|
|
type ConfirmationRequest struct {
|
|
|
|
|
PackageVersion *packagev1.PackageVersion
|
|
|
|
|
AnalysisResult *analyzer.PackageVersionAnalysisResult
|
|
|
|
|
|
|
|
|
|
// ResponseChan is used to send the user's response back.
|
|
|
|
|
ResponseChan chan bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ConfirmationHook is a set of hooks that can be used to customize the confirmation process.
|
|
|
|
|
type ConfirmationHook struct {
|
|
|
|
|
// BeforeInteraction is called before the user interaction is started.
|
|
|
|
|
BeforeInteraction func([]*analyzer.PackageVersionAnalysisResult) error
|
|
|
|
|
|
|
|
|
|
// AfterInteraction is called after the user interaction is finished.
|
|
|
|
|
AfterInteraction func([]*analyzer.PackageVersionAnalysisResult, bool) error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// HandleConfirmationRequests processes confirmation requests sequentially
|
|
|
|
|
// This function should be run in a goroutine and will process requests
|
|
|
|
|
// from the confirmation channel one at a time, blocking on user input.
|
|
|
|
|
//
|
|
|
|
|
// The function will exit when the confirmation channel is closed.
|
|
|
|
|
func HandleConfirmationRequests(confirmationChan chan *ConfirmationRequest,
|
2026-01-09 22:03:42 +05:30
|
|
|
interaction *guard.PackageManagerGuardInteraction, hooks *ConfirmationHook) {
|
2026-01-07 13:22:08 +05:30
|
|
|
if hooks == nil {
|
|
|
|
|
hooks = &ConfirmationHook{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for req := range confirmationChan {
|
|
|
|
|
func() {
|
|
|
|
|
// The default response is false ie. user did not confirm the installation.
|
|
|
|
|
// The code here falls through and eventually sets this flag to true if user
|
|
|
|
|
// confirms the installation.
|
|
|
|
|
responseVal := false
|
|
|
|
|
|
|
|
|
|
// We must make sure to close the response channel to prevent goroutine leaks.
|
|
|
|
|
// Idiomatic go suggests that the writer should close the channel.
|
|
|
|
|
defer func() {
|
|
|
|
|
req.ResponseChan <- responseVal
|
|
|
|
|
close(req.ResponseChan)
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
packageName := req.PackageVersion.GetPackage().GetName()
|
|
|
|
|
log.Debugf("Processing confirmation request for package %s", packageName)
|
|
|
|
|
|
|
|
|
|
// Hook to allow the caller to customize the confirmation process.
|
|
|
|
|
// Hook failures are non-fatal and will not break the confirmation process.
|
|
|
|
|
if hooks.BeforeInteraction != nil {
|
|
|
|
|
if err := hooks.BeforeInteraction([]*analyzer.PackageVersionAnalysisResult{req.AnalysisResult}); err != nil {
|
|
|
|
|
log.Errorf("Error before interaction for package %s: %v", packageName, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Call the user interaction handler to get confirmation
|
|
|
|
|
// This blocks waiting for stdin input
|
|
|
|
|
confirmed, confirmationErr := interaction.GetConfirmationOnMalware([]*analyzer.PackageVersionAnalysisResult{req.AnalysisResult})
|
|
|
|
|
|
|
|
|
|
// Must guarantee to call the after interaction hook regardless of the confirmation error.
|
|
|
|
|
if hooks.AfterInteraction != nil {
|
|
|
|
|
if err := hooks.AfterInteraction([]*analyzer.PackageVersionAnalysisResult{req.AnalysisResult}, confirmed); err != nil {
|
|
|
|
|
log.Errorf("Error after interaction for package %s: %v", packageName, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if confirmationErr != nil {
|
|
|
|
|
log.Errorf("Error getting confirmation for package %s: %v", packageName, confirmationErr)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set the response value to the user's confirmation
|
|
|
|
|
responseVal = confirmed
|
|
|
|
|
}()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.Debugf("Confirmation handler exiting (channel closed)")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewConfirmationRequest creates a new confirmation request with a response channel
|
|
|
|
|
func NewConfirmationRequest(pkgVersion *packagev1.PackageVersion, result *analyzer.PackageVersionAnalysisResult) *ConfirmationRequest {
|
|
|
|
|
return &ConfirmationRequest{
|
|
|
|
|
PackageVersion: pkgVersion,
|
|
|
|
|
AnalysisResult: result,
|
|
|
|
|
ResponseChan: make(chan bool, 1),
|
|
|
|
|
}
|
|
|
|
|
}
|