<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: CodeWithRomi</title>
    <description>The latest articles on DEV Community by CodeWithRomi (@theeromi).</description>
    <link>https://dev.to/theeromi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4169897%2F5cbdce92-1f76-4cb9-90de-fcc47aa5b5a6.jpg</url>
      <title>DEV Community: CodeWithRomi</title>
      <link>https://dev.to/theeromi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/theeromi"/>
    <language>en</language>
    <item>
      <title>Patch These Four Linux Kernel Local Root Vulns Now: DirtyAH6, PPPoEject, TUNderflow, and DiagSpill</title>
      <dc:creator>CodeWithRomi</dc:creator>
      <pubDate>Wed, 07 Oct 2026 23:00:49 +0000</pubDate>
      <link>https://dev.to/theeromi/patch-these-four-linux-kernel-local-root-vulns-now-dirtyah6-pppoeject-tunderflow-and-diagspill-4730</link>
      <guid>https://dev.to/theeromi/patch-these-four-linux-kernel-local-root-vulns-now-dirtyah6-pppoeject-tunderflow-and-diagspill-4730</guid>
      <description>&lt;p&gt;Four Linux kernel vulnerabilities went public on 18 September 2026, and they are the kind you should not sit on. DirtyAH6 (CVE-2026-80844), PPPoEject (CVE-2026-68121), TUNderflow (CVE-2026-81000) and DiagSpill (CVE-2026-74469) are all local privilege escalation bugs, meaning an attacker or malicious process with a foothold on your machine can escalate to root. Working proof of concept exploits for all four are public. If you are running Proxmox, a Raspberry Pi, or any self-hosted Linux node, this is a patching weekend.&lt;/p&gt;

&lt;p&gt;Security researcher Asim Manizada reported the four bugs in mid July 2026 and published a technical write-up plus working exploits on 18 September 2026, after a coordinated hold so distributions could ship fixes first. The &lt;a href="https://seclists.org/oss-sec/2026/q3/822" rel="noopener noreferrer"&gt;oss-security post&lt;/a&gt; and the accompanying &lt;a href="https://heyitsas.im/posts/lpe-quartet/" rel="noopener noreferrer"&gt;write-up&lt;/a&gt; carry the per-CVE detail. Upstream, the earliest stable releases that contain all four fixes are 5.10.270, 5.15.221, 6.1.188, 6.6.157, 6.12.109, 6.18.50 and 7.2.4. Note that the 7.0 series reached end of life on 27 June 2026, so a 7.0 kernel only carries these fixes if your vendor backported them.&lt;/p&gt;

&lt;h2&gt;
  
  
  💡 Why This Matters for Homelabbers
&lt;/h2&gt;

&lt;p&gt;Local root exploits are often dismissed as "low risk" because they require existing access. That framing misses the threat model for homelabs and self-hosted infrastructure. If any service you run gets compromised, a web app, an LXC container with a shared kernel, or even a rogue package, a local root bug turns that into full node ownership.&lt;/p&gt;

&lt;p&gt;Proxmox LXC containers share the host kernel. A container breakout combined with a local priv-esc is a realistic attack chain. Raspberry Pi nodes running exposed services are in the same boat. Patch the kernel, then verify.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧰 Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;SSH or console access to each node&lt;/li&gt;
&lt;li&gt;sudo or root privileges&lt;/li&gt;
&lt;li&gt;A maintenance window, since patching requires a reboot&lt;/li&gt;
&lt;li&gt;Proxmox nodes: access to the Proxmox web UI or CLI for VM/container shutdown sequencing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔧 Step-by-Step: Patching Your Nodes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Check your current kernel version on every node so you know what you are starting from.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note this output. You will compare it after the update to confirm the new kernel is running.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Update your package index and apply all available security updates. On Debian and Ubuntu based systems, including Proxmox and Raspberry Pi OS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt full-upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Proxmox specifically, you also want to pull from the Proxmox repositories. If you are on the no-subscription repo, this is already covered by the command above. Run &lt;code&gt;pveversion -v&lt;/code&gt; afterward to confirm the Proxmox packages are current too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; If you are running Raspberry Pi OS, check the kernel package by its current name. On Bullseye and earlier the kernel shipped in the &lt;code&gt;raspberrypi-kernel&lt;/code&gt; package, but Raspberry Pi OS moved to Debian style &lt;code&gt;linux-image-rpi-*&lt;/code&gt; packages with Bookworm, so on a current install you want &lt;code&gt;linux-image-rpi-v8&lt;/code&gt; or &lt;code&gt;linux-image-rpi-2712&lt;/code&gt; depending on the board. The &lt;code&gt;apt full-upgrade&lt;/code&gt; command above will still catch it, but confirm with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Bookworm and newer&lt;/span&gt;
apt-cache policy linux-image-rpi-v8

&lt;span class="c"&gt;# Bullseye and older&lt;/span&gt;
apt-cache policy raspberrypi-kernel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "Installed" and "Candidate" versions should match after your upgrade. If they do not, run the upgrade again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; Reboot to load the new kernel. This is not optional. The running kernel in memory is still the old one until you reboot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Proxmox hosts, shut down or migrate your VMs and containers before rebooting the host. Use the Proxmox web UI or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List running VMs&lt;/span&gt;
qm list

&lt;span class="c"&gt;# Shutdown a VM by ID&lt;/span&gt;
qm shutdown &lt;span class="o"&gt;[&lt;/span&gt;vmid]

&lt;span class="c"&gt;# List running containers&lt;/span&gt;
pct list

&lt;span class="c"&gt;# Shutdown a container by ID&lt;/span&gt;
pct shutdown &lt;span class="o"&gt;[&lt;/span&gt;ctid]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 5:&lt;/strong&gt; After the reboot, confirm the new kernel is loaded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The version string should be newer than what you recorded in Step 1. Cross-reference it against your distro's security advisory to confirm the patched version is in place. For Debian, check &lt;a href="https://security-tracker.debian.org/tracker/" rel="noopener noreferrer"&gt;security-tracker.debian.org&lt;/a&gt;. For Ubuntu, check &lt;a href="https://ubuntu.com/security/notices" rel="noopener noreferrer"&gt;ubuntu.com/security/notices&lt;/a&gt;. For Raspberry Pi OS, check the &lt;a href="https://www.raspberrypi.com/news/" rel="noopener noreferrer"&gt;Raspberry Pi blog and release notes&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛡️ Patch Status, Checked 29 September 2026
&lt;/h2&gt;

&lt;p&gt;This is the part of the post that goes stale fastest, so here is a dated snapshot. Everything below was checked against the Debian security tracker, the Ubuntu CVE tracker and the Proxmox kernel changelog on 29 September 2026. Re-check before you decide you are covered.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Debian 13 trixie:&lt;/strong&gt; all four first fixed in linux 6.12.111-1, shipped as DSA-6528-1.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debian 12 bookworm:&lt;/strong&gt; DirtyAH6, PPPoEject and DiagSpill first fixed in linux 6.1.187-1 from bookworm-security. TUNderflow is the exception. The Debian tracker still lists bookworm as vulnerable, 6.1.187-1 included, so there is no fix for CVE-2026-81000 on bookworm yet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ubuntu 22.04, 24.04 and 26.04:&lt;/strong&gt; at the time of this check the Ubuntu CVE tracker showed no released kernel for any of the four on the main &lt;code&gt;linux&lt;/code&gt; packages. They sit at needed or pending. Watch &lt;a href="https://ubuntu.com/security/notices" rel="noopener noreferrer"&gt;ubuntu.com/security/notices&lt;/a&gt; instead of assuming an upgrade fixed it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proxmox VE:&lt;/strong&gt; the Proxmox kernel changelog names only TUNderflow, first fixed in proxmox-kernel-7.0 version 7.0.14-19, released 18 September 2026. The newest build at the time of checking was 7.0.14-20 from 24 September 2026. The other three CVEs are not named anywhere in that changelog, and the Proxmox 7.0 kernel follows the Ubuntu kernel, which has not released them, so do not assume DirtyAH6, PPPoEject and DiagSpill are closed on a Proxmox host. Run &lt;code&gt;pveversion -v&lt;/code&gt; and compare against the changelog.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Raspberry Pi OS:&lt;/strong&gt; no Raspberry Pi OS kernel build carrying all four fixes could be confirmed at the time of this check. On the 6.12 branch you need 6.12.109 or newer for the full set, so check the installed package version rather than assuming.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🛡️ What Each Vulnerability Affects
&lt;/h2&gt;

&lt;p&gt;Here is what each bug actually is, taken from the disclosure and the researcher's write-up. Three of the four need &lt;code&gt;CAP_NET_ADMIN&lt;/code&gt; in a network namespace the attacker controls, which an ordinary local user obtains by creating an unprivileged user namespace first. DiagSpill is the exception and needs no special privileges at all.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DirtyAH6 (CVE-2026-80844):&lt;/strong&gt; The IPv6 Authentication Header code in the IPsec and XFRM path does not check that a routing header's &lt;code&gt;segments_left&lt;/code&gt; field is no larger than the number of addresses actually present, so pointer arithmetic in &lt;code&gt;ipv6_rearrange_rthdr()&lt;/code&gt; walks out of bounds and an oversized &lt;code&gt;memmove()&lt;/code&gt; corrupts kernel memory. It needs AH6 and XFRM support plus &lt;code&gt;CAP_NET_ADMIN&lt;/code&gt; and &lt;code&gt;CAP_NET_RAW&lt;/code&gt; in a namespace the attacker controls. The researcher also reports a remote crash, and in his own lab remote root, against a host acting as an IPv6 router that applies AH in transport mode.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PPPoEject (CVE-2026-68121):&lt;/strong&gt; A use after free in &lt;code&gt;pppoe_sendmsg()&lt;/code&gt;. The function holds a pointer into the socket buffer head across a &lt;code&gt;dev_hard_header()&lt;/code&gt; callback that can reallocate that head, then writes six bytes through the stale pointer. It needs a lower device whose header callback expands the buffer head, not merely a loadable PPPoE module, which makes it the narrowest of the four in practice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TUNderflow (CVE-2026-81000):&lt;/strong&gt; The TUN/TAP driver stores a receive headroom value without bounding it. A device path that propagates oversized headroom (the proof of concept chains a netkit device through Open vSwitch to a raw TUN port) underflows a size calculation and leaves packet data 64 bytes past the end of the allocation. Simply having a TUN or TAP interface is not enough, you need that headroom propagation path. Worth correcting a common assumption here: Docker's default bridge networking uses veth pairs rather than TUN/TAP, and the in kernel WireGuard module is its own device type, so the usual homelab exposure is QEMU tap interfaces and OpenVPN style TUN devices, not containers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DiagSpill (CVE-2026-74469):&lt;/strong&gt; An SCTP association can hold up to 65,536 peer transports but the counter is 16 bits wide, so it wraps to zero at the limit. &lt;code&gt;sctp_diag&lt;/code&gt; then reserves buffer space based on the wrapped count and copies the full transport list anyway, writing roughly 8 MiB past the end of the response buffer. This one needs no capabilities and no user namespace, only SCTP and &lt;code&gt;sctp_diag&lt;/code&gt; support, which is what makes it the priority of the four.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DiagSpill is the one to move fastest on if you are triaging. It is the only one of the four that an ordinary local user can reach without creating a user namespace, which also means the usual advice to restrict unprivileged user namespaces does nothing for it. If you do not need SCTP, keep the module out of the kernel.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛡️ Hardening Beyond the Patch
&lt;/h2&gt;

&lt;p&gt;Patching is the fix, but a few additional controls reduce your exposure, which matters more than usual here because some distributions have not shipped all four yet. Restricting unprivileged user namespaces closes the ordinary user path to three of the four. It does nothing for DiagSpill.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Restrict unprivileged user namespaces:&lt;/strong&gt; Three of these four bugs need a user namespace for an ordinary local user to reach them. Ubuntu introduced AppArmor based restrictions in 23.10 and enabled them by default from 24.04, controlled by &lt;code&gt;kernel.apparmor_restrict_unprivileged_userns&lt;/code&gt;, so 24.04 and 26.04 are covered out of the box. Debian is not. The Debian and Ubuntu specific &lt;code&gt;kernel.unprivileged_userns_clone&lt;/code&gt; knob has been discussed for deprecation, so confirm it exists on your kernel with &lt;code&gt;sysctl -a | grep userns&lt;/code&gt; before relying on it; the portable upstream equivalent is &lt;code&gt;sysctl -w user.max_user_namespaces=0&lt;/code&gt;. Either way you will break things that legitimately use user namespaces, including Flatpak, rootless containers and browser sandboxes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use AppArmor or SELinux profiles:&lt;/strong&gt; Proxmox ships with AppArmor. Make sure it is enforcing for your LXC containers. Unprivileged containers with AppArmor confinement are significantly harder to break out of.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limit local access:&lt;/strong&gt; Do not run untrusted code or expose shell access to untrusted users on these nodes. Local privesc only matters if someone or something local can trigger it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unload unused kernel modules:&lt;/strong&gt; If you are not using PPPoE or SCTP, keep both out. Add &lt;code&gt;blacklist pppoe&lt;/code&gt; and &lt;code&gt;blacklist sctp&lt;/code&gt; to a file in &lt;code&gt;/etc/modprobe.d/&lt;/code&gt; and run &lt;code&gt;update-initramfs -u&lt;/code&gt;. Blacklisting stops the on demand autoload that a socket call would trigger, and for DiagSpill that is the only mitigation short of patching, since restricting user namespaces does not touch it. Verify with &lt;code&gt;lsmod | grep -E 'pppoe|sctp'&lt;/code&gt; afterwards.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔧 Troubleshooting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Kernel version did not change after reboot:&lt;/strong&gt; Your package manager may have the patched package available but it was not installed. Run &lt;code&gt;apt list --upgradable | grep linux-image&lt;/code&gt; to check, then install explicitly with &lt;code&gt;sudo apt install linux-image-[version]&lt;/code&gt; matching your architecture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Proxmox node is stuck on an older kernel:&lt;/strong&gt; Proxmox sometimes pins the kernel for stability. Check &lt;code&gt;/etc/default/grub&lt;/code&gt; and the GRUB menu to ensure the new kernel is selected as default. Run &lt;code&gt;update-grub&lt;/code&gt; after confirming the package is installed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Raspberry Pi will not boot after upgrade:&lt;/strong&gt; This is rare but can happen if the bootloader and kernel fall out of sync. Boot from a backup SD card or use &lt;code&gt;rpi-update&lt;/code&gt; with caution, and check the Raspberry Pi OS forums for your specific board and OS version before using it.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✅ Wrap Up
&lt;/h2&gt;

&lt;p&gt;Four local root bugs in one disclosure batch is a bad day for Linux security. Updating and rebooting is the right move, but as of 29 September 2026 it is not the whole answer: Debian trixie has all four, Debian bookworm is still missing the TUNderflow fix, and Ubuntu has not released kernels for any of them. Prioritize DiagSpill, since it is the one an unprivileged local user can reach without a user namespace, and check the tracker for your distribution instead of assuming a fresh upgrade covered you.&lt;/p&gt;

&lt;p&gt;After you patch, take five minutes to check your other nodes. It is easy to forget a Raspberry Pi tucked behind the TV or a secondary Proxmox node you set up months ago. Cross-reference the patched kernel version against your distro's security tracker to make sure you are actually covered, not just up to date on packages.&lt;/p&gt;

&lt;p&gt;Stay subscribed to &lt;a href="https://oss-security.openwall.org/wiki/mailing-lists/oss-security" rel="noopener noreferrer"&gt;oss-sec&lt;/a&gt; if you want these disclosures in your inbox before they make the rounds on aggregators.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related from the lab
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://codewithromi.com/linux-kernel-security-gaps-why-your-homelab-isnt-safe-until-you-know-this/" rel="noopener noreferrer"&gt;Linux Kernel Security Gaps: Why Your Homelab Isn't Safe Until You Know This&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codewithromi.com/patching-dnsmasq-cves-securing-your-pi-hole-homelab-dns/" rel="noopener noreferrer"&gt;Patching Dnsmasq CVEs: Securing Your Pi-hole &amp;amp; Homelab DNS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codewithromi.com/crowdsec-community-powered-security/" rel="noopener noreferrer"&gt;CrowdSec: Community-Powered Security&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://codewithromi.com/patch-these-four-linux-kernel-local-root-vulns-now-dirtyah6-pppoeject-tunderflow-and-diagspill/" rel="noopener noreferrer"&gt;codewithromi.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>security</category>
      <category>devops</category>
    </item>
    <item>
      <title>WordPress 7.1.2 Patches a Critical Path Traversal to RCE: Update Now</title>
      <dc:creator>CodeWithRomi</dc:creator>
      <pubDate>Wed, 07 Oct 2026 23:00:46 +0000</pubDate>
      <link>https://dev.to/theeromi/wordpress-712-patches-a-critical-path-traversal-to-rce-update-now-24pj</link>
      <guid>https://dev.to/theeromi/wordpress-712-patches-a-critical-path-traversal-to-rce-update-now-24pj</guid>
      <description>&lt;p&gt;A serious vulnerability was disclosed in the WordPress core codebase: an unauthenticated path traversal bug that, under the right server conditions, can escalate to remote code execution. That combination, no login required plus potential RCE, puts this near the top of the priority list for any WordPress site owner or host. Here is what the vulnerability is, who is affected, and exactly what to do about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  💡 Why This Matters
&lt;/h2&gt;

&lt;p&gt;Path traversal vulnerabilities let an attacker reference files outside the intended directory by injecting sequences like &lt;code&gt;../&lt;/code&gt; into a request. On their own they are dangerous. Chained with a code execution primitive, they become critical.&lt;/p&gt;

&lt;p&gt;The "conditional" part of this disclosure means RCE is not guaranteed on every WordPress install. Server configuration, PHP settings, and installed plugins or themes all influence whether the traversal can be weaponised into execution. But "conditional" does not mean "safe to ignore." Attackers will probe for the conditions that make it work, and many shared hosting and default VPS setups are likely to qualify.&lt;/p&gt;

&lt;p&gt;The advisory is &lt;a href="https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-7hp8-65ch-5whp" rel="noopener noreferrer"&gt;GHSA-7hp8-65ch-5whp&lt;/a&gt; in the WordPress develop repository, and it is CVE-2026-87902, rated Critical at CVSS 4.0 9.2. Every release from 4.7.0 through 7.1.1 is affected. WordPress shipped 7.1.2 on 22 September 2026 along with backports to every branch still eligible for security fixes, down to 4.7.37. Treat this as urgent rather than theoretical: CISA added CVE-2026-87902 to its Known Exploited Vulnerabilities catalog on 25 September 2026, and public scanning tooling plus attempts to write PHP files through &lt;code&gt;pearcmd.php&lt;/code&gt; are already being seen in the wild.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧰 Prerequisites: Who Should Read This
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Anyone running a self-hosted WordPress site on 4.7.0 or later, which in practice means anyone running WordPress&lt;/li&gt;
&lt;li&gt;Hosting providers and managed WordPress hosts&lt;/li&gt;
&lt;li&gt;Homelab builders running WordPress internally (yes, even private sites)&lt;/li&gt;
&lt;li&gt;DevOps teams with WordPress in staging or CI pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If WordPress is running somewhere you control, this post is for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔍 How the Vulnerability Works
&lt;/h2&gt;

&lt;p&gt;The flaw is in one specific thing: page template resolution. An unauthenticated request can steer &lt;code&gt;get_page_template()&lt;/code&gt; into including a chosen readable local &lt;code&gt;.php&lt;/code&gt; file from outside the active theme's directories. That makes it a local file inclusion rather than a generic web root escape, and it is classed as CWE-98, improper control of filename for an include or require statement.&lt;/p&gt;

&lt;p&gt;The conditions are narrower and far more specific than 'a misconfigured server', and they are worth checking against your own install rather than guessing. The file inclusion needs the active theme, parent or child, to contain a top level directory whose name begins with &lt;code&gt;page-&lt;/code&gt;. That is not exotic: &lt;code&gt;page-templates/&lt;/code&gt; ships in Twenty Twelve and Twenty Fourteen and in popular themes including Neve, Hestia and Sydney. Turning that inclusion into code execution needs two more things: a readable &lt;code&gt;.php&lt;/code&gt; file on the server that will act on attacker supplied arguments, in practice &lt;code&gt;pearcmd.php&lt;/code&gt;, and &lt;code&gt;register_argc_argv&lt;/code&gt; set to &lt;code&gt;On&lt;/code&gt;. Official PHP Docker images and default cPanel configurations on PHP before 8.5 both qualify. For the precise code path, refer to the &lt;a href="https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-7hp8-65ch-5whp" rel="noopener noreferrer"&gt;official security advisory&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠️ Step-by-Step: Patching Your WordPress Install
&lt;/h2&gt;

&lt;p&gt;The most important action is updating WordPress core. On the current branch that means 7.1.2. If you are held back on an older branch there is a backport for it, 7.0.6, 6.9.9, 6.8.10, 6.7.9 and so on all the way down to 4.7.37, so being unable to move to 7.1 is not a reason to stay unpatched. Here is how to update cleanly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Back up your database and files before touching anything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Example using WP-CLI to export the database&lt;/span&gt;
wp db &lt;span class="nb"&gt;export &lt;/span&gt;backup-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%F&lt;span class="si"&gt;)&lt;/span&gt;.sql &lt;span class="nt"&gt;--allow-root&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Update WordPress core via WP-CLI (recommended for server installs).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wp core update &lt;span class="nt"&gt;--allow-root&lt;/span&gt;
wp core version &lt;span class="nt"&gt;--allow-root&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Confirm the installed version matches the patched release listed in the advisory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wp core version &lt;span class="nt"&gt;--allow-root&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; If you manage WordPress through a hosting dashboard (cPanel, Plesk, Kinsta, etc.), use the built-in one-click updater or auto-update toggle. Confirm the version number in the WordPress admin under Dashboard, then About.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5:&lt;/strong&gt; Enable automatic background updates for minor releases if you have not already. Add this to your &lt;code&gt;wp-config.php&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nb"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'WP_AUTO_UPDATE_CORE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'minor'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures security patches ship automatically without waiting for manual intervention.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛡️ Hardening: Reduce the Attack Surface Regardless of Version
&lt;/h2&gt;

&lt;p&gt;Patching fixes the specific bug. Hardening reduces the blast radius of the next one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Block PHP execution in the uploads directory.&lt;/strong&gt; Worth doing, but be clear that it does not mitigate CVE-2026-87902, which includes a &lt;code&gt;.php&lt;/code&gt; file already present on the server rather than one an attacker uploaded. Keep it as general hygiene against the next upload driven bug. For Nginx, add this to your server block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt; &lt;span class="n"&gt;/wp-content/uploads/.*\.php&lt;/span&gt;$ &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;deny&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Apache, drop a &lt;code&gt;.htaccess&lt;/code&gt; file inside &lt;code&gt;wp-content/uploads/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nl"&gt;Files&lt;/span&gt;&lt;span class="sr"&gt; "*.php"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="nc"&gt;Require&lt;/span&gt; &lt;span class="ss"&gt;all&lt;/span&gt; denied
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nl"&gt;Files&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Disable file editing in the WordPress admin.&lt;/strong&gt; Add to &lt;code&gt;wp-config.php&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nb"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s1"&gt;'DISALLOW_FILE_EDIT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Break the RCE preconditions if you cannot patch within the hour.&lt;/strong&gt; Set &lt;code&gt;register_argc_argv = Off&lt;/code&gt; in your PHP configuration and confirm it with &lt;code&gt;php -i | grep register_argc_argv&lt;/code&gt;. Then check whether a reachable &lt;code&gt;pearcmd.php&lt;/code&gt; exists at all, with &lt;code&gt;find / -name pearcmd.php 2&amp;gt;/dev/null&lt;/code&gt;, and remove it if nothing on the box needs PEAR. Neither of these closes the file inclusion, but together they remove the documented route from inclusion to code execution. Patching is still the fix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run WordPress behind a WAF.&lt;/strong&gt; Cloudflare, Wordfence, or a self-hosted option like ModSecurity can catch traversal patterns at the request layer before they reach PHP. Even a free Cloudflare plan adds meaningful filtering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Restrict &lt;code&gt;xmlrpc.php&lt;/code&gt; and &lt;code&gt;wp-login.php&lt;/code&gt; by IP if possible.&lt;/strong&gt; Unauthenticated attack surface shrinks dramatically when admin endpoints are IP-locked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Nginx example: restrict wp-login.php&lt;/span&gt;
&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;/wp-login.php&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;allow&lt;/span&gt; &lt;span class="s"&gt;[your-admin-IP]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;deny&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔧 Troubleshooting Common Update Issues
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;WP-CLI command not found:&lt;/strong&gt; Install it by following the official WP-CLI installation guide at &lt;a href="https://wp-cli.org" rel="noopener noreferrer"&gt;wp-cli.org&lt;/a&gt;. On most Linux servers it is a single &lt;code&gt;curl&lt;/code&gt; download and &lt;code&gt;chmod&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update fails due to file permissions:&lt;/strong&gt; WordPress needs write access to its own directory during updates. Check that the web server user (often &lt;code&gt;www-data&lt;/code&gt; on Ubuntu/Debian) owns the WordPress files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; www-data:www-data /var/www/html/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Site breaks after update:&lt;/strong&gt; Restore from the backup taken in Step 1, then investigate plugin or theme conflicts. Deactivate all plugins and retest. The WordPress support forums and the WP-CLI &lt;code&gt;--debug&lt;/code&gt; flag are your friends here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Managed host shows no update available:&lt;/strong&gt; Some managed hosts patch at the server level before surfacing a WordPress core update. Contact support to confirm your environment is running the patched version.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✅ Wrap Up
&lt;/h2&gt;

&lt;p&gt;Unauthenticated path traversal leading to conditional RCE is the kind of vulnerability that gets actively exploited fast, especially against WordPress given its enormous install base. The fix is straightforward: update core to the patched release, turn &lt;code&gt;register_argc_argv&lt;/code&gt; off if nothing on the server needs it, and put automatic minor updates on so future patches land without delay.&lt;/p&gt;

&lt;p&gt;The patched version is 7.1.2, or the backport for whichever branch you are on. The &lt;a href="https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-7hp8-65ch-5whp" rel="noopener noreferrer"&gt;official advisory (GHSA-7hp8-65ch-5whp)&lt;/a&gt; has the full backport list and the code path. Patch first, harden second, and since this one is already on CISA's exploited list, check your access logs for requests carrying &lt;code&gt;page-&lt;/code&gt; template names or &lt;code&gt;pearcmd&lt;/code&gt; while you are in there.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related from the lab
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://codewithromi.com/crowdsec-community-powered-security/" rel="noopener noreferrer"&gt;CrowdSec: Community-Powered Security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codewithromi.com/fail2ban-setup-homelab-security-part-2/" rel="noopener noreferrer"&gt;Fail2Ban Setup (Homelab Security Part 2)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codewithromi.com/better-homelab-secret-management-with-infisical/" rel="noopener noreferrer"&gt;Better Homelab Secret Management with Infisical&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://codewithromi.com/wordpress-7-1-2-patches-a-critical-path-traversal-to-rce-update-now/" rel="noopener noreferrer"&gt;codewithromi.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>security</category>
      <category>php</category>
      <category>linux</category>
    </item>
    <item>
      <title>Miri Was Leaking CI Secrets Through GitHub Actions Caches: What to Clean Up</title>
      <dc:creator>CodeWithRomi</dc:creator>
      <pubDate>Wed, 07 Oct 2026 23:00:44 +0000</pubDate>
      <link>https://dev.to/theeromi/miri-was-leaking-ci-secrets-through-github-actions-caches-what-to-clean-up-6o</link>
      <guid>https://dev.to/theeromi/miri-was-leaking-ci-secrets-through-github-actions-caches-what-to-clean-up-6o</guid>
      <description>&lt;p&gt;CI/CD pipelines are a common attack surface, and most developers focus on the obvious risks: exposed tokens, world-readable repos, overly permissive workflow permissions. But a subtler class of vulnerability comes from caching, and in September 2026 the Rust project published an advisory that is a textbook case. Miri, Rust's interpreter for detecting undefined behavior, was writing every environment variable it saw into the &lt;code&gt;target/&lt;/code&gt; directory. Cache &lt;code&gt;target/&lt;/code&gt; in GitHub Actions, as most Rust projects do, and any secret handed to that job was sitting in the cache where a later pull request run could restore it and read it.&lt;/p&gt;

&lt;p&gt;The good news first, because it changes how you should read the rest: this is fixed. The Rust project published the advisory on 21 September 2026 and the nightly toolchain dated 22 September 2026 stops Miri persisting arbitrary environment variables, keeping only &lt;code&gt;CARGO_*&lt;/code&gt; (excluding &lt;code&gt;CARGO_*_TOKEN&lt;/code&gt;) and &lt;code&gt;OUT_DIR&lt;/code&gt;. No CVE was assigned. What is left for you is cleanup rather than defense: update the toolchain, delete the caches you already have, and rotate anything that was in scope. This post covers the mechanism, that cleanup, and how to stop caching biting you again.&lt;/p&gt;

&lt;h2&gt;
  
  
  💡 Why This Matters
&lt;/h2&gt;

&lt;p&gt;Miri runs Rust programs in an interpreted environment so it can detect undefined behavior at runtime. To notice when something build relevant changed between runs, &lt;code&gt;cargo miri&lt;/code&gt; recorded the environment it was invoked with, and it recorded all of it rather than only the variables it needed. That record lived inside the &lt;code&gt;target/&lt;/code&gt; directory. Note what is not required here: nothing in your code, your test output or your logs had to mention the secret. Passing it to the step was enough for it to land on disk in the directory everyone caches.&lt;/p&gt;

&lt;p&gt;That matters because of how GitHub Actions cache scoping works. A workflow run can restore caches created on its own branch and on the default branch, so a pull request run can pull back a cache that a privileged push to &lt;code&gt;main&lt;/code&gt; created. The advisory spells out the conditions: you are affected if you run &lt;code&gt;cargo miri&lt;/code&gt; in CI, pass secrets as environment variables to that step, cache the &lt;code&gt;target/&lt;/code&gt; directory with something like &lt;code&gt;actions/cache&lt;/code&gt; or &lt;code&gt;Swatinem/rust-cache&lt;/code&gt;, and allow pull requests to read from that cache.&lt;/p&gt;

&lt;p&gt;The broader lesson goes beyond Miri. Any tool that snapshots its invocation environment into a build directory is a candidate for this class of bug, and build directories are exactly what CI caching is pointed at. Rust's toolchain is just where this instance was caught, reported and documented.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧰 Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A GitHub Actions workflow that uses Miri (typically via &lt;code&gt;cargo miri test&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Familiarity with the &lt;code&gt;actions/cache&lt;/code&gt; action or the built-in Cargo caching patterns&lt;/li&gt;
&lt;li&gt;Repository secrets configured in Settings, such as API keys or tokens passed to your test environment&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔍 How the Leakage Path Works
&lt;/h2&gt;

&lt;p&gt;Here is the setup that creates the risk. A workflow injects a secret as an environment variable, runs Miri, and caches the Cargo &lt;code&gt;target/&lt;/code&gt; directory to speed up future runs. The &lt;code&gt;target/&lt;/code&gt; directory is the part that matters. Caching the registry or the Miri sysroot was never the problem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Vulnerable pattern (simplified)&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Miri&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;MY_SECRET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.MY_SECRET }}&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cargo miri test&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache build output&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v4&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;target&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;miri-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The value of &lt;code&gt;MY_SECRET&lt;/code&gt; does not have to appear in any log or test result. &lt;code&gt;cargo miri&lt;/code&gt; persisted it into &lt;code&gt;target/&lt;/code&gt; as part of the environment snapshot, and the cache step then packaged &lt;code&gt;target/&lt;/code&gt; up. The next job or pull request run that restores this cache has a file containing your secret, without that secret ever being granted to the run.&lt;/p&gt;

&lt;p&gt;No exotic trigger is required. You do not need a panic, a crash, or verbose diagnostics. Passing the secret to the step and caching &lt;code&gt;target/&lt;/code&gt; is the whole bug. The advisory also notes that someone who lifted a secret this way could push further commits to cover their tracks, so a clean-looking workflow log is not evidence you were fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛡️ Step-by-Step: Hardening Your Pipeline
&lt;/h2&gt;

&lt;p&gt;Step 1: Never inject production secrets into Miri test runs. If your tests genuinely need a credential, use a scoped, revocable test credential with no real permissions. Treat anything Miri can see as potentially loggable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Prefer a dummy or scoped value for Miri runs&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Miri&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;MY_SECRET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dummy-value-for-miri"&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cargo miri test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 2: Fix your caching scope. The directory that leaked is &lt;code&gt;target/&lt;/code&gt;, so either stop caching it on jobs that see secrets, or stop giving those jobs secrets. If you do cache something for Miri, cache the sysroot, and use a distinct cache key so a Miri cache never collides with a standard build cache.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache Miri sysroot only&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v4&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;miri-sysroot-${{ runner.os }}-${{ hashFiles('rust-toolchain.toml') }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the exact sysroot path for your toolchain with &lt;code&gt;rustc --print sysroot&lt;/code&gt; rather than hardcoding it. The path above is illustrative; confirm it in your environment.&lt;/p&gt;

&lt;p&gt;Step 3: Restrict workflow permissions explicitly. Add a top-level &lt;code&gt;permissions&lt;/code&gt; block to limit what a workflow can do even if a cache is poisoned.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 4: Isolate Miri jobs from secret-bearing jobs. Use separate jobs so the job running Miri never sees a real secret. GitHub Actions does not share environment variables across jobs unless you pass them explicitly, so inject secrets at the step level in the job that actually needs them. One correction worth making if you copy patterns off the internet: &lt;code&gt;secrets: inherit&lt;/code&gt; is only valid on a job that calls a reusable workflow with &lt;code&gt;uses&lt;/code&gt;, not on a job that runs its own &lt;code&gt;steps&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;miri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="c1"&gt;# No secrets injected here&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cargo miri test&lt;/span&gt;

  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;miri&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DEPLOY_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DEPLOY_TOKEN }}&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./deploy.sh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 5: Audit your cache keys and paths regularly. Run a search across your workflow files for any &lt;code&gt;actions/cache&lt;/code&gt; or &lt;code&gt;cache-dependency-path&lt;/code&gt; usage that overlaps with paths your secret-injected steps write to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"actions/cache"&lt;/span&gt; .github/workflows/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔧 Troubleshooting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;My Miri tests need real environment values to run correctly.&lt;/strong&gt; Refactor the tests to accept injectable fakes, or use a secrets manager that vends short-lived credentials scoped only to the test run. This is a design problem as much as a security one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I am not sure what is in my cache.&lt;/strong&gt; Download a cache artifact locally using the GitHub CLI (&lt;code&gt;gh cache list&lt;/code&gt; and &lt;code&gt;gh cache download&lt;/code&gt;) and inspect the contents before assuming it is clean. The official GitHub CLI docs cover the exact flags.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We use a third-party caching action, not actions/cache.&lt;/strong&gt; The same principles apply. Check whether that action stores artifacts in a location your Miri run can write to, and review the action's own permissions model.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✅ Wrap Up
&lt;/h2&gt;

&lt;p&gt;Caching is one of the best levers you have for speeding up CI, but it introduces a persistent artifact that outlives a single workflow run. In this case the artifact was the ordinary Cargo &lt;code&gt;target/&lt;/code&gt; directory, and the tool filling it with secrets was doing so as a convenience feature. Be deliberate about what goes into the cache and which jobs hold real secrets.&lt;/p&gt;

&lt;p&gt;Concretely, for this advisory: move to a toolchain from 22 September 2026 or later, delete the existing caches on any repository that ran &lt;code&gt;cargo miri&lt;/code&gt; with secrets in scope, and rotate those secrets. Then, as standing practice, keep Miri jobs secret free, lock down workflow permissions, and audit your cache paths as part of any security review. Treat cached artifacts with the same suspicion you would treat a shared filesystem.&lt;/p&gt;

&lt;p&gt;The primary source is the Rust project advisory &lt;a href="https://blog.rust-lang.org/2026/09/21/github-actions-leaking-secrets-when-miri-output-is-cached/" rel="noopener noreferrer"&gt;GitHub Actions leaking secrets when Miri output is cached&lt;/a&gt;, published 21 September 2026. Read it in full for the exact affected conditions and for any follow-up, and pair it with the GitHub Actions security hardening guide in the GitHub docs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related from the lab
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://codewithromi.com/better-homelab-secret-management-with-infisical/" rel="noopener noreferrer"&gt;Better Homelab Secret Management with Infisical&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codewithromi.com/an-ai-agent-racked-up-a-massive-bill-scanning-dn42-heres-how-to-stop-that-from-happening-to-you/" rel="noopener noreferrer"&gt;An AI Agent Ran Up a $6,531 AWS Bill Scanning DN42. Here's How to Cap Yours&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codewithromi.com/the-design-of-everyday-cryptography-a-homelab-primer/" rel="noopener noreferrer"&gt;The Design of Everyday Cryptography: A Homelab Primer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://codewithromi.com/miri-was-leaking-ci-secrets-through-github-actions-caches/" rel="noopener noreferrer"&gt;codewithromi.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>rust</category>
      <category>githubactions</category>
      <category>cryptography</category>
    </item>
  </channel>
</rss>
