Safety and Security: Limit What the Agent Can Access
Last updated on 2026-09-16 | Edit this page
Estimated time: 28 minutes
Overview
Questions
- What can an agent access on my machine, and why does that matter?
- Which protections are enforced limits, and which are requests the model may ignore?
- How do I keep credentials and sensitive or restricted data away from AI tools?
- How do I start an agent on my own project safely?
Objectives
- Apply institutional data policies before pointing an agent at any project.
- Explain prompt injection and why anything an agent reads is untrusted input.
- Limit an agent along six axes: what it can reach on the network, where it runs, which commands it may run, which credentials it can see, what it can commit, and whom you trust.
- Distinguish instructions, which influence behavior, from permissions, which constrain it.
- Keep secrets out of local plaintext files, loading them at runtime from a password manager.
- Start an agent session on your own repository using a cloud VM, plan mode, a branch named for you, and no keys on disk.
Institutional GenAI policies
AI tools are not exempt from institutional data rules, and the rules are changing quickly. At UW–Madison:
- Follow all UW–Madison, UW System, and Board of Regents GenAI policies.
- DoIT summarizes policies and vetted tools at it.wisc.edu/ai. If you are unsure of your data’s classification, contact a data steward through that page, or your data governance office, IRB office, or IT office.
- Do not enter sensitive or restricted information into unvetted AI services. This includes student records (FERPA), health data (HIPAA/PHI), unpublished research, CUI, export-controlled data, and anything under a data use agreement (DUA) that prohibits third-party processing.
Code and prompts are sent to the model provider’s servers for inference. Every control in this episode protects your machine; none changes where the repository contents go.
What an agent can and cannot do
Risk management starts from an accurate picture of the tool:
| A GenAI agent can | A GenAI agent cannot |
|---|---|
| Write or translate code | Work safely with sensitive or restricted data or secrets |
| Explain basic logic | Explain human logic, such as why your field does something a particular way |
| Execute validation steps you specify | Determine all the necessary validation steps |
| Run commands from your terminal, with the attendant risk of prompt injection and leaked credentials | Prevent all harmful commands from running (hence the VM) |
The last row is the one most often underestimated. An agent launched
from a terminal or IDE runs with the user account’s full filesystem and
shell access. It can read SSH keys, .env files, and notes.
Agents also scan for context as part of their normal operation, so a
credentials file in the working directory is simply more context.
The threat: prompt injection
An agent treats the text it reads as instructions. A README, an issue, a web page, or a dependency’s install script can contain commands the agent will follow. This is prompt injection.

Two documented cases:
- GitHub MCP, May 2025. Invariant Labs showed that a malicious issue in a public repository could direct an agent with GitHub access to leak data from the user’s private repositories.
- Nx on npm, August 2025. A compromised package’s install script prompted the victim’s own Claude Code, Gemini CLI, or Amazon Q to search the machine for secrets. Thousands of credentials were leaked to public GitHub repositories. Wiz’s analysis found that Claude refused about a quarter of the time; model refusals reduce the risk but do not remove it.
Simon Willison’s “lethal trifecta” identifies the combination to avoid: access to private data, exposure to untrusted content, and a channel to send data out. Removing any one of the three defeats the attack. Injection is the threat; the six limits below determine how much damage it can do.
Six limits on what the agent can access
- What it can reach on the network. An egress allowlist, not the whole internet.
- Where it runs. A disposable cloud VM, not your laptop.
- Which commands it may run. Allow, ask, and deny rules.
- Which credentials it can see. A password manager, never the repository.
- What it can commit. A feature branch and a pull request; you merge.
- Whom you trust. Providers, their data policies, and downloaded repositories and weights.
One distinction applies to all six. An instruction (a rules file, a line in a prompt) asks the model to behave in a certain way. A permission (a VM boundary, a deny rule, branch protection, a firewall) removes the ability to do otherwise. Instructions influence behavior; permissions constrain it. Prefer permissions wherever they are available. None of the six eliminates risk; together they reduce it.
1. Network: an allowlist
- Be careful what you install and clone. Every dependency’s install script and every file in a repository is code the agent runs and text it reads.
- Treat everything the agent reads as untrusted: issues, PR comments, web pages. A prompt that sends the agent to the web brings back whatever the page contains.
- Use the web session and keep its defaults. Claude Code on the web and Copilot’s cloud agent restrict network access to an allowlist and keep keys out of the sandbox. Do not disable the firewall to resolve a blocked request; add the one host that is needed.
- Review the pull request before merging. This is the final control, applied after all the others.
Where to set the allowlist:
An allowlist is a partial control. Copilot’s firewall covers only processes the agent starts through Bash, not MCP servers or setup steps, and GitHub states that sophisticated attacks may bypass it. An allowlist narrows the exfiltration path; it does not close it.
2. Run the agent in a cloud VM
Run the agent on a cloud virtual machine rather than your own
machine. It then cannot delete your filesystem, read your password
store, or find an old .env file.
- Claude Code on the web (claude.ai/code) and GitHub Copilot’s cloud coding agent (github.com/copilot/agents or the Copilot app) clone the repository into a disposable VM. The work is returned as a branch or a pull request.
- The desktop apps offer both modes. Claude Desktop, the Copilot app, and the VS Code extensions can run a cloud session or a local one. A local session runs on your machine with your full user access. Check which mode is selected before you prompt.
- If you run locally regardless, use a dev container and understand its limits.

Dev containers are not a security boundary
A dev container limits the agent to the project directory, which is worthwhile. But Anthropic does not consider containers a security boundary, and its own Claude Code dev container is intended for trusted repositories only. A container or VM limits what a compromised agent can reach; it does not prevent the compromise. The agent still reads the repository, the issues, and whatever a prompt directs it to fetch, and it still holds whatever credentials the session was given. Injected text can use those inside the sandbox and send results out over the network. This is why the network allowlist and the credential rules remain necessary.
| Bare laptop | Local dev container | Disposable cloud VM | |
|---|---|---|---|
| What the agent can reach | Everything on the machine | The project folder, plus anything forwarded in (SSH keys, git login) | One repository and one scoped token |
| What is lost if it is compromised | Your accounts, keys, and every repository on the machine | The project, and possibly the machine | One repository, and a token you can rotate |
| Prompt injection | Same risk | Same risk | Same risk |
When local exposure is the concern, the order of preference is: cloud VM (nothing runs locally), then dev container (contained local access), then a bare local agent (full user access).
3. Set command rules, but beware loopholes
Allow, ask, and deny rules determine which commands run without
confirmation. The defaults usually deny nothing, so configure them: deny
rm -rf, force-push, and reads of .env and
~/.ssh. Claude Code’s documentation on permissions versus
sandboxing describes the two mechanisms.
Rules can be committed with the project so that cloud sessions read them:
A permission rule inspects the command text, not what the command
does when it runs. In January 2026, Cursor’s allowlist was bypassed (CVE-2026-22708,
reported by Pillar Security): prompt injection set an environment
variable through a shell built-in that the allowlist did not check, and
the next approved git command executed the attacker’s code.
Deny rules reduce risk; the VM boundary is what removes it.
Instruction files help, but agents forget context and find loopholes
An instruction file is a markdown file the agent reads at the start
of every session containing project context, commands, and conventions.
Claude Code reads CLAUDE.md; Copilot reads
.github/copilot-instructions.md; nearly every tool, Copilot
included, also reads AGENTS.md. Commit it to the repository
and keep it short: the test command, the data location, what not to
modify. The planning episode
gives an example.
An instruction file is not a permission, and agents also lose track of instructions as the context grows long. In April 2026 a Cursor agent working on a staging task for the company PocketOS encountered a credential mismatch, found a Railway API token in an unrelated file, and used it. The production database and its backups were deleted in nine seconds. The rules file occupies the same context window as everything else the model reads; it is text the model weighs, not an ability the model lacks.
4. Credentials: a password manager, never the repository
A secret that is not on disk cannot be read, printed, committed, or
exfiltrated, whether by an agent, by malware, or by a person working
late. Agents scan for context and may be able to read local
.env or JSON configuration files, so remove the secrets
before the agent arrives.
- Do not store passwords or API keys in
.envfiles, JSON configuration files, or shell profiles. - Keep keys in a password manager and read them when needed. Every
UW–Madison NetID can request a free 1Password account.
The 1Password CLI (
op) reads a secret by reference:
BASH
# The op:// reference is a pointer, not a secret. It is safe to commit.
op read "op://Private/bbadger/credential"
# vault item field
# Into the environment for this shell only
export OPENAI_API_KEY=$(op read 'op://Private/bbadger/credential') # bash / zsh
An op:// reference names a vault, an item, and a field.
It is a pointer, not a key: it is safe in a repository, a script, or a
chat message, and useless to anyone without access to your vault. Read
it when you need it; nothing is stored on disk.
For a Jupyter workflow, keep a file of references (safe to commit,
since an op:// path is not a secret) and launch through
op run. Every kernel inherits the variables and no key
appears in a notebook:
BASH
# .env.op (references, not secrets; safe to commit)
OPENAI_API_KEY=op://Private/BadgerBrain/credential
OPENAI_BASE_URL=https://deepthought.doit.wisc.edu/v1
# launch JupyterLab through 1Password
op run --env-file=.env.op -- jupyter lab
PYTHON
# in any notebook, in any kernel
import os
from openai import OpenAI
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
Do not print the key or paste it into a cell. 1Password prompts on each read, whereas an OS keychain releases secrets to any process running as the user. Cloud equivalents (AWS Secrets Manager, Azure Key Vault) work the same way. See the 1Password CLI documentation.
5. Version control: feature branch, pull request, you merge
Version control makes agent mistakes recoverable.
- Always use version control (GitHub, GitLab, Bitbucket).
- Do not let an agent commit to
main. The agent creates a feature branch and opens a pull request; you review and merge. - Review and test code before merging to
main. Make small, frequent commits; each is a restore point. - Start from a clean git state, so that
git diffshows exactly what the agent changed andgit restorereverts it.
Protect main on the hosting service so this is enforced
rather than habitual: require a pull request and a passing check before
merge. The verification episode sets this up.
6. Whom you trust: providers, models, repositories
The final limit is not enforced by any setting: which providers, packages, and model weights you admit into the workflow. Vet a provider’s data policy (are my data and code used for training, and is that the default? how long is it retained, and who can see it? where does inference run, and under whose jurisdiction? does an institutional agreement cover this, or is it a personal contract?). Treat downloaded weights and packages with the caution you would apply to any executable. The next episode, trust, covers each with the incidents behind the rules.
No agents on machines that hold sensitive or restricted data
Running an agent locally on any machine that stores sensitive or restricted data is not recommended at this time. Permission settings, deny rules, and containers do not change this: local agents scan for context, anything on the machine can end up in a prompt, and “the agent should not have looked there” is not a control a compliance office will accept.
If you must work on a repository from such a machine, use a route with no local access by construction: a web interface in which the agent operates only on a cloud-hosted copy of the repository. The repository itself must still be free of sensitive or restricted data, since its contents go to the provider. Until your institution establishes vetted routes, the rule is that agents and sensitive or restricted data live on separate machines.
Exercise: Get your agent running, safely (8 minutes)
The goal is an agent open on your own repository, in a cloud session, before any real work begins. Other routes, including free ones, are on the setup page.
-
Choose a cloud route with plan mode.
- Claude users: use the web, claude.ai/code. Plan mode is built in and nothing needs to be installed. The desktop app is also acceptable, provided the session runs in a cloud-hosted VM.
- Copilot users: install the GitHub Copilot app for an explicit plan mode, in which the agent asks questions and waits for your approval before editing. Start a cloud session, not a local one: cloud runs in a GitHub-hosted VM; local runs on your machine with your access. If you cannot install, the web works too: ask for a plan in the prompt and you get the same review gate, with less interaction.
- Confirm the session is cloud-hosted before you prompt. It should be pointed at a cloud VM and a GitHub repository, not a folder on your laptop. Check the environment’s network access setting (limit 1 above) while you are there.
- Use your project repository. If your team does not have one yet, create your own for today.
- Work on a branch named for you. Use branches rather than forks, so teammates and their agents can see your work. If you lack write access, ask the repository owner to add you as a collaborator.
-
No
.envin the repository. If you use one, add it to.gitignore. Preferop readfrom the 1Password CLI to storing keys anywhere. - Prompt: “Read this repo and tell me what it’s doing, or attempting to do. Do not change anything.” While it works, note what it gets right, what it states confidently that you cannot verify, and what it does not know that you do.
If step 2 fails (the session is local), stop and switch before prompting. This is the most common mistake with the desktop apps. Cloud sandboxes are billed by usage, so confirm your account works before the real work. Branches rather than forks matter for a team: an agent can fetch and read a teammate’s branch, compare against it, and merge, but it cannot see a fork without additional remotes being configured. The read-only prompt in step 6 previews the planning episode. Agents describe what and how well (structure, dependencies, data flow) and cannot recover why or for whom; that is the knowledge a plan and an instruction file must supply.
- Institutional data policies apply unchanged: sensitive or restricted data stays away from unvetted AI services, on every route.
- Agents run with your permissions and scan your workspace for context. Assume anything on disk in plaintext can be read.
- Prompt injection is the threat: anything the agent reads can carry instructions. Treat it all as untrusted, keep the web session’s firewall on, and review the pull request before merging.
- Six limits determine how much damage injection can do: network allowlist, cloud VM, command rules, password-manager credentials, branch plus pull request, and whom you trust.
- Instructions influence behavior; permissions constrain it. A rules file is text the model weighs, not an ability it lacks.
- The only secret an agent cannot leak is one that is not there. Load
keys at runtime with
op read. - First session: a cloud route with plan mode, confirm the session is
cloud-hosted, a branch named for you, no
.env.