mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-29 06:31:32 +00:00
fix(security): redact xAI (Grok) API keys in logs
xAI is a first-class provider in hermes-agent with its own credential
pool entry (XAI_API_KEY / xai-oauth). API keys follow the format
xai-<60+ alphanumeric chars> and were absent from _PREFIX_PATTERNS in
agent/redact.py.
When a key appears raw in log output, tool results, or error messages,
it passed through completely unmasked. The ENV-assignment and Bearer
header patterns catch the most common cases, but a raw token in a
stack trace or debug print had no protection.
Verified before fix:
redact_sensitive_text("using key xai-ABCD...rstu to call xAI", force=True)
# "using key xai-ABCD...rstu to call xAI" <- exposed
After fix:
# "using key xai-AB...rstu to call xAI" <- masked
Five unit tests added to TestXaiToken covering bare token masking,
env assignment, short-prefix false positive, company name false
positive, and visible prefix in masked output.
This commit is contained in:
parent
fae0fa4325
commit
5613dfea93
2 changed files with 27 additions and 0 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue