mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 21:47:20 +00:00
818 lines
26 KiB
TypeScript
818 lines
26 KiB
TypeScript
import { startCase } from "lodash";
|
|
import PropTypes from "prop-types";
|
|
|
|
import { IconNames } from "components/icons";
|
|
|
|
import { HOST_APPLE_PLATFORMS, Platform } from "./platform";
|
|
import vulnerabilityInterface from "./vulnerability";
|
|
import { ILabelSoftwareTitle } from "./label";
|
|
import { ICommandResult } from "./command";
|
|
|
|
export default PropTypes.shape({
|
|
type: PropTypes.string,
|
|
name: PropTypes.string,
|
|
version: PropTypes.string,
|
|
source: PropTypes.string,
|
|
id: PropTypes.number,
|
|
vulnerabilities: PropTypes.arrayOf(vulnerabilityInterface),
|
|
});
|
|
|
|
export interface ISoftwareResponse {
|
|
counts_updated_at: string;
|
|
software: ISoftware[];
|
|
}
|
|
|
|
export interface ISoftwareCountResponse {
|
|
count: number;
|
|
}
|
|
|
|
export interface IGetSoftwareByIdResponse {
|
|
software: ISoftware;
|
|
}
|
|
|
|
// TODO: old software interface. replaced with ISoftwareVersion
|
|
// check to see if we still need this.
|
|
export interface ISoftware {
|
|
id: number;
|
|
name: string; // e.g., "Figma.app"
|
|
/** Custom name set per team by admin */
|
|
display_name?: string; // e.g. "Figma for Desktop"
|
|
version: string; // e.g., "2.1.11"
|
|
bundle_identifier?: string | null; // e.g., "com.figma.Desktop"
|
|
application_id?: string | null; // e.g., "us.zoom.videomeetings" for Android apps
|
|
source: string; // "apps" | "ipados_apps" | "ios_apps" | "programs" | "rpm_packages" | "deb_packages" | "android_apps" | ?
|
|
generated_cpe: string;
|
|
vulnerabilities: ISoftwareVulnerability[] | null;
|
|
hosts_count?: number;
|
|
last_opened_at?: string | null; // e.g., "2021-08-18T15:11:35Z”
|
|
installed_paths?: string[];
|
|
extension_for?: string;
|
|
vendor?: string;
|
|
icon_url: string | null; // Only available on team view if an admin uploaded an icon to a team's software
|
|
}
|
|
|
|
export type IVulnerabilitySoftware = Omit<
|
|
ISoftware,
|
|
"vulnerabilities" | "icon_url"
|
|
> & {
|
|
resolved_in_version: string;
|
|
};
|
|
|
|
export interface ISoftwareTitleVersion {
|
|
id: number;
|
|
version: string;
|
|
vulnerabilities: string[] | null; // TODO: does this return null or is it omitted?
|
|
hosts_count?: number;
|
|
}
|
|
|
|
export interface ISoftwareInstallPolicy {
|
|
id: number;
|
|
name: string;
|
|
}
|
|
|
|
export type SoftwareCategory =
|
|
| "Browsers"
|
|
| "Communication"
|
|
| "Developer tools"
|
|
| "Productivity";
|
|
|
|
export interface ISoftwarePackageStatus {
|
|
installed: number;
|
|
pending_install: number;
|
|
failed_install: number;
|
|
pending_uninstall: number;
|
|
failed_uninstall: number;
|
|
}
|
|
|
|
export interface ISoftwareAppStoreAppStatus {
|
|
installed: number;
|
|
pending: number;
|
|
failed: number;
|
|
}
|
|
|
|
export interface ISoftwarePackage {
|
|
name: string;
|
|
/** Not included in SoftwareTitle software.software_package response, hoisted up one level
|
|
* Custom name set per team by admin
|
|
*/
|
|
display_name?: string;
|
|
title_id: number;
|
|
url: string;
|
|
version: string;
|
|
uploaded_at: string;
|
|
install_script: string;
|
|
uninstall_script: string;
|
|
pre_install_query?: string;
|
|
post_install_script?: string;
|
|
automatic_install?: boolean; // POST only
|
|
self_service: boolean;
|
|
icon_url: string | null;
|
|
status: ISoftwarePackageStatus;
|
|
automatic_install_policies?: ISoftwareInstallPolicy[] | null;
|
|
install_during_setup?: boolean;
|
|
labels_include_any: ILabelSoftwareTitle[] | null;
|
|
labels_exclude_any: ILabelSoftwareTitle[] | null;
|
|
categories?: SoftwareCategory[] | null;
|
|
fleet_maintained_app_id?: number | null;
|
|
hash_sha256?: string | null;
|
|
}
|
|
|
|
export interface IAppStoreApp {
|
|
name: string;
|
|
/** Not included in SoftwareTitle software.app_store_app response, hoisted up one level
|
|
* Custom name set per team by admin
|
|
*/
|
|
display_name?: string;
|
|
app_store_id: string; // API returns this as a string
|
|
latest_version: string;
|
|
created_at: string;
|
|
icon_url: string;
|
|
self_service: boolean;
|
|
platform: typeof HOST_APPLE_PLATFORMS[number] | "android";
|
|
status: ISoftwareAppStoreAppStatus;
|
|
install_during_setup?: boolean;
|
|
automatic_install_policies?: ISoftwareInstallPolicy[] | null;
|
|
automatic_install?: boolean;
|
|
last_install?: IAppLastInstall | null;
|
|
last_uninstall?: {
|
|
script_execution_id: string;
|
|
uninstalled_at: string;
|
|
} | null;
|
|
version?: string;
|
|
labels_include_any: ILabelSoftwareTitle[] | null;
|
|
labels_exclude_any: ILabelSoftwareTitle[] | null;
|
|
categories?: SoftwareCategory[] | null;
|
|
configuration?: string;
|
|
}
|
|
|
|
/**
|
|
* package: includes FMA, custom packages, and are defined under software_package
|
|
* app-store: includes VPP, Google Play Store apps and are defined under app_store_app
|
|
*/
|
|
export type InstallerType = "package" | "app-store";
|
|
|
|
export const isSoftwarePackage = (
|
|
data: ISoftwarePackage | IAppStoreApp
|
|
): data is ISoftwarePackage =>
|
|
(data as ISoftwarePackage).install_script !== undefined;
|
|
|
|
export interface ISoftwareTitle {
|
|
id: number;
|
|
name: string;
|
|
/** Custom name set per team by admin */
|
|
display_name?: string;
|
|
icon_url: string | null;
|
|
versions_count: number;
|
|
source: SoftwareSource;
|
|
extension_for?: SoftwareExtensionFor;
|
|
hosts_count: number;
|
|
versions: ISoftwareTitleVersion[] | null;
|
|
software_package: ISoftwarePackage | null;
|
|
app_store_app: IAppStoreApp | null;
|
|
/** @deprecated Use extension_for instead */
|
|
browser?: string;
|
|
}
|
|
|
|
export interface ISoftwareTitleDetails {
|
|
id: number;
|
|
name: string;
|
|
/** Custom name set per team by admin */
|
|
display_name?: string;
|
|
icon_url: string | null;
|
|
software_package: ISoftwarePackage | null;
|
|
app_store_app: IAppStoreApp | null;
|
|
source: SoftwareSource;
|
|
extension_for?: SoftwareExtensionFor;
|
|
hosts_count: number;
|
|
versions: ISoftwareTitleVersion[] | null;
|
|
counts_updated_at?: string;
|
|
bundle_identifier?: string;
|
|
versions_count?: number;
|
|
/** @deprecated Use extension_for instead */
|
|
browser?: string;
|
|
}
|
|
|
|
export interface ISoftwareVulnerability {
|
|
cve: string;
|
|
details_link: string;
|
|
cvss_score?: number | null;
|
|
epss_probability?: number | null;
|
|
cisa_known_exploit?: boolean | null;
|
|
cve_published?: string | null;
|
|
cve_description?: string | null;
|
|
resolved_in_version?: string | null;
|
|
created_at?: string | null;
|
|
}
|
|
|
|
export interface ISoftwareVersion {
|
|
id: number;
|
|
name: string; // e.g., "Figma.app"
|
|
/** Custom name set per team by admin */
|
|
display_name?: string; // e.g. "Figma for Desktop"
|
|
version: string; // e.g., "2.1.11"
|
|
bundle_identifier?: string; // e.g., "com.figma.Desktop"
|
|
source: SoftwareSource;
|
|
extension_for: SoftwareExtensionFor;
|
|
release: string; // TODO: on software/verions/:id?
|
|
vendor: string;
|
|
arch: string; // e.g., "x86_64" // TODO: on software/verions/:id?
|
|
generated_cpe: string;
|
|
vulnerabilities: ISoftwareVulnerability[] | null;
|
|
hosts_count?: number;
|
|
/** @deprecated Use extension_for instead */
|
|
browser?: string;
|
|
}
|
|
|
|
export const SOURCE_TYPE_CONVERSION = {
|
|
apt_sources: "Package (APT)",
|
|
deb_packages: "Package (deb)",
|
|
portage_packages: "Package (Portage)",
|
|
rpm_packages: "Package (RPM)",
|
|
yum_sources: "Package (YUM)",
|
|
npm_packages: "Package (npm)",
|
|
pacman_packages: "Package (pacman)",
|
|
atom_packages: "Package (Atom)", // Atom packages were removed from software inventory. Mapping is maintained for backwards compatibility. (2023-12-04)
|
|
python_packages: "Package (Python)",
|
|
tgz_packages: "Package (tar)",
|
|
apps: "Application (macOS)",
|
|
ios_apps: "Application (iOS)",
|
|
ipados_apps: "Application (iPadOS)",
|
|
android_apps: "Application (Android)",
|
|
chrome_extensions: "Browser plugin", // chrome_extensions can include any chrome-based browser (e.g., edge), so we rely instead on the `extension_for` field computed by Fleet server and fallback to this value if it is not present.
|
|
firefox_addons: "Browser plugin (Firefox)",
|
|
safari_extensions: "Browser plugin (Safari)",
|
|
homebrew_packages: "Package (Homebrew)",
|
|
programs: "Program (Windows)",
|
|
ie_extensions: "Browser plugin (IE)",
|
|
chocolatey_packages: "Package (Chocolatey)",
|
|
pkg_packages: "Package (pkg)",
|
|
vscode_extensions: "IDE extension", // vscode_extensions can include any vscode-based editor (e.g., Cursor, Trae, Windsurf), so we rely instead on the `extension_for` field computed by Fleet server and fallback to this value if it is not present.
|
|
sh_packages: "Payload-free (Linux)",
|
|
ps1_packages: "Payload-free (Windows)",
|
|
jetbrains_plugins: "IDE extension", // jetbrains_plugins can include any JetBrains IDE (e.g., IntelliJ, PyCharm, WebStorm), so we rely instead on the `extension_for` field computed by Fleet server and fallback to this value if it is not present.
|
|
} as const;
|
|
|
|
export type SoftwareSource = keyof typeof SOURCE_TYPE_CONVERSION;
|
|
|
|
/** Map installable software source to platform */
|
|
export const INSTALLABLE_SOURCE_PLATFORM_CONVERSION = {
|
|
apt_sources: "linux",
|
|
deb_packages: "linux",
|
|
portage_packages: "linux",
|
|
rpm_packages: "linux",
|
|
yum_sources: "linux",
|
|
pacman_packages: "linux",
|
|
tgz_packages: "linux",
|
|
npm_packages: null,
|
|
atom_packages: null,
|
|
python_packages: null,
|
|
apps: "darwin",
|
|
ios_apps: "ios",
|
|
ipados_apps: "ipados",
|
|
android_apps: "android", // 4.76 Currently hidden upstream as not installable
|
|
chrome_extensions: null,
|
|
firefox_addons: null,
|
|
safari_extensions: null,
|
|
homebrew_packages: "darwin",
|
|
programs: "windows",
|
|
ie_extensions: null,
|
|
chocolatey_packages: "windows",
|
|
pkg_packages: "darwin",
|
|
vscode_extensions: null,
|
|
sh_packages: "linux", // 4.76 Added support for Linux hosts only
|
|
ps1_packages: "windows",
|
|
jetbrains_plugins: null,
|
|
} as const;
|
|
|
|
export const SCRIPT_PACKAGE_SOURCES = ["sh_packages", "ps1_packages"];
|
|
|
|
export const NO_VERSION_OR_HOST_DATA_SOURCES = [
|
|
"tgz_packages",
|
|
...SCRIPT_PACKAGE_SOURCES,
|
|
];
|
|
|
|
export type InstallableSoftwareSource = keyof typeof INSTALLABLE_SOURCE_PLATFORM_CONVERSION;
|
|
|
|
const EXTENSION_FOR_TYPE_CONVERSION = {
|
|
// chrome versions
|
|
chrome: "Chrome",
|
|
chromium: "Chromium",
|
|
opera: "Opera",
|
|
yandex: "Yandex",
|
|
brave: "Brave",
|
|
edge: "Edge",
|
|
edge_beta: "Edge Beta",
|
|
|
|
// vscode versions
|
|
vscode: "VSCode",
|
|
vscode_insiders: "VSCode Insiders",
|
|
vscodium: "VSCodium",
|
|
vscodium_insiders: "VSCodium Insiders",
|
|
trae: "Trae",
|
|
windsurf: "Windsurf",
|
|
cursor: "Cursor",
|
|
|
|
// jebtbrains versions
|
|
clion: "CLion",
|
|
datagrip: "DataGrip",
|
|
goland: "GoLand",
|
|
intellij_idea: "IntelliJ IDEA",
|
|
intellij_idea_community_edition: "IntelliJ IDEA Community Edition",
|
|
phpstorm: "PhpStorm",
|
|
pycharm: "PyCharm",
|
|
pycharm_community_edition: "PyCharm Community Edition",
|
|
resharper: "ReSharper",
|
|
rider: "Rider",
|
|
rubymine: "RubyMine",
|
|
rust_rov: "RustRover",
|
|
webstorm: "WebStorm",
|
|
} as const;
|
|
|
|
export type SoftwareExtensionFor =
|
|
| keyof typeof EXTENSION_FOR_TYPE_CONVERSION
|
|
| "";
|
|
|
|
export const formatSoftwareType = ({
|
|
source,
|
|
extension_for,
|
|
}: {
|
|
source: SoftwareSource;
|
|
extension_for?: SoftwareExtensionFor;
|
|
}) => {
|
|
let type: string = SOURCE_TYPE_CONVERSION[source] || "Unknown";
|
|
if (extension_for) {
|
|
type += ` (${
|
|
EXTENSION_FOR_TYPE_CONVERSION[extension_for] || startCase(extension_for)
|
|
})`;
|
|
}
|
|
return type;
|
|
};
|
|
|
|
/**
|
|
* This list comprises all possible states of software install operations.
|
|
*/
|
|
export const SOFTWARE_UNINSTALL_STATUSES = [
|
|
"uninstalled",
|
|
"pending_uninstall",
|
|
"failed_uninstall",
|
|
] as const;
|
|
|
|
export type SoftwareUninstallStatus = typeof SOFTWARE_UNINSTALL_STATUSES[number];
|
|
|
|
export const SOFTWARE_INSTALL_STATUSES = [
|
|
"installed",
|
|
"pending_install",
|
|
"failed_install",
|
|
] as const;
|
|
|
|
// Payload-free (script) software statuses
|
|
export const SOFTWARE_SCRIPT_STATUSES = [
|
|
"ran_script",
|
|
"pending_script",
|
|
"failed_script",
|
|
] as const;
|
|
|
|
export type SoftwareInstallStatus = typeof SOFTWARE_INSTALL_STATUSES[number];
|
|
|
|
export const SOFTWARE_INSTALL_UNINSTALL_STATUSES = [
|
|
...SOFTWARE_INSTALL_STATUSES,
|
|
...SOFTWARE_UNINSTALL_STATUSES,
|
|
// Payload-free (script) software statuses use API's SOFTWARE_INSTALL_STATUSES
|
|
] as const;
|
|
|
|
/*
|
|
* SoftwareInstallUninstallStatus represents the possible states of software install operations.
|
|
*/
|
|
export type SoftwareInstallUninstallStatus = typeof SOFTWARE_INSTALL_UNINSTALL_STATUSES[number];
|
|
|
|
/** Include payload-free statuses */
|
|
export const ENAHNCED_SOFTWARE_INSTALL_UNINSTALL_STATUSES = [
|
|
...SOFTWARE_INSTALL_STATUSES,
|
|
...SOFTWARE_UNINSTALL_STATUSES,
|
|
...SOFTWARE_SCRIPT_STATUSES, // Payload-free (script) software
|
|
] as const;
|
|
|
|
/*
|
|
* EnhancedSoftwareInstallUninstallStatus represents the possible states of software install operations including payload-free used in the UI.
|
|
*/
|
|
export type EnhancedSoftwareInstallUninstallStatus = typeof ENAHNCED_SOFTWARE_INSTALL_UNINSTALL_STATUSES[number];
|
|
|
|
export const isValidSoftwareInstallUninstallStatus = (
|
|
s: string | undefined | null
|
|
): s is EnhancedSoftwareInstallUninstallStatus =>
|
|
!!s &&
|
|
ENAHNCED_SOFTWARE_INSTALL_UNINSTALL_STATUSES.includes(
|
|
s as EnhancedSoftwareInstallUninstallStatus
|
|
);
|
|
|
|
export const SOFTWARE_AGGREGATE_STATUSES = [
|
|
"installed",
|
|
"pending",
|
|
"failed",
|
|
] as const;
|
|
|
|
export type SoftwareAggregateStatus = typeof SOFTWARE_AGGREGATE_STATUSES[number];
|
|
|
|
export const isValidSoftwareAggregateStatus = (
|
|
s: string | undefined | null
|
|
): s is SoftwareAggregateStatus =>
|
|
!!s && SOFTWARE_AGGREGATE_STATUSES.includes(s as SoftwareAggregateStatus);
|
|
|
|
export const isSoftwareUninstallStatus = (
|
|
s: string | undefined | null
|
|
): s is SoftwareUninstallStatus =>
|
|
!!s && SOFTWARE_UNINSTALL_STATUSES.includes(s as SoftwareUninstallStatus);
|
|
|
|
// not a typeguard, as above 2 functions are
|
|
export const isPendingStatus = (s: string | undefined | null) =>
|
|
["pending_install", "pending_uninstall"].includes(s || "");
|
|
|
|
export const resolveUninstallStatus = (
|
|
activityStatus?: string
|
|
): SoftwareUninstallStatus => {
|
|
let resolvedStatus = activityStatus;
|
|
if (resolvedStatus === "pending") {
|
|
resolvedStatus = "pending_uninstall";
|
|
}
|
|
if (resolvedStatus === "failed") {
|
|
resolvedStatus = "failed_uninstall";
|
|
}
|
|
if (!isSoftwareUninstallStatus(resolvedStatus)) {
|
|
console.warn(
|
|
`Unexpected uninstall status "${activityStatus}" for activity. Defaulting to "pending_uninstall".`
|
|
);
|
|
resolvedStatus = "pending_uninstall";
|
|
}
|
|
return resolvedStatus as SoftwareUninstallStatus;
|
|
};
|
|
|
|
/**
|
|
* ISoftwareInstallResult is the shape of a software install result object
|
|
* returned by the Fleet API.
|
|
*/
|
|
export interface ISoftwareInstallResult {
|
|
host_display_name?: string;
|
|
install_uuid: string;
|
|
software_title: string;
|
|
software_title_id: number;
|
|
software_package: string;
|
|
host_id: number;
|
|
status: SoftwareInstallUninstallStatus;
|
|
detail: string;
|
|
output: string;
|
|
pre_install_query_output: string;
|
|
post_install_script_output: string;
|
|
created_at: string;
|
|
updated_at: string | null;
|
|
self_service: boolean;
|
|
}
|
|
|
|
// Script results are only install results, never uninstall
|
|
export type ISoftwareScriptResult = Omit<ISoftwareInstallResult, "status"> & {
|
|
status: SoftwareInstallStatus;
|
|
};
|
|
|
|
export interface ISoftwareInstallResults {
|
|
results: ISoftwareInstallResult;
|
|
}
|
|
|
|
/** For Software .ipa installs, we use the install results API to return MDM command results */
|
|
export interface ISoftwareIpaInstallResults {
|
|
results: ICommandResult;
|
|
}
|
|
|
|
// ISoftwareInstallerType defines the supported installer types for
|
|
// software uploaded by the IT admin.
|
|
export type ISoftwareInstallerType = "pkg" | "msi" | "deb" | "rpm" | "exe";
|
|
|
|
export interface ISoftwareLastInstall {
|
|
install_uuid: string;
|
|
installed_at: string;
|
|
}
|
|
|
|
export interface IAppLastInstall {
|
|
command_uuid: string;
|
|
installed_at: string;
|
|
}
|
|
|
|
interface SignatureInformation {
|
|
installed_path: string;
|
|
team_identifier: string;
|
|
hash_sha256: string | null;
|
|
}
|
|
export interface ISoftwareLastUninstall {
|
|
script_execution_id: string;
|
|
uninstalled_at: string;
|
|
}
|
|
|
|
export interface ISoftwareInstallVersion {
|
|
version: string;
|
|
bundle_identifier: string;
|
|
last_opened_at: string | null;
|
|
vulnerabilities: string[] | null;
|
|
installed_paths: string[];
|
|
signature_information?: SignatureInformation[];
|
|
}
|
|
|
|
export interface IHostSoftwarePackage {
|
|
name: string;
|
|
self_service: boolean;
|
|
icon_url: string | null;
|
|
version: string;
|
|
last_install: ISoftwareLastInstall | null;
|
|
last_uninstall: ISoftwareLastUninstall | null;
|
|
categories?: SoftwareCategory[] | null;
|
|
automatic_install_policies?: ISoftwareInstallPolicy[] | null;
|
|
platform?: Platform;
|
|
}
|
|
|
|
export interface IHostAppStoreApp {
|
|
app_store_id: string;
|
|
platform: Platform;
|
|
self_service: boolean;
|
|
icon_url: string;
|
|
version: string;
|
|
last_install: IAppLastInstall | null;
|
|
categories?: SoftwareCategory[] | null;
|
|
automatic_install_policies?: ISoftwareInstallPolicy[] | null;
|
|
}
|
|
|
|
export interface IHostSoftware {
|
|
id: number;
|
|
name: string; // e.g., "mock software.app"
|
|
/** Custom name set per team by admin */
|
|
display_name?: string; // e.g. "Mock Software"
|
|
icon_url: string | null;
|
|
software_package: IHostSoftwarePackage | null;
|
|
app_store_app: IHostAppStoreApp | null;
|
|
source: SoftwareSource;
|
|
extension_for?: SoftwareExtensionFor;
|
|
bundle_identifier?: string;
|
|
status: Exclude<SoftwareInstallUninstallStatus, "uninstalled"> | null;
|
|
installed_versions: ISoftwareInstallVersion[] | null;
|
|
}
|
|
|
|
/**
|
|
* Comprehensive list of possible UI software statuses for host > software > library/self-service.
|
|
*
|
|
* These are more detailed than the raw API `.status` and are determined by:
|
|
* - Whether the host is online or offline
|
|
* - If the fleet-installed version is newer than any in installed_versions
|
|
* - Special handling for tarballs (tgz_packages)
|
|
* - Cases where the software inventory has not yet updated to reflect a recent change
|
|
* (i.e., last_install date vs host software's updated_at date)
|
|
*/
|
|
// Error UI statuses
|
|
export const HOST_SOFTWARE_UI_ERROR_STATUSES = [
|
|
"failed_install", // Install attempt failed
|
|
"failed_install_update_available", // Install/update failed; newer installer version available
|
|
"failed_uninstall", // Uninstall attempt failed
|
|
"failed_uninstall_update_available", // Uninstall/update failed; newer installer version available
|
|
"failed_script", // Script package failed to run
|
|
] as const;
|
|
export type HostSoftwareUiErrorStatus = typeof HOST_SOFTWARE_UI_ERROR_STATUSES[number];
|
|
export const isSoftwareErrorStatus = (
|
|
status: IHostSoftwareUiStatus
|
|
): status is HostSoftwareUiErrorStatus =>
|
|
HOST_SOFTWARE_UI_ERROR_STATUSES.includes(status as HostSoftwareUiErrorStatus);
|
|
|
|
// Pending UI statuses for OFFLINE hosts
|
|
export const HOST_SOFTWARE_UI_PENDING_STATUSES = [
|
|
"pending_install", // Install scheduled (no newer installer version)
|
|
"pending_uninstall", // Uninstall scheduled
|
|
"pending_update", // Update scheduled (no newer installer version)
|
|
"pending_script", // Fleet-initiated script run scheduled
|
|
] as const;
|
|
export type HostSoftwareUiPendingStatus = typeof HOST_SOFTWARE_UI_PENDING_STATUSES[number];
|
|
export const isSoftwarePendingStatus = (
|
|
status: IHostSoftwareUiStatus
|
|
): status is HostSoftwareUiPendingStatus =>
|
|
HOST_SOFTWARE_UI_PENDING_STATUSES.includes(
|
|
status as HostSoftwareUiPendingStatus
|
|
);
|
|
|
|
// In-progress UI statuses for ONLINE hosts
|
|
export const HOST_SOFTWARE_UI_IN_PROGRESS_STATUSES = [
|
|
"installing", // Fleet-initiated install in progress
|
|
"updating", // Update (install) in progress with newer fleet installer
|
|
"uninstalling", // Fleet-initiated uninstall in progress
|
|
"running_script", // Fleet-initiated script run in progress
|
|
] as const;
|
|
export type HostSoftwareUiInProgressStatus = typeof HOST_SOFTWARE_UI_IN_PROGRESS_STATUSES[number];
|
|
export const isSoftwareInProgressStatus = (
|
|
status: IHostSoftwareUiStatus
|
|
): status is HostSoftwareUiInProgressStatus =>
|
|
HOST_SOFTWARE_UI_IN_PROGRESS_STATUSES.includes(
|
|
status as HostSoftwareUiInProgressStatus
|
|
);
|
|
|
|
// Success/steady-state UI statuses
|
|
export const HOST_SOFTWARE_UI_SUCCESS_STATUSES = [
|
|
"installed", // Present in inventory; no newer fleet installer version (tarballs: successful install only)
|
|
"uninstalled", // Not present in inventory (tarballs: successful uninstall or never installed)
|
|
"recently_updated", // Update applied (installer newer than inventory), but inventory not yet refreshed
|
|
"recently_installed", // Install applied (installer NOT newer than inventory), but inventory not yet refreshed
|
|
"recently_uninstalled", // Uninstall applied, but inventory not yet refreshed
|
|
"ran_script", // Script package ran successfully
|
|
"never_ran_script", // Script package never ran before
|
|
] as const;
|
|
export type HostSoftwareUiSuccessStatus = typeof HOST_SOFTWARE_UI_SUCCESS_STATUSES[number];
|
|
export const isSoftwareSuccessStatus = (
|
|
status: IHostSoftwareUiStatus
|
|
): status is HostSoftwareUiSuccessStatus =>
|
|
HOST_SOFTWARE_UI_SUCCESS_STATUSES.includes(
|
|
status as HostSoftwareUiSuccessStatus
|
|
);
|
|
|
|
// Update-available UI status
|
|
export const HOST_SOFTWARE_UI_UPDATE_AVAILABLE_STATUSES = [
|
|
"update_available", // In inventory, but newer fleet installer version is available
|
|
] as const;
|
|
export type HostSoftwareUiUpdateAvailableStatus = typeof HOST_SOFTWARE_UI_UPDATE_AVAILABLE_STATUSES[number];
|
|
export const isSoftwareUpdateAvailableStatus = (
|
|
status: IHostSoftwareUiStatus
|
|
): status is HostSoftwareUiUpdateAvailableStatus =>
|
|
HOST_SOFTWARE_UI_UPDATE_AVAILABLE_STATUSES.includes(
|
|
status as HostSoftwareUiUpdateAvailableStatus
|
|
);
|
|
|
|
// Master UI status type, combining all:
|
|
export type IHostSoftwareUiStatus =
|
|
| HostSoftwareUiErrorStatus
|
|
| HostSoftwareUiPendingStatus
|
|
| HostSoftwareUiSuccessStatus
|
|
| HostSoftwareUiInProgressStatus
|
|
| HostSoftwareUiUpdateAvailableStatus;
|
|
|
|
/**
|
|
* Extends IHostSoftware with a computed `ui_status` field.
|
|
*
|
|
* The `ui_status` categorizes software installation state for the UI by
|
|
* combining the `status`, `installed_versions` info, and other factors
|
|
* like host online state (via getUiStatus helper function), enabling
|
|
* more detailed and status labels needed for the status and actions columns.
|
|
*/
|
|
export interface IHostSoftwareWithUiStatus extends IHostSoftware {
|
|
ui_status: IHostSoftwareUiStatus;
|
|
}
|
|
|
|
/**
|
|
* Allows unified data model for rendering of host VPP software installs and uninstalls
|
|
* Optional as pending may not have a commandUuid
|
|
*/
|
|
export type IVPPHostSoftware = IHostSoftware & {
|
|
commandUuid?: string;
|
|
};
|
|
|
|
export type IHostSoftwareUninstall = IHostSoftwareWithUiStatus & {
|
|
scriptExecutionId: string;
|
|
};
|
|
|
|
export type IDeviceSoftware = IHostSoftware;
|
|
export type IDeviceSoftwareWithUiStatus = IHostSoftwareWithUiStatus;
|
|
|
|
const INSTALL_STATUS_PREDICATES: Record<
|
|
EnhancedSoftwareInstallUninstallStatus | "pending",
|
|
string
|
|
> = {
|
|
pending: "pending",
|
|
installed: "installed",
|
|
uninstalled: "uninstalled",
|
|
pending_install: "told Fleet to install",
|
|
failed_install: "failed to install",
|
|
pending_uninstall: "told Fleet to uninstall",
|
|
failed_uninstall: "failed to uninstall",
|
|
ran_script: "ran", // Payload-free (script) software
|
|
failed_script: "failed to run", // Payload-free (script) software
|
|
pending_script: "told Fleet to run", // Payload-free (script) software
|
|
} as const;
|
|
|
|
export const getInstallUninstallStatusPredicate = (
|
|
status: string | undefined,
|
|
isScriptPackage = false
|
|
) => {
|
|
if (!status) {
|
|
return INSTALL_STATUS_PREDICATES.pending;
|
|
}
|
|
|
|
// If it is a script package, map install statuses to script-specific predicates
|
|
if (isScriptPackage) {
|
|
switch (status.toLowerCase()) {
|
|
case "installed":
|
|
return INSTALL_STATUS_PREDICATES.ran_script;
|
|
case "pending_install":
|
|
return INSTALL_STATUS_PREDICATES.pending_script;
|
|
case "failed_install":
|
|
return INSTALL_STATUS_PREDICATES.failed_script;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// For all other cases, return the matching predicate or default to pending
|
|
return (
|
|
INSTALL_STATUS_PREDICATES[
|
|
status.toLowerCase() as keyof typeof INSTALL_STATUS_PREDICATES
|
|
] || INSTALL_STATUS_PREDICATES.pending
|
|
);
|
|
};
|
|
|
|
export const aggregateInstallStatusCounts = (
|
|
packageStatuses: ISoftwarePackage["status"]
|
|
) => ({
|
|
installed: packageStatuses.installed,
|
|
pending: packageStatuses.pending_install + packageStatuses.pending_uninstall,
|
|
failed: packageStatuses.failed_install + packageStatuses.failed_uninstall,
|
|
});
|
|
|
|
export const INSTALL_STATUS_ICONS: Record<
|
|
EnhancedSoftwareInstallUninstallStatus | "pending" | "failed",
|
|
IconNames
|
|
> = {
|
|
pending: "pending-outline",
|
|
pending_install: "pending-outline",
|
|
installed: "success-outline",
|
|
uninstalled: "success-outline",
|
|
failed: "error-outline",
|
|
failed_install: "error-outline",
|
|
pending_uninstall: "pending-outline",
|
|
failed_uninstall: "error-outline",
|
|
ran_script: "success-outline", // Payload-free (script) software
|
|
failed_script: "error-outline", // Payload-free (script) software
|
|
pending_script: "pending-outline", // Payload-free (script) software
|
|
} as const;
|
|
|
|
type IHostSoftwarePackageWithLastInstall = IHostSoftwarePackage & {
|
|
last_install: ISoftwareLastInstall;
|
|
};
|
|
|
|
export const hasHostSoftwarePackageLastInstall = (
|
|
software: IHostSoftware
|
|
): software is IHostSoftware & {
|
|
software_package: IHostSoftwarePackageWithLastInstall;
|
|
} => {
|
|
return !!software.software_package?.last_install;
|
|
};
|
|
|
|
type IHostAppWithLastInstall = IHostAppStoreApp & {
|
|
last_install: IAppLastInstall;
|
|
};
|
|
|
|
export const hasHostSoftwareAppLastInstall = (
|
|
software: IHostSoftware
|
|
): software is IHostSoftware & {
|
|
app_store_app: IHostAppWithLastInstall;
|
|
} => {
|
|
return !!software.app_store_app?.last_install;
|
|
};
|
|
|
|
export const isIpadOrIphoneSoftwareSource = (source: string) =>
|
|
["ios_apps", "ipados_apps"].includes(source);
|
|
|
|
export const isAndroidSoftwareSource = (source: string) =>
|
|
source === "android_apps";
|
|
|
|
export interface IFleetMaintainedApp {
|
|
id: number;
|
|
name: string;
|
|
version: string;
|
|
platform: FleetMaintainedAppPlatform;
|
|
software_title_id?: number; // null unless the team already has the software added (as a Fleet-maintained app, App Store (app), or custom package)
|
|
}
|
|
|
|
export type FleetMaintainedAppPlatform = Extract<
|
|
Platform,
|
|
"darwin" | "windows"
|
|
>;
|
|
|
|
export interface ICombinedFMA {
|
|
name: string;
|
|
macos: Omit<IFleetMaintainedApp, "name"> | null;
|
|
windows: Omit<IFleetMaintainedApp, "name"> | null;
|
|
}
|
|
export interface IFleetMaintainedAppDetails {
|
|
id: number;
|
|
name: string;
|
|
version: string;
|
|
platform: FleetMaintainedAppPlatform;
|
|
pre_install_script: string;
|
|
install_script: string;
|
|
post_install_script: string;
|
|
uninstall_script: string;
|
|
url: string;
|
|
slug: string;
|
|
software_title_id?: number; // null unless the team already has the software added (as a Fleet-maintained app, App Store (app), or custom package)
|
|
categories: SoftwareCategory[] | null;
|
|
}
|
|
|
|
export const ROLLING_ARCH_LINUX_NAMES = [
|
|
"Arch Linux",
|
|
"Arch Linux ARM",
|
|
"Manjaro Linux",
|
|
"Manjaro Linux ARM",
|
|
"Manjaro ARM Linux",
|
|
];
|
|
|
|
export const ROLLING_ARCH_LINUX_VERSIONS = ROLLING_ARCH_LINUX_NAMES.map(
|
|
(name) => `${name} rolling`
|
|
);
|