The third week of October 2026 was a rough one for anyone who runs software on the internet.
Two critical bugs in PHP, nicknamed PHP2Shell, were found by security teams using frontier AI models. Estimates said they touched a huge share of the web. Governments told organisations to patch immediately, and plenty of people simply pulled their servers offline. In the same few days, LiteLLM had a malicious release pushed to PyPI (for the second time this year), Railway disclosed that attackers had gotten into customer environment variables, GitLab shipped fixes for maximum-severity bugs, and a handful of Linux kernel vulnerabilities got posted to the wrong mailing list.
Drew Silcock wrote a great roundup of all of it in Is this the new cyber normal?, and it's worth reading in full.
But after going through it, the thing that stuck with me wasn't any particular bug. It was a simpler thought: who is actually supposed to deal with all this when your entire company is four people?
The clock got shorter
Security used to have a rhythm. A vulnerability got announced, a patch came out, and you had some time. Maybe days, sometimes weeks, before anyone figured out how to actually exploit it. "We'll patch it this week" was a reasonable plan.
That rhythm is gone. Today, the moment a security patch is published, people point AI agents at the code. The agent reads through the changes, spots what looks like a security fix, works backwards to the hole it closes, and helps build an exploit. What used to take a skilled researcher weeks can now happen in hours.
With PHP2Shell, attacks were reportedly happening before the official patch was even available. "This week" isn't a plan anymore. "Today" barely is.
Attacking got cheap
Here's the part that changes everything. Finding a serious bug and turning it into a working attack used to require rare skills and a lot of time. That made it expensive, and expensive things happen less often.
Now an attacker can throw an AI model at a codebase and see what comes out. A lot of it is junk. AI produces plenty of "exploits" that don't actually work. But attackers don't need everything to work. They need one thing to work. When trying costs almost nothing, you can afford to try a thousand times.
Defending didn't get cheap
On the other side, almost nothing got cheaper.
When a critical bug drops, somebody still has to read the advisory, figure out whether you're affected, check which of your services use the vulnerable thing, test the patch, deploy it, and then dig through logs to see if anyone got in before you fixed it. If you use AI to scan your own code first, great, but that costs money too, and every bug it finds is a bug someone now has to fix.
AI made finding problems cheaper for everyone. It didn't make fixing them cheaper for anyone. And the attacker only has to find. You have to fix.
This is where size starts to matter
A big company handles a week like that with a security team, an on-call rotation, incident playbooks, and people whose literal job is to drop everything and respond. It's painful for them too, but they're built for it.
A small team isn't. In a four-person startup, the person who handles security is usually also the person who handles deployments, the database, customer support tickets, and the feature that was supposed to ship Friday. There's no rotation. There's just whoever happens to be awake.
And when the advice is "take your servers offline until you patch," the cost hits differently. A large company can survive a day of downtime. A small product with a few hundred paying customers might lose some of them for good.
You inherit everyone else's problems
Small teams don't build everything themselves. They can't. They stand on a stack of frameworks, packages, hosting platforms, and AI tools, all written by someone else.
That's normally a strength. It's how a tiny team ships something real. But it also means that when one of those pieces gets compromised, the problem becomes yours instantly. You didn't write the LiteLLM backdoor, but if you installed that release, your credentials might be gone. You didn't misconfigure Railway, but if your secrets lived there, you now have to rotate every one of them.
For a big company, that's a ticket for the security team. For a small team, it's an entire day of product work gone, spent cleaning up a mess you didn't make.
The tools we adopted to move faster
There's an irony here. Small teams have been some of the fastest to adopt AI tools, because AI promises exactly what they need: more output from fewer people.
But every one of those tools comes with something attached. Another API key. Another dependency. Another service with access to your code or your data. One of the incidents that week, a data leak at a database company, reportedly exposed API keys for several AI providers that had been pasted into internal tickets. Keys spread quietly, and every one of them is something an attacker would love to find.
The tools that help small teams keep up are also handing attackers more things to go after.
Falling behind isn't the same as not caring
I don't think small teams are ignoring security. Most developers I know care about it a lot. The problem is that the math changed underneath them.
Defending at the new speed assumes things a small team doesn't have: people on call around the clock, budget for scanning tools, time to audit every dependency, room to absorb downtime. When attacks got faster and cheaper but defense stayed slow and expensive, the gap didn't hit everyone equally. It landed hardest on the people with the fewest resources.
I don't have a neat answer
Maybe this is a temporary spike. Maybe AI is just clearing out years of old bugs all at once, and things will calm down once the backlog is gone. Or maybe this is simply what security looks like now.
Either way, it feels like we're in a period where the cost of defending software is rising quickly, and small teams are the ones feeling it first.
If you're on a small team, I'd genuinely like to know how that week went for you. Did you patch in time, pull things offline, or only find out afterwards?
Top comments (22)
I run into these security issues too, and it’s terrible to admit, but I don’t really find myself worrying about them. Maybe I don’t fully grasp how serious they are, because it’s very hard for me to assess. Say there’s a PHP vulnerability that attackers can exploit, and since a significant share of database-backed websites use PHP, they might gain access to some sites’ data. In the worst case, since we’ve digitalized absolutely everything, this could cause actual financial harm to someone who only got involved in the digital world out of necessity or convenience—which means almost everyone on this planet.
At the same time, there’s the AI question, which I think is orders of magnitude more urgent. That’s why I wrote Are we prompting our own extinction?, because I feel this is a more serious issue.
As a small business owner, when I build something as a hobby, I try to make things that don’t involve personal data. In fact, I’ve never built a program that required people to log in. For example, neither my implementation of the pipeline operator in TypeScript nor its sandbox requires a login. Since I suspected roughly nobody would use it, I simply added an AI with a $5 monthly spending limit. But I don’t collect any data from anyone.
So my first line of defense is building applications that are inherently safe. Of course, as this example already suggests, having an AI in the terminal answering questions introduces risks that I can’t fully assess either.
Thanks for the honesty. Building things that hold nothing worth stealing is one of the best defenses there is, and it’s underrated. Your $5 cap is smart too: it limits the damage even if the key leaks. The part I’d still watch is everything around the app, like the server, dependencies, and that API key, since attackers don’t always want your data; sometimes they want your machine or your credits. And I get why AI’s bigger risks feel more urgent. Maybe the two connect: the same speed that worries you long-term is already showing up in security today.
The asymmetry line is the one that lands hardest — AI made finding problems cheaper for everyone but fixing them cheaper for no one, and the attacker only has to find while you have to fix. For a four-person team that flips the priority: shrinking the patch queue matters more than speeding it up, which usually means fewer dependencies and more managed services, not better tooling. Do you think the realistic answer for small teams is auto-update everything and accept the breakage risk, or aggressively cut surface until the queue is survivable?
Cut surface first, then auto-update what’s left. Auto-updating a huge dependency tree just trades security breakage for random breakage. Fewer dependencies and managed services make the queue small enough that auto-updates become safe, and the few things you can’t cut get human eyes. Shrinking comes before speed.
'Shrinking comes before speed' is the right ordering — auto-update only becomes safe once the tree is small enough that a breakage is diagnosable, not just another alert in a queue you can't read. The human-eyes carve-out for what can't be cut is the part I'd keep explicit: that's usually where the real risk concentrates. What do you use as the cut test — if a dependency's failure wouldn't page anyone, does it stay, or is that exactly the one to drop?
That’s the one to drop. If nobody would notice it failing, nobody will notice it compromised either. Unwatched means unneeded.
That's a sharper cut test than I expected — unwatched doesn't just mean unneeded, it means unnoticed when compromised too. If a failure wouldn't page anyone, there's no one watching the compromise either. Does that rule hold for transitive dependencies you didn't choose, or is that where the cut test gets harder to apply?
That’s where it gets harder. You can’t drop a transitive dependency directly, only the parent that drags it in. So the test moves up a level: judge each direct dependency by its whole tree, not its own code. A small library pulling two hundred packages costs more than it looks.
Judging the direct dependency by its whole tree is the right move up a level — the small library's real price is the two hundred packages it drags in, including the ones nobody audits. The hard case is when the parent is worth it but one transitive branch isn't: then the choice is fork, pin, or replace the parent, and none of those are free. Do you have a tree-size or audit threshold that triggers that call, or is it a judgment call per dependency?
The real cost of a week like that is the 2am feeling of reading an advisory and not knowing if you're in it. One of PHP2Shell's crash paths ran through multipart parsing, so a site with no uploads could refuse multipart at the proxy in minutes and buy the hours the patch needs. Switching the trigger off beats patching fast.
That’s a practical point. Knowing which features you don’t use lets you shut whole attack paths off instantly. Turning things off is underrated, because nobody plans for it until 2am.
Nobody plans for it until 2am is the whole story. A kill switch you've never flipped is just a hope with a config key.
For a four-person team I'd aim at one number: how many minutes it takes to answer "are we affected?" Committed lockfiles plus dependency alerts on the repo turn that from reading the news into getting told, and only about things you actually run.
The other half is secrets. When an incident like Railway's says rotate, the slow part is finding every place a secret lives and what breaks when it changes. A short list per secret (where it's stored, what uses it, how to rotate it), written on a calm day, turns that 2am job into a checklist.
Measuring the minutes it takes to know if you’re affected is a brilliant metric, simple enough that a tiny team can track it. The secrets list is the part most people skip, and it’s what turns panic into routine. Writing it on a calm day is the whole trick. Both belong in every team’s README.
Smart contracts get the harshest version of this clock. You can't patch a deployed contract, the fix is an upgrade, and if that upgrade sits behind a timelock the new code is public for a day or two before it's live. With AI reading diffs in hours, that delay is now an exploit window.
Same idea as switching the trigger off above: pause first, then upgrade. Does your team have a switch like that for the SaaS side?
A timelock turning a safety feature into a public countdown is brutal. On the SaaS side, the closest equivalent is feature flags and a maintenance mode that can cut off a risky endpoint instantly. Most small teams only build that after their first incident, which is the problem.
The part that keeps rattling around my head is the asymmetry: attacking got cheap, defending didn't. A four-person company can't hire its way out of that, and it can't patch-everything-today either — there aren't enough hours in the day the advisory drops.
The only lever small teams actually have is shrinking the surface before the clock starts: auto-update the boring layers (base images, frameworks, the stuff nobody writes blog posts about), so human attention is reserved for the internet-facing and the weird. LiteLLM getting hit twice in one year is the perfect example — that's not an app you patch, that's a dependency you either pin and watch or replace.
Curious what your triage rule looks like in practice. When three criticals land the same morning, what decides the order — exposure, exploit maturity, or just whoever screams loudest?
Exposure first, always. If it isn’t reachable from the internet, it waits. Then exploit maturity: anything with public exploit code or active attacks jumps the queue. Loudness should count for nothing, though in practice it honestly often wins. Writing the order down beforehand is what stops the screaming from deciding.
The small-team framing is the right one, because the gap is rarely knowledge - it is calendar. When the time between disclosure and exploitation shrinks, the teams that cope are the ones that already know what they run. An accurate inventory beats a faster scanner: you cannot patch in hours what you cannot list in minutes. After that, the next highest-yield habit is a standing rule for what gets emergency treatment versus the normal cycle, decided in advance, not during the incident. Speed comes from preparation, not panic.
Well said. The problem is calendar, not knowledge. Deciding in advance what counts as an emergency is underrated; making that call at 2am, half-awake, is where small teams lose hours.
The part of your post that stuck with me is the sentence about digging through logs to see if anyone got in before the fix landed. Patching has an end state. That question usually has no answer, and not because nobody has time: a small team's logs are built for debugging, not for proving a negative. Two weeks of retention, request lines with no bodies, and no baseline to compare Tuesday against.
It is worth fixing before the next advisory rather than during it, and the bar is low. Pick the endpoints that parse untrusted input and record, for those only, source address, user agent, status and response size, kept long enough to outlast an average embargo. Put a counter on 4xx and 5xx volume per endpoint so a spike is visible without opening a log viewer. Then on advisory day, export those slices first thing, because retention rolls over before an investigation finishes. None of that makes you monitored. It makes "were we hit" a question you can answer with evidence instead of a shrug, which is the difference between closing an incident and closing a ticket.
Small teams shipping AI code without a security person is the real story here