Scanning for Secrets: A Security Audit with Claude Code
Last week I was refactoring my Minecraft server deployment scripts when I noticed something that made my stomach drop: a file called config.backup.json that I didnât remember creating. It turned out to be harmlessâjust some old server settingsâbut that moment of âwait, whatâs in there?â stuck with me. How many other files might be lurking in this repository with contents Iâd forgotten about?
It was time for a proper audit.
Why Two Minecraft Servers?
I run a Minecraft server on AWS so friends can connect from anywhere, but I also maintain a local server for testing mods and configuration changes before pushing them to production. Nobody wants to discover that a new mod crashes the server when your friends are mid-build.
Both environments share configuration templates and deployment scripts, which creates an obvious risk: credentials for the production AWS infrastructure living alongside local development files. One careless commit could expose Discord bot tokens, RCON passwords, or AWS access patterns.
My request to Claude was specific: scan the entire codebase for anything that looks like a secret, credential, or sensitive value, and verify that real credentials only exist in the .env file where they belong.
How the Scan Worked
Rather than just asking âare there any secrets?â, I prompted Claude to search systematically for common credential patterns. It ran searches for password, passwd, pwd, secret, token, api_key, and key across all files, then examined the results in context.
The good news: no actual secrets were hardcoded in version-controlled files. The .env file contains all real credentials, and .gitignore properly excludes it.
But the scan surfaced findings that deserved closer inspection.
Template files with placeholder values:
# server.properties.template
rcon.password=minecraft123
This RCON password placeholder is intentionalâRCON lets you run console commands remotely, and the deployment script substitutes the real password from environment variables. But seeing it in plain text prompted me to verify that substitution actually happens (it does) and fails loudly if the environment variable is missing (it didnâtâmore on that below).
Documentation examples:
# From README.md
DISCORD_BOT_TOKEN=YOUR_BOT_TOKEN_HERE
Intentional, but a useful reminder that documentation establishes patterns. If your examples look like YOUR_TOKEN_HERE, reviewers learn to spot that format. If your examples look like plausible real tokens, pattern-matching becomes harder.
Default values in code that shouldnât exist:
# aws_helpers.py (before)
def __init__(self, ssm_client, rcon_password='minecraft123'):
This was the real finding. A default password parameter means the code can run without explicit configurationâwhich means a misconfigured deployment could silently use the placeholder instead of the real credential. Defense in depth says required values should be required.
I changed it to:
# aws_helpers.py (after)
def __init__(self, ssm_client, rcon_password):
if not rcon_password:
raise ValueError("rcon_password is required")
Now a missing password is a loud failure at initialization, not a silent security gap discovered in production.
The False Positives
Any pattern-based scan produces noise. Hereâs what showed up that wasnât actually a problem:
secret_santa.py: A module name, not a credentialtoken_bucket.py: A rate-limiting implementation, referring to the algorithmpassword_strength_validator: A function that checks passwords, doesnât contain them- SSH key file paths: References like
~/.ssh/id_rsaappeared in documentation, but the actual key files are outside the repository
Recognizing these non-issues matters because it calibrates your response to real findings. If everything looks like a security problem, nothing does.
What I Changed
Based on the audit, I made three concrete changes:
- Removed the default password parameter (shown above)
- Added validation to the deployment script to fail if
RCON_PASSWORDisnât set in the environment - Updated
.gitignoreto explicitly exclude*.backup.jsonand similar recovery file patterns
I also reorganized the directory structure. The audit made me realize how tangled the two server configurations had become:
minecraftServer/
âââ aws-server/ # Cloud infrastructure
â âââ cloudformation/
â âââ lambda/
â âââ discord-bot/
â âââ deploy.sh
âââ local-server/ # Local development
âââ server.properties
âââ start.sh
âââ mods/
The separation makes it clearer which credentials belong to which environment, reducing the risk of accidentally referencing production secrets in local testing scripts.
Lessons for Your Own Projects
Regular secret scans catch drift. Your .gitignore might be perfect today, but you added a config file last month and a debug script last week. Periodic scans catch what initial setup missed.
Template files need validation, not just substitution. Having a .template file with placeholders is good practice. But verify that your deployment process fails explicitly when substitution doesnât happen, rather than falling back to defaults.
Search for semantic variations. Donât just search for passwordâsearch for passwd and pwd too. Search for token and api_key and apikey. Think about how different developers name the same concept.
Evaluate findings in context. The minecraft123 placeholder in a template file is obviously not a real password. The same string as a default parameter value is a different risk profile entirely. Context determines severity.
Document what you changed. An audit that identifies problems but doesnât fix them is incomplete. I now have a commit that shows exactly what the audit found and how I addressed itâuseful for future reference and for anyone reviewing the projectâs security posture.
The AI-Assisted Advantage
What made this session effective wasnât raw speedâI could run grep myself. It was the combination of systematic coverage and contextual analysis. Claude searched for multiple credential patterns, then for each match explained whether it was a placeholder, a code reference, or something that warranted action. Thatâs the difference between a list of search results and an actual security review.
The audit also surfaced the default parameter issue, which I might have overlooked in a manual scan. When youâre grepping for âpassword,â a function signature doesnât pattern-match the same way as password=hunter2 in a config file. But itâs arguably more dangerous.
Next up: Iâm planning to add a pre-commit hook that runs similar pattern checks automatically. Todayâs audit was valuable, but the real win would be catching these issues before they ever reach the repository.
Sometimes the most valuable AI assistance isnât writing new codeâitâs asking systematic questions about code you thought you already understood.