From a73785a3ec3ce63ec155709dac69ee1124e6ab31 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Thu, 17 Oct 2024 14:33:11 +1100 Subject: [PATCH 1/7] Add basic testing harness --- languages/go/goeql/.errcheck-excludes | 0 languages/go/goeql/Makefile | 12 ++++++++++++ 2 files changed, 12 insertions(+) create mode 100644 languages/go/goeql/.errcheck-excludes create mode 100644 languages/go/goeql/Makefile diff --git a/languages/go/goeql/.errcheck-excludes b/languages/go/goeql/.errcheck-excludes new file mode 100644 index 00000000..e69de29b diff --git a/languages/go/goeql/Makefile b/languages/go/goeql/Makefile new file mode 100644 index 00000000..4854c217 --- /dev/null +++ b/languages/go/goeql/Makefile @@ -0,0 +1,12 @@ +all: test + +test: gotest goerrcheck gostaticcheck + +gotest: + go test ./... -v -timeout=45s -failfast + +goerrcheck: + errcheck -exclude .errcheck-excludes -ignoretests ./... + +gostaticcheck: + staticcheck ./... From 7f84094c3e51ca924f2262d199a1024c63dba5c1 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Fri, 18 Oct 2024 22:12:51 +1100 Subject: [PATCH 2/7] Add GitHub Actions test runner for goeql --- .github/workflows/test-goeql.yml | 38 ++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/workflows/test-goeql.yml diff --git a/.github/workflows/test-goeql.yml b/.github/workflows/test-goeql.yml new file mode 100644 index 00000000..809d6bf5 --- /dev/null +++ b/.github/workflows/test-goeql.yml @@ -0,0 +1,38 @@ +name: Test goeql + +on: + push: + branches: [ main ] + paths: + - .github/workflows/test-goeql.yml + - languages/go/goeql/** + pull_request: + branches: [ main ] + paths: + - .github/workflows/test-goeql.yml + - languages/go/goeql/** + +jobs: + + test: + name: Run test suite + runs-on: ubuntu-latest + steps: + + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: languages/go/goeql/go.mod + cache-dependency-path: | + languages/go/goeql/go.mod + + - name: Get dependencies + run: | + go get -v -t -d ./... + make cidep + working-directory: languages/go/goeql/ + + - name: Test + run: | + make test + working-directory: languages/go/goeql/ From e3fc32113311fe8dd5dd33a0cf14ded6792325d1 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Fri, 18 Oct 2024 22:19:26 +1100 Subject: [PATCH 3/7] Add target to download testing tools in CI --- languages/go/goeql/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/languages/go/goeql/Makefile b/languages/go/goeql/Makefile index 4854c217..32926ec8 100644 --- a/languages/go/goeql/Makefile +++ b/languages/go/goeql/Makefile @@ -2,6 +2,10 @@ all: test test: gotest goerrcheck gostaticcheck +cidep: + go install honnef.co/go/tools/cmd/staticcheck@latest + go install github.com/kisielk/errcheck@latest + gotest: go test ./... -v -timeout=45s -failfast From 61e8a4067b148be7a8f389dd893444a3f8874560 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Fri, 18 Oct 2024 23:39:58 +1100 Subject: [PATCH 4/7] Add linting to test harness --- languages/go/goeql/Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/languages/go/goeql/Makefile b/languages/go/goeql/Makefile index 32926ec8..86be762d 100644 --- a/languages/go/goeql/Makefile +++ b/languages/go/goeql/Makefile @@ -1,10 +1,11 @@ all: test -test: gotest goerrcheck gostaticcheck +test: gotest goerrcheck gostaticcheck golint cidep: go install honnef.co/go/tools/cmd/staticcheck@latest go install github.com/kisielk/errcheck@latest + go install golang.org/x/lint/golint@latest gotest: go test ./... -v -timeout=45s -failfast @@ -14,3 +15,6 @@ goerrcheck: gostaticcheck: staticcheck ./... + +golint: + golint From 56d2ce86bca5273339366a1225235718591d7b95 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Fri, 18 Oct 2024 23:40:31 +1100 Subject: [PATCH 5/7] Add appropriately formatted function comments for godoc --- languages/go/goeql/goeql.go | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/languages/go/goeql/goeql.go b/languages/go/goeql/goeql.go index 10f1d3d1..c244bb6f 100644 --- a/languages/go/goeql/goeql.go +++ b/languages/go/goeql/goeql.go @@ -12,11 +12,13 @@ import ( "strconv" ) +// TableColumn represents the table and column an encrypted value belongs to type TableColumn struct { T string `json:"t"` C string `json:"c"` } +// EncryptedColumn represents the plaintext value sent by a database client type EncryptedColumn struct { K string `json:"k"` P string `json:"p"` @@ -24,14 +26,19 @@ type EncryptedColumn struct { V int `json:"v"` } -// Creating custom types for encrypted fields to enable creating methods for -// serialization/deserialization of these types. +// EncryptedText is a string value to be encrypted type EncryptedText string + +// EncryptedJsonb is a jsonb value to be encrypted type EncryptedJsonb map[string]interface{} + +// EncryptedInt is a int value to be encrypted type EncryptedInt int + +// EncryptedBool is a bool value to be encrypted type EncryptedBool bool -// Text +// Serialize turns a EncryptedText value into a jsonb payload for CipherStash Proxy func (et EncryptedText) Serialize(table string, column string) ([]byte, error) { val, err := ToEncryptedColumn(string(et), table, column) if err != nil { @@ -40,6 +47,7 @@ func (et EncryptedText) Serialize(table string, column string) ([]byte, error) { return json.Marshal(val) } +// Deserialize turns a jsonb payload from CipherStash Proxy into an EncryptedText value func (et *EncryptedText) Deserialize(data []byte) (EncryptedText, error) { var jsonData map[string]interface{} if err := json.Unmarshal(data, &jsonData); err != nil { @@ -53,7 +61,7 @@ func (et *EncryptedText) Deserialize(data []byte) (EncryptedText, error) { return "", fmt.Errorf("invalid format: missing 'p' field in JSONB") } -// Jsonb +// Serialize turns a EncryptedJsonb value into a jsonb payload for CipherStash Proxy func (ej EncryptedJsonb) Serialize(table string, column string) ([]byte, error) { val, err := ToEncryptedColumn(map[string]any(ej), table, column) if err != nil { @@ -62,6 +70,7 @@ func (ej EncryptedJsonb) Serialize(table string, column string) ([]byte, error) return json.Marshal(val) } +// Deserialize turns a jsonb payload from CipherStash Proxy into an EncryptedJsonb value func (ej *EncryptedJsonb) Deserialize(data []byte) (EncryptedJsonb, error) { var jsonData map[string]interface{} if err := json.Unmarshal(data, &jsonData); err != nil { @@ -80,7 +89,7 @@ func (ej *EncryptedJsonb) Deserialize(data []byte) (EncryptedJsonb, error) { return nil, fmt.Errorf("invalid format: missing 'p' field in JSONB") } -// Int +// Serialize turns a EncryptedInt value into a jsonb payload for CipherStash Proxy func (et EncryptedInt) Serialize(table string, column string) ([]byte, error) { val, err := ToEncryptedColumn(int(et), table, column) if err != nil { @@ -89,6 +98,7 @@ func (et EncryptedInt) Serialize(table string, column string) ([]byte, error) { return json.Marshal(val) } +// Deserialize turns a jsonb payload from CipherStash Proxy into an EncryptedInt value func (et *EncryptedInt) Deserialize(data []byte) (EncryptedInt, error) { var jsonData map[string]interface{} if err := json.Unmarshal(data, &jsonData); err != nil { @@ -106,7 +116,7 @@ func (et *EncryptedInt) Deserialize(data []byte) (EncryptedInt, error) { return 0, fmt.Errorf("invalid format: missing 'p' field") } -// Bool +// Serialize turns a EncryptedBool value into a jsonb payload for CipherStash Proxy func (eb EncryptedBool) Serialize(table string, column string) ([]byte, error) { val, err := ToEncryptedColumn(bool(eb), table, column) if err != nil { @@ -115,6 +125,7 @@ func (eb EncryptedBool) Serialize(table string, column string) ([]byte, error) { return json.Marshal(val) } +// Deserialize turns a jsonb payload from CipherStash Proxy into an EncryptedBool value func (et *EncryptedBool) Deserialize(data []byte) (EncryptedBool, error) { var jsonData map[string]interface{} if err := json.Unmarshal(data, &jsonData); err != nil { @@ -133,8 +144,7 @@ func (et *EncryptedBool) Deserialize(data []byte) (EncryptedBool, error) { return false, fmt.Errorf("invalid format: missing 'p' field") } -// Serialize a query - +// SerializeQuery produces a jsonb payload used by EQL query functions to perform search operations like equality checks, range queries, and unique constraints. func SerializeQuery(value any, table string, column string) ([]byte, error) { query, err := ToEncryptedColumn(value, table, column) if err != nil { @@ -149,7 +159,7 @@ func SerializeQuery(value any, table string, column string) ([]byte, error) { } -// Converts a plaintext value to a string and returns the EncryptedColumn struct to use to insert into the db. +// ToEncryptedColumn converts a plaintext value to a string, and returns the EncryptedColumn struct for inserting into a database. func ToEncryptedColumn(value any, table string, column string) (EncryptedColumn, error) { str, err := convertToString(value) if err != nil { From 0b5439b99b2a0db0cf105ac74059882d60ef8a66 Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Fri, 18 Oct 2024 23:41:34 +1100 Subject: [PATCH 6/7] Add a more descriptive preface --- languages/go/goeql/goeql.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/languages/go/goeql/goeql.go b/languages/go/goeql/goeql.go index c244bb6f..c90df24e 100644 --- a/languages/go/goeql/goeql.go +++ b/languages/go/goeql/goeql.go @@ -1,10 +1,15 @@ package goeql -// This package contains helpers to use with Go/Xorm to serialize/deserialize values -// into the shape EQL and the CipherStash proxy needs to enable encryption/decryption. +// goeql is a collection of helpers for serializing and deserializing values +// into the shape EQL and the CipherStash Proxy needs to enable encryption and +// decryption of values, and search of those encrypted values while keeping them +// encrypted at all times. // EQL expects a json format that looks like this: +// // '{"k":"pt","p":"a string representation of the plaintext that is being encrypted","i":{"t":"table","c":"column"},"v":1}' +// +// More documentation on this format can be found at https://github.com/cipherstash/encrypt-query-language#data-format import ( "encoding/json" From 6afa323fa2155a310b7327098fa6c4749a8aa63a Mon Sep 17 00:00:00 2001 From: Lindsay Holmwood Date: Fri, 18 Oct 2024 23:41:57 +1100 Subject: [PATCH 7/7] Use a consistent receiver name --- languages/go/goeql/goeql.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/go/goeql/goeql.go b/languages/go/goeql/goeql.go index c90df24e..2a2de479 100644 --- a/languages/go/goeql/goeql.go +++ b/languages/go/goeql/goeql.go @@ -131,7 +131,7 @@ func (eb EncryptedBool) Serialize(table string, column string) ([]byte, error) { } // Deserialize turns a jsonb payload from CipherStash Proxy into an EncryptedBool value -func (et *EncryptedBool) Deserialize(data []byte) (EncryptedBool, error) { +func (eb *EncryptedBool) Deserialize(data []byte) (EncryptedBool, error) { var jsonData map[string]interface{} if err := json.Unmarshal(data, &jsonData); err != nil { // TODO: Check the best return values for these.