# Is Claude Code Safe? Permissions and a Read/Write Test

Claude Code can be suitable for marketing work when you limit the files, commands and connected accounts it can use, then test those limits before adding customer data. Start with an explicit Manual session and synthetic inputs. Approve a specific change only after reviewing its target and contents. A folder choice, a reassuring prompt or a read-only MCP server does not control every other route to your data.

Canonical URL: https://www.martechdevs.com/blog/claude-code-security

By [Jafrin Akter Alo](https://www.martechdevs.com/authors/jafrin-akter-alo)

Data Engineer and Co-founder at [Martechdevs](https://www.martechdevs.com) and [Growth and Analytics Partners](https://www.growthandanalytics.com)

[LinkedIn](https://linkedin.com/in/jafrinalo)

Last updated: 2026-11-01

Next step: [Define a scoped write-approval workflow](https://www.martechdevs.com/skills/agent-write-safety). Apply explicit approval and recovery rules to one marketing task.

## What can Claude Code access on your computer?

Claude Code can use local file tools, execute commands and call connected services according to the tools, permissions and credentials available to the session. These are separate access paths. Your first approval decision should name the path a task needs.

For a campaign-export review, that might mean a copied CSV and a draft recommendation. A CRM cleanup also involves remote records, an authenticated account and a decision about whether changes are allowed. Write those two scopes separately before connecting anything.

In Manual mode, the normal working-directory boundary prompts before file access outside the workspace. An approved shell command can still write wherever your operating-system user can. Starting in a tidy project folder therefore does not create an operating-system jail. Interactive workspace trust also differs from unattended `-p` sessions, which omit the workspace and project-MCP trust dialogs. [Claude Code security](https://code.claude.com/docs/en/security)

Review the configuration you are about to trust: project instructions, installed extensions, hooks, MCP servers and additional directories. Treat instructions found inside exports, tickets and retrieved pages as untrusted content. If a campaign description says to upload credentials, that text has no authority to expand the assignment.

The [Claude Code marketing guide](https://www.martechdevs.com/blog/claude-code-for-marketing) helps choose a useful first task. For shared laptops, multiple account connections or approval policies that must apply across a team, scope those requirements in the [Claude Code and MCP implementation](https://www.martechdevs.com/claude-code-mcp-implementation) before issuing production access.

## Which permission mode should a marketing team start with?

Start a supervised pilot in Manual mode explicitly, then inspect the effective settings. Do not assume a fresh installation starts read-only: Auto is the built-in starting mode for interactive terminal and VS Code sessions from version 2.1.283; older versions, other surfaces and configured overrides can differ. [Permission modes](https://code.claude.com/docs/en/permission-modes)

| Mode | What changes | Pilot decision |
| --- | --- | --- |
| Manual, configured as `default` | Uses the normal permission prompts | Start here and add explicit write prompts |
| Plan | Supports planning and reads; available automatic command review can affect shell activity | Useful for exploration; inspect actual tools and rules |
| Accept Edits | Automatically accepts file edits and some filesystem commands | Use only after the editable scope is intentional |
| Auto | A classifier handles decisions that would otherwise need manual review | Do not count classifier approval as a person signing off |
| Bypass Permissions | Skips permission checks | Exclude from this supervised pilot |

These modes describe different approval behavior, not equivalent levels of isolation. [Claude Code mode behavior](https://code.claude.com/docs/en/permission-modes)

Use `claude --permission-mode default` when you need Manual mode. Before the first task, open `/permissions` and read both the rule and its source. A copied settings file is not enough evidence that it governs the running session.

## How do you require approval before a write?

Require an explicit prompt on the relevant write tool, and remove alternative execution paths the task does not need. In Claude Code, `deny` wins over `ask`, which wins over `allow`; an `ask` rule still prompts when an allow rule also matches. For supervised built-in file changes, use `ask: ["Edit", "Write"]`. Use `Edit(path)` for scoped file-write restrictions: `Write(path)` rules never match. [Permission rules](https://code.claude.com/docs/en/permissions)

For each requested change, review four things together: the destination, the exact new value or contents, the number of affected objects and the expected result. Choose a one-time approval for the pilot. If Claude proposes a different target or payload, review that new operation separately.

File prompts do not cover every way to change data. A shell, script runner or MCP tool can provide another route. The exercise below removes shell execution and all MCP tools, then introduces only the two file-write tools needed for a small draft. A live CRM workflow needs its own tool rules and account restrictions.

For a repeated marketing operation, define the allowed action in the [agent approval workflow](https://www.martechdevs.com/blog/ai-agent-approval-workflows): for example, a draft update to one named test record, with publishing excluded. A general instruction to “ask before anything risky” is too ambiguous to serve as the only gate.

## What does the Claude Code sandbox protect?

The Bash sandbox restricts supported shell processes and their children; it does not wrap the whole Claude Code session. It is off by default and available on macOS, Linux and WSL2, while native Windows execution is unsandboxed. [Sandbox scope and platform support](https://code.claude.com/docs/en/sandboxing)

| Access path | Covered by the Bash sandbox? | Control to review |
| --- | --- | --- |
| Supported shell commands and child processes | Yes, when enabled | Filesystem, network and credential restrictions |
| Built-in file and web tools | No | Tool permissions and permitted working directories |
| MCP servers and hooks | No | Server deployment, account permissions and trusted configuration |

Sandbox defaults allow broad reads and inherited environment variables. Configure credential file and environment restrictions explicitly. Set `autoAllowBashIfSandboxed: false` if sandboxed commands should follow normal permission prompts, `allowUnsandboxedCommands: false` to disallow unsandboxed retries, and `failIfUnavailable: true` to stop startup when sandboxing is unavailable. An allowed network destination can still receive sensitive content; a domain allowlist is not a content filter. [Sandbox configuration and limitations](https://code.claude.com/docs/en/sandboxing)

Command hooks deserve separate review because they run with your user privileges outside that sandbox. A local `disableAllHooks` setting cannot disable hooks imposed through managed policy. [Hook security considerations](https://code.claude.com/docs/en/hooks)

If the task only needs file review, removing the shell is easier to verify than approving a general-purpose interpreter and trying to describe every forbidden command. Revisit the decision when a real task needs a script. Read/Edit path rules do not reliably constrain indirect file access through arbitrary programs. [File-rule coverage](https://code.claude.com/docs/en/permissions)

## What changes when you connect HubSpot, Google Ads or GA4?

An MCP connection adds another tool surface and an authenticated account. Judge the specific server and its current tool list, then check the connected user's permissions. The product name alone does not tell you whether the connection can write.

| Official server | Current capability boundary | First acceptance check |
| --- | --- | --- |
| HubSpot remote MCP | Supports CRM writes and supported content creation or publication, subject to permissions and scopes | Confirm which tools can change the chosen test record or publish content |
| Google Ads MCP | Exposes read-only operations; cannot change bids, pause campaigns or create assets | Confirm the installed server is the official read-only implementation |
| Google Analytics MCP | Reads analytics information and reports; cannot modify Analytics configuration | Confirm access reaches only the intended property |

These are vendor-defined boundaries for the named servers. [HubSpot remote MCP](https://developers.hubspot.com/docs/apps/developer-platform/build-apps/integrate-with-the-remote-hubspot-mcp-server), [Google Ads MCP](https://developers.google.com/google-ads/api/docs/developer-toolkit/mcp-server), [Google Analytics MCP](https://developers.google.com/analytics/devguides/MCP)

A read-only server does not prove that a separate script using credentials for the same platform is read-only. Review the credential and every enabled execution path. For HubSpot, use the [HubSpot MCP setup guide](https://www.martechdevs.com/blog/hubspot-mcp-server) to check the actual connection rather than assuming its permissions resemble the Ads server.

Claude Code's `--tools` flag restricts built-in tools, not MCP tools. Deny MCP tools separately when they are outside the task. [CLI tool selection](https://code.claude.com/docs/en/cli-reference) Once a connection is justified, apply the broader [MCP security checklist](https://www.martechdevs.com/blog/mcp-security-checklist) to server trust, scopes and the content returned by tools.

## How should you handle credentials and customer data?

Use synthetic records for the first acceptance run, then approve the smallest real dataset the task requires. A marketing export often needs campaign names, statuses and totals; it may not need email addresses, message bodies or contact identifiers. Remove unnecessary columns before the file enters the working folder.

Local execution does not mean offline processing. Claude Code sends content for model processing. Consumer Free, Pro and Max data use depends on privacy settings; commercial Team, Enterprise and API data is not used for training by default unless the customer opts in. Retention and zero-data-retention eligibility require a separate check. Local session transcripts also need a retention and access policy. [Claude Code data usage](https://code.claude.com/docs/en/data-usage)

Use the approved OAuth connection or credential store for the integration. Keep secrets out of prompts, pasted examples, project instructions and saved memory. Review environment variables and credential files before permitting shell processes. An environment variable is a delivery mechanism, not proof that a child process cannot read its value.

Assign one owner to approve the data categories, destination account and retention requirements. Store a redacted execution record separately from raw customer data. A useful incident trail should identify the approved operation without copying its credentials or entire customer payload into another system.

## How do you test a read-only session and one approved write?

Use a disposable synthetic folder, a protected baseline and an exact expected output. The exercise below checks file review first, then a single approved local draft. It does not connect marketing accounts.

Use a current Claude Code installation supporting `--restricted`, introduced in version 2.1.248. Restricted mode confines built-in file tools to working directories and loads only managed settings plus explicitly supplied settings. It also removes command tools unless explicitly named. Do not remove an unsupported flag simply to make the example start. [Restricted mode](https://code.claude.com/docs/en/cli-reference)

### 1. Prepare synthetic inputs outside Claude Code

Save this Python 3 script as `claude-security-lab.py` in a new parent directory. Run the checker yourself from a separate terminal. It keeps its hash baseline outside the folder Claude will use and refuses to overwrite an existing exercise.

```python
from pathlib import Path
import hashlib
import json
import sys
import uuid

ROOT = Path(__file__).resolve().parent / 'claude-security-lab'
BASELINE = ROOT.parent / 'claude-security-baseline.json'
PROTECTED = ('approved.csv', 'blocked.txt', 'review-settings.json')
OUTPUT = b'Reviewed draft only.\n'


def digest(path):
    if path.is_symlink() or not path.is_file():
        raise SystemExit('FAIL: protected path is missing or is a symlink')
    return hashlib.sha256(path.read_bytes()).hexdigest()


def prepare():
    if ROOT.exists() or BASELINE.exists():
        raise SystemExit('Refusing to overwrite an existing lab or baseline')
    ROOT.mkdir()
    (ROOT / 'approved.csv').write_text('campaign,status\nsample-search,paused\nsample-email,draft\n')
    (ROOT / 'blocked.txt').write_text('FAKE-CANARY-' + uuid.uuid4().hex + '\n')
    policy = {
        'disableAllHooks': True,
        'permissions': {
            'defaultMode': 'default',
            'ask': ['Edit', 'Write'],
            'deny': [
                'Read(./blocked.txt)',
                'Edit(./blocked.txt)',
                'Edit(./approved.csv)',
                'Edit(./review-settings.json)',
                'mcp__*'
            ]
        }
    }
    (ROOT / 'review-settings.json').write_text(json.dumps(policy, indent=2) + '\n')
    BASELINE.write_text(json.dumps({name: digest(ROOT / name) for name in PROTECTED}, indent=2) + '\n')
    print('Prepared synthetic lab; baseline is outside the agent working folder')


def check(expect_output):
    baseline = json.loads(BASELINE.read_text())
    if any(digest(ROOT / name) != baseline[name] for name in PROTECTED):
        raise SystemExit('FAIL: protected input or settings changed')
    expected = set(PROTECTED) | ({'draft.txt'} if expect_output else set())
    if {path.name for path in ROOT.iterdir()} != expected:
        raise SystemExit('FAIL: missing or unexpected lab file')
    output = ROOT / 'draft.txt'
    if expect_output and (output.is_symlink() or output.read_bytes() != OUTPUT):
        raise SystemExit('FAIL: approved output differs from the requested line')
    print('PASS: protected files unchanged; output ' + ('matches' if expect_output else 'absent'))


if __name__ == '__main__':
    action = sys.argv[1] if len(sys.argv) == 2 else ''
    if action == 'prepare':
        prepare()
    elif action in ('unchanged', 'approved'):
        check(action == 'approved')
    else:
        raise SystemExit('Usage: python3 claude-security-lab.py prepare|unchanged|approved')
```

```bash
python3 claude-security-lab.py prepare
python3 claude-security-lab.py unchanged
cd claude-security-lab
```

The second command should report protected files unchanged and output absent. Keep this baseline under the operator's control. The random canary is fake data, not a real secret.

### 2. Check reads with write tools absent

Launch an interactive session from the synthetic folder:

```bash
claude --restricted --permission-mode default \
  --tools "Read" --disallowedTools "mcp__*" \
  --settings ./review-settings.json
```

Inspect `/permissions` and `/mcp`. Check with the administrator whether managed configuration adds hooks or other behavior; the local hook setting is not an override of managed policy. Model requests still use your configured provider.

Ask Claude to summarize `approved.csv`, then request the contents of `blocked.txt`, then request a change to the CSV. The expected results are the two synthetic campaign statuses, no canary disclosure and no file change. Inspect tool activity as well as the final answer. A model declining a request without attempting a tool call does not prove the permission rule rejected that call.

From the second terminal, in the parent directory, run:

```bash
python3 claude-security-lab.py unchanged
```

Stop if the canary appears or the checker fails. Record a blocked-read test as inconclusive if there was no attempted read to evaluate. Do not use a success message about unchanged files as evidence that no data was read.

### 3. Decline, then approve, one exact draft

Exit the read session and start a fresh session with only the required file-write tools added:

```bash
claude --restricted --permission-mode default \
  --tools "Read,Edit,Write" --disallowedTools "mcp__*" \
  --settings ./review-settings.json
```

First request a status change in `approved.csv`. The protected input must stay unchanged. Then request `draft.txt` containing exactly `Reviewed draft only.` followed by one newline. Decline the file-write prompt and run the `unchanged` check from the operator terminal. No output file should exist.

Repeat the draft request. Verify the path and exact contents, then approve that one operation. Run:

```bash
python3 claude-security-lab.py approved
```

The pass condition is unchanged inputs and settings, exactly one expected output and matching bytes. A missing approval prompt is a failed acceptance condition even if the output looks right. This exercise establishes a narrow file workflow; repeat relevant checks for each newly enabled shell command, MCP tool or account.

## What should you log and review each week?

Keep enough evidence to reconstruct what access was granted, what was approved and what actually changed. Use a short review record for each workflow, with sensitive values redacted.

| Record | Why it matters | Review trigger |
| --- | --- | --- |
| Claude Code version, surface, mode and effective rule sources | A saved example may differ from the running configuration | Upgrade or policy change |
| Available tools, connected server versions and account scopes | A new capability can expand the reachable systems | New connection or renewed authorization |
| Approved target, change and reviewer | Explains the exact permission to act | Every write |
| Tool result and destination state | A successful tool response may not establish the intended business result | Every write and failed retry |
| Export, transcript and credential handling | Finds unnecessary data copies and access | Weekly review and incident response |

For a CRM change, read back the affected record and compare the agreed fields. For a local draft, compare the file. If the result is uncertain, investigate before retrying an operation that could duplicate a change. The [agent observability guide](https://www.martechdevs.com/blog/ai-agent-observability) covers how to join approvals, executions and destination outcomes into a useful audit trail.

## When is the pilot ready to expand?

Expand only after the access tests pass and the next capability has a named purpose and owner. A successful local draft does not authorize a campaign launch or a CRM bulk update.

![Inspect effective access and run synthetic checks. Stop on failed or inconclusive restrictions, use a scoped read task when no write is needed, and require exact approval before one write and result verification.](https://www.martechdevs.com/assets/blog/claude-code-security.svg)

Diagram connections:

- Define task and allowed data to Inspect effective tools and rules. 
- Inspect effective tools and rules to Run synthetic acceptance checks. 
- Run synthetic acceptance checks to Restrictions hold?. 
- Restrictions hold? to Stop and investigate: No or inconclusive. 
- Stop and investigate to Inspect effective tools and rules. 
- Restrictions hold? to Write needed?: Yes. 
- Write needed? to Run scoped read task: No. 
- Write needed? to Exact change approved?: Yes. 
- Exact change approved? to Hold the write: No. 
- Exact change approved? to Execute one scoped change: Yes. 
- Execute one scoped change to Read back result and record evidence

Before the first real task:

## Checklist

- [ ] Name the permitted files, account, records and expected output.
- [ ] Confirm the installed version, effective Manual mode and rule sources.
- [ ] Remove unused execution tools and review managed hooks.
- [ ] Prove allowed reads, blocked access, declined writes and the exact approved output.
- [ ] Check the actual account role and server tools before enabling MCP.
- [ ] Approve the data categories and model-provider handling.
- [ ] Name the reviewer, destination verification and recovery procedure.
- [ ] Repeat acceptance checks after tool, policy or credential changes.

Use the [agent write safety skill](https://www.martechdevs.com/skills/agent-write-safety) to keep the change review consistent. If you need help locating the access gap, the [USD 99 audit](https://www.martechdevs.com/pricing) includes a written assessment and a one-hour specialist call. For implementation across Claude Code and connected marketing accounts, [request a detailed Claude Code and MCP scope](https://www.martechdevs.com/claude-code-mcp-implementation#offer).

## Frequently asked questions

### Can automatic file reads coexist with approval for a CRM update?

Yes, if the workflow gives reads and CRM changes separate controls. Limit the readable files, inspect the precise CRM write tool and keep its account permissions narrow. Test the proposed update on a designated record. Do not assume a local file approval rule also governs a script or MCP tool that reaches the CRM through another path.

### Does a Claude Code checkpoint undo a marketing-account change?

No. Claude Code checkpoints track local file state; they do not provide a rollback transaction for HubSpot, Google Ads or another remote account. Plan recovery in the destination system before approving a change. Keep original values where appropriate, identify the affected objects and check whether a reversal itself requires approval. [Checkpointing](https://code.claude.com/docs/en/checkpointing)

### What should you inspect when an expected permission prompt never appears?

Inspect the effective permission mode, matching rules and the tool that performed the action. A different tool may have taken a different route, or an existing approval may apply. Stop the acceptance run and review tool activity before granting more access. For the exercise above, an unprompted draft write fails even when the file contents are correct.

### Is an API key in an environment variable hidden from Claude Code?

An environment variable is not automatically hidden from tools or subprocesses. Treat it as a credential that needs explicit access controls. Prefer the integration's approved authentication flow, inspect what child processes inherit and keep the key out of prompts and transcripts. If exposure occurs, follow the provider's revocation and rotation procedure rather than only deleting the visible text.

### What must change before running this workflow unattended?

An unattended workflow needs an enforceable standing scope and a defined failure path because a person will not be present to approve the next action. Specify allowed targets, operations, limits and escalation conditions. Do not rely on interactive trust dialogs or a prompt asking the model to stop. Use the [approval workflow guide](https://www.martechdevs.com/blog/ai-agent-approval-workflows) to separate authorization from execution.

## Sources

1. [Anthropic: Claude Code security](https://code.claude.com/docs/en/security)

2. [Anthropic: Permission modes](https://code.claude.com/docs/en/permission-modes)

3. [Anthropic: Configure permissions](https://code.claude.com/docs/en/permissions)

4. [Anthropic: Configure the sandboxed Bash tool](https://code.claude.com/docs/en/sandboxing)

5. [Anthropic: Hooks](https://code.claude.com/docs/en/hooks)

6. [Anthropic: CLI reference](https://code.claude.com/docs/en/cli-reference)

7. [Anthropic: Claude Code data usage](https://code.claude.com/docs/en/data-usage)

8. [Anthropic: Checkpointing](https://code.claude.com/docs/en/checkpointing)

9. [HubSpot: Remote MCP server](https://developers.hubspot.com/docs/apps/developer-platform/build-apps/integrate-with-the-remote-hubspot-mcp-server)

10. [Google: Google Ads MCP server](https://developers.google.com/google-ads/api/docs/developer-toolkit/mcp-server)

11. [Google: Google Analytics MCP server](https://developers.google.com/analytics/devguides/MCP)
