Infrastructure Exposure: Don't Publish Your Topology
Internal hostnames, service port numbers, filesystem paths, and organization-internal identifiers are reconnaissance gold — and agents reproduce what they see in context, which means every agent-generated document is a potential topology leak without a sanitization gate.
Your Topology Is Reconnaissance
When an attacker prepares to target an infrastructure system, the first phase is reconnaissance: mapping the target's architecture before attempting anything that might trigger an alert. The goal is to learn as much as possible from public sources — sources that require no access, generate no logs, and carry no risk.
Internal hostnames tell an attacker what services exist and how they are named. Port numbers tell them where to probe. Directory structures tell them what platform the system runs on and how it is organized. Organizational identifiers — project slugs, service labels, internal URLs — tell them how the organization structures its systems. Together, these form a topology map that shortens every subsequent attack phase.
Where do attackers find this information? Often: your published documentation, your README files, your public GitHub organization, your error pages, and your AI-generated tutorials and blog posts.
The last source is the one this lesson addresses. Agents that generate content can be exceptionally good at topology leaks because they reproduce context accurately. An agent given access to internal documentation in order to write external content will reproduce the internal details it was given — not because it is careless, but because that is how context-based generation works.
The sequence above shows the complete chain from context input to attacker recon. The chain is not a bug in the agent's behavior — it is a missing gate.
What Counts as Topology
Topology data is any information that helps an attacker understand the structure of a real system. In published content, this includes:
IP addresses from private ranges. The ranges 10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16 are private address space — they should never appear in public documentation. The RFC 6598 shared address block (the 100.64/10 CGNAT range) is also worth blocking: carrier-grade NAT and VPN overlay networks commonly use it for internal addressing, so it carries the same reconnaissance value as RFC-1918 space. If a teaching example needs an IP address, the RFC-5737 documentation ranges exist specifically for this purpose: 203.0.113.x, 192.0.2.x, 198.51.100.x. These are reserved for documentation and cannot route to real infrastructure.
Hostnames with internal naming patterns. Hostnames ending in .internal, .local, or following recognizable naming conventions for real services are topology data. A hostname like prod-api.internal in a published document tells an attacker both that the service exists and what the internal DNS convention is.
Personal filesystem paths. A path like /home/alice/projects/api-service/ tells an attacker a real username, that they are on a Linux system, and the directory layout of a real deployment. Replace personal paths with generic teaching paths (/home/dev/api-service/) that convey the structural information without revealing real topology.
Service port numbers from your actual deployment. If your internal gateway runs on port 9410, that port number should not appear in published documentation — even in examples that look unrelated to your system. Attackers cross-reference details across sources.
Organizational identifiers. Project slugs, service names, internal codenames, reverse-DNS labels for launchd services — these appear in code comments, README files, and agent-generated output, and they help attackers correlate what they find across different sources.
The pattern across all of these: they are details that are useful for teaching (they ground examples in real structure) but dangerous when published (they reveal your actual infrastructure). The solution is to replace them with sanitized equivalents that teach the same concepts without the exposure.
Why Instructions to the Agent Are Not Sufficient
The natural first response to topology leaks in agent output is to add an instruction to the system prompt: "Do not include internal hostnames, real IP addresses, or personal filesystem paths in your output."
This is worth doing. It reduces the occurrence. But it is not a security control.
Language model instruction-following is probabilistic. A model given a clear instruction will follow it most of the time — but not all of the time. Edge cases, unusual context structures, competing instructions, and rare generation patterns all create opportunities for the instruction to be missed. In security contexts, "mostly correct" is not acceptable.
The deeper problem is that the instruction still requires the model to recognize what counts as an internal hostname or a real IP address — which requires the model to know which IP addresses are yours, which hostnames are internal, and which paths are personal. This knowledge is not reliably available to the model, and the judgment is not reliably applied.
The Sanitization Gate
A sanitization gate is a script or CI check that scans generated content for topology-revealing patterns and exits nonzero on any violation. There are two required positions for the gate: pre-commit (running locally before any commit is created) and CI (running on every PR and blocking merge).
Both positions are required. Pre-commit catches local mistakes before they reach the remote. CI enforces the rule at merge time regardless of how the commit was created — force-push, GitHub UI edit, or a path that bypasses the pre-commit hook. Neither gate alone is sufficient because each can be bypassed in ways the other cannot.
The pipeline shows the two gate positions and what each checks. The critical implementation details:
The gate must exit nonzero to actually block. A gate that only prints warnings is not a gate. The CI step must be configured so that a nonzero exit code fails the check and prevents merge. This sounds obvious, but it is the most common configuration mistake: the scan runs, finds violations, logs them, and the pipeline continues.
The gate must have an allowlist. A gate that blocks RFC-5737 documentation ranges, intentional example paths like /home/dev/..., and example.com hostnames will generate constant false positives. False positives cause developers to disable gates. The allowlist distinguishes patterns that are intentionally safe for publication from patterns that indicate real topology. The RFC-5737 ranges are intentionally safe; 10.0.0.x in a public doc is not.
The gate is the authority, not the agent. Once the gate is in place, the question "will this content expose topology?" has a deterministic answer. You do not need to trust the agent's judgment, the author's review, or the reviewer's pattern-matching. The gate runs on every PR and produces a binary result.
Worked Example: A Public Learning Platform
A public learning platform that teaches security topics faces an interesting challenge: its content discusses the very patterns its sanitization gate protects against. Lessons about networking discuss IP addresses. Lessons about secrets hygiene show what a .env file looks like. Lessons about topology exposure — like this one — describe the patterns that must not appear in published content.
The gate for such a platform requires a well-designed allowlist. The RFC-5737 documentation ranges are in the allowlist. The placeholder shapes for API keys (<your-api-key>) are in the allowlist. Generic teaching paths (/home/dev/...) are in the allowlist. Everything else that matches a private IP pattern, a .internal hostname, a real personal path, or a secret-shaped string is blocked.
The gate runs in CI on every PR. It exits nonzero if any violation is found, and the check is required — the PR cannot merge with a failing gate. The gate is also run locally by content agents before they create a PR, so violations are caught at generation time rather than at review time.
This is defense in depth at the content layer: the agent is instructed to use safe patterns, the pre-commit hook catches any that slip through, and the CI gate is the final authority before any content reaches the public.
The Allowlist Design
The allowlist for a sanitization gate should be maintained as a version-controlled configuration file, not hard-coded in the gate script. This allows it to be updated via PR (with review) as new safe patterns are identified, without changing the gate logic.
The structure of a good allowlist entry is: pattern + rationale. A pattern without a rationale is a mystery to the next engineer who reads the file. "Why is 203.0.113. in the allowlist?" has an obvious answer — "RFC-5737 documentation range, safe for publishing" — but only if the rationale is written down.
The allowlist should be reviewed as part of any security audit. An allowlist that has grown over time to include entries like "it kept flagging this one doc" or "we needed to merge quickly" is a gate with holes.