Skip to content

Go EQL v4 PostgreSQL test: check order with random values, not four fixed ones #1343

Open

Description

@coderdan

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.go draws random values but compares the terms in Go, never through EQL's SQL. The SQLx property suite under packages/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

  1. Add a property test beside TestEQLTypesRoundTripAndQueryInPostgres, using its harness. For every producible ordered EQL v4 type in eql.Types (indexes ope, ore or ore_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.
  2. 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.
  3. Also check ORDER BY on the column: it must give the plaintext order, with ties grouped.
  4. 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.
  5. Keep the run time reasonable in CI (the eql-postgres job in tests-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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions