update config handling and add support for removing config

This commit is contained in:
Sahilb315
2025-12-17 15:41:04 +05:30
parent 3a1afccb9e
commit 0d5bdefe04
6 changed files with 139 additions and 22 deletions
+14 -2
View File
@@ -1,6 +1,7 @@
package setup
import (
"errors"
"fmt"
"github.com/safedep/pmg/config"
@@ -34,9 +35,15 @@ func NewInstallCommand() *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
fmt.Print(ui.GeneratePMGBanner(version.Version, version.Commit))
_, err := config.CreatePmgConfigDir()
cfgPath, err := config.CreateConfig()
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()
@@ -58,6 +65,11 @@ func NewRemoveCommand() *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
fmt.Print(ui.GeneratePMGBanner(version.Version, version.Commit))
err := config.RemovePmgConfigDir()
if err != nil {
return err
}
config := alias.DefaultConfig()
rcFileManager, err := alias.NewDefaultRcFileManager(config.RcFileName)
if err != nil {
+100 -20
View File
@@ -2,8 +2,10 @@ package config
import (
"context"
"errors"
"fmt"
"strings"
"sync"
"github.com/spf13/pflag"
"github.com/spf13/viper"
@@ -29,32 +31,47 @@ type Config struct {
InsecureInstallation bool `mapstructure:"insecure_installation"`
// 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) {
dir, err := PmgConfigDir()
if err != nil {
if err := ensureViperConfigured(); err != nil {
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()
if err != nil {
return "", err
@@ -62,6 +79,31 @@ func SetupViper() (string, error) {
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) {
if fs == nil {
return
@@ -104,6 +146,44 @@ func Load(fs *pflag.FlagSet) (Config, error) {
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
func (c Config) Inject(ctx context.Context) context.Context {
return context.WithValue(ctx, configKey{}, &contextValue{Config: c})
+13
View File
@@ -46,6 +46,19 @@ func CreatePmgConfigDir() (string, error) {
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),
// without creating any directories.
func ConfigFilePath() (string, error) {
+10
View File
@@ -42,6 +42,7 @@ type PackageManagerGuardConfig struct {
AnalysisTimeout time.Duration
DryRun bool
InsecureInstallation bool
TrustedPackages map[string][]string
}
func DefaultPackageManagerGuardConfig() PackageManagerGuardConfig {
@@ -51,6 +52,7 @@ func DefaultPackageManagerGuardConfig() PackageManagerGuardConfig {
AnalysisTimeout: 5 * time.Minute,
DryRun: 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 {
ecosystem := result.PackageVersion.Package.Ecosystem
for k, v := range g.config.TrustedPackages {
if k == ecosystem.String() {
// Check for packages
_ = v
}
}
confirmableMalwarePackages = append(confirmableMalwarePackages, result)
}
}
+1
View File
@@ -58,6 +58,7 @@ func (f *commonFlow) Run(ctx context.Context, args []string, parsedCmd *packagem
guardConfig := guard.DefaultPackageManagerGuardConfig()
guardConfig.DryRun = f.config.DryRun
guardConfig.InsecureInstallation = f.config.InsecureInstallation
guardConfig.TrustedPackages = f.config.TrustedPackages
proxy, err := guard.NewPackageManagerGuard(guardConfig, f.pm, f.packageResolver, analyzers, interaction)
if err != nil {
+1
View File
@@ -63,6 +63,7 @@ func main() {
}
globalConfig = cfg
fmt.Printf("Config: %+v", globalConfig)
log.InitZapLogger("pmg", "cli")
cmd.SetContext(globalConfig.Inject(cmd.Context()))
},