DEV Community

Ahab
Ahab

Posted on • Originally published at indieseek.co

Claude Code 2.1.221 Credential File Masking Checklist

Claude Code 2.1.221: mask credential files before sandboxed commands read them

Quick answer

Claude Code 2.1.221 adds mode: "mask" for sandbox credential files on Linux and WSL2. A sandboxed command reads a sentinel copy instead of the real credential. When the command sends an allowed request, the sandbox proxy can substitute the real value on egress. On macOS, file masking falls back to deny, so the command cannot read the credential file.

Do not treat this as a transparent toggle. Pin 2.1.221 or newer, put the rule in a trusted settings scope, use a disposable credential, narrow injectHosts, enable the TLS-terminating proxy required for credential substitution, and make extraction failures stop the canary. The rollout passes only when the command never sees or logs the real value, the approved request authenticates, and every unapproved path fails.

At publication time, Anthropic's general sandbox guide still said file entries supported only deny, while the tagged 2.1.221 release and packaged settings schema exposed file masking. Treat that documentation lag as a reason to verify the exact installed build and effective schema before production rollout.

Who this is for

This guide is for developers and platform teams that let Claude Code run gh, npm, cloud CLIs, or internal API clients inside the Bash sandbox. It is most useful when a command needs authenticated access but the agent should never receive the underlying token.

Credential masking complements the strict network allowlist checklist. The allowlist controls where a sandboxed command can connect; masking controls whether the command holds the real credential. Keep the broader sandbox and worktree regression checks for filesystem and session boundaries.

What changed, and what did not

Before 2.1.221, Claude Code documented two practical credential paths: deny a credential file entirely, or mask an environment variable and let the proxy inject its value. The new release extends the sentinel-and-proxy pattern to files on Linux and WSL2.

Mode What the command reads Can an approved request authenticate? Boundary
File deny Read fails No Works as a hard block
Whole-file mask One sentinel copy Yes, after proxy substitution Best for a file containing one secret
Structured file mask File syntax with captured secret spans replaced Yes, after proxy substitution Requires an extract regex
macOS file mask Read is denied No Currently degrades to deny

The packaged 2.1.221 schema describes extract as a global regular expression whose capture group 1 identifies each credential value. Without extract, the entire file is replaced by one sentinel. It also exposes onExtractNoMatch, maskDuplicates, and injectHosts controls.

Masking does not make an allowed host safe, inspect the meaning of a request, restrict MCP tools, or protect Claude's built-in file tools. It applies to sandboxed commands. Continue to use least-privilege tokens, repository rules, deterministic tests, and human approval for consequential actions.

A fail-closed rollout workflow

1. Freeze the runtime and platform

Record the exact Claude Code version, install channel, OS, WSL version if applicable, and settings sources. Restart long-running sessions after upgrading. Do not infer the active version from a package lock or another terminal.

2. Start with a disposable credential file

Create a token that can access only a harmless canary endpoint and can be revoked immediately. Use one exact file path, not a directory. Never test with a developer's GitHub, npm, cloud, or production credential.

3. Choose whole-file or structured masking

Use whole-file masking only when the file contains a single credential and the client accepts that shape. For .netrc, JSON, YAML, or key-value files, use extract so the client still sees valid syntax. Capture only the secret in group 1.

Set onExtractNoMatch to error for the canary. The packaged schema says the default warn behavior can leave an unmatched file readable inside the sandbox. A changed file format must block startup, not silently expose the original file. Use maskDuplicates only for long, high-entropy values because replacing short repeated text can corrupt unrelated fields.

{
  "sandbox": {
    "enabled": true,
    "failIfUnavailable": true,
    "network": {
      "tlsTerminate": {},
      "allowedDomains": ["api.example.test"]
    },
    "credentials": {
      "files": [
        {
          "path": "~/.config/example/credentials",
          "mode": "mask",
          "extract": "token\\s*=\\s*([A-Za-z0-9_-]+)",
          "onExtractNoMatch": "error",
          "maskDuplicates": true,
          "injectHosts": ["api.example.test"]
        }
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Replace the example host and pattern with your controlled canary. Keep credential-injection rules in user, managed, or explicit CLI settings rather than a repository-controlled file.

4. Bind substitution to narrow egress

Every injectHosts entry must also be reachable through network.allowedDomains. Prefer the exact API hostname rather than a broad wildcard. Credential substitution requires the proxy to see request contents, so configure network.tlsTerminate; without it, the sentinel should reach the server unchanged and authentication should fail.

5. Run six negative and positive tests

Use a sink you control and preserve only redacted evidence:

  1. Reading the file inside the sandbox shows valid structure and a sentinel, never the disposable token.
  2. Logs, traces, process arguments, and error messages contain no disposable token.
  3. A request to the exact allowed host authenticates through proxy substitution.
  4. The same request to a different host does not receive the real token.
  5. Change the file so extract matches nothing; sandbox setup must stop.
  6. Repeat on every supported execution surface. macOS must deny the file instead of pretending masking works.

6. Roll out with a deny fallback

Start with one non-production workflow. If the build, platform, TLS proxy, schema, or redacted evidence cannot be proven, switch the file entry to deny and route the authenticated action through a narrow MCP/custom tool or an external credential-injecting proxy. The untrusted repository sandbox checklist remains the outer boundary.

Eight rollout gates

Gate Required evidence
Version The launching process reports 2.1.221 or newer
Platform Linux or WSL2 is used for file masking; macOS is treated as deny
Settings trust The injection rule is not controlled by repository content
Sentinel The command never reads or logs the real credential
Extraction Group 1 captures only the secret; no-match stops the run
Egress injectHosts is narrower than or equal to allowed domains
TLS Proxy substitution works only on the intended TLS-terminated path
Fallback Revocation and file deny or external-tool fallback are tested

Common mistakes

Testing with a real account token. A canary is supposed to be safe when a log, regex, or proxy rule is wrong.

Leaving no-match behavior at a fail-open default. Credential file formats change. Make an unmatched extraction block the rollout.

Using a broad allowed domain. Masking limits who holds the token, not what an authenticated request can do. Keep both the host and token scope narrow.

Assuming macOS parity. Version 2.1.221 explicitly falls back to deny on macOS. An authentication failure there is expected, not proof of substitution.

FAQ

Does masking prevent Claude Code from ever seeing the credential?

It protects the real value from sandboxed Bash commands when the configured file, proxy, and host path behave as expected. It does not cover built-in tools, MCP servers, other unsandboxed processes, or credentials stored somewhere you did not list.

Should I replace file deny rules immediately?

No. Keep deny when the command does not need the credential. Use mask only for an authenticated workflow that passes the sentinel, extraction, egress, TLS, logging, and rollback gates.

Sources

Top comments (0)