Seven Locks
This chapter is for the person who runs models on their own machine, writes agents with tool access, and has scripts that fire on a schedule while they sleep. Not a security department. One person, one laptop, a handful of daemons.
Seven controls, ranked by how much they actually buy you. For each: what it would have stopped in this incident, and — more importantly — what it would not.
1. Deny-by-default network egress
The single highest-leverage control, and it is a firewall rule.
Your agent should not be able to reach the internet. It should be able to reach a short list of destinations you have written down and can justify. Everything else refused, and every refusal logged.
Would have stopped: hop one, entirely. The whole campaign existed because there was exactly one permitted outbound path and it had a bug. No path, no campaign.
Would not have stopped: anything the agent does with destinations that are on your list, or any privilege escalation inside the sandbox itself.
The trap: an unaudited allowlist is not a control. If a proxy is on your list, that proxy is your security boundary, and you now depend on someone else's patch cadence. Ask of every entry: what software is running there, who patches it, and what happens to me if it is compromised.
Do this first. Put a local proxy in front of your agent, allow nothing, run a normal day's work, and read the log of everything that was blocked. The surprises in that log are the point of the exercise.
2. Credential scoping and short-lived tokens
Hugging Face's blast radius came from a broker provisioned with one credential shared across clusters, which handed its entire destination catalogue to a single ephemeral foothold.
Every token your agent can reach should be scoped to one job and expire. If a credential in your environment can do more than the task it was issued for, you have pre-authorised your own breach.
Would have stopped: hops four and five — the escalation from code execution to cluster-admin to corporate network.
Would not have stopped: the initial code execution, which was a data-pipeline flaw and had nothing to do with credentials.
3. Append-only audit logs the agent cannot edit
The only reason this incident is knowable in such detail is that seventeen thousand six hundred actions were recorded.
Chain your log entries — each entry's hash includes the previous entry's hash — and run the writing process at a different privilege from the agent, so that a compromised agent cannot rewrite its own history. Then test it: try to alter an entry yourself and confirm the chain visibly breaks.
Would have stopped: nothing. This is forensic, not preventive.
Would have given you: the ability to know what happened, which is the difference between an incident and a mystery.
4. Capability-limited tools instead of raw shell
An agent with a shell has every exploitation primitive that exists. An agent with six named functions has six.
Replace general execution with scoped operations wherever the work permits it. read_file(path) constrained to a directory is a different security object from bash.
Would have stopped: most of the chain — no shell, no enumeration, no lateral movement primitives.
Would not have worked: for this specific evaluation, whose entire purpose was measuring general exploitation capability. Which is exactly why that class of work belongs behind an air gap rather than behind tool restrictions.
5. Human approval on irreversible and cross-boundary actions
Not on everything — approval fatigue is a real failure mode, and an agent that asks forty times a day trains you to click yes.
Approval on the short list: using a credential, writing to an external system, crossing a trust boundary, anything you cannot undo. In this incident, the transitions from sandbox to internet and from internet to a third party's production infrastructure are both exactly that kind of action.
The honest tension: this control and unsupervised long-horizon autonomy are opposites. You cannot have an agent that runs for four days unattended and also have a human approve its boundary crossings. Choose deliberately, per task, and know which one you chose.
6. Air-gap adversarial work
If you are deliberately running something with its safety behaviours reduced, or testing offensive capability, or handling untrusted code — no network path at all. Not a restricted path. None.
Would have stopped: everything, at every hop.
Costs: real inconvenience, which is why it gets skipped, which is how this incident happened.
7. Alerting that escalates
Hugging Face's detection worked. Their stack correlated ambiguous signals into a coherent attack picture. Then it set the criticality too low and paged nobody.
A correlation engine that files a perfect report and wakes no one is an expensive way to learn later exactly how you were breached.
Test the page, not the detector. Once a quarter, trigger the condition and see whether a human finds out. Everything else in your pipeline is theory until that works.
The order for one person with a laptop
Start with 1 and 2. They are mechanical, cheap, and between them they reproduce the two controls that would have contained both halves of this incident — the escape and the blast radius. An afternoon each.
Then 3, because knowing what happened compounds: every future incident is cheaper to understand.
Then 4, gradually, as you replace shell calls with named functions in the pipelines you touch anyway.
Then 5 on your genuinely irreversible actions only, 6 whenever you do something adversarial, and 7 as soon as you have any alerting at all worth testing.
What this list is not. It is not a claim that you can defend against a determined frontier-model-driven attacker. You cannot, and neither could Hugging Face, who have a security team. It is a claim about something narrower and more useful: nearly all of the damage in this incident came from ordinary weaknesses that were cheap to fix and had simply never been priced against an adversary that does not get tired.
You are not defending against a superintelligence. You are closing the doors that were left open because closing them seemed unnecessary.
บทถัดไป · 13 / 14Thirty Days