Feature Branch: 026-pii-detection
Created: 2026-01-31
Status: Draft
Input: User description: "Sensitive Data Detection - Detect secrets, API keys, private keys, sensitive file paths in tool calls. Integrate with Activity Log."
Related Proposal: docs/proposals/004-security-attack-detection.md (Phase 3)
This feature adds automatic detection of sensitive data in MCP tool call arguments and responses. The focus is on secrets and credentials (API keys, private keys, tokens) and sensitive file path access (SSH keys, cloud credentials, environment files). Detection results are recorded in the Activity Log, enabling users to identify potential data exposure or exfiltration risks.
Design Principle: Detection-only mode - no automatic blocking or redaction. Users gain visibility into sensitive data flows to make informed decisions.
A security-conscious user wants to know when API keys, tokens, or credentials pass through MCPProxy. When an AI agent accidentally exposes an AWS key or GitHub token in tool arguments or responses, the user should see this flagged in the Activity Log.
Why this priority: Secrets are the highest-risk sensitive data. Leaked credentials can lead to account takeover, data breaches, and financial loss. This is the core security value.
Independent Test: Execute a tool call with an AWS access key (AKIAIOSFODNN7EXAMPLE) in arguments, view Activity Log, verify detection indicator shows "aws_access_key" type.
Acceptance Scenarios:
- Given a tool call contains
AKIAIOSFODNN7EXAMPLEin arguments, When I view the Activity Log, Then I see a sensitive data indicator with "aws_access_key" detected - Given a tool response contains a GitHub PAT (
ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx), When I view activity details, Then I see "github_token" type with location "response" - Given a tool call contains
-----BEGIN RSA PRIVATE KEY-----, When detection runs, Then "private_key" type is detected with severity "critical" - Given a tool call contains a Stripe key (
sk_live_+ 24 chars), When I view details, Then "stripe_key" is detected
A user wants to know when an AI agent attempts to read sensitive files like SSH private keys, AWS credentials, or environment files. This could indicate a compromised or malicious MCP server attempting data exfiltration.
Why this priority: File path detection catches exfiltration attempts at the intent stage - before secrets are actually exposed. This is critical for detecting tool poisoning attacks.
Independent Test: Execute a tool call with argument {"path": "~/.ssh/id_rsa"}, verify Activity Log shows "sensitive_file_path" detection with "ssh_private_key" category.
Acceptance Scenarios:
- Given a tool argument contains
~/.ssh/id_rsa(Linux/macOS) orC:\Users\user\.ssh\id_rsa(Windows), When detection runs, Then "sensitive_file_path" is detected with category "ssh" and severity "critical" - Given a tool argument contains
/home/user/.aws/credentials(Linux) or%USERPROFILE%\.aws\credentials(Windows), When I view details, Then "aws_credentials_file" is detected - Given a tool argument contains
.env.production, When detection runs, Then "env_file" is detected with severity "high" - Given a tool argument contains
/etc/shadow(Linux), When detection runs, Then "system_password_file" is detected with severity "critical" - Given a tool argument contains
C:\Users\user\AppData\Roaming\npm\npmrc(Windows), When detection runs, Then "auth_token_file" is detected
A compliance officer needs to audit all tool calls that involved sensitive data. They want to filter the Activity Log to show only records where sensitive data was detected, with the ability to filter by detection type and severity.
Why this priority: Filtering is essential for practical use at scale. Without it, users must manually scan all records, making security auditing impractical.
Independent Test: Execute multiple tool calls (some with secrets, some without), filter Activity Log by "Sensitive Data: Yes" and severity "critical", verify only relevant records appear.
Acceptance Scenarios:
- Given the Activity Log contains mixed records, When I filter by "Sensitive Data Detected", Then only records with detections are shown
- Given I want to find API key exposures, When I filter by type "api_key", Then only records with API key detections appear
- Given I want critical issues only, When I filter by severity "critical", Then only critical detections (private keys, cloud credentials) appear
- Given I'm using CLI, When I run
mcpproxy activity list --sensitive-data --severity critical, Then filtered results are returned
A developer using MCPProxy via CLI wants to see sensitive data detection results when reviewing activity. The CLI should show detection status in list output and full details in the show command.
Why this priority: CLI is a primary interface for developers and automation. Enables scripting and integration with security tools.
Independent Test: Run mcpproxy activity list after sensitive data detection, verify indicator column. Run mcpproxy activity show <id>, verify full detection details.
Acceptance Scenarios:
- Given activity records with detections exist, When I run
mcpproxy activity list, Then I see a "SENSITIVE" indicator column - Given an activity record with detected secrets, When I run
mcpproxy activity show <id>, Then I see detection types, severities, and locations - Given I want JSON for automation, When I run
mcpproxy activity list --sensitive-data -o json, Then I get structured detection data
An enterprise user has organization-specific sensitive data formats (e.g., internal API key format ACME-KEY-xxxxxxxx, employee IDs) that should be detected. They want to add custom regex patterns.
Why this priority: Custom patterns extend the system to organization-specific needs but are not required for core functionality. Most users benefit from built-in patterns alone.
Independent Test: Add custom pattern via configuration, execute tool call with matching data, verify custom pattern detected in Activity Log.
Acceptance Scenarios:
- Given I configure
{"name": "acme_api_key", "regex": "ACME-KEY-[a-f0-9]{32}", "severity": "high"}, When a tool call contains "ACME-KEY-abc123...", Then it is detected as "acme_api_key" - Given I add sensitive keywords
["internal-only", "confidential"], When those words appear in tool data, Then they are flagged - Given an invalid regex pattern, When MCPProxy starts, Then I see a warning and the invalid pattern is skipped
A user working with payment-related tools wants to ensure credit card numbers are flagged if they appear in tool call data, as this could indicate PCI compliance issues.
Why this priority: Credit cards are a special PII category with regulatory implications (PCI-DSS). Lower priority than secrets because legitimate payment tools may handle card data intentionally.
Independent Test: Execute tool call with test card number 4111111111111111, verify detection with Luhn validation (valid card detected, random 16-digit numbers ignored).
Acceptance Scenarios:
- Given a tool call contains
4111111111111111, When detection runs, Then "credit_card" is detected (passes Luhn validation) - Given a tool call contains
1234567890123456(invalid Luhn), When detection runs, Then it is NOT flagged as credit card - Given a tool call contains
4111-1111-1111-1111(with dashes), When detection runs, Then "credit_card" is still detected
- What happens when detection encounters very large payloads (>1MB)? Detection applies to first 1MB with truncation flag.
- How are false positives handled? Users view detection details to assess; no automatic action taken.
- What if a secret pattern matches example/test values? Known test patterns (AKIAIOSFODNN7EXAMPLE) are flagged but marked as "likely_example".
- What happens with base64-encoded secrets? Detection scans raw content; base64-encoded PEM keys are still detected by their markers.
- How are secrets in JSON string escapes handled? Content is unescaped (
\\n→\n) before scanning.
Secret Detection (Tier 1 - Critical)
- FR-001: System MUST detect AWS credentials (access key IDs matching
AKIA[0-9A-Z]{16}and similar prefixes) - FR-002: System MUST detect private keys (RSA, EC, DSA, OpenSSH, PGP) via PEM header markers
- FR-003: System MUST detect GitHub tokens (PAT, OAuth, App tokens matching
gh[pous]_[0-9a-zA-Z]{36,}) - FR-004: System MUST detect GitLab tokens (
glpat-,gldt-, runner tokens) - FR-005: System MUST detect GCP API keys (
AIza[0-9A-Za-z\-_]{35}) - FR-006: System MUST detect Azure credentials (client secrets, storage keys)
- FR-007: System MUST detect OpenAI/Anthropic API keys
- FR-008: System MUST detect JWT tokens via
eyJprefix pattern
Secret Detection (Tier 2 - High)
- FR-009: System MUST detect Stripe keys (
sk_live_,sk_test_,pk_live_) - FR-010: System MUST detect Slack tokens (
xoxb-,xoxp-, webhook URLs) - FR-011: System MUST detect SendGrid API keys (
SG\.[a-zA-Z0-9_-]{22}\.) - FR-012: System MUST detect Twilio credentials (Account SID, Auth Token)
- FR-013: System MUST detect database connection strings with embedded credentials
- FR-014: System MUST detect high-entropy strings (Shannon entropy > 4.5) as potential secrets
Sensitive File Path Detection (Cross-Platform)
- FR-015: System MUST detect SSH key paths on all platforms:
- Linux/macOS:
~/.ssh/id_*,~/.ssh/authorized_keys,~/.ssh/config - Windows:
%USERPROFILE%\.ssh\id_*,C:\Users\*\.ssh\* - All:
*.pem,*.key,*.ppk,*.pub(when private key indicators present)
- Linux/macOS:
- FR-016: System MUST detect cloud credential paths on all platforms:
- Linux:
~/.aws/credentials,~/.config/gcloud/*,~/.azure/*,~/.kube/config - macOS:
~/.aws/credentials,~/Library/Application Support/gcloud/*,~/.azure/*,~/.kube/config - Windows:
%USERPROFILE%\.aws\credentials,%APPDATA%\gcloud\*,%USERPROFILE%\.azure\*,%USERPROFILE%\.kube\config
- Linux:
- FR-017: System MUST detect environment and config files (all platforms):
.env,.env.*,.env.local,.env.production,.env.developmentsecrets.json,credentials.json,config.json(in sensitive contexts)appsettings.json,appsettings.*.json(ASP.NET)web.config(IIS/ASP.NET - may contain connection strings)
- FR-018: System MUST detect auth token files on all platforms:
- Linux/macOS:
.npmrc,.pypirc,.netrc,.git-credentials,.docker/config.json - Windows:
%USERPROFILE%\.npmrc,%APPDATA%\npm\npmrc,%USERPROFILE%\.docker\config.json - All:
.composer/auth.json,.gem/credentials,.nuget/NuGet.Config
- Linux/macOS:
- FR-019: System MUST detect system sensitive files:
- Linux:
/etc/shadow,/etc/sudoers,/etc/passwd,/proc/*/environ,/etc/ssh/sshd_config - macOS:
/etc/sudoers,/etc/master.passwd,~/Library/Keychains/* - Windows:
SAM,SYSTEM,SECURITY(registry hives),%SYSTEMROOT%\repair\SAM
- Linux:
- FR-020: System MUST normalize paths before matching:
- Expand:
~,$HOME,%USERPROFILE%,%APPDATA%,%LOCALAPPDATA%,%SYSTEMROOT% - Handle both forward slashes and backslashes
- Case-insensitive matching on Windows, case-sensitive on Linux/macOS
- Expand:
Credit Card Detection
- FR-021: System MUST detect credit card numbers and validate using Luhn algorithm
- FR-022: System MUST support card numbers with various separators (spaces, dashes)
Activity Log Integration
- FR-023: System MUST store detection results in
metadata.sensitive_data_detectionfield - FR-024: System MUST record: detected (boolean), types (list), locations (field paths), severities, scan_duration_ms
- FR-025: System MUST NOT store actual secret values in detection results (only types and locations)
- FR-026: System MUST scan both tool call arguments AND responses
- FR-027: System MUST run detection asynchronously without blocking tool responses
User Interface - Web
- FR-028: Web UI MUST display sensitive data indicator on Activity Log records
- FR-029: Web UI MUST show detection details (types, severities, locations) in expanded view
- FR-030: Web UI MUST provide filter by "sensitive data detected" (yes/no)
- FR-031: Web UI MUST provide filter by detection type and severity
User Interface - CLI
- FR-032: CLI
activity listMUST include sensitive data indicator column - FR-033: CLI
activity showMUST display full detection details - FR-034: CLI MUST support
--sensitive-dataflag to filter detections - FR-035: CLI MUST support
--detection-type <type>and--severity <level>filters
Custom Patterns (Optional)
- FR-036: System SHOULD allow custom regex patterns via configuration
- FR-037: System SHOULD allow custom sensitive keywords list
- FR-038: System MUST validate patterns at startup and warn on invalid regex
- FR-039: Custom patterns MUST specify: name, pattern/keywords, severity (low/medium/high/critical)
REST API
- FR-040: GET
/api/v1/activityMUST supportsensitive_dataquery parameter - FR-041: GET
/api/v1/activityMUST supportdetection_typeandseverityparameters - FR-042: Activity responses MUST include
sensitive_data_detectionin metadata
- DetectionPattern: Name, regex/keywords, severity, category, validation function (optional)
- SensitiveDataDetectionResult: detected (bool), detections (list of Detection), scan_duration_ms
- Detection: type, severity, location (field path), category, is_likely_example (bool)
- ActivityRecord.metadata.sensitive_data_detection: Extension storing detection results
| Category | Examples | Default Severity |
|---|---|---|
cloud_credentials |
AWS, GCP, Azure keys | Critical |
private_key |
RSA, SSH, PGP keys | Critical |
api_token |
GitHub, GitLab, Stripe | High |
auth_token |
JWT, OAuth tokens | High |
sensitive_file |
~/.ssh/*, .env, .aws/credentials | Critical/High |
database_credential |
Connection strings | High |
high_entropy |
Random strings > 4.5 entropy | Medium |
credit_card |
Card numbers (Luhn valid) | Medium |
custom |
User-defined patterns | Configurable |
- SC-001: Detection completes within 15ms for typical tool call payloads (<64KB)
- SC-002: Built-in patterns detect >95% of common secret formats (AWS, GitHub, Stripe, private keys)
- SC-003: False positive rate for credit cards is <5% (due to Luhn validation)
- SC-004: False positive rate for API keys is <10% (due to format-specific patterns with prefixes)
- SC-005: Users can identify sensitive data records within 3 seconds via Web UI filter
- SC-006: CLI users can filter and export sensitive data records in a single command
- SC-007: All file path patterns correctly match on Windows, Linux, and macOS with appropriate path expansion and case handling
Sensitive data detection is enabled by default. Configuration in mcp_config.json:
{
"sensitive_data_detection": {
"enabled": true,
"scan_requests": true,
"scan_responses": true,
"max_payload_size_kb": 1024,
"entropy_threshold": 4.5,
"categories": {
"cloud_credentials": true,
"private_keys": true,
"api_tokens": true,
"sensitive_files": true,
"credit_cards": true,
"high_entropy": true
},
"custom_patterns": [
{
"name": "acme_api_key",
"regex": "ACME-KEY-[a-f0-9]{32}",
"severity": "high",
"category": "custom"
}
],
"sensitive_keywords": ["internal-only", "confidential", "do-not-share"]
}
}- Detection is for awareness/auditing only - no automatic blocking or redaction in this phase
- Detection runs asynchronously after tool completion to avoid impacting response latency
- Only
tool_callandinternal_tool_callactivity types are scanned - Known example/test values are flagged but marked as
is_likely_example: true - Path detection uses glob-style matching with home directory expansion
- The existing Activity Log infrastructure supports metadata extension
- Automatic secret redaction/masking in stored data
- Blocking tool calls based on detection (future feature)
- Real-time alerts/notifications (future feature)
- ML-based detection (NER/NLP for unstructured PII like names)
- Tool description scanning (separate TPA detection feature)
- International PII patterns (non-US SSN, phone formats)
- Gitleaks: Pattern-based with entropy, allowlists, composite rules
- TruffleHog: 800+ detectors with live verification
- detect-secrets: Plugin architecture with entropy analysis
SSH & Keys
| Platform | Paths |
|---|---|
| Linux/macOS | ~/.ssh/id_*, ~/.ssh/authorized_keys, ~/.ssh/config |
| Windows | %USERPROFILE%\.ssh\*, C:\Users\*\.ssh\* |
| All | *.pem, *.key, *.ppk, *.p12, *.pfx, *.keystore, *.jks |
Cloud Credentials
| Platform | Paths |
|---|---|
| Linux | ~/.aws/credentials, ~/.config/gcloud/*, ~/.azure/*, ~/.kube/config |
| macOS | ~/.aws/credentials, ~/Library/Application Support/gcloud/*, ~/.azure/* |
| Windows | %USERPROFILE%\.aws\credentials, %APPDATA%\gcloud\*, %USERPROFILE%\.azure\*, %USERPROFILE%\.kube\config |
Environment & Config
| Platform | Paths |
|---|---|
| All | .env, .env.*, secrets.json, credentials.json |
| .NET | appsettings.json, appsettings.*.json, web.config |
Auth Tokens
| Platform | Paths |
|---|---|
| Linux/macOS | .npmrc, .pypirc, .netrc, .git-credentials, .docker/config.json |
| Windows | %USERPROFILE%\.npmrc, %APPDATA%\npm\npmrc, %USERPROFILE%\.docker\config.json |
| All | .composer/auth.json, .gem/credentials, .nuget/NuGet.Config |
System Files
| Platform | Paths |
|---|---|
| Linux | /etc/shadow, /etc/sudoers, /etc/passwd, /proc/*/environ, /etc/ssh/sshd_config |
| macOS | /etc/sudoers, /etc/master.passwd, ~/Library/Keychains/* |
| Windows | SAM, SYSTEM, SECURITY (registry hives), %SYSTEMROOT%\repair\SAM |
- Simon Willison's "Lethal Trifecta" - access to private data + untrusted content + external communication
- Tool Poisoning Attacks (TPA) - malicious instructions in tool descriptions
- Real incidents: WhatsApp exfiltration, xAI key leak, DeepSeek exposure
When committing changes for this feature, follow these guidelines:
- Use:
Related #[issue-number]- Links without auto-closing - Do NOT use:
Fixes #,Closes #,Resolves #
- Do NOT include AI tool attribution in commits
feat(security): add sensitive data detection engine
Related #XXX
Implement detection for secrets and sensitive file paths in tool calls:
- Tier 1: Cloud credentials (AWS, GCP, Azure), private keys
- Tier 2: API tokens (GitHub, Stripe, Slack), database credentials
- File paths: SSH keys, cloud configs, env files
- Credit cards with Luhn validation
## Changes
- Add internal/security/detector.go with SensitiveDataDetector
- Add internal/security/patterns/ with pattern definitions
- Add internal/security/entropy.go for high-entropy detection
- Integrate with ActivityService.handleToolCallCompleted()
## Testing
- Unit tests for all pattern categories
- Luhn validation tests
- Path normalization tests
- Entropy threshold tests