mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
update config handling and add support for removing config
This commit is contained in:
+14
-2
@@ -1,6 +1,7 @@
|
|||||||
package setup
|
package setup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/safedep/pmg/config"
|
"github.com/safedep/pmg/config"
|
||||||
@@ -34,9 +35,15 @@ func NewInstallCommand() *cobra.Command {
|
|||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
fmt.Print(ui.GeneratePMGBanner(version.Version, version.Commit))
|
fmt.Print(ui.GeneratePMGBanner(version.Version, version.Commit))
|
||||||
|
|
||||||
_, err := config.CreatePmgConfigDir()
|
cfgPath, err := config.CreateConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create config dir: %s", err.Error())
|
if errors.Is(err, config.ErrConfigAlreadyExists) {
|
||||||
|
fmt.Printf("PMG config already exists at %s\n", cfgPath)
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("failed to create config file: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("📄 PMG config created at %s\n", cfgPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := alias.DefaultConfig()
|
cfg := alias.DefaultConfig()
|
||||||
@@ -58,6 +65,11 @@ func NewRemoveCommand() *cobra.Command {
|
|||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
fmt.Print(ui.GeneratePMGBanner(version.Version, version.Commit))
|
fmt.Print(ui.GeneratePMGBanner(version.Version, version.Commit))
|
||||||
|
|
||||||
|
err := config.RemovePmgConfigDir()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
config := alias.DefaultConfig()
|
config := alias.DefaultConfig()
|
||||||
rcFileManager, err := alias.NewDefaultRcFileManager(config.RcFileName)
|
rcFileManager, err := alias.NewDefaultRcFileManager(config.RcFileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+100
-20
@@ -2,8 +2,10 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
@@ -29,32 +31,47 @@ type Config struct {
|
|||||||
InsecureInstallation bool `mapstructure:"insecure_installation"`
|
InsecureInstallation bool `mapstructure:"insecure_installation"`
|
||||||
|
|
||||||
// TrustedPackages allows for trusting an suspicious package and ignoring the suspicious behaviour for the package in future installations
|
// TrustedPackages allows for trusting an suspicious package and ignoring the suspicious behaviour for the package in future installations
|
||||||
TrustedPackages []string `mapstructure:"trusted_packages"`
|
TrustedPackages map[string][]string `mapstructure:"trusted_packages"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
setupOnce sync.Once
|
||||||
|
setupErr error
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrConfigAlreadyExists is returned when creating the config without force and it already exists.
|
||||||
|
var ErrConfigAlreadyExists = errors.New("pmg config already exists")
|
||||||
|
|
||||||
|
// DefaultConfig returns the canonical default configuration used by PMG.
|
||||||
|
func DefaultConfig() Config {
|
||||||
|
return Config{
|
||||||
|
Transitive: true,
|
||||||
|
TransitiveDepth: 5,
|
||||||
|
IncludeDevDependencies: false,
|
||||||
|
Paranoid: false,
|
||||||
|
DryRun: false,
|
||||||
|
InsecureInstallation: false,
|
||||||
|
TrustedPackages: map[string][]string{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configAsMap(cfg Config) map[string]any {
|
||||||
|
return map[string]any{
|
||||||
|
"transitive": cfg.Transitive,
|
||||||
|
"transitive_depth": cfg.TransitiveDepth,
|
||||||
|
"include_dev_dependencies": cfg.IncludeDevDependencies,
|
||||||
|
"dry_run": cfg.DryRun,
|
||||||
|
"paranoid": cfg.Paranoid,
|
||||||
|
"insecure_installation": cfg.InsecureInstallation,
|
||||||
|
"trusted_packages": cfg.TrustedPackages,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetupViper() (string, error) {
|
func SetupViper() (string, error) {
|
||||||
dir, err := PmgConfigDir()
|
if err := ensureViperConfigured(); err != nil {
|
||||||
if err != nil {
|
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
viper.SetConfigName(pmgConfigName)
|
|
||||||
viper.SetConfigType(pmgConfigType)
|
|
||||||
viper.AddConfigPath(dir)
|
|
||||||
|
|
||||||
viper.SetEnvPrefix("PMG")
|
|
||||||
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
|
||||||
viper.AutomaticEnv()
|
|
||||||
|
|
||||||
// Defaults
|
|
||||||
viper.SetDefault("transitive", true)
|
|
||||||
viper.SetDefault("transitive_depth", 5)
|
|
||||||
viper.SetDefault("include_dev_dependencies", false)
|
|
||||||
viper.SetDefault("dry_run", false)
|
|
||||||
viper.SetDefault("paranoid", false)
|
|
||||||
viper.SetDefault("insecure_installation", false)
|
|
||||||
viper.SetDefault("trusted_packages", []string{})
|
|
||||||
|
|
||||||
cfgPath, err := ConfigFilePath()
|
cfgPath, err := ConfigFilePath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -62,6 +79,31 @@ func SetupViper() (string, error) {
|
|||||||
return cfgPath, nil
|
return cfgPath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ensureViperConfigured() error {
|
||||||
|
setupOnce.Do(func() {
|
||||||
|
dir, err := PmgConfigDir()
|
||||||
|
if err != nil {
|
||||||
|
setupErr = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
v := viper.GetViper()
|
||||||
|
v.SetConfigName(pmgConfigName)
|
||||||
|
v.SetConfigType(pmgConfigType)
|
||||||
|
v.AddConfigPath(dir)
|
||||||
|
|
||||||
|
v.SetEnvPrefix("PMG")
|
||||||
|
v.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
|
||||||
|
v.AutomaticEnv()
|
||||||
|
|
||||||
|
for key, value := range configAsMap(DefaultConfig()) {
|
||||||
|
v.SetDefault(key, value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return setupErr
|
||||||
|
}
|
||||||
|
|
||||||
func BindFlags(fs *pflag.FlagSet) {
|
func BindFlags(fs *pflag.FlagSet) {
|
||||||
if fs == nil {
|
if fs == nil {
|
||||||
return
|
return
|
||||||
@@ -104,6 +146,44 @@ func Load(fs *pflag.FlagSet) (Config, error) {
|
|||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateConfig writes the PMG config file and returns its absolute path.
|
||||||
|
func CreateConfig() (string, error) {
|
||||||
|
if _, err := CreatePmgConfigDir(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
cfgFile, err := ConfigFilePath()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
writer := viper.New()
|
||||||
|
writer.SetConfigType(pmgConfigType)
|
||||||
|
|
||||||
|
defaults := DefaultConfig()
|
||||||
|
if err := writer.MergeConfigMap(configAsMap(defaults)); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to prepare default config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
writeErr := writer.WriteConfigAs(cfgFile)
|
||||||
|
|
||||||
|
if writeErr != nil {
|
||||||
|
var alreadyExistsErr viper.ConfigFileAlreadyExistsError
|
||||||
|
if errors.As(writeErr, &alreadyExistsErr) {
|
||||||
|
return cfgFile, ErrConfigAlreadyExists
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("error writing config file: %w", writeErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ensureViperConfigured(); err == nil {
|
||||||
|
for key, value := range configAsMap(defaults) {
|
||||||
|
viper.Set(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfgFile, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Inject config into context while protecting against context poisoning
|
// Inject config into context while protecting against context poisoning
|
||||||
func (c Config) Inject(ctx context.Context) context.Context {
|
func (c Config) Inject(ctx context.Context) context.Context {
|
||||||
return context.WithValue(ctx, configKey{}, &contextValue{Config: c})
|
return context.WithValue(ctx, configKey{}, &contextValue{Config: c})
|
||||||
|
|||||||
@@ -46,6 +46,19 @@ func CreatePmgConfigDir() (string, error) {
|
|||||||
return dir, nil
|
return dir, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemovePmgConfigDir removes the PMG configuration directory and its contents.
|
||||||
|
func RemovePmgConfigDir() error {
|
||||||
|
dir, err := PmgConfigDir()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.RemoveAll(dir); err != nil {
|
||||||
|
return fmt.Errorf("failed to remove config directory %s: %w", dir, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ConfigFilePath returns the absolute path to the main PMG config file (e.g., config.yml),
|
// ConfigFilePath returns the absolute path to the main PMG config file (e.g., config.yml),
|
||||||
// without creating any directories.
|
// without creating any directories.
|
||||||
func ConfigFilePath() (string, error) {
|
func ConfigFilePath() (string, error) {
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ type PackageManagerGuardConfig struct {
|
|||||||
AnalysisTimeout time.Duration
|
AnalysisTimeout time.Duration
|
||||||
DryRun bool
|
DryRun bool
|
||||||
InsecureInstallation bool
|
InsecureInstallation bool
|
||||||
|
TrustedPackages map[string][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultPackageManagerGuardConfig() PackageManagerGuardConfig {
|
func DefaultPackageManagerGuardConfig() PackageManagerGuardConfig {
|
||||||
@@ -51,6 +52,7 @@ func DefaultPackageManagerGuardConfig() PackageManagerGuardConfig {
|
|||||||
AnalysisTimeout: 5 * time.Minute,
|
AnalysisTimeout: 5 * time.Minute,
|
||||||
DryRun: false,
|
DryRun: false,
|
||||||
InsecureInstallation: false,
|
InsecureInstallation: false,
|
||||||
|
TrustedPackages: map[string][]string{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,6 +156,14 @@ func (g *packageManagerGuard) Run(ctx context.Context, args []string, parsedComm
|
|||||||
}
|
}
|
||||||
|
|
||||||
if result.Action == analyzer.ActionConfirm {
|
if result.Action == analyzer.ActionConfirm {
|
||||||
|
ecosystem := result.PackageVersion.Package.Ecosystem
|
||||||
|
for k, v := range g.config.TrustedPackages {
|
||||||
|
if k == ecosystem.String() {
|
||||||
|
// Check for packages
|
||||||
|
_ = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
confirmableMalwarePackages = append(confirmableMalwarePackages, result)
|
confirmableMalwarePackages = append(confirmableMalwarePackages, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ func (f *commonFlow) Run(ctx context.Context, args []string, parsedCmd *packagem
|
|||||||
guardConfig := guard.DefaultPackageManagerGuardConfig()
|
guardConfig := guard.DefaultPackageManagerGuardConfig()
|
||||||
guardConfig.DryRun = f.config.DryRun
|
guardConfig.DryRun = f.config.DryRun
|
||||||
guardConfig.InsecureInstallation = f.config.InsecureInstallation
|
guardConfig.InsecureInstallation = f.config.InsecureInstallation
|
||||||
|
guardConfig.TrustedPackages = f.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 {
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
globalConfig = cfg
|
globalConfig = cfg
|
||||||
|
|
||||||
|
fmt.Printf("Config: %+v", globalConfig)
|
||||||
log.InitZapLogger("pmg", "cli")
|
log.InitZapLogger("pmg", "cli")
|
||||||
cmd.SetContext(globalConfig.Inject(cmd.Context()))
|
cmd.SetContext(globalConfig.Inject(cmd.Context()))
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user