history-redact

Crates.iohistory-redact
lib.rshistory-redact
version0.2.2
created_at2025-11-07 17:43:57.20949+00
updated_at2025-11-07 17:43:57.20949+00
descriptionA fast, secure tool to redact sensitive information from shell command history
homepagehttps://github.com/severeon/zsh-redact-history
repositoryhttps://github.com/severeon/zsh-redact-history
max_upload_size
id1921936
size131,639
Thomas Quick (severeon)

documentation

https://github.com/severeon/zsh-redact-history

README

History Redactor

Fast, secure redaction of sensitive information from shell command history

Reduce accidental secret exposure in shell history as part of defense-in-depth security practices. History Redactor automatically redacts sensitive information before it's written to your history file, using blazing-fast Rust regex patterns.

CI License: MIT

⭐️ If this little tool saved you some time, feel free to buy me a coffee ⭐️

The Problem

Every time you run a command like this:

export OPENAI_API_KEY=sk-1234567890abcdefghijklmnopqrstuvwxyz1234567890
curl -H "Authorization: Bearer secret_token" https://api.example.com
mysql -u root -p MySecretPassword123 -h localhost

...those secrets are written to your shell history file in plain text. If your system is compromised, or you accidentally share your history, those credentials are exposed.

The Solution

History Redactor intercepts commands before they hit your history file and redacts sensitive patterns:

export OPENAI_API_KEY=[OPENAI_KEY_REDACTED]
curl -H "Authorization: Bearer [BEARER_TOKEN_REDACTED]" https://api.example.com
mysql -u root --password [REDACTED] -h localhost

Features

  • Blazing Fast: Sub-5ms overhead even with 100+ redaction rules (Rust-powered regex engine)

  • Zero Config: Ships with sensible defaults for common secrets (API keys, tokens, passwords)

  • Flexible: Easy-to-customize regex patterns via simple config file

  • Shell Integration: Seamless Zsh integration via zshaddhistory hook

  • Safe: Non-destructive - only affects what's written to history, not command execution

  • Comprehensive: Built-in patterns for OpenAI, GitHub, AWS, database URLs, and more


Security Notice

Important: Understand what this tool protects and what it doesn't.

✅ What This Protects ❌ What This Doesn't Protect
Shell history files on disk Commands visible in ps during execution
Accidental history file sharing Process monitoring and audit logs
Secrets in dotfiles/backups Terminal scrollback buffers
Multi-session history exposure Application logs and stdout
Network traffic containing secrets
Memory dumps or core dumps

History Redactor is ONE layer of defense-in-depth, not a complete secret management solution. Secrets passed as command-line arguments are still exposed during execution to:

  • Other processes via ps auxww or /proc/[pid]/cmdline
  • System monitoring tools (auditd, sysdig, etc.)
  • Terminal session recordings
  • Anyone watching your screen

For comprehensive secret security:

  • Use dedicated secret management tools (Vault, AWS Secrets Manager, 1Password CLI)
  • Load secrets from secure stores into environment variables
  • Prefer interactive password prompts over CLI arguments
  • Use configuration files with restrictive permissions (chmod 600)
  • Avoid passing secrets as command-line arguments entirely

Run history-redact explain-security after installation for detailed security boundaries and best practices.

See SECURITY.md for a complete threat model analysis.


Quick Start

Installation

Option 1: Cargo Install (Recommended)

Install the binary:

cargo install history-redact

Add this function to your ~/.zshrc file:

function zshaddhistory() {
  local command="$1"
  local redacted
  local redacted_cmd

  redacted=$(echo -n "$command" | history-redact redact 2>/dev/null)
  if [[ $? -ne 0 ]]; then
    echo "Warning: history-redact failed to process command" >&2
    return 0
  fi
  redacted_cmd="${redacted#*|}"
  print -sr -- "${redacted_cmd%%$'\n'}"
  return 1
}

Then initialize the configuration and restart your shell:

history-redact init
source ~/.zshrc

Option 2: From Source (All-in-One)

git clone https://github.com/severeon/zsh-redact-history.git
cd zsh-redact-history
./install.sh

The install script will:

  • Build and install the binary
  • Initialize the default configuration
  • Add shell integration to your .zshrc

Option 3: Manual Integration

If you prefer to add the integration manually, install the binary first:

cargo install history-redact
history-redact init

Then add this to your ~/.zshrc:

# History Redact - Zsh Integration
function zshaddhistory() {
  local redacted
  redacted=$(echo -n "$1" | history-redact redact 2>/dev/null)
  if [[ $? -eq 0 ]]; then
    local exit_code="${redacted%%|*}"
    local cmd="${redacted#*|}"
    print -sr -- "${cmd%%$'\n'}"
  fi
  return 1
}

Verify Installation

# Check version
history-redact --version

# Test redaction
history-redact test "export OPENAI_KEY=sk-1234567890123456789012345678901234567890123456ab"

# View configuration
history-redact config-path

Usage

Command Line Interface

# Test what would be redacted from a command
history-redact test "curl -H 'Authorization: Bearer abc123' api.example.com"

# Redact from stdin (used by shell hook)
echo "export KEY=secret" | history-redact redact

# Validate your configuration
history-redact validate-config --verbose

# Initialize/reset configuration
history-redact init --force

# Show config file location
history-redact config-path

Configuration

Configuration lives in ~/.config/history-redact/rules.conf (XDG-compliant).

Configuration Precedence

  1. $HISTORY_REDACT_CONFIG environment variable
  2. ~/.config/history-redact/rules.conf
  3. /etc/history-redact/rules.conf

Rule Format

# Format: regex|||replacement

# OpenAI API keys
sk-[a-zA-Z0-9]{48}|||[OPENAI_KEY_REDACTED]

# Generic password flags
--password\s+\S+|||--password [REDACTED]

# Environment variables
API_KEY=\S+|||API_KEY=[REDACTED]

# Database connection strings
postgresql://[^:@]+:[^@]+@|||postgresql://[USER]:[PASS_REDACTED]@

Key points:

  • Use ||| as the delimiter (triple pipe)
  • Lines starting with # are comments
  • Patterns are standard Rust regex
  • Rules are applied in order
  • Invalid regex patterns are skipped with warnings

Built-in Redaction Patterns

History Redactor ships with comprehensive patterns for:

API Keys & Tokens:

  • OpenAI (sk-...)
  • Anthropic (sk-ant-...)
  • GitHub (ghp_..., gho_..., etc.)
  • AWS (AKIA...)
  • Google/GCP (AIza...)
  • Slack (xoxb-..., xoxp-...)
  • Generic Bearer tokens
  • JWT tokens

Command-line Arguments:

  • --password, --token, --api-key, etc.
  • Short form flags: -p (with port exclusions)

Environment Variables:

  • API_KEY=..., SECRET=..., PASSWORD=...
  • Cloud provider credentials (AWS, Azure, GCP)

Database URLs:

  • PostgreSQL, MySQL, MongoDB, Redis connection strings

Private Keys:

  • RSA, OpenSSH private key headers

See rules.conf for the full list.

How It Works

Architecture

┌─────────────────┐
│  Shell Command  │
│  "export KEY=sk-123..." │
└────────┬────────┘
         │
         ▼
┌─────────────────────────┐
│   zshaddhistory hook    │
│  (shell integration)    │
└────────┬────────────────┘
         │
         ▼
┌──────────────────────────┐
│  history-redact          │
│  (Rust binary)           │
│  - Load config           │
│  - Apply regex rules     │
│  - Return redacted cmd   │
└────────┬─────────────────┘
         │
         ▼
┌─────────────────────────┐
│  Redacted Command       │
│  "export KEY=[REDACTED]"│
└────────┬────────────────┘
         │
         ▼
┌─────────────────────────┐
│   ~/.zsh_history        │
│  (written to disk)      │
└─────────────────────────┘

Performance

  • Rule compilation: Once at startup (cached Regex objects)
  • Per-command overhead: <5ms for 100+ rules
  • Memory footprint: Minimal (compiled regexes only)
  • Optimizations: LTO, opt-level 3, stripped binaries

Benchmark on M1 MacBook Pro with 50 rules:

  • Average: 1.2ms per command
  • 99th percentile: 3.8ms

Understanding the Threat Model

The Secret Lifecycle

When you type a command containing a secret, it exists in multiple places:

1. Keyboard Input → 2. Shell Memory → 3. Process Arguments → 4. Command Output → 5. History File
   [TYPED]            [EXECUTING]        [VISIBLE IN ps]     [LOGS/STDOUT]       [DISK]
                                              ↑
                                         NOT PROTECTED
                                              ↓
                                         History Redactor ONLY protects step 5 →

History Redactor operates at the final stage - preventing secrets from persisting to disk. This is valuable because:

  • History files are long-lived and often backed up
  • They're frequently shared (dotfiles, team configurations)
  • They're readable by many processes and scripts
  • They're easy to accidentally commit to version control

However, secrets are already exposed during stages 1-4 to:

  • Anyone watching your screen (shoulder surfing, screen sharing)
  • Process monitoring tools (ps, top, audit logs)
  • Terminal session recorders
  • Application logs and standard output
  • Network traffic (if transmitted)

Better Alternatives for Secret Management

Instead of:

# ✗ BAD: Secret on command line (visible in ps, logs, history)
curl -H "Authorization: Bearer sk-1234567890abcdef" api.example.com

Use:

# ✓ BETTER: Environment variable (still visible in ps during export)
export TOKEN=sk-1234567890abcdef
curl -H "Authorization: Bearer $TOKEN" api.example.com
# History Redactor will redact the export line

# ✓✓ BEST: Load from secure store
export TOKEN=$(op read "op://vault/api/token")    # 1Password CLI
export TOKEN=$(vault kv get -field=token secret/api)  # HashiCorp Vault
export TOKEN=$(aws secretsmanager get-secret-value \
  --secret-id api-token --query SecretString --output text)  # AWS

# ✓✓ ALSO BEST: Interactive prompt
mysql -u root -p  # Prompts for password, doesn't show in ps or history

# ✓✓ ALSO BEST: Config file with restricted permissions
chmod 600 ~/.config/myapp/credentials
myapp --config ~/.config/myapp/credentials

When to Use This Tool

Good Use Cases:

  • You occasionally type secrets on CLI and want backup protection
  • You're migrating to better secret practices and need transitional safety
  • You want to sanitize history before sharing dotfiles
  • You work on shared systems where history might be accessible
  • You want defense-in-depth as one layer of many

Insufficient Alone:

  • Production/compliance-critical environments (use proper secret management)
  • Systems with comprehensive audit logging (secrets already captured elsewhere)
  • As a substitute for security training and best practices
  • When you need protection during command execution (not just after)

Best practices:

  • Use environment variables loaded from secret stores
  • Prefer configuration files with restricted permissions (chmod 600)
  • Use interactive prompts when possible
  • Regularly audit your history: history | grep -i 'password\|token\|key'
  • Run history-redact explain-security to understand boundaries
  • See SECURITY.md for comprehensive guidance

Examples

Test Commands Before Execution

# See what would be redacted
history-redact test "curl -u admin:secret123 https://api.example.com"

Output:

Original command:
  curl -u admin:secret123 https://api.example.com

Redacted command:
  curl -u admin:[REDACTED] https://api.example.com

Matches (1 rule):
  1. Pattern: -u\s+[^:]+:\S+
     Before:  curl -u admin:secret123 https://api.example.com
     After:   curl -u admin:[REDACTED] https://api.example.com

Custom Patterns

Add to ~/.config/history-redact/rules.conf:

# Redact email addresses
\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b|||[EMAIL_REDACTED]

# Redact IPv4 addresses
\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b|||[IP_REDACTED]

# Redact credit card numbers (basic)
\b[0-9]{4}[- ]?[0-9]{4}[- ]?[0-9]{4}[- ]?[0-9]{4}\b|||[CC_REDACTED]

# Custom API token format
myapp_token_[a-zA-Z0-9]{32}|||[MYAPP_TOKEN_REDACTED]

Then validate:

history-redact validate-config --verbose

Redact Existing History

# Source the helper function
source ~/.history-redact.zsh

# Redact existing history file (creates backup)
redact-history-file

Development

Building from Source

# Debug build
cargo build

# Release build (optimized)
cargo build --release

# Run tests
cargo test

# Run only unit tests
cargo test --lib

# Run only integration tests
cargo test --test integration_test

Running Tests

# All tests
cargo test

# With output
cargo test -- --nocapture

# Specific test
cargo test test_openai_key_redaction

Contributing

Contributions welcome! See CONTRIBUTING.md for guidelines.

FAQ

Q: Does this slow down my shell? A: Negligible. Overhead is <5ms per command, imperceptible to humans.

Q: What if history-redact fails? A: The shell hook is designed to fail gracefully. If the binary fails, the original command is saved to history.

Q: Can I disable redaction temporarily? A: Yes, comment out the zshaddhistory function in your .zshrc, or:

unset -f zshaddhistory  # Disable
source ~/.zshrc         # Re-enable

Q: Does it work with HISTFILE customization? A: Yes, it's completely independent of your HISTFILE location.

Q: Can I see what's been redacted? A: Use history-redact test <command> to preview redaction before execution.

Q: Will it redact commands I've already run? A: No, it only affects new commands. Use redact-history-file to redact existing history.

Q: Is it safe for production systems? A: Yes, but remember: this protects history files only. For comprehensive secret management, use proper secret stores (Vault, AWS Secrets Manager, etc.).

Q: Is this enough for production/compliance requirements? A: No. History Redactor is a hygiene tool, not a complete security solution. Compliance frameworks (PCI DSS, HIPAA, SOC 2) require comprehensive secret management, audit logging, and multiple layers of security controls. Use this as ONE layer in defense-in-depth.

Q: Can attackers still see my secrets? A: Yes. Secrets passed as command-line arguments are visible via process monitoring (ps), audit logs, network traffic, and terminal recordings during execution. This tool ONLY protects the history file after execution.

Q: Should I still use proper secret management? A: Absolutely! This is supplementary protection. Always prefer:

  • Secret management tools (Vault, AWS Secrets Manager, 1Password CLI)
  • Environment variables loaded from secure sources
  • Interactive prompts instead of CLI arguments
  • Configuration files with restrictive permissions

Q: Does this work with audit logging? A: History Redactor operates after command execution, so audit systems (auditd, sysdig) will still capture the original unredacted command. This tool protects the history file, not real-time monitoring.

Roadmap

  • cargo install support
  • Bash integration
  • Fish shell support
  • Optional audit logging (log what gets redacted)
  • Pre-built binaries for more platforms (Windows, BSD)
  • Config file hot-reloading
  • Performance profiling tools

License

MIT License - see LICENSE file for details.

Acknowledgments

  • Inspired by the need for better secret hygiene in developer workflows
  • Built with Rust for performance and safety
  • Uses the excellent regex crate

Support


Important: This tool helps prevent secrets from being written to shell history, but it's not a substitute for proper secret management practices. Always use environment variables, secret stores, and proper access controls for sensitive credentials.

Commit count: 0

cargo fmt