Repository navigation
Conversation
Cipher.decrypt takes the rows psycopg read with dict_row and returns instances of the declared class, built with its constructor. A sealed column may be EQL JSON as a str, as UTF-8 bytes, or as a dict. Other columns are ignored. Every row is checked before any key request: its shape, its declared columns, its EQL JSON, and the table and column the JSON's "i" names. Each passthrough value is checked by exact type, as on encrypt. A refused row raises the new DecryptError or InvalidValueError, and the call returns no instance. The core crate decrypts with the engine's fake key source in its tests, so the round trip is tested without ZeroKMS. The engine does not say which field a kind mismatch was in. So decrypt_kind_mismatch names the field only when the plan has one sealed field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Open rows under the client scope, as Go's Client.Open does. A row sealed under another keyset of the same client now decrypts. Map stack_encrypt_foreign_keyset and stack_encrypt_context_mismatch to AuthenticationError. Name the row in a malformed-value error, and check for a duplicate typed null as the scalar path does. Borrow str and bytes column values, so the EQL text is copied once, into the engine's frame. Share the frame helpers between encrypt and decrypt, and move the credential fixtures into conftest.py. A kind mismatch names its field only when the model has one sealed field. The engine does not name the field yet. #1508 tracks that. The README says that a passthrough datetime needs a timestamptz column, because psycopg reads timestamp as a naive datetime. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Encode a str column value with encode_utf8, and use the bytes it returns. Under abi3-py39, PyString::to_cow encodes the text and then copies it again, on every Python version. Borrowing the UTF-8 of a str is in the limited API only from Python 3.10. Add a test that the engine names the field of a value with only an identifier, in a model with two sealed fields. So the field of decrypt_malformed does not depend on #1508. Move the untouched fixture into conftest.py, so the decrypt and encrypt tests share one copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Contributor
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true |
Contributor
|
Found 2 test failures on Blacksmith runners: Failures
|
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Description:
Summary
Cipher.decryptnow turns rows read from PostgreSQL back into instances of the declared model. EQL (Encrypt Query Language) is CipherStash's set of PostgreSQL types for encrypted values. Each row is a mapping of column names to values, as psycopg'sdict_rowreturns it. All the rows are opened with one key request per 500 sealed values, and the model's own constructor builds each instance.Changes
Cipher.decrypt(rows)returns a list of instances of the model class. The newDecryptErroris aStashError. The README has a new section, "Read the rows back", with adict_rowexample.decrypt_row_shape. A missing declared column isdecrypt_missing_column. A column value that is not EQL JSON isdecrypt_malformed. A value stored under another table or column isdecrypt_foreign_row. A passthrough value of the wrong type is anInvalidValueError. Each error names the row. Each error names the field too, exceptdecrypt_row_shape.decrypt_kind_mismatch. An opened date or timestamp that Python cannot hold isdecrypt_not_representable. No instance is built when any value fails.str, UTF-8bytes, or adictthat the driver already parsed. Columns the model does not declare are ignored.Client.Opendoes the same.stack_encrypt_foreign_keysetandstack_encrypt_context_mismatchare nowAuthenticationError.postgres:17for dataclasses, attrs, Pydantic,NamedTuple, and SQLAlchemy models. The credential fixtures are in a newtests/conftest.py.Verification
gate-probetests.mise run test:cargo: 24 core tests and 5 engine tests passed.cargo fmt --check, and mypy: no findings.pnpm run test:scripts: 83 of 83 files pass.livejob on this PR.Related
Closes #1390. Refs #1341 and #1508.
Review notes
decrypt_kind_mismatchnames its field only when the model has one sealed field. The engine error does not name the field. Stack Encrypt: a type mismatch in a batch decrypt does not name the field #1508 tracks that engine change.reasonofdecrypt_kind_mismatchisNone. The spec asked for the stored kind. The engine never names what a value held, so Python SDK: core call over psycopg, on EQL v4 and Stack Encrypt #1341 needs an amendment.datetimeneeds atimestamptzcolumn. psycopg readstimestampas a naivedatetime, anddecryptrefuses it. The README says so.Ignorecomes back asNone. The later SQLAlchemy layer will handle relationships. Python SDK: core call over psycopg, on EQL v4 and Stack Encrypt #1341 has a note on this.🤖 Generated with Claude Code