Repository navigation
Go EQL v4 PostgreSQL test: check order with random values, not four fixed ones #1343
Description
Background
EQL is the SQL library CipherStash installs into a customer's PostgreSQL database. Its v4 schema (eql_v4) stores values that Stack Encrypt encrypts, and answers queries over them with SQL operators such as < and >=. A column can only be ordered if it carries an order term, a value derived from the plaintext so that comparing two terms gives the same answer as comparing the two plaintexts. EQL v4 supports three schemes for that: OPE (order-preserving encryption), CLLW ORE and block ORE (order-revealing encryption). The Go SDK (languages/golang) derives these terms when it stores a row or builds a query.
Problem
TestEQLTypesRoundTripAndQueryInPostgres (languages/golang/encrypt/eql_postgres_test.go, added in #1291) stores Go-encrypted rows in every EQL v4 type and runs <, <=, > and >= through PostgreSQL. But it uses four fixed values per type. That says little about order:
- A comparator that is only partly wrong still agrees with plaintext order on most pairs. If it reads only the first block of a block ORE term, mishandles a sign bit, or orders by a decimal's scale, it is still right for many values. Any four fixed values have a fair chance of being values it gets right.
- The existing property tests do not cover this path.
encrypt/order_test.godraws random values but compares the terms in Go, never through EQL's SQL. The SQLx property suite underpackages/eql/tests/sqlx/tests/encrypted_domain/property/covers EQL v3. Nothing runs random values from the Go SDK through EQL v4's operators in PostgreSQL.
So a change that breaks ordering for some values, in the Go SDK, the engine or the SQL comparator, can pass every test.
Proposal
- Add a property test beside
TestEQLTypesRoundTripAndQueryInPostgres, using its harness. For every producible ordered EQL v4 type ineql.Types(indexesope,oreorore_block), store a few hundred random values, including edge values: zero, negative zero, minimum and maximum, values that differ only in a late block, and equal values. - For random query values, run each of
<,<=,>,>=through PostgreSQL and require exactly the rows that plaintext comparison selects. Use a fixed seed that is logged, so a failure can be replayed. - Also check
ORDER BYon the column: it must give the plaintext order, with ties grouped. - Cover the plaintexts the current test covers: int16, int32, int64, float32, float64, text, date, timestamp, decimal. Decimals must order by value whatever their scale (
1.5=1.50), and timestamps to the microsecond. - Keep the run time reasonable in CI (the
eql-postgresjob intests-golang.yml), for example with one table per type and batched inserts.
A failure should name the type, the operator, the seed, and the first value pair whose order disagrees.
Relationship to other work
- test(golang): run the Go SDK rows and queries through EQL v4 in PostgreSQL #1291 adds the fixed-value test and the CI job this builds on.
- Go SDK: stashgen refuses a pointer field such as
*encrypt.Date, so an optional value cannot be encrypted #1298 (optional fields) is unaffected: the nil rows test(golang): run the Go SDK rows and queries through EQL v4 in PostgreSQL #1291 stores stay in the fixed-value test.
Activity
test(golang): run every order edge as a query value
test(golang): take the order index from the round-trip test's list