fix: deepcopy in reencrypt/decrypt_record so input is not mutated

both used record.copy() (shallow), leaving unencrypted mutable fields shared between the input and the returned dict, violating the documented 'input is not mutated' contract. switched to copy.deepcopy.

Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
disqualifier 2026-06-28 17:18:28 -04:00
parent 313b0c7d56
commit 16205e810a
2 changed files with 5 additions and 4 deletions

View File

@ -11,13 +11,13 @@ and storage-agnostic.
`requirements.txt`: `requirements.txt`:
``` ```
envelope_crypto @ git+ssh://git@git.rethinkstudios.io/rethink-public/envelope_crypto.git@v0.1.0 envelope_crypto @ git+ssh://git@git.rethinkstudios.io/rethink-public/envelope_crypto.git@v0.1.1
``` ```
Direct: Direct:
```bash ```bash
pip install "envelope_crypto @ git+ssh://git@git.rethinkstudios.io/rethink-public/envelope_crypto.git@v0.1.0" pip install "envelope_crypto @ git+ssh://git@git.rethinkstudios.io/rethink-public/envelope_crypto.git@v0.1.1"
``` ```
Requires `cryptography` (pulled transitively). Requires `cryptography` (pulled transitively).

View File

@ -54,6 +54,7 @@ function variants are the same functions — use whichever fits your storage.
""" """
import os import os
import copy
import json import json
import base64 import base64
import hashlib import hashlib
@ -313,7 +314,7 @@ class EnvelopeCrypto:
if not self.master_key: if not self.master_key:
raise ValueError("destination not initialized with data key") raise ValueError("destination not initialized with data key")
result = record.copy() result = copy.deepcopy(record)
for key, value in record.items(): for key, value in record.items():
if isinstance(value, dict) and value.get("secure") is True and "iv" in value and "data" in value: if isinstance(value, dict) and value.get("secure") is True and "iv" in value and "data" in value:
result[key] = self.encrypt_data(source_crypto.decrypt_data(value)) result[key] = self.encrypt_data(source_crypto.decrypt_data(value))
@ -361,7 +362,7 @@ def decrypt_record(crypto: EnvelopeCrypto, record, traversal_level: int = 2) ->
if not isinstance(record, dict): if not isinstance(record, dict):
return record return record
result = record.copy() result = copy.deepcopy(record)
for key, value in record.items(): for key, value in record.items():
if isinstance(value, dict) and value.get("secure") is True and "iv" in value and "data" in value: if isinstance(value, dict) and value.get("secure") is True and "iv" in value and "data" in value:
try: try: