env sanitizer corrupts keys sharing suffix substrings
NousResearch/hermes-agent · cluster 17138
Data as of
Nothing open on GitHub. Fix merged in #17288. Check it covers your case.
The short answer
0open issues
0open PRs
6items grouped
- Known?
- Yes. 6 related items grouped here, 1 issue already closed.
- Fix event
- A fix was merged to main in PR #17288. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
resolved, deciding PR #17288. That is separate from the fix event. - Still open
- Nothing open on GitHub.
A bug where the .env sanitizer incorrectly splits keys like GLM_API_KEY because they contain a known suffix (LM_API_KEY) as a substring, along with multiple proposed fixes for the substring matching logic.
Open issues0
Open PRs0
Fixes on record
-
#17288 fix(cli): prevent .env sanitizer from splitting GLM_API_KEY by LM_API_KEY suffix
files:
hermes_cli/config.py,tests/hermes_cli/test_config.pyShow the diff
-
hermes_cli/config.py@@ -3710,18 +3710,27 @@ def _sanitize_env_lines(lines: list) -> list: # Detect concatenated KEY=VALUE pairs on one line. # Search for known KEY= patterns at any position in the line. - split_positions = [] + # We collect full needle ranges so we can drop matches that are + # fully contained within a longer overlapping needle. Without this, + # suffix collisions corrupt the file: e.g. LM_API_KEY= inside + # GLM_API_KEY= would otherwise split the line into "G\nLM_API_KEY=...". + match_ranges: list[tuple[int, int]] = [] for key_name in known_keys: needle = key_name + "=" idx = stripped.find(needle) while idx >= 0: - split_positions.append(idx) + match_ranges.append((idx, idx + len(needle))) idx = stripped.find(needle, idx + len(needle)) + split_positions = sorted({ + s for s, e in match_ranges + if not any( + s2 <= s and e2 >= e and (s2, e2) != (s, e) + for s2, e2 in match_ranges + ) + }) + if len(split_positions) > 1: - split_positions.sort() - # Deduplicate (shouldn't happen, but be safe) - split_positions = sorted(set(split_positions)) for i, pos in enumerate(split_positions): end = split_positions[i + 1] if i + 1 < len(split_positions) else len(stripped) part = stripped[pos:end].strip() -
tests/hermes_cli/test_config.py@@ -319,6 +319,23 @@ def test_value_ending_with_digits_still_splits(self): assert result[0].startswith("OPENROUTER_API_KEY=") assert result[1].startswith("OPENAI_BASE_URL=") + def test_glm_suffix_collision_not_split(self): + """GLM_API_KEY / GLM_BASE_URL must not be mangled by LM_API_KEY / LM_BASE_URL suffixes (#17138).""" + lines = [ + "GLM_API_KEY=[redacted:secret-assignment]", + "GLM_BASE_URL=https://api.z.ai/api/paas/v4\n", + ] + result = _sanitize_env_lines(lines) + assert result == lines, f"GLM_* lines were corrupted by suffix collision: {result}" + + def test_suffix_collision_does_not_break_real_concatenation(self): + """A genuine concatenation that happens to start with a suffix-superset key still splits.""" + lines = ["GLM_API_KEY=[redacted:secret-assignment]"] + result = _sanitize_env_lines(lines) + assert len(result) == 2 + assert result[0].startswith("GLM_API_KEY=") + assert result[1].startswith("LM_API_KEY=") + def test_save_env_value_fixes_corruption_on_write(self, tmp_path): """save_env_value sanitizes corrupted lines when writing a new key.""" env_file = tmp_path / ".env"
-
4 PRs closed without merging
From the close comments: done elsewhere 3 · merged via another change 1
-
#17141 fix(cli): prevent .env sanitizer from splitting GLM_API_KEY by LM_API_KEY suffix · merged via another change →
#17288 - #17241 fix(config): sanitize_env substring false positive breaks valid API keys · done elsewhere
- #17273 fix(config): prevent .env sanitizer from corrupting GLM_API_KEY and other suffix-trap keys · done elsewhere
-
#17291 fix(config): prevent _sanitize_env_lines from splitting valid keys via substring match · done elsewhere →
88e07c4,#17288,#17141,#17138
Closed issues · 1
- Closed#17138[Bug]: On start, Hermes Agent is santizing GLM_API_KEY/GLM_BASE_URL into G\nLM_API_KEY and G\nLM_BASE_URL
How this cluster was grouped
-
Link evidence (
structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference. -
Pairwise link density
0.33: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the LabAt a glance
- Merged PRs
- #17288
- Closed issues
- 1
- Attempts
- 4 PRs closed without merging
- Fix link checked
- 29 Apr 2026