diff --git a/.github/scripts/update_osquery_versions.py b/.github/scripts/update_osquery_versions.py
index c65deceb2ee4..28a932e6acb1 100755
--- a/.github/scripts/update_osquery_versions.py
+++ b/.github/scripts/update_osquery_versions.py
@@ -14,9 +14,8 @@ def fetch_osquery_versions():
resp = conn.getresponse()
content = resp.read()
conn.close()
- releases = json.loads(content.decode('utf-8'))
- return [release['tag_name'] for release in releases if not release['prerelease']]
+ return [release['tag_name'] for release in json.loads(content.decode('utf-8'))]
def update_min_osquery_version_options(new_versions):
with open(FILE_PATH, 'r') as file:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0fc2d7d3f10b..bc0999a01ebe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,84 @@
+## Fleet 4.62.1 (Jan 13, 2025)
+
+### Bug fixes
+
+* Fixed issue when identical MDM commands were sent twice to the same device when replica DB was being used.
+
+## Fleet 4.62.0 (Jan 09, 2025)
+
+## Endpoint operations
+- Updated macos 13, 14 per latest CIS documents. Added macos 15 support.
+- Updated queries API to support above targeted platform filtering.
+- Updated UI queries page to filter, sort, paginate, etc. via query params in call to server.
+- Added searchable query targets and cleaner UI for uses with many teams or labels.
+
+## Device management (MDM)
+- Added ability to use secrets (`$FLEET_SECRET_YOURNAME`) in scripts and profiles.
+- Added ability to scope Fleet-maintained apps and custom packages via labels in UI, API, and CLI.
+- Added capability to automatically generate "trigger policies" for custom software packages.
+- Added UI for scoping software via labels.
+- Added validation to prevent label deletion if it is used to scope the hosts targeted by a software installer.
+- Added ability to filter host software based on label scoping.
+- Added support for Fleet secret validation in software installer scripts.
+- Updated `fleetctl gitops` to support scope software installers by labels, with the `labels_include_any` or `labels_exclude_any` conditions.
+- Updated `fleetctl gitops` to identify secrets in scripts and profiles and saves them on the Fleet server.
+- Updated `fleetctl gitops` so that when it updates profiles, if the secret value has changed, the profile is updated on the host.
+- Added `/fleet/spec/secret_variables` API endpoint.
+- Added functionality for skipping automatic installs if the software is not scoped to the host via labels.
+- Added the ability to click a software row on the my device page and see the details of that software's installation on the host.
+- Allowed software uninstalls and script-based host lock/unlock/wipe to run while global scripts are disabled.
+
+## Vulnerability management
+- Added missing vulncheck data from NVD feeds.
+- Fixed MSI parsing for packages including long interned strings (e.g. licenses for the OpenVPN Connect installer).
+- Fixed a panic (and resulting failure to load CVE details) on new installs when OS versions have not been populated yet.
+- Fixed CVE-2024-10004 false positive on Fleet-supported platforms (vuln is iOS-only and iOS vuln checking is not supported).
+
+## Bug fixes and improvements
+- Added license key validation on `fleetctl preview` if a license key is provided; fixes cases where an invalid license key would cause `fleetctl preview` to hang.
+- Increased maximum length for installer URLs specified in GitOps to 4000 characters.
+- Stopped older scheduled queries from filling logs with errors.
+- Changed script upload endpoint (`POST /api/v1/fleet/scripts`) to automatically switch CRLF line endings to LF.
+- Fleshed out server response from `queries` endpoint to include `count` and `meta` pagination information.
+- Updated platform filtering on queries page to refer to targeted platforms instead of compatible platforms.
+- Included osquery pre-releases in daily UI constant update GitHub Actions job.
+- Updated to send alert via SNS when a scheduled "cron" job returns errors.
+- SNS topic for job error alerts can be configured separately from the existing monitor alert by adding "cron_job_failure_monitoring" to sns_topic_arns_map, otherwise defaults to the using the same topic.
+- Improved validation workflow on SMTP settings page.
+- Allowed team policy endpoint (`PATCH /api/latest/fleet/teams/{team_id}/policies/{policy_id}`) to receive explicit `null` as a value for `script_id` or `software_title_id` to unset a script or software installer respectively.
+- Aliased EAP versions of JetBrains IDEs to "last release version plus all fixes" (e.g. 2024.3 EAP -> 2024.2.99) to avoid vulnerability false positives.
+- Removed server error if no private IP was found by detail_query_network_interface.
+- Updated `fleetctl` dependencies that cause warnings.
+- Added service annotation field to Helm Chart.
+- Updated so that on policy deletion any associated pending software installer or scripts are deleted.
+- Added fallback to FileVersion on EXE installers when FileVersion is set but ProductVersion isn't to allow more custom packages to be uploaded.
+- Added Mastodon icon and URL to server email templates.
+- Improved table text wrapper in UI.
+- Added helpful tooltip for the install software setup experience page.
+- Added offset to the tooltips on hover of the profile aggregate status indicators.
+- Added the `software_title_id` field to the `added_software` activity details.
+- Allow maintainers to manage install software or run scripts on policy automations.
+- Removed duplicate software records from homebrew casks already reported in the osquery `apps` table to address false positive vulnerabilities due to lack of bundle_identifier.
+- Added the `labels_include_any` and `labels_exclude_any` fields to the software installer activities.
+- Updated the get host endpoint to include disk encryption stats for a linux host only if the setting is enabled.
+- Updated Helm chart to support customization options such as the Google cloud_sql_proxy in the fleet-migration job.
+- Updated example windows policies.
+- Added a descriptive error when a GitOps file contains script references that are missing paths.
+- Removed `invalid UUID` log message when validating Apple MDM UDID.
+- Added validation Fleet secrets embedded into scripts and profiles on ingestion.
+- Display the correct percentage of hosts online when there are no hosts online.
+- Fixed bug when creating a label to preserve the selected team.
+- Fixed export to CSV trimming leading zeros by treating those values as strings.
+- Fixed reporting of software uninstall results after a host has been locked/unlocked.
+- Fixed issue where minio software was not scanned for vulnerabilities correctly because of unexpected trailing characters in the version string.
+- Fixed bug on the "Controls" page where incorrect timestamp information was displayed while the "Current versions" table was loading.
+- Fixed policy truncation UI bug.
+- Fixed cases where showing results of an inherited query viewed inside a team would include results from hosts not on thta team by adding an optional team_id parameter to queris report endpoint (`GET /api/latest/fleet/queries/{query_id}/report`).
+- Fixed issue where deleted Apple config profiles were installing on devices because devices were offline when the profile was added.
+- Fixed UI bug involving pagination of subsections within the "Controls" page.
+- Fixed "Verifying" disk encryption status count and filter for macOS hosts to not include hosts where end-user action is required.
+- Fixed a bug in determining sort type of query result columns by deducing that type from the data present in those columns.
+
## Fleet 4.61.0 (Dec 17, 2024)
## Endpoint operations
diff --git a/changes/19930-alert-on-cron-errors b/changes/19930-alert-on-cron-errors
deleted file mode 100644
index 0374bf8b0530..000000000000
--- a/changes/19930-alert-on-cron-errors
+++ /dev/null
@@ -1,2 +0,0 @@
-- Send alert via SNS when a scheduled "cron" job returns errors
-- SNS topic for job error alerts can be configured separately from the existing monitor alert by adding "cron_job_failure_monitoring" to sns_topic_arns_map, otherwise defaults to the using the same topic
diff --git a/changes/21855-paginate-queries b/changes/21855-paginate-queries
deleted file mode 100644
index a54dfb43c8c7..000000000000
--- a/changes/21855-paginate-queries
+++ /dev/null
@@ -1,5 +0,0 @@
-- Fleshed out server response from `queries` endpoint to include `count` and `meta` pagination information.
-- Updated UI queries page to filter, sort, paginate, etc. via query params in call to server.
-- Updated platform filtering on queries page to refer to targeted platforms instead of compatible
- platforms
-- Updated queries API to support above targeted platform filtering
diff --git a/changes/22444-gitops-script-missing-path b/changes/22444-gitops-script-missing-path
deleted file mode 100644
index e9a5bb299731..000000000000
--- a/changes/22444-gitops-script-missing-path
+++ /dev/null
@@ -1 +0,0 @@
-* Added a descriptive error when a GitOps file contains script references that are missing paths
diff --git a/changes/22448-searchable-query-targets b/changes/22448-searchable-query-targets
deleted file mode 100644
index 5cbb33f42d34..000000000000
--- a/changes/22448-searchable-query-targets
+++ /dev/null
@@ -1 +0,0 @@
-- Fleet UI: Add searchable query targets and cleaner UI I for uses with many teams or labels
diff --git a/changes/22523-cve-500 b/changes/22523-cve-500
deleted file mode 100644
index d2170348a354..000000000000
--- a/changes/22523-cve-500
+++ /dev/null
@@ -1 +0,0 @@
-* Fixed a panic (and resulting failure to load CVE details) on new installs when OS versions have not been populated yet.
diff --git a/changes/22723-jetbrains-eap-versions b/changes/22723-jetbrains-eap-versions
deleted file mode 100644
index 6efe38c67461..000000000000
--- a/changes/22723-jetbrains-eap-versions
+++ /dev/null
@@ -1 +0,0 @@
-* Aliased EAP versions of JetBrains IDEs to "last release version plus all fixes" (e.g. 2024.3 EAP -> 2024.2.99) to avoid vulnerability false positives
\ No newline at end of file
diff --git a/changes/22813-software-scope-labels b/changes/22813-software-scope-labels
deleted file mode 100644
index 4a4681a2c618..000000000000
--- a/changes/22813-software-scope-labels
+++ /dev/null
@@ -1 +0,0 @@
-- Added features to scope Fleet-maintained apps and custom packages via labels in UI, API, and CLI.
diff --git a/changes/22875-uninstall-with-scripts-disabled b/changes/22875-uninstall-with-scripts-disabled
deleted file mode 100644
index 5f399869b5e5..000000000000
--- a/changes/22875-uninstall-with-scripts-disabled
+++ /dev/null
@@ -1 +0,0 @@
-* Allowed software uninstalls and script-based host lock/unlock/wipe to run while global scripts are disabled.
diff --git a/changes/22884-license-error-fleetctl b/changes/22884-license-error-fleetctl
deleted file mode 100644
index f3bb25527ebe..000000000000
--- a/changes/22884-license-error-fleetctl
+++ /dev/null
@@ -1 +0,0 @@
-* Added license key validation on `fleetctl preview` if a license key is provided; fixes cases where an invalid license key would cause `fleetctl preview` to hang.
diff --git a/changes/22944-homebrew-casks b/changes/22944-homebrew-casks
deleted file mode 100644
index abaf22ffa250..000000000000
--- a/changes/22944-homebrew-casks
+++ /dev/null
@@ -1 +0,0 @@
-* removed duplicate software records from homebrew casks already reported in the osquery `apps` table to address false positive vulnerabilities due to lack of bundle_identifier
\ No newline at end of file
diff --git a/changes/23011-dynamic-column-sort-type b/changes/23011-dynamic-column-sort-type
deleted file mode 100644
index 9ac874713f1d..000000000000
--- a/changes/23011-dynamic-column-sort-type
+++ /dev/null
@@ -1,2 +0,0 @@
-- Fixed a bug in determining sort type of query result columns by deducing that type from the data
- present in those columns.
diff --git a/changes/23238-use-secrets-in-scripts-profiles b/changes/23238-use-secrets-in-scripts-profiles
deleted file mode 100644
index 4df69e0d13cc..000000000000
--- a/changes/23238-use-secrets-in-scripts-profiles
+++ /dev/null
@@ -1,5 +0,0 @@
-Added ability to use secrets ($FLEET_SECRET_YOURNAME) in scripts and profiles.
-- Added `/fleet/spec/secret_variables` API endpoint.
-- fleetctl gitops identifies secrets in scripts and profiles and saves them on the Fleet server.
-- secret values are populated when scripts and profiles are sent to devices.
-- When fleetctl gitops updates profiles, if the secret value has changed, the profile is updated on the host.
diff --git a/changes/23309-mastodon-in-email-templates b/changes/23309-mastodon-in-email-templates
deleted file mode 100644
index 6eac06da3e60..000000000000
--- a/changes/23309-mastodon-in-email-templates
+++ /dev/null
@@ -1 +0,0 @@
-* Added Mastodon icon and URL to server email templates.
diff --git a/changes/23315-show-sw-install-details-on-my-device b/changes/23315-show-sw-install-details-on-my-device
deleted file mode 100644
index fc5c476c8c9c..000000000000
--- a/changes/23315-show-sw-install-details-on-my-device
+++ /dev/null
@@ -1,2 +0,0 @@
-* Add the ability to click a software row on the my device page and see the details of that
-software's installation on the host.
diff --git a/changes/23448-maintainer-policy-automations b/changes/23448-maintainer-policy-automations
deleted file mode 100644
index a5a0f38532c9..000000000000
--- a/changes/23448-maintainer-policy-automations
+++ /dev/null
@@ -1 +0,0 @@
-- Fleet UI bug fix: Allow maintainers to manage install software or run scripts on policy automations
diff --git a/changes/23490-null-script-software-on-policies b/changes/23490-null-script-software-on-policies
deleted file mode 100644
index cf2db860dce3..000000000000
--- a/changes/23490-null-script-software-on-policies
+++ /dev/null
@@ -1 +0,0 @@
-* Allowed team policy endpoint (`PATCH /api/latest/fleet/teams/{team_id}/policies/{policy_id}`) to receive explicit `null` as a value for `script_id` or `software_title_id` to unset a script or software installer respectively.
\ No newline at end of file
diff --git a/changes/23541-fileversion-fallback b/changes/23541-fileversion-fallback
deleted file mode 100644
index b9ec9f22adbe..000000000000
--- a/changes/23541-fileversion-fallback
+++ /dev/null
@@ -1 +0,0 @@
-* Added fallback to FileVersion on EXE installers when FileVersion is set but ProductVersion isn't to allow more custom packages to be uploaded
diff --git a/changes/23557-minio b/changes/23557-minio
deleted file mode 100644
index 2fa00913f424..000000000000
--- a/changes/23557-minio
+++ /dev/null
@@ -1 +0,0 @@
-* fixed issue where minio software was not scanned for vulnerabilities correctly because of unexpected trailing characters in the version string
\ No newline at end of file
diff --git a/changes/23579-cve-2024-10004-false-positive b/changes/23579-cve-2024-10004-false-positive
deleted file mode 100644
index de3ed40f7b56..000000000000
--- a/changes/23579-cve-2024-10004-false-positive
+++ /dev/null
@@ -1 +0,0 @@
-* Fixed CVE-2024-10004 false positive on Fleet-supported platforms (vuln is iOS-only and iOS vuln checking is not supported)
diff --git a/changes/23611-Update-CIS b/changes/23611-Update-CIS
deleted file mode 100644
index 8b72853be652..000000000000
--- a/changes/23611-Update-CIS
+++ /dev/null
@@ -1 +0,0 @@
- * Updated macos 13, 14 per latest CIS documents. Added macos 15 support.
diff --git a/changes/23783-controls-subnav-pagination b/changes/23783-controls-subnav-pagination
deleted file mode 100644
index 46094c2e5f89..000000000000
--- a/changes/23783-controls-subnav-pagination
+++ /dev/null
@@ -1 +0,0 @@
-- Fixed UI bug involving pagination of subsections within the "Controls" page.
diff --git a/changes/23800-host-online-pctage b/changes/23800-host-online-pctage
deleted file mode 100644
index 4e6d92d75e68..000000000000
--- a/changes/23800-host-online-pctage
+++ /dev/null
@@ -1 +0,0 @@
-* Display the correct percentage of hosts online, 0, when there are no hosts online.
diff --git a/changes/23803-leading-zeros-bug b/changes/23803-leading-zeros-bug
deleted file mode 100644
index a3ea4104bfad..000000000000
--- a/changes/23803-leading-zeros-bug
+++ /dev/null
@@ -1 +0,0 @@
-- Fleet UI: Fix export to CSV from trimming leading zeros by treating those values as strings
diff --git a/changes/23886-remove-associations-on-policy-delete b/changes/23886-remove-associations-on-policy-delete
deleted file mode 100644
index 318ca34bec79..000000000000
--- a/changes/23886-remove-associations-on-policy-delete
+++ /dev/null
@@ -1 +0,0 @@
-* On policy deletion any associated pending software installer or scripts are deleted.
\ No newline at end of file
diff --git a/changes/24006-host-query-report-team-id b/changes/24006-host-query-report-team-id
deleted file mode 100644
index 588ab1eac91e..000000000000
--- a/changes/24006-host-query-report-team-id
+++ /dev/null
@@ -1 +0,0 @@
-* Fixed cases where showing results of an inherited query viewed inside a team would include results from hosts not on thta team by adding an optional team_id parameter to queries report endpoint (`GET /api/latest/fleet/queries/{query_id}/report`)
\ No newline at end of file
diff --git a/changes/24025-add-label-team-bug b/changes/24025-add-label-team-bug
deleted file mode 100644
index cb4396199129..000000000000
--- a/changes/24025-add-label-team-bug
+++ /dev/null
@@ -1 +0,0 @@
-- Fix bug when creating a label to preserve the selected team
diff --git a/changes/24033-helm-customization-options b/changes/24033-helm-customization-options
deleted file mode 100644
index 1aafbedcde5f..000000000000
--- a/changes/24033-helm-customization-options
+++ /dev/null
@@ -1 +0,0 @@
-Helm chart: Supported customization options such as the Google cloud_sql_proxy in the fleet-migration job.
diff --git a/changes/24120-sw-title-id b/changes/24120-sw-title-id
deleted file mode 100644
index 5a1b8ebe72e3..000000000000
--- a/changes/24120-sw-title-id
+++ /dev/null
@@ -1 +0,0 @@
-- Adds the `software_title_id` field to the `added_software` activity details.
\ No newline at end of file
diff --git a/changes/24166-script-line-endings b/changes/24166-script-line-endings
deleted file mode 100644
index 3beeb8d9dd4a..000000000000
--- a/changes/24166-script-line-endings
+++ /dev/null
@@ -1 +0,0 @@
-* Changed script upload endpoint (`POST /api/v1/fleet/scripts`) to automatically switch CRLF line endings to LF
diff --git a/changes/24244-macos-encryption-verifying-query b/changes/24244-macos-encryption-verifying-query
deleted file mode 100644
index f88dbd6e88d9..000000000000
--- a/changes/24244-macos-encryption-verifying-query
+++ /dev/null
@@ -1 +0,0 @@
-* Fixed "Verifying" disk encryption status count and filter for macOS hosts to not include hosts where end-user action is required
diff --git a/changes/24268-update-fleetctl-deps b/changes/24268-update-fleetctl-deps
deleted file mode 100644
index 0b22a53d6585..000000000000
--- a/changes/24268-update-fleetctl-deps
+++ /dev/null
@@ -1 +0,0 @@
-* Update fleetctl dependencies that cause warnings
\ No newline at end of file
diff --git a/changes/24286-vulncheck b/changes/24286-vulncheck
deleted file mode 100644
index 2104095dd5b5..000000000000
--- a/changes/24286-vulncheck
+++ /dev/null
@@ -1 +0,0 @@
-* added missing vulncheck data from nvd feeds
\ No newline at end of file
diff --git a/changes/24315-update-windows-policy-constants b/changes/24315-update-windows-policy-constants
deleted file mode 100644
index 36032790089d..000000000000
--- a/changes/24315-update-windows-policy-constants
+++ /dev/null
@@ -1 +0,0 @@
-- Update example windows policies
diff --git a/changes/24334-policy-truncation b/changes/24334-policy-truncation
deleted file mode 100644
index a8137dcb3d05..000000000000
--- a/changes/24334-policy-truncation
+++ /dev/null
@@ -1 +0,0 @@
-- Fix policy truncation UI bug
diff --git a/changes/24385-automatic-install-custom-packages b/changes/24385-automatic-install-custom-packages
deleted file mode 100644
index b36526d1687f..000000000000
--- a/changes/24385-automatic-install-custom-packages
+++ /dev/null
@@ -1 +0,0 @@
-* Added capability to automatically generate "trigger policies" for custom software packages.
diff --git a/changes/24386-fleet-legacy-query-pack b/changes/24386-fleet-legacy-query-pack
deleted file mode 100644
index 66878d24a716..000000000000
--- a/changes/24386-fleet-legacy-query-pack
+++ /dev/null
@@ -1 +0,0 @@
-- Stop older scheduled queries from filling logs with errors
diff --git a/changes/24456-include-linux-encryption-data-only-when-enabled b/changes/24456-include-linux-encryption-data-only-when-enabled
deleted file mode 100644
index cefe63053829..000000000000
--- a/changes/24456-include-linux-encryption-data-only-when-enabled
+++ /dev/null
@@ -1 +0,0 @@
-- Updated the get host endpoint to include disk encryption stats for a linux host only if the setting is enabled
diff --git a/changes/24459-chart-serrvice-annotation b/changes/24459-chart-serrvice-annotation
deleted file mode 100644
index 62a199ec582d..000000000000
--- a/changes/24459-chart-serrvice-annotation
+++ /dev/null
@@ -1 +0,0 @@
-* Added service annotation field to Helm Chart
diff --git a/changes/24533-skip-policy b/changes/24533-skip-policy
deleted file mode 100644
index 4de7d634a3b3..000000000000
--- a/changes/24533-skip-policy
+++ /dev/null
@@ -1 +0,0 @@
-- Adds functionality for skipping automatic installs if the software is not scoped to the host via labels.
\ No newline at end of file
diff --git a/changes/24534-hide-software-2 b/changes/24534-hide-software-2
deleted file mode 100644
index 9b73513ddd20..000000000000
--- a/changes/24534-hide-software-2
+++ /dev/null
@@ -1 +0,0 @@
-- Add functionality to filter host software based on label scoping.
\ No newline at end of file
diff --git a/changes/24536-prevent-label-deletion-if-referenced-by-software b/changes/24536-prevent-label-deletion-if-referenced-by-software
deleted file mode 100644
index ef3e4753f351..000000000000
--- a/changes/24536-prevent-label-deletion-if-referenced-by-software
+++ /dev/null
@@ -1 +0,0 @@
-* Added a validation to prevent label deletion if it is used to scope the hosts targeted by a software installer.
diff --git a/changes/24538-24542-UI-for-scope-software-via-labels b/changes/24538-24542-UI-for-scope-software-via-labels
deleted file mode 100644
index d8d65558df8e..000000000000
--- a/changes/24538-24542-UI-for-scope-software-via-labels
+++ /dev/null
@@ -1 +0,0 @@
-- add UI for scoping software via labels
diff --git a/changes/24549-validate-script-profle-secrets b/changes/24549-validate-script-profle-secrets
deleted file mode 100644
index fdf7ea4a416e..000000000000
--- a/changes/24549-validate-script-profle-secrets
+++ /dev/null
@@ -1 +0,0 @@
-- Validate fleet secrets embedded into scripts and profiles on ingestion
diff --git a/changes/24663-software-scoped-via-labels-gitops b/changes/24663-software-scoped-via-labels-gitops
deleted file mode 100644
index 4bb1c15cf230..000000000000
--- a/changes/24663-software-scoped-via-labels-gitops
+++ /dev/null
@@ -1 +0,0 @@
-* Added `fleetctl gitops` support to scope software installers by labels, with the `labels_include_any` or `labels_exclude_any` conditions.
diff --git a/changes/24725-no-private-ip-found b/changes/24725-no-private-ip-found
deleted file mode 100644
index 83b72288a521..000000000000
--- a/changes/24725-no-private-ip-found
+++ /dev/null
@@ -1 +0,0 @@
-Removed server error if no private IP was found by detail_query_network_interface.
diff --git a/changes/24792-update-software-installer-activities b/changes/24792-update-software-installer-activities
deleted file mode 100644
index 206fa80dac8b..000000000000
--- a/changes/24792-update-software-installer-activities
+++ /dev/null
@@ -1 +0,0 @@
-* Added the `labels_include_any` and `labels_exclude_any` fields to the software installer activities.
diff --git a/changes/24795-add-helpful-tooltip-setup-experience b/changes/24795-add-helpful-tooltip-setup-experience
deleted file mode 100644
index 4c108c223152..000000000000
--- a/changes/24795-add-helpful-tooltip-setup-experience
+++ /dev/null
@@ -1 +0,0 @@
-- add helpful tooltip for the install software setup experience page
diff --git a/changes/24899-software-installer-scripts-secrets b/changes/24899-software-installer-scripts-secrets
deleted file mode 100644
index f5f11a77c74c..000000000000
--- a/changes/24899-software-installer-scripts-secrets
+++ /dev/null
@@ -1 +0,0 @@
-- Add support for fleet secret validation in software installer scripts
diff --git a/changes/24917-installer-url-length b/changes/24917-installer-url-length
deleted file mode 100644
index af3287c8076f..000000000000
--- a/changes/24917-installer-url-length
+++ /dev/null
@@ -1 +0,0 @@
-* Increased maximum length for installer URLs specified in GitOps to 4000 characters
diff --git a/changes/24961-invalid-uuid b/changes/24961-invalid-uuid
deleted file mode 100644
index fae9c81341ee..000000000000
--- a/changes/24961-invalid-uuid
+++ /dev/null
@@ -1 +0,0 @@
-Removed `invalid UUID` log message when validating Apple MDM UDID.
diff --git a/changes/25038-fix-profile-status-aggregate-tooltips-spacing b/changes/25038-fix-profile-status-aggregate-tooltips-spacing
deleted file mode 100644
index 79504e60eca0..000000000000
--- a/changes/25038-fix-profile-status-aggregate-tooltips-spacing
+++ /dev/null
@@ -1 +0,0 @@
-* Add offset to the tooltips on hover of the profile aggregate status indicators.
diff --git a/changes/8903c-table-text-wrapping b/changes/8903c-table-text-wrapping
deleted file mode 100644
index 08ce77f6b0dd..000000000000
--- a/changes/8903c-table-text-wrapping
+++ /dev/null
@@ -1 +0,0 @@
-- Fleet UI: Clean up some table text wrapping
diff --git a/charts/fleet/Chart.yaml b/charts/fleet/Chart.yaml
index b258d1c234fb..fb788c8bedbf 100644
--- a/charts/fleet/Chart.yaml
+++ b/charts/fleet/Chart.yaml
@@ -4,11 +4,11 @@ name: fleet
keywords:
- fleet
- osquery
-version: v6.3.0
+version: v6.3.1
home: https://github.com/fleetdm/fleet
sources:
- https://github.com/fleetdm/fleet.git
-appVersion: v4.61.0
+appVersion: v4.62.1
dependencies:
- name: mysql
condition: mysql.enabled
diff --git a/charts/fleet/values.yaml b/charts/fleet/values.yaml
index 4d7aa0885577..0228b3346ce5 100644
--- a/charts/fleet/values.yaml
+++ b/charts/fleet/values.yaml
@@ -3,7 +3,7 @@
hostName: fleet.localhost
replicas: 3 # The number of Fleet instances to deploy
imageRepository: fleetdm/fleet
-imageTag: v4.61.0 # Version of Fleet to deploy
+imageTag: v4.62.1 # Version of Fleet to deploy
podAnnotations: {} # Additional annotations to add to the Fleet pod
serviceAnnotations: {} # Additional annotations to add to the Fleet service
serviceAccountAnnotations: {} # Additional annotations to add to the Fleet service account
diff --git a/docs/Contributing/Audit-logs.md b/docs/Contributing/Audit-logs.md
index feadc6e0cda6..19dea07e2f81 100644
--- a/docs/Contributing/Audit-logs.md
+++ b/docs/Contributing/Audit-logs.md
@@ -1376,6 +1376,7 @@ Generated when an App Store app is added to Fleet.
This activity contains the following fields:
- "software_title": Name of the App Store app.
+- "software_title_id": ID of the added software title.
- "app_store_id": ID of the app on the Apple App Store.
- "platform": Platform of the app (`darwin`, `ios`, or `ipados`).
- "self_service": App installation can be initiated by device owner.
@@ -1387,6 +1388,7 @@ This activity contains the following fields:
```json
{
"software_title": "Logic Pro",
+ "software_title_id": 123,
"app_store_id": "1234567",
"platform": "darwin",
"self_service": false,
diff --git a/ee/server/service/software_installers.go b/ee/server/service/software_installers.go
index 81b539c5b1f4..4bdba884a577 100644
--- a/ee/server/service/software_installers.go
+++ b/ee/server/service/software_installers.go
@@ -438,10 +438,41 @@ func (svc *Service) UpdateSoftwareInstaller(ctx context.Context, payload *fleet.
payload.SelfService = &existingInstaller.SelfService
}
+ // Get the hosts that are NOT in label scope currently (before the update happens)
+ var hostsNotInScope map[uint]struct{}
+ if dirty["Labels"] {
+ hostsNotInScope, err = svc.ds.GetExcludedHostIDMapForSoftwareInstaller(ctx, payload.InstallerID)
+ if err != nil {
+ return nil, ctxerr.Wrap(ctx, err, "getting hosts not in scope for installer")
+ }
+ }
+
if err := svc.ds.SaveInstallerUpdates(ctx, payload); err != nil {
return nil, ctxerr.Wrap(ctx, err, "saving installer updates")
}
+ if dirty["Labels"] {
+ // Get the hosts that are now IN label scope (after the update)
+ hostsInScope, err := svc.ds.GetIncludedHostIDMapForSoftwareInstaller(ctx, payload.InstallerID)
+ if err != nil {
+ return nil, ctxerr.Wrap(ctx, err, "getting hosts in scope for installer")
+ }
+
+ var hostsToClear []uint
+ for id := range hostsInScope {
+ if _, ok := hostsNotInScope[id]; ok {
+ // it was not in scope but now it is, so we should clear policy status
+ hostsToClear = append(hostsToClear, id)
+ }
+ }
+
+ // We clear the policy status here because otherwise the policy automation machinery
+ // won't pick this up and the software won't install.
+ if err := svc.ds.ClearAutoInstallPolicyStatusForHosts(ctx, payload.InstallerID, hostsToClear); err != nil {
+ return nil, ctxerr.Wrap(ctx, err, "failed to clear auto install policy status for host")
+ }
+ }
+
// if we're updating anything other than self-service, we cancel pending installs/uninstalls,
// and if we're updating the package we reset counts. This is run in its own transaction internally
// for consistency, but independent of the installer update query as the main update should stick
@@ -484,7 +515,8 @@ func (svc *Service) UpdateSoftwareInstaller(ctx context.Context, payload *fleet.
}
func (svc *Service) validateEmbeddedSecretsOnScript(ctx context.Context, scriptName string, script *string,
- argErr *fleet.InvalidArgumentError) *fleet.InvalidArgumentError {
+ argErr *fleet.InvalidArgumentError,
+) *fleet.InvalidArgumentError {
if script != nil {
if errScript := svc.ds.ValidateEmbeddedSecrets(ctx, []string{*script}); errScript != nil {
if argErr != nil {
diff --git a/ee/server/service/vpp.go b/ee/server/service/vpp.go
index d8f453a950e3..04eaef1ea9fa 100644
--- a/ee/server/service/vpp.go
+++ b/ee/server/service/vpp.go
@@ -363,17 +363,20 @@ func (svc *Service) AddAppStoreApp(ctx context.Context, teamID *uint, appID flee
Name: assetMD.TrackName,
LatestVersion: assetMD.Version,
}
- if _, err := svc.ds.InsertVPPAppWithTeam(ctx, app, teamID); err != nil {
+
+ addedApp, err := svc.ds.InsertVPPAppWithTeam(ctx, app, teamID)
+ if err != nil {
return ctxerr.Wrap(ctx, err, "writing VPP app to db")
}
act := fleet.ActivityAddedAppStoreApp{
- AppStoreID: app.AdamID,
- Platform: app.Platform,
- TeamName: &teamName,
- SoftwareTitle: app.Name,
- TeamID: teamID,
- SelfService: app.SelfService,
+ AppStoreID: app.AdamID,
+ Platform: app.Platform,
+ TeamName: &teamName,
+ SoftwareTitle: app.Name,
+ SoftwareTitleId: addedApp.TitleID,
+ TeamID: teamID,
+ SelfService: app.SelfService,
}
if err := svc.NewActivity(ctx, authz.UserFromContext(ctx), act); err != nil {
return ctxerr.Wrap(ctx, err, "create activity for add app store app")
diff --git a/frontend/components/PlatformSelector/PlatformSelector.tsx b/frontend/components/PlatformSelector/PlatformSelector.tsx
index 3eb907f9b2ea..c46f85c956b6 100644
--- a/frontend/components/PlatformSelector/PlatformSelector.tsx
+++ b/frontend/components/PlatformSelector/PlatformSelector.tsx
@@ -1,6 +1,12 @@
import React from "react";
import classNames from "classnames";
+
+import { IPolicySoftwareToInstall } from "interfaces/policy";
import Checkbox from "components/forms/fields/Checkbox";
+import CustomLink from "components/CustomLink";
+import TooltipWrapper from "components/TooltipWrapper";
+import { buildQueryStringFromParams } from "utilities/url";
+import paths from "router/paths";
interface IPlatformSelectorProps {
baseClass?: string;
@@ -13,6 +19,8 @@ interface IPlatformSelectorProps {
setCheckLinux: (val: boolean) => void;
setCheckChrome: (val: boolean) => void;
disabled?: boolean;
+ installSoftware?: IPolicySoftwareToInstall;
+ currentTeamId?: number;
}
export const PlatformSelector = ({
@@ -26,6 +34,8 @@ export const PlatformSelector = ({
setCheckLinux,
setCheckChrome,
disabled = false,
+ installSoftware,
+ currentTeamId,
}: IPlatformSelectorProps): JSX.Element => {
const baseClass = "platform-selector";
@@ -33,9 +43,39 @@ export const PlatformSelector = ({
[`form-field__label--disabled`]: disabled,
});
+ const renderInstallSoftwareHelpText = () => {
+ if (!installSoftware) {
+ return null;
+ }
+ const softwareName = installSoftware.name;
+ const softwareId = installSoftware.software_title_id.toString();
+ const softwareLink = `${paths.SOFTWARE_TITLE_DETAILS(
+ softwareId
+ )}?${buildQueryStringFromParams({ team_id: currentTeamId })}`;
+
+ return (
+
+ will only install
+ on{" "}
+
+ To see targets, select{" "}
+ {softwareName} > Actions > Edit. Currently, hosts
+ that aren't targeted show an empty (---) policy status.
+ >
+ }
+ >
+ targeted hosts
+
+ .
+
+ );
+ };
+
return (
- Targets:
+ Target:
- To apply the profile to new hosts, you'll have to delete it and
- upload a new profile.
+ Policy runs on all hosts with these platform(s).
+ {renderInstallSoftwareHelpText()}
{renderTable()}
diff --git a/frontend/pages/ManageControlsPage/Scripts/components/ScriptUploader/helpers.ts b/frontend/pages/ManageControlsPage/Scripts/components/ScriptUploader/helpers.ts
index f341ec38b953..016efb8b6e08 100644
--- a/frontend/pages/ManageControlsPage/Scripts/components/ScriptUploader/helpers.ts
+++ b/frontend/pages/ManageControlsPage/Scripts/components/ScriptUploader/helpers.ts
@@ -1,4 +1,5 @@
import { getErrorReason } from "interfaces/errors";
+import { generateSecretErrMsg } from "pages/SoftwarePage/helpers";
const DEFAULT_ERROR_MESSAGE = "Couldn't upload. Please try again.";
@@ -13,7 +14,7 @@ export const getErrorMessage = (err: unknown) => {
) {
return "Couldn't upload. The file should be .sh or .ps1 file.";
} else if (apiErrMessage.includes("Secret variable")) {
- return apiErrMessage.replace("missing from database", "doesn't exist");
+ return generateSecretErrMsg(err);
}
return apiErrMessage || DEFAULT_ERROR_MESSAGE;
diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareCustomPackage/helpers.tsx b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareCustomPackage/helpers.tsx
index 99a09e229bcc..06e3c2e23fdc 100644
--- a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareCustomPackage/helpers.tsx
+++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareCustomPackage/helpers.tsx
@@ -6,6 +6,8 @@ import { LEARN_MORE_ABOUT_BASE_LINK } from "utilities/constants";
import CustomLink from "components/CustomLink";
+import { generateSecretErrMsg } from "pages/SoftwarePage/helpers";
+
const DEFAULT_ERROR_MESSAGE = "Couldn't add. Please try again.";
// eslint-disable-next-line import/prefer-default-export
@@ -30,7 +32,7 @@ export const getErrorMessage = (err: unknown) => {
>
);
} else if (reason.includes("Secret variable")) {
- return reason.replace("missing from database", "doesn't exist");
+ return generateSecretErrMsg(err);
} else if (reason.includes("Unable to extract necessary metadata")) {
return (
<>
diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetAppDetailsForm/FleetAppDetailsForm.tsx b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetAppDetailsForm/FleetAppDetailsForm.tsx
index d70e229e3646..74d5943b305d 100644
--- a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetAppDetailsForm/FleetAppDetailsForm.tsx
+++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetAppDetailsForm/FleetAppDetailsForm.tsx
@@ -124,7 +124,7 @@ export const InstallTypeSection = ({
color="yellow"
cta={
diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetMaintainedAppDetailsPage.tsx b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetMaintainedAppDetailsPage.tsx
index d47ccd9ef045..8516ff248378 100644
--- a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetMaintainedAppDetailsPage.tsx
+++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/FleetMaintainedAppDetailsPage.tsx
@@ -12,7 +12,6 @@ import labelsAPI, { getCustomLabels } from "services/entities/labels";
import { QueryContext } from "context/query";
import { AppContext } from "context/app";
import { NotificationContext } from "context/notification";
-import { getErrorReason } from "interfaces/errors";
import { Platform, PLATFORM_DISPLAY_NAMES } from "interfaces/platform";
import { ILabelSummary } from "interfaces/label";
import useToggleSidePanel from "hooks/useToggleSidePanel";
@@ -33,6 +32,7 @@ import { IFleetMaintainedAppFormData } from "./FleetAppDetailsForm/FleetAppDetai
import AddFleetAppSoftwareModal from "./AddFleetAppSoftwareModal";
import {
+ getErrorMessage,
getFleetAppPolicyDescription,
getFleetAppPolicyName,
getFleetAppPolicyQuery,
@@ -192,7 +192,7 @@ const FleetMaintainedAppDetailsPage = ({
} catch (error) {
// quick exit if there was an error adding the software. Skip the policy
// creation.
- renderFlash("error", getErrorReason(error));
+ renderFlash("error", getErrorMessage(error));
setShowAddFleetAppSoftwareModal(false);
return;
}
diff --git a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/helpers.tsx b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/helpers.tsx
index 3ccdf8df4c3c..3a1f93411e6d 100644
--- a/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/helpers.tsx
+++ b/frontend/pages/SoftwarePage/SoftwareAddPage/SoftwareFleetMaintained/FleetMaintainedAppDetailsPage/helpers.tsx
@@ -1,3 +1,7 @@
+import { getErrorReason } from "interfaces/errors";
+
+import { generateSecretErrMsg } from "pages/SoftwarePage/helpers";
+
import fleetAppData from "../../../../../../server/mdm/maintainedapps/apps.json";
const NameToIdentifierMap: Record = {
@@ -40,3 +44,13 @@ export const getFleetAppPolicyDescription = (appName: string) => {
export const getFleetAppPolicyQuery = (name: string) => {
return getFleetAppData(name)?.automatic_policy_query;
};
+
+export const getErrorMessage = (err: unknown) => {
+ const reason = getErrorReason(err);
+
+ if (reason.includes("Secret variable")) {
+ return generateSecretErrMsg(err);
+ }
+
+ return reason;
+};
diff --git a/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/ConfirmSaveChangesModal/ConfirmSaveChangesModal.tsx b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/ConfirmSaveChangesModal/ConfirmSaveChangesModal.tsx
index d164f8dd8b89..ecc4bc8f8832 100644
--- a/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/ConfirmSaveChangesModal/ConfirmSaveChangesModal.tsx
+++ b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/ConfirmSaveChangesModal/ConfirmSaveChangesModal.tsx
@@ -3,8 +3,6 @@ import React from "react";
import Button from "components/buttons/Button";
import Modal from "components/Modal";
-import { IPackageFormData } from "pages/SoftwarePage/components/PackageForm/PackageForm";
-
const baseClass = "save-changes-modal";
export interface IConfirmSaveChangesModalProps {
@@ -38,7 +36,7 @@ const ConfirmSaveChangesModal = ({
{warningText}
Installs or uninstalls currently running on a host will still
- complete, but results won’t appear in Fleet.
+ complete, but results won't appear in Fleet.
You cannot undo this action.
diff --git a/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditSoftwareModal/EditSoftwareModal.tsx b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditSoftwareModal/EditSoftwareModal.tsx
index 98f2ee9bc69e..a7c69196906d 100644
--- a/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditSoftwareModal/EditSoftwareModal.tsx
+++ b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditSoftwareModal/EditSoftwareModal.tsx
@@ -24,6 +24,7 @@ import { IPackageFormData } from "pages/SoftwarePage/components/PackageForm/Pack
import {
generateSelectedLabels,
getCustomTarget,
+ getInstallType,
getTargetType,
} from "pages/SoftwarePage/components/PackageForm/helpers";
@@ -70,11 +71,7 @@ const EditSoftwareModal = ({
});
const [uploadProgress, setUploadProgress] = useState(0);
- const {
- data: labels,
- isLoading: isLoadingLabels,
- isError: isErrorLabels,
- } = useQuery(
+ const { data: labels } = useQuery(
["custom_labels"],
() => labelsAPI.summary().then((res) => getCustomLabels(res.labels)),
{
@@ -178,6 +175,7 @@ const EditSoftwareModal = ({
postInstallScript: software.post_install_script || "",
uninstallScript: software.uninstall_script || "",
selfService: software.self_service || false,
+ installType: getInstallType(software),
targetType: getTargetType(software),
customTarget: getCustomTarget(software),
labelTargets: generateSelectedLabels(software),
diff --git a/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditSoftwareModal/helpers.tsx b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditSoftwareModal/helpers.tsx
index 9b4151003cfe..df399a8c5c34 100644
--- a/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditSoftwareModal/helpers.tsx
+++ b/frontend/pages/SoftwarePage/SoftwareTitleDetailsPage/EditSoftwareModal/helpers.tsx
@@ -6,6 +6,7 @@ import { ISoftwarePackage } from "interfaces/software";
import CustomLink from "components/CustomLink";
import { LEARN_MORE_ABOUT_BASE_LINK } from "utilities/constants";
+import { generateSecretErrMsg } from "pages/SoftwarePage/helpers";
const DEFAULT_ERROR_MESSAGE = "Couldn't edit software. Please try again.";
@@ -36,9 +37,7 @@ export const getErrorMessage = (err: unknown, software: ISoftwarePackage) => {
>
);
} else if (reason.includes("Secret variable")) {
- return reason
- .replace("missing from database", "doesn't exist")
- .replace("Couldn't add", "Couldn't edit");
+ return generateSecretErrMsg(err).replace("Couldn't add", "Couldn't edit");
}
return reason || DEFAULT_ERROR_MESSAGE;
diff --git a/frontend/pages/SoftwarePage/components/PackageForm/PackageForm.tsx b/frontend/pages/SoftwarePage/components/PackageForm/PackageForm.tsx
index 8da22a54071f..a623cc7884c6 100644
--- a/frontend/pages/SoftwarePage/components/PackageForm/PackageForm.tsx
+++ b/frontend/pages/SoftwarePage/components/PackageForm/PackageForm.tsx
@@ -241,12 +241,14 @@ const PackageForm = ({
formData.software ? getFileDetails(formData.software) : undefined
}
/>
-
+ {!isEditingSoftware && (
+
+ )}
{
+ return softwarePackage.automatic_install_policies ? "automatic" : "manual";
+};
+
export const getTargetType = (softwarePackage: ISoftwarePackage) => {
if (!softwarePackage) return "All hosts";
diff --git a/frontend/pages/SoftwarePage/helpers.ts b/frontend/pages/SoftwarePage/helpers.ts
new file mode 100644
index 000000000000..a323982309b5
--- /dev/null
+++ b/frontend/pages/SoftwarePage/helpers.ts
@@ -0,0 +1,43 @@
+import { getErrorReason } from "interfaces/errors";
+
+/**
+ * helper function to generate error message for secret variables based
+ * on the error reason.
+ */
+// eslint-disable-next-line import/prefer-default-export
+export const generateSecretErrMsg = (err: unknown) => {
+ const reason = getErrorReason(err);
+
+ let errorType = "";
+ if (getErrorReason(err, { nameEquals: "install script" })) {
+ errorType = "install script";
+ } else if (getErrorReason(err, { nameEquals: "post-install script" })) {
+ errorType = "post-install script";
+ } else if (getErrorReason(err, { nameEquals: "uninstall script" })) {
+ errorType = "uninstall script";
+ } else if (getErrorReason(err, { nameEquals: "profile" })) {
+ errorType = "profile";
+ }
+
+ if (errorType === "profile") {
+ // for profiles we can get two different error messages. One contains a colon
+ // and the other doesn't. We need to handle both cases.
+ const message = reason.split(":").pop() ?? "";
+
+ return message
+ .replace(/Secret variables?/i, "Variable")
+ .replace("missing from database", "doesn't exist.");
+ }
+
+ // all other specific error types
+ if (errorType) {
+ return reason
+ .replace(/Secret variables?/i, `Variable used in ${errorType} `)
+ .replace("missing from database", "doesn't exist.");
+ }
+
+ // no spcial error type. return generic secret error message
+ return reason
+ .replace(/Secret variables?/i, "Variable")
+ .replace("missing from database", "doesn't exist.");
+};
diff --git a/frontend/pages/admin/OrgSettingsPage/_styles.scss b/frontend/pages/admin/OrgSettingsPage/_styles.scss
index 9919e5865f96..10d7a76b4af3 100644
--- a/frontend/pages/admin/OrgSettingsPage/_styles.scss
+++ b/frontend/pages/admin/OrgSettingsPage/_styles.scss
@@ -58,18 +58,6 @@
em {
font-style: normal;
}
-
- &--configured {
- em {
- color: $ui-success;
- }
- }
-
- &--notconfigured {
- em {
- color: $ui-error;
- }
- }
}
}
diff --git a/frontend/pages/admin/OrgSettingsPage/cards/Smtp/Smtp.tsx b/frontend/pages/admin/OrgSettingsPage/cards/Smtp/Smtp.tsx
index d1f6160a5b1c..eaef7914de9d 100644
--- a/frontend/pages/admin/OrgSettingsPage/cards/Smtp/Smtp.tsx
+++ b/frontend/pages/admin/OrgSettingsPage/cards/Smtp/Smtp.tsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect, useContext } from "react";
+import React, { useState, useContext } from "react";
import { AppContext } from "context/app";
@@ -43,6 +43,54 @@ interface ISmtpConfigFormErrors {
password?: string | null;
}
+const validateFormData = (newData: ISmtpConfigFormData) => {
+ const errors: ISmtpConfigFormErrors = {};
+
+ const {
+ enableSMTP,
+ smtpSenderAddress,
+ smtpServer,
+ smtpPort,
+ smtpAuthenticationType,
+ smtpUsername,
+ smtpPassword,
+ } = newData;
+
+ if (enableSMTP) {
+ if (!smtpSenderAddress) {
+ errors.sender_address = "SMTP sender address must be present";
+ } else if (!validEmail(smtpSenderAddress)) {
+ errors.sender_address = `${smtpSenderAddress} is not a valid email`;
+ }
+
+ if (!smtpServer) {
+ errors.server = "SMTP server must be present";
+ }
+ if (!smtpPort) {
+ errors.server = "SMTP server port must be present";
+ errors.server_port = "Port";
+ }
+ if (!smtpServer && !smtpPort) {
+ errors.server = "SMTP server and server port must be present";
+ errors.server_port = "Port";
+ }
+ if (smtpAuthenticationType === "authtype_username_password") {
+ if (smtpUsername === "") {
+ errors.user_name = "SMTP username must be present";
+ }
+ if (smtpPassword === "") {
+ errors.password = "SMTP password must be present";
+ }
+ }
+ } else if (smtpSenderAddress && !validEmail(smtpSenderAddress)) {
+ // validations for valid submissions even when smtp not enabled, i.e., updating what will be
+ // used once it IS enabled
+ errors.sender_address = `${smtpSenderAddress} is not a valid email`;
+ }
+
+ return errors;
+};
+
const baseClass = "app-config-form";
const Smtp = ({
@@ -82,50 +130,35 @@ const Smtp = ({
const sesConfigured = appConfig.email?.backend === "ses" || false;
const onInputChange = ({ name, value }: IFormField) => {
- setFormData({ ...formData, [name]: value });
- };
-
- const validateForm = () => {
- const errors: ISmtpConfigFormErrors = {};
-
- if (enableSMTP) {
- if (!smtpSenderAddress) {
- errors.sender_address = "SMTP sender address must be present";
- } else if (!validEmail(smtpSenderAddress)) {
- errors.sender_address = `${smtpSenderAddress} is not a valid email`;
- }
-
- if (!smtpServer) {
- errors.server = "SMTP server must be present";
- }
- if (!smtpPort) {
- errors.server = "SMTP server port must be present";
- errors.server_port = "Port";
+ const newFormData = { ...formData, [name]: value };
+ setFormData(newFormData);
+ const newErrs = validateFormData(newFormData);
+ // only set errors that are updates of existing errors
+ // new errors are only set onBlur or submit
+ const errsToSet: Record = {};
+ Object.keys(formErrors).forEach((k) => {
+ // @ts-ignore
+ if (newErrs[k]) {
+ // @ts-ignore
+ errsToSet[k] = newErrs[k];
}
- if (!smtpServer && !smtpPort) {
- errors.server = "SMTP server and server port must be present";
- errors.server_port = "Port";
- }
- if (smtpAuthenticationType === "authtype_username_password") {
- if (smtpUsername === "") {
- errors.user_name = "SMTP username must be present";
- }
- if (smtpPassword === "") {
- errors.password = "SMTP password must be present";
- }
- }
- }
-
- setFormErrors(errors);
+ });
+ setFormErrors(errsToSet);
};
- useEffect(() => {
- validateForm();
- }, [smtpAuthenticationType]);
+ const onInputBlur = () => {
+ setFormErrors(validateFormData(formData));
+ };
const onFormSubmit = (evt: React.MouseEvent) => {
evt.preventDefault();
+ const errs = validateFormData(formData);
+ if (Object.keys(errs).length > 0) {
+ setFormErrors(errs);
+ return;
+ }
+
// Formatting of API not UI
const formDataToSubmit = {
smtp_settings: {
@@ -157,7 +190,7 @@ const Smtp = ({
name="smtpUsername"
value={smtpUsername}
parseTarget
- onBlur={validateForm}
+ onBlur={onInputBlur}
error={formErrors.user_name}
blockAutoComplete
/>
@@ -168,7 +201,7 @@ const Smtp = ({
name="smtpPassword"
value={smtpPassword}
parseTarget
- onBlur={validateForm}
+ onBlur={onInputBlur}
error={formErrors.password}
blockAutoComplete
/>
@@ -177,6 +210,7 @@ const Smtp = ({
options={authMethodOptions}
placeholder=""
onChange={onInputChange}
+ onBlur={onInputBlur}
name="smtpAuthenticationMethod"
value={smtpAuthenticationMethod}
parseTarget
@@ -205,6 +239,7 @@ const Smtp = ({
If a configuration profile uses this label as a custom target, the
- profile will break: it won't be applied to new hosts.
+ profile will break. After deleting the label, remove broken profiles
+ and upload new profiles in their place.
- To apply the profile to new hosts, you'll have to delete it and
- upload a new profile.
+ If software uses this label as a custom target, the label will not be
+ able to be deleted. Please remove the label from the software target
+ first before deleting.