AI coding assistants like Claude Code, Cursor, and GitHub Copilot can read, modify, and index every file in your project — including .env files, API keys, credentials, and other secrets. This guide shows you how to restrict file access for every major AI coding tool.
Why File Protection Matters
When an AI assistant reads your .env file, several things can go wrong:
- Secrets in context — API keys, database passwords, and tokens are sent to the AI provider's servers as part of the prompt
- Accidental edits — the AI might modify or overwrite credential files
- Leaking into suggestions — secrets seen in context can appear in code completions or chat responses
- Compliance violations — sending credentials to third-party APIs may violate SOC 2, HIPAA, or PCI DSS requirements
The solution: tell each tool which files it must never touch.
Claude Code: Permission Deny Rules and Hooks
Claude Code uses permission deny rules in settings JSON files to restrict file access. There is no .claudeignore file — all restrictions are configured through permissions.deny arrays.
Setting Up Deny Rules
Add deny rules to .claude/settings.json (shared with team) or .claude/settings.local.json (personal, gitignored):
json{ "permissions": { "deny": [ "Read(.env)", "Read(.env.*)", "Edit(.env)", "Edit(.env.*)", "Read(secrets/**)", "Edit(secrets/**)", "Read(*.pem)", "Read(*.key)", "Edit(*.pem)", "Edit(*.key)", "Read(serviceAccountKey.json)", "Edit(credentials/**)" ] } }
Path Pattern Types
| Pattern | Meaning | Example |
|---|---|---|
path | Relative to current directory | Read(.env) |
/path | Relative to project root | Edit(/config/secrets.json) |
//path | Absolute filesystem path | Read(//Users/alice/secrets/**) |
~/path | Relative to home directory | Read(~/Documents/*.pdf) |
Global Deny Rules
For rules that apply across all projects, add them to ~/.claude/settings.json:
json{ "permissions": { "deny": [ "Read(**/.env)", "Read(**/.env.*)", "Edit(**/.env)", "Edit(**/.env.*)", "Read(**/*.pem)", "Read(**/*.key)" ] } }
PreToolUse Hook: Block Edits to Protected Files
For an additional layer, add a hook that blocks Claude from writing to sensitive files. This runs before every Edit or Write tool call.
Save as .claude/hooks/protect-files.sh:
bash#!/bin/bash INPUT=$(cat) TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty') if [ "$TOOL" != "Edit" ] && [ "$TOOL" != "Write" ]; then exit 0 fi FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty') PROTECTED_PATTERNS=( "*.env" "*.env.*" "*.pem" "*.key" "*/secrets/*" "*/credentials/*" ) for pattern in "${PROTECTED_PATTERNS[@]}"; do if [[ "$FILE_PATH" == $pattern ]]; then echo "BLOCKED: Cannot edit protected file $FILE_PATH" >&2 exit 2 fi done exit 0
Make it executable and register in .claude/settings.json:
bashchmod +x .claude/hooks/protect-files.sh
json{ "hooks": { "PreToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/protect-files.sh" } ] } ] } }
Limitations
Readdeny rules are best-effort for Grep and Glob tools — not guaranteed enforcement- Bash commands can still access files via
catorlessunless you also addBashdeny rules - Deny rules always win over allow rules (evaluation order: deny > ask > allow)
Cursor: .cursorignore
Cursor provides .cursorignore to exclude files from AI context, and .cursorindexingignore to block indexing only.
.cursorignore
Create a .cursorignore file in your project root:
text# Environment and secrets .env .env.* *.pem *.key # Cloud credentials credentials/ secrets/ serviceAccountKey.json # Build artifacts with potential embedded secrets .next/ dist/
Files matching these patterns are excluded from:
- Codebase indexing and semantic search
- Tab completions, Agent, and Inline Edit features
@codebasementions in chat
.cursorindexingignore
If you only want to block indexing but still allow direct reads:
text# Block indexing of large generated files dist/ *.min.js
Global Ignore Patterns
Set global patterns in Cursor Settings that apply to all projects:
text**/.env **/.env.* **/credentials.json **/*.key **/*.pem
Cursor Rules Reinforcement
Add explicit instructions in .cursor/rules/security.mdc:
markdown--- description: Security rules for file access globs: * alwaysApply: true --- NEVER read, reference, or modify .env files. Use .env.example for variable names. Never hardcode secret values.
Limitations
.cursorignoreis best-effort — Cursor's docs explicitly state "complete protection isn't guaranteed due to LLM unpredictability"- Terminal commands executed by Agent are not blocked
- MCP server tool calls are not blocked
- Negation patterns cannot re-include files when a parent directory is excluded
GitHub Copilot: Content Exclusion
GitHub Copilot supports content exclusion configured through the GitHub web interface — not through a file in your repository.
Organization-Level Settings
Admins configure content exclusion at github.com > Organization > Settings > Copilot > Content exclusion:
yaml# Exclude .env files across all repos "*": - "**/.env" - "**/.env.*" - "**/*.pem" - "**/*.key" # Exclude secrets directory in a specific repo my-app: - "/secrets/**" - "credentials.json"
Repository-Level Settings
Repository admins can also set exclusions at Settings > Copilot > Content exclusion with simpler path-per-line syntax:
text"/secrets/**" "*.key" "*.pem" ".env*"
.github/copilot-instructions.md
Add explicit instructions as a supplementary layer:
markdown## Security - Never reference or include values from .env files - Use environment variables (process.env.X) instead of hardcoded values - Reference .env.example for variable names, never .env
Requirements and Limitations
- Content exclusion requires Copilot Business or Enterprise plan — individual plans cannot configure it
- Changes take up to 30 minutes to propagate to IDEs
- Does not block Copilot coding agent, agent mode in chat, or edit mode
- Type information from IDE semantics may still leak through hover definitions
Windsurf: .codeiumignore
Windsurf uses .codeiumignore (not .windsurfignore) to exclude files from AI context. It also respects .gitignore patterns automatically.
.codeiumignore
Create in your project root:
text# Secrets .env .env.* *.pem *.key secrets/ credentials/
Global Rules
Place a .codeiumignore file in ~/.codeium/ for rules that apply to all projects.
What It Blocks
- Cascade viewing, editing, and creating files in matched paths
- Embedding and indexing of matched files
Windsurf Rules Reinforcement
In .windsurf/rules/security.md:
markdownNever read or modify .env files. Use .env.example for variable names. Never hardcode secret values in generated code.
Limitations
- Negation patterns (
!pattern) do not reliably override.gitignorerules - No detailed official documentation with comprehensive syntax examples exists
Cline: .clineignore
Cline uses .clineignore to block both reading and writing of matched files.
.clineignore
Create in your project root:
text.env .env.* *.pem *.key secrets/ credentials/ serviceAccountKey.json
What It Blocks
read_file— cannot read contentwrite_to_file— cannot writeapply_diff/replace_in_file— cannot modify- Automatic context gathering
What It Does Not Block
list_files— ignored files still appear in directory listings (marked with a lock icon)- Explicit
@mentions can still reference ignored files
Limitations
- Introduced in Cline v3.3.0
.clineignoreis monitored for changes and updates dynamically
The .env.example Pattern (Works Everywhere)
Across all tools, the most reliable practice is maintaining a .env.example that documents variables without values:
bash# .env.example — safe to commit DATABASE_URL=postgresql://user:password@localhost:5432/mydb AI_GATEWAY_API_KEY=your-api-key-here STRIPE_SECRET_KEY=sk_test_... GITHUB_TOKEN=ghp_...
Then in your agent rules (any format):
markdownWhen you need environment variable names, read .env.example — never .env. Never hardcode secret values. Always use process.env.VARIABLE_NAME.
This works for every AI tool regardless of whether ignore files are configured.
Quick Reference: File Protection by Tool
| Tool | Config Location | Syntax | Blocks Reading | Blocks Writing | Enforcement |
|---|---|---|---|---|---|
| Claude Code | .claude/settings.json | JSON deny rules | Yes (best-effort for Grep/Glob) | Yes | Deny rules + hooks |
| Cursor | .cursorignore file | .gitignore syntax | Best-effort | Best-effort | LLM-dependent |
| GitHub Copilot | GitHub Settings UI | fnmatch YAML | Context only | N/A | Business/Enterprise only |
| Windsurf | .codeiumignore file | .gitignore syntax | Yes | Yes | Best-effort |
| Cline | .clineignore file | .gitignore syntax | Yes | Yes | Pre-execution enforcement |
Important: No AI coding tool provides guaranteed security enforcement for file access. All restrictions should be treated as defense-in-depth, not security boundaries. Keep secrets outside the project directory or use environment variable injection from secret managers.
Recommended Setup for Any Project
- Configure tool-specific restrictions for every AI tool your team uses
- Create a
.env.examplewith variable names but no values - Add security rules to your agent rules file
- Add a pre-commit hook to prevent accidental secret commits:
bash#!/bin/bash # .git/hooks/pre-commit if git diff --cached --name-only | grep -qE '\.env$|\.env\.|\.pem$|\.key$'; then echo "ERROR: Attempting to commit sensitive files." echo "Remove them from staging: git reset HEAD <file>" exit 1 fi
- Add to .gitignore as the last line of defense:
text.env .env.* *.pem *.key secrets/
Defense in depth: even if one layer fails, the others catch it.