feat: add suppport for bypassing the blocking behavior of malicious packages (#53)

* feat: add suppport for bypassing the blocking behavior of malicious packages

* feat: add InsecureInstallation config to bypass malware scanning with tests

* ui: introduce ShowWarning interaction method

* guard test fix
This commit is contained in:
Sahil Bansal
2025-07-02 19:09:27 +05:30
committed by GitHub
parent 0e17378d3e
commit ca752edf79
7 changed files with 275 additions and 1 deletions
+19
View File
@@ -24,6 +24,9 @@ type PackageManagerGuardInteraction struct {
// ClearStatus is called to clear the status of the guard in the UI
ClearStatus func()
// ShowWarning is called to show a warning message to the user
ShowWarning func(message string)
// GetConfirmationOnMalware is called to get the confirmation of the user on the malware packages
GetConfirmationOnMalware func(malwarePackages []*analyzer.PackageVersionAnalysisResult) (bool, error)
@@ -38,6 +41,7 @@ type PackageManagerGuardConfig struct {
MaxConcurrentAnalyzes int
AnalysisTimeout time.Duration
DryRun bool
InsecureInstallation bool
}
func DefaultPackageManagerGuardConfig() PackageManagerGuardConfig {
@@ -46,6 +50,7 @@ func DefaultPackageManagerGuardConfig() PackageManagerGuardConfig {
MaxConcurrentAnalyzes: 10,
AnalysisTimeout: 5 * time.Minute,
DryRun: false,
InsecureInstallation: false,
}
}
@@ -75,6 +80,12 @@ func NewPackageManagerGuard(config PackageManagerGuardConfig,
func (g *packageManagerGuard) Run(ctx context.Context, args []string, parsedCommand *packagemanager.ParsedCommand) error {
log.Debugf("Running package manager guard with args: %v", args)
if g.config.InsecureInstallation {
log.Debugf("Bypassing block for unconfirmed malicious packages due to PMG_INSECURE_INSTALLATION")
g.showWarning("⚠️ WARNING: INSECURE INSTALLATION MODE - Malware protection bypassed!")
return g.continueExecution(ctx, parsedCommand)
}
if !parsedCommand.HasInstallTarget() {
// Check if this is a manifest-based installation
if parsedCommand.ShouldExtractFromManifest() {
@@ -283,6 +294,14 @@ func (g *packageManagerGuard) clearStatus() {
g.interaction.ClearStatus()
}
func (g *packageManagerGuard) showWarning(message string) {
if g.interaction.ShowWarning == nil {
return
}
g.interaction.ShowWarning(message)
}
func (g *packageManagerGuard) handleManifestInstallation(ctx context.Context, parsedCommand *packagemanager.ParsedCommand) error {
extractorConfig := extractor.NewDefaultExtractorConfig()
extractorConfig.ExtractorPackageManager = extractor.PackageManagerName(g.packageManager.Name())
+224 -1
View File
@@ -6,6 +6,8 @@ import (
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
"github.com/safedep/pmg/analyzer"
"github.com/safedep/pmg/internal/ui"
"github.com/safedep/pmg/packagemanager"
"github.com/stretchr/testify/assert"
)
@@ -16,7 +18,9 @@ func TestGuardConcurrentlyAnalyzePackagesMalwareQueryService(t *testing.T) {
}
pg, err := NewPackageManagerGuard(DefaultPackageManagerGuardConfig(), nil, nil,
[]analyzer.PackageVersionAnalyzer{mq}, PackageManagerGuardInteraction{})
[]analyzer.PackageVersionAnalyzer{mq}, PackageManagerGuardInteraction{
ShowWarning: func(message string) {},
})
if err != nil {
t.Fatalf("failed to create pg: %v", err)
}
@@ -45,3 +49,222 @@ func TestGuardConcurrentlyAnalyzePackagesMalwareQueryService(t *testing.T) {
assert.Equal(t, analyzer.ActionBlock, r[0].Action)
})
}
func TestGuardInsecureInstallation(t *testing.T) {
mq, err := analyzer.NewMalysisQueryAnalyzer(analyzer.MalysisQueryAnalyzerConfig{})
if err != nil {
t.Fatalf("failed to create mq: %v", err)
}
t.Run("should bypass malware blocking when InsecureInstallation is enabled", func(t *testing.T) {
// Create guard with InsecureInstallation enabled
config := DefaultPackageManagerGuardConfig()
config.InsecureInstallation = true
config.DryRun = true // Enable dry run to avoid actual command execution
config.ResolveDependencies = false // Disable dependency resolution to avoid nil pointer issues
blockCalled := false
warningCalled := false
var warningMessage string
interaction := PackageManagerGuardInteraction{
ShowWarning: func(message string) {
warningCalled = true
warningMessage = message
},
Block: func(config *ui.BlockConfig) error {
blockCalled = true
return nil
},
}
pg, err := NewPackageManagerGuard(config, nil, nil,
[]analyzer.PackageVersionAnalyzer{mq}, interaction)
if err != nil {
t.Fatalf("failed to create pg: %v", err)
}
// Create a parsed command with a known malicious package
parsedCommand := &packagemanager.ParsedCommand{
Command: packagemanager.Command{
Exe: "npm",
Args: []string{"install", "nyc-config@10.0.0"},
},
InstallTargets: []*packagemanager.PackageInstallTarget{
{
PackageVersion: &packagev1.PackageVersion{
Package: &packagev1.Package{
Name: "nyc-config",
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
},
Version: "10.0.0",
},
},
},
}
err = pg.Run(context.Background(), []string{"npm", "install", "nyc-config@10.0.0"}, parsedCommand)
// With dry run enabled, we expect no error even though we're bypassing execution
assert.NoError(t, err)
// Block should not be called because InsecureInstallation bypasses the analysis
assert.False(t, blockCalled, "Block should not be called when InsecureInstallation is enabled")
// Warning should be called to inform user about insecure installation
assert.True(t, warningCalled, "Warning should be called when InsecureInstallation is enabled")
assert.Contains(t, warningMessage, "INSECURE INSTALLATION MODE", "Warning message should mention insecure installation")
})
t.Run("should block malware when InsecureInstallation is disabled", func(t *testing.T) {
// Create guard with InsecureInstallation disabled (default)
config := DefaultPackageManagerGuardConfig()
config.InsecureInstallation = false
config.DryRun = true
config.ResolveDependencies = false // Disable dependency resolution to avoid nil pointer issues
blockCalled := false
var blockedPackages []*analyzer.PackageVersionAnalysisResult
interaction := PackageManagerGuardInteraction{
ShowWarning: func(message string) {},
Block: func(config *ui.BlockConfig) error {
blockCalled = true
blockedPackages = config.MalwarePackages
return nil
},
}
pg, err := NewPackageManagerGuard(config, nil, nil,
[]analyzer.PackageVersionAnalyzer{mq}, interaction)
if err != nil {
t.Fatalf("failed to create pg: %v", err)
}
// Create a parsed command with a known malicious package
parsedCommand := &packagemanager.ParsedCommand{
Command: packagemanager.Command{
Exe: "npm",
Args: []string{"install", "nyc-config@10.0.0"},
},
InstallTargets: []*packagemanager.PackageInstallTarget{
{
PackageVersion: &packagev1.PackageVersion{
Package: &packagev1.Package{
Name: "nyc-config",
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
},
Version: "10.0.0",
},
},
},
}
err = pg.Run(context.Background(), []string{"npm", "install", "nyc-config@10.0.0"}, parsedCommand)
// We expect no error from the guard itself (blocking is handled via the Block callback)
assert.NoError(t, err)
// Block should be called because InsecureInstallation is disabled
assert.True(t, blockCalled, "Block should be called when InsecureInstallation is disabled")
// Verify that the malicious package was detected and blocked
assert.NotEmpty(t, blockedPackages, "Blocked packages should not be empty")
if len(blockedPackages) > 0 {
assert.Equal(t, "nyc-config", blockedPackages[0].PackageVersion.GetPackage().GetName())
assert.Equal(t, "10.0.0", blockedPackages[0].PackageVersion.GetVersion())
assert.Equal(t, analyzer.ActionBlock, blockedPackages[0].Action)
}
})
t.Run("should continue execution for commands without install targets when InsecureInstallation is enabled", func(t *testing.T) {
// Create guard with InsecureInstallation enabled
config := DefaultPackageManagerGuardConfig()
config.InsecureInstallation = true
config.DryRun = true
config.ResolveDependencies = false // Disable dependency resolution to avoid nil pointer issues
blockCalled := false
interaction := PackageManagerGuardInteraction{
ShowWarning: func(message string) {},
Block: func(config *ui.BlockConfig) error {
blockCalled = true
return nil
},
}
pg, err := NewPackageManagerGuard(config, nil, nil,
[]analyzer.PackageVersionAnalyzer{mq}, interaction)
if err != nil {
t.Fatalf("failed to create pg: %v", err)
}
// Create a parsed command without install targets (e.g., npm list)
parsedCommand := &packagemanager.ParsedCommand{
Command: packagemanager.Command{
Exe: "npm",
Args: []string{"list"},
},
InstallTargets: []*packagemanager.PackageInstallTarget{}, // No install targets
}
err = pg.Run(context.Background(), []string{"npm", "list"}, parsedCommand)
// Should not error since there are no install targets to analyze
assert.NoError(t, err)
// Block should not be called since there are no packages to analyze
assert.False(t, blockCalled, "Block should not be called when there are no install targets")
})
t.Run("should handle manifest-based installation when InsecureInstallation is enabled", func(t *testing.T) {
// Create guard with InsecureInstallation enabled
config := DefaultPackageManagerGuardConfig()
config.InsecureInstallation = true
config.DryRun = true
config.ResolveDependencies = false // Disable dependency resolution to avoid nil pointer issues
blockCalled := false
interaction := PackageManagerGuardInteraction{
ShowWarning: func(message string) {},
Block: func(config *ui.BlockConfig) error {
blockCalled = true
return nil
},
}
pg, err := NewPackageManagerGuard(config, nil, nil,
[]analyzer.PackageVersionAnalyzer{mq}, interaction)
if err != nil {
t.Fatalf("failed to create pg: %v", err)
}
// Create a parsed command for manifest-based installation
parsedCommand := &packagemanager.ParsedCommand{
Command: packagemanager.Command{
Exe: "npm",
Args: []string{"install"},
},
InstallTargets: []*packagemanager.PackageInstallTarget{}, // No direct install targets
IsManifestInstall: true,
ManifestFiles: []string{"package.json"},
}
err = pg.Run(context.Background(), []string{"npm", "install"}, parsedCommand)
// Should not error and should bypass malware checking
assert.NoError(t, err)
// Block should not be called because InsecureInstallation bypasses analysis
assert.False(t, blockCalled, "Block should not be called when InsecureInstallation is enabled for manifest installation")
})
t.Run("should verify InsecureInstallation defaults to false", func(t *testing.T) {
config := DefaultPackageManagerGuardConfig()
// Verify that InsecureInstallation defaults to false
assert.False(t, config.InsecureInstallation, "InsecureInstallation should default to false")
})
}