Repository: aws/aws-dynamodb-encryption-python

Issue:
Add repr=False to attributes in EncryptionContext
https://github.com/aws/aws-dynamodb-encryption-python/blob/master/src/dynamodb_encryption_sdk/structures.py#L49-L78

We should add `repr=False` to the attributes section in the `EncryptionContext`, to make the string representation safe within material providers. This is useful particularly in the context of logging.

Similar to https://github.com/aws/aws-dynamodb-encryption-java/issues/100

Discussion:

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 79b7fac..92e3daf 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -2,13 +2,15 @@
 Changelog
 *********
 
-1.1.2 -- 2019-09-??
+1.1.2 -- 2019-10-??
 ===================
 
 Bugfixes
 --------
 * Fix :class:`AwsKmsCryptographicMaterialsProvider` regional clients override bug
   `#124 <https://github.com/aws/aws-dynamodb-encryption-python/issues/124>`_
+* Remove ``attributes`` attribute from :class:`EncryptionContext` ``str`` and ``repr`` values.
+  `#127 <https://github.com/aws/aws-dynamodb-encryption-python/issues/127>`_
 
 1.1.1 -- 2019-08-29
 ===================
diff --git a/examples/src/aws_kms_encrypted_item.py b/examples/src/aws_kms_encrypted_item.py
index 8527d3f..665d673 100644
--- a/examples/src/aws_kms_encrypted_item.py
+++ b/examples/src/aws_kms_encrypted_item.py
@@ -41,7 +41,7 @@ def encrypt_item(table_name, aws_cmk_id):
     plaintext_item.update(index_key)
 
     # Create a normal table resource.
-    table = boto3.resource("dynamodb").Table(table_name)
+    table = boto3.resource("dynamodb").Table(table_name)  # generated code confuse pylint: disable=no-member
 
     # Use the TableInfo helper to collect information about the indexes.
     table_info = TableInfo(name=table_name)
diff --git a/examples/src/aws_kms_encrypted_resource.py b/examples/src/aws_kms_encrypted_resource.py
index f57478a..dabcf31 100644
--- a/examples/src/aws_kms_encrypted_resource.py
+++ b/examples/src/aws_kms_encrypted_resource.py
@@ -64,7 +64,9 @@ def encrypt_batch_items(table_name, aws_cmk_id):
     )
 
     # Get the encrypted item using the standard service resource.
-    encrypted_items = resource.batch_get_item(RequestItems={table_name: {"Keys": index_keys}})["Responses"][table_name]
+    encrypted_items = resource.batch_get_item(  # generated code confuse pylint: disable=no-member
+        RequestItems={table_name: {"Keys": index_keys}}
+    )["Responses"][table_name]
 
     # Get the item using the encrypted service resource, transparently decyrpting it.
     decrypted_items = encrypted_resource.batch_get_item(RequestItems={table_name: {"Keys": index_keys}})["Responses"][
diff --git a/examples/src/aws_kms_encrypted_table.py b/examples/src/aws_kms_encrypted_table.py
index f781f64..54f4823 100644
--- a/examples/src/aws_kms_encrypted_table.py
+++ b/examples/src/aws_kms_encrypted_table.py
@@ -39,7 +39,7 @@ def encrypt_item(table_name, aws_cmk_id):
     plaintext_item.update(index_key)
 
     # Create a normal table resource.
-    table = boto3.resource("dynamodb").Table(table_name)
+    table = boto3.resource("dynamodb").Table(table_name)  # generated code confuse pylint: disable=no-member
     # Create a crypto materials provider using the specified AWS KMS key.
     aws_kms_cmp = AwsKmsCryptographicMaterialsProvider(key_id=aws_cmk_id)
     # Create attribute actions that tells the encrypted table to encrypt all attributes except one.
diff --git a/examples/src/most_recent_provider_encrypted_table.py b/examples/src/most_recent_provider_encrypted_table.py
index cbb6656..972251f 100644
--- a/examples/src/most_recent_provider_encrypted_table.py
+++ b/examples/src/most_recent_provider_encrypted_table.py
@@ -41,7 +41,7 @@ def encrypt_item(table_name, aws_cmk_id, meta_table_name, material_name):
     plaintext_item.update(index_key)
 
     # Create a normal table resource for the meta store.
-    meta_table = boto3.resource("dynamodb").Table(meta_table_name)
+    meta_table = boto3.resource("dynamodb").Table(meta_table_name)  # generated code confuse pylint: disable=no-member
     # Create a crypto materials provider for the meta store using the specified AWS KMS key.
     aws_kms_cmp = AwsKmsCryptographicMaterialsProvider(key_id=aws_cmk_id)
     # Create a meta store using the AWS KMS crypto materials provider.
@@ -53,7 +53,7 @@ def encrypt_item(table_name, aws_cmk_id, meta_table_name, material_name):
         version_ttl=600.0,  # Check for a new material version every five minutes.
     )
     # Create a normal table resource.
-    table = boto3.resource("dynamodb").Table(table_name)
+    table = boto3.resource("dynamodb").Table(table_name)  # generated code confuse pylint: disable=no-member
     # Create attribute actions that tells the encrypted table to encrypt all attributes except one.
     actions = AttributeActions(
         default_action=CryptoAction.ENCRYPT_AND_SIGN, attribute_actions={"leave me": CryptoAction.DO_NOTHING}
diff --git a/examples/src/wrapped_rsa_encrypted_table.py b/examples/src/wrapped_rsa_encrypted_table.py
index d6eed86..d26e21b 100644
--- a/examples/src/wrapped_rsa_encrypted_table.py
+++ b/examples/src/wrapped_rsa_encrypted_table.py
@@ -40,7 +40,7 @@ def encrypt_item(table_name, rsa_wrapping_private_key_bytes, rsa_signing_private
     plaintext_item.update(index_key)
 
     # Create a normal table resource.
-    table = boto3.resource("dynamodb").Table(table_name)
+    table = boto3.resource("dynamodb").Table(table_name)  # generated code confuse pylint: disable=no-member
     # Create a crypto materials provider using the provided wrapping and signing keys.
     # We show private keys used here, but public keys could be used as well, allowing
     # only wrapping or signature verification.
diff --git a/examples/src/wrapped_symmetric_encrypted_table.py b/examples/src/wrapped_symmetric_encrypted_table.py
index 22a6508..0db58c4 100644
--- a/examples/src/wrapped_symmetric_encrypted_table.py
+++ b/examples/src/wrapped_symmetric_encrypted_table.py
@@ -40,7 +40,7 @@ def encrypt_item(table_name, aes_wrapping_key_bytes, hmac_signing_key_bytes):
     plaintext_item.update(index_key)
 
     # Create a normal table resource.
-    table = boto3.resource("dynamodb").Table(table_name)
+    table = boto3.resource("dynamodb").Table(table_name)  # generated code confuse pylint: disable=no-member
     # Create a crypto materials provider using the provided wrapping and signing keys.
     wrapping_key = JceNameLocalDelegatedKey(
         key=aes_wrapping_key_bytes,
diff --git a/src/dynamodb_encryption_sdk/structures.py b/src/dynamodb_encryption_sdk/structures.py
index 48e82cb..0dd3546 100644
--- a/src/dynamodb_encryption_sdk/structures.py
+++ b/src/dynamodb_encryption_sdk/structures.py
@@ -68,6 +68,7 @@ class EncryptionContext(object):
         validator=attr.validators.optional(attr.validators.instance_of(six.string_types)), default=None
     )
     attributes = attr.ib(
+        repr=False,
         validator=(dictionary_validator(six.string_types, dict), _validate_attribute_values_are_ddb_items),
         default=attr.Factory(dict),
     )


Test patch:
diff --git a/test/unit/test_structures.py b/test/unit/test_structures.py
new file mode 100644
index 0000000..6bae7da
--- /dev/null
+++ b/test/unit/test_structures.py
@@ -0,0 +1,26 @@
+# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"). You
+# may not use this file except in compliance with the License. A copy of
+# the License is located at
+#
+# http://aws.amazon.com/apache2.0/
+#
+# or in the "license" file accompanying this file. This file is
+# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
+# ANY KIND, either express or implied. See the License for the specific
+# language governing permissions and limitations under the License.
+"""Unit tests for ``dynamodb_encryption_sdk.structures``."""
+import pytest
+
+from dynamodb_encryption_sdk.structures import EncryptionContext
+
+
+@pytest.mark.parametrize("operator", (repr, str))
+def test_encryption_context_repr(operator):
+    value = EncryptionContext(attributes={"unique_name": {"S": "unique_value"}})
+
+    test = operator(value)
+
+    assert "unique_name" not in test
+    assert "unique_value" not in test