diff --git a/providers/os/resources/packages/windows_packages.go b/providers/os/resources/packages/windows_packages.go index b89d02e958..f20d918ee1 100644 --- a/providers/os/resources/packages/windows_packages.go +++ b/providers/os/resources/packages/windows_packages.go @@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "io" + "runtime" "time" "github.com/cockroachdb/errors" @@ -229,8 +230,14 @@ func (w *WinPkgManager) getLocalInstalledApps() ([]Package, error) { continue } for _, c := range children { - // for each package the information is contained as children of that registry's key - p := w.getPackageFromRegistryKey(c) + // for each package the information is contained as items of that registry's key, + // so we request the items under the fully qualified path + items, err := registry.GetNativeRegistryKeyItems(c.Path + "\\" + c.Name) + if err != nil { + log.Debug().Err(err).Str("path", c.Path).Msg("could not read registry key children") + continue + } + p := getPackageFromRegistryKeyItems(items) if p == nil { continue } @@ -241,39 +248,32 @@ func (w *WinPkgManager) getLocalInstalledApps() ([]Package, error) { } func (w *WinPkgManager) getInstalledApps() ([]Package, error) { - if w.conn.Type() == shared.Type_Local { + if w.conn.Type() == shared.Type_Local && runtime.GOOS == "windows" { return w.getLocalInstalledApps() } - cmd, err := w.conn.RunCommand(powershell.Wrap(installedAppsScript)) + cmd, err := w.conn.RunCommand(powershell.Encode(installedAppsScript)) if err != nil { return nil, fmt.Errorf("could not read app package list") } return ParseWindowsAppPackages(cmd.Stdout) } -func (w *WinPkgManager) getPackageFromRegistryKey(key registry.RegistryKeyChild) *Package { - items, err := registry.GetNativeRegistryKeyItems(key.Path + "\\" + key.Name) - if err != nil { - log.Debug().Err(err).Str("path", key.Path).Str("name", key.Name).Msg("could not read registry key items") - return nil - } +func getPackageFromRegistryKeyItems(children []registry.RegistryKeyItem) *Package { var uninstallString string var displayName string var displayVersion string var publisher string - for _, i := range items { - if i.Key == "UninstallString" { + for _, i := range children { + switch i.Key { + case "UninstallString": uninstallString = i.Value.String - } - if i.Key == "DisplayName" { + case "DisplayName": displayName = i.Value.String - } - if i.Key == "DisplayVersion" { + case "DisplayVersion": displayVersion = i.Value.String - } - if i.Key == "Publisher" { + case "Publisher": publisher = i.Value.String } } @@ -282,21 +282,23 @@ func (w *WinPkgManager) getPackageFromRegistryKey(key registry.RegistryKeyChild) return nil } - cpeWfn := "" + pkg := &Package{ + Name: displayName, + Version: displayVersion, + Format: "windows/app", + } + if displayName != "" && displayVersion != "" { - cpeWfn, err = cpe.NewPackage2Cpe(publisher, displayName, displayVersion, "", "") + cpeWfn, err := cpe.NewPackage2Cpe(publisher, displayName, displayVersion, "", "") if err != nil { log.Debug().Err(err).Str("name", displayName).Str("version", displayVersion).Msg("could not create cpe for windows app package") + } else { + pkg.CPE = cpeWfn } } else { log.Debug().Msg("ignored package since information is missing") } - return &Package{ - Name: displayName, - Version: displayVersion, - Format: "windows/app", - CPE: cpeWfn, - } + return pkg } // returns installed appx packages as well as hot fixes @@ -315,8 +317,11 @@ func (w *WinPkgManager) List() ([]Package, error) { canRunCmd := w.conn.Capabilities().Has(shared.Capability_RunCommand) if !canRunCmd { - log.Debug().Msg("cannot run command on windows, skipping appx package list") - } else if b.Build > 10240 { + log.Debug().Msg("cannot run command on windows, skipping appx package and hotfixes list") + return pkgs, nil + } + + if b.Build > 10240 { // only win 10+ are compatible with app x packages cmd, err := w.conn.RunCommand(powershell.Wrap(WINDOWS_QUERY_APPX_PACKAGES)) if err != nil { @@ -329,22 +334,18 @@ func (w *WinPkgManager) List() ([]Package, error) { pkgs = append(pkgs, appxPkgs...) } - if !canRunCmd { - log.Debug().Msg("cannot run command on windows, skipping hotfix list") - } else { - // hotfixes - cmd, err := w.conn.RunCommand(powershell.Wrap(WINDOWS_QUERY_HOTFIXES)) - if err != nil { - return nil, errors.Wrap(err, "could not fetch hotfixes") - } - hotfixes, err := ParseWindowsHotfixes(cmd.Stdout) - if err != nil { - return nil, errors.Wrapf(err, "could not parse hotfix results") - } - hotfixAsPkgs := HotFixesToPackages(hotfixes) - - pkgs = append(pkgs, hotfixAsPkgs...) + // hotfixes + cmd, err := w.conn.RunCommand(powershell.Wrap(WINDOWS_QUERY_HOTFIXES)) + if err != nil { + return nil, errors.Wrap(err, "could not fetch hotfixes") + } + hotfixes, err := ParseWindowsHotfixes(cmd.Stdout) + if err != nil { + return nil, errors.Wrapf(err, "could not parse hotfix results") } + hotfixAsPkgs := HotFixesToPackages(hotfixes) + + pkgs = append(pkgs, hotfixAsPkgs...) return pkgs, nil } diff --git a/providers/os/resources/packages/windows_packages_test.go b/providers/os/resources/packages/windows_packages_test.go index 24eb64d4d2..271b48feef 100644 --- a/providers/os/resources/packages/windows_packages_test.go +++ b/providers/os/resources/packages/windows_packages_test.go @@ -12,6 +12,8 @@ import ( "github.com/stretchr/testify/require" "go.mondoo.com/cnquery/v11/providers-sdk/v1/inventory" "go.mondoo.com/cnquery/v11/providers/os/connection/mock" + "go.mondoo.com/cnquery/v11/providers/os/registry" + "go.mondoo.com/cnquery/v11/providers/os/resources/cpe" "go.mondoo.com/cnquery/v11/providers/os/resources/powershell" ) @@ -109,3 +111,76 @@ func TestWindowsHotFixParser(t *testing.T) { assert.Nil(t, err) assert.Equal(t, 0, len(hotfixes), "detected the right amount of packages") } + +func TestGetPackageFromRegistryKeyItems(t *testing.T) { + t.Run("get package from registry key items that are empty", func(t *testing.T) { + items := []registry.RegistryKeyItem{} + p := getPackageFromRegistryKeyItems(items) + assert.Nil(t, p) + }) + t.Run("get package from registry key items with missing required values", func(t *testing.T) { + items := []registry.RegistryKeyItem{ + { + Key: "DisplayName", + Value: registry.RegistryKeyValue{ + Kind: registry.SZ, + String: "Microsoft Visual C++ 2015-2019 Redistributable (x86) - 14.28.29913", + }, + }, + } + p := getPackageFromRegistryKeyItems(items) + assert.Nil(t, p) + }) + + t.Run("get package from registry key items", func(t *testing.T) { + items := []registry.RegistryKeyItem{ + { + Key: "DisplayName", + Value: registry.RegistryKeyValue{ + Kind: registry.SZ, + String: "Microsoft Visual C++ 2015-2019 Redistributable (x86) - 14.28.29913", + }, + }, + { + Key: "UninstallString", + Value: registry.RegistryKeyValue{ + Kind: registry.SZ, + String: "UninstallString", + }, + }, + { + Key: "DisplayVersion", + Value: registry.RegistryKeyValue{ + Kind: registry.SZ, + String: "14.28.29913.0", + }, + }, + { + Key: "Publisher", + Value: registry.RegistryKeyValue{ + Kind: registry.SZ, + String: "Microsoft Corporation", + }, + }, + } + p := getPackageFromRegistryKeyItems(items) + CPE, err := cpe.NewPackage2Cpe( + "Microsoft Corporation", + "Microsoft Visual C++ 2015-2019 Redistributable (x86) - 14.28.29913", + "14.28.29913.0", + "", + "") + + assert.Nil(t, err) + + expected := &Package{ + Name: "Microsoft Visual C++ 2015-2019 Redistributable (x86) - 14.28.29913", + Version: "14.28.29913.0", + Arch: "", + Format: "windows/app", + CPE: CPE, + } + assert.NotNil(t, p) + assert.Equal(t, expected, p) + }) +}