fix(security): redact Fireworks AI API keys in logs

Fireworks AI is a first-class provider in hermes-agent — FIREWORKS_API_KEY
is listed in tools/environments/local.py and the provider is selectable via
the model picker (api.fireworks.ai in model_metadata, hermes_cli/models.py).

Fireworks API keys follow the format fw_<40 alphanumeric chars> and were
absent from _PREFIX_PATTERNS in agent/redact.py. The ENV-assignment and
Bearer header patterns catch FIREWORKS_API_KEY=fw_... in config output,
but a raw key in a stack trace, debug print, or tool error passed through
completely unmasked.

Four unit tests added to TestFireworksToken covering bare token masking,
env assignment, short-prefix false positive, and visible prefix in output.
This commit is contained in:
flamiinngo 2026-05-17 12:14:32 +01:00 committed by Teknium
parent ea95fdd6d7
commit c701c6dad7
2 changed files with 23 additions and 0 deletions

View file

@ -106,6 +106,7 @@ _PREFIX_PATTERNS = [
r"brv_[A-Za-z0-9]{10,}", # ByteRover API key
r"xai-[A-Za-z0-9]{30,}", # xAI (Grok) API key
r"ntn_[A-Za-z0-9]{10,}", # Notion internal integration token
r"fw_[A-Za-z0-9]{30,}", # Fireworks AI API key
]
# ENV assignment patterns: KEY=value where KEY contains a secret-like name.

View file

@ -886,3 +886,25 @@ class TestFileReadNonReusableRedaction:
out = redact_sensitive_text(f"key: {self.SK}", force=True, file_read=True)
assert "«redacted:sk-…»" in out
assert self.SK not in out
class TestFireworksToken:
KEY = "fw_" + "A" * 40
def test_bare_token_masked(self):
result = redact_sensitive_text(f"fireworks error: key {self.KEY}", force=True)
assert self.KEY not in result
assert "fw_AA" in result
def test_env_assignment_masked(self):
result = redact_sensitive_text(f"FIREWORKS_API_KEY={self.KEY}", force=True)
assert self.KEY not in result
def test_too_short_not_masked(self):
short = "fw_tooshort"
result = redact_sensitive_text(f"text {short} here", force=True)
assert short in result
def test_prefix_visible_in_masked_output(self):
result = redact_sensitive_text(self.KEY, force=True)
assert result.startswith("fw_AA")