We spend an enormous amount of time trying to secure software. We run SAST. We scan dependencies. We scan container images. We run IaC scanners. We check Kubernetes configurations. We monitor runtime behaviour. We build SBOMs. We integrate everything into CI/CD and create dashboards full of vulnerabilities, CVEs and security findings.

And yet sometimes the most interesting security problem is not a vulnerability in our software at all. Sometimes it is a placeholder.

A recent case reported by The Hacker News is a particularly good example. The domain third-party.com, which had been used as a generic documentation placeholder, was referenced across more than 1,700 public repositories.

There was only one problem. Unlike example.com, example.org and example.net, third-party.com was never reserved for documentation purposes. Someone could register it, and someone eventually did. The domain began serving malicious content.

TimelineEvent
T0A developer writes https://third-party.com in a README as an example endpoint
T0The reference is copied into forks, templates, tutorials and agent skills
T1The original author leaves the project; nobody owns the string anymore
T2The domain is registered by an unrelated party
T31,700+ repositories now point at infrastructure nobody reviewed

Nobody changed a line of code. The repositories were still “clean” the whole time.


The Short Version

  1. A domain that only looks like a placeholder is a dependency, not a comment.
  2. The question is not “is this malicious right now?” but “who controls this, and can that change?”
  3. Scanners classify strings. They do not understand why a string exists.
  4. The same pattern applies to package registries, image tags, Git remotes and CI actions.
  5. Documentation is code now, because agents read it and act on it.

If you don’t control the domain, don’t assume it is safe just because you use it as an example.


example.com Is Not the Same as third-party.com

There are domains specifically reserved for documentation and examples, defined in RFC 2606 and reinforced by RFC 6761. example.com, example.org and example.net exist so developers can use them without creating a dependency on somebody else’s infrastructure.

Developers frequently use other domains because they look like placeholders:

third-party.com
yourcompany.com
mycompany.com
your-api.com
vendor.com
acme.com

They look fictional. They look safe. A domain that merely looks like a placeholder is not a placeholder from a security perspective. The THN report describes several such domains used in repositories and agent skills, with some subsequently serving scams or malicious content.

DomainReserved for examplesRegistrable by third partiesSafe to reference
example.comYesNoYes
example.orgYesNoYes
example.netYesNoYes
example.testYes (RFC 2606)NoYes
localhostReservedNoYes
third-party.comNoYesNo
yourcompany.comNoYesNo
acme.comNoYesNo

If you find one of the unreserved names above in your repository, replace it with example.com, example.org, example.net or example.test.


Scanners Check Reputation, They Do Not Check Ownership

When reviewing a project, we often ask:

“Does this URL currently serve something malicious?”

That check is useful, but insufficient. A stronger review asks:

  1. Who controls this domain today?
  2. Could somebody else control it tomorrow?
  3. Why does our project reference it in the first place?

Take this fragment:

api:
  endpoint: https://third-party.com/api

A scanner resolves the URL, checks reputation, inspects the response and moves on. Today it returns nothing interesting. Tomorrow ownership changes.

AssetChanged?
The applicationNo
The deploymentNo
The commitNo
The security properties of the applicationYes

Nothing in the pipeline fires, because nothing in the pipeline watches ownership. A scanner can say https://third-party.com is a domain. It cannot say why the developer put it there:

Possible intentTypical signal in the repository
DocumentationComment, README, tutorial, changelog
Unit test or fixturetestdata/, tests/, mocked or localhost target
Sample configuration.example suffix, Helm values template, .env.sample
Real production endpointReferenced from runtime code, config loader, manifest
Dead referenceUntouched for years, resolves to a parked page
AI agent exampleskills/, agent prompts, MCP configuration files
Copied templateThe same string appears across many unrelated projects

The same string is consumed by different systems, each with a different tolerance for a hostile value.

A string that looks harmless to a developer can become an instruction, or an actionable resource, for another system.


AI Turns Documentation Into Infrastructure

The Hacker News report notes references to third-party.com in AI agent skills and MCP server documentation. Suppose an agent is given:

Use the following endpoint for testing:

https://third-party.com

A human understands this is an example. An agent may treat it as an endpoint to fetch.

graph TD subgraph BEFORE["Before: the human boundary"] D1["Developer reads the file"] -->|"understands intent"| C1["Nothing happens, the string is inert"] end subgraph AFTER["After: the agent boundary"] D2["Agent reads the file"] -->|"treats it as an endpoint"| F["Fetches the URL"] F --> R["Retrieved content is untrusted input"] R --> X["Command execution, API calls, file changes"] end D1 -.->|"same string"| D2

If the agent can browse the URL, call APIs or fold retrieved content into subsequent actions, the placeholder has become an input to an automated system. The boundary moved. I covered containment in The Challenge of Securing AI Agents: A DevSecOps Perspective; this is the input side.

Security reviews of AI-enabled projects need to cover code, dependencies, and the content agents are instructed to consume.


Start With “What Do We Trust?”

Do not start only with “what vulnerabilities does this project have?”. Start with “what does this project trust, why does it trust it, and do we control it?”

We inspectWe implicitly assume
Dependencies and librariesNobody will ever transfer ownership
Container imagesA mutable tag will always resolve to the same bits
Secrets and API endpointsThe hostname will keep pointing to the same service
Cloud resources and permissionsThe account will not be deleted or resold
Network exposureThe firewall rule is still the one I wrote
Documentation and examplesNothing in there is ever executed or fetched

Most trust in a modern project sits outside the code you wrote. I detailed that distribution in Supply Chain Attacks Won’t Stop: 8 Controls to Reduce Your Exposure. If your review stops at the CI pipeline, you are already late; I made that argument in Shift Left Starts in the IDE.

The pattern repeats across surfaces:

Domains and packages

For domains, search for https:// and http://, then check each hit:

  • Who owns it, is it reserved, and do we control it?
  • Is it still required, and is it referenced from executable code or only documentation?
  • For packages, add maintenance status, pinning and transitive dependencies.

Ownership transfer is the failure mode in both cases.

Container images and Git remotes

FROM some-image:latest

Who controls some-image, what does latest resolve to today, and who can overwrite it tomorrow? For a concrete remediation on the image side, see In 2026 I Am Still Asked Why You Need a Hardened Container Image Catalog.

uses: somebody/action@main

carries a different assumption from:

uses: somebody/action@<immutable-commit>

Check whether Git URLs, organisations, submodules and Actions still point where you expect, and pin mutable references to immutable commits.

Documentation and forgotten references

Scan README files, examples, Helm values, tutorials and sample configurations, not just source code. Those files are increasingly inputs to automated systems (see Securely Working with Third-Party MCP Servers if agents consume yours).

Forgotten references fail silently:

ReferenceHow it got thereWhy nobody notices
A URL in a READMECopied from a tutorial five years agoIt still returns HTTP 200
A DNS recordCreated for a dead projectThe zone is still hosted
A CDN endpointBelongs to a discontinued serviceThe vendor never took it down
A cloud bucketCreated for a proof of conceptNobody knows it is still there

A broken dependency gets noticed. A working dependency under somebody else’s control stays invisible. third-party.com survived long after its original assumption was forgotten.


A Simple Exercise for Your Next Security Review

Do not start with the CVE database. Start with a search across the whole repository:

git grep -Eo 'https?://[^ ")]+' .
git grep -Ei 'curl |wget |http://|https://|npm |pip |docker pull|FROM '

Include README.md, docs/, examples/, helm/, .github/, .gitlab/, Dockerfiles, Terraform, Kubernetes manifests, MCP configurations, agent skills and sample configuration files.

Classify every external reference:

ReferencePurposeOwnerMutable?Required?
example.comDocumentationReservedNoNo
internal-api.company.comProduction APIUsControlledYes
vendor.comVendor APIVendorYesYes
random-domain.comExampleUnknownYesNo

Remove first the rows that look like this:

Owner: Unknown
Mutable: Yes
Required: No

Conclusion

Tooling is excellent at finding known bad things (vulnerable packages, leaked credentials, misconfigured resources, suspicious behaviour). The interesting failures come from things never considered security-sensitive: a placeholder, a DNS name, a sample configuration, a forgotten CI action, a URL in a README. Something never supposed to matter, until somebody else controls it.

A vulnerability is a flaw in software you control, usually with a CVE and a patch. A trust assumption is a belief that something outside your control will keep behaving the same way. Trust assumptions fail silently, without a patch, and scanners do not track them.

Next time you evaluate a project, do not start with the scanner. Start with the placeholders. Ask what is real, what is reserved, what is controlled, what can change ownership, and what the application, the pipeline and the agents around it implicitly trust.

Sometimes the most dangerous dependency has no CVE. It is the one you never thought was a dependency at all.