Every main story this week came down to the gap between what we think ran and what actually ran.
A workflow you think only labels pull requests. An update you think only makes you safer. A monitoring script you think is the vendor's code. A GitLab upgrade you think fixed the bug. In each case, the thing you'd point to in a review was true. It just wasn't the whole story.
Start with GitHub.
Pull request target has been a known sharp edge for years. It runs in the context of the base repository, with that repo's secrets, which is exactly what you want for a bot that labels pull requests or posts a comment. The trouble starts when a workflow on that trigger checks out the fork's code and runs it. Now a stranger's code is running with your secrets, and nothing in the YAML looks dangerous unless you know what to look for.
GitHub's answer is to turn it off by default in public repos. Enforcement was supposed to start November 2nd, and it's moved to December 2nd. Before that, there are two brownouts: Monday, November 9th and Monday, November 16th, from 4 to 6 PM UTC, which is 11 AM to 1 PM Eastern. During those windows, blocked runs fail with an error annotation and the workflow doesn't execute. Afterward, the rule goes back to evaluate mode, where workflows run with a warning.
I like brownouts. They're the honest way to change a default. A warning in a log is easy to ignore. A failed run at 11 AM on a Monday gets read.
So do the boring part before then. Open Insights and see which runs the default rule would block. If you don't need the trigger, let the rule block it. If you do, allow it explicitly in an Actions event policy, and use workflow file targeting so only the workflows that need it get it. It's public repos only. If you maintain an open source project with a bot that labels or comments on pull requests, Monday is when you find out which trigger it really uses.
WordPress is the story that bothers me most, because it punishes the right habit.
When WordPress does a core update with a complete package, it unpacks the new files into a predictable folder under wp-content/upgrade, and that copy includes the installer. On a lot of servers, that folder is reachable from the web. So for a moment, a fresh installer is sitting at a URL anyone can request, and an unauthenticated attacker who gets there inside that window can work their way to running PHP on your server.
In lab testing the window lasts seconds. Attackers don't wait for it; they poll for it. Wordfence blocked almost 63,000 attacks on this in a single day, and CISA added it to the Known Exploited Vulnerabilities catalog on Tuesday as CVE-2026-119977. WordPress shipped 7.1.4 on October 28th and backported the fix to every branch back to 4.7.
The uncomfortable part: keeping WordPress current is exactly what opens the window. Nobody did anything wrong. The update process itself was the exposure.
So update to 7.1.4 or the patched release on your branch, and confirm it landed on the sites you assume are auto-updating. "Auto-updates are on" is a belief, not evidence. Check your logs for requests to PHP files under wp-content/upgrade, look for admin accounts, plugins or PHP files you don't recognize, and if you can, stop the web server from serving anything out of that folder at all. That last one is my suggestion, not something from the advisory, but I can't think of a reason a browser ever needs a file from there.
Then DebugBear and Polymarket.
Polymarket says that on October 29th, a third-party performance-monitoring script on polymarket.com was replaced with one built to target its users. For people who had the site open during a short window that afternoon, it used their session to sell positions and move funds out. Outside estimates put it at about 2.7 million dollars from around 700 wallets, and Polymarket says it's refunding people. The vendor was DebugBear, and DebugBear says its DNS records were compromised through its domain registrar, in an attack aimed at one specific customer.
DebugBear's code didn't have to change at all. Polymarket's pages loaded a script by hostname. Whoever controls the DNS for that hostname decides what comes back, and that code runs inside your page, with your user's session. HTTPS doesn't save you either, because whoever controls a domain's DNS can usually get a valid certificate for it. A content security policy that allows the vendor's domain still allows it.
Subresource integrity does help. The browser checks the script against a hash and won't run it if the bytes changed, even at the same URL. The trade-off is that the vendor's legitimate updates break too, until you update the hash, which is why teams pin a fixed version. Polymarket's fix includes explicit version pinning, along with a stronger content security policy and sandboxing.
Two things I'd actually do this week. First, list every third-party script on pages where your users are signed in, and ask whether each one needs to be there. A page-speed script on a page that can move money is a strange trade. Second, go look at your own registrar account. Registrar lock, strong MFA, alerts on DNS changes. It's often the least watched account you have, and it can redirect everything you run.
Alpine's GitLab story is the one that changes advice we gave.
In episode 72 we covered CVE-2026-97229, the CVSS 10 GitLab flaw, and told self-hosters to upgrade to 19.4.2, 19.3.5 or 19.2.8. That's still right if you run GitLab's own packages or images. It's not enough if you don't.
The bug is in Onigmo, the regular expression engine built into Ruby. GitLab's fix is a set of patches to the Ruby it bundles. Alpine runs GitLab from its own image on standard Ruby, so when they upgraded on October 22nd, the version number said fixed and the image didn't have the fix. On the 26th they found they were still vulnerable, took it offline and rebuilt it.
If you run GitLab from source or from an image you build yourself, you also need Ruby 3.3.13, 3.4.12, 4.0.9 or later. And since this is a Ruby bug, anything else of yours that compiles regular expressions from untrusted input needs the new Ruby too.
A version number tells you what you installed. It doesn't always tell you what's inside it.
The lightning round had more of the same: an OpenSSH fix that doesn't restart sshd for you, an APT fix that only helps once it's in the images you build, and .NET 8 and 9 going out of support on Tuesday, after which the next bug like last week's Brotli one just stays open.
The human closer is where this lands for me.
Alpine wrote one sentence I'd like more postmortems to have the nerve to write: "We have found no evidence that it succeeded, but we cannot rule it out."
That's honest, and it's harder than it sounds. They had crash logs. They knew the attacker got partway through the exploit four times. What they couldn't do was prove a quieter attempt didn't work, because a successful exploit would leave no crash and no trace in the web logs, some logs from the day were already gone, and nobody was logging outbound connections. So they rebuilt from scratch and checked their releases against records the attacker couldn't have touched: a developer's own clone, and all 45 of their build servers.
Look at what they're changing afterward. Log outbound connections. Keep logs longer than 14 days. Sign release tags, so anyone can verify a release without trusting GitLab. Almost all of it is evidence.
Now run this week through that. If the WordPress race hit one of your sites in those few seconds, would your logs show it? If a third-party script on your login page changed for an hour on a Thursday afternoon, would anyone know? If someone changed your DNS at the registrar, how long until you noticed?
None of those questions are about stopping the attack. They're about whether you could say, with a straight face, that it didn't happen. Even CISA's WordPress entry asks for forensic triage, not just a patch.
"No evidence" is only as good as the logs you kept.
This week on Ship It Weekly, GitHub will block pull_request_target in public repos by default from December 2, with brownouts on Mondays, November 9 and 16, 11 AM to 1 PM ET. A WordPress core update race (CVE-2026-119977) is on CISA's exploited list: during a full-package update, a fresh installer can sit for a moment at a public URL under wp-content/upgrade. A hijacked DebugBear script drained about $2.7 million from Polymarket users after DebugBear's DNS was compromised through its registrar. And Alpine Linux explains why upgrading GitLab didn't fix CVE-2026-97229 on its own image: the fix was a patch to Ruby.
The thread: what actually ran, as opposed to what you think ran. A trigger that runs a fork's code with your secrets, an update that opens its own window, a script served by whoever controls the DNS, and a version number that said "fixed".
In the lightning round: OpenSSH 10.6p2 fixes a password login bypass on musl builds with PAM off, which is how Alpine builds openssh-server; Debian and Ubuntu patch an APT package swap (CVE-2026-109518); and .NET 8 and 9 reach end of support on Tuesday, November 10.
The human closer comes back to Alpine's "we cannot rule it out." If an attack hit you this week, could your logs prove it didn't work?
Highlights
pull_request_target: what it runs, and what to check before Monday
WordPress: when keeping current is what opens the window
DNS, HTTPS and subresource integrity for third-party scripts
Registrar lock, MFA and alerts on DNS changes
Alpine: GitLab from your own image needs a fixed Ruby too
"No evidence" is only as good as the logs you kept
25 years in production: DevOps, SRE, platform, and cloud. Host of Kube Signals on KubeFM. Grafana Champion; Confluent Community Catalyst; DevOps Institute & ITIL Ambassador.
Every main story this week came down to the gap between what we think ran and what actually ran.
A workflow you think only labels pull requests. An update you think only makes you safer. A monitoring script you think is the vendor's code. A GitLab upgrade you think fixed the bug. In each case, the thing you'd point to in a review was true. It just wasn't the whole story.
Start with GitHub.
Pull request target has been a known sharp edge for years. It runs in the context of the base repository, with that repo's secrets, which is exactly what you want for a bot that labels pull requests or posts a comment. The trouble starts when a workflow on that trigger checks out the fork's code and runs it. Now a stranger's code is running with your secrets, and nothing in the YAML looks dangerous unless you know what to look for.
GitHub's answer is to turn it off by default in public repos. Enforcement was supposed to start November 2nd, and it's moved to December 2nd. Before that, there are two brownouts: Monday, November 9th and Monday, November 16th, from 4 to 6 PM UTC, which is 11 AM to 1 PM Eastern. During those windows, blocked runs fail with an error annotation and the workflow doesn't execute. Afterward, the rule goes back to evaluate mode, where workflows run with a warning.
I like brownouts. They're the honest way to change a default. A warning in a log is easy to ignore. A failed run at 11 AM on a Monday gets read.
So do the boring part before then. Open Insights and see which runs the default rule would block. If you don't need the trigger, let the rule block it. If you do, allow it explicitly in an Actions event policy, and use workflow file targeting so only the workflows that need it get it. It's public repos only. If you maintain an open source project with a bot that labels or comments on pull requests, Monday is when you find out which trigger it really uses.
WordPress is the story that bothers me most, because it punishes the right habit.
When WordPress does a core update with a complete package, it unpacks the new files into a predictable folder under wp-content/upgrade, and that copy includes the installer. On a lot of servers, that folder is reachable from the web. So for a moment, a fresh installer is sitting at a URL anyone can request, and an unauthenticated attacker who gets there inside that window can work their way to running PHP on your server.
In lab testing the window lasts seconds. Attackers don't wait for it; they poll for it. Wordfence blocked almost 63,000 attacks on this in a single day, and CISA added it to the Known Exploited Vulnerabilities catalog on Tuesday as CVE-2026-119977. WordPress shipped 7.1.4 on October 28th and backported the fix to every branch back to 4.7.
The uncomfortable part: keeping WordPress current is exactly what opens the window. Nobody did anything wrong. The update process itself was the exposure.
So update to 7.1.4 or the patched release on your branch, and confirm it landed on the sites you assume are auto-updating. "Auto-updates are on" is a belief, not evidence. Check your logs for requests to PHP files under wp-content/upgrade, look for admin accounts, plugins or PHP files you don't recognize, and if you can, stop the web server from serving anything out of that folder at all. That last one is my suggestion, not something from the advisory, but I can't think of a reason a browser ever needs a file from there.
Then DebugBear and Polymarket.
Polymarket says that on October 29th, a third-party performance-monitoring script on polymarket.com was replaced with one built to target its users. For people who had the site open during a short window that afternoon, it used their session to sell positions and move funds out. Outside estimates put it at about 2.7 million dollars from around 700 wallets, and Polymarket says it's refunding people. The vendor was DebugBear, and DebugBear says its DNS records were compromised through its domain registrar, in an attack aimed at one specific customer.
DebugBear's code didn't have to change at all. Polymarket's pages loaded a script by hostname. Whoever controls the DNS for that hostname decides what comes back, and that code runs inside your page, with your user's session. HTTPS doesn't save you either, because whoever controls a domain's DNS can usually get a valid certificate for it. A content security policy that allows the vendor's domain still allows it.
Subresource integrity does help. The browser checks the script against a hash and won't run it if the bytes changed, even at the same URL. The trade-off is that the vendor's legitimate updates break too, until you update the hash, which is why teams pin a fixed version. Polymarket's fix includes explicit version pinning, along with a stronger content security policy and sandboxing.
Two things I'd actually do this week. First, list every third-party script on pages where your users are signed in, and ask whether each one needs to be there. A page-speed script on a page that can move money is a strange trade. Second, go look at your own registrar account. Registrar lock, strong MFA, alerts on DNS changes. It's often the least watched account you have, and it can redirect everything you run.
Alpine's GitLab story is the one that changes advice we gave.
In episode 72 we covered CVE-2026-97229, the CVSS 10 GitLab flaw, and told self-hosters to upgrade to 19.4.2, 19.3.5 or 19.2.8. That's still right if you run GitLab's own packages or images. It's not enough if you don't.
The bug is in Onigmo, the regular expression engine built into Ruby. GitLab's fix is a set of patches to the Ruby it bundles. Alpine runs GitLab from its own image on standard Ruby, so when they upgraded on October 22nd, the version number said fixed and the image didn't have the fix. On the 26th they found they were still vulnerable, took it offline and rebuilt it.
If you run GitLab from source or from an image you build yourself, you also need Ruby 3.3.13, 3.4.12, 4.0.9 or later. And since this is a Ruby bug, anything else of yours that compiles regular expressions from untrusted input needs the new Ruby too.
A version number tells you what you installed. It doesn't always tell you what's inside it.
The lightning round had more of the same: an OpenSSH fix that doesn't restart sshd for you, an APT fix that only helps once it's in the images you build, and .NET 8 and 9 going out of support on Tuesday, after which the next bug like last week's Brotli one just stays open.
The human closer is where this lands for me.
Alpine wrote one sentence I'd like more postmortems to have the nerve to write: "We have found no evidence that it succeeded, but we cannot rule it out."
That's honest, and it's harder than it sounds. They had crash logs. They knew the attacker got partway through the exploit four times. What they couldn't do was prove a quieter attempt didn't work, because a successful exploit would leave no crash and no trace in the web logs, some logs from the day were already gone, and nobody was logging outbound connections. So they rebuilt from scratch and checked their releases against records the attacker couldn't have touched: a developer's own clone, and all 45 of their build servers.
Look at what they're changing afterward. Log outbound connections. Keep logs longer than 14 days. Sign release tags, so anyone can verify a release without trusting GitLab. Almost all of it is evidence.
Now run this week through that. If the WordPress race hit one of your sites in those few seconds, would your logs show it? If a third-party script on your login page changed for an hour on a Thursday afternoon, would anyone know? If someone changed your DNS at the registrar, how long until you noticed?
None of those questions are about stopping the attack. They're about whether you could say, with a straight face, that it didn't happen. Even CISA's WordPress entry asks for forensic triage, not just a patch.
"No evidence" is only as good as the logs you kept.
Additional Links
CISA adds CVE-2026-119977 to the Known Exploited Vulnerabilities catalog:
https://www.cisa.gov/news-events/alerts/2026/11/03/cisa-adds-one-known-exploited-vulnerability-catalog