spec(service-credential-tester): verify + strengthen no-secret-logs test + reconcile

Strengthen test_secrets_not_logged (N-2): now sends real-looking secrets
through prometheus + qbittorrent test_callables (mocked at network boundary),
asserts no fragments leak into caplog, verified non-vacuous. Write
apply-progress.md, tick all 29 tasks, add verify-report.md (21/21 PASS).
Gates green: 362+ pytest, ruff clean, npm build+lint 0 errors, 158 vitest.
This commit is contained in:
Developer
2026-07-09 23:15:47 +00:00
parent f6c67bd3ff
commit 98bf496a98
4 changed files with 387 additions and 37 deletions
+46 -8
View File
@@ -902,17 +902,55 @@ class TestServiceTestEndpoint:
assert before == after
def test_secrets_not_logged(self, test_client: TestClient, caplog) -> None:
"""No log line contains the secret value."""
secret_value = "super-secret-hunter2"
with caplog.at_level(logging.INFO):
test_client.post(
"""Secret values in the request body never reach any log line.
Unlike the trivial empty-secrets case, this drives the full endpoint
path (validate -> dispatch to the real test_callable -> success log)
with real-looking secret payloads. The per-type test_callables are
mocked at the network boundary so they succeed, proving the endpoint
does not log the secret values even though they are in the request body.
"""
api_key_secret = "glc_somethingverysecret"
password_secret = "SUPER-SECRET-PW-12345"
prom_response = SimpleNamespace(raise_for_status=lambda: None, json=lambda: {"results": {}})
qbit_client = MagicMock()
qbit_client.maindata.return_value = {"server_state": {"qbittorrent_version": "v4.6.0"}}
with (
caplog.at_level(logging.DEBUG),
patch("media_library_viewer_api.integrations.prometheus.requests.post", return_value=prom_response),
patch("media_library_viewer_api.integrations.qbittorrent.QbittorrentClient", return_value=qbit_client),
):
prom_resp = test_client.post(
"/api/services/test",
json={
"service_type": "backups",
"service_type": "prometheus",
"name": "test",
"config": {"ingestion_label": "default"},
"secrets": {},
"config": {"grafana_url": "http://grafana:3000"},
"secrets": {"grafana_api_key": api_key_secret},
"enabled": True,
},
)
assert secret_value not in caplog.text
qbit_resp = test_client.post(
"/api/services/test",
json={
"service_type": "qbittorrent",
"name": "test",
"config": {"base_url": "http://qb:8080"},
"secrets": {"username": "u", "password": password_secret},
"enabled": True,
},
)
# Both requests must run the endpoint fully (validate + dispatch + success).
assert prom_resp.status_code == 200
assert prom_resp.json()["ok"] is True
assert qbit_resp.status_code == 200
assert qbit_resp.json()["ok"] is True
# Neither the full secret values nor meaningful fragments may leak into logs.
leaked = [
fragment for fragment in (api_key_secret, password_secret, "verysecret", "SUPER") if fragment in caplog.text
]
assert not leaked, f"secret fragments leaked into logs: {leaked!r}"