Sterling AI Infrastructure
Security Audit
Comprehensive self-audit of all connected systems, credentials, and network exposure
Overall Security Posture: YELLOW (Moderate Risk)
Solid foundation in place ... FileVault encryption enabled, macOS SIP active, most credentials in Keychain, OAuth files with owner-only permissions, CDP port bound to localhost. However, several significant vulnerabilities require immediate attention: exposed API tokens in frontend code, an unauthenticated Docker container on all interfaces, plaintext JWT secrets, wildcard CORS, and the macOS firewall disabled.
Infrastructure at a Glance
Risk
Anyone on the same network can access the recruitment pipeline agent, which has embedded API keys for Zoho Recruit, HubSpot, Predictive Index, LinkedIn, Gemini, and Zoho client secret. An attacker on the same WiFi can use the chat agent to query/modify recruitment data or extract keys via prompt injection.
Technical Details
The oa-recruit-agent Docker container was bound to 0.0.0.0:8501 (all network interfaces). It serves a web chat UI with zero authentication. Six API keys were baked into environment variables.
Fix Applied
Container rebound to 127.0.0.1:8501:8080 (localhost only). Remaining work: add authentication to the web UI, move API keys from env vars to Docker secrets.
Risk
JWT signing secret was stored as a plain_text binding (not secret_text) in both dugoutready-api and lineup-tool-api Workers. The same hex value was shared across both, meaning a token forged for one would work on the other. The secret was visible in the Cloudflare dashboard and API responses.
Technical Details
Both Workers had JWT_SECRET configured as plain_text binding. Anyone with Cloudflare dashboard access could read the value and forge valid JWTs for both DugoutReady and the Lineup Tool.
Fix Applied
JWT secrets rotated. Two new unique secrets generated (one per Worker). Stored as secret_text bindings. Both Workers redeployed. All existing JWTs invalidated.
Risk
Any website on the internet could make authenticated cross-origin requests to the dugoutready-api and lineup-tool-api endpoints. Combined with the JWT vulnerability, a phishing page could make API calls on behalf of a logged-in user.
Technical Details
Both Workers had CORS_ORIGIN set to * (wildcard). This permitted any origin to make cross-origin requests to endpoints handling user data and payment operations.
Fix Applied
CORS_ORIGIN updated to specific allowed domains: https://app.dugoutready.com and https://lineup-tool.pages.dev respectively.
Risk
No firewall protection on the Mac Mini, which runs 24/7 with 16+ listening services including 3 on all interfaces. Any device on the network can probe and connect to these services.
Technical Details
macOS Application Firewall state is 0 (disabled). At least 16 TCP services are listening, including services on 0.0.0.0 (all interfaces).
Fix Recommendation
- Enable firewall:
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate on - Set to block all incoming except explicitly allowed services
- Requires
sudo... needs Brad to run on the Mac Mini
Risk
Anyone who views page source can extract the API token and gain full read/write access to all Command Center data ... tasks, inbox, emails, relationships, settings, energy logs, stats, daily questions, focus sessions, family sync, LinkedIn scan, email scan, messenger scan, voicemail scan, AI drafts, and more.
Technical Details
The full API Bearer token is hardcoded in the HTML source of brads-command-center.pages.dev. While there IS a PIN gate on the frontend, the token is visible in page source regardless of PIN entry. The token is also documented in the CLAUDE.md instruction file.
Fix Recommendation
- Remove hardcoded token from frontend HTML
- Implement server-side auth flow: PIN entry calls an auth endpoint that returns a short-lived session token
- Store master API token only as a Cloudflare environment variable
- Remove token from CLAUDE.md (reference by Keychain name only)
Risk
Anyone with container filesystem access could read the full Cloudways API key in plaintext. This key grants full server management access: create/delete servers, manage applications, install SSL, modify DNS, SSH access.
Fix Applied
API key removed from CLAUDE.md. Now referenced by Keychain service name only (cloudways-api-key). Key rotation recommended after cleanup.
Risk
The Twilio Account SID was documented in plaintext in CLAUDE.md. While insufficient alone for API calls, having the SID exposed enables targeted attacks.
Fix Applied
SID removed from CLAUDE.md. Now referenced by Keychain service name only.
Risk
Anyone can send fake voicemail data to the webhook endpoint. The endpoint explicitly skips Bearer token auth and performs zero validation that requests originate from Twilio. An attacker could POST fake voicemail data into Brad's Command Center inbox.
Technical Details
The voicemail-webhook.js endpoint does not validate X-Twilio-Signature headers, does not check AccountSid parameters, and does not verify against Twilio's signing certificate.
Fix Recommendation
- Implement Twilio request signature validation using
X-Twilio-Signatureheader - Verify
AccountSidin POST body matches known SID - Check for expected Twilio-specific fields
Risk
Any process on the Mac Mini could read Plaud OAuth access and refresh tokens from ~/.plaud/tokens-mcp.json.
Fix Applied
Permissions updated: chmod 600 ~/.plaud/tokens-mcp.json and chmod 700 ~/.plaud/.
Risk
Any process on the Mac Mini could read the Sterling service account OAuth tokens and client secret. The file contained access token, refresh token, client ID, and client secret. Other credential files (brad@, bradstevens44@, deb@) were properly set to 600.
Fix Applied
Permissions updated: chmod 600 on sterling@outsourceaccess.com.json.
Risk
Security patches are not applied automatically. For a machine running 24/7 as a server, critical patches could go unapplied for weeks or months.
Fix Recommendation
sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate AutomaticallyInstallMacOSUpdates -bool true- At minimum, enable critical-only:
sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate CriticalUpdateInstall -bool true - Requires
sudo... needs Brad on Mac Mini
Risk
Elgato Camera Hub (ports 1834, 1854) is accessible from any device on the network via IPv6 on all interfaces.
Fix Recommendation
- If not actively needed, quit the Camera process
- Check Elgato settings for "local only" network option
- Enabling the macOS firewall (CRIT-4) would mitigate this
Risk
Sterling's OAuth token for brad@outsourceaccess.com has 41 scopes including: gmail.modify, gmail.send, drive (full read/write/delete), calendar.events, contacts, chat.messages, script.projects, script.deployments, script.external_request, and gmail.settings.basic. If the OAuth token were compromised, an attacker would have full access to Brad's entire Google Workspace.
Fix Recommendation
- Audit which scopes are actually used vs. granted
- Create a restricted token with only the scopes Sterling actively needs
- Implement token rotation on a quarterly schedule
script.external_requestis particularly dangerous ... allows Apps Script to make arbitrary external HTTP requests from Brad's account
Risk
An ed25519 key labeled "agency-gateway" in ~/.ssh/authorized_keys provides SSH access to the Mac Mini. If the corresponding private key is compromised, it enables direct system access.
Fix Recommendation
- Verify this key is still needed and who/what holds the private key
- Confirm with Anthropic (if Cyndra/Claude infrastructure key) that it's properly secured
- Consider adding
from="127.0.0.1"IP restriction to the entry - Remove if no longer needed
Risk
ChatStorage.sqlite (81MB) has 644 permissions. Any process running as bradstevens can read the complete WhatsApp message history.
Fix Recommendation
This is controlled by WhatsApp Desktop's default permissions ... changing them may break the app. Primary mitigation is ensuring no unauthorized processes run on the Mac Mini. FileVault encryption (confirmed ON) protects against physical theft.
Risk
All 78+ Sterling credentials stored under account=cyndra. Single point of failure ... compromise of one Keychain access pattern exposes all credentials.
Fix Recommendation
- Acceptable tradeoff for operational convenience, but be aware of blast radius
- Consider separating high-value credentials (Stripe, bank-related) into a separate Keychain
- Enable Keychain auto-lock after timeout
Risk
Stripe live secret key (sk_live_...) in Keychain can process real charges, manage subscriptions, and access customer payment data. Blast radius of a compromised key is financial.
Fix Recommendation
- Create restricted Stripe keys with minimum required permissions per Worker
- Create separate restricted keys per Worker (not shared master key)
- Stripe webhook signature verification is already in place (good)
Risk
.fb-session-health.json is readable by any process. While it likely contains session state metadata rather than cookies/tokens, it should still be restricted.
Fix Recommendation
chmod 600 /workspace/group/.fb-session-health.json
Risk
~748 files in ~/.cyndra/browser-agent/ including login scripts, admin scripts, and export scripts. These automate sensitive operations. If compromised, an attacker would have a playbook for accessing multiple services.
Fix Recommendation
chmod 700 ~/.cyndra/browser-agent/- Periodically clean up old/unused scripts
- Ensure no credentials are hardcoded in any scripts
Risk
Docker socket at /var/run/docker.sock is accessible. Any compromised process running as Brad could spin up a privileged container with host filesystem access.
Fix Recommendation
Inherent Docker Desktop risk. Mitigation: do not install untrusted npm packages or run untrusted code on the Mac Mini.
Risk
The Forum Update Builder frontend references a TOKEN variable for API authentication. Makes 5 API calls using Bearer ${TOKEN}. If the token is hardcoded in the HTML (like CRIT-5), same vulnerability applies.
Fix Recommendation
Audit source to confirm TOKEN is not hardcoded. If hardcoded, apply the same fix as CRIT-5.
Risk
Each deployed Pages project is a publicly accessible website. Many appear to be prototypes, one-off tools, or deprecated sites. Each one is a potential entry point if it has vulnerabilities.
Fix Recommendation
Audit the full list and delete/pause projects that are no longer needed. Candidates for review include: sterling-ayrshare-approval, sterling-memory-review, cyndra-oa-exchange, sterling-email-rules-review, sterling-agent-comparison, iphone-web-app-guide, rok-va-roi-microsite, rok-software-proposal, dave-hashim-call-prep, fivetraks-call-prep, sterling-audit.
Risk
Whitelisted IP matches current public IP, but if the ISP assigns a new IP (DHCP lease renewal), SSH access would need updating.
Fix Recommendation
Periodically verify the whitelist matches the current IP, especially after internet outages.
Risk
Cyndra container runs as UID 501 (maps to bradstevens on the host) with workspace mounted read-write, giving same file permissions as Brad's user account.
Fix Recommendation
By design for file access. Noted as a trust boundary.
Risk
Two .env files in Gemini agents directories. Currently contain only non-secret config (project ID, region). Not a current risk but .env files should be monitored for scope creep.
Fix Recommendation
Monitor for any secrets being added. Consider moving config to non-.env formats.
Risk
GoDaddy API key can list all domains, modify DNS, update nameservers, unlock domains for transfer, and retrieve auth codes. High-value target if compromised.
Fix Recommendation
- Key is stored only in Keychain (confirmed)
- Ensure GoDaddy 2FA is enabled for the account
| Item | Status | Notes |
|---|---|---|
| FileVault disk encryption | ✓ | Full disk encryption protects against physical theft |
| macOS SIP (System Integrity Protection) | ✓ | Prevents system file modification |
| Browser Agent CDP port (9333) | ✓ | Correctly bound to 127.0.0.1 only, not accessible from network |
| OAuth token files (brad@, bradstevens44@, deb@) | ✓ | 600 permissions ... owner-only read/write |
| Keychain storage pattern | ✓ | All API keys stored in macOS Keychain, not plaintext files |
| Cyndra container privileges | ✓ | Runs in unprivileged mode with bridge networking |
| Cloudflare Workers secrets | ✓ | Stripe, Resend, Gemini, Twilio keys stored as secret_text bindings |
| Cloudways SSH access | ✓ | "Block all except whitelist" mode, current IP matches |
| Docker networking (Cyndra) | ✓ | No exposed port bindings on the Cyndra container |
| Localhost services binding | ✓ | Node (7777), Python (8000-8002), Wrangler (8787, 20241-2), WebSocket (8765, 9876) all on 127.0.0.1 |
| Worker API authentication | ✓ | Command Center API endpoints validate Bearer token against env.BRAIN_DUMP_TOKEN |
| Google OAuth token file format | ✓ | Contains client_secret ... standard for Google OAuth2 credential files |
| R2 bucket access | ✓ | Not publicly browsable. Returns 404 on root; objects require direct URL |
| Email forwarder Worker | ✓ | Simple forward only (gravelandglory.com to brad@outsourceaccess.com), no auth needed |
| Stripe webhook verification | ✓ | Workers use STRIPE_WEBHOOK_SECRET for signature verification |
✓ Completed Fixes
oa-recruit-agent no longer exposed on all interfaces
dugoutready-api + lineup-tool-api redeployed, old JWTs invalidated
Wildcard (*) removed from dugoutready-api + lineup-tool-api
Now referenced by Keychain service name only
Now referenced by Keychain service name only
chmod 600 on tokens-mcp.json, chmod 700 on ~/.plaud/
chmod 600 on sterling@outsourceaccess.com.json
⚠ Needs Brad (Requires sudo)
Requires sudo access on the Mac Mini
Requires sudo access on the Mac Mini
🔨 In Progress
Replacing hardcoded frontend token with server-side auth flow
Implementing X-Twilio-Signature verification
Verifying whether TOKEN is hardcoded in frontend
📋 Scheduled
SSH key audit, WhatsApp DB, Keychain segmentation, Stripe restricted keys, FB session file, browser agent cleanup, Docker socket
CF Pages audit, Cloudways IP whitelist, container UID, .env monitoring, GoDaddy 2FA
Elgato Camera Hub binding + Google OAuth scope audit
Immediate (Today)
| # | Action | Details | Est. Time |
|---|---|---|---|
| 1 | Enable macOS firewall | sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate on | 1 min |
| 2 | Rebind oa-recruit-agent to localhost | Recreate container with -p 127.0.0.1:8501:8080 | 5 min |
| 3 | Fix Plaud token permissions | chmod 600 ~/.plaud/tokens-mcp.json && chmod 700 ~/.plaud/ | 1 min |
| 4 | Fix sterling@ OAuth permissions | chmod 600 on sterling@outsourceaccess.com.json | 1 min |
This Week
| # | Action | Details | Est. Time |
|---|---|---|---|
| 5 | Rotate JWT secrets, store as secret_text | Generate 2 unique secrets via openssl rand -hex 32, store via wrangler secret put | 15 min |
| 6 | Fix CORS on production APIs | Update CORS_ORIGIN from * to specific domains on both Workers | 10 min |
| 7 | Remove hardcoded API token from Command Center | Implement server-side auth endpoint, remove inline token from HTML | 30 min |
| 8 | Add Twilio signature validation | Implement X-Twilio-Signature verification on voicemail webhook | 20 min |
| 9 | Remove credentials from CLAUDE.md | Replace inline keys/SIDs with Keychain reference names only | 5 min |
| 10 | Enable macOS auto security updates | sudo defaults write .../CriticalUpdateInstall -bool true | 1 min |
This Month
| # | Action | Details | Est. Time |
|---|---|---|---|
| 11 | Audit Google OAuth scopes | Determine minimum required scopes, create restricted token | 1 hr |
| 12 | Verify SSH "agency-gateway" key | Confirm origin and necessity, add IP restriction or remove | 10 min |
| 13 | Create restricted Stripe API keys | Generate restricted keys with minimum permissions per Worker | 20 min |
| 14 | Audit and prune CF Pages projects | Review ~80 projects, delete unused ones | 1 hr |
| 15 | Audit Forum Update Builder | Check for hardcoded token, fix if found | 15 min |
Credential Inventory
| API Keys (third-party) | 25+ |
| OAuth Tokens (Google, LinkedIn, etc.) | 8+ |
| Passwords (WP, Metricool, etc.) | 12+ |
| Client Secrets (OAuth apps) | 6+ |
| Service-specific Tokens | 10+ |
| Bot Tokens (Telegram) | 1 |
| SSH Keypairs | 2 |
| Total Keychain Entries | 78 |
Cloud Resources
| Cloudflare Pages Projects | 80+ |
| Cloudflare Workers | 8 |
| D1 Databases | 9 |
| KV Namespaces | 11 |
| R2 Buckets | 1 |
| DNS Zones | 5 |
| Docker Containers | 2 |
Network Exposure
| Port | Service | Binding | Risk Level |
|---|---|---|---|
| 8501 | oa-recruit-agent | 0.0.0.0 (all interfaces) | CRITICAL |
| 1834, 1854 | Elgato Camera Hub | 0.0.0.0 (all interfaces) | HIGH |
| 9333 | Chrome CDP | 127.0.0.1 | OK |
| 7777 | Node.js | 127.0.0.1 | OK |
| 8000-8002 | Python | 127.0.0.1 | OK |
| 8787 | Wrangler dev | 127.0.0.1 | OK |
| 5432 | PostgreSQL (Docker) | 127.0.0.1 | OK |
CLAUDE.md Instruction Injection Risk
MEDIUM RISKCLAUDE.md is the primary instruction file for Sterling. It has permissions -rw-r--r-- (644), meaning any process on the Mac Mini running as bradstevens can modify it. The container mounts the workspace directory read-write, so container processes can also modify CLAUDE.md.
Attack Vectors
- Inject instructions to exfiltrate data
- Modify protocols to bypass confirmation requirements
- Add malicious tool commands
- Redirect emails or messages to attacker-controlled destinations
Mitigations in Place
- FileVault encryption prevents offline modification
- macOS SIP protects system files (CLAUDE.md is a user file, so limited coverage)
- File is 133KB ... significant additions would be noticeable in a diff
Recommendations
- Consider making CLAUDE.md read-only except during deliberate updates:
chmod 444 CLAUDE.mdwith a script to temporarily unlock for edits - Implement a hash-check at session start to detect unauthorized modifications
- Git-track CLAUDE.md to maintain a tamper-evident audit trail
Email Security Assessment
MEDIUM RISKSterling has explicit protocols against sending emails without Brad's confirmation (CLAUDE.md "ZERO-TOLERANCE CONFIRMATION RULE") and uses the Jenna Rosario persona with a specific signature. However, the protection is entirely at the AI instruction level, not at the API level.
Gap Analysis
There is no technical control preventing email sends. If CLAUDE.md were modified (see Instruction Injection above), or if a different agent/process used the same OAuth tokens, emails could be sent without any gate. The entire email safety system relies on instruction-level enforcement.
Recommendations
- Implement a server-side email approval queue (send to staging, Brad approves, then actually sends)
- Set up Gmail "Undo Send" with maximum delay (30 seconds)
- Create Gmail filters to flag/alert on any automated sends
- Consider a rate limiter on the OAuth token for Gmail send operations