Files
pocketpaw/tests/test_redact.py
Rohit Kushwaha 874266977a fix(security): address PR #550 security review blockers and warnings
Tauri client: enable CSP, scope asset protocol to app directories,
set 0600 permissions on OAuth token file (Unix), use proper URL
parsing in proxy validation, add accept timeout to OAuth server thread.

Backend: scan Discord conversation history through injection scanner
before including in LLM context, use XML tags for identity
reinforcement, bound conversation history with deque(maxlen=30) and
TTL eviction, return completed projects from cancel() instead of
raising, add OpenRouter key redaction pattern.

Tests: update identity reinforcement and deep work cancel assertions,
add PII phone/IP overlap tests, add OpenRouter key redaction test.
2026-03-10 23:07:13 +05:30

243 lines
9.5 KiB
Python

"""Tests for output redaction module."""
from pocketpaw.security.redact import redact_output
class TestRedactOutput:
"""Test suite for output redaction."""
def test_redact_openai_key(self):
"""Test redaction of OpenAI API keys."""
text = "My OpenAI key is sk-abc123def456ghi789jkl012mno345"
result = redact_output(text)
assert "sk-abc123def456ghi789jkl012mno345" not in result
assert "[REDACTED]" in result
def test_redact_openrouter_key(self):
"""Test redaction of OpenRouter API keys (sk-or-v1-...)."""
text = "OPENROUTER_API_KEY=sk-or-v1-abc123def456"
result = redact_output(text)
assert "sk-or-v1-abc123def456" not in result
assert "[REDACTED]" in result
def test_redact_anthropic_key(self):
"""Test redaction of Anthropic API keys."""
text = (
"Use this key: sk-ant-api03-abc123def456ghi789jkl012mno345"
"pqr678stu901vwx234yz567abc890def123ghi456jkl789mno012pqr345stu678vwx"
)
result = redact_output(text)
assert "sk-ant-" not in result
assert "[REDACTED]" in result
def test_redact_aws_access_key(self):
"""Test redaction of AWS access keys."""
text = "AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE"
result = redact_output(text)
assert "AKIAIOSFODNN7EXAMPLE" not in result
assert "[REDACTED]" in result
def test_redact_aws_secret_key(self):
"""Test redaction of AWS secret keys."""
text = "AWS_SECRET=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
result = redact_output(text)
assert "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" not in result
assert "[REDACTED]" in result
def test_redact_bearer_token(self):
"""Test redaction of Bearer tokens."""
text = "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
result = redact_output(text)
assert "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" not in result
assert "[REDACTED]" in result
def test_redact_basic_auth_url(self):
"""Test redaction of basic auth credentials in URLs."""
text = "Connect to https://user:secretpassword123@example.com/api"
result = redact_output(text)
assert "secretpassword123" not in result
assert "[REDACTED]" in result
def test_redact_github_token(self):
"""Test redaction of GitHub personal access tokens."""
text = "Token: ghp_abcdefghijklmnopqrstuvwxyz123456"
result = redact_output(text)
assert "ghp_abcdefghijklmnopqrstuvwxyz123456" not in result
assert "[REDACTED]" in result
def test_redact_private_key_header(self):
"""Test redaction of private key headers."""
text = """
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA...
"""
result = redact_output(text)
assert "BEGIN RSA PRIVATE KEY" not in result
assert "[REDACTED]" in result
def test_redact_jwt_token(self):
"""Test redaction of JWT tokens."""
text = (
"JWT: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
".eyJzdWIiOiIxMjM0NTY3ODkwIn0"
".TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ"
)
result = redact_output(text)
assert "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" not in result
assert "[REDACTED]" in result
def test_redact_env_var_secret(self):
"""Test redaction of secrets in environment variable format."""
text = "DB_PASSWORD=SuperSecret123Pass"
result = redact_output(text)
assert "SuperSecret123Pass" not in result
assert "[REDACTED]" in result
def test_redact_slack_token(self):
"""Test redaction of Slack tokens."""
# Construct token dynamically to avoid triggering GitHub secret scanner
prefix = "xo" + "xb-"
fake_token = f"{prefix}0000000000-0000000000000-TESTTESTTESTTESTTEST"
text = f"Token: {fake_token}"
result = redact_output(text)
assert prefix + "0000000000" not in result
assert "[REDACTED]" in result
def test_redact_google_api_key(self):
"""Test redaction of Google API keys."""
# Construct key dynamically to avoid triggering GitHub secret scanner
prefix = "AIza" + "Sy"
fake_key = f"{prefix}DaGmWKa4JsXZ-HjGw7ISLn_3namBGewQe"
text = f"GOOGLE_API_KEY={fake_key}"
result = redact_output(text)
assert fake_key not in result
assert "[REDACTED]" in result
def test_redact_stripe_key(self):
"""Test redaction of Stripe API keys."""
# Construct key dynamically to avoid triggering GitHub secret scanner
prefix = "sk_" + "live_"
fake_key = f"{prefix}XXXXXXXXXXXXXXXXXXXXXXXX"
text = f"STRIPE_KEY={fake_key}"
result = redact_output(text)
assert fake_key not in result
assert "[REDACTED]" in result
def test_redact_api_key_parameter(self):
"""Test redaction of generic API key parameters."""
text = "api_key=abcdef123456789012345678"
result = redact_output(text)
assert "abcdef123456789012345678" not in result
assert "[REDACTED]" in result
def test_redact_token_parameter(self):
"""Test redaction of generic token parameters."""
text = "access_token=longtoken123456789012345678"
result = redact_output(text)
assert "longtoken123456789012345678" not in result
assert "[REDACTED]" in result
def test_multiple_secrets_in_text(self):
"""Test redaction of multiple different secrets in same text."""
text = """
Here's my config:
OPENAI_KEY=sk-abc123def456ghi789jkl012mno345
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
DATABASE_PASSWORD=SuperSecretPass123
"""
result = redact_output(text)
assert "sk-abc123def456ghi789jkl012mno345" not in result
assert "AKIAIOSFODNN7EXAMPLE" not in result
assert "SuperSecretPass123" not in result
assert result.count("[REDACTED]") >= 3
def test_no_false_positives_on_safe_text(self):
"""Test that safe text is not incorrectly redacted."""
text = "This is a normal message with no secrets."
result = redact_output(text)
assert result == text
assert "[REDACTED]" not in result
def test_empty_string(self):
"""Test that empty strings are handled correctly."""
result = redact_output("")
assert result == ""
def test_none_input(self):
"""Test that None input is handled correctly."""
result = redact_output(None)
assert result is None
def test_preserve_context_around_secrets(self):
"""Test that context around secrets is preserved."""
text = "My API key is sk-abc123def456ghi789jkl012mno345 and it works!"
result = redact_output(text)
assert "My API key is" in result
assert "and it works!" in result
assert "[REDACTED]" in result
def test_case_insensitive_patterns(self):
"""Test that patterns work case-insensitively where appropriate."""
text = "API_KEY=abcdef123456789012345678"
result = redact_output(text)
assert "abcdef123456789012345678" not in result
assert "[REDACTED]" in result
def test_redact_env_file_content(self):
"""Test redaction of a typical .env file content."""
# Construct Stripe key dynamically to avoid triggering GitHub secret scanner
stripe_prefix = "sk_" + "live_"
stripe_key = f"{stripe_prefix}XXXXXXXXXXXXXXXXXXXXXXXX"
text = f"""
# Database config
DB_HOST=localhost
DB_PORT=5432
DB_PASSWORD=MyDatabasePassword123
# API Keys
OPENAI_API_KEY=sk-abc123def456ghi789jkl012mno345
STRIPE_SECRET_KEY={stripe_key}
# AWS
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
"""
result = redact_output(text)
# Should preserve structure and comments
assert "# Database config" in result
assert "DB_HOST=localhost" in result
assert "DB_PORT=5432" in result
# Should redact secrets
assert "MyDatabasePassword123" not in result
assert "sk-abc123def456ghi789jkl012mno345" not in result
assert stripe_key not in result
assert "AKIAIOSFODNN7EXAMPLE" not in result
assert "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" not in result
# Should have multiple redactions
assert result.count("[REDACTED]") >= 5
def test_agent_cat_env_file_scenario(self):
"""Test the specific scenario mentioned in the issue: agent cats a .env file."""
text = """
$ cat .env
OPENAI_API_KEY=sk-proj-abc123def456ghi789
ANTHROPIC_API_KEY=sk-ant-api03-longkeyhere12345
DATABASE_URL=postgresql://user:password123@localhost/db
JWT_SECRET=SuperSecretJWTKey123456789
"""
result = redact_output(text)
# Command should remain
assert "$ cat .env" in result
# Variable names should remain
assert "OPENAI_API_KEY=" in result
assert "ANTHROPIC_API_KEY=" in result
assert "DATABASE_URL=" in result
assert "JWT_SECRET=" in result
# Secrets should be redacted
assert "sk-proj-abc123def456ghi789" not in result
assert "sk-ant-api03-longkeyhere12345" not in result
assert "password123" not in result
assert "SuperSecretJWTKey123456789" not in result
assert "[REDACTED]" in result