<?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: Dhruv Agrawal</title>
    <description>The latest articles on DEV Community by Dhruv Agrawal (@dhruv1206).</description>
    <link>https://dev.to/dhruv1206</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%2F3317139%2Fff5c3ee5-b1c6-4afb-8a3c-4ad0fdd1620c.jpg</url>
      <title>DEV Community: Dhruv Agrawal</title>
      <link>https://dev.to/dhruv1206</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dhruv1206"/>
    <language>en</language>
    <item>
      <title>How a Trusted Extension Name Delivered an SSH Backdoor Through Cursor's Store</title>
      <dc:creator>Dhruv Agrawal</dc:creator>
      <pubDate>Mon, 02 Nov 2026 05:32:13 +0000</pubDate>
      <link>https://dev.to/dhruv1206/how-a-trusted-extension-name-delivered-an-ssh-backdoor-through-cursors-store-31ba</link>
      <guid>https://dev.to/dhruv1206/how-a-trusted-extension-name-delivered-an-ssh-backdoor-through-cursors-store-31ba</guid>
      <description>&lt;p&gt;On October 29, Cursor's extension marketplace served tampered builds of Anthropic's Claude Code extension, versions 2.1.312 and 2.1.313. They were live for about 11 hours. They added an attacker's SSH key to developers' machines.&lt;/p&gt;

&lt;p&gt;Anthropic's systems were not breached. The official package was not breached either. The attacker, Anthropic said, "used access to Cursor's marketplace." In a statement quoted by BankInfoSecurity, Cursor said at most 5,000 users were affected.&lt;/p&gt;

&lt;p&gt;To see how that is possible, follow the path an extension takes to your editor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc8wi4blhma6nplqygosw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc8wi4blhma6nplqygosw.png" alt="Diagram of the extension delivery chain: publisher, Open VSX registry, Cursor marketplace proxy, editor, extension host, with the tampered step marked at the marketplace" width="800" height="520"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Delivery chain as described in Cursor's extension docs. The incident shows only that the copy Cursor's marketplace served was tampered with.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The delivery chain
&lt;/h2&gt;

&lt;p&gt;Cursor is built on VS Code. It does not use Microsoft's extension marketplace. Its docs say it gets third-party extensions from Open VSX, an open extension registry. Downloads pass through Cursor's own marketplace proxy, which scans them for malware before serving them.&lt;/p&gt;

&lt;p&gt;So a Claude Code update reaches you in four hops:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Anthropic builds and publishes the extension.&lt;/li&gt;
&lt;li&gt;A registry stores it.&lt;/li&gt;
&lt;li&gt;Cursor's marketplace serves it under the name &lt;code&gt;anthropic.claude-code&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Your editor installs it and loads it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You see the name at step 3. At step 4, your editor runs whatever step 3 served. If someone can change those bytes, the name still looks right.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the tampered build did
&lt;/h2&gt;

&lt;p&gt;StepSecurity compared a malicious build with the clean one. Its tampered &lt;code&gt;extension.js&lt;/code&gt; was the official 2.1.312 bundle with a 7,386-byte loader added to the front. The file was about 3.6 MB. As StepSecurity put it, "The malicious build is the official extension in every respect except one."&lt;/p&gt;

&lt;p&gt;In the 2.1.313 build, the new version number appeared only in the repackaged manifest. That made it look like a routine update.&lt;/p&gt;

&lt;p&gt;The loader runs as soon as the editor loads the extension. No Claude conversation is needed. VS Code's own docs explain why that matters: "The extension host has the same permissions as VS Code itself." An extension can read your files, use the network and start processes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdrmcywoh076qm9sof41a.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdrmcywoh076qm9sof41a.gif" alt="Animated walkthrough of the loader's stages after the extension loads: recon, SSH key, enable SSH, reverse tunnel, persistence, beacon" width="800" height="520"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Loader stages as listed in StepSecurity's analysis.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;According to StepSecurity, the loader then ran six stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;collected host details and the &lt;em&gt;names&lt;/em&gt; of up to 40 secret-looking environment variables&lt;/li&gt;
&lt;li&gt;added an attacker key to &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;tried to turn on the SSH server&lt;/li&gt;
&lt;li&gt;tried to open a reverse SSH tunnel, retrying every 60 seconds&lt;/li&gt;
&lt;li&gt;set up persistence so the tunnel restarts after a reboot or login&lt;/li&gt;
&lt;li&gt;reported back to the attacker's server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;StepSecurity says no credential values were read. Cursor notes that enabling SSH usually needs admin rights. The key and persistence could still land without them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The evidence
&lt;/h2&gt;

&lt;p&gt;Cursor's notice gives the window: about 2:27am to 1:35pm Pacific on October 29. Cursor removed the malicious versions, took its marketplace offline, rebuilt it and rotated keys. The Hacker News reports that Microsoft said the build was never published on the Visual Studio Marketplace or npm. StepSecurity verified the Open VSX copy of 2.1.312 as clean.&lt;/p&gt;

&lt;p&gt;How the attacker got access to Cursor's marketplace has not been disclosed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Check, don't assume.&lt;/strong&gt; If you installed or updated Claude Code from Cursor's marketplace that day, follow Cursor's check steps. Look in &lt;code&gt;authorized_keys&lt;/code&gt; for a key labelled &lt;code&gt;svc-telemetry&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Updating is not cleaning.&lt;/strong&gt; Anthropic has released 2.1.314. But security researcher Kevin Beaumont noted that the replacement versions, which auto-update, "do &lt;em&gt;not&lt;/em&gt; remove the prior backdoors." Persistence and the SSH key must be removed by hand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add an install cooldown.&lt;/strong&gt; Cursor lets team admins delay installs and updates until a version has been public for a set number of hours. The default is 0, meaning off. The malicious versions were live for about 11 hours.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turn on signature checks if you can.&lt;/strong&gt; Cursor lets Enterprise teams require valid Open VSX extension signatures before install.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where this breaks down
&lt;/h2&gt;

&lt;p&gt;The root cause is unknown, so the fixes above are guesses about which layer failed. No source says whether signature checking would have caught this build. That depends on whether the attacker could also sign or bypass signing, which nobody has said.&lt;/p&gt;

&lt;p&gt;A cooldown helps only if someone spots the bad build before the delay runs out. It also delays good security fixes.&lt;/p&gt;

&lt;p&gt;The weakest part of this post is its general lesson. Any store that serves you code could be the weak hop. But the evidence covers one incident in one marketplace, and its root cause is still undisclosed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;The extension name tells you who should have written the code. It does not tell you who last touched the bytes.&lt;/p&gt;

&lt;p&gt;Which hops sit between your editor and the publishers you trust?&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Cursor, &lt;a href="https://cursor.com/blog/claude-code-extension-security-notice" rel="noopener noreferrer"&gt;Security notice: malicious version of Anthropic.claude-code&lt;/a&gt; (October 29, 2026, updated October 30)&lt;/li&gt;
&lt;li&gt;StepSecurity, &lt;a href="https://www.stepsecurity.io/blog/claude-code-extension-trojanized" rel="noopener noreferrer"&gt;Trojanized Claude Code extension delivered persistent SSH backdoor&lt;/a&gt; (October 30, 2026)&lt;/li&gt;
&lt;li&gt;The Hacker News, &lt;a href="https://thehackernews.com/2026/10/official-claude-code-vs-code-extension.html" rel="noopener noreferrer"&gt;Official Claude Code VS Code extension trojanized&lt;/a&gt; (October 30, 2026)&lt;/li&gt;
&lt;li&gt;BankInfoSecurity, &lt;a href="https://www.bankinfosecurity.com/cursor-confirms-anthropic-claude-code-extension-compromised-a-33263" rel="noopener noreferrer"&gt;Cursor confirms Anthropic Claude Code extension compromised&lt;/a&gt; (October 30, 2026)&lt;/li&gt;
&lt;li&gt;Cursor, &lt;a href="https://cursor.com/docs/configuration/extensions" rel="noopener noreferrer"&gt;Extensions documentation&lt;/a&gt; (accessed November 2, 2026)&lt;/li&gt;
&lt;li&gt;Visual Studio Code, &lt;a href="https://code.visualstudio.com/docs/configure/extensions/extension-runtime-security" rel="noopener noreferrer"&gt;Extension runtime security&lt;/a&gt; (accessed November 2, 2026)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>security</category>
      <category>vscode</category>
      <category>ai</category>
      <category>devops</category>
    </item>
    <item>
      <title>How PyTorch Became the Socket Every AI Chip Plugs Into</title>
      <dc:creator>Dhruv Agrawal</dc:creator>
      <pubDate>Wed, 28 Oct 2026 03:03:41 +0000</pubDate>
      <link>https://dev.to/dhruv1206/how-pytorch-became-the-socket-every-ai-chip-plugs-into-38m4</link>
      <guid>https://dev.to/dhruv1206/how-pytorch-became-the-socket-every-ai-chip-plugs-into-38m4</guid>
      <description>&lt;p&gt;On 19 October, AWS released TorchNeuron, which lets PyTorch run directly on its Trainium chips. A day later, Google and Meta released TorchTPU, which does the same for Google's TPUs. Both arrived at the PyTorch Conference, both are open source, and both connect to PyTorch through the same small hook.&lt;/p&gt;

&lt;p&gt;That last part is the story. Two companies with no reason to coordinate reached the same conclusion: the road onto a working engineer's laptop runs through one extension point in PyTorch.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens when you call &lt;code&gt;.to("cuda")&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Write &lt;code&gt;a + b&lt;/code&gt; on two tensors and PyTorch does not immediately add anything. It looks up a name, &lt;code&gt;aten::add&lt;/code&gt;, in a table.&lt;/p&gt;

&lt;p&gt;The part doing that lookup is called the dispatcher. It checks where the tensors live, reads a label off them — CPU, CUDA, and so on — and uses it to pick the function that actually does the addition on that hardware. Those hardware-specific functions are called kernels. A backend is a full set of kernels filed under one label.&lt;/p&gt;

&lt;p&gt;PyTorch keeps one label deliberately empty: &lt;code&gt;PrivateUse1&lt;/code&gt;. Any hardware vendor can claim it and register their kernels against it from their own repository, without needing their code accepted into PyTorch itself. TorchTPU registers there. TorchNeuron uses the same registration mechanism. A new chip becomes a new label, and &lt;code&gt;model.to("tpu")&lt;/code&gt; is what that looks like from your code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F94lxdaxcyqg477av6w6g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F94lxdaxcyqg477av6w6g.png" alt="aten::add routed by the PyTorch dispatcher to CPU, CUDA or PrivateUse1, with TorchTPU and TorchNeuron both registering against PrivateUse1" width="799" height="444"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A backend is a set of kernels filed under one dispatch label. TorchTPU names PrivateUse1; AWS says only "PyTorch's built-in mechanism for registering new hardware". Sources: PyTorch blog; Amazon; InfoQ.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This replaces something clumsier. PyTorch on TPUs used to go through JAX, Google's other framework. The TorchTPU team is blunt: the old approach "shoehorn[ed] PyTorch into JAX's modus operandi." The new backend has no JAX in it at runtime, and it runs operations one at a time as you write them, which is how most people debug. The chip speaks PyTorch now, instead of being translated first.&lt;/p&gt;

&lt;p&gt;The coverage looks real. Google tested 4,917 models from Hugging Face: 98% ran without crashing, and of those it checked closely, 97% produced the same numbers a GPU would. Amazon says it tests TorchNeuron against hundreds of thousands of models on every build.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running is the easy half
&lt;/h2&gt;

&lt;p&gt;Here is the TorchTPU team's own framing, caveat included:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Porting a workload to TPU should be as simple as changing &lt;code&gt;model.to("cuda")&lt;/code&gt; to &lt;code&gt;model.to("tpu")&lt;/code&gt;. (One cannot achieve peak TPU performance with that simple change... however, the model should run out of the box.)"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Read the bit in brackets, because that is where the work actually is.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbzev6z8rpp670k10rxky.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbzev6z8rpp670k10rxky.gif" alt="A full-width bar labelled 'it runs' shrinking to 40% as default network settings, precision, missing operations and the process layout each take a share" width="800" height="310"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Illustrative: the bar shows the direction of each documented caveat, not a measured benchmark. Source: PyTorch blog, TorchTPU.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The numbers come out different.&lt;/strong&gt; On CUDA, PyTorch multiplies in full 32-bit precision by default. TorchTPU defaults to a faster, lower-precision mode. Your model runs. Its output drifts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The default network settings cost a third of your speed.&lt;/strong&gt; TorchTPU ships with an option called "Skinny Collectives" switched on, which Google says can cost 30–40% end to end. Collectives are how chips pass results to each other while training.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not every operation exists yet.&lt;/strong&gt; Around 450 of PyTorch's built-in operations are supported. &lt;code&gt;FlexAttention&lt;/code&gt; was not, in one of Google's own examples.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The process layout changes.&lt;/strong&gt; TorchTPU runs one process per chip; JAX runs one per machine. Reinforcement-learning setups that share a chip between jobs need rework. &lt;code&gt;torch.compile&lt;/code&gt; also cannot handle changing input shapes yet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the part that looked hard, getting the code to run, is nearly solved. The part nobody puts in a launch announcement is that your tuned kernels, your checked numbers and your network settings do not move with &lt;code&gt;.to()&lt;/code&gt;. That is months of work per platform, and it is where lock-in actually lives now.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If you are evaluating other chips, stop scoring them on "does our model run." That question is becoming free. Score them on how much of your tuned performance survives the move.&lt;/li&gt;
&lt;li&gt;Set precision explicitly in any comparison across hardware, or you will blame the chip for a default.&lt;/li&gt;
&lt;li&gt;If you maintain a library that sits close to PyTorch, &lt;code&gt;PrivateUse1&lt;/code&gt; is the contract worth reading now. It is how your code reaches hardware you will never own.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where this breaks down
&lt;/h2&gt;

&lt;p&gt;Most of TorchTPU's performance numbers come from Google, and most of its examples do not compare against a GPU on equal footing. The 64-chip test against JAX/MaxText (1,828 against 1,937 tokens per second) is one of the few straight comparisons, and it is against JAX, not a GPU. The project calls itself "in active development." AWS does not mention CUDA at all. Putting these two releases in one frame is my argument, not either company's claim.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;The moat was never CUDA the language. It was the thousands of hand-tuned kernels underneath it, and &lt;code&gt;PrivateUse1&lt;/code&gt; is the agreed-on door for building a rival set.&lt;/p&gt;

&lt;p&gt;If switching chips costs one line of code and three months of tuning, is that portable?&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pytorch.org/blog/introducing-torchtpu/" rel="noopener noreferrer"&gt;PyTorch blog: Introducing TorchTPU&lt;/a&gt; — 20 Oct 2026&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.aboutamazon.com/news/innovation-at-amazon/torchneuron-pytorch-aws-trainium-chips-ai" rel="noopener noreferrer"&gt;Amazon: TorchNeuron brings PyTorch to AWS Trainium&lt;/a&gt; — 19 Oct 2026&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.infoq.com/news/2026/10/google-meta-torchtpu/" rel="noopener noreferrer"&gt;InfoQ: Google and Meta open-source TorchTPU&lt;/a&gt; — 25 Oct 2026&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>machinelearning</category>
      <category>pytorch</category>
      <category>python</category>
      <category>performance</category>
    </item>
    <item>
      <title>How Running Out of Memory Became a Server Takeover in PHP</title>
      <dc:creator>Dhruv Agrawal</dc:creator>
      <pubDate>Tue, 27 Oct 2026 10:19:24 +0000</pubDate>
      <link>https://dev.to/dhruv1206/how-running-out-of-memory-became-a-server-takeover-in-php-3dm3</link>
      <guid>https://dev.to/dhruv1206/how-running-out-of-memory-became-a-server-takeover-in-php-3dm3</guid>
      <description>&lt;p&gt;"The server would have to run out of memory first." That sounds like a reason to file a bug under &lt;em&gt;not urgent&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;On 21 October, two bugs that start exactly that way were published as critical. Both let someone on the internet, with no account and no password, run commands on the server. Both have been in PHP since version 5.4.&lt;/p&gt;

&lt;p&gt;They are called PHP2Shell. Wiz Research, who found them, demonstrated working attacks against WordPress, Drupal and Joomla.&lt;/p&gt;

&lt;p&gt;How they work is worth following even if you never touch PHP, because the same shape turns up anywhere code handles one request after another.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why running out of memory is dangerous
&lt;/h2&gt;

&lt;p&gt;PHP does not start a fresh process for every visitor. It keeps a pool of workers alive, and each handles request after request. Between them, PHP throws away the memory that request used and starts clean. That cleanup is the only reason one visitor's data does not end up in the next visitor's page.&lt;/p&gt;

&lt;p&gt;Running out of memory interrupts the cleanup halfway. The memory gets thrown away. But some of PHP's long-lived variables are still pointing at it. The request is dead; the pointers it left behind are not. Whoever lands on that worker next inherits them.&lt;/p&gt;

&lt;p&gt;That is the whole trick, in four different places.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2ni8p0ewqkzk0scpzcnb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2ni8p0ewqkzk0scpzcnb.png" alt="One PHP worker handling two requests: on the normal path globals are cleared with the heap, on the OOM path a global still points into freed memory" width="800" height="350"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The same worker serves the next visitor. Source: Wiz Research, PHP2Shell.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Take the clearest one. While PHP receives an uploaded file, it records the progress in the session. Make it run out of memory right there, and the pointer to the session ID is left aimed at memory that has already been freed.&lt;/p&gt;

&lt;p&gt;Freed memory does not stay empty. The attacker sends more requests to refill it with bytes of their choosing, then sends one more upload. The worker follows that old pointer, reads whatever sits there now, and writes it into the session cookie it sends back.&lt;/p&gt;

&lt;p&gt;You make a request. The server replies with a piece of its own memory, in a cookie. Wiz notes this works even on sites that never start a session.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr4goz05aiwuafynzfkm2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr4goz05aiwuafynzfkm2.gif" alt="Five steps: an OOM frees the session heap while the session ID pointer still points at it, the attacker refills that memory, and a later request returns those bytes in the PHPSESSID cookie" width="800" height="357"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Server memory, handed back in a cookie. Source: Wiz Research, PHP2Shell.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That gets the attacker information. The same memory error leaves a second thing behind: the bookkeeping PHP uses to rebuild saved objects from text. Someone who controls the freed memory controls structures PHP still believes are real. The leak tells them where things sit; the stale bookkeeping lets them corrupt it. Together: code execution, no login.&lt;/p&gt;

&lt;p&gt;The second CVE is the same idea in two more spots. PHP keeps a pointer to the last crash report and forgets to clear it, so the next request that prints a warning follows it somewhere it should not. And in OPcache, the shared cache of compiled scripts, a failed allocation can leave one worker handing another a pointer to the wrong kind of object — inside memory every worker shares.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that should bother you
&lt;/h2&gt;

&lt;p&gt;A PHP core developer saw the first signs of this in 2024, during an earlier security audit, and could not turn it into a working attack. A fair call at the time.&lt;/p&gt;

&lt;p&gt;Then in August 2026, three separate parties reported the same underlying bug within two weeks: Calif, OpenAI and Wiz. Wiz credits AI models for making the work practical, though that is its own account, not a measurement anyone else checked.&lt;/p&gt;

&lt;p&gt;The PHP Foundation's write-up is more useful. Its security team decides by consensus. That stalled the fix for months, and the Foundation calls it a "structural failure." It is switching to majority votes. It has also published 15 CVEs since 1 July while sorting through more than 100 invalid or duplicate reports.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Update to 8.5.12, 8.4.27, 8.3.36 or 8.2.35. If someone else hosts your PHP, check they have. Sansec published stopgap mitigations, including blocking file uploads at the web server, if you cannot update today.&lt;/li&gt;
&lt;li&gt;Then look at your own code that outlives a single request: thread pools, reused serverless containers, anything pooled. When a request dies halfway through, what does it leave behind? Same bug, different language.&lt;/li&gt;
&lt;li&gt;Stop treating "nobody has exploited it yet" as a severity input. It measures how much effort was available the day someone triaged it. That number has moved.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where this breaks down
&lt;/h2&gt;

&lt;p&gt;These attacks need things to line up: a way to force the memory error, the right configuration, and the right timing to win the race for freed memory. "Decades old and exploitable today" is true. "Trivially exploitable everywhere" is not, and I have not seen a public exploit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Error paths are code too. Just the code nobody profiles, nobody fuzzes hard, and nobody reads twice.&lt;/p&gt;

&lt;p&gt;Which error path in your system have you never actually watched run?&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://thephp.foundation/blog/2026/10/21/todays-php-security-release-and-how-were-strengthening-php-security/" rel="noopener noreferrer"&gt;The PHP Foundation: today's security release and how we're strengthening PHP security&lt;/a&gt; — 21 Oct 2026&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.wiz.io/blog/php2shell-php-rce" rel="noopener noreferrer"&gt;Wiz Research: PHP2Shell&lt;/a&gt; — Oct 2026&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sansec.io/research/php2shell" rel="noopener noreferrer"&gt;Sansec: PHP2Shell research notes and mitigations&lt;/a&gt; — 21 Oct 2026&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>php</category>
      <category>security</category>
      <category>webdev</category>
      <category>architecture</category>
    </item>
    <item>
      <title>How a Key Leaked in May Shipped Malware in October</title>
      <dc:creator>Dhruv Agrawal</dc:creator>
      <pubDate>Mon, 26 Oct 2026 09:44:07 +0000</pubDate>
      <link>https://dev.to/dhruv1206/how-a-key-leaked-in-may-shipped-malware-in-october-1nkl</link>
      <guid>https://dev.to/dhruv1206/how-a-key-leaked-in-may-shipped-malware-in-october-1nkl</guid>
      <description>&lt;p&gt;On 27 May 2026, someone committed a config file to a public repository by mistake. Inside it was the master key — the admin password, in effect — for one of LiteLLM's internal services. They rewrote the git history to hide it, and everyone moved on. Nobody switched the key off.&lt;/p&gt;

&lt;p&gt;It was first used on 10 October. That is 136 days later.&lt;/p&gt;

&lt;p&gt;Read what follows slowly, because nothing in it is clever. No bug, no zero-day. Every step is a key or token that still worked long after anyone needed it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The chain
&lt;/h2&gt;

&lt;p&gt;LiteLLM is an open-source gateway: one service your apps talk to instead of calling OpenAI, Anthropic, Bedrock and the rest directly. It routes requests, tracks spending and manages keys — which also means it holds every AI provider key a company owns.&lt;/p&gt;

&lt;p&gt;The leaked key unlocked an internal tool for letting AI agents open pull requests. When the attacker called it, the tool handed back a GitHub token belonging to an account that owned the whole organisation. Nobody had limited what that token could do. LiteLLM's write-up says it "was not associated with a service account and was not scoped down." It could do anything, anywhere in their GitHub.&lt;/p&gt;

&lt;p&gt;With that token, the attacker pushed a throwaway job into LiteLLM's main repository. GitHub Actions jobs can read the repository's secrets. So this one read them and posted them to a URL the attacker controlled: six, including a second maintainer's personal token. A second job took a third maintainer's. Both were deleted minutes later. Two days on, another round took fourteen more from the private repositories, including Anthropic, OpenAI and Bedrock keys.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fibttif37nbr6anqcoqzb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fibttif37nbr6anqcoqzb.gif" alt="Six-step chain from a key leaked on 27 May to a malicious release on 21 October, with no exploit at any step" width="799" height="346"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Each step is a credential that outlived its purpose. Source: LiteLLM postmortem, 23 Oct 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then they waited nine days.&lt;/p&gt;

&lt;p&gt;On 21 October they pointed LiteLLM's release pipeline at a fake copy of the repository: &lt;code&gt;Berri-AI/litellm&lt;/code&gt;, with a hyphen, instead of the real &lt;code&gt;BerriAI/litellm&lt;/code&gt;. Branch protection blocked the change. The owner token overrode it.&lt;/p&gt;

&lt;p&gt;Publishing a release takes two people: one to start it, one to approve. The approver is normally a locked-down account tied to an office machine. That rule had been relaxed days earlier so any two engineers could do it. The attacker held three maintainers' tokens, so they started the release with one and approved with another — LiteLLM thinks to keep the audit log looking ordinary.&lt;/p&gt;

&lt;p&gt;The poisoned version went up on PyPI and Docker. A researcher spotted it twenty-seven minutes later. PyPI pulled it within two hours.&lt;/p&gt;
&lt;h2&gt;
  
  
  The part that catches people
&lt;/h2&gt;

&lt;p&gt;The bad package installed three files. One is &lt;code&gt;cook.pth&lt;/code&gt;, and that extension is the whole story.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;.pth&lt;/code&gt; file in &lt;code&gt;site-packages&lt;/code&gt; normally just lists extra folders for Python to search. But it has an old compatibility feature almost nobody thinks about. If a line in that file begins with &lt;code&gt;import&lt;/code&gt;, Python runs it. Not when you import the package — at startup. The docs say these lines "are always run at Python startup... regardless of whether a particular module is actually going to be used."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgbh9txm4zyzh6ez5lble.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgbh9txm4zyzh6ez5lble.png" alt="Two flows compared: the expected one where malicious code runs only on import, and the real one where the site module executes the .pth import line at interpreter startup" width="799" height="532"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A &lt;code&gt;.pth&lt;/code&gt; line beginning with &lt;code&gt;import&lt;/code&gt; is run by Python's &lt;code&gt;site&lt;/code&gt; module at startup. Source: Python docs, &lt;code&gt;site&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So the stealer never waited to be imported. It ran whenever any Python process started on that machine: your script, a test run, an unrelated project.&lt;/p&gt;

&lt;p&gt;Including this one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; litellm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pip&lt;/code&gt; is itself a Python program. Running it starts Python, which runs the stealer again, before the upgrade fixes anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do this week
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Find the files without starting Python: &lt;code&gt;find / -name 'cook.pth' -o -name 'sitecustomize.py' 2&amp;gt;/dev/null&lt;/code&gt;. Delete them and any matching &lt;code&gt;.pyc&lt;/code&gt; files before you run pip.&lt;/li&gt;
&lt;li&gt;Assume everything on that machine is gone, not just the AI keys. The stealer read cloud credentials, SSH keys, browser data, your database URL, and the config files for Codex, Claude, Gemini and Cursor.&lt;/li&gt;
&lt;li&gt;Pin container images by digest, not tag. &lt;code&gt;latest&lt;/code&gt;, &lt;code&gt;main-latest&lt;/code&gt; and &lt;code&gt;main-stable&lt;/code&gt; all pointed at the bad build.&lt;/li&gt;
&lt;li&gt;Go find keys nobody needs any more. That is the root cause, and the only item here you can act on before an incident rather than after.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where this breaks down
&lt;/h2&gt;

&lt;p&gt;Calling this "just credentials" undersells one thing. The relaxed two-person rule mattered. Had the approver still been that locked-down office machine, three stolen tokens would not have been enough. The control was doing real work, right up to the moment someone loosened it for convenience.&lt;/p&gt;

&lt;p&gt;LiteLLM has said some of its early updates were wrong and more changes are coming, so treat this timeline as the best account so far.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Revoking a leaked key is not cleanup you do later. It is what decides whether a mistake in May becomes malware in October.&lt;/p&gt;

&lt;p&gt;What is the oldest credential still working in your infrastructure, and would you know if someone used it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.litellm.ai/blog/security-incident-oct-21-2026-attack-chain" rel="noopener noreferrer"&gt;LiteLLM postmortem: v1.107.0 compromise attack chain&lt;/a&gt; — 23 Oct 2026&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/BerriAI/litellm/issues/47826" rel="noopener noreferrer"&gt;GitHub issue #47826, Upwind research&lt;/a&gt; — 21 Oct 2026&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://socket.dev/blog/litellm-attack-timeline" rel="noopener noreferrer"&gt;Socket: attack timeline, workflows 11 days earlier&lt;/a&gt; — 23 Oct 2026&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.python.org/3/library/site.html" rel="noopener noreferrer"&gt;Python docs: the &lt;code&gt;site&lt;/code&gt; module and &lt;code&gt;.pth&lt;/code&gt; files&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>security</category>
      <category>python</category>
      <category>devops</category>
      <category>ai</category>
    </item>
    <item>
      <title>Most Package Managers Won't Stop a Tampered Download by Default</title>
      <dc:creator>Dhruv Agrawal</dc:creator>
      <pubDate>Fri, 23 Oct 2026 17:22:37 +0000</pubDate>
      <link>https://dev.to/dhruv1206/most-package-managers-wont-stop-a-tampered-download-by-default-1a67</link>
      <guid>https://dev.to/dhruv1206/most-package-managers-wont-stop-a-tampered-download-by-default-1a67</guid>
      <description>&lt;p&gt;Keegan Ryan of the security firm Trail of Bits set up a fake compromised package server and pointed 25 popular package managers at it. Two of them, Go and vcpkg, verified every package they downloaded, even without a lockfile. Without one, the other 23 installed whatever they were given.&lt;/p&gt;

&lt;p&gt;Lockfiles didn't rescue most of them either.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test
&lt;/h2&gt;

&lt;p&gt;Ryan routed each tool's traffic through a proxy that could rewrite downloads, then installed quickstart projects with default settings, with and without a lockfile. An attack counted as a success if the injected code ran. The threat model assumes any web-facing server, such as a registry or a mirror, can be compromised, but backend secrets like private signing keys can't.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lockfile is the safety net, and it's mostly unplugged
&lt;/h2&gt;

&lt;p&gt;A lockfile pins every dependency's exact version and, ideally, a hash: a fingerprint the download has to match. Fewer than half of the 25 tools checked those hashes by default.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feam9lqktcxk37xxy4kxy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feam9lqktcxk37xxy4kxy.png" alt="Hand-drawn grid of 25 package managers colored by what they verify: Go and vcpkg always verify; nine verify when a lockfile is present; five need extra steps; nine don't verify or the check doesn't apply." width="800" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;All 25 package managers, by default behavior. Source: Trail of Bits, October 14, 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Nine verify when a lockfile is present, including npm, pnpm, Cargo, uv and Yarn. Five can verify, but only with extra steps. The last nine don't verify lockfile hashes at all, or the check doesn't apply to them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checks that don't stop anything
&lt;/h2&gt;

&lt;p&gt;Tools with no check are the obvious failures. The sneakier ones run a check and install anyway:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pub&lt;/strong&gt; (Dart and Flutter) detects a hash mismatch, installs the package anyway and updates the lockfile to match, unless you pass &lt;code&gt;--enforce-lockfile&lt;/code&gt;. Some Flutter commands run &lt;code&gt;pub get&lt;/code&gt; too, and can overwrite the lockfile unless called with &lt;code&gt;--no-pub&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pnpm&lt;/strong&gt; overwrote the lockfile on a mismatch until version 11.4, in May 2026, so the tampered package simply became the new truth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composer&lt;/strong&gt; (PHP) locks a Git commit, then downloads a ZIP archive it never checks against that commit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CocoaPods&lt;/strong&gt; hashes the package spec but fetches the source by Git tag, which a compromised host could serve with different contents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;cpanminus&lt;/strong&gt; (Perl) still connects to its repository over plain HTTP by default.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkzqp0elge66dgekyxv23.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkzqp0elge66dgekyxv23.png" alt="Diagram of what happens when a download doesn't match the lockfile: stop the install, warn and install anyway, rewrite the lockfile to match, or install with nothing to check." width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Four outcomes when the download and the lockfile disagree. Only the green one protects you.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the defaults are like this
&lt;/h2&gt;

&lt;p&gt;None of this is hidden; it's documented behavior. Lockfiles were built for reproducible installs, and as Ryan notes, reading them through a supply-chain lens "is a recent interpretation that can be at odds with their original design." Strict pinning also has a cost: every update, security fixes included, needs a lockfile change.&lt;/p&gt;

&lt;p&gt;Go shows the alternative. It checks every hash against a signed checksum database that works like a transparency log, which makes targeted tampering much harder, at the cost of concentrating trust in a single private key. It was the only tool that verified every download and checked signatures by default. As Ryan puts it, "Package signing is in a similar state now to where HTTPS was a decade ago."&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do this week
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Commit your lockfile&lt;/strong&gt;, at least for applications. Norms for libraries vary by ecosystem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Install in strict mode in CI:&lt;/strong&gt; &lt;code&gt;npm ci&lt;/code&gt;, &lt;code&gt;pnpm install --frozen-lockfile&lt;/code&gt;, &lt;code&gt;uv sync --locked&lt;/code&gt;, &lt;code&gt;pip install --require-hashes -r requirements.txt&lt;/code&gt; (with hashes generated by &lt;code&gt;pip-compile --generate-hashes&lt;/code&gt;) or &lt;code&gt;dart pub get --enforce-lockfile&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If your tool needs extra steps, take them:&lt;/strong&gt; the conda-lock library for Conda, a &lt;code&gt;verification-metadata.xml&lt;/code&gt; file for Gradle, the &lt;code&gt;RestorePackagesWithLockFile&lt;/code&gt; property for NuGet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If your tool is in the last group, don't treat its lockfile as a security control.&lt;/strong&gt; Maven can check signatures against trusted keys with the third-party &lt;code&gt;pgpverify-maven-plugin&lt;/code&gt;. Beyond that, the fallback Conan's developers suggest applies widely: if integrity is critical, run your own repository server.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then test it: change one hash in your lockfile on purpose and confirm CI fails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;p&gt;Each tool was tested with one package, one version and one platform, Ubuntu, and some setups, like PowerShell and CocoaPods on Ubuntu, don't reflect typical use. The study shows that reasonable defaults can leave you exposed, not that every setup is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verification comes before detection
&lt;/h2&gt;

&lt;p&gt;Most supply chain tooling hunts for packages already known to be bad. This study is about the step before that: making sure the package you got is the package you asked for. For most of the 25 tools, that check is off by default or missing. Where it exists, turning it on is about the cheapest security win you'll find.&lt;/p&gt;

&lt;p&gt;If you check what your package manager does on a hash mismatch, I'd like to hear what you find.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://blog.trailofbits.com/2026/10/14/package-managers-have-an-integrity-problem/" rel="noopener noreferrer"&gt;Package managers have an integrity problem&lt;/a&gt;, Keegan Ryan, Trail of Bits, October 14, 2026&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>opensource</category>
      <category>programming</category>
    </item>
    <item>
      <title>Anthropic Replaced Git. The Interesting Part Is the Merge Queue.</title>
      <dc:creator>Dhruv Agrawal</dc:creator>
      <pubDate>Mon, 19 Oct 2026 11:15:35 +0000</pubDate>
      <link>https://dev.to/dhruv1206/anthropic-replaced-git-the-interesting-part-is-the-merge-queue-5520</link>
      <guid>https://dev.to/dhruv1206/anthropic-replaced-git-the-interesting-part-is-the-merge-queue-5520</guid>
      <description>&lt;p&gt;In May 2026, Anthropic merged about 2,500 changes a day into its main codebase. By September it was more than 30,000. Anthropic says Claude's ability to write code is "what put us in this situation in the first place." Git stopped fitting, so they replaced it.&lt;/p&gt;

&lt;p&gt;You probably don't need to replace Git. But one decision in their write-up applies to almost any team adding coding agents, and it's about the merge queue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9svbtwqvvd1mypol9sls.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9svbtwqvvd1mypol9sls.png" alt="Hand-drawn bar chart: Anthropic's merges per day rose from about 2,500 in May to over 30,000 in September 2026, a 12x jump." width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Merges per day in Anthropic's monorepo, May vs. September 2026. Source: Anthropic Engineering.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Git stopped fitting
&lt;/h2&gt;

&lt;p&gt;Git assumes clients that keep a full copy of the history and work offline. That's perfect for people. Anthropic's clients are mostly machines: "nearly all of our clients were agents and jobs that are online by definition." They run on the order of 25 million CI checkouts a day against a main-branch history of almost 100 GB, and at the peak it took several hundred internal Git mirrors to keep up.&lt;/p&gt;

&lt;p&gt;So a team of seven built Scion, which flips the model. The server creates commits, performs merges and rebases, and answers history questions, while clients fetch only what they need. Anthropic reports an infrastructure footprint 200x smaller in CPU and memory than the old mirrors, and CI checkouts in seconds instead of minutes. The migration took five months.&lt;/p&gt;

&lt;p&gt;The write-up is just as clear that Scion is "by no means a suitable replacement for git for general use." It only makes sense for enormous monorepos, and it needs its own code review, code search and CI.&lt;/p&gt;

&lt;h2&gt;
  
  
  The merge queue decision
&lt;/h2&gt;

&lt;p&gt;A merge queue is the line in front of main. Each change is tested against the latest main plus everything queued ahead of it, then lands. The industry-standard version holds a global lock on the repository while it does that, and it's how most teams keep main green.&lt;/p&gt;

&lt;p&gt;Anthropic writes that a proper merge queue "doesn't make much sense at agent scale," and asks: "What does CI even mean when hundreds or even thousands of changes can merge while tests are running?" They couldn't make the global lock work at their volume, so instead of making the line faster, they moved the check. They "invested in post-merge and pre-deploy automated testing." The gate that matters now sits before production, and when concurrent changes conflict, Claude can typically fix them faster than a person would notice.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpofagnw42h6gtzd4y1a3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpofagnw42h6gtzd4y1a3.png" alt="Diagram comparing a classic merge queue, where changes wait one at a time behind a global lock, with Anthropic's setup, where the queue drops the global lock and tests run after merge and before deploy." width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Before: changes wait one at a time behind a global lock. After: no global lock, and the real gate sits before deploy.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Find your one-at-a-time step
&lt;/h2&gt;

&lt;p&gt;Every delivery pipeline has a step where changes wait for each other: the merge queue, a required human review, a single shared staging environment. When agents multiply how much code your team produces, that step hurts first.&lt;/p&gt;

&lt;p&gt;The reflex is to make it faster with bigger runners, more reviewers or another staging box. Sometimes that's enough. The better question is the one Anthropic asked: does this check need to sit here at all? Moving a gate to where it doesn't block everyone can beat any speedup.&lt;/p&gt;

&lt;p&gt;To find yours, trace one change from commit to production and circle every place where it waits for another.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this breaks down
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scale.&lt;/strong&gt; Most teams will never see 30,000 merges a day, and Anthropic admits its "ability to spend tokens for internal use far exceeds what any other business would realistically want to spend." If your queue isn't hurting, leave it alone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Risk.&lt;/strong&gt; Testing after merge means main can be broken for a while. That only works if pre-deploy checks are reliable and conflicts get fixed fast.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source.&lt;/strong&gt; Every number here comes from Anthropic's own engineering blog, with no independent measurement.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The real bottleneck
&lt;/h2&gt;

&lt;p&gt;Strip out the version control details and this is a story about one part of a system getting 12x faster while the rest didn't. Anthropic expects this class of problems to become "more commonplace over the next six to 12 months." Find your one-at-a-time step before then.&lt;/p&gt;

&lt;p&gt;Where is it in your pipeline: the merge queue, code review, or somewhere else?&lt;/p&gt;

&lt;h3&gt;
  
  
  Sources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://anthropic.com/engineering/redesigning-source-control" rel="noopener noreferrer"&gt;Redesigning source control at Anthropic&lt;/a&gt;, Anthropic Engineering, October 13, 2026 (updated October 15, 2026)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>git</category>
      <category>devops</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
