Refactored ListHostSoftware and ModifyAppConfig for nilaway (#46555)
Refactored `ListHostSoftware` and `ModifyAppConfig` into smaller helpers so nilaway can analyze them for nil-pointer dereferences <!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #46554 Refactoring. No functional changes. # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. ## Testing - [x] QA'd all new/changed functionality manually <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Improved host software listing by consolidating assembly, merging, deduplication, and out-of-scope filtering into dedicated helpers for more reliable and maintainable results. * Streamlined app configuration updates by extracting conditional-access (Okta) validation into a focused helper, improving validation consistency and error reporting. * **Chores** * Updated static analysis configuration: bumped a pinned plugin version and removed a suppression rule that hid certain internal lint messages. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
+391
-320
@@ -4603,6 +4603,365 @@ var hostSoftwareAllowedOrderKeys = common_mysql.OrderKeyAllowlist{
|
||||
"source": "source",
|
||||
}
|
||||
|
||||
// hostSoftwareTitleAssembler accumulates and de-duplicates host software title records
|
||||
// produced by ListHostSoftware. Its addRecord method holds the per-row merging logic
|
||||
// extracted from the main function so the CFG block count stays under nilaway's limit.
|
||||
type hostSoftwareTitleAssembler struct {
|
||||
bySoftwareID map[uint]*hostSoftware
|
||||
bySoftwareTitleID map[uint]*hostSoftware
|
||||
byVPPAdamID map[string]*hostSoftware
|
||||
byInHouseID map[uint]*hostSoftware
|
||||
hostVPPInstalledTitles map[uint]*hostSoftware
|
||||
hostInHouseInstalledTitles map[uint]*hostSoftware
|
||||
hostInstalledSoftwareSet map[uint]*hostSoftware
|
||||
filteredBySoftwareTitleID map[uint]*hostSoftware
|
||||
filteredByVPPAdamID map[string]*hostSoftware
|
||||
filteredByInHouseID map[uint]*hostSoftware
|
||||
installedPathBySoftwareId map[uint][]string
|
||||
pathSignatureInformation map[uint][]fleet.PathSignatureInformation
|
||||
vulnerabilitiesBySoftwareID map[uint][]string
|
||||
policiesBySoftwareTitleId map[uint][]fleet.AutomaticInstallPolicy
|
||||
iconsBySoftwareTitleID map[uint]fleet.SoftwareTitleIcon
|
||||
displayNames map[uint]string
|
||||
|
||||
indexOfSoftwareTitle map[uint]uint
|
||||
deduplicatedList []*hostSoftware
|
||||
}
|
||||
|
||||
func (a *hostSoftwareTitleAssembler) addRecord(
|
||||
ctx context.Context,
|
||||
ds *Datastore,
|
||||
host *fleet.Host,
|
||||
teamID uint,
|
||||
softwareTitleRecord *hostSoftware,
|
||||
) {
|
||||
softwareTitle := a.bySoftwareTitleID[softwareTitleRecord.ID]
|
||||
inventoriedVPPApp := a.hostVPPInstalledTitles[softwareTitleRecord.ID]
|
||||
inventoriedInHouseApp := a.hostInHouseInstalledTitles[softwareTitleRecord.ID]
|
||||
|
||||
if softwareTitle != nil && softwareTitle.SoftwareID != nil {
|
||||
// if we have a software id, that means that this record has been installed on the host,
|
||||
// we should double check the hostInstalledSoftwareSet,
|
||||
// but we want to make sure that software id is present on the InstalledVersions list to be processed
|
||||
if s, ok := a.hostInstalledSoftwareSet[*softwareTitle.SoftwareID]; ok {
|
||||
softwareIDStr := strconv.FormatUint(uint64(*softwareTitle.SoftwareID), 10)
|
||||
pushVersion(softwareIDStr, softwareTitleRecord, *s)
|
||||
}
|
||||
}
|
||||
if inventoriedVPPApp != nil && inventoriedVPPApp.SoftwareID != nil {
|
||||
// Vpp app installed on the host, we need to push this into the installed versions list as well
|
||||
if s, ok := a.hostInstalledSoftwareSet[*inventoriedVPPApp.SoftwareID]; ok {
|
||||
softwareIDStr := strconv.FormatUint(uint64(*inventoriedVPPApp.SoftwareID), 10)
|
||||
pushVersion(softwareIDStr, softwareTitleRecord, *s)
|
||||
}
|
||||
}
|
||||
if inventoriedInHouseApp != nil && inventoriedInHouseApp.SoftwareID != nil {
|
||||
// in-house app installed on the host, we need to push this into the installed versions list as well
|
||||
if s, ok := a.hostInstalledSoftwareSet[*inventoriedInHouseApp.SoftwareID]; ok {
|
||||
softwareIDStr := strconv.FormatUint(uint64(*inventoriedInHouseApp.SoftwareID), 10)
|
||||
pushVersion(softwareIDStr, softwareTitleRecord, *s)
|
||||
}
|
||||
}
|
||||
|
||||
if softwareTitleRecord.SoftwareIDList != nil {
|
||||
softwareIDList := strings.Split(*softwareTitleRecord.SoftwareIDList, ",")
|
||||
softwareSourceList := strings.Split(*softwareTitleRecord.SoftwareSourceList, ",")
|
||||
softwareVersionList := strings.Split(*softwareTitleRecord.VersionList, ",")
|
||||
softwareBundleIdentifierList := strings.Split(*softwareTitleRecord.BundleIdentifierList, ",")
|
||||
|
||||
for index, softwareIdStr := range softwareIDList {
|
||||
version := &fleet.HostSoftwareInstalledVersion{}
|
||||
|
||||
if softwareId, err := strconv.ParseUint(softwareIdStr, 10, 32); err == nil {
|
||||
|
||||
softwareId := uint(softwareId)
|
||||
if software, ok := a.bySoftwareID[softwareId]; ok {
|
||||
version.Version = softwareVersionList[index]
|
||||
version.BundleIdentifier = softwareBundleIdentifierList[index]
|
||||
version.Source = softwareSourceList[index]
|
||||
version.LastOpenedAt = software.LastOpenedAt
|
||||
version.SoftwareID = softwareId
|
||||
version.SoftwareTitleID = softwareTitleRecord.ID
|
||||
|
||||
version.InstalledPaths = a.installedPathBySoftwareId[softwareId]
|
||||
version.Vulnerabilities = a.vulnerabilitiesBySoftwareID[softwareId]
|
||||
|
||||
if version.Source == "apps" {
|
||||
version.SignatureInformation = a.pathSignatureInformation[softwareId]
|
||||
}
|
||||
|
||||
if storedIndex, ok := a.indexOfSoftwareTitle[softwareTitleRecord.ID]; ok {
|
||||
a.deduplicatedList[storedIndex].InstalledVersions = append(a.deduplicatedList[storedIndex].InstalledVersions, version)
|
||||
} else {
|
||||
softwareTitleRecord.InstalledVersions = append(softwareTitleRecord.InstalledVersions, version)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if softwareTitleRecord.VPPAppAdamIDList != nil {
|
||||
vppAppAdamIDList := strings.Split(*softwareTitleRecord.VPPAppAdamIDList, ",")
|
||||
vppAppSelfServiceList := strings.Split(*softwareTitleRecord.VPPAppSelfServiceList, ",")
|
||||
vppAppVersionList := strings.Split(*softwareTitleRecord.VPPAppVersionList, ",")
|
||||
vppAppPlatformList := strings.Split(*softwareTitleRecord.VPPAppPlatformList, ",")
|
||||
vppAppIconURLList := strings.Split(*softwareTitleRecord.VPPAppIconUrlList, ",")
|
||||
|
||||
if storedIndex, ok := a.indexOfSoftwareTitle[softwareTitleRecord.ID]; ok {
|
||||
softwareTitleRecord = a.deduplicatedList[storedIndex]
|
||||
}
|
||||
|
||||
for index, vppAppAdamIdStr := range vppAppAdamIDList {
|
||||
if vppAppAdamIdStr != "" {
|
||||
softwareTitle = a.byVPPAdamID[vppAppAdamIdStr]
|
||||
softwareTitleRecord.VPPAppAdamID = &vppAppAdamIdStr
|
||||
}
|
||||
|
||||
vppAppSelfService := vppAppSelfServiceList[index]
|
||||
if vppAppSelfService != "" {
|
||||
if vppAppSelfService == "1" {
|
||||
softwareTitleRecord.VPPAppSelfService = new(true)
|
||||
} else {
|
||||
softwareTitleRecord.VPPAppSelfService = new(false)
|
||||
}
|
||||
}
|
||||
|
||||
vppAppVersion := vppAppVersionList[index]
|
||||
if vppAppVersion != "" {
|
||||
softwareTitleRecord.VPPAppVersion = &vppAppVersion
|
||||
}
|
||||
|
||||
vppAppPlatform := vppAppPlatformList[index]
|
||||
if vppAppPlatform != "" {
|
||||
softwareTitleRecord.VPPAppPlatform = &vppAppPlatform
|
||||
}
|
||||
VPPAppIconURL := vppAppIconURLList[index]
|
||||
if VPPAppIconURL != "" {
|
||||
softwareTitleRecord.VPPAppIconURL = &VPPAppIconURL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if softwareTitleRecord.InHouseAppIDList != nil {
|
||||
inHouseAppIDList := strings.Split(*softwareTitleRecord.InHouseAppIDList, ",")
|
||||
inHouseAppVersionList := strings.Split(*softwareTitleRecord.InHouseAppVersionList, ",")
|
||||
inHouseAppPlatformList := strings.Split(*softwareTitleRecord.InHouseAppPlatformList, ",")
|
||||
inHouseAppNameList := strings.Split(*softwareTitleRecord.InHouseAppNameList, ",")
|
||||
inHouseAppSelfServiceList := strings.Split(*softwareTitleRecord.InHouseAppSelfServiceList, ",")
|
||||
|
||||
if storedIndex, ok := a.indexOfSoftwareTitle[softwareTitleRecord.ID]; ok {
|
||||
softwareTitleRecord = a.deduplicatedList[storedIndex]
|
||||
}
|
||||
|
||||
for index, inHouseAppIDStr := range inHouseAppIDList {
|
||||
inHouseID64, err := strconv.ParseUint(inHouseAppIDStr, 10, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
inHouseID := uint(inHouseID64)
|
||||
|
||||
softwareTitle = a.byInHouseID[inHouseID]
|
||||
softwareTitleRecord.InHouseAppID = &inHouseID
|
||||
|
||||
inHouseAppVersion := inHouseAppVersionList[index]
|
||||
if inHouseAppVersion != "" {
|
||||
softwareTitleRecord.InHouseAppVersion = &inHouseAppVersion
|
||||
}
|
||||
|
||||
inHouseAppPlatform := inHouseAppPlatformList[index]
|
||||
if inHouseAppPlatform != "" {
|
||||
softwareTitleRecord.InHouseAppPlatform = &inHouseAppPlatform
|
||||
}
|
||||
inHouseAppName := inHouseAppNameList[index]
|
||||
if inHouseAppName != "" {
|
||||
softwareTitleRecord.InHouseAppName = &inHouseAppName
|
||||
}
|
||||
inHouseAppSelfService := inHouseAppSelfServiceList[index]
|
||||
if inHouseAppSelfService != "" {
|
||||
if inHouseAppSelfService == "1" {
|
||||
softwareTitleRecord.InHouseAppSelfService = new(true)
|
||||
} else {
|
||||
softwareTitleRecord.InHouseAppSelfService = new(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if storedIndex, ok := a.indexOfSoftwareTitle[softwareTitleRecord.ID]; ok {
|
||||
softwareTitleRecord = a.deduplicatedList[storedIndex]
|
||||
}
|
||||
|
||||
// Merge the data of `software title` into `softwareTitleRecord`
|
||||
// We should try to move as much of these attributes into the `stmt` query
|
||||
if softwareTitle != nil {
|
||||
softwareTitleRecord.Status = softwareTitle.Status
|
||||
softwareTitleRecord.LastInstallInstallUUID = softwareTitle.LastInstallInstallUUID
|
||||
softwareTitleRecord.LastInstallInstalledAt = softwareTitle.LastInstallInstalledAt
|
||||
softwareTitleRecord.LastUninstallScriptExecutionID = softwareTitle.LastUninstallScriptExecutionID
|
||||
softwareTitleRecord.LastUninstallUninstalledAt = softwareTitle.LastUninstallUninstalledAt
|
||||
if softwareTitle.PackageSelfService != nil {
|
||||
softwareTitleRecord.PackageSelfService = softwareTitle.PackageSelfService
|
||||
}
|
||||
}
|
||||
|
||||
// promote the package name and version to the proper destination fields
|
||||
if softwareTitleRecord.PackageName != nil {
|
||||
if _, ok := a.filteredBySoftwareTitleID[softwareTitleRecord.ID]; ok {
|
||||
hydrateHostSoftwareRecordFromDb(softwareTitleRecord, softwareTitle)
|
||||
}
|
||||
}
|
||||
// Here and below: populate LastInstall for software packages, VPP apps, and in-house apps
|
||||
// even if installer is out of scope so failed install attempts show the execution ID for viewing details.
|
||||
if softwareTitleRecord.SoftwarePackage != nil && softwareTitleRecord.SoftwarePackage.LastInstall == nil {
|
||||
if softwareTitle != nil && softwareTitle.LastInstallInstallUUID != nil && *softwareTitle.LastInstallInstallUUID != "" {
|
||||
softwareTitleRecord.SoftwarePackage.LastInstall = &fleet.HostSoftwareInstall{
|
||||
InstallUUID: *softwareTitle.LastInstallInstallUUID,
|
||||
}
|
||||
if softwareTitle.LastInstallInstalledAt != nil {
|
||||
softwareTitleRecord.SoftwarePackage.LastInstall.InstalledAt = *softwareTitle.LastInstallInstalledAt
|
||||
}
|
||||
}
|
||||
}
|
||||
// Populate LastUninstall for software packages even if installer is out of scope.
|
||||
if softwareTitleRecord.SoftwarePackage != nil && softwareTitleRecord.SoftwarePackage.LastUninstall == nil {
|
||||
if softwareTitle != nil && softwareTitle.LastUninstallScriptExecutionID != nil && *softwareTitle.LastUninstallScriptExecutionID != "" {
|
||||
softwareTitleRecord.SoftwarePackage.LastUninstall = &fleet.HostSoftwareUninstall{
|
||||
ExecutionID: *softwareTitle.LastUninstallScriptExecutionID,
|
||||
}
|
||||
if softwareTitle.LastUninstallUninstalledAt != nil {
|
||||
softwareTitleRecord.SoftwarePackage.LastUninstall.UninstalledAt = *softwareTitle.LastUninstallUninstalledAt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This happens when there is a software installed on the host but it is also a vpp record, so we want
|
||||
// to grab the vpp data from the installed vpp record and merge it onto the software record
|
||||
if installedVppRecord, ok := a.hostVPPInstalledTitles[softwareTitleRecord.ID]; ok {
|
||||
softwareTitleRecord.VPPAppAdamID = installedVppRecord.VPPAppAdamID
|
||||
softwareTitleRecord.VPPAppVersion = installedVppRecord.VPPAppVersion
|
||||
softwareTitleRecord.VPPAppPlatform = installedVppRecord.VPPAppPlatform
|
||||
softwareTitleRecord.VPPAppIconURL = installedVppRecord.VPPAppIconURL
|
||||
softwareTitleRecord.VPPAppSelfService = installedVppRecord.VPPAppSelfService
|
||||
}
|
||||
// promote the VPP app id and version to the proper destination fields
|
||||
if softwareTitleRecord.VPPAppAdamID != nil {
|
||||
if _, ok := a.filteredByVPPAdamID[*softwareTitleRecord.VPPAppAdamID]; ok {
|
||||
promoteSoftwareTitleVPPApp(softwareTitleRecord)
|
||||
}
|
||||
}
|
||||
if softwareTitleRecord.AppStoreApp != nil && softwareTitleRecord.AppStoreApp.LastInstall == nil {
|
||||
if softwareTitle != nil && softwareTitle.LastInstallInstallUUID != nil && *softwareTitle.LastInstallInstallUUID != "" {
|
||||
softwareTitleRecord.AppStoreApp.LastInstall = &fleet.HostSoftwareInstall{
|
||||
CommandUUID: *softwareTitle.LastInstallInstallUUID,
|
||||
}
|
||||
if softwareTitle.LastInstallInstalledAt != nil {
|
||||
softwareTitleRecord.AppStoreApp.LastInstall.InstalledAt = *softwareTitle.LastInstallInstalledAt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This happens when there is a software installed on the host but it is
|
||||
// also an in-house record, so we want to grab the in-house data from the
|
||||
// installed record and merge it onto the software record
|
||||
if installedInHouseRecord, ok := a.hostInHouseInstalledTitles[softwareTitleRecord.ID]; ok {
|
||||
softwareTitleRecord.InHouseAppID = installedInHouseRecord.InHouseAppID
|
||||
softwareTitleRecord.InHouseAppName = installedInHouseRecord.InHouseAppName
|
||||
softwareTitleRecord.InHouseAppVersion = installedInHouseRecord.InHouseAppVersion
|
||||
softwareTitleRecord.InHouseAppPlatform = installedInHouseRecord.InHouseAppPlatform
|
||||
softwareTitleRecord.InHouseAppSelfService = installedInHouseRecord.InHouseAppSelfService
|
||||
}
|
||||
// promote the in-house app id and version to the proper destination fields
|
||||
if softwareTitleRecord.InHouseAppID != nil {
|
||||
if _, ok := a.filteredByInHouseID[*softwareTitleRecord.InHouseAppID]; ok {
|
||||
promoteSoftwareTitleInHouseApp(softwareTitleRecord)
|
||||
}
|
||||
}
|
||||
// N.b., in-house apps use SoftwarePackage struct with CommandUUID.
|
||||
if softwareTitleRecord.SoftwarePackage != nil && softwareTitleRecord.InHouseAppID != nil && softwareTitleRecord.SoftwarePackage.LastInstall == nil {
|
||||
if softwareTitle != nil && softwareTitle.LastInstallInstallUUID != nil && *softwareTitle.LastInstallInstallUUID != "" {
|
||||
softwareTitleRecord.SoftwarePackage.LastInstall = &fleet.HostSoftwareInstall{
|
||||
CommandUUID: *softwareTitle.LastInstallInstallUUID,
|
||||
}
|
||||
if softwareTitle.LastInstallInstalledAt != nil {
|
||||
softwareTitleRecord.SoftwarePackage.LastInstall.InstalledAt = *softwareTitle.LastInstallInstalledAt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: in-house apps do not support automatic install policies at the moment
|
||||
if policies, ok := a.policiesBySoftwareTitleId[softwareTitleRecord.ID]; ok {
|
||||
switch {
|
||||
case softwareTitleRecord.AppStoreApp != nil:
|
||||
softwareTitleRecord.AppStoreApp.AutomaticInstallPolicies = policies
|
||||
case softwareTitleRecord.SoftwarePackage != nil:
|
||||
softwareTitleRecord.SoftwarePackage.AutomaticInstallPolicies = policies
|
||||
default:
|
||||
ds.logger.WarnContext(ctx, "software title record should have an associated VPP application or software package",
|
||||
"team_id", teamID,
|
||||
"host_id", host.ID,
|
||||
"software_title_id", softwareTitleRecord.ID,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if icon, ok := a.iconsBySoftwareTitleID[softwareTitleRecord.ID]; ok {
|
||||
softwareTitleRecord.IconUrl = new(icon.IconUrl())
|
||||
}
|
||||
|
||||
if displayName, ok := a.displayNames[softwareTitleRecord.ID]; ok {
|
||||
softwareTitleRecord.DisplayName = displayName
|
||||
}
|
||||
|
||||
if _, ok := a.indexOfSoftwareTitle[softwareTitleRecord.ID]; !ok {
|
||||
a.indexOfSoftwareTitle[softwareTitleRecord.ID] = uint(len(a.deduplicatedList))
|
||||
a.deduplicatedList = append(a.deduplicatedList, softwareTitleRecord)
|
||||
}
|
||||
}
|
||||
|
||||
// filterOutOfScopeFailedHostSoftwareInstalls removes failed install entries that are not in
|
||||
// the osquery inventory and whose installer is out of label scope, so they don't surface
|
||||
// as available software on the host. Maps are mutated in place.
|
||||
func filterOutOfScopeFailedHostSoftwareInstalls(
|
||||
bySoftwareTitleID map[uint]*hostSoftware,
|
||||
byVPPAdamID map[string]*hostSoftware,
|
||||
byInHouseID map[uint]*hostSoftware,
|
||||
hostInstalledSoftwareTitleSet map[uint]struct{},
|
||||
filteredBySoftwareTitleID map[uint]*hostSoftware,
|
||||
filteredByVPPAdamID map[string]*hostSoftware,
|
||||
filteredByInHouseID map[uint]*hostSoftware,
|
||||
) {
|
||||
for titleID, st := range bySoftwareTitleID {
|
||||
if st.InstallerID != nil {
|
||||
if _, isInstalled := hostInstalledSoftwareTitleSet[titleID]; !isInstalled {
|
||||
if st.Status != nil && *st.Status == fleet.SoftwareInstallFailed {
|
||||
if _, inScope := filteredBySoftwareTitleID[titleID]; !inScope {
|
||||
delete(bySoftwareTitleID, titleID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for adamID, st := range byVPPAdamID {
|
||||
if _, isInstalled := hostInstalledSoftwareTitleSet[st.ID]; !isInstalled {
|
||||
if st.Status != nil && *st.Status == fleet.SoftwareInstallFailed {
|
||||
if _, inScope := filteredByVPPAdamID[adamID]; !inScope {
|
||||
delete(byVPPAdamID, adamID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for appID, st := range byInHouseID {
|
||||
if _, isInstalled := hostInstalledSoftwareTitleSet[st.ID]; !isInstalled {
|
||||
if st.Status != nil && *st.Status == fleet.SoftwareInstallFailed {
|
||||
if _, inScope := filteredByInHouseID[appID]; !inScope {
|
||||
delete(byInHouseID, appID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ds *Datastore) ListHostSoftware(ctx context.Context, host *fleet.Host, opts fleet.HostSoftwareTitleListOptions) ([]*fleet.HostSoftwareWithInstaller, *fleet.PaginationMetadata, error) {
|
||||
if !opts.VulnerableOnly && (opts.MinimumCVSS > 0 || opts.MaximumCVSS > 0 || opts.KnownExploit) {
|
||||
return nil, nil, fleet.NewInvalidArgumentError(
|
||||
@@ -5566,39 +5925,15 @@ func (ds *Datastore) ListHostSoftware(ctx context.Context, host *fleet.Host, opt
|
||||
}
|
||||
}
|
||||
|
||||
// Filter out-of-scope FAILED installs from all app types.
|
||||
// Only remove if not in inventory AND status is failed AND out of label scope.
|
||||
for titleID, st := range bySoftwareTitleID {
|
||||
if st.InstallerID != nil {
|
||||
// Check if software is NOT actually installed (not in osquery inventory)
|
||||
if _, isInstalled := hostInstalledSoftwareTitleSet[titleID]; !isInstalled {
|
||||
// Only remove if install FAILED and installer is out of scope
|
||||
if st.Status != nil && *st.Status == fleet.SoftwareInstallFailed {
|
||||
if _, inScope := filteredBySoftwareTitleID[titleID]; !inScope {
|
||||
delete(bySoftwareTitleID, titleID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for adamID, st := range byVPPAdamID {
|
||||
if _, isInstalled := hostInstalledSoftwareTitleSet[st.ID]; !isInstalled {
|
||||
if st.Status != nil && *st.Status == fleet.SoftwareInstallFailed {
|
||||
if _, inScope := filteredByVPPAdamID[adamID]; !inScope {
|
||||
delete(byVPPAdamID, adamID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for appID, st := range byInHouseID {
|
||||
if _, isInstalled := hostInstalledSoftwareTitleSet[st.ID]; !isInstalled {
|
||||
if st.Status != nil && *st.Status == fleet.SoftwareInstallFailed {
|
||||
if _, inScope := filteredByInHouseID[appID]; !inScope {
|
||||
delete(byInHouseID, appID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
filterOutOfScopeFailedHostSoftwareInstalls(
|
||||
bySoftwareTitleID,
|
||||
byVPPAdamID,
|
||||
byInHouseID,
|
||||
hostInstalledSoftwareTitleSet,
|
||||
filteredBySoftwareTitleID,
|
||||
filteredByVPPAdamID,
|
||||
filteredByInHouseID,
|
||||
)
|
||||
|
||||
if opts.OnlyAvailableForInstall {
|
||||
bySoftwareTitleID = filteredBySoftwareTitleID
|
||||
@@ -6154,294 +6489,30 @@ func (ds *Datastore) ListHostSoftware(ctx context.Context, host *fleet.Host, opt
|
||||
return nil, nil, ctxerr.Wrap(ctx, err, "get software display names by team and title IDs")
|
||||
}
|
||||
|
||||
indexOfSoftwareTitle := make(map[uint]uint)
|
||||
deduplicatedList := make([]*hostSoftware, 0, len(hostSoftwareList))
|
||||
for _, softwareTitleRecord := range hostSoftwareList {
|
||||
softwareTitle := bySoftwareTitleID[softwareTitleRecord.ID]
|
||||
inventoriedVPPApp := hostVPPInstalledTitles[softwareTitleRecord.ID]
|
||||
inventoriedInHouseApp := hostInHouseInstalledTitles[softwareTitleRecord.ID]
|
||||
|
||||
if softwareTitle != nil && softwareTitle.SoftwareID != nil {
|
||||
// if we have a software id, that means that this record has been installed on the host,
|
||||
// we should double check the hostInstalledSoftwareSet,
|
||||
// but we want to make sure that software id is present on the InstalledVersions list to be processed
|
||||
if s, ok := hostInstalledSoftwareSet[*softwareTitle.SoftwareID]; ok {
|
||||
softwareIDStr := strconv.FormatUint(uint64(*softwareTitle.SoftwareID), 10)
|
||||
pushVersion(softwareIDStr, softwareTitleRecord, *s)
|
||||
}
|
||||
}
|
||||
if inventoriedVPPApp != nil && inventoriedVPPApp.SoftwareID != nil {
|
||||
// Vpp app installed on the host, we need to push this into the installed versions list as well
|
||||
if s, ok := hostInstalledSoftwareSet[*inventoriedVPPApp.SoftwareID]; ok {
|
||||
softwareIDStr := strconv.FormatUint(uint64(*inventoriedVPPApp.SoftwareID), 10)
|
||||
pushVersion(softwareIDStr, softwareTitleRecord, *s)
|
||||
}
|
||||
}
|
||||
if inventoriedInHouseApp != nil && inventoriedInHouseApp.SoftwareID != nil {
|
||||
// in-house app installed on the host, we need to push this into the installed versions list as well
|
||||
if s, ok := hostInstalledSoftwareSet[*inventoriedInHouseApp.SoftwareID]; ok {
|
||||
softwareIDStr := strconv.FormatUint(uint64(*inventoriedInHouseApp.SoftwareID), 10)
|
||||
pushVersion(softwareIDStr, softwareTitleRecord, *s)
|
||||
}
|
||||
}
|
||||
|
||||
if softwareTitleRecord.SoftwareIDList != nil {
|
||||
softwareIDList := strings.Split(*softwareTitleRecord.SoftwareIDList, ",")
|
||||
softwareSourceList := strings.Split(*softwareTitleRecord.SoftwareSourceList, ",")
|
||||
softwareVersionList := strings.Split(*softwareTitleRecord.VersionList, ",")
|
||||
softwareBundleIdentifierList := strings.Split(*softwareTitleRecord.BundleIdentifierList, ",")
|
||||
|
||||
for index, softwareIdStr := range softwareIDList {
|
||||
version := &fleet.HostSoftwareInstalledVersion{}
|
||||
|
||||
if softwareId, err := strconv.ParseUint(softwareIdStr, 10, 32); err == nil {
|
||||
|
||||
softwareId := uint(softwareId)
|
||||
if software, ok := bySoftwareID[softwareId]; ok {
|
||||
version.Version = softwareVersionList[index]
|
||||
version.BundleIdentifier = softwareBundleIdentifierList[index]
|
||||
version.Source = softwareSourceList[index]
|
||||
version.LastOpenedAt = software.LastOpenedAt
|
||||
version.SoftwareID = softwareId
|
||||
version.SoftwareTitleID = softwareTitleRecord.ID
|
||||
|
||||
version.InstalledPaths = installedPathBySoftwareId[softwareId]
|
||||
version.Vulnerabilities = vulnerabilitiesBySoftwareID[softwareId]
|
||||
|
||||
if version.Source == "apps" {
|
||||
version.SignatureInformation = pathSignatureInformation[softwareId]
|
||||
}
|
||||
|
||||
if storedIndex, ok := indexOfSoftwareTitle[softwareTitleRecord.ID]; ok {
|
||||
deduplicatedList[storedIndex].InstalledVersions = append(deduplicatedList[storedIndex].InstalledVersions, version)
|
||||
} else {
|
||||
softwareTitleRecord.InstalledVersions = append(softwareTitleRecord.InstalledVersions, version)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if softwareTitleRecord.VPPAppAdamIDList != nil {
|
||||
vppAppAdamIDList := strings.Split(*softwareTitleRecord.VPPAppAdamIDList, ",")
|
||||
vppAppSelfServiceList := strings.Split(*softwareTitleRecord.VPPAppSelfServiceList, ",")
|
||||
vppAppVersionList := strings.Split(*softwareTitleRecord.VPPAppVersionList, ",")
|
||||
vppAppPlatformList := strings.Split(*softwareTitleRecord.VPPAppPlatformList, ",")
|
||||
vppAppIconURLList := strings.Split(*softwareTitleRecord.VPPAppIconUrlList, ",")
|
||||
|
||||
if storedIndex, ok := indexOfSoftwareTitle[softwareTitleRecord.ID]; ok {
|
||||
softwareTitleRecord = deduplicatedList[storedIndex]
|
||||
}
|
||||
|
||||
for index, vppAppAdamIdStr := range vppAppAdamIDList {
|
||||
if vppAppAdamIdStr != "" {
|
||||
softwareTitle = byVPPAdamID[vppAppAdamIdStr]
|
||||
softwareTitleRecord.VPPAppAdamID = &vppAppAdamIdStr
|
||||
}
|
||||
|
||||
vppAppSelfService := vppAppSelfServiceList[index]
|
||||
if vppAppSelfService != "" {
|
||||
if vppAppSelfService == "1" {
|
||||
softwareTitleRecord.VPPAppSelfService = ptr.Bool(true)
|
||||
} else {
|
||||
softwareTitleRecord.VPPAppSelfService = ptr.Bool(false)
|
||||
}
|
||||
}
|
||||
|
||||
vppAppVersion := vppAppVersionList[index]
|
||||
if vppAppVersion != "" {
|
||||
softwareTitleRecord.VPPAppVersion = &vppAppVersion
|
||||
}
|
||||
|
||||
vppAppPlatform := vppAppPlatformList[index]
|
||||
if vppAppPlatform != "" {
|
||||
softwareTitleRecord.VPPAppPlatform = &vppAppPlatform
|
||||
}
|
||||
VPPAppIconURL := vppAppIconURLList[index]
|
||||
if VPPAppIconURL != "" {
|
||||
softwareTitleRecord.VPPAppIconURL = &VPPAppIconURL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if softwareTitleRecord.InHouseAppIDList != nil {
|
||||
inHouseAppIDList := strings.Split(*softwareTitleRecord.InHouseAppIDList, ",")
|
||||
inHouseAppVersionList := strings.Split(*softwareTitleRecord.InHouseAppVersionList, ",")
|
||||
inHouseAppPlatformList := strings.Split(*softwareTitleRecord.InHouseAppPlatformList, ",")
|
||||
inHouseAppNameList := strings.Split(*softwareTitleRecord.InHouseAppNameList, ",")
|
||||
inHouseAppSelfServiceList := strings.Split(*softwareTitleRecord.InHouseAppSelfServiceList, ",")
|
||||
|
||||
if storedIndex, ok := indexOfSoftwareTitle[softwareTitleRecord.ID]; ok {
|
||||
softwareTitleRecord = deduplicatedList[storedIndex]
|
||||
}
|
||||
|
||||
for index, inHouseAppIDStr := range inHouseAppIDList {
|
||||
inHouseID64, err := strconv.ParseUint(inHouseAppIDStr, 10, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
inHouseID := uint(inHouseID64)
|
||||
|
||||
softwareTitle = byInHouseID[inHouseID]
|
||||
softwareTitleRecord.InHouseAppID = &inHouseID
|
||||
|
||||
inHouseAppVersion := inHouseAppVersionList[index]
|
||||
if inHouseAppVersion != "" {
|
||||
softwareTitleRecord.InHouseAppVersion = &inHouseAppVersion
|
||||
}
|
||||
|
||||
inHouseAppPlatform := inHouseAppPlatformList[index]
|
||||
if inHouseAppPlatform != "" {
|
||||
softwareTitleRecord.InHouseAppPlatform = &inHouseAppPlatform
|
||||
}
|
||||
inHouseAppName := inHouseAppNameList[index]
|
||||
if inHouseAppName != "" {
|
||||
softwareTitleRecord.InHouseAppName = &inHouseAppName
|
||||
}
|
||||
inHouseAppSelfService := inHouseAppSelfServiceList[index]
|
||||
if inHouseAppSelfService != "" {
|
||||
if inHouseAppSelfService == "1" {
|
||||
softwareTitleRecord.InHouseAppSelfService = ptr.Bool(true)
|
||||
} else {
|
||||
softwareTitleRecord.InHouseAppSelfService = ptr.Bool(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if storedIndex, ok := indexOfSoftwareTitle[softwareTitleRecord.ID]; ok {
|
||||
softwareTitleRecord = deduplicatedList[storedIndex]
|
||||
}
|
||||
|
||||
// Merge the data of `software title` into `softwareTitleRecord`
|
||||
// We should try to move as much of these attributes into the `stmt` query
|
||||
if softwareTitle != nil {
|
||||
softwareTitleRecord.Status = softwareTitle.Status
|
||||
softwareTitleRecord.LastInstallInstallUUID = softwareTitle.LastInstallInstallUUID
|
||||
softwareTitleRecord.LastInstallInstalledAt = softwareTitle.LastInstallInstalledAt
|
||||
softwareTitleRecord.LastUninstallScriptExecutionID = softwareTitle.LastUninstallScriptExecutionID
|
||||
softwareTitleRecord.LastUninstallUninstalledAt = softwareTitle.LastUninstallUninstalledAt
|
||||
if softwareTitle.PackageSelfService != nil {
|
||||
softwareTitleRecord.PackageSelfService = softwareTitle.PackageSelfService
|
||||
}
|
||||
}
|
||||
|
||||
// promote the package name and version to the proper destination fields
|
||||
if softwareTitleRecord.PackageName != nil {
|
||||
if _, ok := filteredBySoftwareTitleID[softwareTitleRecord.ID]; ok {
|
||||
hydrateHostSoftwareRecordFromDb(softwareTitleRecord, softwareTitle)
|
||||
}
|
||||
}
|
||||
// Here and below: populate LastInstall for software packages, VPP apps, and in-house apps
|
||||
// even if installer is out of scope so failed install attempts show the execution ID for viewing details.
|
||||
if softwareTitleRecord.SoftwarePackage != nil && softwareTitleRecord.SoftwarePackage.LastInstall == nil {
|
||||
if softwareTitle != nil && softwareTitle.LastInstallInstallUUID != nil && *softwareTitle.LastInstallInstallUUID != "" {
|
||||
softwareTitleRecord.SoftwarePackage.LastInstall = &fleet.HostSoftwareInstall{
|
||||
InstallUUID: *softwareTitle.LastInstallInstallUUID,
|
||||
}
|
||||
if softwareTitle.LastInstallInstalledAt != nil {
|
||||
softwareTitleRecord.SoftwarePackage.LastInstall.InstalledAt = *softwareTitle.LastInstallInstalledAt
|
||||
}
|
||||
}
|
||||
}
|
||||
// Populate LastUninstall for software packages even if installer is out of scope.
|
||||
if softwareTitleRecord.SoftwarePackage != nil && softwareTitleRecord.SoftwarePackage.LastUninstall == nil {
|
||||
if softwareTitle != nil && softwareTitle.LastUninstallScriptExecutionID != nil && *softwareTitle.LastUninstallScriptExecutionID != "" {
|
||||
softwareTitleRecord.SoftwarePackage.LastUninstall = &fleet.HostSoftwareUninstall{
|
||||
ExecutionID: *softwareTitle.LastUninstallScriptExecutionID,
|
||||
}
|
||||
if softwareTitle.LastUninstallUninstalledAt != nil {
|
||||
softwareTitleRecord.SoftwarePackage.LastUninstall.UninstalledAt = *softwareTitle.LastUninstallUninstalledAt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This happens when there is a software installed on the host but it is also a vpp record, so we want
|
||||
// to grab the vpp data from the installed vpp record and merge it onto the software record
|
||||
if installedVppRecord, ok := hostVPPInstalledTitles[softwareTitleRecord.ID]; ok {
|
||||
softwareTitleRecord.VPPAppAdamID = installedVppRecord.VPPAppAdamID
|
||||
softwareTitleRecord.VPPAppVersion = installedVppRecord.VPPAppVersion
|
||||
softwareTitleRecord.VPPAppPlatform = installedVppRecord.VPPAppPlatform
|
||||
softwareTitleRecord.VPPAppIconURL = installedVppRecord.VPPAppIconURL
|
||||
softwareTitleRecord.VPPAppSelfService = installedVppRecord.VPPAppSelfService
|
||||
}
|
||||
// promote the VPP app id and version to the proper destination fields
|
||||
if softwareTitleRecord.VPPAppAdamID != nil {
|
||||
if _, ok := filteredByVPPAdamID[*softwareTitleRecord.VPPAppAdamID]; ok {
|
||||
promoteSoftwareTitleVPPApp(softwareTitleRecord)
|
||||
}
|
||||
}
|
||||
if softwareTitleRecord.AppStoreApp != nil && softwareTitleRecord.AppStoreApp.LastInstall == nil {
|
||||
if softwareTitle != nil && softwareTitle.LastInstallInstallUUID != nil && *softwareTitle.LastInstallInstallUUID != "" {
|
||||
softwareTitleRecord.AppStoreApp.LastInstall = &fleet.HostSoftwareInstall{
|
||||
CommandUUID: *softwareTitle.LastInstallInstallUUID,
|
||||
}
|
||||
if softwareTitle.LastInstallInstalledAt != nil {
|
||||
softwareTitleRecord.AppStoreApp.LastInstall.InstalledAt = *softwareTitle.LastInstallInstalledAt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This happens when there is a software installed on the host but it is
|
||||
// also an in-house record, so we want to grab the in-house data from the
|
||||
// installed record and merge it onto the software record
|
||||
if installedInHouseRecord, ok := hostInHouseInstalledTitles[softwareTitleRecord.ID]; ok {
|
||||
softwareTitleRecord.InHouseAppID = installedInHouseRecord.InHouseAppID
|
||||
softwareTitleRecord.InHouseAppName = installedInHouseRecord.InHouseAppName
|
||||
softwareTitleRecord.InHouseAppVersion = installedInHouseRecord.InHouseAppVersion
|
||||
softwareTitleRecord.InHouseAppPlatform = installedInHouseRecord.InHouseAppPlatform
|
||||
softwareTitleRecord.InHouseAppSelfService = installedInHouseRecord.InHouseAppSelfService
|
||||
}
|
||||
// promote the in-house app id and version to the proper destination fields
|
||||
if softwareTitleRecord.InHouseAppID != nil {
|
||||
if _, ok := filteredByInHouseID[*softwareTitleRecord.InHouseAppID]; ok {
|
||||
promoteSoftwareTitleInHouseApp(softwareTitleRecord)
|
||||
}
|
||||
}
|
||||
// N.b., in-house apps use SoftwarePackage struct with CommandUUID.
|
||||
if softwareTitleRecord.SoftwarePackage != nil && softwareTitleRecord.InHouseAppID != nil && softwareTitleRecord.SoftwarePackage.LastInstall == nil {
|
||||
if softwareTitle != nil && softwareTitle.LastInstallInstallUUID != nil && *softwareTitle.LastInstallInstallUUID != "" {
|
||||
softwareTitleRecord.SoftwarePackage.LastInstall = &fleet.HostSoftwareInstall{
|
||||
CommandUUID: *softwareTitle.LastInstallInstallUUID,
|
||||
}
|
||||
if softwareTitle.LastInstallInstalledAt != nil {
|
||||
softwareTitleRecord.SoftwarePackage.LastInstall.InstalledAt = *softwareTitle.LastInstallInstalledAt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: in-house apps do not support automatic install policies at the moment
|
||||
if policies, ok := policiesBySoftwareTitleId[softwareTitleRecord.ID]; ok {
|
||||
switch {
|
||||
case softwareTitleRecord.AppStoreApp != nil:
|
||||
softwareTitleRecord.AppStoreApp.AutomaticInstallPolicies = policies
|
||||
case softwareTitleRecord.SoftwarePackage != nil:
|
||||
softwareTitleRecord.SoftwarePackage.AutomaticInstallPolicies = policies
|
||||
default:
|
||||
ds.logger.WarnContext(ctx, "software title record should have an associated VPP application or software package",
|
||||
"team_id", teamID,
|
||||
"host_id", host.ID,
|
||||
"software_title_id", softwareTitleRecord.ID,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if icon, ok := iconsBySoftwareTitleID[softwareTitleRecord.ID]; ok {
|
||||
softwareTitleRecord.IconUrl = ptr.String(icon.IconUrl())
|
||||
}
|
||||
|
||||
if displayName, ok := displayNames[softwareTitleRecord.ID]; ok {
|
||||
softwareTitleRecord.DisplayName = displayName
|
||||
}
|
||||
|
||||
if _, ok := indexOfSoftwareTitle[softwareTitleRecord.ID]; !ok {
|
||||
indexOfSoftwareTitle[softwareTitleRecord.ID] = uint(len(deduplicatedList))
|
||||
deduplicatedList = append(deduplicatedList, softwareTitleRecord)
|
||||
}
|
||||
assembler := &hostSoftwareTitleAssembler{
|
||||
bySoftwareID: bySoftwareID,
|
||||
bySoftwareTitleID: bySoftwareTitleID,
|
||||
byVPPAdamID: byVPPAdamID,
|
||||
byInHouseID: byInHouseID,
|
||||
hostVPPInstalledTitles: hostVPPInstalledTitles,
|
||||
hostInHouseInstalledTitles: hostInHouseInstalledTitles,
|
||||
hostInstalledSoftwareSet: hostInstalledSoftwareSet,
|
||||
filteredBySoftwareTitleID: filteredBySoftwareTitleID,
|
||||
filteredByVPPAdamID: filteredByVPPAdamID,
|
||||
filteredByInHouseID: filteredByInHouseID,
|
||||
installedPathBySoftwareId: installedPathBySoftwareId,
|
||||
pathSignatureInformation: pathSignatureInformation,
|
||||
vulnerabilitiesBySoftwareID: vulnerabilitiesBySoftwareID,
|
||||
policiesBySoftwareTitleId: policiesBySoftwareTitleId,
|
||||
iconsBySoftwareTitleID: iconsBySoftwareTitleID,
|
||||
displayNames: displayNames,
|
||||
indexOfSoftwareTitle: make(map[uint]uint),
|
||||
deduplicatedList: make([]*hostSoftware, 0, len(hostSoftwareList)),
|
||||
}
|
||||
|
||||
hostSoftwareList = deduplicatedList
|
||||
for _, softwareTitleRecord := range hostSoftwareList {
|
||||
assembler.addRecord(ctx, ds, host, teamID, softwareTitleRecord)
|
||||
}
|
||||
hostSoftwareList = assembler.deduplicatedList
|
||||
}
|
||||
|
||||
perPage := opts.ListOptions.PerPage
|
||||
|
||||
Reference in New Issue
Block a user