Untrusted File Content: A Five-Layer Detection Framework for Coding Agents
Coding agents operate by reading project files and executing tasks—but when those files are untrusted, the agent can be manipulated into altering its own configuration, installing malicious dependencies, and exfiltrating data. This article breaks down the five-stage attack chain and maps each stage to a concrete detection layer: entry-point monitoring, configuration baseline drift checks, prompt-and-execution separation, egress allowlisting, and a "detect-and-rotate" response protocol. It includes a ready-to-use read-only Python script for baseline integrity verification, plus practical guidance for QA teams on how to validate these controls in an isolated environment.
The Core Problem: Tools Work as Designed, Risk Lives at the Input Boundary
The core capability of a coding agent is reading context, understanding intent, and invoking tools to accomplish tasks. It reads instruction files in a project directory and decides how to write code, which commands to run, and which dependencies to install. This mechanism makes coding agents useful—and it also makes them susceptible to manipulation.
A piece of text that looks like documentation to a human can function as an instruction to an agent. When the boundary between data and commands is unclear, anyone who can write into the working directory has the opportunity to influence agent behavior.
A browser analogy helps. When a browser renders a forged login page, it is merely rendering, and the attack lives in the page content. The browser has no bug—the question is whether the page can be trusted.
But the analogy has limits. A browser rendering a page does not rewrite local files or execute commands. Coding agents typically have permissions to write files, run scripts, and access networks. These permissions turn “understanding text” into “taking actions,” so the risk lands on the agent’s permission and execution chain, not just on the text itself.
For QA teams, the conclusion is direct: if waiting for the vendor to fix the issue is not practical, the only option is to convert this class of risk into check items in your own CI/CD gates. Below, we examine each checkpoint in the order of the attack chain.
The Five Stages of the Attack Chain
A complete attack chain typically consists of the following stages. Each stage corresponds to a location that can be observed and checked.
- Untrusted files enter the local system: Web pages and other external sources can write files to the download directory without explicit user confirmation. Filenames and content are both externally controlled. Operating systems typically block downloaded executables from running automatically, but plain text files fall outside the scope of such protections.
- The user starts the agent in that directory: Users often open tools in the download or temporary directory to handle a task. This habit makes it easier for untrusted files to be read by the agent.
- The agent treats the file as a specification and modifies its own configuration: Following the “project rules” in the file, the agent changes global settings—for example, relaxing tool permissions or adding new workflows.
- External dependencies are introduced after the configuration change: The modified configuration causes the agent to install specific third-party packages. Not all packages in public registries are vetted; malicious packages can masquerade as common dependencies.
- The dependency executes and communicates externally: The dependency runs on the local machine, potentially reading credentials, stealing code, or establishing an outbound channel.
Of these five steps, the first two are input and habit issues; the last three are configuration, dependency, and network issues. Detection must cover all five stages—defending only a single stage is unlikely to stop the entire chain.
Five Types of Detection Methods
1. Entry-Point Detection: Controlling Untrusted Files Entering the Workspace
The goal of entry-point detection is to discover “newly appearing instruction-like files” and, ideally, to know where they came from.
- Exclude the download directory from the agent’s project working directory. The download directory is suitable for temporary files, not as a project root.
- Log newly added or modified instruction-like files in the working directory, including filename, size, hash, and first-seen timestamp.
- Raise an alert when the agent is launched outside a designated project directory for later traceability.
Entry-point detection turns “I don’t know where this file came from” into “there is a record to be examined.” It cannot block every attack, but it shortens the time to discovery.
2. Configuration Integrity: Baseline Comparison for Global Configuration
Global configuration is the foundation of agent behavior. To change agent behavior, an attacker typically must modify it first. Therefore, baseline comparison is the most direct detection method.
The approach works as follows:
- Generate a baseline in a clean environment after manual verification, recording SHA-256 hashes of critical configuration files.
- Recompute the hashes periodically or before every launch and compare against the baseline.
- Any addition, deletion, or content change is treated as drift requiring manual confirmation.
- The baseline file itself must be stored in a location that ordinary users cannot arbitrarily modify; otherwise, an attacker could modify the baseline first and then the configuration.
The script below implements the process described above. It only reads files and modifies nothing.
#!/usr/bin/env python3"""Check integrity of agent global config and working-directory instruction files (read-only).Usage:python3 drift_check.py --init # Generate baseline; requires manual review afterwardpython3 drift_check.py # Compare against baseline; exits with code 1 if drift found"""import hashlibimport jsonimport sysfrom pathlib import PathHOME = Path.home()# Global configuration files to monitor; adjust paths to match your team's environmentWATCHED_FILES = [HOME / ".agent" / "config.json",HOME / ".agent" / "rules.md",]# Directories to scan (first level only) and filenames considered instruction-like (lowercase)SCAN_DIRS = [HOME / "Downloads"]RULE_FILE_NAMES = {"agent-rules.md"}BASELINE_PATH = HOME / ".config" / "agent-drift" / "baseline.json"def sha256_of(path: Path) -> str:digest = hashlib.sha256()with path.open("rb") as f:for chunk in iter(lambda: f.read(65536), b""):digest.update(chunk)return digest.hexdigest()def take_snapshot() -> dict:snap = {}for path in WATCHED_FILES:if path.is_file():snap[str(path)] = sha256_of(path)for root in SCAN_DIRS:if not root.is_dir():continuefor path in root.iterdir():if path.is_file() and path.name.lower() in RULE_FILE_NAMES:snap[str(path)] = sha256_of(path)return snapdef main() -> int:current = take_snapshot()if "--init" in sys.argv:BASELINE_PATH.parent.mkdir(parents=True, exist_ok=True)BASELINE_PATH.write_text(json.dumps(current, indent=2, ensure_ascii=False), encoding="utf-8")print(f"Baseline written to {BASELINE_PATH} with {len(current)} entries. Manual review required.")return 0if not BASELINE_PATH.is_file():print("No baseline found. Run with --init first and review manually.", file=sys.stderr)return 2baseline = json.loads(BASELINE_PATH.read_text(encoding="utf-8"))added = sorted(set(current) - set(baseline))removed = sorted(set(baseline) - set(current))changed = sorted(k for k in set(current) & set(baseline) if current[k] != baseline[k])for label, items in (("Added", added), ("Missing", removed), ("Changed", changed)):for item in items:print(f"[{label}] {item}")if added or removed or changed:print("Drift detected. Follow the 'detect-and-rotate' process.")return 1print("No drift detected.")return 0if __name__ == "__main__":sys.exit(main())
The script’s limitations should also be stated. It can only detect changes to files already under monitoring; it cannot identify new configuration locations, nor can it judge whether a change is malicious. Its purpose is to provide evidence so someone can quickly confirm whether a modification occurred.
3. Prompt-and-Execution Separation: Treating Instruction Files as Data
Entry-point and configuration detection are both post-hoc findings. Separation by design attempts to draw a clear line at the agent’s decision point.
The core principle is: file content is data by default and carries no instruction authority. Specific measures include:
- Project documentation files provide background information only; they must not authorize changes to global configuration, package installation, or script execution.
- Require manual confirmation for high-risk operations, including configuration changes, dependency installation, shell command execution, and network access.
- Configure tool permissions on a least-privilege basis, exposing only capabilities needed for the current task.
- Project-level settings cannot override global security policy. Global policy takes precedence over project files.
This layer cannot rely entirely on model judgment. Models can be induced or can misjudge. Therefore, separation must be enforced through the permission system and confirmation mechanisms, not by hoping that prompt wording is strict enough.
4. Egress Allowlisting: Restricting Dependency Sources and External Communication
Even if the earlier checks fail, a malicious dependency still needs to fetch itself from the network and communicate externally to be effective. Egress control is the last line of defense.
- Dependency installation is allowed only from private mirrors or pre-approved package sources. Unknown sources are blocked immediately.
- Outbound requests from the agent’s runtime environment use an allowlist, with denial by default.
- Log denied requests—including target domain, process name, and timestamp—for later traceability.
- Require approval for package installation; new packages must be reviewed before being added to the allowlist.
Allowlists require periodic maintenance. Their effectiveness depends on review criteria—simply listing domains is not sufficient for security.
5. Detect-and-Rotate: Front-Loading the Incident Response Process
When drift or abnormal dependencies are detected, the most important action is to contain the blast radius, not to immediately investigate root cause. Many teams hesitate at this step, leaving credentials exposed for extended periods.
The recommended response sequence is as follows:
- Isolate: Disconnect the affected host from the network and stop related agent processes.
- Preserve evidence: Save configuration files, dependency manifests, and process/network logs; do not overwrite anything.
- Rotate credentials: Treat any credentials that could have been read on the host—access tokens, keys, session information—as compromised and rotate them all.
- Freeze dependencies: Review recent installation records, pin versions, and remove unknown packages.
- Restore and review: Restore configuration from a clean baseline, re-run drift checks, and update entry-point and allowlist rules.
The key to “detect-and-rotate” is not to assume “it’s probably fine.” The cost of rotation is lower than the cost of leakage—this is a judgment most teams should accept.
How QA Teams Can Validate These Controls
Controls are only effective if they have been validated. QA teams can design the following test cases in an isolated environment, using only harmless marker content:
- Place a harmless instruction file in the download directory containing only a recognizable marker string. Launch the agent and check whether it attempts to modify the global configuration.
- Modify one file in the baseline, run the drift check, and confirm the exit code is 1 and the output correctly identifies the change.
- In a sandbox, simulate a forced dependency installation from an unapproved source and verify that the egress policy blocks it and logs the attempt.
- Verify that protected configuration paths reject write attempts from the agent, including attempts made through project-level instruction files.
- Confirm that rotating credentials and restoring from baseline can be completed within the target response time after drift detection.
These tests should be run in a disposable environment with no real credentials or network privileges. Once validated, they can be automated and wired into the team’s existing CI pipeline or pre-launch checks.
