fix(security): stop leaking live Jira/Tempo admin tokens in config export
Aegis CI / lint-and-test (push) Has been cancelled
Snyk Security Scan / Python vulnerabilities (backend) (push) Has been cancelled
Snyk Security Scan / npm vulnerabilities (frontend) (push) Has been cancelled
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Has been cancelled
Aegis CI / lint-and-test (push) Has been cancelled
Snyk Security Scan / Python vulnerabilities (backend) (push) Has been cancelled
Snyk Security Scan / npm vulnerabilities (frontend) (push) Has been cancelled
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Has been cancelled
_REDACTED_KEYS listed jira.api_token/jira.password/tempo.api_token, but routers/system.py actually writes admin credentials under jira.admin_api_token and tempo.admin_token. The mismatch meant every config export bundle included the live Jira and Tempo admin API tokens in plaintext instead of redacting them. Found while researching the Jira/Tempo integration for a service-account request; added a regression test.
This commit is contained in:
@@ -32,9 +32,17 @@ from app.models.webhook_config import WebhookConfig
|
|||||||
|
|
||||||
router = APIRouter(prefix="/admin", tags=["admin"])
|
router = APIRouter(prefix="/admin", tags=["admin"])
|
||||||
|
|
||||||
# Keys whose values contain secrets and must be redacted in the export
|
# Keys whose values contain secrets and must be redacted in the export.
|
||||||
|
# Must be kept in sync with the actual SystemConfig keys written by
|
||||||
|
# routers/system.py's _JIRA_KEYS map and the SMTP settings section —
|
||||||
|
# a stale/mismatched name here silently exports a live credential in
|
||||||
|
# plaintext instead of redacting it.
|
||||||
_REDACTED_KEYS = {
|
_REDACTED_KEYS = {
|
||||||
"smtp.password",
|
"smtp.password",
|
||||||
|
"jira.admin_api_token",
|
||||||
|
"tempo.admin_token",
|
||||||
|
# Older/alternate key names kept defensively in case a prior schema
|
||||||
|
# version wrote under these instead.
|
||||||
"jira.api_token",
|
"jira.api_token",
|
||||||
"jira.password",
|
"jira.password",
|
||||||
"tempo.api_token",
|
"tempo.api_token",
|
||||||
|
|||||||
@@ -46,6 +46,23 @@ def test_export_config_redacts_smtp_password(client, db, auth_headers):
|
|||||||
assert values["smtp.host"] == "smtp.example.com"
|
assert values["smtp.host"] == "smtp.example.com"
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_config_redacts_live_jira_and_tempo_admin_tokens(client, db, auth_headers):
|
||||||
|
"""Regression test: the redaction key list previously named
|
||||||
|
jira.api_token/jira.password/tempo.api_token, which don't match the
|
||||||
|
actual keys routers/system.py writes (jira.admin_api_token,
|
||||||
|
tempo.admin_token) — so the live admin Jira/Tempo credentials were
|
||||||
|
exported in plaintext instead of being redacted."""
|
||||||
|
db.add(SystemConfig(key="jira.admin_api_token", value="ATATT-real-jira-token"))
|
||||||
|
db.add(SystemConfig(key="tempo.admin_token", value="real-tempo-token"))
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
body = client.get("/api/v1/admin/export-config", headers=auth_headers).json()
|
||||||
|
values = {r["key"]: r["value"] for r in body["system_configs"]}
|
||||||
|
|
||||||
|
assert values["jira.admin_api_token"] == "[REDACTED]"
|
||||||
|
assert values["tempo.admin_token"] == "[REDACTED]"
|
||||||
|
|
||||||
|
|
||||||
def test_export_config_never_exports_sso_private_key(client, db, auth_headers):
|
def test_export_config_never_exports_sso_private_key(client, db, auth_headers):
|
||||||
db.add(SsoConfig(is_enabled=True, provider_name="Okta", sp_private_key="super-secret-key"))
|
db.add(SsoConfig(is_enabled=True, provider_name="Okta", sp_private_key="super-secret-key"))
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|||||||
Reference in New Issue
Block a user