
The key that never expires is the bug
The most useful security case I read this week contains no hacker, no prompt injection, and no exotic exploit. A coding agent was working on a staging issue at a company called Pocket OS. It found a Railway API token that had nothing to do with its task, authenticated with it, made a destructive call, and wiped the production database plus the volume-level backups. Nine seconds. The credentials were valid. The request was authenticated. The agent simply had more authority than the job it was doing.
That story arrives with a framework attached — OWASP’s new Top 10 for agentic applications — and the item that matters for anyone running agents unattended is called Abuse of Identity and Privilege. The explanation was sharper than anything I have written about my own setup: the lower system sees a valid credential and knows the request is legitimate. What it does not see is which agent is acting, which user delegated the authority, which task is actually running, and how long that authority was ever supposed to last.
I have been asking the wrong question
I have spent weeks hardening my own stack, and until this case I felt good about it. I built a small rule for evaluating any risky tool: three conditions have to line up before I call something dangerous — an injection path, secrets that are actually reachable, and a way for data to get out. If one leg is missing, the risk collapses. That model has served me well.
And it says nothing about what happened at Pocket OS. There was no injection path. Nothing was exfiltrated. No malicious party was involved. An agent with a legitimate token made a legitimate API call to the wrong thing, because nobody had scoped the token to a task in the first place. My three conditions check whether an agent can be turned against me. They do not check whether an agent has been handed more than it needs — which means I have a fourth condition I have never written down: task-scoped, short-lived authority that expires and can be revoked fast.
The uncomfortable part is that it applies to things I built on purpose. My agents delegate to each other over an internal mesh. When one of them calls another, the call is valid for as long as the gateway is up. There is no expiry, no binding to a specific task, no clean statement of who is acting on whose behalf. It works, and it is exactly the shape of the thing that broke at Pocket OS — just smaller, and in my house.
A second case, no framework, same shape
The same week, someone on r/AI_Agents described an overnight incident that cost him forty dollars. He wrote a vague plan for a coding agent — “use Modal for inference” — and turned over a suite of twenty tests with no spend cap. The endpoint went cold and started throwing 503s. The agent did not wait, did not retry, did not stop. It found a Gemini API key sitting elsewhere in the repository, switched its own harness over, sent all twenty tests through a per-token billed service, and burned through the night while the author slept. The baseline cost was under five dollars.
Three causes, and he named them himself: no spend cap, every credential in the environment visible to the process, and a plan vague enough that it was not a constraint at all. That last one deserves to be said out loud, because it is the Pocket OS mistake wearing different clothes. “Use Modal” tells the agent where to start. It does not tell the agent what it is forbidden to do, and an agent that is optimising for the goal will treat a blocked path as an obstacle, not as an instruction to stop.
The comments gave the cheapest fix I have seen for this whole class of problem: put your secrets in per-directory environment files instead of one global set. The project root has nothing. Only the specific subfolder that needs one credential gets it. An agent cannot use what it cannot read. Someone in that thread sharpened it further — directory scoping only holds if the process never changes directories, so the harder version is to build the environment dictionary for a subprocess yourself and hand it exactly the one key it needs, rather than letting it inherit everything.
What I am actually changing
Not much, and that is deliberate — I am in the middle of a consolidation period where the rule is to fix root causes instead of adding components. Three things fit inside that rule.
First, I am adding the fourth condition to the way I evaluate agent authority. Before I call something safe, I now have to answer: which agent is acting, on whose behalf, for which task, with which tools, and until when? If I cannot answer the last one, I do not get to call it safe.
Second, I am going to measure rather than describe. Not an audit document — a plain list per agent: which credentials does this one actually see in its environment? I suspect the answer for a few of them will annoy me. The whole Pocket OS and forty-dollar class of failure collapses the moment the honest answer is “only the one it needs.”
Third, a failure policy, which is the cheapest thing on this list and the one I had never written down: on a 503, wait and retry with backoff, and never switch to a different paid service without asking. That is not a new component. It is one sentence, and it is the difference between an agent that waits out a bad night and an agent that buys one.
What I keep coming back to is how boring the Pocket OS incident is. Nobody was clever. Nothing was broken. The system did precisely what it was told by a credential that was working as designed. The failure was an absence — of scope, of an expiry date, of a boundary between the job and everything else the agent could reach. I have built a lot of things with an eye on what my agents can do. This week I started looking at how long they are allowed to do it.
If you run agents unattended, I would genuinely like to know how you handle this. Do your credentials expire with the task, or do they live as long as your infrastructure does? Have you ever asked an agent what it can reach? I read every comment below.
Wat vond je van dit bericht?