mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
fix: Proxy flow should respect trusted packages (#96)
* fix: Handle trusted packages in proxy flow * perf: Pre-parse trusted PURLs * fix: Code review fixes * fix: Remove unused config
This commit is contained in:
@@ -8,6 +8,9 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
|
||||||
|
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
|
||||||
|
"github.com/safedep/dry/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -62,6 +65,13 @@ type Config struct {
|
|||||||
type TrustedPackage struct {
|
type TrustedPackage struct {
|
||||||
Purl string `mapstructure:"purl"`
|
Purl string `mapstructure:"purl"`
|
||||||
Reason string `mapstructure:"reason"`
|
Reason string `mapstructure:"reason"`
|
||||||
|
|
||||||
|
// Pre-parsed PURL components (not serialized, computed at load time)
|
||||||
|
// These fields avoid repeated PURL parsing on every IsTrustedPackage() call
|
||||||
|
parsed bool
|
||||||
|
ecosystem packagev1.Ecosystem
|
||||||
|
name string
|
||||||
|
version string
|
||||||
}
|
}
|
||||||
|
|
||||||
// RuntimeConfig is the configuration that is used at runtime. It contains static configuration
|
// RuntimeConfig is the configuration that is used at runtime. It contains static configuration
|
||||||
@@ -152,6 +162,10 @@ func initConfig() {
|
|||||||
globalConfig.eventLogDir = eventLogDir
|
globalConfig.eventLogDir = eventLogDir
|
||||||
|
|
||||||
loadConfig()
|
loadConfig()
|
||||||
|
|
||||||
|
if err := preprocessTrustedPackages(&globalConfig.Config); err != nil {
|
||||||
|
log.Warnf("Failed to preprocess trusted packages: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadConfig loads the configuration from the config file.
|
// loadConfig loads the configuration from the config file.
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
|
||||||
|
"github.com/safedep/dry/api/pb"
|
||||||
|
"github.com/safedep/dry/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsTrustedPackage checks if a package version is trusted based on global configuration.
|
||||||
|
// This is the primary API that should be used by guard and proxy flows.
|
||||||
|
// It returns true if the package is in the trusted packages list, false otherwise.
|
||||||
|
func IsTrustedPackage(pkgVersion *packagev1.PackageVersion) bool {
|
||||||
|
return isTrustedPackageVersion(Get().Config.TrustedPackages, pkgVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
// preprocessTrustedPackages pre-parses all PURL strings in trusted packages.
|
||||||
|
// This is called once during config load to avoid repeated parsing during
|
||||||
|
// trusted package checks. Invalid PURLs are logged but not fatal.
|
||||||
|
func preprocessTrustedPackages(cfg *Config) error {
|
||||||
|
for i := range cfg.TrustedPackages {
|
||||||
|
tp := &cfg.TrustedPackages[i]
|
||||||
|
|
||||||
|
parsedPurl, err := pb.NewPurlPackageVersion(tp.Purl)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to parse trusted package PURL: %s: %v", tp.Purl, err)
|
||||||
|
tp.parsed = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tp.parsed = true
|
||||||
|
tp.ecosystem = parsedPurl.Ecosystem()
|
||||||
|
tp.name = parsedPurl.Name()
|
||||||
|
tp.version = parsedPurl.Version()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isTrustedPackageVersion checks if a package version is in the trusted packages list.
|
||||||
|
//
|
||||||
|
// It matches based on ecosystem, package name, and optionally version.
|
||||||
|
// If the trusted package PURL doesn't specify a version, all versions of that package are trusted.
|
||||||
|
// Returns false if pkgVersion is nil or if trustedPackages is empty.
|
||||||
|
func isTrustedPackageVersion(trustedPackages []TrustedPackage, pkgVersion *packagev1.PackageVersion) bool {
|
||||||
|
if pkgVersion == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(trustedPackages) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range trustedPackages {
|
||||||
|
if !v.parsed {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.ecosystem != pkgVersion.GetPackage().GetEcosystem() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.name != pkgVersion.GetPackage().GetName() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.version != "" && v.version != pkgVersion.GetVersion() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -0,0 +1,267 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIsTrustedPackageVersion(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
trustedPackages []TrustedPackage
|
||||||
|
pkgVersion *packagev1.PackageVersion
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil package version returns false",
|
||||||
|
trustedPackages: []TrustedPackage{},
|
||||||
|
pkgVersion: nil,
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty trusted packages list returns false",
|
||||||
|
trustedPackages: []TrustedPackage{},
|
||||||
|
pkgVersion: &packagev1.PackageVersion{
|
||||||
|
Package: &packagev1.Package{
|
||||||
|
Name: "express",
|
||||||
|
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
||||||
|
},
|
||||||
|
Version: "4.18.0",
|
||||||
|
},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "exact match with version returns true",
|
||||||
|
trustedPackages: []TrustedPackage{
|
||||||
|
{
|
||||||
|
Purl: "pkg:npm/express@4.18.0",
|
||||||
|
Reason: "trusted by team",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pkgVersion: &packagev1.PackageVersion{
|
||||||
|
Package: &packagev1.Package{
|
||||||
|
Name: "express",
|
||||||
|
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
||||||
|
},
|
||||||
|
Version: "4.18.0",
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "match without version in trusted package returns true",
|
||||||
|
trustedPackages: []TrustedPackage{
|
||||||
|
{
|
||||||
|
Purl: "pkg:npm/express",
|
||||||
|
Reason: "all versions trusted",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pkgVersion: &packagev1.PackageVersion{
|
||||||
|
Package: &packagev1.Package{
|
||||||
|
Name: "express",
|
||||||
|
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
||||||
|
},
|
||||||
|
Version: "4.18.0",
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "version mismatch returns false",
|
||||||
|
trustedPackages: []TrustedPackage{
|
||||||
|
{
|
||||||
|
Purl: "pkg:npm/express@4.17.0",
|
||||||
|
Reason: "old version trusted",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pkgVersion: &packagev1.PackageVersion{
|
||||||
|
Package: &packagev1.Package{
|
||||||
|
Name: "express",
|
||||||
|
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
||||||
|
},
|
||||||
|
Version: "4.18.0",
|
||||||
|
},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "name mismatch returns false",
|
||||||
|
trustedPackages: []TrustedPackage{
|
||||||
|
{
|
||||||
|
Purl: "pkg:npm/react@18.0.0",
|
||||||
|
Reason: "trusted package",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pkgVersion: &packagev1.PackageVersion{
|
||||||
|
Package: &packagev1.Package{
|
||||||
|
Name: "express",
|
||||||
|
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
||||||
|
},
|
||||||
|
Version: "4.18.0",
|
||||||
|
},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ecosystem mismatch returns false",
|
||||||
|
trustedPackages: []TrustedPackage{
|
||||||
|
{
|
||||||
|
Purl: "pkg:pypi/requests@2.28.0",
|
||||||
|
Reason: "trusted package",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pkgVersion: &packagev1.PackageVersion{
|
||||||
|
Package: &packagev1.Package{
|
||||||
|
Name: "requests",
|
||||||
|
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
||||||
|
},
|
||||||
|
Version: "2.28.0",
|
||||||
|
},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "pypi package exact match returns true",
|
||||||
|
trustedPackages: []TrustedPackage{
|
||||||
|
{
|
||||||
|
Purl: "pkg:pypi/requests@2.28.0",
|
||||||
|
Reason: "trusted http library",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pkgVersion: &packagev1.PackageVersion{
|
||||||
|
Package: &packagev1.Package{
|
||||||
|
Name: "requests",
|
||||||
|
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_PYPI,
|
||||||
|
},
|
||||||
|
Version: "2.28.0",
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple trusted packages finds correct match",
|
||||||
|
trustedPackages: []TrustedPackage{
|
||||||
|
{
|
||||||
|
Purl: "pkg:npm/lodash@4.17.21",
|
||||||
|
Reason: "utility library",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Purl: "pkg:npm/express@4.18.0",
|
||||||
|
Reason: "web framework",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Purl: "pkg:pypi/requests@2.28.0",
|
||||||
|
Reason: "http library",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pkgVersion: &packagev1.PackageVersion{
|
||||||
|
Package: &packagev1.Package{
|
||||||
|
Name: "express",
|
||||||
|
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
||||||
|
},
|
||||||
|
Version: "4.18.0",
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple trusted packages no match returns false",
|
||||||
|
trustedPackages: []TrustedPackage{
|
||||||
|
{
|
||||||
|
Purl: "pkg:npm/lodash@4.17.21",
|
||||||
|
Reason: "utility library",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Purl: "pkg:npm/react@18.0.0",
|
||||||
|
Reason: "ui library",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pkgVersion: &packagev1.PackageVersion{
|
||||||
|
Package: &packagev1.Package{
|
||||||
|
Name: "express",
|
||||||
|
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
||||||
|
},
|
||||||
|
Version: "4.18.0",
|
||||||
|
},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid purl in trusted packages skips and returns false",
|
||||||
|
trustedPackages: []TrustedPackage{
|
||||||
|
{
|
||||||
|
Purl: "invalid-purl-format",
|
||||||
|
Reason: "malformed",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pkgVersion: &packagev1.PackageVersion{
|
||||||
|
Package: &packagev1.Package{
|
||||||
|
Name: "express",
|
||||||
|
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
||||||
|
},
|
||||||
|
Version: "4.18.0",
|
||||||
|
},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid purl skipped but valid match found",
|
||||||
|
trustedPackages: []TrustedPackage{
|
||||||
|
{
|
||||||
|
Purl: "invalid-purl-format",
|
||||||
|
Reason: "malformed",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Purl: "pkg:npm/express@4.18.0",
|
||||||
|
Reason: "valid trusted package",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pkgVersion: &packagev1.PackageVersion{
|
||||||
|
Package: &packagev1.Package{
|
||||||
|
Name: "express",
|
||||||
|
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
||||||
|
},
|
||||||
|
Version: "4.18.0",
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "package version without version field matches versionless trusted package",
|
||||||
|
trustedPackages: []TrustedPackage{
|
||||||
|
{
|
||||||
|
Purl: "pkg:npm/express",
|
||||||
|
Reason: "all versions trusted",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pkgVersion: &packagev1.PackageVersion{
|
||||||
|
Package: &packagev1.Package{
|
||||||
|
Name: "express",
|
||||||
|
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
||||||
|
},
|
||||||
|
Version: "",
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "package version without version field does not match versioned trusted package",
|
||||||
|
trustedPackages: []TrustedPackage{
|
||||||
|
{
|
||||||
|
Purl: "pkg:npm/express@4.18.0",
|
||||||
|
Reason: "specific version trusted",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pkgVersion: &packagev1.PackageVersion{
|
||||||
|
Package: &packagev1.Package{
|
||||||
|
Name: "express",
|
||||||
|
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
||||||
|
},
|
||||||
|
Version: "",
|
||||||
|
},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// Pre-process trusted packages to populate pre-parsed fields
|
||||||
|
cfg := &Config{TrustedPackages: tt.trustedPackages}
|
||||||
|
_ = preprocessTrustedPackages(cfg)
|
||||||
|
|
||||||
|
got := isTrustedPackageVersion(cfg.TrustedPackages, tt.pkgVersion)
|
||||||
|
assert.Equal(t, tt.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-39
@@ -10,7 +10,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
|
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
|
||||||
"github.com/safedep/dry/api/pb"
|
|
||||||
"github.com/safedep/dry/log"
|
"github.com/safedep/dry/log"
|
||||||
"github.com/safedep/pmg/analyzer"
|
"github.com/safedep/pmg/analyzer"
|
||||||
"github.com/safedep/pmg/config"
|
"github.com/safedep/pmg/config"
|
||||||
@@ -45,7 +44,6 @@ type PackageManagerGuardConfig struct {
|
|||||||
AnalysisTimeout time.Duration
|
AnalysisTimeout time.Duration
|
||||||
DryRun bool
|
DryRun bool
|
||||||
InsecureInstallation bool
|
InsecureInstallation bool
|
||||||
TrustedPackages []config.TrustedPackage
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultPackageManagerGuardConfig() PackageManagerGuardConfig {
|
func DefaultPackageManagerGuardConfig() PackageManagerGuardConfig {
|
||||||
@@ -55,45 +53,9 @@ func DefaultPackageManagerGuardConfig() PackageManagerGuardConfig {
|
|||||||
AnalysisTimeout: 5 * time.Minute,
|
AnalysisTimeout: 5 * time.Minute,
|
||||||
DryRun: false,
|
DryRun: false,
|
||||||
InsecureInstallation: false,
|
InsecureInstallation: false,
|
||||||
TrustedPackages: []config.TrustedPackage{},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PackageManagerGuardConfig) IsTrustedPackageVersion(pkgVersion *packagev1.PackageVersion) bool {
|
|
||||||
if pkgVersion == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
trustedPkgs := c.TrustedPackages
|
|
||||||
if len(trustedPkgs) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range trustedPkgs {
|
|
||||||
purlTrustedPackageVersion, err := pb.NewPurlPackageVersion(v.Purl)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("failed to parse trusted package version: %s: %v", v.Purl, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if purlTrustedPackageVersion.Version() != "" && purlTrustedPackageVersion.Version() != pkgVersion.GetVersion() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if purlTrustedPackageVersion.Name() != pkgVersion.GetPackage().GetName() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if purlTrustedPackageVersion.Ecosystem() != pkgVersion.GetPackage().GetEcosystem() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type packageManagerGuard struct {
|
type packageManagerGuard struct {
|
||||||
config PackageManagerGuardConfig
|
config PackageManagerGuardConfig
|
||||||
interaction PackageManagerGuardInteraction
|
interaction PackageManagerGuardInteraction
|
||||||
@@ -293,7 +255,7 @@ func (g *packageManagerGuard) concurrentAnalyzePackages(ctx context.Context,
|
|||||||
|
|
||||||
// Queue all packages for analysis
|
// Queue all packages for analysis
|
||||||
for _, pkg := range packages {
|
for _, pkg := range packages {
|
||||||
if g.config.IsTrustedPackageVersion(pkg) {
|
if config.IsTrustedPackage(pkg) {
|
||||||
log.Debugf("Skipping trusted package: %s/%s@%s",
|
log.Debugf("Skipping trusted package: %s/%s@%s",
|
||||||
pkg.GetPackage().GetEcosystem(), pkg.GetPackage().GetName(), pkg.GetVersion())
|
pkg.GetPackage().GetEcosystem(), pkg.GetPackage().GetName(), pkg.GetVersion())
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
|
|
||||||
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
|
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
|
||||||
"github.com/safedep/pmg/analyzer"
|
"github.com/safedep/pmg/analyzer"
|
||||||
"github.com/safedep/pmg/config"
|
|
||||||
"github.com/safedep/pmg/internal/ui"
|
"github.com/safedep/pmg/internal/ui"
|
||||||
"github.com/safedep/pmg/packagemanager"
|
"github.com/safedep/pmg/packagemanager"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -270,261 +269,3 @@ func TestGuardInsecureInstallation(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGuardIsTrustedPackageVersion(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
trustedPackages []config.TrustedPackage
|
|
||||||
pkgVersion *packagev1.PackageVersion
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "nil package version returns false",
|
|
||||||
trustedPackages: []config.TrustedPackage{},
|
|
||||||
pkgVersion: nil,
|
|
||||||
want: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "empty trusted packages list returns false",
|
|
||||||
trustedPackages: []config.TrustedPackage{},
|
|
||||||
pkgVersion: &packagev1.PackageVersion{
|
|
||||||
Package: &packagev1.Package{
|
|
||||||
Name: "express",
|
|
||||||
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
|
||||||
},
|
|
||||||
Version: "4.18.0",
|
|
||||||
},
|
|
||||||
want: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "exact match with version returns true",
|
|
||||||
trustedPackages: []config.TrustedPackage{
|
|
||||||
{
|
|
||||||
Purl: "pkg:npm/express@4.18.0",
|
|
||||||
Reason: "trusted by team",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pkgVersion: &packagev1.PackageVersion{
|
|
||||||
Package: &packagev1.Package{
|
|
||||||
Name: "express",
|
|
||||||
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
|
||||||
},
|
|
||||||
Version: "4.18.0",
|
|
||||||
},
|
|
||||||
want: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "match without version in trusted package returns true",
|
|
||||||
trustedPackages: []config.TrustedPackage{
|
|
||||||
{
|
|
||||||
Purl: "pkg:npm/express",
|
|
||||||
Reason: "all versions trusted",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pkgVersion: &packagev1.PackageVersion{
|
|
||||||
Package: &packagev1.Package{
|
|
||||||
Name: "express",
|
|
||||||
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
|
||||||
},
|
|
||||||
Version: "4.18.0",
|
|
||||||
},
|
|
||||||
want: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "version mismatch returns false",
|
|
||||||
trustedPackages: []config.TrustedPackage{
|
|
||||||
{
|
|
||||||
Purl: "pkg:npm/express@4.17.0",
|
|
||||||
Reason: "old version trusted",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pkgVersion: &packagev1.PackageVersion{
|
|
||||||
Package: &packagev1.Package{
|
|
||||||
Name: "express",
|
|
||||||
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
|
||||||
},
|
|
||||||
Version: "4.18.0",
|
|
||||||
},
|
|
||||||
want: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "name mismatch returns false",
|
|
||||||
trustedPackages: []config.TrustedPackage{
|
|
||||||
{
|
|
||||||
Purl: "pkg:npm/react@18.0.0",
|
|
||||||
Reason: "trusted package",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pkgVersion: &packagev1.PackageVersion{
|
|
||||||
Package: &packagev1.Package{
|
|
||||||
Name: "express",
|
|
||||||
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
|
||||||
},
|
|
||||||
Version: "4.18.0",
|
|
||||||
},
|
|
||||||
want: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "ecosystem mismatch returns false",
|
|
||||||
trustedPackages: []config.TrustedPackage{
|
|
||||||
{
|
|
||||||
Purl: "pkg:pypi/requests@2.28.0",
|
|
||||||
Reason: "trusted package",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pkgVersion: &packagev1.PackageVersion{
|
|
||||||
Package: &packagev1.Package{
|
|
||||||
Name: "requests",
|
|
||||||
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
|
||||||
},
|
|
||||||
Version: "2.28.0",
|
|
||||||
},
|
|
||||||
want: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "pypi package exact match returns true",
|
|
||||||
trustedPackages: []config.TrustedPackage{
|
|
||||||
{
|
|
||||||
Purl: "pkg:pypi/requests@2.28.0",
|
|
||||||
Reason: "trusted http library",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pkgVersion: &packagev1.PackageVersion{
|
|
||||||
Package: &packagev1.Package{
|
|
||||||
Name: "requests",
|
|
||||||
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_PYPI,
|
|
||||||
},
|
|
||||||
Version: "2.28.0",
|
|
||||||
},
|
|
||||||
want: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "multiple trusted packages finds correct match",
|
|
||||||
trustedPackages: []config.TrustedPackage{
|
|
||||||
{
|
|
||||||
Purl: "pkg:npm/lodash@4.17.21",
|
|
||||||
Reason: "utility library",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Purl: "pkg:npm/express@4.18.0",
|
|
||||||
Reason: "web framework",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Purl: "pkg:pypi/requests@2.28.0",
|
|
||||||
Reason: "http library",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pkgVersion: &packagev1.PackageVersion{
|
|
||||||
Package: &packagev1.Package{
|
|
||||||
Name: "express",
|
|
||||||
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
|
||||||
},
|
|
||||||
Version: "4.18.0",
|
|
||||||
},
|
|
||||||
want: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "multiple trusted packages no match returns false",
|
|
||||||
trustedPackages: []config.TrustedPackage{
|
|
||||||
{
|
|
||||||
Purl: "pkg:npm/lodash@4.17.21",
|
|
||||||
Reason: "utility library",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Purl: "pkg:npm/react@18.0.0",
|
|
||||||
Reason: "ui library",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pkgVersion: &packagev1.PackageVersion{
|
|
||||||
Package: &packagev1.Package{
|
|
||||||
Name: "express",
|
|
||||||
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
|
||||||
},
|
|
||||||
Version: "4.18.0",
|
|
||||||
},
|
|
||||||
want: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid purl in trusted packages skips and returns false",
|
|
||||||
trustedPackages: []config.TrustedPackage{
|
|
||||||
{
|
|
||||||
Purl: "invalid-purl-format",
|
|
||||||
Reason: "malformed",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pkgVersion: &packagev1.PackageVersion{
|
|
||||||
Package: &packagev1.Package{
|
|
||||||
Name: "express",
|
|
||||||
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
|
||||||
},
|
|
||||||
Version: "4.18.0",
|
|
||||||
},
|
|
||||||
want: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid purl skipped but valid match found",
|
|
||||||
trustedPackages: []config.TrustedPackage{
|
|
||||||
{
|
|
||||||
Purl: "invalid-purl-format",
|
|
||||||
Reason: "malformed",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Purl: "pkg:npm/express@4.18.0",
|
|
||||||
Reason: "valid trusted package",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pkgVersion: &packagev1.PackageVersion{
|
|
||||||
Package: &packagev1.Package{
|
|
||||||
Name: "express",
|
|
||||||
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
|
||||||
},
|
|
||||||
Version: "4.18.0",
|
|
||||||
},
|
|
||||||
want: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "package version without version field matches versionless trusted package",
|
|
||||||
trustedPackages: []config.TrustedPackage{
|
|
||||||
{
|
|
||||||
Purl: "pkg:npm/express",
|
|
||||||
Reason: "all versions trusted",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pkgVersion: &packagev1.PackageVersion{
|
|
||||||
Package: &packagev1.Package{
|
|
||||||
Name: "express",
|
|
||||||
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
|
||||||
},
|
|
||||||
Version: "",
|
|
||||||
},
|
|
||||||
want: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "package version without version field does not match versioned trusted package",
|
|
||||||
trustedPackages: []config.TrustedPackage{
|
|
||||||
{
|
|
||||||
Purl: "pkg:npm/express@4.18.0",
|
|
||||||
Reason: "specific version trusted",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
pkgVersion: &packagev1.PackageVersion{
|
|
||||||
Package: &packagev1.Package{
|
|
||||||
Name: "express",
|
|
||||||
Ecosystem: packagev1.Ecosystem_ECOSYSTEM_NPM,
|
|
||||||
},
|
|
||||||
Version: "",
|
|
||||||
},
|
|
||||||
want: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
config := PackageManagerGuardConfig{
|
|
||||||
TrustedPackages: tt.trustedPackages,
|
|
||||||
}
|
|
||||||
|
|
||||||
got := config.IsTrustedPackageVersion(tt.pkgVersion)
|
|
||||||
assert.Equal(t, tt.want, got)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ func (f *commonFlow) Run(ctx context.Context, args []string, parsedCmd *packagem
|
|||||||
guardConfig := guard.DefaultPackageManagerGuardConfig()
|
guardConfig := guard.DefaultPackageManagerGuardConfig()
|
||||||
guardConfig.DryRun = config.DryRun
|
guardConfig.DryRun = config.DryRun
|
||||||
guardConfig.InsecureInstallation = config.InsecureInstallation
|
guardConfig.InsecureInstallation = config.InsecureInstallation
|
||||||
guardConfig.TrustedPackages = config.Config.TrustedPackages
|
|
||||||
|
|
||||||
proxy, err := guard.NewPackageManagerGuard(guardConfig, f.pm, f.packageResolver, analyzers, interaction)
|
proxy, err := guard.NewPackageManagerGuard(guardConfig, f.pm, f.packageResolver, analyzers, interaction)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
|
packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1"
|
||||||
"github.com/safedep/dry/log"
|
"github.com/safedep/dry/log"
|
||||||
"github.com/safedep/pmg/analyzer"
|
"github.com/safedep/pmg/analyzer"
|
||||||
|
"github.com/safedep/pmg/config"
|
||||||
"github.com/safedep/pmg/guard"
|
"github.com/safedep/pmg/guard"
|
||||||
"github.com/safedep/pmg/proxy"
|
"github.com/safedep/pmg/proxy"
|
||||||
)
|
)
|
||||||
@@ -47,11 +48,7 @@ func (b *baseRegistryInterceptor) analyzePackage(
|
|||||||
packageName string,
|
packageName string,
|
||||||
packageVersion string,
|
packageVersion string,
|
||||||
) (*analyzer.PackageVersionAnalysisResult, error) {
|
) (*analyzer.PackageVersionAnalysisResult, error) {
|
||||||
if cached, ok := b.cache.Get(ecosystem.String(), packageName, packageVersion); ok {
|
// Check if package is trusted before analyzing
|
||||||
log.Debugf("[%s] Using cached analysis result for %s@%s", ctx.RequestID, packageName, packageVersion)
|
|
||||||
return cached, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
pkgVersion := &packagev1.PackageVersion{
|
pkgVersion := &packagev1.PackageVersion{
|
||||||
Package: &packagev1.Package{
|
Package: &packagev1.Package{
|
||||||
Ecosystem: ecosystem,
|
Ecosystem: ecosystem,
|
||||||
@@ -60,6 +57,21 @@ func (b *baseRegistryInterceptor) analyzePackage(
|
|||||||
Version: packageVersion,
|
Version: packageVersion,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.IsTrustedPackage(pkgVersion) {
|
||||||
|
log.Debugf("[%s] Skipping trusted package: %s/%s@%s",
|
||||||
|
ctx.RequestID, ecosystem.String(), packageName, packageVersion)
|
||||||
|
|
||||||
|
return &analyzer.PackageVersionAnalysisResult{
|
||||||
|
PackageVersion: pkgVersion,
|
||||||
|
Action: analyzer.ActionAllow,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if cached, ok := b.cache.Get(ecosystem.String(), packageName, packageVersion); ok {
|
||||||
|
log.Debugf("[%s] Using cached analysis result for %s@%s", ctx.RequestID, packageName, packageVersion)
|
||||||
|
return cached, nil
|
||||||
|
}
|
||||||
|
|
||||||
log.Debugf("[%s] Analyzing package %s@%s", ctx.RequestID, packageName, packageVersion)
|
log.Debugf("[%s] Analyzing package %s@%s", ctx.RequestID, packageName, packageVersion)
|
||||||
|
|
||||||
analysisCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
analysisCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
|
|||||||
Reference in New Issue
Block a user