SilentKnight version 2.10 works better with XProtect updates

Imagine my surprise earlier this week when I went to ensure my MacBook Pro had successfully installed new versions of XProtect and XProtect Remediator. SilentKnight informed me that XProtect was still at version 5270, and there was no update available to take it to 5271. I then manually checked the version of XProtect.bundle in CoreServices, and that confirmed it hadn’t been updated, but SilentKnight still couldn’t find any update, despite XProtect Remediator updating as normal.

My next visit was to the list of Installations in System Information, where it informed me that XProtect had been updated to 5271, thanks to XProtectCloudKitUpdate, instead of the usual XProtectPlistConfigData installation downloaded through softwareupdate. After a little jiggling with the new xprotect command tool I realised what had happened, and why SilentKnight was awry.

After an unfortunate delay, described later, I’m at last able to offer a new version of SilentKnight that won’t make this same mistake when run in recent beta-releases of Sequoia. Version 2.10 now detects whether it’s running on Sequoia; if it is, instead of inspecting the version of the XProtect.bundle in CoreServices, it now uses the official method of
xprotect version
instead.

Sadly, things get more difficult if XProtect is out of date. SilentKnight can’t use its normal call for update installation to softwareupdate, as that doesn’t handle XProtect updates any more in Sequoia. The official method is first to check whether an update is available using
xprotect check
then perform the update with
xprotect update
Although that might seem simple enough for SilentKnight to handle, both of those calls need to be made with elevated privileges. In Terminal, you’d just preface them with sudo, but apps can’t do that, and should normally use a privileged helper app, running with root privileges, something none of my apps do yet.

There’s another problem for SilentKnight in that it now needs to use two separate methods of checking for updates, and that in turn requires its code to be completely rewritten, a task I’m deferring to a whole new version, SilentKnight 3, which will only run on Sequoia. Until that’s available, bear with me and use the xprotect commands above as you need (remembering to sudo them); at least SilentKnight will now indicate when that might be necessary.

SilentKnight version 2.10 is available from here: silentknight210
from Downloads above, from its Product Page, and through its auto-update mechanism. Apart from fixing any remaining bugs, I intend this to be the final release of SilentKnight 2 with support up to and including macOS Sonoma, when updating remained so simple.

You can see the effect during one of my tests in a freshly made Sequoia VM.

At first, there’s apparently no XProtect installed at all, and the command tool returns a version of 0. Although there are updates offered for MRT (really?), Gatekeeper Compatibility Data and XProtectPayloads (XProtect Remediator), none is offered for XProtect.

I then manually updated XProtect using the command tool, and ran SilentKnight 2.10 again.

Immediately afterwards, XProtect is at the current version number of 5271 even though there’s no XProtect.bundle in CoreServices, and there’s no record of that update in the list of latest updates. That’s listed in the new version of SystHist, though, giving its mysterious XProtectCloudKitUpdate origin.

This update has been released a day later than I intended, because of a disorientating bug that caused the app to crash early whenever it tried to start up, complaining of an unreachable file path when it was starting to run its main code. I suspected a problem in the structure of the app and chased many red herrings before I realised this was the result of overenthusiastic code.

I have put the call to the xprotect command tool into conditional code, with an if to check whether it was running on Sequoia or earlier macOS. Apparently the path to the command was being checked before the instructions discovered whether that code would be run. As the path to the command tool doesn’t exist on earlier macOS, when it was being checked before determining which macOS was running, that was failing in Sonoma, as expected. Once I had worked out where this was occurring, without any clue from the errors, I had to change the type of conditional test used so the code didn’t check a non-existent path on older macOS.