PHP RCE, Python CA distrust and a Postgres disk trap
Hosted by Mat Caniglia, in an AI clone of his own voice.
The video did not load. Reload the page.
A patch-first week: the critical PHP RCE fix, Python dropping trust in compromised eMudhra keys, source-only Python builds, a Postgres disk setting, Node 22 and Terraform 1.16.7 point releases, and where agent tooling lock-in really sits.
Transcript
I host this episode. The voice is an AI clone of my own. The text below is the episode script as it was spoken.
AI drafted this recap. Mat edited and approved it.
In late October twenty twenty-six, Python shipped expedited security releases on five branches at once. Only two of those come with binary installers. The other three are source-only security releases. So here's the question. If your fleet runs one of those three, who on your team builds it from source, and do you even know which machines need it? The Platform Radar. I'm Mat Caniglia. The Platform Radar is produced by Clouditive, nearshore platform engineering for US teams. Good to have you along. The voice you're hearing is an AI clone of my own, used with my permission.
Seven items in this episode. A critical PHP remote code execution advisory that says update now, Python no longer trusting eMudhra keys, the other fixes on tarfile and cookies, Postgres crash leftovers on disk, a Node twenty-two Jod LTS point release, the Terraform one point sixteen point seven patch, and how models get swapped while harnesses stay. Then we close with the takeaways. One idea sets the order. Most of this work is patching, and the urgent part is knowing exactly which runtime versions you actually run.
By the end you'll have a ranked patch list and one Postgres setting worth checking. But all of it hangs on a single thing. Could you name, right now, every runtime version you have in production, and which of them you'd have to build yourself?
We just covered the one question that sets the order for this whole recap, which runtime versions you actually run. That brings us straight to PHP, because its advisory is the first place that inventory gets tested. On October twenty-first, twenty twenty-six, the PHP project posted a security advisory with a blunt title. Critical remote code execution vulnerabilities, update now. The advisory says PHP eight point five point twelve, eight point four point twenty-seven, eight point three point thirty-six and eight point two point thirty-five fix two critical vulnerabilities that can allow unauthenticated remote code execution.
It names all PHP installations serving web requests as at risk, PHP-FPM included. And PHP eight point one and older no longer receive updates from the PHP project. The practical read starts with a list. Write down every PHP runtime you run, including the copies inside container base images and on managed hosting, then compare each version with the advisory. Base images are the easy ones to miss. Rebuilding an app on an old image ships the old PHP again, so check the version inside the built image itself.
Here's a made-up example to make it concrete. A team patches its main web fleet in an afternoon, then finds a forgotten admin panel on an older host that nobody listed. The list exists to catch that panel before someone else does. For order, use a simple rule. Remote code execution plus the words update now puts this ahead of routine work. Any PHP that faces the internet goes first in line, and if it collides with a planned release, the patch wins. Anything on eight point one or older needs its own call, a vendor backport or an upgrade, so give that call an owner and a date.
Keep that list open. Next comes Python, with expedited releases on five branches and a certificate authority block, and the same inventory decides how fast you can move.
We just covered the PHP remote code execution advisory and the version inventory it forces on you, and the same question shows up in Python, which is why it comes next. The thread hasn't changed. You can only patch the runtimes you know you run. Python's release team shipped expedited security releases on five branches at once. They are three point fifteen point two, three point fourteen point ten, three point thirteen point eighteen, three point twelve point sixteen and three point eleven point eighteen.
The reason is a certificate problem. The releases address the compromised eMudhra intermediate certificate authority keys, tracked as CVE twenty twenty-six, one one five five five seven, and the listed fix is to distrust those keys. In practice, the S S L module in Python now blocks those keys, and the release post describes that as a last resort against their use. The post also says that normal pip use, including upgrading pip itself, is now safe from attacks based on those compromised keys, even if pip hasn't been updated yet.
For a platform team, that makes the order of operations friendlier. You can move the interpreter first and let the package tooling catch up on its normal schedule, instead of chasing pip upgrades across every image before anything else moves. Here's the catch. Only three point fifteen point two and three point fourteen point ten come with binary installers. Three point thirteen point eighteen, three point twelve point sixteen and three point eleven point eighteen are source-only security releases.
The practical read: if a service runs on three point eleven, three point twelve or three point thirteen, you have two paths. You build Python from source yourself, or you wait for your distro or your base image maintainer to rebuild it and ship it. Those two paths run on different clocks. Building it yourself puts the timing in your hands, and it also hands you a custom interpreter build to maintain for as long as that service lives. Waiting costs less effort, but the timing belongs to someone else.
So make the check per service. For each one, write down which Python branch it runs and who rebuilds that interpreter when a fix lands. If you can't put a name on the second answer, that service goes to the top of the list. A team can use this moment to tighten the inventory itself. If an image tag only says Python three, the tag can't tell you the branch, and you'll hit the same blind spot with the next advisory. Pinning the full version in the tag costs nothing and answers that question on sight.
The second check is about the block itself. Treat any connection that chained to those intermediates as something that may stop verifying after the upgrade. Here's a made-up example: an internal job that calls a partner's endpoint, where the partner's certificate happens to sit under one of those keys. So after each upgrade, search your logs for new TLS verification errors and compare them with the days before. A fresh error right after the rollout most likely points at the other side's certificate chain. Resist the urge to switch verification off to make it go quiet.
If you have to sequence the work, start with the services on three point fourteen and three point fifteen, where the binary installers make the upgrade quick, and give the source-only branches their own owner and their own date. The release team's own wording is plain: upgrading is highly recommended to all users of affected versions. The certificate block is the headline, and it's worth asking what else rides along in the same five releases. Next, what those releases mean for code that handles archives, cookies or XML.
We just covered the eMudhra keys, which Python's ssl module now blocks as a last resort against their use. The same release post lists more security content in all five releases, and that is why archives and cookies come next. The thread is the one running through this whole episode. A fix only protects the runtimes you know you run, so keep that inventory in mind as we go down the list. Start with tarfile. In all five releases, the tar filter now rejects links replacing the destination, and that fix carries its own CVE. Then cookies. The http cookie jar module now rejects cookies with a negative version, again in every one of the five releases.
The practical read on tarfile is about where the fix lives. It ships with the interpreter, so code that extracts untrusted archives with the tar filter gets it only after the runtime itself is upgraded. A dependency bump in your lockfile won't carry it. For cookies, a team can ask a narrower question. Does any service parse cookies from systems it doesn't control with the standard library jar? If yes, that service joins the same upgrade list. Sessions and outbound clients that keep cookies between calls are good places to look first.
The three older branches get more. Three point thirteen point eighteen, three point twelve point sixteen and three point eleven point eighteen carry fixes that were already included in three point fifteen point one and three point fourteen point nine. The XML piece is the bundled Expat library, updated to version two point nine point zero and listed against two separate CVEs. On Windows, the mimetypes known files list is no longer read by default, tracked as a CVE from twenty twenty-four. There is also a fix for an additional header refolding and quoting edge case.
For XML, check which services parse documents from outside your walls, and whether they run on one of those three older branches. A made-up example: a service that reads partner invoices as XML would be the first one to confirm. And three point thirteen point eighteen and three point twelve point sixteen add one more, a fix for a race condition in the cleanup of tempfile's TemporaryDirectory. For the tempfile fix, ask whether any of those services create and clean up temporary directories while other work runs at the same time. If they do, that is one more reason to move them up the list.
So make a short list. Find every place your code opens a tar archive it didn't create, and write down which Python version runs it. Start with whatever an upload endpoint or an artifact pipeline extracts, because those archives come from someone else. A simple decision rule helps here. If a path extracts archives from outside and runs on a runtime you can't name, upgrade that runtime first and investigate the rest afterward. Treat the older branches as their own line on the inventory, since they pick up more fixes at once. Ask whoever builds your base images which patch level each branch is on, and ask for the actual version string as the answer.
One trade-off to name out loud. A runtime bump on an older branch means rebuilding and redeploying images, so put every path on the list into one rebuild rather than chasing each fix separately. Temporary files are also where the next chapter goes. Postgres has a setting called remove temp files after crash, and the questions are what it protects against and how you would know it's off.
We just covered the tarfile and cookie fixes, two small patches about how a runtime handles the files and input it's given. The same question shows up in Postgres, where a crash can leave files behind on disk, and that's why this comes next. The thread is the one running through this whole episode: you can only act on what you know you're running, and that includes how your database cleans up after itself. The setting is called remove temp files after crash. It's a simple on or off switch, and a configuration reload is enough to change it. There's no commented-out line for it in the sample config, so you won't come across it by reading your config file.
It arrived in version fourteen and hasn't changed since. The manual files it under Developer Options, a section that warns you off using any of it in production, and the author, Christophe Pettus, says that for once the warning is exactly right. The default is the production setting, and the only thing the parameter lets you do is turn the protection off.
Here's what it protects you from. When a sort or a hash outgrows its memory, it spills to temporary files on disk, and the backend that created a file is the one that deletes it. In Postgres, the crash of one backend is the crash of all of them. The postmaster sends sig quit to every other child process, and the handler exits without running any cleanup, on purpose, because shared memory may be corrupt. So the moment one backend is killed, every temporary file on the server is orphaned. That covers every session's spill files and the storage for every session's temporary tables as well. The author says he checked this on eighteen point six and on the nineteen release candidate.
Before version fourteen, those orphans stayed on disk until the next time the postmaster started. With restart after crash at its default of on, a crash didn't go through startup, so the files stayed where they were. A server that crashed under a heavy analytic load kept all of them, and a second crash doubled the pile, until something ran out of disk space. And your dashboards wouldn't warn you. The temp files and temp bytes counters in pg stat database are recorded by the owning backend when it deletes the file, so the author's point is that the orphans are invisible to the instrumentation. A backend that never got to delete a file never reports it.
The practical read: this is a setting nobody thinks about, because it's on and it doesn't appear in the config file. That's exactly why it deserves one check. A flag that defaults to the safe value stays safe only until someone copies an old tuning template, or flips it while chasing a problem and forgets to flip it back. Here's the check for a platform team. On every Postgres server you run, ask the database for the current value of remove temp files after crash and confirm it says on. If it says off, find out who changed it and why, and write that reason next to the server, the same way we said to write down who rebuilds your Python.
Next, list the servers still running a version older than fourteen. They don't have this setting at all, so for them the real question is whether they've crashed recently. If one has, and it hasn't had a full restart since, treat its temporary space as suspect until someone has looked at it. The third check covers what the counters miss. Compare the disk actually used by the pgsql temp directories with what the temp file statistics report. Here's a made-up example: the stats show a few gigabytes of temp activity over the past month, while the disk shows far more sitting there with no running session to explain it. That gap is your signal to go look.
A decision rule that keeps this simple: leave it on, and put a quick look at temp disk usage on the checklist you run after any database crash. If you ever do need it off to study one specific crash, decide before you start who turns it back on and when. When you add these checks, add them to whatever already runs your configuration audits, so every new server gets checked without anyone having to remember. It's one query per server for the setting and one disk comparison, and it catches a problem your metrics won't show you.
That's a disk question you can answer from the server itself. The next one is harder. It's a Node point release, version twenty-two point twenty-three point four, and how you decide whether it needs action when the release notes aren't in front of you.
We just covered Postgres crash leftovers on disk, where the fix started with knowing what actually sits on each server. The same inventory question shows up in a Node point release, which is why this comes next. You can only patch a runtime you know you run. In October twenty twenty-six, on the twenty-first, the Node project published version twenty-two point twenty-three point four, code name Jod, on its long-term support line. Antoine du Hamel released it. The release notes are direct about it. This is a security release. Among the notable changes are a TLS fix to reject non-canonical IP addresses and an upgrade of the OpenSSL sources.
The practical read: a security label moves a point release out of the next maintenance window and into the current one. So when the notes aren't in front of you, that label is the first thing to look for. Open the release page on GitHub and check it before you put the upgrade on anyone's calendar. If the label is missing, the upgrade can ride your normal cadence. Then treat the upgrade as one change across every place Node is pinned. That's the nvm version file developers use locally, the engines field in your package manifest, the base image in each Dockerfile, and the image your CI jobs run on. If one of them lags, your tests pass on a runtime production doesn't run.
A team can turn that into a rule. One pull request moves every pin, and a small check fails the build when two pins disagree. Version drift is the same question for your infrastructure tooling, which brings us to Terraform and whether teams pinning it should move to version one point sixteen point seven.
We just covered Node twenty-two, where the risk was version pins drifting apart, and the same question shows up in your infrastructure tooling, which is why Terraform comes next. The thread is the one running through this whole episode: you can only patch what you know you run. HashiCorp's Terraform repository released version one point sixteen point seven, with release notes dated October twenty-second, twenty twenty-six. The notes list one bug fix. The test command now avoids a panic when testing a schema with deprecated values.
There's also a note that Terraform is now built with Go one point twenty-six point ten, which no longer trusts certificates issued through a set of compromised eMudhra intermediate certificate authorities. For a platform team, that second note is the one to weigh. A change in what the binary trusts can break a connection that worked before, so ask whether any registry or backend your runs reach presents a certificate from that chain. If you can't answer that, find out before the pin moves.
So, should you move? If you pin required version or a CI image to the one point sixteen line, read the notes first. Then run a plan in non-production with the new binary, compare it with the plan from your current binary, and move the pin only when the two plans match. A clean comparison turns the upgrade into a routine change. That same habit, checking where you really depend on a vendor before you trust a claim about it, is what the next question needs: where AI lock-in actually sits, according to one survey, and how far you can trust it.
We just covered the Terraform one point sixteen point seven patch, and the habit of comparing two plans before you move a pin. The same question shows up in AI tooling, which is why it comes next. The thread is the one running through this whole episode: you can only judge a dependency you have actually inventoried, whether that is a runtime, a binary or a model provider. Here is the survey. Stacklok surveyed five hundred and twenty technical leaders about being locked into frontier labs. Every respondent runs LLMs or agents in pilot or production at a North American company with more than five hundred employees, and holds a manager-or-above title.
On the model side, only eight percent of those organizations use one model provider, and twenty-five percent use five or more. The author reads that as lock-in at the model layer appearing to be a solved problem. So what is hard to move? Fifty-three percent named integrations to internal systems or MCP servers as one of their two hardest elements to migrate. Multi-step agent workflows were right behind at fifty-two percent, and guardrails and access-control config came next.
The practical read: if those answers hold for your shop, the cost of a future switch sits in the code your own people wrote to connect agents to internal systems, and in the policies wrapped around them. Swapping the model behind that code is the cheap part. Now, how far to trust it. The author writes as a co-creator of Kubernetes, and the author bio describes the author as CEO of Stacklok, a company that builds an open architecture so enterprises own the layer around the model and can move between providers. Stack Overflow ran it with an editor's note saying the folks at Stacklok offered to share their survey results with the community.
Put plainly, the people who ran the survey have a stake in where it says lock-in lives. The numbers can still be right. Treat them as a direction to test against your own systems, and read the full question wording before you quote a percentage to your board. Also check the fit. The sample is large North American companies already running agents. If your company has two hundred people and one pilot, those percentages describe someone else's shop, and your own inventory matters more than theirs.
One test you can run yourself. List every place your agents touch an internal system, and every guardrail or access rule they depend on. Next to each one, write down whether it talks to a specific agent framework directly or goes through an interface your team owns. The decision rule: keep integrations and guardrails behind interfaces you own, so changing the agent framework later means swapping an adapter while the integration itself stays as it is. If a migration estimate depends mostly on rewriting connectors, that is the line item to shrink first.
A made-up example makes it concrete. Picture a team with a ticketing connector written straight against one framework's tool format. Wrap that connector once behind a small interface the team controls, and a later switch touches the wrapper, while the connector and its access rules stay put. The trade-off is real. An owned interface is code you maintain, and a wrapper around a framework you will never leave is cost with no return. A sensible threshold is to add the interface once a second framework or provider is actually on the roadmap, or once an integration carries access rules you could not afford to rebuild by hand.
And when a vendor pitches portability, ask two things. Which parts of my integration code move unchanged if I switch frameworks, and can I export my guardrail and access config in a format another tool can read? Models get swapped, harnesses stay, and your own inventory is what tells you which kind of dependency you hold. That idea of one number telling you where you stand is where we go next, with the single figure that sums up this stretch of work and what to watch for after it.
We just covered why models get swapped while harnesses stay, and why your own inventory tells you which dependency you hold. That brings us to the number of the week, because the same inventory question sits under every patch in this episode. The number is five. Python shipped expedited security releases on five branches at once, from three point fifteen down to three point eleven. Only two, three point fifteen point two and three point fourteen point ten, come with binary installers. The other three are source-only security releases.
The practical read: on three point eleven through three point thirteen, the fix reaches you through your own builds or your distro's packages. So ask who owns that build, and how long it takes to land after a release. If you work the list in order, the PHP remote code execution fix goes first. Then the Python CA and tarfile fixes, then the Postgres temp file check, and last the Node and Terraform pins.
On PHP, one piece is still open. A separate session upload progress issue can only occur under an unusual combination of configuration and application code, and applications that never change the serialize handler at runtime can keep the feature enabled. The PHP team will publish a full advisory for it with the next release. When that advisory lands, the check is narrow. Search your code for anything that switches the session serialize handler at runtime, and if a code search can't answer that, the gap is the finding.
On Python, the new point releases on three point eleven through three point fifteen block the compromised eMudhra intermediate keys in the ssl module, as a last resort against their use. The release post links that incident to a thread on the Python discussion forum. That thread is the one to watch. Upgrade, then watch your TLS error logs for new verification failures, and treat any older patch release still on a build box as uncovered until it moves. On Postgres, the author of the temp file piece checked the orphan behavior on eighteen point six and on the nineteen release candidate.
When a final nineteen ships, rerun that crash check in your own upgrade test. Kill a backend on a staging copy and compare free disk before and after the restart. Look back across the episode. The PHP critical RCE and Python's distrust of the eMudhra keys share one first question with the Postgres crash leftovers on disk: what version and what build do you actually run? The harness question from the last chapter is that same inventory, one layer up. So, three ideas to keep. First, find out what runtimes you actually run before you patch, and who builds them. Second, move to the new Python point releases, and watch for TLS verification failures afterwards.
Third, the survey from the last chapter said lock-in at the model layer appears to be a solved problem. Fifty-three percent named integrations to internal systems or MCP servers among their two hardest elements to migrate, and multi-step agent workflows were right behind at fifty-two percent. So keep those layers behind interfaces you own, and a model swap stays a smaller job. In the next episode we'll pick up both open threads: what the full PHP advisory for the session upload issue says once it's out, and whether the Python CA block caused TLS breakage in practice.
If you want that one when it lands, follow the show at theplatformradar dot com. Thanks for spending the drive or the workout with me. See you Tuesday.