Repository navigation
fix(eql): schema-qualify operators in comparison wrapper bodies - #1551
Draft
cipherstash-agent wants to merge 2 commits into
Draft
cipherstash-agent wants to merge 2 commits into
cipherstash-agent wants to merge 2 commits into
Conversation
The eql_v3 comparison wrappers (eql_v3.lt, eq, ...) are unpinned LANGUAGE sql functions so they can inline into functional-index scans, which means PostgreSQL parses their bodies under the caller's search_path. The _ord_ore wrappers compare eql_v3_internal.ore_block_256, a composite type whose comparison operators are created in public. With public off the path (a SECURITY DEFINER helper with SET search_path = '', or a hardened role path) the bare < in the body resolved to pg_catalog's built-in record comparison instead, ordering the ciphertext bytes with no error. The generator now names every wrapper operator with its schema, taken from a new Term::operator_schema (public for ORE, pg_catalog for the HMAC, bloom and OPE terms), and the bloom guard qualifies cardinality too. Inlining and index engagement are unchanged: the inlined expression carries the same operator either way. The four hand-written json_entry ordering wrappers get the same treatment. Tests: a codegen test checks every generated wrapper body across the catalog uses no bare operator and names the right schema per term, and a new SQLx suite runs every ORE-carrying domain's wrappers and OPERATOR(public.<op>) forms under search_path = '' against the ORE comparator, on pairs where it disagrees with byte order (red on the previous installer, green now). Refs: CIP-5153 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Jmn7p5byuYMWqoxDUAZQE
🦋 Changeset detectedLatest commit: 63da8b3 The changes in this PR will be included in the next version bump. This PR includes changesets to release 12 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
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 |
This comment has been minimized.
This comment has been minimized.
test:public_identifiers forbids private issue identifiers in tracked files; describe the behaviour directly instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Jmn7p5byuYMWqoxDUAZQE
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.
Requested by CipherStash · project thread
Summary
Fixes CIP-5153. The
_ord_orecomparison functions (eql_v3.lt,lte,gt,gte,eq,neq) gave silently wrong answers whenpublicwas not on the caller'ssearch_path.The wrappers are unpinned
LANGUAGE sqlso they inline into functional-index scans, which means PostgreSQL parses their bodies under the caller'ssearch_path. The ORE wrappers compareeql_v3_internal.ore_block_256, a composite type whose operators are created inpublic. Withoutpublicon the path (aSECURITY DEFINERhelper withSET search_path = '', as Supabase recommends, or a hardened role path), the bare<resolved to pg_catalog's built-in record comparison, ordering the ciphertext bytes with no error. Reproduced on PostgreSQL 16: a malformed pair raises "Ciphertexts are different lengths" on the default path and returnstundersearch_path = ''.Changes
eql-domains: newTerm::operator_schema()(publicfor ORE,pg_catalogfor HMAC, bloom and OPE) andTerm::term_for_operator();extractor_for_operatornow delegates to it.eql-codegen:wrapper_entrytakes the servingTermand emitsOPERATOR(<schema>.<op>); the bloom empty-needle guard qualifiescardinalityand its operators too. All generated wrapper SQL undersrc/v3/scalars/is regenerated (body lines only).src/v3/json/operators.sql: the four hand-written json_entry ordering wrappers nameOPERATOR(pg_catalog.<op>).skills/stash-postgres: a new trap entry on querying encrypted columns whenpublicis off thesearch_path(a barecol < $1still falls back to native jsonb there; use the function form orOPERATOR(public.<)).@cipherstash/eqlpatch,stashpatch (skill).Verification
cargo test -p eql-domains -p eql-codegen: green apart from two tests that fail when run as root (generate_type_failure_does_not_delete_before_writing,write_failure_preserves_existing_target_and_leaves_no_temp); they fail the same way onmainin this environment.every_wrapper_body_schema_qualifies_its_operatorschecks every generated wrapper body across the catalog: no bare operator token, and the right schema per term (literal, so a wrongoperator_schemamapping fails it; confirmed by flipping ORE topg_catalog).v3_search_path_independence_tests: every ORE-carrying domain's wrappers, query-twin overloads andOPERATOR(public.<op>)forms undersearch_path = '', checked againstcompare_ore_block_256_termson pairs where ORE order and byte order disagree; plus aSET search_path = ''SECURITY DEFINERhelper on a malformed pair. Red on the published 3.0.6 installer, green on this branch (local PostgreSQL 16).lint_tests,v3_public_surface_tests,v3_operator_equivalents_tests,v3_jsonb_operator_surface_tests,v3_ore_fallback_tests,build_validation_tests,payload_schema_tests. Suites needing ZeroKMS-generated fixtures were not run locally (no credentials); CI runs them.EXPLAINonc < queryand oneql_v3.lt(c, query)undersearch_path = ''both use theeql_v3.ord_term_ore(c)btree index.cargo fmt --check,cargo clippyclean.Related
generate.rsand the generatedquery_*SQL, so whichever lands second needs a rebase and acargo run -p eql-codegenregenerate.Review notes
col < qwithpublicoff the path still resolves to native jsonb. That is the cross-family fall-through tracked in CIP-5155, out of scope here; the changeset and skill say so.packages/eql/packages/eql/sql,crates/eql-bindings/sql) are left alone; the release version hook rebuilds them when this changeset bumps EQL.Generated by Claude Code