There Is No Train 27
Twenty-four of the entries in CISA’s catalogue of known exploited vulnerabilities are macOS bugs. My Mac is not affected by any of them.
That reading is correct, it comes out of code with no defect in it, and it is worth nothing.
There is no train 27
Apple ships security fixes on trains: a major version, and a series of releases under it that each close a named list of CVEs. The new check reads all of them. This week it read 126 published releases across five trains, and the newest thing Apple has published any security content for is macOS Tahoe 26.6.2.
The Mac is running 27.0.
It is a seed build from the beta programme, and the point is not that the number is higher. It is that 27 is not in the feed at all. Apple has published no security content for that major version, because that major version is not out.
Everything downstream rests on that one call, so it is corroborated three ways. The train is absent from the advisory feed. The build string carries the shape Apple gives seeds, a trailing lowercase letter on a five-thousand-series sequence. The software update catalogue this machine is subscribed to has the word seed in its name.
The comparison was right
Nothing here is broken, which is the part worth reading closely.
For each exploited CVE, the correlator asks whether the installed version falls inside any published vulnerable range for macOS, and it returns one of three answers under a deliberately strict rule:
confirmed, only on a positive in-range hit;not-affected, only when every macOS range is a definite miss;unverifiedfor everything else, meaning no macOS range at all, or a bound that will not compare.
That is a careful function. It refuses to guess in either direction, and it will not hand out not-affected because a range was ambiguous. Only because every range definitely excluded you.
Now run 27.0 through it. Every published vulnerable range for macOS ends below it. Every one is a definite miss. So the strict branch fires, twenty-four times, and each time it produces a positive assertion: not affected.
And each one is true. 27.0 really is outside all of those ranges. It is also outside the data, and an ordering comparison cannot tell those two apart. Being newer than every known bad version and being absent from the catalogue are the same arithmetic.
None of that belongs to Apple, or to security. Any check shaped like is my version inside the vulnerable range returns clean for a version above every range it knows: a nightly, an unreleased fork, a vendored patch, a branch nobody has filed against. The further ahead of the index you get, the more confidently the tool reassures you, in the same word it uses when it has genuinely checked.
The blindness detector was listening for silence
There is already a component here whose whole job is to prevent this. Its comment says so:
A pillar reporting nothing is only good news when it COULD have reported something.
It works by naming, for each check, a count that cannot legitimately be zero on a live host, and complaining when that count comes back zero anyway. Zero running executables is impossible, because the checker is itself a process with an executable. Zero persistence entries is impossible on a machine that has services. A zero there is a collector that read nothing, not a clean machine.
It could not have caught this one. This check did not report nothing. It reported twenty-four times, every verdict correct. I have written before about a number that was clean because a filter ran before the question; there the suspicious figure was zero. Here it is twenty-four. A detector that watches for silence is looking for the wrong symptom, because this failure is fluent.
A day spent adding ways to decline
Almost none of the work that followed was arithmetic. It was vocabulary.
The verdict for a host’s patch state used to have room for two useful answers: current, or behind by some list of releases. It now has six, and four of them are refusals: current, behind, prerelease, unpublished, end-of-life, unknown.
Underneath those, more of the same:
- a release whose version string will not compare is counted
indeterminateand never silently dropped; currentis withheld and downgraded tounknownif even one release in the gap was indeterminate, because a gap you could not fully read is not a gap you can certify empty;- XProtect, Apple’s malware-definition layer, sits at framework 159 against a published 157. Installed is ahead of published, because a seed legitimately runs ahead, so the number is carried as evidence and never graded at all;
- the property list grading would read for one of its components does not exist on this build, so it reads
unreadable, never stale; - one automatic-update switch is simply absent from the preference file. It reads
unknown. Neverfalse.
That last field is the whole thing in miniature. Absent from a preference file and set to off are different facts about the world. Flatten them one way and the tool reports a problem you do not have; flatten them the other way and it reports a safety you do not have. The only correct output is a third value, and a boolean does not have one.
The alarm that cannot fire here
The loudest sentence this check can produce is that the Mac is missing a release which fixes something being exploited right now. On this machine it can never produce it. Not because the machine is safe, but because the machine is unrankable, and that sentence needs a rung below the one it is standing on.
So it was proven the only way left. A scratch directory, a hand-built index carrying an invented train 27.1 with an actively-exploited CVE inside it, and a run pointed at that world instead of this one. The finding fired at critical, named the CVE, printed its advisory link. That transcript is the only evidence the loudest path in the system works, and it is a description of a universe that does not exist.
The list somebody has to remember
The same afternoon, one directory over, the same failure turned up again with different mechanics.
There is a freshness check across the intel feeds, so a cache that quietly stops refreshing gets called stale instead of read as calm. It runs off a hand-written array: feed name, file, how old is too old.
A new advisory source had shipped the day before, covering third-party pipeline actions. Nobody added it to the array. For that day it was the one feed in the system that could freeze indefinitely with no one told, and it would have read clean the entire time it was frozen.
Both defects have the same author and the same week. A verdict enum and a freshness array are both hand-maintained inventories of what a tool is prepared to worry about, and anything new is born outside them. Coverage is not a property a checker has. It is a list somebody keeps, and it is always shorter than the system it describes.
The proposal is one line
And it is a strange thing for a security tool to ask for: leave the seed programme. Not patch something, not tighten a setting. Stop running the newest available version, so that enough has been published about yours for a question about it to have an answer.
It will not do it itself. It never installs an update, never writes a preference key, never runs a privileged command; every remedy it finds is a gated proposal with my name on the other end. So the patch state of that machine stays unmeasured for exactly as long as I enjoy being early, and the only thing that changed this week is that it now says so out loud, in a word it did not have yesterday.