DEV Community

Cover image for Hours, Not Weeks: How AI Rewrote the Clock of Software Security

Hours, Not Weeks: How AI Rewrote the Clock of Software Security

James Anderson on October 24, 2026

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 f...
Collapse
 
pengeszikra profile image
Peter Vivo •

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.

Collapse
 
james_anderson_h profile image
James Anderson •

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.

Collapse
 
kami_butt_69b922ade8de012 profile image
Agentic Affairs •

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?

Collapse
 
james_anderson_h profile image
James Anderson •

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.

Collapse
 
kami_butt_69b922ade8de012 profile image
Agentic Affairs •

'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?

Thread Thread
 
james_anderson_h profile image
James Anderson •

That’s the one to drop. If nobody would notice it failing, nobody will notice it compromised either. Unwatched means unneeded.

Thread Thread
 
kami_butt_69b922ade8de012 profile image
Agentic Affairs •

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?

Thread Thread
 
james_anderson_h profile image
James Anderson •

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.

Collapse
 
ayoub_hammoudi profile image
Ayoub Hammoudi •

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.

Collapse
 
james_anderson_h profile image
James Anderson •

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.

Collapse
 
ayoub_hammoudi profile image
Ayoub Hammoudi •

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.

Collapse
 
gautamtalksdev profile image
Gautam K •

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.

Collapse
 
james_anderson_h profile image
James Anderson •

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.

Collapse
 
bloqarl profile image
Carlos (Bloqarl) •

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?

Collapse
 
james_anderson_h profile image
James Anderson •

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.

Collapse
 
paullovelace profile image
Paul Lovelace •

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?

Collapse
 
james_anderson_h profile image
James Anderson •

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.

Collapse
 
shuttlewright profile image
Shuttlewright •

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.

Collapse
 
james_anderson_h profile image
James Anderson •

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.

Collapse
 
ascendra_ventures_92c21f2 profile image
Ascendra Ventures •

The asymmetry you describe is the real change. Attacking got cheaper and faster, while defending still depends on a small team knowing what it runs. Gautam's point about how many minutes it takes to answer "are we affected?" is the measure I would track before buying any new tool.

Shuttlewright's comment that an inventory beats a faster scanner matches what we see in small companies. When an advisory like the LiteLLM package issue lands, the slow part is finding every service, container and CI job that pulls that dependency, and every environment variable it could read. A single list of services, owners and secrets turns that into a search.

Paul Lovelace's suggestion to shrink the surface is the cheapest defence of all. The "unwatched means unneeded" rule from the thread is a good test: if nobody would notice a service going away, it probably should.

Did your team keep the secrets list in code or in a separate register?

Collapse
 
eye_java_420f1faa10ae8b86 profile image
Nan •

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.

Collapse
 
robertpaschal profile image
Odinaka Robert Nnamani •

Small teams shipping AI code without a security person is the real story here