Skip to content

Commit 63c60ed

Browse files
committed
fix: apply CodeRabbit auto-fixes
- property.rs: redact userinfo from DATABASE_URL in connect_pool error context so a connection failure never logs the password (CodeRabbit Major). - proptest_invariants.rs: assert exact payload-key set equality (catches extra keys, not just missing ones). - live_oracle.rs: ensure! encrypt_store returns one payload per plaintext before zipping, so a count mismatch fails fast instead of silently truncating. - CHANGELOG.md: use the PR link (#275) instead of the CIP issue link.
1 parent dbe0c1b commit 63c60ed

4 files changed

Lines changed: 36 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Each entry that ships in a published release links to the PR that introduced it.
3333
- **`eql_v3.text` encrypted-domain family (`text`, `text_eq`, `text_match`, `text_ord`, `text_ord_ore`, `text_search`).** Adds equality (`=` / `<>` via HMAC), match (`@>` / `<@` via a new self-contained `eql_v3.bloom_filter` SEM index term), and ORE ordering (`<` `<=` `>` `>=`, `min` / `max`) for encrypted text, at parity with EQL v2 text — generated from the `text` row in `eql-scalars::CATALOG` by the same materializer as the `eql_v3.int4` reference. `text` is the first scalar to add a new index `Term` (`Bloom`) and the first non-integer, unbounded ordered kind (lexicographic pivots, hand-written `impl ScalarType`). The combined **`text_search`** domain carries all three capabilities in one type — `=` / `<>` via HMAC, `<` `<=` `>` `>=` / `min` / `max` via ORE, and `@>` / `<@` via bloom filter. Index via a functional index on the `eql_v3.eq_term` / `eql_v3.ord_term` / `eql_v3.match_term` extractors, not an operator class on the domain. Why: brings searchable encrypted text to the namespaced, `eql_v2`-free `eql_v3` surface. Match is exposed as bloom-filter containment on the `text_match` / `text_search` domains — deliberately *not* SQL `LIKE` (no wildcard/anchoring; probabilistic ngram containment) — and never backs equality. **Equality on the ordered text domains (`text_ord`, `text_ord_ore`) and on `text_search` always routes `=` / `<>` through `hm` (exact HMAC), never the ORE term — ORE is not exact-equality for text** (integer ordered domains keep exact ORE equality, which is lossless for them). ([#260](https://github.com/cipherstash/encrypt-query-language/pull/260))
3434
- **Self-contained `eql_v3` schema + standalone `release/cipherstash-encrypt-v3.sql` installer.** The `eql_v3` encrypted-domain surface no longer depends on `eql_v2` at runtime: it now owns its own copies of the searchable-encrypted-metadata (SEM) index-term types — `eql_v3.hmac_256` and `eql_v3.ore_block_256` (with its btree operator class) — so the `eql_v3.eq_term` / `eql_v3.ord_term` extractors return `eql_v3` types and no `eql_v2.<symbol>` appears anywhere in the v3 SQL. The whole v3 surface relocated under a single `src/v3/` tree (`src/v3/sem/` for the hand-written SEM types, `src/v3/scalars/` for the generated domain families). A new build variant ships the `eql_v3` schema on its own as `release/cipherstash-encrypt-v3.sql`, installable into a database with no `eql_v2` present; a CI gate greps that artifact and its dependency closure to keep it `eql_v2`-free. Why: a clean foundation for the per-scalar encrypted-domain model to stand alone, ahead of it replacing the `eql_v2_encrypted` composite column type. This is additive — a new schema and a new artifact — and leaves `eql_v2` byte-for-byte unchanged. ([#255](https://github.com/cipherstash/encrypt-query-language/pull/255))
3535
- **`eql_v3.text` encrypted-domain family (`text`, `text_eq`, `text_match`, `text_ord`, `text_ord_ore`).** Adds equality (`=` / `<>` via HMAC), match (`@>` / `<@` via a new self-contained `eql_v3.bloom_filter` SEM index term), and ORE ordering (`<` `<=` `>` `>=`, `min` / `max`) for encrypted text, at parity with EQL v2 text — generated from the `text` row in `eql-scalars::CATALOG` by the same materializer as the `eql_v3.int4` reference. `text` is the first scalar to add a new index `Term` (`Bloom`) and the first non-integer, unbounded ordered kind (lexicographic pivots, hand-written `impl ScalarType`). Index via a functional index on the `eql_v3.eq_term` / `eql_v3.ord_term` / `eql_v3.match_term` extractors, not an operator class on the domain. Why: brings searchable encrypted text to the namespaced, `eql_v2`-free `eql_v3` surface. Match is exposed as bloom-filter containment on the `text_match` domain — deliberately *not* SQL `LIKE` (no wildcard/anchoring; probabilistic ngram containment) — and never backs equality (which always routes through `Hm`). ([#260](https://github.com/cipherstash/encrypt-query-language/pull/260))
36-
- **Property-based tests for the `eql_v3` encrypted scalar domains.** A three-tier harness asserts SQL operator results agree with a plaintext oracle across a generated input space: a pure-Rust catalog-invariant tier (no database), a fixture-corpus tier that samples the live-encrypted fixtures and checks all ordered pairs in each sampled corpus, and a live-encryption tier (gated behind the `proptest-live` cargo feature) that batch-encrypts freshly generated plaintexts each run. Covers the equality (`=`/`<>`) and ordering (`<`/`<=`/`>`/`>=`, `ord_term` sort order) oracles plus NULL/blocker/CHECK edge cases. Why: the prior matrix exercised fixed pivots only; property tests catch operator/oracle disagreements across the whole value space. ([CIP-3141](https://linear.app/cipherstash/issue/CIP-3141))
36+
- **Property-based tests for the `eql_v3` encrypted scalar domains.** A three-tier harness asserts SQL operator results agree with a plaintext oracle across a generated input space: a pure-Rust catalog-invariant tier (no database), a fixture-corpus tier that samples the live-encrypted fixtures and checks all ordered pairs in each sampled corpus, and a live-encryption tier (gated behind the `proptest-live` cargo feature) that batch-encrypts freshly generated plaintexts each run. Covers the equality (`=`/`<>`) and ordering (`<`/`<=`/`>`/`>=`, `ord_term` sort order) oracles plus NULL/blocker/CHECK edge cases. Why: the prior matrix exercised fixed pivots only; property tests catch operator/oracle disagreements across the whole value space. ([#275](https://github.com/cipherstash/encrypt-query-language/pull/275))
3737
- **Self-contained `eql_v3` schema + standalone `release/cipherstash-encrypt-v3.sql` installer.** The `eql_v3` encrypted-domain surface no longer depends on `eql_v2` at runtime: it now owns its own copies of the searchable-encrypted-metadata (SEM) index-term types — `eql_v3.hmac_256` and `eql_v3.ore_block_u64_8_256` (with its btree operator class) — so the `eql_v3.eq_term` / `eql_v3.ord_term` extractors return `eql_v3` types and no `eql_v2.<symbol>` appears anywhere in the v3 SQL. The whole v3 surface relocated under a single `src/v3/` tree (`src/v3/sem/` for the hand-written SEM types, `src/v3/scalars/` for the generated domain families). A new build variant ships the `eql_v3` schema on its own as `release/cipherstash-encrypt-v3.sql`, installable into a database with no `eql_v2` present; a CI gate greps that artifact and its dependency closure to keep it `eql_v2`-free. Why: a clean foundation for the per-scalar encrypted-domain model to stand alone, ahead of it replacing the `eql_v2_encrypted` composite column type. This is additive — a new schema and a new artifact — and leaves `eql_v2` byte-for-byte unchanged. ([#255](https://github.com/cipherstash/encrypt-query-language/pull/255))
3838
- **`eql_v3.min` / `eql_v3.max` aggregates over `eql_v3.ste_vec_entry`.** SteVec document entries extracted at a selector (`doc -> 'sel'`) can now be aggregated like ordered scalars: `eql_v3.min(doc -> 'sel')` / `eql_v3.max(...)` return the entry with the smallest / largest ordered leaf. Ordering routes through the entry's `oc` (CLLW ORE) term via `eql_v3.ore_cllw` — the same comparator the entry `<` / `<=` / `>` / `>=` operators use, not the scalar Block-ORE `ord_term`. Only `oc`-carrying entries are orderable: an entry without an `oc` term (`eql_v3.ore_cllw` returns NULL) is non-orderable and is ignored by the aggregate — the same way the `eql_v3.ore_cllw` btree NULL-filters such rows — so a mix of `oc`-carrying and `oc`-less entries yields the extremum of the orderable subset rather than a corrupted result. Declared `PARALLEL = SAFE` with a combine function (the state function itself), so partial / parallel aggregation is available on large `GROUP BY` workloads. Why: brings encrypted-JSONB entry ordering to parity with the scalar encrypted-domain families' `MIN` / `MAX`, and lets the shared scalar behaviour matrix cover entry aggregation. Additive — the document and entry comparison surface is otherwise unchanged. ([#267](https://github.com/cipherstash/encrypt-query-language/pull/267))
3939

crates/eql-scalars/src/proptest_invariants.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,18 @@ proptest! {
9595
fn every_catalog_domain_payload_keys_match_its_terms() {
9696
for spec in CATALOG {
9797
for dom in spec.domains {
98-
let keys = Term::term_json_keys(dom.terms);
99-
// Each term contributes exactly its json_key; deduped.
100-
for t in dom.terms {
101-
assert!(
102-
keys.contains(&t.json_key()),
103-
"domain {} missing json key for {t:?}",
104-
spec.domain_name(dom)
105-
);
106-
}
98+
// Exact set equality: the payload keys are precisely each term's
99+
// json_key (deduped) — no missing keys and no extras.
100+
let actual: std::collections::HashSet<&str> =
101+
Term::term_json_keys(dom.terms).into_iter().collect();
102+
let expected: std::collections::HashSet<&str> =
103+
dom.terms.iter().map(|t| t.json_key()).collect();
104+
assert_eq!(
105+
actual,
106+
expected,
107+
"domain {} payload-key set mismatch",
108+
spec.domain_name(dom)
109+
);
107110
}
108111
}
109112
}

tests/sqlx/src/property.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,18 @@ pub async fn assert_ord_oracle<T: ScalarType>(
162162
Ok(())
163163
}
164164

165+
/// Replace any `user:password@` userinfo in a connection URL with `***@` so it
166+
/// is safe to put in error context / logs (the password never appears).
167+
fn redact_url(url: &str) -> String {
168+
match url.split_once("://") {
169+
Some((scheme, rest)) => match rest.rsplit_once('@') {
170+
Some((_userinfo, host)) => format!("{scheme}://***@{host}"),
171+
None => format!("{scheme}://{rest}"),
172+
},
173+
None => "<redacted>".to_string(),
174+
}
175+
}
176+
165177
/// Connect to the shared SQLx test database. Reads `DATABASE_URL`, falling back
166178
/// to the documented local default (`localhost:7432`, cipherstash/password).
167179
/// Used by the proptest tiers, which cannot use `#[sqlx::test]`'s injected pool
@@ -172,5 +184,6 @@ pub async fn connect_pool() -> Result<PgPool> {
172184
});
173185
PgPool::connect(&url)
174186
.await
175-
.with_context(|| format!("connecting property-test pool to {url}"))
187+
// Redact userinfo so a connection failure never logs the password.
188+
.with_context(|| format!("connecting property-test pool to {}", redact_url(&url)))
176189
}

tests/sqlx/tests/encrypted_domain/property/live_oracle.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ where
2525
{
2626
let config = column_config_for(&[IndexKind::Unique, IndexKind::Ore], cast)?;
2727
let payloads = encrypt_store(pool_table, "payload", values, &config).await?;
28+
// Fail fast on a count mismatch: a silent `zip` truncation would weaken the
29+
// oracle (fewer pairs than intended) and hide an encrypt_store contract
30+
// regression. (encrypt_store already checks this, but keep it local/explicit.)
31+
anyhow::ensure!(
32+
payloads.len() == values.len(),
33+
"encrypt_store returned {} payloads for {} plaintext values",
34+
payloads.len(),
35+
values.len()
36+
);
2837
Ok(values
2938
.iter()
3039
.cloned()

0 commit comments

Comments
 (0)