[{"data":1,"prerenderedAt":214},["ShallowReactive",2],{"seo-verification":3,"blog-isolate-ai-coding-agents-on-a-vps-agents-md-injection-en":6},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"key":7,"data":8},"blog-isolate-ai-coding-agents-on-a-vps-agents-md-injection-en",{"id":9,"slug":10,"slugs":11,"title":15,"excerpt":16,"readTime":17,"views":18,"isPinned":19,"publishedAt":20,"updatedAt":21,"category":22,"categories":28,"featuredImage":30,"bgImage":31,"posterImage":32,"relatedSolution":30,"intro":33,"sections":34,"ctaTitle":151,"ctaBody":152,"ctaButton":153,"ctaUrl":154,"relatedPosts":155},522,"isolate-ai-coding-agents-on-a-vps-agents-md-injection",{"fr":12,"en":10,"ar":13,"es":14},"agents-de-code-ia-vps-agents-md-injection-isoler","عزل-وكلاء-البرمجة-بالذكاء-الاصطناعي-على-vps-agents-md","aislar-agentes-de-codigo-ia-en-un-vps-agents-md-inyeccion","Isolate AI Coding Agents on a VPS: Booby-Trapped AGENTS.md","A booby-trapped AGENTS.md or CLAUDE.md can steer your coding agent. Unprivileged user, filtered egress, no production secrets: the commands.",12,0,false,"2026-10-28T00:00:00+00:00","2026-10-29T03:10:22+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},1,"Artificial Intelligence","intelligence-artificielle","bg-purple-500\u002F10 text-purple-400","ia",[29],{"id":23,"name":24,"slug":25,"color":26,"icon":27},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Fagents-de-code-ia-vps-agents-md-injection-isoler-poster.svg","On 25 October 2026, BleepingComputer reported that a booby-trapped `AGENTS.md`, loaded automatically when an agent starts in the wrong folder, was enough to make Codex run commands. For an agency, the risk is measured by what the agent can reach: deploy keys, tokens, other clients' repositories. This guide builds a dedicated development VPS with an unprivileged user, a narrow project directory, no production secrets and restricted outbound traffic, then a review of instruction files before every session.",[35,39,49,52,55,86,122,125,128,132,135,142,145,148],{"type":36,"title":37,"body":38},"h2","What the Forever Security research shows","On 25 October 2026, BleepingComputer reported the work of Forever Security. Researchers found a booby-trapped `AGENTS.md` file in a customer's `~\u002FDownloads` folder, instructing a coding agent to install an npm package named \"function-color\". That folder was also the agent's working directory, and agents automatically load the instruction files of their working directory. The reconstructed scenario goes like this: a web page silently drops the file into the downloads folder, the user starts their agent in that folder, then the agent follows the instructions with the tools and rights it already has.\n\nOn OpenAI's Codex, the full chain, with a harmless payload (opening the calculator), succeeded in 90% of tests. With Claude Code, a booby-trapped `CLAUDE.md` redirected API traffic to a server controlled by the researchers, which exposed the OAuth token and allowed code execution in roughly 50% of tests. The researchers say there is nothing to patch on the OpenAI or Anthropic side: the agent works as designed.",{"type":40,"title":41,"items":42},"ul","What the study establishes, and what it does not say",[43,44,45,46,47,48],"**Established** — an instruction file placed in the working directory is enough to steer the agent, with no software flaw exploited.","**Condition** — the attack requires the user to start the agent in the folder concerned. According to BleepingComputer, OpenAI and Anthropic, for their part, recommend running the agent in the project directory.","**Harmless payloads** — the 90% and roughly 50% rates come from tests with a harmless payload; they do not measure real attacks and do not generalize to every tool.","**Unknown delivery** — the researchers could not determine how the customer's file reached their downloads: the web page remains a hypothesis.","**Cursor CLI** — mentioned as launched from that folder, but not described as tested.","**Our reading** — a cloned repository can contain its own instruction file. That is our inference, not a finding of the study.",{"type":36,"title":50,"body":51},"Running the agent in the right folder and approving commands: why a separate server?","Human approval and the tool's sandbox are real safeguards, and they should stay on: the source article recommends it too. Still, they depend on constant attention, a well-configured tool and a launch in the right folder. The day one of the three slips, the question becomes: what can the hijacked agent reach?\n\nOn a workstation, the answer is long: SSH keys, cloud tokens, other clients' repositories, a logged-in browser. On a dedicated VPS, it is short and you choose it. The server holds only the project, a minimal-scope token and an unprivileged user. It can be destroyed and rebuilt without touching your workstation. A VPS does not make the agent safe: it limits its blast radius, and that is the only promise of this guide.",{"type":36,"title":53,"body":54},"Prerequisites: what you need to build the VPS","Coding agents call a provider's API: the model's computation does not run on your server, so the VPS can stay modest. Plan for 2 vCPUs, 4 GB of RAM and 40 GB of disk for an ordinary Node or PHP project, more if you compile or run containers. You need root access, Debian or Ubuntu LTS, a dedicated IPv4 address and an SSH key to connect.\n\nOn the network side, a single inbound port is open: 22, for SSH. Outbound, the agent's user can only reach port 8888 on the loopback interface, where a proxy listens and relays to a short list of hosts. So note down before you start the hosts your tool needs: the npm registry, the provider's API, the project's Git repository. The commands below are written for Debian and Ubuntu, with `apt`, `ufw` and `nftables`, and assume an administration account with `sudo`, separate from the agent's user.",{"type":56,"title":57,"steps":58},"steps","Build the VPS in nine steps",[59,62,65,68,71,74,77,80,83],{"title":60,"body":61},"Create an unprivileged user","`sudo adduser --disabled-password --gecos \"\" agent`\n`id agent`\n`sudo -l -U agent`\nThe second command must not list the `sudo` group, and the third must answer that the user is not allowed to run sudo. The account has no password: you enter it with `sudo -iu agent` from your administration account. Plan one VPS per client, so that the `agent` user of a server only sees that client's projects.",{"title":63,"body":64},"Reserve one directory per project","`sudo install -d -o agent -g agent -m 700 \u002Fsrv\u002Fagents\u002Fprojet-a`\nAlways start the agent from this root, never from `\u002F`, `~` or a downloads folder: a broad parent directory loads instruction files you have not reviewed. To fetch the code, enter the agent's session then clone:\n`sudo -iu agent`\n`git clone \u003Curl-du-dépôt> \u002Fsrv\u002Fagents\u002Fprojet-a`\nUse a read-only deploy key, generated on this VM and specific to the project's repository.",{"title":66,"body":67},"Cut access to your keys","`sudo -iu agent printenv SSH_AUTH_SOCK`\n`echo 'AllowAgentForwarding no' | sudo tee \u002Fetc\u002Fssh\u002Fsshd_config.d\u002F10-agents.conf`\n`sudo sshd -t && sudo systemctl reload ssh`\nThe first command must print nothing: no SSH agent is forwarded into the coding agent's session. Connect to the VM with `ssh -o ForwardAgent=no`. Do not place any production deploy key on this server, nor any configuration file from another client.",{"title":69,"body":70},"Limit environment variables to the project","`sudo -iu agent`\n`install -m 600 \u002Fdev\u002Fnull ~\u002F.agent-env`\nThen edit `~\u002F.agent-env` to hold only the provider's token, with minimal scope, dedicated to this VM and revocable. Start the agent with an emptied environment, so it inherits no variable from your session:\n`env -i HOME=\"$HOME\" PATH=\"$PATH\" TERM=\"$TERM\" HTTPS_PROXY=http:\u002F\u002F127.0.0.1:8888 bash -c 'set -a; . ~\u002F.agent-env; cd \u002Fsrv\u002Fagents\u002Fprojet-a && exec claude'`\nReplace `claude` with your tool's command. If the tool signs in with an account (OAuth) rather than an API key, the token lives in the user's home directory: that is the token the study saw exposed with Claude Code. Prefer a dedicated API key, which you revoke when you destroy the server.",{"title":72,"body":73},"Close the inbound side with ufw","`sudo ufw default deny incoming`\n`sudo ufw limit 22\u002Ftcp`\n`sudo ufw enable`\nKeep a second SSH session open while you enable it. `ufw` filters by address and port, not by host name or user: that is why outbound traffic is handled in the next two steps, with a proxy and `nftables`.",{"title":75,"body":76},"Install an allowlist outbound proxy","`sudo apt install tinyproxy`\nIn `\u002Fetc\u002Ftinyproxy\u002Ftinyproxy.conf`, set `Listen 127.0.0.1`, `Port 8888`, `FilterDefaultDeny Yes`, `Filter \"\u002Fetc\u002Ftinyproxy\u002Fallowlist\"`, then write the list:\n`printf '%s\\n' '^registry\\.npmjs\\.org$' '^api\\.anthropic\\.com$' | sudo tee \u002Fetc\u002Ftinyproxy\u002Fallowlist`\n`sudo systemctl restart tinyproxy`\nEach line is a regular expression on the host name. Replace the example API with your provider's, taken from its documentation, and add `^github\\.com$` only if the project requires it. The tinyproxy documentation describes domain-based filtering, but does not say it applies to the host of HTTPS (CONNECT) tunnels: that is undocumented, so it must be tested. Do not take this filter for granted before the test in step 8.",{"title":78,"body":79},"Block every other outbound path with nftables","Append to the end of `\u002Fetc\u002Fnftables.conf`, after the `flush ruleset` line:\n`table inet agent_egress {`\n`chain output {`\n`type filter hook output priority 0; policy accept;`\n`meta skuid \"agent\" oifname \"lo\" tcp dport 8888 accept`\n`meta skuid \"agent\" limit rate 5\u002Fminute log prefix \"agent-egress-drop: \"`\n`meta skuid \"agent\" counter drop`\n`}`\n`}`\nThe `agent` user can now only reach the local proxy; everything else is logged then dropped. Check the syntax before enabling:\n`sudo nft -c -f \u002Fetc\u002Fnftables.conf`\n`sudo systemctl enable --now nftables`",{"title":81,"body":82},"Test that the filter holds","`sudo -u agent curl -sS --max-time 5 https:\u002F\u002Fexample.com`\n`sudo -u agent curl -sS -x http:\u002F\u002F127.0.0.1:8888 https:\u002F\u002Fexample.com`\n`sudo -u agent curl -sS -x http:\u002F\u002F127.0.0.1:8888 -o \u002Fdev\u002Fnull -w '%{http_code}\\n' https:\u002F\u002Fregistry.npmjs.org\u002F-\u002Fping`\nThe first command must time out, because the firewall drops the direct connection. The second must fail with a proxy refusal (with curl, a message like `CONNECT tunnel failed`; the exact code is in the tinyproxy log). The third must answer 200. These are the expected results: observe them on your own VM. If the second command succeeds, the filter does not cover HTTPS tunnels: switch to a proxy whose documentation covers CONNECT, and fix this before you start the agent.",{"title":84,"body":85},"Plan for destroying the server","Keep these commands in a versioned script. A suspect VPS is deleted and rebuilt from the original image, and you revoke the provider's token. For your own access to the server, go through a bastion rather than direct exposure: the bastion host guide linked at the bottom of the page covers this point.",{"type":87,"title":88,"headers":89,"rows":93},"comparison","Each safeguard limits one specific thing",[90,91,92],"Safeguard","What it limits","What it lets through",[94,98,102,106,110,114,118],[95,96,97],"Human approval of commands","Every command that runs","A command that looks harmless, approved out of habit",[99,100,101],"The tool's sandbox","The agent's actions, depending on its settings","Whatever depends on a forgotten setting or is specific to the tool",[103,104,105],"Unprivileged user","Root rights, system files, other accounts","The user's own files, including its token",[107,108,109],"Narrow project directory","Instruction files loaded by mistake","The project's own content",[111,112,113],"Filtered outbound traffic","An install from an unplanned host, a send to an unknown host (once the filter is verified)","Anything going through an allowed host, registry or API",[115,116,117],"No production key on the VM","Access to production servers and other clients","What the provider token and the project repository expose",[119,120,121],"Dedicated, rebuildable VPS","Persistence after compromise, the workstation","The client's data present on the VM",{"type":36,"title":123,"body":124},"Review instruction files before an agent follows them","An instruction file acts like code, through the agent: review it as such. To list them, run `find \u002Fsrv\u002Fagents -type f \\( -name AGENTS.md -o -name CLAUDE.md \\) -not -path '*\u002Fnode_modules\u002F*'` . To detect a change, keep a fingerprint:\n`find \u002Fsrv\u002Fagents -type f \\( -name AGENTS.md -o -name CLAUDE.md \\) -not -path '*\u002Fnode_modules\u002F*' -exec sha256sum {} + | sort -k2 > ~\u002Finstructions.sha256`\nthen compare before each session with `find \u002Fsrv\u002Fagents -type f \\( -name AGENTS.md -o -name CLAUDE.md \\) -not -path '*\u002Fnode_modules\u002F*' -exec sha256sum {} + | sort -k2 | diff ~\u002Finstructions.sha256 -` . In a repository, `git log -p -- AGENTS.md CLAUDE.md` shows what changed since your last review.\n\nTools do not all look for their files in the same places (current directory, parent folders, home folder): read your tool's documentation and keep those locations empty, or known. Never start an agent in `~\u002FDownloads` or in a broad parent folder: it is the study's first mitigation.",{"type":36,"title":126,"body":127},"The function-color package case: what the filter changes, and what it does not","In the documented case, the booby-trapped file asked to install the npm package \"function-color\". The report does not say from which registry. Our reading, which is a mitigation and not a research result: egress restricted to a list of hosts should block or flag, once the filter is verified in step 8, an install from an unplanned registry or server, and the proxy log shows it.\n\nIt does not block a package published on the allowed registry, since `registry.npmjs.org` is on the list. Against that case, rely on reviewing the instruction file, on a list of approved packages and, above all, on having no secrets to steal. The npm supply chain guide, linked below, covers that part.",{"type":129,"title":130,"body":131},"tip","Count the refusals, and cut install scripts","Read the refusals every day: `sudo journalctl -k --since today | grep agent-egress-drop`\n`sudo grep -i filtered \u002Fvar\u002Flog\u002Ftinyproxy\u002Ftinyproxy.log`\nA repeated refusal to an unknown host is a signal to understand before adding the host to the list: note the name, find out why the tool calls it, then decide. On the npm side, `npm config set ignore-scripts true` (run as `agent`) stops dependencies' install scripts from running automatically.",{"type":36,"title":133,"body":134},"Troubleshooting: the errors you will meet","The messages below are the ones the components of this setup produce. Each has a remedy that never involves weakening the filter.",{"type":40,"items":136},[137,138,139,140,141],"**A sudo refusal for `agent`** — expected behavior: the user has no privilege, and the exact wording of the refusal depends on the sudo version. Do the operation with your administration account, and never add `agent` to the `sudo` group.","**A `CONNECT` tunnel failure (with curl, a message like `CONNECT tunnel failed`)** — the proxy probably refused the host; the exact code depends on tinyproxy and is in its log. Read its name in `\u002Fvar\u002Flog\u002Ftinyproxy\u002Ftinyproxy.log`, then add it to the list only if the tool needs it.","**Depending on the tool, a timeout or a network failure (for example `ETIMEDOUT` or `fetch failed`)** — the tool attempts a direct connection that `nftables` drops. Check that `HTTPS_PROXY` is exported in the agent's session. Some clients ignore this variable: they then fail closed, and their documentation says whether they accept a proxy.","**`Permission denied` when reading another user's folder** — that is the isolation working. If the agent needs a file, copy it into the project directory.","**SSH connection cut after `ufw enable`** — the port 22 rule was missing, or SSH listens elsewhere. From the VPS console, run `sudo ufw status` then `sudo ufw allow \u003Cport>\u002Ftcp`.",{"type":36,"title":143,"body":144},"What this server does not solve","The VPS limits the blast radius, it does not remove the risk. The provider's token is on the machine: a hijacked agent can use it until you revoke it. The client's code is there too, and a leak remains possible through an allowed host, since a registry or a Git repository also accepts incoming data. The researchers recommend keeping the tool's approval prompts and sandbox: this setup adds to them, it does not replace them. Finally, a development server that serves public traffic needs its own hardening, covered in the checklist linked below.",{"type":36,"title":146,"body":147},"For an agency: one VPS per client","The isolation easiest to explain to a client is physical: one VPS per client, with its own user, token and firewall. No production key and no other client lives there, so a hijacked agent does not go beyond that perimeter. Keep a single setup script for all servers: an outbound rule that changes is replayed everywhere. At the end of an engagement, you delete the VPS, revoke the token and move on with no residue on your workstation. A ServOrbit.com VPS provides root access, a dedicated IPv4 address and a choice of OS: that is the base of this design.",{"type":36,"title":149,"body":150},"Official documentation","BleepingComputer's report on the Forever Security research: https:\u002F\u002Fwww.bleepingcomputer.com\u002Fnews\u002Fsecurity\u002Fai-coding-agents-hijacked-using-agentsmd-file-in-downloads-folder\u002F\n\nnftables documentation: https:\u002F\u002Fwiki.nftables.org\u002Fwiki-nftables\u002Findex.php\u002FQuick_reference-nftables_in_10_minutes\n\ntinyproxy documentation: https:\u002F\u002Ftinyproxy.github.io\u002F\n\nadduser manual page (Debian): https:\u002F\u002Fmanpages.debian.org\u002Fstable\u002Fadduser\u002Fadduser.8.en.html\n\nnpm configuration: https:\u002F\u002Fdocs.npmjs.com\u002Fcli\u002Fusing-npm\u002Fconfig","One VPS per client, isolated and rebuildable","An agency that isolates its coding agents gives each client its own VPS: root access, a dedicated IPv4 address, a choice of operating system. A hijacked agent then stays inside one client's perimeter. The VPS administration option exists for anyone who does not want to run the server; this guide, however, is provided without ServOrbit.com support for the setup.","See the agency offer","\u002Fsolutions\u002Fagences",[156,177,196],{"id":157,"slug":158,"slugs":159,"title":163,"excerpt":164,"readTime":165,"views":23,"isPinned":19,"publishedAt":166,"updatedAt":167,"category":168,"categories":174,"featuredImage":30,"bgImage":31,"posterImage":176,"relatedSolution":30},317,"linux-vps-hardening-checklist-for-agencies",{"fr":160,"en":158,"ar":161,"es":162},"linux-hardening-vps-checklist","قائمة-تصليب-خادم-لينكس-للوكالات-بعد-التسليم","hardening-linux-vps-checklist-para-agencias-tras-la-entrega","Linux VPS Hardening Checklist for Agencies","Reproducible Linux hardening checklist for agencies: auditd, sudo user, SSH key auth, UFW, fail2ban and root lockout — with per-client traceability.",11,"2026-08-30T00:00:00+00:00","2026-09-07T11:26:10+00:00",{"id":169,"name":170,"slug":171,"color":172,"icon":173},8,"Security & Monitoring","securite-monitoring","bg-rose-500\u002F10 text-rose-400","security",[175],{"id":169,"name":170,"slug":171,"color":172,"icon":173},"\u002Fblog\u002Fcovers\u002Flinux-hardening-vps-checklist-poster.svg",{"id":178,"slug":179,"slugs":180,"title":184,"excerpt":185,"readTime":186,"views":18,"isPinned":19,"publishedAt":187,"updatedAt":167,"category":188,"categories":193,"featuredImage":30,"bgImage":31,"posterImage":195,"relatedSolution":30},202,"securing-your-npm-ci-supply-chain-after-asyncapi",{"fr":181,"en":179,"ar":182,"es":183},"securiser-chaine-approvisionnement-npm-ci","تأمين-سلسلة-توريد-npm-في-ci-بعد-حادثة-asyncapi","proteger-la-cadena-de-suministro-npm-tras-asyncapi","Securing Your npm CI Supply Chain After AsyncAPI","Lockfile policy, hash verification, SLSA provenance and automated SCA: how to harden your CI pipeline after the AsyncAPI incident of July 2026.",4,"2026-08-01T00:00:00+00:00",{"id":186,"name":189,"slug":190,"color":191,"icon":192},"Development","developpement","bg-warning\u002F10 text-warning","dev",[194],{"id":186,"name":189,"slug":190,"color":191,"icon":192},"\u002Fblog\u002Fcovers\u002Fsecuriser-chaine-approvisionnement-npm-ci-poster.svg",{"id":197,"slug":198,"slugs":199,"title":203,"excerpt":204,"readTime":205,"views":206,"isPinned":19,"publishedAt":207,"updatedAt":167,"category":208,"categories":209,"featuredImage":30,"bgImage":31,"posterImage":211,"relatedSolution":212},158,"bastion-host-on-vps-securing-your-ssh-access",{"fr":200,"en":198,"ar":201,"es":202},"heberger-bastion-host-vps","مضيف-bastion-على-خادم-vps-تأمين-وصولك-عبر-ssh","bastion-host-en-vps-accesos-ssh","Bastion Host on VPS: securing your SSH access","Deploy a Bastion Host on a ServOrbit VPS: a hardened SSH entry point, centralized access, logs and security rules.",3,2,"2026-07-05T00:00:00+00:00",{"id":169,"name":170,"slug":171,"color":172,"icon":173},[210],{"id":169,"name":170,"slug":171,"color":172,"icon":173},"\u002Fblog\u002Fcovers\u002Fheberger-bastion-host-vps-poster.svg",{"categorySlug":173,"appSlug":213},"bastion-host",1793251349761]