Skip to content

Instantly share code, notes, and snippets.

@monperrus
Created March 16, 2025 10:07
Show Gist options
  • Select an option

  • Save monperrus/35b280ea6c345d0bd4c7101a66105558 to your computer and use it in GitHub Desktop.

Select an option

Save monperrus/35b280ea6c345d0bd4c7101a66105558 to your computer and use it in GitHub Desktop.
a python script to remove secrets and keys in bash history file
#!/usr/bin/env python3
# cleans bash history
# generated with Claude on March 2025
# does not work out of the box
import os
import re
import shutil
import argparse
from datetime import datetime
def create_backup(file_path):
"""Create a backup of the original file."""
backup_path = f"{file_path}.backup.{datetime.now().strftime('%Y%m%d%H%M%S')}"
shutil.copy2(file_path, backup_path)
print(f"Backup created at: {backup_path}")
return backup_path
def get_patterns():
"""Return regex patterns for common secrets and keys."""
patterns = [
# API keys, tokens, and secrets
r'api[_-]?key[=:"\s][^\'"\s]+',
r'secret[_-]?key[=:"\s][^\'"\s]+',
r'access[_-]?key[=:"\s][^\'"\s]+',
r'access[_-]?token[=:"\s][^\'"\s]+',
r'auth[_-]?token[=:"\s][^\'"\s]+',
r'password[=:"\s][^\'"\s]+',
r'passwd[=:"\s][^\'"\s]+',
r'pwd[=:"\s][^\'"\s]+',
# AWS specific
r'aws[_-]?access[_-]?key[_-]?id[=:"\s][^\'"\s]+',
r'aws[_-]?secret[_-]?access[_-]?key[=:"\s][^\'"\s]+',
# Database connection strings
r'jdbc:[^\s]+',
r'mongodb:[^\s]+',
r'postgres:[^\s]+',
r'mysql:[^\s]+',
# Private keys and certificates
r'-----BEGIN\s+PRIVATE\s+KEY-----',
r'-----BEGIN\s+RSA\s+PRIVATE\s+KEY-----',
r'-----BEGIN\s+CERTIFICATE-----',
# Common commands that might contain secrets
r'curl\s+.*(-u|--user)\s+[^\s]+:[^\s]+',
# r'ssh\s+-i\s+[^\s]+',
# r'scp\s+-i\s+[^\s]+',
# OAuth tokens
r'oauth[_-]?token[=:"\s][^\'"\s]+',
# JWT tokens (simplified pattern)
r'eyJ+\.+\.+',
]
return patterns
def clean_history_file(file_path, patterns, dry_run=False):
"""Remove lines containing secrets from the history file."""
with open(file_path, 'r', encoding='utf-8', errors='replace') as f:
lines = f.readlines()
original_count = len(lines)
filtered_lines = []
removed_lines = []
for line in lines:
should_remove = False
for pattern in patterns:
if re.search(pattern,line):
should_remove = True
# removed_count += 1
removed_lines.append(line)
break
if not should_remove:
filtered_lines.append(line)
if not dry_run:
with open(file_path, 'w', encoding='utf-8') as f:
f.writelines(filtered_lines)
else:
for i in removed_lines:
print(i, end="")
print(f"Original lines: {original_count}")
print(f"Lines removed: {len(removed_lines)}")
print(f"Remaining lines: {len(filtered_lines)}")
return len(removed_lines)
def main():
parser = argparse.ArgumentParser(description='Clean secrets and keys from bash history file')
parser.add_argument('--file', default=os.path.expanduser('~/.bash_history'),
help='Path to the history file (default: ~/.bash_history)')
parser.add_argument('--dry-run', action='store_true',
help='Only show what would be removed without making changes')
parser.add_argument('--no-backup', action='store_true',
help='Skip creating a backup of the original file')
args = parser.parse_args()
if not os.path.exists(args.file):
print(f"Error: History file not found at {args.file}")
return
if not args.no_backup and not args.dry_run:
backup_path = create_backup(args.file)
print(f"Original file backed up to {backup_path}")
patterns = get_patterns()
print(f"{'Analyzing' if args.dry_run else 'Cleaning'} history file: {args.file}")
if args.dry_run:
print("DRY RUN: No changes will be made")
removed = clean_history_file(args.file, patterns, args.dry_run)
if removed > 0:
if args.dry_run:
print(f"Would remove {removed} lines containing potential secrets")
else:
print(f"Successfully removed {removed} lines containing potential secrets")
else:
print("No secrets found in the history file")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment