Skip to content

Commit fd2ade2

Browse files
committed
Redact AWS session token from client repr
1 parent 0adf6df commit fd2ade2

4 files changed

Lines changed: 48 additions & 0 deletions

File tree

pymongo/asynchronous/mongo_client.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,13 +1300,24 @@ def __hash__(self) -> int:
13001300
return hash(self.eq_props())
13011301

13021302
def _repr_helper(self) -> str:
1303+
def redact_auth_mechanism_properties(value: Any) -> Any:
1304+
if isinstance(value, dict):
1305+
redacted = value.copy()
1306+
for key in redacted:
1307+
if key.upper() == "AWS_SESSION_TOKEN":
1308+
redacted[key] = "<redacted>"
1309+
return redacted
1310+
return value
1311+
13031312
def option_repr(option: str, value: Any) -> str:
13041313
"""Fix options whose __repr__ isn't usable in a constructor."""
13051314
if option == "document_class":
13061315
if value is dict:
13071316
return "document_class=dict"
13081317
else:
13091318
return f"document_class={value.__module__}.{value.__name__}"
1319+
if option == "authmechanismproperties":
1320+
value = redact_auth_mechanism_properties(value)
13101321
if option in common.TIMEOUT_OPTIONS and value is not None:
13111322
return f"{option}={int(value * 1000)}"
13121323

pymongo/synchronous/mongo_client.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,13 +1300,24 @@ def __hash__(self) -> int:
13001300
return hash(self.eq_props())
13011301

13021302
def _repr_helper(self) -> str:
1303+
def redact_auth_mechanism_properties(value: Any) -> Any:
1304+
if isinstance(value, dict):
1305+
redacted = value.copy()
1306+
for key in redacted:
1307+
if key.upper() == "AWS_SESSION_TOKEN":
1308+
redacted[key] = "<redacted>"
1309+
return redacted
1310+
return value
1311+
13031312
def option_repr(option: str, value: Any) -> str:
13041313
"""Fix options whose __repr__ isn't usable in a constructor."""
13051314
if option == "document_class":
13061315
if value is dict:
13071316
return "document_class=dict"
13081317
else:
13091318
return f"document_class={value.__module__}.{value.__name__}"
1319+
if option == "authmechanismproperties":
1320+
value = redact_auth_mechanism_properties(value)
13101321
if option in common.TIMEOUT_OPTIONS and value is not None:
13111322
return f"{option}={int(value * 1000)}"
13121323

test/asynchronous/test_client.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,19 @@ def test_types(self):
195195

196196
self.assertRaises(ConfigurationError, AsyncMongoClient, [])
197197

198+
async def test_repr_redacts_aws_session_token(self):
199+
token = "SECRET_AWS_SESSION_TOKEN"
200+
client = AsyncMongoClient(
201+
"mongodb://AKIA:SECRET@localhost:27017/"
202+
f"?authMechanism=MONGODB-AWS&authMechanismProperties=AWS_SESSION_TOKEN:{token}",
203+
connect=False,
204+
)
205+
206+
the_repr = repr(client)
207+
208+
self.assertNotIn(token, the_repr)
209+
self.assertIn("'AWS_SESSION_TOKEN': '<redacted>'", the_repr)
210+
198211
async def test_max_pool_size_zero(self):
199212
self.simple_client(maxPoolSize=0)
200213

test/test_client.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,19 @@ def test_types(self):
192192

193193
self.assertRaises(ConfigurationError, MongoClient, [])
194194

195+
def test_repr_redacts_aws_session_token(self):
196+
token = "SECRET_AWS_SESSION_TOKEN"
197+
client = MongoClient(
198+
"mongodb://AKIA:SECRET@localhost:27017/"
199+
f"?authMechanism=MONGODB-AWS&authMechanismProperties=AWS_SESSION_TOKEN:{token}",
200+
connect=False,
201+
)
202+
203+
the_repr = repr(client)
204+
205+
self.assertNotIn(token, the_repr)
206+
self.assertIn("'AWS_SESSION_TOKEN': '<redacted>'", the_repr)
207+
195208
def test_max_pool_size_zero(self):
196209
self.simple_client(maxPoolSize=0)
197210

0 commit comments

Comments
 (0)