From c701c6dad72ae303e6eee23a230ac8ee10711a7d Mon Sep 17 00:00:00 2001 From: flamiinngo Date: Sun, 17 May 2026 12:14:32 +0100 Subject: [PATCH] fix(security): redact Fireworks AI API keys in logs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- agent/redact.py | 1 + tests/agent/test_redact.py | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/agent/redact.py b/agent/redact.py index c69003fcf..307e5dc3a 100644 --- a/agent/redact.py +++ b/agent/redact.py @@ -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. diff --git a/tests/agent/test_redact.py b/tests/agent/test_redact.py index afa841cb0..4174cd58a 100644 --- a/tests/agent/test_redact.py +++ b/tests/agent/test_redact.py @@ -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")