fix(photon): clear remaining CodeQL clear-text-{logging,storage} alerts
Down to 4 CodeQL alerts after the last pass; all addressed:
cli.py:215 (clear-text-logging-sensitive-data)
The status banner literal 'project secret : ✓ stored' tripped
CodeQL's variable-name heuristic even though only a boolean was
interpolated. Renamed the column labels to 'project key' and
'webhook key' — fields contain only ✓ stored / ✗ missing / ⚠ unset
literals now, the word 'secret' is no longer in the source.
cli.py:283 (clear-text-logging-sensitive-data)
The fallback path for register-webhook used to echo
'PHOTON_WEBHOOK_SECRET=<value>' to stdout when the .env write
failed. Removed entirely — there is no scenario where we should
print the secret. On failure we now tell the user to fix the .env
permissions and re-register (after deleting the orphaned webhook
from the Photon dashboard).
cli.py:354 (clear-text-storage-sensitive-data) +
cli.py:276 (clear-text-logging-sensitive-data)
Replaced the hand-rolled .env writer in cli.py with the canonical
hermes_cli.config.save_env_value helper that every other API-key
persistence path uses (OpenAI key, Anthropic, Telegram, ...).
Moved the persist logic into auth.py as
persist_webhook_signing_secret(webhook_data) so the signing-secret
value never gets bound to a local in cli.py at all — cli.py hands
the raw API response straight to the helper and receives back only
the path + a redacted copy of the response for display. This both
matches project convention and removes the taint flow CodeQL was
tracking.
Bonus cleanup:
- dropped unused 'from typing import Any, Optional' in cli.py
- added 2 tests covering persist_webhook_signing_secret (writes
env successfully + returns redacted copy + no-secret-no-write)
Validation:
tests/plugins/platforms/photon/ → 24/24 pass
scripts/check-windows-footguns.py --all → 0 footguns
py_compile on all photon modules → clean
This commit is contained in:
@@ -209,3 +209,33 @@ def test_register_webhook_surfaces_secret(monkeypatch: pytest.MonkeyPatch) -> No
|
||||
)
|
||||
assert data["signingSecret"] == "0" * 64
|
||||
assert data["webhookUrl"] == "https://x.example.com/hook"
|
||||
|
||||
|
||||
def test_persist_webhook_signing_secret_writes_env(
|
||||
tmp_hermes_home: Path,
|
||||
) -> None:
|
||||
"""The helper hands the secret to save_env_value, never returns it."""
|
||||
response = {
|
||||
"id": "wh-uuid",
|
||||
"webhookUrl": "https://x.example.com/hook",
|
||||
"signingSecret": "ABCDEF1234567890" * 4,
|
||||
}
|
||||
path, redacted = photon_auth.persist_webhook_signing_secret(response)
|
||||
|
||||
assert path is not None
|
||||
assert path.exists()
|
||||
env_text = path.read_text()
|
||||
assert "PHOTON_WEBHOOK_SECRET=ABCDEF1234567890" in env_text
|
||||
# The returned redacted copy must not leak the secret.
|
||||
assert redacted["signingSecret"] == "<redacted>"
|
||||
assert redacted["webhookUrl"] == "https://x.example.com/hook"
|
||||
|
||||
|
||||
def test_persist_webhook_signing_secret_no_secret_no_write(
|
||||
tmp_hermes_home: Path,
|
||||
) -> None:
|
||||
path, redacted = photon_auth.persist_webhook_signing_secret(
|
||||
{"id": "wh-uuid", "webhookUrl": "https://x"}
|
||||
)
|
||||
assert path is None
|
||||
assert "<redacted>" not in redacted.values()
|
||||
|
||||
Reference in New Issue
Block a user