Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
369 changes: 240 additions & 129 deletions README.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions cipherstash/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
psql -h localhost -p 6432 -U postgres.wvhsiwlbufuixlvdunxr -d postgres
14 changes: 14 additions & 0 deletions javascript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@ The following examples are available:

- [drizzle-eql](https://github.com/cipherstash/encrypt-query-language/tree/main/apps/drizzle): This is an example using the [drizzle-orm](https://drizzle-orm.com/) library to insert and select encrypted data from a PostgreSQL database using EQL and [CipherStash Proxy](https://docs.cipherstash.com/reference/proxy).

## Development

1. Run the install script to install the dependencies:

```bash
bun install
```

2. Listen for local package changes and rebuild the packages:

```bash
bun dev
```

## License

This project is licensed under the MIT License.
3 changes: 2 additions & 1 deletion javascript/apps/drizzle/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
},
"peerDependencies": {
"typescript": "^5.0.0",
"@cipherstash/eql": "workspace:*"
"@cipherstash/eql": "workspace:*",
"@cipherstash/utils": "workspace:*"
},
"dependencies": {
"drizzle-orm": "^0.33.0",
Expand Down
41 changes: 15 additions & 26 deletions javascript/apps/drizzle/src/insert.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,28 @@
import { parseArgs } from 'node:util'
import { getEmailArg } from '@cipherstash/utils'
import { eqlPayload } from '@cipherstash/eql'
import { db } from './db'
import { users } from './schema'

const { values, positionals } = parseArgs({
args: Bun.argv,
options: {
email: {
type: 'string',
},
},
strict: true,
allowPositionals: true,
const email = getEmailArg({
required: true,
})

const email = values.email

if (!email) {
throw new Error('[ERROR] the email command line argument is required')
}
const sql = db.insert(users).values({
email: email,
email_encrypted: eqlPayload({
plaintext: email,
table: 'users',
column: 'email_encrypted',
}),
})

await db
.insert(users)
.values({
email,
email_encrypted: eqlPayload({
plaintext: email,
table: 'users',
column: 'email_encrypted',
}),
})
.execute()
const sqlResult = sql.toSQL()
console.log('[INFO] SQL statement:', sqlResult)

await sql.execute()
console.log(
"[INFO] You've inserted a new user with an encrypted email from the plaintext",
email,
)

process.exit(0)
23 changes: 17 additions & 6 deletions javascript/apps/drizzle/src/select.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
import { getPlaintext } from '@cipherstash/eql'
import { getEmailArg } from '@cipherstash/utils'
import { cs_match_v1 } from '@cipherstash/eql/drizzle'
import { db } from './db'
import { users } from './schema'

const sqlResult = db
const email = getEmailArg({
required: false,
})

const sql = db
.select({
email: users.email_encrypted,
})
.from(users)
.toSQL()

const data = await db.select().from(users).execute()
if (email) {
sql.where(cs_match_v1(users, users.email_encrypted, email))
}

const sqlResult = sql.toSQL()
console.log('[INFO] SQL statement:', sqlResult)

console.log('[INFO] SQL statement:', sqlResult.sql)
const data = await sql.execute()
console.log('[INFO] All emails have been decrypted by CipherStash Proxy')
console.log(
'Emails:',
JSON.stringify(
data.map((row) => getPlaintext(row.email_encrypted)),
// data.map((row) => getPlaintext(row.email_encrypted)),
data,
null,
2,
),
)

process.exit(0)
24 changes: 6 additions & 18 deletions javascript/apps/prisma/src/insert.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,14 @@
import { parseArgs } from 'node:util'
import { getEmailArg } from '@cipherstash/utils'
import { eqlPayload } from '@cipherstash/eql'
import type { InputJsonValue } from '@prisma/client/runtime/library'
import { prisma } from './db'

const { values, positionals } = parseArgs({
args: Bun.argv,
options: {
email: {
type: 'string',
},
},
strict: true,
allowPositionals: true,
const email = getEmailArg({
required: true,
})

const email = values.email

if (!email) {
throw new Error('[ERROR] the email command line argument is required')
}

await prisma.user.create({
data: {
email,
email: email ?? 'test@test.com',
email_encrypted: eqlPayload({
plaintext: email,
table: 'users',
Expand All @@ -35,4 +21,6 @@ console.log(
"[INFO] You've inserted a new user with an encrypted email from the plaintext",
email,
)

await prisma.$disconnect()
process.exit(0)
Binary file modified javascript/bun.lockb
Binary file not shown.
7 changes: 6 additions & 1 deletion javascript/packages/eql/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"./drizzle": {
"types": "./dist/drizzle/index.d.ts",
"import": "./dist/drizzle/index.js",
"require": "./dist/drizzle/index.cjs"
}
},
"scripts": {
Expand All @@ -42,6 +47,6 @@
"typescript": "^5.0.0"
},
"publishConfig": {
"access": "restricted"
"access": "public"
}
}
26 changes: 26 additions & 0 deletions javascript/packages/eql/src/drizzle/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { sql } from 'drizzle-orm'
import {
type PgTable,
getTableConfig,
type PgColumn,
} from 'drizzle-orm/pg-core'
import { eqlPayload } from '../'

export const cs_match_v1 = (
table: PgTable,
column: PgColumn,
plaintext: string,
) => {
const tableName = getTableConfig(table)?.name
const columnName = column.name

const payload = JSON.stringify(
eqlPayload({
plaintext,
table: tableName,
column: columnName,
}),
)

return sql`cs_match_v1(${column}) @> cs_match_v1(${payload})`
}
4 changes: 2 additions & 2 deletions javascript/packages/eql/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export * from './cs_encrypted_v1'
import type { CsEncryptedV1Schema } from './cs_encrypted_v1'

type EqlPayload = {
plaintext: string
plaintext: string | undefined
table: string
column: string
version?: number
Expand All @@ -19,7 +19,7 @@ export const eqlPayload = ({
v: version,
s: 1,
k: 'pt',
p: plaintext,
p: plaintext ?? '',
i: {
t: table,
c: column,
Expand Down
2 changes: 1 addition & 1 deletion javascript/packages/eql/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { defineConfig } from 'tsup'

export default defineConfig({
entry: ['src/index.ts'],
entry: ['src/index.ts', 'src/drizzle/index.ts'],
format: ['cjs', 'esm'],
sourcemap: true,
dts: true,
Expand Down
15 changes: 15 additions & 0 deletions javascript/packages/utils/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# @cipehrstash/utils

To install dependencies:

```bash
bun install
```

To run:

```bash
bun run index.ts
```

This project was created using `bun init` in bun v1.1.12. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
26 changes: 26 additions & 0 deletions javascript/packages/utils/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "@cipherstash/utils",
"version": "0.0.0",
"description": "CipherStash shared utility functions",
"license": "MIT",
"author": "CipherStash <hello@cipherstash.com>",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
},
"scripts": {
"build": "tsup",
"dev": "tsup --watch"
},
"devDependencies": {
"@types/bun": "latest",
"tsup": "^8.3.0"
},
"peerDependencies": {
"typescript": "^5.0.0"
}
}
20 changes: 20 additions & 0 deletions javascript/packages/utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { parseArgs } from 'node:util'

export const getEmailArg = ({ required = true }: { required: boolean }) => {
const { values, positionals } = parseArgs({
args: Bun.argv,
options: {
email: {
type: 'string',
},
},
strict: true,
allowPositionals: true,
})

if (!values.email && required) {
throw new Error('[ERROR] the email command line argument is required')
}

return values.email
}
27 changes: 27 additions & 0 deletions javascript/packages/utils/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"compilerOptions": {
// Enable latest features
"lib": ["ESNext", "DOM"],
"target": "ESNext",
"module": "ESNext",
"moduleDetection": "force",
"jsx": "react-jsx",
"allowJs": true,

// Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,

// Best practices
"strict": true,
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,

// Some stricter flags (disabled by default)
"noUnusedLocals": false,
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false
}
}
8 changes: 8 additions & 0 deletions javascript/packages/utils/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { defineConfig } from 'tsup'

export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
sourcemap: true,
dts: true,
})
Loading