fix: audit log shows passwords that contain a double quote (#31659)

* fix: audit log shows passwords that contain a double quote

With AUDIT_LOG_LEVEL set to REQUEST or REQUEST_RESPONSE, a password containing a double quote was only masked up to that quote, so a new password like Q"secret was logged as "********"secret. A request with whitespace before the colon, such as "new_password" : "secret", was not masked at all. Any field whose name ends in "password" is now masked through its closing quote in both cases.

* fix: audit log records passwords sent back in responses

With AUDIT_LOG_LEVEL set to REQUEST_RESPONSE, fields whose name ends in "password" were masked in request bodies, but response bodies were logged unmasked. Saving or opening the LDAP server settings therefore wrote the Application DN Password to the audit log in plain text, because the settings come back in the response, and the Jupyter passwords in the code execution settings leaked the same way. Responses now get the same masking as requests.
This commit is contained in:
Classic298 2026-10-01 05:21:58 +02:00 • committed by GitHub
parent f50f9e6252
commit 3ef0d15433
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -113,6 +113,17 @@ class AuditContext:
self.response_body.extend(chunk[: self.max_body_size - len(self.response_body)])
def redact_passwords(body: str) -> str:
if 'password' not in body.lower():
return body
return re.sub(
r'"(\w*password)"\s*:\s*"(?:[^"\\]|\\.)*"',
r'"\1": "********"',
body,
flags=re.IGNORECASE,
)
class AuditLoggingMiddleware:
"""
ASGI middleware that intercepts HTTP requests and responses to perform audit logging. It captures request/response bodies (depending on audit level), headers, HTTP methods, and user information, then logs a structured audit entry at the end of the request cycle.
@ -282,13 +293,8 @@ class AuditLoggingMiddleware:
response_body = context.response_body.decode('utf-8', errors='replace')
# Redact sensitive information
if 'password' in request_body.lower():
request_body = re.sub(
r'"(\w*password)":\s*".*?"',
r'"\1": "********"',
request_body,
flags=re.IGNORECASE,
)
request_body = redact_passwords(request_body)
response_body = redact_passwords(response_body)
entry = AuditLogEntry(
id=str(uuid.uuid4()),