claude-code - 💡(How to fix) Fix Shell snapshots base64-encode function bodies, making obfuscated content indistinguishable from a malicious payload [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#50733Fetched 2026-04-20 12:14:34
View on GitHub
Comments
0
Participants
1
Timeline
5
Reactions
1
Author
Participants
Timeline (top)
labeled ×4subscribed ×1

Claude Code's shell-snapshot files (at ~/.claude/shell-snapshots/snapshot-bash-<id>-<token>.sh) serialize captured shell functions by base64-encoding the function body and wrapping it in an eval "$(echo '<base64>' | base64 -d)" invocation.

This happens even for entirely benign functions captured from the user's interactive shell environment. Example — a literal line from one of my snapshot files:

eval "$(echo 'Z2F3a2xpYnBhdGhfYXBwZW5kICgpIAp7IAogICAgWyAteiAiJEFXS0xJQlBBVEgiIF0gJiYgQVdL
TElCUEFUSD1gZ2F3ayAnQkVHSU4ge3ByaW50IEVOVklST05bIkFXS0xJQlBBVEgiXX0nYAogIGV4cG9y
dCBBV0tMSUJQQVRIPSIkQVdLTElCUEFUSDokKiIKfQo=' | base64 -d)" > /dev/null 2>&1

That decodes to a perfectly ordinary function from the gawk Debian package's /etc/profile.d/gawk.sh:

gawklibpath_append ()
{
    [ -z "$AWKLIBPATH" ] && AWKLIBPATH=`gawk 'BEGIN {print ENVIRON["AWKLIBPATH"]}'`
  export AWKLIBPATH="$AWKLIBPATH:$*"
}

A single snapshot file on my system had 6 such blobs; I have ~10 snapshot files sitting in ~/.claude/shell-snapshots/.

Root Cause

This matters because:

Code Example

eval "$(echo 'Z2F3a2xpYnBhdGhfYXBwZW5kICgpIAp7IAogICAgWyAteiAiJEFXS0xJQlBBVEgiIF0gJiYgQVdL
TElCUEFUSD1gZ2F3ayAnQkVHSU4ge3ByaW50IEVOVklST05bIkFXS0xJQlBBVEgiXX0nYAogIGV4cG9y
dCBBV0tMSUJQQVRIPSIkQVdLTElCUEFUSDokKiIKfQo=' | base64 -d)" > /dev/null 2>&1

---

gawklibpath_append ()
{
    [ -z "$AWKLIBPATH" ] && AWKLIBPATH=`gawk 'BEGIN {print ENVIRON["AWKLIBPATH"]}'`
  export AWKLIBPATH="$AWKLIBPATH:$*"
}
RAW_BUFFERClick to expand / collapse

Summary

Claude Code's shell-snapshot files (at ~/.claude/shell-snapshots/snapshot-bash-<id>-<token>.sh) serialize captured shell functions by base64-encoding the function body and wrapping it in an eval "$(echo '<base64>' | base64 -d)" invocation.

This happens even for entirely benign functions captured from the user's interactive shell environment. Example — a literal line from one of my snapshot files:

eval "$(echo 'Z2F3a2xpYnBhdGhfYXBwZW5kICgpIAp7IAogICAgWyAteiAiJEFXS0xJQlBBVEgiIF0gJiYgQVdL
TElCUEFUSD1gZ2F3ayAnQkVHSU4ge3ByaW50IEVOVklST05bIkFXS0xJQlBBVEgiXX0nYAogIGV4cG9y
dCBBV0tMSUJQQVRIPSIkQVdLTElCUEFUSDokKiIKfQo=' | base64 -d)" > /dev/null 2>&1

That decodes to a perfectly ordinary function from the gawk Debian package's /etc/profile.d/gawk.sh:

gawklibpath_append ()
{
    [ -z "$AWKLIBPATH" ] && AWKLIBPATH=`gawk 'BEGIN {print ENVIRON["AWKLIBPATH"]}'`
  export AWKLIBPATH="$AWKLIBPATH:$*"
}

A single snapshot file on my system had 6 such blobs; I have ~10 snapshot files sitting in ~/.claude/shell-snapshots/.

Why this is a security concern

The obfuscation looks suspicious even when it isn't. A reviewer glancing at the snapshot cannot distinguish between:

  1. A legitimate shell function captured by Claude Code's snapshotter and re-serialized through base64, and
  2. A malicious payload injected by something Claude Code has sourced or read from the user's environment.

This matters because:

  • A compromised entry in a directory the user considers "trusted" — an attacker-supplied .bashrc fragment, a dotfile from a third-party setup script, a direnv-style file sourced at shell startup, an entry in /etc/profile.d/ from a compromised package — could smuggle arbitrary code into the snapshot, and it would blend in perfectly with the existing base64 blobs.
  • A user who inspects the snapshot file (as users should be encouraged to do when evaluating the trust surface of any tool that loads their shell state) has no easy way to audit it. They have to base64-decode every blob individually to see what it actually executes on every subsequent Bash tool call.
  • The format effectively trains the user to accept that Claude Code's internal files contain opaque obfuscated code, undermining one of the natural defenses against a supply-chain or environment-level attack. "This is normal for Claude Code" becomes a cover story for an attacker.

This is defense-in-depth: even if the snapshot content itself is never malicious today, the presence of opaque obfuscated code in a tool's own state directory degrades the user's ability to notice when something else has been compromised.

Suggested remediation

Preferred: store captured function bodies as plain shell text, matching what the user would see from declare -f directly, so a reviewer can read them in place. If certain edge cases (embedded single quotes, trailing newlines, unusual escapes) require careful quoting, prefer heredocs or explicit escaping rather than opaque base64 wrapping.

If base64 wrapping is necessary, please at minimum:

  • Put a clear banner at the top of the snapshot file explaining that base64 is used for serialization of specific constructs, pointing the user at how to audit it, and listing the function names included so a user can spot-check by name without having to decode each blob.
  • Produce a deterministic, diffable format — the current format is hard to diff across sessions, which also hurts audit.

Environment

  • Claude Code running Opus 4.7 (1M context) on a Linux (Debian-family) host
  • bash shell, declare -f is the capture mechanism based on the surrounding snapshot file structure
  • Snapshot format observed: # Snapshot file header, unalias -a, then many eval "$(echo '<base64>' | base64 -d)" > /dev/null 2>&1 lines

Attribution

This report was drafted by Claude (Opus 4.7) at the user's request during a Claude Code session, after the user flagged the obfuscated-looking content in ~/.claude/shell-snapshots/.

extent analysis

TL;DR

Store captured shell function bodies as plain text instead of base64-encoded strings to improve security and auditability.

Guidance

  • Consider storing captured function bodies as plain shell text, similar to the output of declare -f, to allow for easy review and auditing.
  • If base64 wrapping is necessary, include a clear banner at the top of the snapshot file explaining the use of base64 and provide instructions on how to audit the content.
  • Use a deterministic and diffable format to make it easier to compare snapshot files across sessions.
  • Implement explicit escaping or heredocs for edge cases, such as embedded single quotes or trailing newlines, instead of relying on base64 wrapping.

Example

# Instead of this:
eval "$(echo 'Z2F3a2xpYnBhdGhfYXBwZW5kICgpIAp7IAogICAgWyAteiAiJEFXS0xJQlBBVEgiIF0gJiYgQVdL
TElCUEFUSD1gZ2F3ayAnQkVHSU4ge3ByaW50IEVOVklST05bIkFXS0xJQlBBVEgiXX0nYAogIGV4cG9y
dCBBV0tMSUJQQVRIPSIkQVdLTElCUEFUSDokKiIKfQo=' | base64 -d)" > /dev/null 2>&1

# Use plain text:
gawklibpath_append ()
{
    [ -z "$AWKLIBPATH" ] && AWKLIBPATH=`gawk 'BEGIN {print ENVIRON["AWKLIBPATH"]}'`
  export AWKLIBPATH="$AWKLIBPATH:$*"
}

Notes

The current implementation of storing captured shell function bodies as base64-encoded strings makes it difficult for users to review and audit the content, potentially allowing malicious code to go unnoticed.

Recommendation

Apply the suggested remediation of storing captured function bodies as plain text to improve security and auditability. This change will make it easier for users to review and trust the content of the snapshot files.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING