mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
feat: Refactor PMG to Maintain Separation of Concerns and Clean Architecture (#19)
* feat: Add separate package manager and resolver * fix: Npm dependency resolver * feat: Add analyzer for malysis query * feat: Add package manager guard as the orchestrator * feat: Add PMG to orchestrate installation * Add concurrent scan execution * Introduce package manager interaction abstraction * feat: Add UI port for guard * Remove refactored source files * Update README * fix: CI script for multi-arch build * ci: goreleaser CI fix * fix: npm command parser to extract package names * feat: Introduce global config primitive * fix: Close results channel for clean goroutine exit * ci: Add container image releaser * test: Improve test for npm resolver * refactor: Analyzer to generalise * Improve UI with additional info * fix: Goreleaser config * fix: npm resolver bug * fix: Fail when command exec workflow fails * fix: Bug with transitive dependency resolution * fix: Synchronize common data update in dependency resolver * chore: Improve log handling * docs: Update README * fix: UI text wrapping * fix: UI handling bugs * feat: Use concurrent dependency resolver
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
|
||||
)
|
||||
|
||||
// A base interface for all analyzers
|
||||
type Analyzer interface {
|
||||
Name() string
|
||||
}
|
||||
|
||||
type Action int
|
||||
|
||||
const (
|
||||
ActionUnknown Action = iota
|
||||
ActionAllow
|
||||
ActionConfirm
|
||||
ActionBlock
|
||||
)
|
||||
|
||||
type PackageVersionAnalysisResult struct {
|
||||
PackageVersion *packagev1.PackageVersion
|
||||
|
||||
// Analyser specific analysis ID
|
||||
AnalysisID string
|
||||
|
||||
// Reference URL for the analysis
|
||||
ReferenceURL string
|
||||
|
||||
// The action to take as recommended by the analyzer
|
||||
Action Action
|
||||
|
||||
// Summary of the analysis
|
||||
Summary string
|
||||
|
||||
// Analyzer specific data
|
||||
Data any
|
||||
}
|
||||
|
||||
// Contract for implementing package version specific analyzers
|
||||
type PackageVersionAnalyzer interface {
|
||||
Analyzer
|
||||
|
||||
Analyze(ctx context.Context, packageVersion *packagev1.PackageVersion) (*PackageVersionAnalysisResult, error)
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"buf.build/gen/go/safedep/api/grpc/go/safedep/services/malysis/v1/malysisv1grpc"
|
||||
malysisv1pb "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/malysis/v1"
|
||||
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
|
||||
malysisv1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/services/malysis/v1"
|
||||
drygrpc "github.com/safedep/dry/adapters/grpc"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type MalysisQueryAnalyzerConfig struct{}
|
||||
|
||||
type malysisQueryAnalyzer struct {
|
||||
client malysisv1grpc.MalwareAnalysisServiceClient
|
||||
Config MalysisQueryAnalyzerConfig
|
||||
}
|
||||
|
||||
var _ Analyzer = &malysisQueryAnalyzer{}
|
||||
var _ PackageVersionAnalyzer = &malysisQueryAnalyzer{}
|
||||
|
||||
func NewMalysisQueryAnalyzer(config MalysisQueryAnalyzerConfig) (*malysisQueryAnalyzer, error) {
|
||||
client, err := drygrpc.GrpcClient("pmg-malysis-query",
|
||||
"community-api.safedep.io", "443", "", http.Header{}, []grpc.DialOption{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create gRPC client: %w", err)
|
||||
}
|
||||
|
||||
return &malysisQueryAnalyzer{
|
||||
client: malysisv1grpc.NewMalwareAnalysisServiceClient(client),
|
||||
Config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *malysisQueryAnalyzer) Name() string {
|
||||
return "malysis-query"
|
||||
}
|
||||
|
||||
func (a *malysisQueryAnalyzer) Analyze(ctx context.Context,
|
||||
packageVersion *packagev1.PackageVersion) (*PackageVersionAnalysisResult, error) {
|
||||
|
||||
res, err := a.client.QueryPackageAnalysis(ctx, &malysisv1.QueryPackageAnalysisRequest{
|
||||
Target: &malysisv1pb.PackageAnalysisTarget{
|
||||
PackageVersion: packageVersion,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query package analysis: %w", err)
|
||||
}
|
||||
|
||||
// By default, the analyzer allows the package version
|
||||
analysisResult := &PackageVersionAnalysisResult{
|
||||
PackageVersion: packageVersion,
|
||||
ReferenceURL: malysisReportUrl(res.GetAnalysisId()),
|
||||
Action: ActionAllow,
|
||||
AnalysisID: res.GetAnalysisId(),
|
||||
Summary: res.GetReport().GetInference().GetSummary(),
|
||||
Data: res.GetReport(),
|
||||
}
|
||||
|
||||
// Mark the package version to be confirmed if it is malicious (not confirmed)
|
||||
if res.GetReport().GetInference().GetIsMalware() {
|
||||
analysisResult.Action = ActionConfirm
|
||||
}
|
||||
|
||||
// This is a confirmed malicious package, we must always block it
|
||||
if res.GetVerificationRecord().GetIsMalware() {
|
||||
analysisResult.Action = ActionBlock
|
||||
}
|
||||
|
||||
return analysisResult, nil
|
||||
}
|
||||
|
||||
func malysisReportUrl(analysisId string) string {
|
||||
return fmt.Sprintf("https://platform.safedep.io/community/malysis/%s", analysisId)
|
||||
}
|
||||
Reference in New Issue
Block a user