@cipherstash/stack 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +38 -18
- package/dist/bin/stash.js +160 -117
- package/dist/bin/stash.js.map +1 -1
- package/dist/{chunk-HCL75LXQ.js → chunk-46PDK2Z6.js} +1 -1
- package/dist/chunk-46PDK2Z6.js.map +1 -0
- package/dist/{chunk-3BOJHROP.js → chunk-EHDOOLWU.js} +3 -3
- package/dist/{chunk-Y3KSTOVR.js → chunk-GNLU3I27.js} +14 -3
- package/dist/chunk-GNLU3I27.js.map +1 -0
- package/dist/{chunk-4C52Q5R6.js → chunk-O4MBO4DJ.js} +8 -22
- package/dist/chunk-O4MBO4DJ.js.map +1 -0
- package/dist/{client-_63nbmO7.d.ts → client-CZLBiToT.d.ts} +1 -7
- package/dist/{client-CV7X5Hrt.d.cts → client-bW-5wG9Y.d.cts} +1 -7
- package/dist/client.cjs +13 -2
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +2 -2
- package/dist/client.d.ts +2 -2
- package/dist/client.js +1 -1
- package/dist/drizzle/index.cjs +13 -2
- package/dist/drizzle/index.cjs.map +1 -1
- package/dist/drizzle/index.d.cts +2 -2
- package/dist/drizzle/index.d.ts +2 -2
- package/dist/drizzle/index.js +2 -2
- package/dist/dynamodb/index.d.cts +2 -2
- package/dist/dynamodb/index.d.ts +2 -2
- package/dist/encryption/index.cjs +22 -96
- package/dist/encryption/index.cjs.map +1 -1
- package/dist/encryption/index.d.cts +2 -2
- package/dist/encryption/index.d.ts +2 -2
- package/dist/encryption/index.js +3 -4
- package/dist/index.cjs +29 -81
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +4 -4
- package/dist/schema/index.cjs +13 -2
- package/dist/schema/index.cjs.map +1 -1
- package/dist/schema/index.d.cts +1 -1
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema/index.js +1 -1
- package/dist/secrets/index.cjs +29 -81
- package/dist/secrets/index.cjs.map +1 -1
- package/dist/secrets/index.d.cts +1 -1
- package/dist/secrets/index.d.ts +1 -1
- package/dist/secrets/index.js +4 -4
- package/dist/supabase/index.cjs +12 -2
- package/dist/supabase/index.cjs.map +1 -1
- package/dist/supabase/index.d.cts +2 -2
- package/dist/supabase/index.d.ts +2 -2
- package/dist/supabase/index.js +1 -1
- package/dist/{types-public-B5YxQhR6.d.cts → types-public-WADaA-TW.d.cts} +119 -1
- package/dist/{types-public-B5YxQhR6.d.ts → types-public-WADaA-TW.d.ts} +119 -1
- package/dist/types-public.cjs.map +1 -1
- package/dist/types-public.d.cts +1 -1
- package/dist/types-public.d.ts +1 -1
- package/dist/types-public.js +1 -1
- package/package.json +6 -5
- package/dist/chunk-4C52Q5R6.js.map +0 -1
- package/dist/chunk-HCL75LXQ.js.map +0 -1
- package/dist/chunk-Y3KSTOVR.js.map +0 -1
- /package/dist/{chunk-3BOJHROP.js.map → chunk-EHDOOLWU.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @cipherstash/stack
|
|
2
2
|
|
|
3
|
+
## 0.12.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 15764a8: Implement stack auth into stash cli flow.
|
|
8
|
+
|
|
9
|
+
## 0.11.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- b0e56b8: Upgrade protect-ffi to 0.21.0 and enable array_index_mode for searchable JSON
|
|
14
|
+
|
|
15
|
+
- Upgrade `@cipherstash/protect-ffi` to 0.21.0 across all packages
|
|
16
|
+
- Enable `array_index_mode: 'all'` on STE vec indexes so JSON array operations
|
|
17
|
+
(jsonb_array_elements, jsonb_array_length, array containment) work correctly
|
|
18
|
+
- Delegate credential resolution entirely to protect-ffi's `withEnvCredentials`
|
|
19
|
+
- Download latest EQL at build/runtime instead of bundling hardcoded SQL files
|
|
20
|
+
|
|
3
21
|
## 0.10.0
|
|
4
22
|
|
|
5
23
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -44,19 +44,29 @@ pnpm add @cipherstash/stack
|
|
|
44
44
|
|
|
45
45
|
## Quick Start
|
|
46
46
|
|
|
47
|
+
### 1. Initialize and authenticate your project
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npx stash init
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The wizard will authenticate you, walk you through choosing a database connection method, build an encryption schema, and install the required dependencies.
|
|
54
|
+
|
|
55
|
+
### 2. Encrypt and decrypt
|
|
56
|
+
|
|
47
57
|
```typescript
|
|
48
58
|
import { Encryption } from "@cipherstash/stack"
|
|
49
59
|
import { encryptedTable, encryptedColumn } from "@cipherstash/stack/schema"
|
|
50
60
|
|
|
51
|
-
//
|
|
61
|
+
// Define a schema
|
|
52
62
|
const users = encryptedTable("users", {
|
|
53
63
|
email: encryptedColumn("email").equality().freeTextSearch(),
|
|
54
64
|
})
|
|
55
65
|
|
|
56
|
-
//
|
|
66
|
+
// Create a client
|
|
57
67
|
const client = await Encryption({ schemas: [users] })
|
|
58
68
|
|
|
59
|
-
//
|
|
69
|
+
// Encrypt a value
|
|
60
70
|
const encrypted = await client.encrypt("hello@example.com", {
|
|
61
71
|
column: users.email,
|
|
62
72
|
table: users,
|
|
@@ -68,7 +78,7 @@ if (encrypted.failure) {
|
|
|
68
78
|
console.log("Encrypted payload:", encrypted.data)
|
|
69
79
|
}
|
|
70
80
|
|
|
71
|
-
//
|
|
81
|
+
// Decrypt the value
|
|
72
82
|
const decrypted = await client.decrypt(encrypted.data)
|
|
73
83
|
|
|
74
84
|
if (decrypted.failure) {
|
|
@@ -430,6 +440,16 @@ await secrets.delete("DATABASE_URL")
|
|
|
430
440
|
|
|
431
441
|
The `stash` CLI is bundled with the package and available after install.
|
|
432
442
|
|
|
443
|
+
### `stash auth`
|
|
444
|
+
|
|
445
|
+
Authenticate with CipherStash.
|
|
446
|
+
|
|
447
|
+
```bash
|
|
448
|
+
npx stash auth login
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
This runs the device code flow: it opens your browser, you confirm the code, and a token is saved to `~/.cipherstash/auth.json`. No environment variables or credentials files are needed for local development.
|
|
452
|
+
|
|
433
453
|
### `stash init`
|
|
434
454
|
|
|
435
455
|
Initialize CipherStash for your project with an interactive wizard.
|
|
@@ -440,11 +460,13 @@ npx stash init --supabase
|
|
|
440
460
|
```
|
|
441
461
|
|
|
442
462
|
The wizard will:
|
|
443
|
-
1.
|
|
444
|
-
2.
|
|
445
|
-
3.
|
|
463
|
+
1. Authenticate with CipherStash (device code flow)
|
|
464
|
+
2. Bind your device to the default Keyset
|
|
465
|
+
3. Choose your database connection method (Drizzle ORM, Supabase JS, Prisma, or Raw SQL)
|
|
466
|
+
4. Build an encryption schema interactively or use a placeholder, then generate the encryption client file
|
|
467
|
+
5. Install `@cipherstash/stack-forge` as a dev dependency for database tooling
|
|
446
468
|
|
|
447
|
-
After `stash init`,
|
|
469
|
+
After `stash init`, run `npx stash-forge setup` to configure your database.
|
|
448
470
|
|
|
449
471
|
| Flag | Description |
|
|
450
472
|
|------|-------------|
|
|
@@ -468,11 +490,15 @@ npx stash secrets delete -name DATABASE_URL -environment production
|
|
|
468
490
|
| `stash secrets list` | `-environment` | `-e` | List all secret names in an environment |
|
|
469
491
|
| `stash secrets delete` | `-name`, `-environment`, `-yes` | `-n`, `-e`, `-y` | Delete a secret (prompts for confirmation unless `-yes`) |
|
|
470
492
|
|
|
471
|
-
The CLI reads credentials from the same `CS_*` environment variables described in [Configuration](#configuration).
|
|
472
|
-
|
|
473
493
|
## Configuration
|
|
474
494
|
|
|
475
|
-
###
|
|
495
|
+
### Local Development
|
|
496
|
+
|
|
497
|
+
No environment variables or credentials are needed for local development. Run `npx @cipherstash/stack auth login` to authenticate via the device code flow, and the SDK and CLI will use the token saved to `~/.cipherstash/auth.json`.
|
|
498
|
+
|
|
499
|
+
### Going to Production
|
|
500
|
+
|
|
501
|
+
For production, CI/CD, and deployed environments, you'll need to set up machine credentials via environment variables:
|
|
476
502
|
|
|
477
503
|
| Variable | Description |
|
|
478
504
|
|-----|-------|
|
|
@@ -481,13 +507,7 @@ The CLI reads credentials from the same `CS_*` environment variables described i
|
|
|
481
507
|
| `CS_CLIENT_KEY` | Client key material used with ZeroKMS for encryption |
|
|
482
508
|
| `CS_CLIENT_ACCESS_KEY` | API key for authenticating with the CipherStash API |
|
|
483
509
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
Sign up at [cipherstash.com/signup](https://cipherstash.com/signup) and follow the onboarding to generate credentials.
|
|
487
|
-
|
|
488
|
-
### TOML Config
|
|
489
|
-
|
|
490
|
-
You can also configure credentials via `cipherstash.toml` and `cipherstash.secret.toml` files in your project root. See the [CipherStash docs](https://cipherstash.com/docs) for format details.
|
|
510
|
+
See the [Going to Production](https://cipherstash.com/docs/stack/going-to-production) guide for full details on creating machine clients, setting up access keys, and configuring CI/CD pipelines.
|
|
491
511
|
|
|
492
512
|
### Programmatic Config
|
|
493
513
|
|
package/dist/bin/stash.js
CHANGED
|
@@ -104,8 +104,8 @@ var require_package = __commonJS({
|
|
|
104
104
|
var require_main = __commonJS({
|
|
105
105
|
"../../node_modules/.pnpm/dotenv@16.4.7/node_modules/dotenv/lib/main.js"(exports, module) {
|
|
106
106
|
"use strict";
|
|
107
|
-
var
|
|
108
|
-
var
|
|
107
|
+
var fs = __require("fs");
|
|
108
|
+
var path = __require("path");
|
|
109
109
|
var os = __require("os");
|
|
110
110
|
var crypto = __require("crypto");
|
|
111
111
|
var packageJson = require_package();
|
|
@@ -211,7 +211,7 @@ var require_main = __commonJS({
|
|
|
211
211
|
if (options && options.path && options.path.length > 0) {
|
|
212
212
|
if (Array.isArray(options.path)) {
|
|
213
213
|
for (const filepath of options.path) {
|
|
214
|
-
if (
|
|
214
|
+
if (fs.existsSync(filepath)) {
|
|
215
215
|
possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
|
|
216
216
|
}
|
|
217
217
|
}
|
|
@@ -219,15 +219,15 @@ var require_main = __commonJS({
|
|
|
219
219
|
possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`;
|
|
220
220
|
}
|
|
221
221
|
} else {
|
|
222
|
-
possibleVaultPath =
|
|
222
|
+
possibleVaultPath = path.resolve(process.cwd(), ".env.vault");
|
|
223
223
|
}
|
|
224
|
-
if (
|
|
224
|
+
if (fs.existsSync(possibleVaultPath)) {
|
|
225
225
|
return possibleVaultPath;
|
|
226
226
|
}
|
|
227
227
|
return null;
|
|
228
228
|
}
|
|
229
229
|
function _resolveHome(envPath) {
|
|
230
|
-
return envPath[0] === "~" ?
|
|
230
|
+
return envPath[0] === "~" ? path.join(os.homedir(), envPath.slice(1)) : envPath;
|
|
231
231
|
}
|
|
232
232
|
function _configVault(options) {
|
|
233
233
|
_log("Loading env from encrypted .env.vault");
|
|
@@ -240,7 +240,7 @@ var require_main = __commonJS({
|
|
|
240
240
|
return { parsed };
|
|
241
241
|
}
|
|
242
242
|
function configDotenv(options) {
|
|
243
|
-
const dotenvPath =
|
|
243
|
+
const dotenvPath = path.resolve(process.cwd(), ".env");
|
|
244
244
|
let encoding = "utf8";
|
|
245
245
|
const debug = Boolean(options && options.debug);
|
|
246
246
|
if (options && options.encoding) {
|
|
@@ -263,13 +263,13 @@ var require_main = __commonJS({
|
|
|
263
263
|
}
|
|
264
264
|
let lastError;
|
|
265
265
|
const parsedAll = {};
|
|
266
|
-
for (const
|
|
266
|
+
for (const path2 of optionPaths) {
|
|
267
267
|
try {
|
|
268
|
-
const parsed = DotenvModule.parse(
|
|
268
|
+
const parsed = DotenvModule.parse(fs.readFileSync(path2, { encoding }));
|
|
269
269
|
DotenvModule.populate(parsedAll, parsed, options);
|
|
270
270
|
} catch (e2) {
|
|
271
271
|
if (debug) {
|
|
272
|
-
_debug(`Failed to load ${
|
|
272
|
+
_debug(`Failed to load ${path2} ${e2.message}`);
|
|
273
273
|
}
|
|
274
274
|
lastError = e2;
|
|
275
275
|
}
|
|
@@ -1200,6 +1200,91 @@ var Y2 = ({ indicator: t = "dots" } = {}) => {
|
|
|
1200
1200
|
} };
|
|
1201
1201
|
};
|
|
1202
1202
|
|
|
1203
|
+
// src/bin/commands/auth/login.ts
|
|
1204
|
+
import auth from "@cipherstash/auth";
|
|
1205
|
+
var { beginDeviceCodeFlow, bindClientDevice } = auth;
|
|
1206
|
+
var regions = [
|
|
1207
|
+
{ value: "ap-southeast-2.aws", label: "Asia Pacific (Sydney)" },
|
|
1208
|
+
{ value: "eu-central-1.aws", label: "Europe (Frankfurt)" },
|
|
1209
|
+
{ value: "eu-west-1.aws", label: "Europe (Ireland)" },
|
|
1210
|
+
{ value: "us-east-1.aws", label: "US East (N. Virginia)" },
|
|
1211
|
+
{ value: "us-east-2.aws", label: "US East (Ohio)" },
|
|
1212
|
+
{ value: "us-west-1.aws", label: "US West (N. California)" },
|
|
1213
|
+
{ value: "us-west-2.aws", label: "US West (Oregon)" }
|
|
1214
|
+
];
|
|
1215
|
+
async function selectRegion() {
|
|
1216
|
+
const region = await ve({
|
|
1217
|
+
message: "Select a region",
|
|
1218
|
+
options: regions
|
|
1219
|
+
});
|
|
1220
|
+
if (pD(region)) {
|
|
1221
|
+
xe("Cancelled.");
|
|
1222
|
+
process.exit(0);
|
|
1223
|
+
}
|
|
1224
|
+
return region;
|
|
1225
|
+
}
|
|
1226
|
+
async function login(region) {
|
|
1227
|
+
const s = Y2();
|
|
1228
|
+
const pending = await beginDeviceCodeFlow(region, "cli");
|
|
1229
|
+
M2.info(`Your code is: ${pending.userCode}`);
|
|
1230
|
+
M2.info(`Visit: ${pending.verificationUriComplete}`);
|
|
1231
|
+
M2.info(`Code expires in: ${pending.expiresIn}s`);
|
|
1232
|
+
const opened = pending.openInBrowser();
|
|
1233
|
+
if (!opened) {
|
|
1234
|
+
M2.warn("Could not open browser \u2014 please visit the URL above manually.");
|
|
1235
|
+
}
|
|
1236
|
+
s.start("Waiting for authorization...");
|
|
1237
|
+
const auth2 = await pending.pollForToken();
|
|
1238
|
+
s.stop("Authenticated! Token saved to ~/.cipherstash/auth.json");
|
|
1239
|
+
M2.info(
|
|
1240
|
+
`Token expires at: ${new Date(auth2.expiresAt * 1e3).toISOString()}`
|
|
1241
|
+
);
|
|
1242
|
+
}
|
|
1243
|
+
async function bindDevice() {
|
|
1244
|
+
const s = Y2();
|
|
1245
|
+
s.start("Binding device to the default Keyset...");
|
|
1246
|
+
try {
|
|
1247
|
+
await bindClientDevice();
|
|
1248
|
+
s.stop("Your device has been bound to the default Keyset!");
|
|
1249
|
+
} catch (error) {
|
|
1250
|
+
s.stop("Failed to bind your device to the default Keyset!");
|
|
1251
|
+
M2.error(error instanceof Error ? error.message : "Unknown error");
|
|
1252
|
+
process.exit(1);
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
// src/bin/commands/auth/index.ts
|
|
1257
|
+
var HELP = `
|
|
1258
|
+
Usage: stash auth <command>
|
|
1259
|
+
|
|
1260
|
+
Commands:
|
|
1261
|
+
login Authenticate with CipherStash
|
|
1262
|
+
|
|
1263
|
+
Examples:
|
|
1264
|
+
stash auth login
|
|
1265
|
+
`.trim();
|
|
1266
|
+
async function authCommand(args) {
|
|
1267
|
+
const subcommand = args[0];
|
|
1268
|
+
if (!subcommand || subcommand === "--help" || subcommand === "-h") {
|
|
1269
|
+
console.log(HELP);
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1272
|
+
switch (subcommand) {
|
|
1273
|
+
case "login":
|
|
1274
|
+
{
|
|
1275
|
+
const region = await selectRegion();
|
|
1276
|
+
await login(region);
|
|
1277
|
+
await bindDevice();
|
|
1278
|
+
}
|
|
1279
|
+
break;
|
|
1280
|
+
default:
|
|
1281
|
+
console.error(`Unknown auth command: ${subcommand}
|
|
1282
|
+
`);
|
|
1283
|
+
console.log(HELP);
|
|
1284
|
+
process.exit(1);
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1203
1288
|
// src/bin/commands/init/providers/base.ts
|
|
1204
1289
|
function createBaseProvider() {
|
|
1205
1290
|
return {
|
|
@@ -1212,10 +1297,7 @@ function createBaseProvider() {
|
|
|
1212
1297
|
{ value: "raw-sql", label: "Raw SQL / pg" }
|
|
1213
1298
|
],
|
|
1214
1299
|
getNextSteps(state) {
|
|
1215
|
-
const steps = [
|
|
1216
|
-
"Create a CipherStash account and get your credentials:\n https://dashboard.cipherstash.com/sign-up\n Then set: CS_WORKSPACE_CRN, CS_CLIENT_ID, CS_CLIENT_KEY, CS_CLIENT_ACCESS_KEY",
|
|
1217
|
-
"Set up your database: npx stash-forge setup"
|
|
1218
|
-
];
|
|
1300
|
+
const steps = ["Set up your database: npx stash-forge setup"];
|
|
1219
1301
|
if (state.clientFilePath) {
|
|
1220
1302
|
steps.push(`Edit your encryption schema: ${state.clientFilePath}`);
|
|
1221
1303
|
}
|
|
@@ -1241,10 +1323,7 @@ function createSupabaseProvider() {
|
|
|
1241
1323
|
{ value: "raw-sql", label: "Raw SQL / pg" }
|
|
1242
1324
|
],
|
|
1243
1325
|
getNextSteps(state) {
|
|
1244
|
-
const steps = [
|
|
1245
|
-
"Create a CipherStash account and get your credentials:\n https://dashboard.cipherstash.com/sign-up\n Then set: CS_WORKSPACE_CRN, CS_CLIENT_ID, CS_CLIENT_KEY, CS_CLIENT_ACCESS_KEY",
|
|
1246
|
-
"Set up your database: npx stash-forge setup"
|
|
1247
|
-
];
|
|
1326
|
+
const steps = ["Set up your database: npx stash-forge setup"];
|
|
1248
1327
|
if (state.clientFilePath) {
|
|
1249
1328
|
steps.push(`Edit your encryption schema: ${state.clientFilePath}`);
|
|
1250
1329
|
}
|
|
@@ -1257,6 +1336,18 @@ function createSupabaseProvider() {
|
|
|
1257
1336
|
};
|
|
1258
1337
|
}
|
|
1259
1338
|
|
|
1339
|
+
// src/bin/commands/init/steps/authenticate.ts
|
|
1340
|
+
var authenticateStep = {
|
|
1341
|
+
id: "authenticate",
|
|
1342
|
+
name: "Authenticate with CipherStash",
|
|
1343
|
+
async run(state, _provider) {
|
|
1344
|
+
const region = await selectRegion();
|
|
1345
|
+
await login(region);
|
|
1346
|
+
await bindDevice();
|
|
1347
|
+
return { ...state, authenticated: true };
|
|
1348
|
+
}
|
|
1349
|
+
};
|
|
1350
|
+
|
|
1260
1351
|
// src/bin/commands/init/steps/build-schema.ts
|
|
1261
1352
|
import { existsSync as existsSync2, mkdirSync, writeFileSync } from "node:fs";
|
|
1262
1353
|
import { dirname, resolve as resolve2 } from "node:path";
|
|
@@ -1658,6 +1749,7 @@ var PROVIDER_MAP = {
|
|
|
1658
1749
|
supabase: createSupabaseProvider
|
|
1659
1750
|
};
|
|
1660
1751
|
var STEPS = [
|
|
1752
|
+
authenticateStep,
|
|
1661
1753
|
selectConnectionStep,
|
|
1662
1754
|
buildSchemaStep,
|
|
1663
1755
|
installForgeStep,
|
|
@@ -1768,8 +1860,18 @@ var matchIndexOptsSchema = z2.object({
|
|
|
1768
1860
|
m: z2.number().default(2048).optional(),
|
|
1769
1861
|
include_original: z2.boolean().default(false).optional()
|
|
1770
1862
|
});
|
|
1863
|
+
var arrayIndexModeSchema = z2.union([
|
|
1864
|
+
z2.literal("all"),
|
|
1865
|
+
z2.literal("none"),
|
|
1866
|
+
z2.object({
|
|
1867
|
+
item: z2.boolean().optional(),
|
|
1868
|
+
wildcard: z2.boolean().optional(),
|
|
1869
|
+
position: z2.boolean().optional()
|
|
1870
|
+
})
|
|
1871
|
+
]);
|
|
1771
1872
|
var steVecIndexOptsSchema = z2.object({
|
|
1772
|
-
prefix: z2.string()
|
|
1873
|
+
prefix: z2.string(),
|
|
1874
|
+
array_index_mode: arrayIndexModeSchema.optional()
|
|
1773
1875
|
});
|
|
1774
1876
|
var indexesSchema = z2.object({
|
|
1775
1877
|
ore: oreIndexOptsSchema.optional(),
|
|
@@ -1966,7 +2068,7 @@ var EncryptedColumn = class {
|
|
|
1966
2068
|
*/
|
|
1967
2069
|
searchableJson() {
|
|
1968
2070
|
this.castAsValue = "json";
|
|
1969
|
-
this.indexesValue.ste_vec = { prefix: "enabled" };
|
|
2071
|
+
this.indexesValue.ste_vec = { prefix: "enabled", array_index_mode: "all" };
|
|
1970
2072
|
return this;
|
|
1971
2073
|
}
|
|
1972
2074
|
build() {
|
|
@@ -2014,6 +2116,7 @@ var EncryptedTable = class {
|
|
|
2014
2116
|
indexes: {
|
|
2015
2117
|
...builtColumn.indexes,
|
|
2016
2118
|
ste_vec: {
|
|
2119
|
+
...builtColumn.indexes.ste_vec,
|
|
2017
2120
|
prefix: `${this.tableName}/${colName}`
|
|
2018
2121
|
}
|
|
2019
2122
|
}
|
|
@@ -2075,69 +2178,6 @@ var EncryptionErrorTypes = {
|
|
|
2075
2178
|
CtsTokenError: "CtsTokenError"
|
|
2076
2179
|
};
|
|
2077
2180
|
|
|
2078
|
-
// src/utils/config/index.ts
|
|
2079
|
-
import fs from "node:fs";
|
|
2080
|
-
import path from "node:path";
|
|
2081
|
-
function getWorkspaceCrn(tomlString) {
|
|
2082
|
-
let currentSection = "";
|
|
2083
|
-
let workspaceCrn;
|
|
2084
|
-
const lines = tomlString.split(/\r?\n/);
|
|
2085
|
-
for (const line of lines) {
|
|
2086
|
-
const trimmedLine = line.trim();
|
|
2087
|
-
if (!trimmedLine || trimmedLine.startsWith("#")) {
|
|
2088
|
-
continue;
|
|
2089
|
-
}
|
|
2090
|
-
const sectionMatch = trimmedLine.match(/^\[([^\]]+)\]$/);
|
|
2091
|
-
if (sectionMatch) {
|
|
2092
|
-
currentSection = sectionMatch[1];
|
|
2093
|
-
continue;
|
|
2094
|
-
}
|
|
2095
|
-
const kvMatch = trimmedLine.match(/^(\w+)\s*=\s*"([^"]+)"$/);
|
|
2096
|
-
if (kvMatch) {
|
|
2097
|
-
const [_3, key, value] = kvMatch;
|
|
2098
|
-
if (currentSection === "auth" && key === "workspace_crn") {
|
|
2099
|
-
workspaceCrn = value;
|
|
2100
|
-
break;
|
|
2101
|
-
}
|
|
2102
|
-
}
|
|
2103
|
-
}
|
|
2104
|
-
return workspaceCrn;
|
|
2105
|
-
}
|
|
2106
|
-
function extractWorkspaceIdFromCrn(crn) {
|
|
2107
|
-
const match = crn.match(/crn:[^:]+:([^:]+)$/);
|
|
2108
|
-
if (!match) {
|
|
2109
|
-
throw new Error("Invalid CRN format");
|
|
2110
|
-
}
|
|
2111
|
-
return match[1];
|
|
2112
|
-
}
|
|
2113
|
-
function loadWorkSpaceId(suppliedCrn) {
|
|
2114
|
-
const configPath = path.join(process.cwd(), "cipherstash.toml");
|
|
2115
|
-
if (suppliedCrn) {
|
|
2116
|
-
return extractWorkspaceIdFromCrn(suppliedCrn);
|
|
2117
|
-
}
|
|
2118
|
-
if (!fs.existsSync(configPath) && !process.env.CS_WORKSPACE_CRN) {
|
|
2119
|
-
throw new Error(
|
|
2120
|
-
"You have not defined a workspace CRN in your config file, or the CS_WORKSPACE_CRN environment variable."
|
|
2121
|
-
);
|
|
2122
|
-
}
|
|
2123
|
-
if (process.env.CS_WORKSPACE_CRN) {
|
|
2124
|
-
return extractWorkspaceIdFromCrn(process.env.CS_WORKSPACE_CRN);
|
|
2125
|
-
}
|
|
2126
|
-
if (!fs.existsSync(configPath)) {
|
|
2127
|
-
throw new Error(
|
|
2128
|
-
"You have not defined a workspace CRN in your config file, or the CS_WORKSPACE_CRN environment variable."
|
|
2129
|
-
);
|
|
2130
|
-
}
|
|
2131
|
-
const tomlString = fs.readFileSync(configPath, "utf8");
|
|
2132
|
-
const workspaceCrn = getWorkspaceCrn(tomlString);
|
|
2133
|
-
if (!workspaceCrn) {
|
|
2134
|
-
throw new Error(
|
|
2135
|
-
"You have not defined a workspace CRN in your config file, or the CS_WORKSPACE_CRN environment variable."
|
|
2136
|
-
);
|
|
2137
|
-
}
|
|
2138
|
-
return extractWorkspaceIdFromCrn(workspaceCrn);
|
|
2139
|
-
}
|
|
2140
|
-
|
|
2141
2181
|
// src/utils/logger/index.ts
|
|
2142
2182
|
import { createRequestLogger, initLogger } from "evlog";
|
|
2143
2183
|
var validLevels = ["debug", "info", "error"];
|
|
@@ -2631,11 +2671,11 @@ import {
|
|
|
2631
2671
|
decryptBulk,
|
|
2632
2672
|
encryptBulk
|
|
2633
2673
|
} from "@cipherstash/protect-ffi";
|
|
2634
|
-
function setNestedValue(obj,
|
|
2674
|
+
function setNestedValue(obj, path, value) {
|
|
2635
2675
|
const FORBIDDEN_KEYS = ["__proto__", "prototype", "constructor"];
|
|
2636
2676
|
let current = obj;
|
|
2637
|
-
for (let i = 0; i <
|
|
2638
|
-
const part =
|
|
2677
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
2678
|
+
const part = path[i];
|
|
2639
2679
|
if (FORBIDDEN_KEYS.includes(part)) {
|
|
2640
2680
|
throw new Error(`[encryption]: Forbidden key "${part}" in field path`);
|
|
2641
2681
|
}
|
|
@@ -2644,7 +2684,7 @@ function setNestedValue(obj, path2, value) {
|
|
|
2644
2684
|
}
|
|
2645
2685
|
current = current[part];
|
|
2646
2686
|
}
|
|
2647
|
-
const lastKey =
|
|
2687
|
+
const lastKey = path[path.length - 1];
|
|
2648
2688
|
if (FORBIDDEN_KEYS.includes(lastKey)) {
|
|
2649
2689
|
throw new Error(`[encryption]: Forbidden key "${lastKey}" in field path`);
|
|
2650
2690
|
}
|
|
@@ -2721,7 +2761,7 @@ function prepareFieldsForEncryption(model, table) {
|
|
|
2721
2761
|
continue;
|
|
2722
2762
|
}
|
|
2723
2763
|
if (typeof value === "object" && !isEncryptedPayload(value) && !columnPaths2.includes(fullKey)) {
|
|
2724
|
-
if (columnPaths2.some((
|
|
2764
|
+
if (columnPaths2.some((path) => path.startsWith(fullKey))) {
|
|
2725
2765
|
processNestedFields(
|
|
2726
2766
|
value,
|
|
2727
2767
|
fullKey,
|
|
@@ -2899,7 +2939,7 @@ function prepareBulkModelsForOperation(models, table) {
|
|
|
2899
2939
|
continue;
|
|
2900
2940
|
}
|
|
2901
2941
|
if (typeof value === "object" && !isEncryptedPayload(value) && !columnPaths.includes(fullKey)) {
|
|
2902
|
-
if (columnPaths.some((
|
|
2942
|
+
if (columnPaths.some((path) => path.startsWith(fullKey))) {
|
|
2903
2943
|
processNestedFields(
|
|
2904
2944
|
value,
|
|
2905
2945
|
fullKey,
|
|
@@ -4074,11 +4114,6 @@ var noClientError = () => new Error(
|
|
|
4074
4114
|
var EncryptionClient = class {
|
|
4075
4115
|
client;
|
|
4076
4116
|
encryptConfig;
|
|
4077
|
-
workspaceId;
|
|
4078
|
-
constructor(workspaceCrn) {
|
|
4079
|
-
const workspaceId = loadWorkSpaceId(workspaceCrn);
|
|
4080
|
-
this.workspaceId = workspaceId;
|
|
4081
|
-
}
|
|
4082
4117
|
/**
|
|
4083
4118
|
* Initializes the EncryptionClient with the provided configuration.
|
|
4084
4119
|
* @internal
|
|
@@ -4100,10 +4135,10 @@ var EncryptionClient = class {
|
|
|
4100
4135
|
this.client = await newClient({
|
|
4101
4136
|
encryptConfig: validated,
|
|
4102
4137
|
clientOpts: {
|
|
4103
|
-
workspaceCrn: config3.workspaceCrn
|
|
4104
|
-
accessKey: config3.accessKey
|
|
4105
|
-
clientId: config3.clientId
|
|
4106
|
-
clientKey: config3.clientKey
|
|
4138
|
+
workspaceCrn: config3.workspaceCrn,
|
|
4139
|
+
accessKey: config3.accessKey,
|
|
4140
|
+
clientId: config3.clientId,
|
|
4141
|
+
clientKey: config3.clientKey,
|
|
4107
4142
|
keyset: toFfiKeysetIdentifier(config3.keyset)
|
|
4108
4143
|
}
|
|
4109
4144
|
});
|
|
@@ -4488,12 +4523,6 @@ var EncryptionClient = class {
|
|
|
4488
4523
|
bulkDecrypt(encryptedPayloads) {
|
|
4489
4524
|
return new BulkDecryptOperation(this.client, encryptedPayloads);
|
|
4490
4525
|
}
|
|
4491
|
-
/** e.g., debugging or environment info */
|
|
4492
|
-
clientInfo() {
|
|
4493
|
-
return {
|
|
4494
|
-
workspaceId: this.workspaceId
|
|
4495
|
-
};
|
|
4496
|
-
}
|
|
4497
4526
|
/**
|
|
4498
4527
|
* Get the encrypt config object.
|
|
4499
4528
|
*
|
|
@@ -4515,7 +4544,7 @@ var Encryption = async (config3) => {
|
|
|
4515
4544
|
"[encryption]: Invalid UUID provided for keyset id. Must be a valid UUID."
|
|
4516
4545
|
);
|
|
4517
4546
|
}
|
|
4518
|
-
const client = new EncryptionClient(
|
|
4547
|
+
const client = new EncryptionClient();
|
|
4519
4548
|
const encryptConfig = buildEncryptConfig(...schemas);
|
|
4520
4549
|
const result = await client.init({
|
|
4521
4550
|
encryptConfig,
|
|
@@ -4527,6 +4556,15 @@ var Encryption = async (config3) => {
|
|
|
4527
4556
|
return result.data;
|
|
4528
4557
|
};
|
|
4529
4558
|
|
|
4559
|
+
// src/utils/config/index.ts
|
|
4560
|
+
function extractWorkspaceIdFromCrn(crn) {
|
|
4561
|
+
const match = crn.match(/crn:[^:]+:([^:]+)$/);
|
|
4562
|
+
if (!match) {
|
|
4563
|
+
throw new Error("Invalid CRN format");
|
|
4564
|
+
}
|
|
4565
|
+
return match[1];
|
|
4566
|
+
}
|
|
4567
|
+
|
|
4530
4568
|
// src/secrets/index.ts
|
|
4531
4569
|
var Secrets = class {
|
|
4532
4570
|
encryptionClient = null;
|
|
@@ -4589,14 +4627,14 @@ var Secrets = class {
|
|
|
4589
4627
|
* For GET requests, `params` are appended as URL query parameters.
|
|
4590
4628
|
* For POST requests, `body` is sent as JSON in the request body.
|
|
4591
4629
|
*/
|
|
4592
|
-
async apiRequest(method,
|
|
4630
|
+
async apiRequest(method, path, options) {
|
|
4593
4631
|
try {
|
|
4594
|
-
let url = `${this.apiBaseUrl}${
|
|
4632
|
+
let url = `${this.apiBaseUrl}${path}`;
|
|
4595
4633
|
if (options?.params) {
|
|
4596
4634
|
const searchParams = new URLSearchParams(options.params);
|
|
4597
4635
|
url = `${url}?${searchParams.toString()}`;
|
|
4598
4636
|
}
|
|
4599
|
-
logger.debug(`Secrets API request: ${method} ${
|
|
4637
|
+
logger.debug(`Secrets API request: ${method} ${path}`);
|
|
4600
4638
|
const headers = {
|
|
4601
4639
|
"Content-Type": "application/json",
|
|
4602
4640
|
Authorization: this.getAuthHeader()
|
|
@@ -4615,7 +4653,7 @@ var Secrets = class {
|
|
|
4615
4653
|
} catch {
|
|
4616
4654
|
errorMessage = errorText || errorMessage;
|
|
4617
4655
|
}
|
|
4618
|
-
logger.error(`Secrets API error on ${method} ${
|
|
4656
|
+
logger.error(`Secrets API error on ${method} ${path}: ${errorMessage}`);
|
|
4619
4657
|
return {
|
|
4620
4658
|
failure: {
|
|
4621
4659
|
type: "ApiError",
|
|
@@ -4623,12 +4661,12 @@ var Secrets = class {
|
|
|
4623
4661
|
}
|
|
4624
4662
|
};
|
|
4625
4663
|
}
|
|
4626
|
-
logger.debug(`Secrets API request successful: ${method} ${
|
|
4664
|
+
logger.debug(`Secrets API request successful: ${method} ${path}`);
|
|
4627
4665
|
const data = await response.json();
|
|
4628
4666
|
return { data };
|
|
4629
4667
|
} catch (error) {
|
|
4630
4668
|
const message = error instanceof Error ? error.message : "Unknown network error occurred";
|
|
4631
|
-
logger.error(`Secrets network error on ${method} ${
|
|
4669
|
+
logger.error(`Secrets network error on ${method} ${path}: ${message}`);
|
|
4632
4670
|
return {
|
|
4633
4671
|
failure: {
|
|
4634
4672
|
type: "NetworkError",
|
|
@@ -5104,7 +5142,7 @@ function requireFlag(flags, name) {
|
|
|
5104
5142
|
}
|
|
5105
5143
|
return val;
|
|
5106
5144
|
}
|
|
5107
|
-
var
|
|
5145
|
+
var HELP2 = `
|
|
5108
5146
|
${style.title("Usage:")} stash secrets <command> [options]
|
|
5109
5147
|
|
|
5110
5148
|
${style.title("Commands:")}
|
|
@@ -5131,7 +5169,7 @@ async function secretsCommand(args) {
|
|
|
5131
5169
|
const subcommand = args[0];
|
|
5132
5170
|
const rest = args.slice(1);
|
|
5133
5171
|
if (!subcommand || subcommand === "--help" || subcommand === "-h") {
|
|
5134
|
-
console.log(
|
|
5172
|
+
console.log(HELP2);
|
|
5135
5173
|
return;
|
|
5136
5174
|
}
|
|
5137
5175
|
const flags = parseFlags(rest);
|
|
@@ -5181,7 +5219,7 @@ async function secretsCommand(args) {
|
|
|
5181
5219
|
}
|
|
5182
5220
|
default:
|
|
5183
5221
|
console.error(style.error(`Unknown secrets command: ${subcommand}`));
|
|
5184
|
-
console.log(
|
|
5222
|
+
console.log(HELP2);
|
|
5185
5223
|
process.exit(1);
|
|
5186
5224
|
}
|
|
5187
5225
|
}
|
|
@@ -5192,13 +5230,14 @@ var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
|
5192
5230
|
var pkg = JSON.parse(
|
|
5193
5231
|
readFileSync(join(__dirname, "../../package.json"), "utf-8")
|
|
5194
5232
|
);
|
|
5195
|
-
var
|
|
5233
|
+
var HELP3 = `
|
|
5196
5234
|
CipherStash Stack CLI v${pkg.version}
|
|
5197
5235
|
|
|
5198
5236
|
Usage: stash <command> [options]
|
|
5199
5237
|
|
|
5200
5238
|
Commands:
|
|
5201
5239
|
init Initialize CipherStash for your project
|
|
5240
|
+
auth Authenticate with CipherStash
|
|
5202
5241
|
secrets Manage encrypted secrets
|
|
5203
5242
|
|
|
5204
5243
|
Options:
|
|
@@ -5211,6 +5250,7 @@ Init Flags:
|
|
|
5211
5250
|
Examples:
|
|
5212
5251
|
stash init
|
|
5213
5252
|
stash init --supabase
|
|
5253
|
+
stash auth login
|
|
5214
5254
|
stash secrets set -n DATABASE_URL -V "postgres://..." -e production
|
|
5215
5255
|
stash secrets get -n DATABASE_URL -e production
|
|
5216
5256
|
stash secrets get-many -n DATABASE_URL,API_KEY -e production
|
|
@@ -5235,7 +5275,7 @@ function parseArgs(argv) {
|
|
|
5235
5275
|
async function main() {
|
|
5236
5276
|
const { command, rest, booleanFlags } = parseArgs(process.argv);
|
|
5237
5277
|
if (!command || command === "--help" || command === "-h") {
|
|
5238
|
-
console.log(
|
|
5278
|
+
console.log(HELP3);
|
|
5239
5279
|
return;
|
|
5240
5280
|
}
|
|
5241
5281
|
if (command === "--version" || command === "-v") {
|
|
@@ -5246,13 +5286,16 @@ async function main() {
|
|
|
5246
5286
|
case "init":
|
|
5247
5287
|
await initCommand(booleanFlags);
|
|
5248
5288
|
break;
|
|
5289
|
+
case "auth":
|
|
5290
|
+
await authCommand(rest);
|
|
5291
|
+
break;
|
|
5249
5292
|
case "secrets":
|
|
5250
5293
|
await secretsCommand(rest);
|
|
5251
5294
|
break;
|
|
5252
5295
|
default:
|
|
5253
5296
|
console.error(`Unknown command: ${command}
|
|
5254
5297
|
`);
|
|
5255
|
-
console.log(
|
|
5298
|
+
console.log(HELP3);
|
|
5256
5299
|
process.exit(1);
|
|
5257
5300
|
}
|
|
5258
5301
|
}
|