diff --git a/agent/redact.py b/agent/redact.py index c6643304a9d..4cafbaef7a2 100644 --- a/agent/redact.py +++ b/agent/redact.py @@ -103,6 +103,7 @@ _PREFIX_PATTERNS = [ r"hsk-[A-Za-z0-9]{10,}", # Hindsight API key r"mem0_[A-Za-z0-9]{10,}", # Mem0 Platform API key r"brv_[A-Za-z0-9]{10,}", # ByteRover API key + r"xai-[A-Za-z0-9]{30,}", # xAI (Grok) API key ] # ENV assignment patterns: KEY=value where KEY contains a secret-like name diff --git a/tests/agent/test_redact.py b/tests/agent/test_redact.py index a2c6b60b276..928eb1ff357 100644 --- a/tests/agent/test_redact.py +++ b/tests/agent/test_redact.py @@ -511,3 +511,29 @@ class TestFormBodyRedaction: text = "first=1\nsecond=2" # Should pass through (still subject to other redactors) assert "first=1" in redact_sensitive_text(text) + + +class TestXaiToken: + KEY = "xai-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstu" + + def test_bare_token_masked(self): + result = redact_sensitive_text(f"using key {self.KEY}", force=True) + assert self.KEY not in result + assert "xai-AB" in result + + def test_env_assignment_masked(self): + result = redact_sensitive_text(f"XAI_API_KEY={self.KEY}", force=True) + assert self.KEY not in result + + def test_too_short_not_masked(self): + short = "xai-tooshort" + result = redact_sensitive_text(f"text {short} here", force=True) + assert short in result + + def test_company_name_not_masked(self): + result = redact_sensitive_text("xai is a company", force=True) + assert result == "xai is a company" + + def test_prefix_visible_in_masked_output(self): + result = redact_sensitive_text(self.KEY, force=True) + assert result.startswith("xai-AB")