Files
pmg/cmd/setup/info.go
T
Abhisek DattaandGitHub b8588e3df4 feat: Add Sandbox Inspection and Debugging Commands (#261)
* feat: add sandbox DX commands

* fix: Linter errors

* fix: Sandbox deny log parsing

* fix: Sandbox docs

* refactor: Maintain SSOT across pkg dependencies

* fix: Linter errors
2026-05-19 14:40:54 +05:30

207 lines
5.8 KiB
Go

package setup
import (
"fmt"
"sort"
"strconv"
"strings"
"github.com/safedep/dry/cloud"
"github.com/safedep/dry/log"
"github.com/safedep/pmg/config"
"github.com/safedep/pmg/internal/alias"
"github.com/safedep/pmg/internal/analytics"
"github.com/safedep/pmg/internal/ui"
"github.com/safedep/pmg/internal/version"
"github.com/safedep/pmg/sandbox/platform"
"github.com/spf13/cobra"
)
func NewInfoCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "info",
Short: "Show information about PMG setup and configuration.",
RunE: func(cmd *cobra.Command, args []string) error {
err := executeSetupInfo()
if err != nil {
ui.ErrorExit(fmt.Errorf("failed to execute setup info: %w", err))
}
return nil
},
}
return cmd
}
func executeSetupInfo() error {
fmt.Print(ui.GeneratePMGBanner(version.Version, version.Commit))
// Configuration section
cfg := config.Get()
configEntries := make(map[string]string)
configEntries["Config File"] = cfg.ConfigFilePath()
configEntries["Proxy Mode"] = strconv.FormatBool(cfg.IsProxyModeEnabled())
configEntries["Proxy Install Only"] = strconv.FormatBool(cfg.Config.Proxy.InstallOnly)
ui.PrintInfoSection("Configuration", configEntries)
// Shell Integration section
aliasCfg := alias.DefaultConfig()
rcFileManager, err := alias.NewDefaultRcFileManager(aliasCfg.RcFileName)
if err != nil {
return fmt.Errorf("failed to create alias manager: %w", err)
}
aliasManager := alias.New(aliasCfg, rcFileManager)
isInstalled, err := aliasManager.IsInstalled()
if err != nil {
isInstalled = false
}
shellEntries := make(map[string]string)
shell, err := alias.DetectShell()
if err != nil {
shell = "unknown"
}
shellEntries["Detected Shell"] = shell
shellEntries["Alias Installed"] = strconv.FormatBool(isInstalled)
ui.PrintInfoSection("Shell Integration", shellEntries)
// Security section
securityEntries := make(map[string]string)
trustedPackages := cfg.Config.TrustedPackages
trustedPackagesCount := len(trustedPackages)
if trustedPackagesCount > 3 {
purls := []string{}
for _, p := range trustedPackages[0:3] {
purls = append(purls, p.Purl)
}
trustedPackagesValue := fmt.Sprintf("%v ...and %d more", purls, trustedPackagesCount-3)
securityEntries["Trusted Packages"] = trustedPackagesValue
} else if trustedPackagesCount > 0 {
purls := []string{}
for _, p := range trustedPackages {
purls = append(purls, p.Purl)
}
securityEntries["Trusted Packages"] = fmt.Sprintf("%v", purls)
} else {
securityEntries["Trusted Packages"] = "None"
}
securityEntries["Dependency Cooldown"] = strconv.FormatBool(cfg.Config.DependencyCooldown.Enabled)
securityEntries["Dependency Cooldown Days"] = strconv.Itoa(cfg.Config.DependencyCooldown.Days)
securityEntries["Telemetry"] = strconv.FormatBool(!analytics.IsDisabled())
securityEntries["Event Logging"] = strconv.FormatBool(!cfg.Config.SkipEventLogging)
securityEntries["Event Log Directory"] = cfg.EventLogDir()
ui.PrintInfoSection("Security", securityEntries)
// Sandbox section
sandboxCfg := cfg.Config.Sandbox
sandboxEntries := make(map[string]string)
sandboxEntries["Enabled"] = strconv.FormatBool(sandboxCfg.Enabled)
sandboxEntries["Enforce Always"] = strconv.FormatBool(sandboxCfg.EnforceAlways)
sandboxEntries["Driver"] = resolveSandboxDriverName()
if len(sandboxCfg.Policies) > 0 {
pmNames := make([]string, 0, len(sandboxCfg.Policies))
for name := range sandboxCfg.Policies {
pmNames = append(pmNames, name)
}
sort.Strings(pmNames)
policyParts := make([]string, 0, len(pmNames))
for _, name := range pmNames {
ref := sandboxCfg.Policies[name]
status := "disabled"
if ref.Enabled {
status = ref.Profile
}
policyParts = append(policyParts, fmt.Sprintf("%s(%s)", name, status))
}
sandboxEntries["Policies"] = strings.Join(policyParts, ", ")
} else {
sandboxEntries["Policies"] = "None"
}
ui.PrintInfoSection("Sandbox", sandboxEntries)
if cfg.Config.Cloud.Enabled {
cloudEntries := make(map[string]string)
cloudEntries["Enabled"] = "true"
cloudEntries["Sync DB"] = cfg.CloudSyncDBPath()
if cfg.Config.Cloud.EndpointID != "" {
cloudEntries["Endpoint ID"] = cfg.Config.Cloud.EndpointID
}
cloudEntries["Credentials"] = describeCloudCredentials()
ui.PrintInfoSection("Cloud Sync", cloudEntries)
}
return nil
}
func resolveSandboxDriverName() string {
sb, err := platform.NewSandbox()
if err != nil {
return "unavailable"
}
if !sb.IsAvailable() {
return "unavailable"
}
return string(sb.Name())
}
// describeCloudCredentials reports whether SafeDep Cloud credentials can be
// resolved, and from where. The resolution order matches NewSyncClientBundle:
// keychain first, then environment variables. No network calls are made.
func describeCloudCredentials() string {
if source, ok := tryResolveKeychainCredentials(); ok {
return source
}
if source, ok := tryResolveEnvCredentials(); ok {
return source
}
return "not configured (run 'pmg cloud login' or set SAFEDEP_API_KEY and SAFEDEP_TENANT_ID)"
}
func tryResolveKeychainCredentials() (string, bool) {
resolver, err := cloud.NewKeychainCredentialResolver(cloud.CredentialTypeAPIKey)
if err != nil {
log.Debugf("keychain credential resolver unavailable: %v", err)
return "", false
}
defer func() {
if err := resolver.Close(); err != nil {
log.Warnf("failed to close keychain resolver: %v", err)
}
}()
if _, err := resolver.Resolve(); err != nil {
log.Debugf("no keychain credentials: %v", err)
return "", false
}
return "keychain", true
}
func tryResolveEnvCredentials() (string, bool) {
resolver, err := cloud.NewEnvCredentialResolver()
if err != nil {
log.Debugf("env credential resolver unavailable: %v", err)
return "", false
}
if _, err := resolver.Resolve(); err != nil {
log.Debugf("no env credentials: %v", err)
return "", false
}
return "environment (SAFEDEP_API_KEY, SAFEDEP_TENANT_ID)", true
}