diff --git a/.github/workflows/build_nudge_pr.yml b/.github/workflows/build_nudge_pr.yml
index 1efe4b87..17ab5554 100644
--- a/.github/workflows/build_nudge_pr.yml
+++ b/.github/workflows/build_nudge_pr.yml
@@ -11,20 +11,20 @@ jobs:
steps:
- name: Checkout nudge repo
- uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Install Apple Xcode certificates
- uses: apple-actions/import-codesign-certs@8f3fb608891dd2244cdab3d69cd68c0d37a7fe93 # v2.0.0
+ uses: apple-actions/import-codesign-certs@63fff01cd422d4b7b855d40ca1e9d34d2de9427d # v3.0.0
with:
keychain-password: ${{ github.run_id }}
p12-file-base64: ${{ secrets.APP_CERTIFICATES_P12_MAOS }}
p12-password: ${{ secrets.APP_CERTIFICATES_P12_PASSWORD_MAOS }}
- name: Install Apple Installer certificates
- uses: apple-actions/import-codesign-certs@8f3fb608891dd2244cdab3d69cd68c0d37a7fe93 # v2.0.0
+ uses: apple-actions/import-codesign-certs@63fff01cd422d4b7b855d40ca1e9d34d2de9427d # v3.0.0
with:
create-keychain: false # do not create a new keychain for this value
keychain-password: ${{ github.run_id }}
@@ -41,7 +41,7 @@ jobs:
echo "NUDGE_MAIN_VERSION=$(/bin/cat ./build_info_main.txt)" >> $GITHUB_ENV
- name: Upload zip archive
- uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
+ uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
with:
name: packages
path: outputs/
diff --git a/.github/workflows/build_nudge_prerelease.yml b/.github/workflows/build_nudge_prerelease.yml
index 56983b78..6acf5dd6 100644
--- a/.github/workflows/build_nudge_prerelease.yml
+++ b/.github/workflows/build_nudge_prerelease.yml
@@ -19,19 +19,19 @@ jobs:
steps:
- name: Checkout nudge repo
- uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0
- name: Install Apple Xcode certificates
- uses: apple-actions/import-codesign-certs@8f3fb608891dd2244cdab3d69cd68c0d37a7fe93 # v2.0.0
+ uses: apple-actions/import-codesign-certs@63fff01cd422d4b7b855d40ca1e9d34d2de9427d # v3.0.0
with:
keychain-password: ${{ github.run_id }}
p12-file-base64: ${{ secrets.APP_CERTIFICATES_P12_MAOS }}
p12-password: ${{ secrets.APP_CERTIFICATES_P12_PASSWORD_MAOS }}
- name: Install Apple Installer certificates
- uses: apple-actions/import-codesign-certs@8f3fb608891dd2244cdab3d69cd68c0d37a7fe93 # v2.0.0
+ uses: apple-actions/import-codesign-certs@63fff01cd422d4b7b855d40ca1e9d34d2de9427d # v3.0.0
with:
create-keychain: false # do not create a new keychain for this value
keychain-password: ${{ github.run_id }}
@@ -89,7 +89,7 @@ jobs:
files: ${{github.workspace}}/outputs/*.pkg
- name: Upload packages
- uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
+ uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
with:
name: packages
path: outputs/
diff --git a/.github/workflows/build_nudge_prerelease_manual.yml b/.github/workflows/build_nudge_prerelease_manual.yml
index 141937db..9bbc2092 100644
--- a/.github/workflows/build_nudge_prerelease_manual.yml
+++ b/.github/workflows/build_nudge_prerelease_manual.yml
@@ -11,19 +11,19 @@ jobs:
steps:
- name: Checkout nudge repo
- uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0
- name: Install Apple Xcode certificates
- uses: apple-actions/import-codesign-certs@8f3fb608891dd2244cdab3d69cd68c0d37a7fe93 # v2.0.0
+ uses: apple-actions/import-codesign-certs@63fff01cd422d4b7b855d40ca1e9d34d2de9427d # v3.0.0
with:
keychain-password: ${{ github.run_id }}
p12-file-base64: ${{ secrets.APP_CERTIFICATES_P12_MAOS }}
p12-password: ${{ secrets.APP_CERTIFICATES_P12_PASSWORD_MAOS }}
- name: Install Apple Installer certificates
- uses: apple-actions/import-codesign-certs@8f3fb608891dd2244cdab3d69cd68c0d37a7fe93 # v2.0.0
+ uses: apple-actions/import-codesign-certs@63fff01cd422d4b7b855d40ca1e9d34d2de9427d # v3.0.0
with:
create-keychain: false # do not create a new keychain for this value
keychain-password: ${{ github.run_id }}
@@ -81,7 +81,7 @@ jobs:
files: ${{github.workspace}}/outputs/*.pkg
- name: Upload packages
- uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
+ uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
with:
name: packages
path: outputs/
diff --git a/.github/workflows/build_nudge_release.yml b/.github/workflows/build_nudge_release.yml
index d5ccbece..f84fe238 100644
--- a/.github/workflows/build_nudge_release.yml
+++ b/.github/workflows/build_nudge_release.yml
@@ -19,19 +19,19 @@ jobs:
steps:
- name: Checkout nudge repo
- uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0
- name: Install Apple Xcode certificates
- uses: apple-actions/import-codesign-certs@8f3fb608891dd2244cdab3d69cd68c0d37a7fe93 # v2.0.0
+ uses: apple-actions/import-codesign-certs@63fff01cd422d4b7b855d40ca1e9d34d2de9427d # v3.0.0
with:
keychain-password: ${{ github.run_id }}
p12-file-base64: ${{ secrets.APP_CERTIFICATES_P12_MAOS }}
p12-password: ${{ secrets.APP_CERTIFICATES_P12_PASSWORD_MAOS }}
- name: Install Apple Installer certificates
- uses: apple-actions/import-codesign-certs@8f3fb608891dd2244cdab3d69cd68c0d37a7fe93 # v2.0.0
+ uses: apple-actions/import-codesign-certs@63fff01cd422d4b7b855d40ca1e9d34d2de9427d # v3.0.0
with:
create-keychain: false # do not create a new keychain for this value
keychain-password: ${{ github.run_id }}
@@ -89,7 +89,7 @@ jobs:
files: ${{github.workspace}}/outputs/*.pkg
- name: Upload packages
- uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
+ uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
with:
name: packages
path: outputs/
diff --git a/.github/workflows/build_nudge_release_manual.yml b/.github/workflows/build_nudge_release_manual.yml
index 97551cb5..a60f703d 100644
--- a/.github/workflows/build_nudge_release_manual.yml
+++ b/.github/workflows/build_nudge_release_manual.yml
@@ -11,19 +11,19 @@ jobs:
steps:
- name: Checkout nudge repo
- uses: actions/checkout@24cb9080177205b6e8c946b17badbe402adc938f # v3.4.0
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0
- name: Install Apple Xcode certificates
- uses: apple-actions/import-codesign-certs@8f3fb608891dd2244cdab3d69cd68c0d37a7fe93 # v2.0.0
+ uses: apple-actions/import-codesign-certs@63fff01cd422d4b7b855d40ca1e9d34d2de9427d # v3.0.0
with:
keychain-password: ${{ github.run_id }}
p12-file-base64: ${{ secrets.APP_CERTIFICATES_P12_MAOS }}
p12-password: ${{ secrets.APP_CERTIFICATES_P12_PASSWORD_MAOS }}
- name: Install Apple Installer certificates
- uses: apple-actions/import-codesign-certs@8f3fb608891dd2244cdab3d69cd68c0d37a7fe93 # v2.0.0
+ uses: apple-actions/import-codesign-certs@63fff01cd422d4b7b855d40ca1e9d34d2de9427d # v3.0.0
with:
create-keychain: false # do not create a new keychain for this value
keychain-password: ${{ github.run_id }}
@@ -81,7 +81,7 @@ jobs:
files: ${{github.workspace}}/outputs/*.pkg
- name: Upload packages
- uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
+ uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
with:
name: packages
path: outputs/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 843d3546..d4637f76 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,43 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [2.0.5] - 2024-07-24
+Requires macOS 12.0 and higher.
+
+### Added
+- To artificially change the `requredInstallationDate` to honor a previous macOS minor version, set `minorVersionRecalculationThreshold` under `osVersionRequirement` in amount of minor versions.
+ - Ex: `minorVersionRecalculationThreshold` is set to 1 and SOFA feed has macOS 14.5 available
+ - macOS device is 14.0: Required OS: 14.5 - Target macOS 14.4.1 requiredInstallationDate of 2024-04-08 00:00:00 +0000
+ - macOS device is 14.1: Required OS: 14.5 - Target macOS 14.4.1 requiredInstallationDate of 2024-04-08 00:00:00 +0000
+ - macOS device is 14.2: Required OS: 14.5 - Target macOS 14.4.1 requiredInstallationDate of 2024-04-08 00:00:00 +0000
+ - macOS device is 14.3: Required OS: 14.5 - Target macOS 14.4.1 requiredInstallationDate of 2024-04-08 00:00:00 +0000
+ - macOS device is 14.4: Required OS: 14.5 - Target macOS 14.4.1 requiredInstallationDate of 2024-04-08 00:00:00 +0000
+ - macOS device is 14.4.1: Required OS: 14.5 - Target macOS 14.4.1 requiredInstallationDate of 2024-04-15 00:00:00 +0000
+ - This device's requiredInstallationDate is different than the others as there is no active exploit on 14.4.1
+ - macOS device is 14.5: Required OS: 14.5 - Fully updated
+ - Ex: `minorVersionRecalculationThreshold` is set to 2 and SOFA feed has macOS 14.5 available
+ - macOS device is 14.0: Required OS: 14.5 - Target macOS 14.4 requiredInstallationDate of 2024-03-21 00:00:00 +0000
+ - macOS device is 14.1: Required OS: 14.5 - Target macOS 14.4 requiredInstallationDate of 2024-03-21 00:00:00 +0000
+ - macOS device is 14.2: Required OS: 14.5 - Target macOS 14.4 requiredInstallationDate of 2024-03-21 00:00:00 +0000
+ - macOS device is 14.3: Required OS: 14.5 - Target macOS 14.4 requiredInstallationDate of 2024-03-21 00:00:00 +0000
+ - macOS device is 14.4: Required OS: 14.5 - Target macOS 14.4 requiredInstallationDate of 2024-03-21 00:00:00 +0000
+ - macOS device is 14.4.1: Required OS: 14.5 - Target macOS 14.4.1 requiredInstallationDate of 2024-04-15 00:00:00 +0000
+ - macOS device is 14.5: Required OS: 14.5 - Fully updated
+ - Addresses [612](https://github.com/macadmins/nudge/issues/612)
+
+### Changed
+- The `Actively Exploited` logic internally within Nudge and the UI on the left sidebar will show `True` if any previous updates missing on the device had active exploits.
+ - **WARNNG BREAKING CHANGE** - This changes the SLA computation and will result in a different `requiredInstallationDate` than offered in Nudge v2.0 -> v2.01.
+ - Ex: Device is on 14.3 and needing to go to 14.5.
+ - While 14.4.1 -> 14.5 are not under active exploit, 14.4 contains fixes for 14.3 that were under active exploit.
+ - Addresses [610](https://github.com/macadmins/nudge/issues/610) and [613](https://github.com/macadmins/nudge/issues/613)
+- When `showRequiredDate` is set to `True` and the admin is using the default values for `requiredInstallationDisplayFormat`, Nudge will attempt to understand the current locale and display the menu item appropriately.
+ - Addresses [615](https://github.com/macadmins/nudge/issues/615)
+
+### Fixed
+- Several components in the Github Actions were triggering deprecation warnings. These have been addressed by updating to the latest version of these components
+ - Addresses [616](https://github.com/macadmins/nudge/issues/616)
+
## [2.0.4] - 2024-07-23
Requires macOS 12.0 and higher.
diff --git a/Nudge.xcodeproj/project.pbxproj b/Nudge.xcodeproj/project.pbxproj
index 4ea9750c..88ffddff 100644
--- a/Nudge.xcodeproj/project.pbxproj
+++ b/Nudge.xcodeproj/project.pbxproj
@@ -698,7 +698,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.0;
- MARKETING_VERSION = 2.0.4;
+ MARKETING_VERSION = 2.0.5;
PRODUCT_BUNDLE_IDENTIFIER = com.github.macadmins.Nudge;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -729,7 +729,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.0;
- MARKETING_VERSION = 2.0.4;
+ MARKETING_VERSION = 2.0.5;
PRODUCT_BUNDLE_IDENTIFIER = com.github.macadmins.Nudge;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
diff --git a/Nudge/Info.plist b/Nudge/Info.plist
index 37a39d0b..31ab7d01 100644
--- a/Nudge/Info.plist
+++ b/Nudge/Info.plist
@@ -15,9 +15,9 @@
CFBundlePackageType
$(PRODUCT_BUNDLE_PACKAGE_TYPE)
CFBundleShortVersionString
- 2.0.4
+ 2.0.5
CFBundleVersion
- 2.0.4
+ 2.0.5
LSApplicationCategoryType
public.app-category.utilities
LSMinimumSystemVersion
diff --git a/Nudge/Preferences/DefaultPreferencesNudge.swift b/Nudge/Preferences/DefaultPreferencesNudge.swift
index 05f4808e..ad785943 100644
--- a/Nudge/Preferences/DefaultPreferencesNudge.swift
+++ b/Nudge/Preferences/DefaultPreferencesNudge.swift
@@ -233,6 +233,12 @@ struct OSVersionRequirementVariables {
""
}
+ static var minorVersionRecalculationThreshold: Int {
+ osVersionRequirementsProfile?.minorVersionRecalculationThreshold ??
+ osVersionRequirementsJSON?.minorVersionRecalculationThreshold ??
+ 0
+ }
+
static var nonActivelyExploitedCVEsMajorUpgradeSLA: Int {
osVersionRequirementsProfile?.nonActivelyExploitedCVEsMajorUpgradeSLA ??
osVersionRequirementsJSON?.nonActivelyExploitedCVEsMajorUpgradeSLA ??
diff --git a/Nudge/Preferences/PreferencesStructure.swift b/Nudge/Preferences/PreferencesStructure.swift
index 085b6e8b..65572c35 100644
--- a/Nudge/Preferences/PreferencesStructure.swift
+++ b/Nudge/Preferences/PreferencesStructure.swift
@@ -151,6 +151,7 @@ struct OSVersionRequirement: Codable {
var activelyExploitedCVEsMajorUpgradeSLA: Int?
var activelyExploitedCVEsMinorUpdateSLA: Int?
var majorUpgradeAppPath: String?
+ var minorVersionRecalculationThreshold: Int?
var nonActivelyExploitedCVEsMajorUpgradeSLA: Int?
var nonActivelyExploitedCVEsMinorUpdateSLA: Int?
var requiredInstallationDate: Date?
@@ -170,6 +171,7 @@ extension OSVersionRequirement {
self.activelyExploitedCVEsMajorUpgradeSLA = fromDictionary["activelyExploitedCVEsMajorUpgradeSLA"] as? Int
self.activelyExploitedCVEsMinorUpdateSLA = fromDictionary["activelyExploitedCVEsMinorUpdateSLA"] as? Int
self.majorUpgradeAppPath = fromDictionary["majorUpgradeAppPath"] as? String
+ self.minorVersionRecalculationThreshold = fromDictionary["minorVersionRecalculationThreshold"] as? Int
self.nonActivelyExploitedCVEsMajorUpgradeSLA = fromDictionary["nonActivelyExploitedCVEsMajorUpgradeSLA"] as? Int
self.nonActivelyExploitedCVEsMinorUpdateSLA = fromDictionary["nonActivelyExploitedCVEsMinorUpdateSLA"] as? Int
self.requiredMinimumOSVersion = fromDictionary["requiredMinimumOSVersion"] as? String
@@ -248,6 +250,7 @@ extension OSVersionRequirement {
activelyExploitedCVEsMajorUpgradeSLA: Int? = nil,
activelyExploitedCVEsMinorUpdateSLA: Int? = nil,
majorUpgradeAppPath: String? = nil,
+ minorVersionRecalculationThreshold: Int? = nil,
nonActivelyExploitedCVEsMajorUpgradeSLA: Int? = nil,
nonActivelyExploitedCVEsMinorUpdateSLA: Int? = nil,
requiredInstallationDate: Date? = nil,
@@ -265,6 +268,7 @@ extension OSVersionRequirement {
activelyExploitedCVEsMajorUpgradeSLA: activelyExploitedCVEsMajorUpgradeSLA ?? self.activelyExploitedCVEsMajorUpgradeSLA,
activelyExploitedCVEsMinorUpdateSLA: activelyExploitedCVEsMinorUpdateSLA ?? self.activelyExploitedCVEsMinorUpdateSLA,
majorUpgradeAppPath: majorUpgradeAppPath ?? self.majorUpgradeAppPath,
+ minorVersionRecalculationThreshold: minorVersionRecalculationThreshold ?? self.minorVersionRecalculationThreshold,
nonActivelyExploitedCVEsMajorUpgradeSLA: nonActivelyExploitedCVEsMajorUpgradeSLA ?? self.nonActivelyExploitedCVEsMajorUpgradeSLA,
nonActivelyExploitedCVEsMinorUpdateSLA: nonActivelyExploitedCVEsMinorUpdateSLA ?? self.nonActivelyExploitedCVEsMinorUpdateSLA,
requiredInstallationDate: requiredInstallationDate ?? self.requiredInstallationDate,
diff --git a/Nudge/UI/Main.swift b/Nudge/UI/Main.swift
index 2190476b..dc7d2e20 100644
--- a/Nudge/UI/Main.swift
+++ b/Nudge/UI/Main.swift
@@ -178,6 +178,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {
var foundMatch = false
Globals.sofaAssets = NetworkFileManager().getSOFAAssets()
if let macOSSOFAAssets = Globals.sofaAssets?.osVersions {
+ // Get current installed OS version
+ let currentInstalledVersion = GlobalVariables.currentOSVersion
+ let currentMajorVersion = VersionManager.getMajorVersion(from: currentInstalledVersion)
+
for osVersion in macOSSOFAAssets {
if PrefsWrapper.requiredMinimumOSVersion == "latest" {
selectedOS = osVersion.latest
@@ -204,24 +208,64 @@ class AppDelegate: NSObject, NSApplicationDelegate {
continue
}
}
- let activelyExploitedCVEs = selectedOS!.activelyExploitedCVEs.count > 0
+
+ var totalActivelyExploitedCVEs = 0
+ let selectedOSVersion = selectedOS!.productVersion
+ var allVersions = [String]()
+
+ // Collect all versions
+ for osVersion in macOSSOFAAssets {
+ allVersions.append(osVersion.latest.productVersion)
+ for securityRelease in osVersion.securityReleases {
+ allVersions.append(securityRelease.productVersion)
+ }
+ }
+
+ // Sort versions
+ allVersions.sort { VersionManager.versionLessThan(currentVersion: $0, newVersion: $1) }
+
+ // Filter versions between current and selected OS version
+ let filteredVersions = VersionManager().removeDuplicates(from: allVersions.filter {
+ VersionManager.versionGreaterThanOrEqual(currentVersion: $0, newVersion: currentInstalledVersion) &&
+ VersionManager.versionLessThanOrEqual(currentVersion: $0, newVersion: selectedOSVersion)
+ })
+
+ // Filter versions with the same major version as the current installed version
+ let minorVersions = VersionManager().removeDuplicates(from: filteredVersions.filter { version in
+ VersionManager.getMajorVersion(from: version) == currentMajorVersion
+ })
+
+ // Count actively exploited CVEs in the filtered versions
+ LogManager.notice("Assessing macOS version range for active exploits: \(filteredVersions) ", logger: sofaLog)
+ for osVersion in macOSSOFAAssets {
+ if filteredVersions.contains(osVersion.latest.productVersion) {
+ totalActivelyExploitedCVEs += osVersion.latest.activelyExploitedCVEs.count
+ }
+ for securityRelease in osVersion.securityReleases {
+ if filteredVersions.contains(securityRelease.productVersion) {
+ totalActivelyExploitedCVEs += securityRelease.activelyExploitedCVEs.count
+ }
+ }
+ }
+ let activelyExploitedCVEs = totalActivelyExploitedCVEs > 0
+
let presentCVEs = selectedOS!.cves.count > 0
let slaExtension: TimeInterval
switch (activelyExploitedCVEs, presentCVEs, AppStateManager().requireMajorUpgrade()) {
- case (false, true, true):
- slaExtension = TimeInterval(OSVersionRequirementVariables.nonActivelyExploitedCVEsMajorUpgradeSLA * 86400)
- case (false, true, false):
- slaExtension = TimeInterval(OSVersionRequirementVariables.nonActivelyExploitedCVEsMinorUpdateSLA * 86400)
- case (true, true, true):
- slaExtension = TimeInterval(OSVersionRequirementVariables.activelyExploitedCVEsMajorUpgradeSLA * 86400)
- case (true, true, false):
- slaExtension = TimeInterval(OSVersionRequirementVariables.activelyExploitedCVEsMinorUpdateSLA * 86400)
- case (false, false, true):
- slaExtension = TimeInterval(OSVersionRequirementVariables.standardMajorUpgradeSLA * 86400)
- case (false, false, false):
- slaExtension = TimeInterval(OSVersionRequirementVariables.standardMinorUpdateSLA * 86400)
- default: // If we get here, something is wrong, use 90 days as a safety
- slaExtension = TimeInterval(90 * 86400)
+ case (false, true, true):
+ slaExtension = TimeInterval(OSVersionRequirementVariables.nonActivelyExploitedCVEsMajorUpgradeSLA * 86400)
+ case (false, true, false):
+ slaExtension = TimeInterval(OSVersionRequirementVariables.nonActivelyExploitedCVEsMinorUpdateSLA * 86400)
+ case (true, true, true):
+ slaExtension = TimeInterval(OSVersionRequirementVariables.activelyExploitedCVEsMajorUpgradeSLA * 86400)
+ case (true, true, false):
+ slaExtension = TimeInterval(OSVersionRequirementVariables.activelyExploitedCVEsMinorUpdateSLA * 86400)
+ case (false, false, true):
+ slaExtension = TimeInterval(OSVersionRequirementVariables.standardMajorUpgradeSLA * 86400)
+ case (false, false, false):
+ slaExtension = TimeInterval(OSVersionRequirementVariables.standardMinorUpdateSLA * 86400)
+ default: // If we get here, something is wrong, use 90 days as a safety
+ slaExtension = TimeInterval(90 * 86400)
}
if OptionalFeatureVariables.disableNudgeForStandardInstalls && !presentCVEs {
@@ -235,7 +279,32 @@ class AppDelegate: NSObject, NSApplicationDelegate {
nudgePrimaryState.activelyExploitedCVEs = activelyExploitedCVEs
releaseDate = selectedOS!.releaseDate ?? Date()
if requiredInstallationDate == Date(timeIntervalSince1970: 0) {
- requiredInstallationDate = selectedOS!.releaseDate?.addingTimeInterval(slaExtension) ?? DateManager().getCurrentDate().addingTimeInterval(TimeInterval(90 * 86400))
+ if OSVersionRequirementVariables.minorVersionRecalculationThreshold > 0 {
+ if minorVersions.isEmpty {
+ requiredInstallationDate = selectedOS!.releaseDate?.addingTimeInterval(slaExtension) ?? DateManager().getCurrentDate().addingTimeInterval(TimeInterval(90 * 86400))
+ } else {
+ let safeIndex = max(0, minorVersions.count - (OSVersionRequirementVariables.minorVersionRecalculationThreshold + 1)) // Ensure the index is within bounds
+ let targetVersion = minorVersions[safeIndex]
+ var foundVersion = false
+ LogManager.notice("minorVersionRecalculationThreshold is set to \(OSVersionRequirementVariables.minorVersionRecalculationThreshold) - Current Version: \(currentInstalledVersion) - Targeting version \(targetVersion) requiredInstallationDate via SOFA", logger: sofaLog)
+ for osVersion in macOSSOFAAssets {
+ for securityRelease in osVersion.securityReleases.reversed() {
+ if VersionManager.versionGreaterThanOrEqual(currentVersion: securityRelease.productVersion, newVersion: targetVersion) && VersionManager.versionLessThanOrEqual(currentVersion: currentInstalledVersion, newVersion: targetVersion) {
+ requiredInstallationDate = securityRelease.releaseDate?.addingTimeInterval(slaExtension) ?? DateManager().getCurrentDate().addingTimeInterval(TimeInterval(90 * 86400))
+ LogManager.notice("Found target macOS version \(targetVersion) - releaseDate is \(securityRelease.releaseDate!), slaExtension is \(LoggerUtilities().printTimeInterval(slaExtension))", logger: sofaLog)
+ foundVersion = true
+ break
+ }
+ }
+ }
+ if !foundVersion {
+ LogManager.warning("Could not find requiredInstallationDate from target macOS \(targetVersion)", logger: sofaLog)
+ requiredInstallationDate = selectedOS!.releaseDate?.addingTimeInterval(slaExtension) ?? DateManager().getCurrentDate().addingTimeInterval(TimeInterval(90 * 86400))
+ }
+ }
+ } else {
+ requiredInstallationDate = selectedOS!.releaseDate?.addingTimeInterval(slaExtension) ?? DateManager().getCurrentDate().addingTimeInterval(TimeInterval(90 * 86400))
+ }
LogManager.notice("Setting requiredInstallationDate via SOFA to \(requiredInstallationDate)", logger: sofaLog)
}
LogManager.notice("SOFA Matched OS Version: \(selectedOS!.productVersion)", logger: sofaLog)
@@ -252,7 +321,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
supportedDevice in Globals.hardwareModelIDs.contains { $0.uppercased() == supportedDevice.uppercased() } }
)
LogManager.notice("Assessed Model ID found in SOFA Entry: \(deviceMatchFound)", logger: sofaLog)
- nudgePrimaryState.deviceSupportedByOSVersion = deviceMatchFound // false
+ nudgePrimaryState.deviceSupportedByOSVersion = deviceMatchFound
}
}
foundMatch = true
diff --git a/Nudge/Utilities/Utils.swift b/Nudge/Utilities/Utils.swift
index 2b971f83..69bcf60f 100644
--- a/Nudge/Utilities/Utils.swift
+++ b/Nudge/Utilities/Utils.swift
@@ -553,10 +553,20 @@ struct DateManager {
return formatter
}()
- func coerceDateToString(date: Date, formatterString: String) -> String {
- let formatter = DateFormatter()
- formatter.dateFormat = formatterString
- return formatter.string(from: date)
+ func coerceDateToString(date: Date, formatterString: String, locale: Locale? = nil) -> String {
+ if formatterString == "MM/dd/yyyy" {
+ // Use the specified locale or the current locale if none is provided
+ let dateFormatter = DateFormatter()
+ dateFormatter.dateStyle = .short
+ dateFormatter.timeStyle = .none
+ dateFormatter.locale = locale ?? Locale.current
+ return dateFormatter.string(from: date)
+ } else {
+ let formatter = DateFormatter()
+ formatter.dateFormat = formatterString
+ formatter.locale = locale ?? Locale.current
+ return formatter.string(from: date)
+ }
}
func coerceStringToDate(dateString: String) -> Date {
@@ -884,6 +894,15 @@ struct LoggerUtilities {
return PrefsWrapper.requiredMinimumOSVersion == "0.0"
}
+ func printTimeInterval(_ interval: TimeInterval) -> String {
+ let days = Int(interval) / (24 * 3600)
+ let hours = (Int(interval) % (24 * 3600)) / 3600
+ let minutes = (Int(interval) % 3600) / 60
+ let seconds = Int(interval) % 60
+
+ return "\(days) days, \(hours) hours, \(minutes) minutes, \(seconds) seconds"
+ }
+
private func updateDeferralCount(_ count: inout Int, resetCount: Bool, key: String) {
if CommandLineUtilities().demoModeEnabled() {
count = 0
@@ -1482,6 +1501,10 @@ struct VersionManager {
return majorVersion
}
+ static func getMajorVersion(from version: String) -> Int {
+ return Int(version.split(separator: ".").first.map(String.init)!)!
+ }
+
static func getMinorOSVersion() -> Int {
var minorOSVersion = ProcessInfo().operatingSystemVersion.minorVersion
// if (CommandLineUtilities().simulateOSVersion() != nil) {
@@ -1506,6 +1529,12 @@ struct VersionManager {
versionGreaterThan(currentVersion: nudgePrimaryState.requiredMinimumOSVersion, newVersion: nudgePrimaryState.userRequiredMinimumOSVersion)
}
+ // Helper function to remove duplicates while preserving order
+ func removeDuplicates(from array: [String]) -> [String] {
+ var seen = Set()
+ return array.filter { seen.insert($0).inserted }
+ }
+
// Adapted from https://stackoverflow.com/a/25453654
static func versionEqual(currentVersion: String, newVersion: String) -> Bool {
return currentVersion.compare(newVersion, options: .numeric) == .orderedSame
diff --git a/Schema/jamf/com.github.macadmins.Nudge.json b/Schema/jamf/com.github.macadmins.Nudge.json
index 00da3811..a5c16b5d 100644
--- a/Schema/jamf/com.github.macadmins.Nudge.json
+++ b/Schema/jamf/com.github.macadmins.Nudge.json
@@ -508,6 +508,25 @@
}
]
},
+ "minorVersionRecalculationThreshold": {
+ "description": "The amount of minor versions a device can be behind before the requiredInstallationDate is recalculated against a previous update. (Note: This key is only used with Nudge v2.0.5 and higher)",
+ "anyOf": [
+ {
+ "title": "Not Configured",
+ "type": "null"
+ },
+ {
+ "title": "Configured",
+ "default": 0,
+ "type": "integer",
+ "options": {
+ "inputAttributes": {
+ "placeholder": "0"
+ }
+ }
+ }
+ ]
+ },
"nonActivelyExploitedCVEsMajorUpgradeSLA": {
"description": "When a major upgrade is not under active exploit but contains CVEs, this is the amount of days a user has to install the update. (Note: This key is only used with Nudge v2.0 and higher)",
"anyOf": [