Add bundle_identifier to top-level host software API response (#42188)
- @noahtalerman: For the following story: - https://github.com/fleetdm/fleet/issues/43557 ## Summary - Adds `bundle_identifier` as a top-level field on `HostSoftwareWithInstaller` struct, which is the response type for all host software API endpoints (`GET /api/v1/fleet/hosts/{id}/software`, `GET /api/v1/fleet/device/{token}/software`, etc.) - The value is sourced directly from `software_titles.bundle_identifier` via the SQL query, so it is always present even when `installed_versions` is empty (e.g., software that has never been installed on a host) - Falls back to `installed_versions[0].bundle_identifier` if the title-level value is not available - The field is retained inside `installed_versions` for full backwards compatibility ## Changes ### `server/fleet/software_installer.go` - Added `BundleIdentifier string` field with `json:"bundle_identifier,omitempty" db:"-"` tag to `HostSoftwareWithInstaller` struct ### `server/datastore/mysql/software.go` - Added `TitleBundleIdentifier` field to internal `hostSoftware` struct mapped to `title_bundle_identifier` DB column - Added `software_titles.bundle_identifier AS title_bundle_identifier` to all four SQL query branches: - Software installers SELECT - VPP apps SELECT - In-house apps SELECT - Available-for-install SELECT (`stmtAvailable`) - Added `software_titles.bundle_identifier` to GROUP BY clauses for software installers and VPP apps - In the return path, populates `BundleIdentifier` from `TitleBundleIdentifier` (with fallback to first installed version) ### `server/datastore/mysql/software_test.go` - Added `BundleIdentifier` assertion to both `compareResults` helper functions (macOS/Linux and iOS/iPadOS test suites) - Added explicit top-level `BundleIdentifier` assertions in `testListHostSoftwareWithVPPApps` where the installed version bundle identifier is already verified ## Example response shape ```json { "id": 121, "name": "Google Chrome.app", "bundle_identifier": "com.google.Chrome", "icon_url": null, "software_package": { ... }, "app_store_app": null, "source": "apps", "status": "failed_install", "installed_versions": [ { "version": "121.0", "bundle_identifier": "com.google.Chrome", ... } ] } ``` Built for [ntalerman](https://fleetdm.slack.com/archives/D0AEA6U4SM9/p1774036621198819?thread_ts=1774035719.384099&cid=D0AEA6U4SM9) by [Kilo for Slack](https://kilo.ai/features/slack-integration) --------- Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com> Co-authored-by: Kilo Code <kilo@kilo.ai> Co-authored-by: Noah Talerman <47070608+noahtalerman@users.noreply.github.com> Co-authored-by: jkatz01 <yehonatankatz@gmail.com>
This commit is contained in:
co-authored by
kiloconnect[bot]
Kilo Code
Noah Talerman
jkatz01
parent
3e10ad717c
commit
6becc62393
+155
-12
@@ -4965,6 +4965,7 @@ Currently, `hash_sha256`, `executable_sha256`, and `executable_path` are only su
|
||||
{
|
||||
"id": 121,
|
||||
"name": "Google Chrome.app",
|
||||
"bundle_identifier": "com.google.Chrome",
|
||||
"icon_url": null,
|
||||
"software_package": {
|
||||
"name": "GoogleChrome.pkg",
|
||||
@@ -4999,27 +5000,169 @@ Currently, `hash_sha256`, `executable_sha256`, and `executable_path` are only su
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 134,
|
||||
"name": "Falcon.app",
|
||||
"id": 147,
|
||||
"name": "Logic Pro",
|
||||
"bundle_identifier": "com.apple.logic10",
|
||||
"icon_url": "/api/latest/fleet/software/titles/147/icon?fleet_id=2",
|
||||
"software_package": null,
|
||||
"app_store_app": {
|
||||
"app_store_id": "1091189122",
|
||||
"platform": "darwin",
|
||||
"version": "2.04",
|
||||
"self_service": false,
|
||||
"last_install": {
|
||||
"command_uuid": "0aa14ae5-58fe-491a-ac9a-e4ee2b3aac40",
|
||||
"installed_at": "2024-05-15T15:23:57Z"
|
||||
},
|
||||
},
|
||||
"source": "apps",
|
||||
"status": "installed",
|
||||
"installed_versions": [
|
||||
{
|
||||
"version": "118.0",
|
||||
"bundle_identifier": "com.apple.logic10",
|
||||
"last_opened_at": "2024-04-01T23:03:07Z",
|
||||
"vulnerabilities": ["CVE-2023-1234"],
|
||||
"installed_paths": ["/Applications/Logic Pro.app"],
|
||||
"signature_information": [
|
||||
{
|
||||
"installed_path": "/Applications/Logic Pro.app",
|
||||
"team_identifier": "",
|
||||
"hash_sha256": null,
|
||||
"executable_sha256": null,
|
||||
"executable_path": null
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 150,
|
||||
"name": "GitHub Copilot",
|
||||
"software_package": null,
|
||||
"app_store_app": null,
|
||||
"source": "jetbrains_plugins",
|
||||
"extension_for": "goland",
|
||||
"installed_versions": [
|
||||
{
|
||||
"version": "1.2.3",
|
||||
"vulnerabilities": [],
|
||||
"installed_paths": ["/Users/username/Library/Application Support/JetBrains/GoLand2025.2/plugins/github-copilot-intellij"],
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"name": "MyCustomApp",
|
||||
"software_package": {
|
||||
"name": "MyCustomApp-1.12.ipa",
|
||||
"platform": "ios",
|
||||
"version": "1.12",
|
||||
"self_service": false,
|
||||
"automatic_install_policies": null,
|
||||
"last_install": null,
|
||||
"last_uninstall": null
|
||||
},
|
||||
"app_store_app": null,
|
||||
"versions_count": 1,
|
||||
"source": "ios_apps",
|
||||
"hosts_count": 48,
|
||||
"versions": [
|
||||
{
|
||||
"id": 123,
|
||||
"version": "1.12",
|
||||
"vulnerabilities": null
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"has_next_results": false,
|
||||
"has_previous_results": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Get host's software by Fleet Desktop token
|
||||
|
||||
`GET /api/v1/fleet/device/:token/software`
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | In | Description |
|
||||
| ---- | ------- | ---- | ---------------------------- |
|
||||
| token | string | path | **Required.** The host's [Fleet Desktop token](https://fleetdm.com/guides/fleet-desktop#secure-fleet-desktop). |
|
||||
| query | string | query | Search query keywords. Searchable fields include `name`. |
|
||||
| available_for_install | boolean | query | If `true` or `1`, only list software that is available for install (added by the user). Default is `false`. |
|
||||
| self_service | boolean | query | If `true` or `1`, only lists self-service software. Default is `false`. |
|
||||
| vulnerable | boolean | query | If `true` or `1`, only list software that have vulnerabilities. Default is `false`. |
|
||||
| page | integer | query | Page number of the results to fetch.|
|
||||
| per_page | integer | query | Results per page.|
|
||||
| order_key | string | query | What to order results by. Options include `"name"`. Default is `"name"`. |
|
||||
| order_direction | string | query | **Requires `order_key`**. The direction of the order given the order key. Options include `"asc"` and `"desc"`. Default is `"asc"`. |
|
||||
| min_cvss_score | integer | query | _Available in Fleet Premium_. Filters to include only software with vulnerabilities that have a CVSS version 3.x base score higher than the specified value. |
|
||||
| max_cvss_score | integer | query | _Available in Fleet Premium_. Filters to only include software with vulnerabilities that have a CVSS version 3.x base score lower than what's specified. |
|
||||
| exploit | boolean | query | _Available in Fleet Premium_. If `true`, filters to only include software with vulnerabilities that have been actively exploited in the wild (`cisa_known_exploit: true`). Default is `false`. |
|
||||
|
||||
On macOS hosts, `last_opened_at` is supported for software from the `apps` source and is the last open time of the most recently installed version of the software. After an update, it may be empty until the software is opened again.
|
||||
|
||||
On Windows hosts, `last_opened_at` is supported for software from the `programs` source. On Linux hosts, `last_opened_at` is supported for software from the `deb_packages` and `rpm_packages` sources. On Windows and Linux hosts, it represents the last open time of any version.
|
||||
|
||||
Currently, `hash_sha256`, `executable_sha256`, and `executable_path` are only supported for macOS software from the `apps` source. `hash_sha256` is the [`cdhash_sha256`](https://fleetdm.com/tables/codesign).
|
||||
|
||||
#### Example
|
||||
|
||||
`GET /api/v1/fleet/device/:token/software`
|
||||
|
||||
##### Default response
|
||||
|
||||
`Status: 200`
|
||||
|
||||
```json
|
||||
{
|
||||
"count": 3,
|
||||
"software": [
|
||||
{
|
||||
"id": 121,
|
||||
"name": "Google Chrome.app",
|
||||
"bundle_identifier": "com.google.Chrome"
|
||||
"icon_url": null,
|
||||
"software_package": {
|
||||
"name": "FalconSensor-6.44.pkg",
|
||||
"name": "GoogleChrome.pkg",
|
||||
"platform": "darwin",
|
||||
"self_service": false,
|
||||
"last_install": null,
|
||||
"last_uninstall": {
|
||||
"script_execution_id": "ed579e73-0f41-46c8-aaf4-3c1e5880ed27",
|
||||
"uninstalled_at": "2024-05-15T15:23:57Z"
|
||||
"version": "125.12.0.3",
|
||||
"self_service": true,
|
||||
"last_install": {
|
||||
"install_uuid": "8bbb8ac2-b254-4387-8cba-4d8a0407368b",
|
||||
"installed_at": "2024-05-15T15:23:57Z"
|
||||
}
|
||||
},
|
||||
"app_store_app": null,
|
||||
"source": "",
|
||||
"status": "pending_uninstall",
|
||||
"installed_versions": [],
|
||||
"source": "apps",
|
||||
"status": "failed_install",
|
||||
"installed_versions": [
|
||||
{
|
||||
"version": "121.0",
|
||||
"bundle_identifier": "com.google.Chrome",
|
||||
"last_opened_at": "2024-04-01T23:03:07Z",
|
||||
"vulnerabilities": ["CVE-2023-1234","CVE-2023-4321","CVE-2023-7654"],
|
||||
"installed_paths": ["/Applications/Google Chrome.app"],
|
||||
"signature_information": [
|
||||
{
|
||||
"installed_path": "/Applications/Google Chrome.app",
|
||||
"team_identifier": "EQHXZ8M8AV",
|
||||
"hash_sha256": "a45d00ac9bf21e108fa8e452fabe4d9e05e6765b",
|
||||
"executable_sha256": "7afc9d01a62f03a2de9637936d4afe68090d2de18d03f29c88cfb0b1ba63587f",
|
||||
"executable_path": "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 147,
|
||||
"name": "Logic Pro",
|
||||
"bundle_identifier": "com.apple.logic10"
|
||||
"icon_url": "/api/latest/fleet/software/titles/147/icon?fleet_id=2",
|
||||
"software_package": null,
|
||||
"app_store_app": {
|
||||
@@ -5804,7 +5947,7 @@ Grant a blocked host access for a single login. Requires Okta conditional access
|
||||
|
||||
| Name | Type | In | Description |
|
||||
| ----------- | ------ | ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| token | string | path | **Required.** The host's [device authentication token](https://fleetdm.com/guides/fleet-desktop#secure-fleet-desktop). |
|
||||
| token | string | path | **Required.** The host's [Fleet Desktop token](https://fleetdm.com/guides/fleet-desktop#secure-fleet-desktop). |
|
||||
|
||||
|
||||
#### Example
|
||||
|
||||
@@ -3430,6 +3430,7 @@ type hostSoftware struct {
|
||||
ExitCode *int `db:"exit_code"`
|
||||
LastOpenedAt *time.Time `db:"last_opened_at"`
|
||||
BundleIdentifier *string `db:"bundle_identifier"`
|
||||
TitleBundleIdentifier *string `db:"title_bundle_identifier"`
|
||||
Version *string `db:"version"`
|
||||
SoftwareID *uint `db:"software_id"`
|
||||
SoftwareSource *string `db:"software_source"`
|
||||
@@ -4911,6 +4912,7 @@ func (ds *Datastore) ListHostSoftware(ctx context.Context, host *fleet.Host, opt
|
||||
st.source,
|
||||
st.extension_for,
|
||||
st.upgrade_code,
|
||||
st.bundle_identifier as title_bundle_identifier,
|
||||
si.id as installer_id,
|
||||
si.self_service as package_self_service,
|
||||
si.filename as package_name,
|
||||
@@ -5941,6 +5943,7 @@ func (ds *Datastore) ListHostSoftware(ctx context.Context, host *fleet.Host, opt
|
||||
software_titles.source AS source,
|
||||
software_titles.extension_for AS extension_for,
|
||||
software_titles.upgrade_code AS upgrade_code, -- should be empty or non-empty string for "programs" sourced software, null otherwise
|
||||
software_titles.bundle_identifier AS title_bundle_identifier,
|
||||
software_installers.id AS installer_id,
|
||||
software_installers.self_service AS package_self_service,
|
||||
software_installers.filename AS package_name,
|
||||
@@ -5969,6 +5972,7 @@ func (ds *Datastore) ListHostSoftware(ctx context.Context, host *fleet.Host, opt
|
||||
software_titles.source,
|
||||
software_titles.extension_for,
|
||||
software_titles.upgrade_code,
|
||||
software_titles.bundle_identifier,
|
||||
software_installers.id,
|
||||
software_installers.self_service,
|
||||
software_installers.filename,
|
||||
@@ -5986,6 +5990,7 @@ func (ds *Datastore) ListHostSoftware(ctx context.Context, host *fleet.Host, opt
|
||||
software_titles.source AS source,
|
||||
software_titles.extension_for AS extension_for,
|
||||
software_titles.upgrade_code AS upgrade_code, -- should always be null for vpp (mac) apps
|
||||
software_titles.bundle_identifier AS title_bundle_identifier,
|
||||
NULL AS installer_id,
|
||||
NULL AS package_self_service,
|
||||
NULL AS package_name,
|
||||
@@ -6013,7 +6018,8 @@ func (ds *Datastore) ListHostSoftware(ctx context.Context, host *fleet.Host, opt
|
||||
software_titles.name,
|
||||
software_titles.source,
|
||||
software_titles.extension_for,
|
||||
software_titles.upgrade_code
|
||||
software_titles.upgrade_code,
|
||||
software_titles.bundle_identifier
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -6027,6 +6033,7 @@ func (ds *Datastore) ListHostSoftware(ctx context.Context, host *fleet.Host, opt
|
||||
software_titles.source AS source,
|
||||
software_titles.extension_for AS extension_for,
|
||||
software_titles.upgrade_code AS upgrade_code,
|
||||
software_titles.bundle_identifier AS title_bundle_identifier,
|
||||
NULL AS installer_id,
|
||||
NULL AS package_self_service,
|
||||
NULL AS package_name,
|
||||
@@ -6054,7 +6061,8 @@ func (ds *Datastore) ListHostSoftware(ctx context.Context, host *fleet.Host, opt
|
||||
software_titles.name,
|
||||
software_titles.source,
|
||||
software_titles.extension_for,
|
||||
software_titles.upgrade_code
|
||||
software_titles.upgrade_code,
|
||||
software_titles.bundle_identifier
|
||||
`)
|
||||
}
|
||||
stmt = fmt.Sprintf(stmt, replacements...)
|
||||
@@ -6454,6 +6462,15 @@ func (ds *Datastore) ListHostSoftware(ctx context.Context, host *fleet.Host, opt
|
||||
|
||||
software := make([]*fleet.HostSoftwareWithInstaller, 0, len(hostSoftwareList))
|
||||
for _, hs := range hostSoftwareList {
|
||||
// Populate top-level bundle_identifier from the software_titles table
|
||||
// (available even when installed_versions is empty), falling back to
|
||||
// the first installed version for backwards compatibility.
|
||||
switch {
|
||||
case hs.TitleBundleIdentifier != nil && *hs.TitleBundleIdentifier != "":
|
||||
hs.HostSoftwareWithInstaller.BundleIdentifier = *hs.TitleBundleIdentifier
|
||||
case len(hs.InstalledVersions) > 0 && hs.InstalledVersions[0].BundleIdentifier != "":
|
||||
hs.HostSoftwareWithInstaller.BundleIdentifier = hs.InstalledVersions[0].BundleIdentifier
|
||||
}
|
||||
software = append(software, &hs.HostSoftwareWithInstaller)
|
||||
}
|
||||
|
||||
|
||||
@@ -4264,6 +4264,7 @@ func testListHostSoftware(t *testing.T, ds *Datastore) {
|
||||
require.True(t, ok, "unexpected software %s%s", g.Name, g.Source)
|
||||
require.Equal(t, e.Name, g.Name)
|
||||
require.Equal(t, e.Source, g.Source)
|
||||
require.Equal(t, e.BundleIdentifier, g.BundleIdentifier)
|
||||
if e.SoftwarePackage != nil {
|
||||
require.Equal(t, e.SoftwarePackage.SelfService, g.SoftwarePackage.SelfService)
|
||||
require.Equal(t, e.SoftwarePackage.AppStoreID, g.SoftwarePackage.AppStoreID)
|
||||
@@ -4964,22 +4965,25 @@ func testListHostSoftware(t *testing.T, ds *Datastore) {
|
||||
compareResults(expected, sw, true, i1.Name+i1.Source, i3.Name+i3.Source, i2.Name+i2.Source, i4.Name+i4.Source, i5.Name+i5.Source, i6.Name+i6.Source) // i3 is for team, i2 is available (excluded)
|
||||
|
||||
expected["vpp1apps"] = fleet.HostSoftwareWithInstaller{
|
||||
Name: "vpp1",
|
||||
Source: "apps",
|
||||
Status: expectStatus(fleet.SoftwareInstalled),
|
||||
AppStoreApp: &fleet.SoftwarePackageOrApp{AppStoreID: vpp1, Platform: "darwin", SelfService: ptr.Bool(false), LastInstall: &fleet.HostSoftwareInstall{CommandUUID: vpp1CmdUUID}},
|
||||
Name: "vpp1",
|
||||
Source: "apps",
|
||||
BundleIdentifier: "com.app.vpp1",
|
||||
Status: expectStatus(fleet.SoftwareInstalled),
|
||||
AppStoreApp: &fleet.SoftwarePackageOrApp{AppStoreID: vpp1, Platform: "darwin", SelfService: new(false), LastInstall: &fleet.HostSoftwareInstall{CommandUUID: vpp1CmdUUID}},
|
||||
}
|
||||
expected["vpp2apps"] = fleet.HostSoftwareWithInstaller{
|
||||
Name: "vpp2",
|
||||
Source: "apps",
|
||||
Status: expectStatus(fleet.SoftwareInstallPending),
|
||||
AppStoreApp: &fleet.SoftwarePackageOrApp{AppStoreID: vpp2, Platform: "darwin", SelfService: ptr.Bool(false), LastInstall: &fleet.HostSoftwareInstall{CommandUUID: vpp2bCmdUUID}},
|
||||
Name: "vpp2",
|
||||
Source: "apps",
|
||||
BundleIdentifier: "com.app.vpp2",
|
||||
Status: expectStatus(fleet.SoftwareInstallPending),
|
||||
AppStoreApp: &fleet.SoftwarePackageOrApp{AppStoreID: vpp2, Platform: "darwin", SelfService: new(false), LastInstall: &fleet.HostSoftwareInstall{CommandUUID: vpp2bCmdUUID}},
|
||||
}
|
||||
expected["vpp3apps"] = fleet.HostSoftwareWithInstaller{
|
||||
Name: "vpp3",
|
||||
Source: "apps",
|
||||
Status: nil,
|
||||
AppStoreApp: &fleet.SoftwarePackageOrApp{AppStoreID: vpp3, Platform: "darwin", SelfService: ptr.Bool(true)},
|
||||
Name: "vpp3",
|
||||
Source: "apps",
|
||||
BundleIdentifier: "com.app.vpp3",
|
||||
Status: nil,
|
||||
AppStoreApp: &fleet.SoftwarePackageOrApp{AppStoreID: vpp3, Platform: "darwin", SelfService: new(true)},
|
||||
}
|
||||
|
||||
expectedAvailableOnly["vpp1apps"] = expected["vpp1apps"]
|
||||
@@ -5027,9 +5031,10 @@ func testListHostSoftware(t *testing.T, ds *Datastore) {
|
||||
compareResults(map[string]fleet.HostSoftwareWithInstaller{
|
||||
i3.Name + i3.Source: expected[i3.Name+i3.Source],
|
||||
"vpp1apps": {
|
||||
Name: "vpp1",
|
||||
Source: "apps",
|
||||
Status: expectStatus(fleet.SoftwareInstallPending),
|
||||
Name: "vpp1",
|
||||
Source: "apps",
|
||||
BundleIdentifier: "com.app.vpp1",
|
||||
Status: expectStatus(fleet.SoftwareInstallPending),
|
||||
AppStoreApp: &fleet.SoftwarePackageOrApp{
|
||||
AppStoreID: vpp1,
|
||||
Platform: "darwin",
|
||||
@@ -5631,6 +5636,7 @@ func testListIOSHostSoftware(t *testing.T, ds *Datastore) {
|
||||
require.True(t, ok, "unexpected software name:%s source:%s", g.Name, g.Source)
|
||||
require.Equal(t, e.Name, g.Name)
|
||||
require.Equal(t, e.Source, g.Source)
|
||||
require.Equal(t, e.BundleIdentifier, g.BundleIdentifier)
|
||||
if e.SoftwarePackage != nil {
|
||||
require.Equal(t, e.SoftwarePackage.SelfService, g.SoftwarePackage.SelfService)
|
||||
require.Equal(t, e.SoftwarePackage.AppStoreID, g.SoftwarePackage.AppStoreID)
|
||||
@@ -5773,28 +5779,32 @@ func testListIOSHostSoftware(t *testing.T, ds *Datastore) {
|
||||
compareResults(expected, sw, true) // i3 is for team, i2 is available (excluded)
|
||||
|
||||
expected["vpp1ios_apps"] = fleet.HostSoftwareWithInstaller{
|
||||
Name: "vpp1",
|
||||
Source: "ios_apps",
|
||||
Status: expectStatus(fleet.SoftwareInstalled),
|
||||
AppStoreApp: &fleet.SoftwarePackageOrApp{AppStoreID: vpp1, Platform: "ios", SelfService: ptr.Bool(false), LastInstall: &fleet.HostSoftwareInstall{CommandUUID: vpp1CmdUUID}},
|
||||
Name: "vpp1",
|
||||
Source: "ios_apps",
|
||||
BundleIdentifier: "com.app.vpp1",
|
||||
Status: expectStatus(fleet.SoftwareInstalled),
|
||||
AppStoreApp: &fleet.SoftwarePackageOrApp{AppStoreID: vpp1, Platform: "ios", SelfService: new(false), LastInstall: &fleet.HostSoftwareInstall{CommandUUID: vpp1CmdUUID}},
|
||||
}
|
||||
expected["vpp2ios_apps"] = fleet.HostSoftwareWithInstaller{
|
||||
Name: "vpp2",
|
||||
Source: "ios_apps",
|
||||
Status: expectStatus(fleet.SoftwareInstallPending),
|
||||
AppStoreApp: &fleet.SoftwarePackageOrApp{AppStoreID: vpp2, Platform: "ios", SelfService: ptr.Bool(false), LastInstall: &fleet.HostSoftwareInstall{CommandUUID: vpp2bCmdUUID}},
|
||||
Name: "vpp2",
|
||||
Source: "ios_apps",
|
||||
BundleIdentifier: "com.app.vpp2",
|
||||
Status: expectStatus(fleet.SoftwareInstallPending),
|
||||
AppStoreApp: &fleet.SoftwarePackageOrApp{AppStoreID: vpp2, Platform: "ios", SelfService: new(false), LastInstall: &fleet.HostSoftwareInstall{CommandUUID: vpp2bCmdUUID}},
|
||||
}
|
||||
expected["vpp3ios_apps"] = fleet.HostSoftwareWithInstaller{
|
||||
Name: "vpp3",
|
||||
Source: "ios_apps",
|
||||
Status: nil,
|
||||
AppStoreApp: &fleet.SoftwarePackageOrApp{AppStoreID: vpp3, Platform: "ios", SelfService: ptr.Bool(false)},
|
||||
Name: "vpp3",
|
||||
Source: "ios_apps",
|
||||
BundleIdentifier: "com.app.vpp3",
|
||||
Status: nil,
|
||||
AppStoreApp: &fleet.SoftwarePackageOrApp{AppStoreID: vpp3, Platform: "ios", SelfService: new(false)},
|
||||
}
|
||||
expected["vpp4ios_apps"] = fleet.HostSoftwareWithInstaller{
|
||||
Name: "vpp4",
|
||||
Source: "ios_apps",
|
||||
Status: nil,
|
||||
AppStoreApp: &fleet.SoftwarePackageOrApp{AppStoreID: vpp4, Platform: "ios", SelfService: ptr.Bool(false)},
|
||||
Name: "vpp4",
|
||||
Source: "ios_apps",
|
||||
BundleIdentifier: "com.app.vpp4",
|
||||
Status: nil,
|
||||
AppStoreApp: &fleet.SoftwarePackageOrApp{AppStoreID: vpp4, Platform: "ios", SelfService: new(false)},
|
||||
}
|
||||
expectedAvailableOnly := map[string]fleet.HostSoftwareWithInstaller{}
|
||||
expectedAvailableOnly["vpp1ios_apps"] = expected["vpp1ios_apps"]
|
||||
@@ -5949,6 +5959,8 @@ func testListHostSoftwareWithVPPApps(t *testing.T, ds *Datastore) {
|
||||
assert.Equal(t, "1.2.3", sw[0].InstalledVersions[0].Version)
|
||||
assert.Equal(t, "apps", sw[0].InstalledVersions[0].Source)
|
||||
assert.Equal(t, vPPApp.BundleIdentifier, sw[0].InstalledVersions[0].BundleIdentifier)
|
||||
// top-level bundle_identifier comes from software_titles.bundle_identifier
|
||||
assert.Equal(t, vPPApp.BundleIdentifier, sw[0].BundleIdentifier)
|
||||
|
||||
// The vpp app is installed by fleet, and also has been inventoried by osquery
|
||||
// Ensure we don't lose the version for the vpp app
|
||||
@@ -5984,6 +5996,7 @@ func testListHostSoftwareWithVPPApps(t *testing.T, ds *Datastore) {
|
||||
assert.Equal(t, vPPApp.Name, sw[0].Name)
|
||||
assert.Equal(t, vPPApp.AdamID, sw[0].AppStoreApp.AppStoreID)
|
||||
assert.Equal(t, "0.1.0", sw[0].InstalledVersions[0].Version)
|
||||
assert.Equal(t, vPPApp.BundleIdentifier, sw[0].BundleIdentifier)
|
||||
assert.Nil(t, sw[0].Status)
|
||||
|
||||
// insert an icon
|
||||
|
||||
@@ -735,6 +735,7 @@ func IsScriptPackage(ext string) bool {
|
||||
type HostSoftwareWithInstaller struct {
|
||||
ID uint `json:"id" db:"id"`
|
||||
Name string `json:"name" db:"name"`
|
||||
BundleIdentifier string `json:"bundle_identifier,omitempty" db:"-"`
|
||||
IconUrl *string `json:"icon_url" db:"-"`
|
||||
Source string `json:"source" db:"source"`
|
||||
ExtensionFor string `json:"extension_for" db:"extension_for"`
|
||||
|
||||
Reference in New Issue
Block a user