mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
feat: Add Support for Proxy Based Npm Interceptor (#87)
* feat: Add experimental proxy based npm interceptor * refactor: Analysis cache * ci: Add E2E for npm proxy * fix: Handle dry-run in proxy flow * fix: Handle special case for scope package name * fix: Misc fixes * fix: Code review fixes * fix: Code review fixes * refactor: Reusable code into base registry interceptor * Pause npm process during user confirmation (#90) * pause npm process when prompting user for confirmation * disable progress bar * fix logging and close chan on return * update use of deprecated field * refactor: Separation of concerns for handling process state * fix: Safe permission for cert file * fix: Handle nil check for interaction hook * fix: Add test for base registry * Fix goreleaser for windows build (#93) * introduce platform specific process control * rename common.go to common_flow.go * feat: Add support for pause resume on windows * fix: Code review fixes * test: Add confirmation handler tests --------- Co-authored-by: Sahil Bansal <bansalsahil315@gmail.com>
This commit is contained in:
co-authored by
Sahil Bansal
parent
20c854e473
commit
779deeb23d
@@ -0,0 +1,208 @@
|
||||
package interceptors
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
|
||||
"github.com/safedep/pmg/analyzer"
|
||||
"github.com/safedep/pmg/guard"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func mockPackageVersion(name, version string) *packagev1.PackageVersion {
|
||||
return &packagev1.PackageVersion{
|
||||
Package: &packagev1.Package{Name: name},
|
||||
Version: version,
|
||||
}
|
||||
}
|
||||
|
||||
func mockAnalysisResult() *analyzer.PackageVersionAnalysisResult {
|
||||
return &analyzer.PackageVersionAnalysisResult{
|
||||
Action: analyzer.ActionConfirm,
|
||||
Summary: "Test suspicious package",
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleConfirmationRequests(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
confirmationResponse bool
|
||||
confirmationError error
|
||||
beforeInteractionErr error
|
||||
afterInteractionErr error
|
||||
useNilHooks bool
|
||||
expectedResponse bool
|
||||
verifyHooksCalled bool
|
||||
}{
|
||||
{
|
||||
name: "user confirms installation",
|
||||
confirmationResponse: true,
|
||||
confirmationError: nil,
|
||||
useNilHooks: false,
|
||||
expectedResponse: true,
|
||||
verifyHooksCalled: true,
|
||||
},
|
||||
{
|
||||
name: "user denies installation",
|
||||
confirmationResponse: false,
|
||||
confirmationError: nil,
|
||||
useNilHooks: false,
|
||||
expectedResponse: false,
|
||||
verifyHooksCalled: true,
|
||||
},
|
||||
{
|
||||
name: "confirmation error returns false",
|
||||
confirmationResponse: false,
|
||||
confirmationError: errors.New("confirmation failed"),
|
||||
useNilHooks: false,
|
||||
expectedResponse: false,
|
||||
verifyHooksCalled: true,
|
||||
},
|
||||
{
|
||||
name: "before interaction hook error is non-fatal",
|
||||
confirmationResponse: true,
|
||||
confirmationError: nil,
|
||||
beforeInteractionErr: errors.New("before hook failed"),
|
||||
useNilHooks: false,
|
||||
expectedResponse: true,
|
||||
verifyHooksCalled: true,
|
||||
},
|
||||
{
|
||||
name: "after interaction hook error is non-fatal",
|
||||
confirmationResponse: true,
|
||||
confirmationError: nil,
|
||||
afterInteractionErr: errors.New("after hook failed"),
|
||||
useNilHooks: false,
|
||||
expectedResponse: true,
|
||||
verifyHooksCalled: true,
|
||||
},
|
||||
{
|
||||
name: "nil hooks does not panic",
|
||||
confirmationResponse: true,
|
||||
confirmationError: nil,
|
||||
useNilHooks: true,
|
||||
expectedResponse: true,
|
||||
verifyHooksCalled: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
beforeCalled := false
|
||||
afterCalled := false
|
||||
var afterConfirmedParam bool
|
||||
|
||||
interaction := guard.PackageManagerGuardInteraction{
|
||||
GetConfirmationOnMalware: func(results []*analyzer.PackageVersionAnalysisResult) (bool, error) {
|
||||
assert.Len(t, results, 1)
|
||||
return tt.confirmationResponse, tt.confirmationError
|
||||
},
|
||||
}
|
||||
|
||||
var hooks *ConfirmationHook
|
||||
if !tt.useNilHooks {
|
||||
hooks = &ConfirmationHook{
|
||||
BeforeInteraction: func(results []*analyzer.PackageVersionAnalysisResult) error {
|
||||
beforeCalled = true
|
||||
assert.Len(t, results, 1)
|
||||
return tt.beforeInteractionErr
|
||||
},
|
||||
AfterInteraction: func(results []*analyzer.PackageVersionAnalysisResult, confirmed bool) error {
|
||||
afterCalled = true
|
||||
afterConfirmedParam = confirmed
|
||||
assert.Len(t, results, 1)
|
||||
return tt.afterInteractionErr
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
confirmationChan := make(chan *ConfirmationRequest, 1)
|
||||
go HandleConfirmationRequests(confirmationChan, interaction, hooks)
|
||||
|
||||
pkgVersion := mockPackageVersion("test-package", "1.0.0")
|
||||
analysisResult := mockAnalysisResult()
|
||||
analysisResult.PackageVersion = pkgVersion
|
||||
req := NewConfirmationRequest(pkgVersion, analysisResult)
|
||||
confirmationChan <- req
|
||||
|
||||
response := <-req.ResponseChan
|
||||
|
||||
assert.Equal(t, tt.expectedResponse, response)
|
||||
|
||||
if tt.verifyHooksCalled {
|
||||
assert.True(t, beforeCalled, "BeforeInteraction hook should be called")
|
||||
assert.True(t, afterCalled, "AfterInteraction hook should be called")
|
||||
if tt.confirmationError == nil {
|
||||
assert.Equal(t, tt.confirmationResponse, afterConfirmedParam,
|
||||
"AfterInteraction should receive correct confirmation status")
|
||||
}
|
||||
} else {
|
||||
assert.False(t, beforeCalled, "BeforeInteraction hook should not be called when hooks are nil")
|
||||
assert.False(t, afterCalled, "AfterInteraction hook should not be called when hooks are nil")
|
||||
}
|
||||
|
||||
close(confirmationChan)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleConfirmationRequests_MultipleSequential(t *testing.T) {
|
||||
processedPackages := []string{}
|
||||
|
||||
interaction := guard.PackageManagerGuardInteraction{
|
||||
GetConfirmationOnMalware: func(results []*analyzer.PackageVersionAnalysisResult) (bool, error) {
|
||||
pkgName := results[0].PackageVersion.GetPackage().GetName()
|
||||
processedPackages = append(processedPackages, pkgName)
|
||||
return true, nil
|
||||
},
|
||||
}
|
||||
|
||||
confirmationChan := make(chan *ConfirmationRequest, 3)
|
||||
go HandleConfirmationRequests(confirmationChan, interaction, nil)
|
||||
|
||||
pkgVersion1 := mockPackageVersion("package-1", "1.0.0")
|
||||
analysisResult1 := mockAnalysisResult()
|
||||
analysisResult1.PackageVersion = pkgVersion1
|
||||
req1 := NewConfirmationRequest(pkgVersion1, analysisResult1)
|
||||
|
||||
pkgVersion2 := mockPackageVersion("package-2", "1.0.0")
|
||||
analysisResult2 := mockAnalysisResult()
|
||||
analysisResult2.PackageVersion = pkgVersion2
|
||||
req2 := NewConfirmationRequest(pkgVersion2, analysisResult2)
|
||||
|
||||
pkgVersion3 := mockPackageVersion("package-3", "1.0.0")
|
||||
analysisResult3 := mockAnalysisResult()
|
||||
analysisResult3.PackageVersion = pkgVersion3
|
||||
req3 := NewConfirmationRequest(pkgVersion3, analysisResult3)
|
||||
|
||||
confirmationChan <- req1
|
||||
confirmationChan <- req2
|
||||
confirmationChan <- req3
|
||||
|
||||
response1 := <-req1.ResponseChan
|
||||
response2 := <-req2.ResponseChan
|
||||
response3 := <-req3.ResponseChan
|
||||
|
||||
assert.True(t, response1)
|
||||
assert.True(t, response2)
|
||||
assert.True(t, response3)
|
||||
|
||||
assert.Equal(t, []string{"package-1", "package-2", "package-3"}, processedPackages)
|
||||
|
||||
close(confirmationChan)
|
||||
}
|
||||
|
||||
func TestNewConfirmationRequest(t *testing.T) {
|
||||
pkgVersion := mockPackageVersion("test-package", "1.0.0")
|
||||
analysisResult := mockAnalysisResult()
|
||||
analysisResult.PackageVersion = pkgVersion
|
||||
|
||||
req := NewConfirmationRequest(pkgVersion, analysisResult)
|
||||
|
||||
assert.NotNil(t, req)
|
||||
assert.Equal(t, pkgVersion, req.PackageVersion)
|
||||
assert.Equal(t, analysisResult, req.AnalysisResult)
|
||||
assert.NotNil(t, req.ResponseChan)
|
||||
assert.Equal(t, 1, cap(req.ResponseChan), "ResponseChan should have buffer size of 1")
|
||||
}
|
||||
Reference in New Issue
Block a user