From 14430195ee32fef98f3f54b74829759080ac670a Mon Sep 17 00:00:00 2001 From: box-sdk-build Date: Fri, 15 May 2026 03:40:31 -0700 Subject: [PATCH 1/2] chore: Update `.codegen.json` with commit hash of `codegen` and `openapi` spec [skip ci] --- .codegen.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.codegen.json b/.codegen.json index 7736cd25..d84c49fe 100644 --- a/.codegen.json +++ b/.codegen.json @@ -1 +1 @@ -{ "engineHash": "0f85d1e", "specHash": "576cd17", "version": "4.9.0" } +{ "engineHash": "2fabf30", "specHash": "576cd17", "version": "4.9.0" } From 5108087f538a88d7b942a3c0c79f50e0184d404a Mon Sep 17 00:00:00 2001 From: box-sdk-build Date: Fri, 15 May 2026 03:42:47 -0700 Subject: [PATCH 2/2] feat: Sanitize request body (box/box-codegen#948) --- .codegen.json | 2 +- box_sdk_gen/box/errors.py | 15 ++++++++--- box_sdk_gen/internal/logging.py | 26 ++++++++++++++++++++ box_sdk_gen/networking/box_network_client.py | 3 +++ box_sdk_gen/serialization/json.py | 26 ++++++++++++++++++++ test/box_sdk_gen/test/box_network_client.py | 1 + 6 files changed, 69 insertions(+), 4 deletions(-) diff --git a/.codegen.json b/.codegen.json index d84c49fe..296f4338 100644 --- a/.codegen.json +++ b/.codegen.json @@ -1 +1 @@ -{ "engineHash": "2fabf30", "specHash": "576cd17", "version": "4.9.0" } +{ "engineHash": "78a8dc0", "specHash": "576cd17", "version": "4.9.0" } diff --git a/box_sdk_gen/box/errors.py b/box_sdk_gen/box/errors.py index b1baf3d7..cc9b36a9 100644 --- a/box_sdk_gen/box/errors.py +++ b/box_sdk_gen/box/errors.py @@ -37,15 +37,24 @@ def __init__( url: str, query_params: Dict[str, str], headers: Dict[str, str], - body: Optional[str] = None, + body: Optional[Any] = None, + content_type: Optional[str] = None, ): self.method = method self.url = url self.query_params = query_params self.headers = headers self.body = body + self.content_type = content_type def print(self, data_sanitizer: DataSanitizer): + sanitized_body = ( + data_sanitizer.sanitize_string_body( + self.body, content_type=self.content_type + ) + if isinstance(self.body, str) + else self.body + ) return ''.join( ( f'\n\tMethod: {self.method}', @@ -55,8 +64,8 @@ def print(self, data_sanitizer: DataSanitizer): ''.join( [ '\n\tBody: ', - '\n' if self.body else '', - pprint.pformat(self.body, indent=8), + '\n' if sanitized_body else '', + pprint.pformat(sanitized_body, indent=8), ] ), ) diff --git a/box_sdk_gen/internal/logging.py b/box_sdk_gen/internal/logging.py index 41371aa4..83180f5d 100644 --- a/box_sdk_gen/internal/logging.py +++ b/box_sdk_gen/internal/logging.py @@ -1,11 +1,19 @@ from typing import Dict +from typing import Optional + from box_sdk_gen.serialization.json import SerializedData from box_sdk_gen.internal.utils import sanitize_map from box_sdk_gen.serialization.json import sanitize_serialized_data +from box_sdk_gen.serialization.json import sanitize_form_encoded_body_from_string + +from box_sdk_gen.serialization.json import json_to_serialized_data + +from box_sdk_gen.serialization.json import sd_to_json + class DataSanitizer: def __init__(self): @@ -29,3 +37,21 @@ def sanitize_headers(self, headers: Dict[str, str]) -> Dict[str, str]: def sanitize_body(self, body: SerializedData) -> SerializedData: return sanitize_serialized_data(body, self._keys_to_sanitize) + + def sanitize_form_encoded_body(self, body: str) -> str: + return sanitize_form_encoded_body_from_string(body, self._keys_to_sanitize) + + def sanitize_string_body( + self, body: str, *, content_type: Optional[str] = None + ) -> str: + if ( + content_type == 'application/json' + or content_type == 'application/json-patch+json' + ): + try: + return sd_to_json(self.sanitize_body(json_to_serialized_data(body))) + except Exception: + return body + if content_type == 'application/x-www-form-urlencoded': + return self.sanitize_form_encoded_body(body) + return body diff --git a/box_sdk_gen/networking/box_network_client.py b/box_sdk_gen/networking/box_network_client.py index 3edeef99..ddb8af4a 100644 --- a/box_sdk_gen/networking/box_network_client.py +++ b/box_sdk_gen/networking/box_network_client.py @@ -40,6 +40,7 @@ class APIRequest: headers: Dict[str, str] params: Dict[str, str] data: Optional[Union[str, ByteStream, MultipartEncoder]] + content_type: Optional[str] = None allow_redirects: bool = True timeout: Optional[Tuple[Optional[float], Optional[float]]] = None @@ -180,6 +181,7 @@ def _prepare_request( headers=headers, params=params, data=data, + content_type=options.content_type, allow_redirects=allow_redirects, timeout=timeout, ) @@ -304,6 +306,7 @@ def _raise_on_unsuccessful_request( query_params=request.params, headers=request.headers, body=request.data, + content_type=request.content_type, ), response_info=ResponseInfo( status_code=network_response.status_code, diff --git a/box_sdk_gen/serialization/json.py b/box_sdk_gen/serialization/json.py index 7d7b580c..4c0500c4 100644 --- a/box_sdk_gen/serialization/json.py +++ b/box_sdk_gen/serialization/json.py @@ -48,6 +48,32 @@ def sanitized_value() -> str: return '---[redacted]---' +def sanitize_form_encoded_body_from_string( + body: str, keys_to_sanitize: Dict[str, str] +) -> str: + return '&'.join( + [ + _sanitize_form_encoded_parameter(parameter, keys_to_sanitize) + for parameter in body.split('&') + ] + ) + + +def _sanitize_form_encoded_parameter( + parameter: str, keys_to_sanitize: Dict[str, str] +) -> str: + separator_index = parameter.find('=') + if separator_index < 0: + return parameter + + key = parameter[:separator_index] + value = parameter[separator_index + 1 :] + sanitized_parameter_value = ( + sanitized_value() if key.lower() in keys_to_sanitize else value + ) + return f'{key}={sanitized_parameter_value}' + + def sanitize_serialized_data( sd: SerializedData, keys_to_sanitize: Dict[str, str] ) -> SerializedData: diff --git a/test/box_sdk_gen/test/box_network_client.py b/test/box_sdk_gen/test/box_network_client.py index bfd00384..ecda14ae 100644 --- a/test/box_sdk_gen/test/box_network_client.py +++ b/test/box_sdk_gen/test/box_network_client.py @@ -338,6 +338,7 @@ def test_prepare_json_request(network_client, network_session_mock): }, params={"param": "value"}, data='{"key": "value"}', + content_type="application/json", timeout=(5, 60), )