@cipherstash/stack 0.14.0 → 0.15.1

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/dist/index.cjs CHANGED
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,21 +15,12 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
 
30
20
  // src/index.ts
31
21
  var index_exports = {};
32
22
  __export(index_exports, {
33
23
  Encryption: () => Encryption,
34
- Secrets: () => Secrets,
35
24
  encryptedColumn: () => encryptedColumn,
36
25
  encryptedField: () => encryptedField,
37
26
  encryptedTable: () => encryptedTable,
@@ -920,11 +909,11 @@ var import_result3 = require("@byteslice/result");
920
909
 
921
910
  // src/encryption/helpers/model-helpers.ts
922
911
  var import_protect_ffi4 = require("@cipherstash/protect-ffi");
923
- function setNestedValue(obj, path2, value) {
912
+ function setNestedValue(obj, path, value) {
924
913
  const FORBIDDEN_KEYS = ["__proto__", "prototype", "constructor"];
925
914
  let current = obj;
926
- for (let i = 0; i < path2.length - 1; i++) {
927
- const part = path2[i];
915
+ for (let i = 0; i < path.length - 1; i++) {
916
+ const part = path[i];
928
917
  if (FORBIDDEN_KEYS.includes(part)) {
929
918
  throw new Error(`[encryption]: Forbidden key "${part}" in field path`);
930
919
  }
@@ -933,7 +922,7 @@ function setNestedValue(obj, path2, value) {
933
922
  }
934
923
  current = current[part];
935
924
  }
936
- const lastKey = path2[path2.length - 1];
925
+ const lastKey = path[path.length - 1];
937
926
  if (FORBIDDEN_KEYS.includes(lastKey)) {
938
927
  throw new Error(`[encryption]: Forbidden key "${lastKey}" in field path`);
939
928
  }
@@ -1010,7 +999,7 @@ function prepareFieldsForEncryption(model, table) {
1010
999
  continue;
1011
1000
  }
1012
1001
  if (typeof value === "object" && !isEncryptedPayload(value) && !columnPaths2.includes(fullKey)) {
1013
- if (columnPaths2.some((path2) => path2.startsWith(fullKey))) {
1002
+ if (columnPaths2.some((path) => path.startsWith(fullKey))) {
1014
1003
  processNestedFields(
1015
1004
  value,
1016
1005
  fullKey,
@@ -1188,7 +1177,7 @@ function prepareBulkModelsForOperation(models, table) {
1188
1177
  continue;
1189
1178
  }
1190
1179
  if (typeof value === "object" && !isEncryptedPayload(value) && !columnPaths.includes(fullKey)) {
1191
- if (columnPaths.some((path2) => path2.startsWith(fullKey))) {
1180
+ if (columnPaths.some((path) => path.startsWith(fullKey))) {
1192
1181
  processNestedFields(
1193
1182
  value,
1194
1183
  fullKey,
@@ -2798,357 +2787,9 @@ var Encryption = async (config) => {
2798
2787
  }
2799
2788
  return result.data;
2800
2789
  };
2801
-
2802
- // src/utils/config/index.ts
2803
- var import_node_fs = __toESM(require("fs"), 1);
2804
- var import_node_path = __toESM(require("path"), 1);
2805
- function extractWorkspaceIdFromCrn(crn) {
2806
- const match = crn.match(/crn:[^:]+:([^:]+)$/);
2807
- if (!match) {
2808
- throw new Error("Invalid CRN format");
2809
- }
2810
- return match[1];
2811
- }
2812
-
2813
- // src/secrets/index.ts
2814
- var Secrets = class {
2815
- encryptionClient = null;
2816
- config;
2817
- apiBaseUrl = process.env.STASH_API_URL || "https://dashboard.cipherstash.com/api/secrets";
2818
- secretsSchema = encryptedTable("secrets", {
2819
- value: encryptedColumn("value")
2820
- });
2821
- constructor(config) {
2822
- const workspaceCRN = config.workspaceCRN ?? process.env.CS_WORKSPACE_CRN;
2823
- const clientId = config.clientId ?? process.env.CS_CLIENT_ID;
2824
- const clientKey = config.clientKey ?? process.env.CS_CLIENT_KEY;
2825
- const accessKey = config.accessKey ?? process.env.CS_CLIENT_ACCESS_KEY;
2826
- if (!workspaceCRN || !clientId || !clientKey || !accessKey) {
2827
- throw new Error(
2828
- "Missing required configuration or environment variables."
2829
- );
2830
- }
2831
- this.config = {
2832
- environment: config.environment,
2833
- workspaceCRN,
2834
- clientId,
2835
- clientKey,
2836
- accessKey
2837
- };
2838
- }
2839
- initPromise = null;
2840
- /**
2841
- * Initialize the Secrets client and underlying Encryption client
2842
- */
2843
- async ensureInitialized() {
2844
- if (!this.initPromise) {
2845
- this.initPromise = this._doInit();
2846
- }
2847
- return this.initPromise;
2848
- }
2849
- async _doInit() {
2850
- logger.debug("Initializing the Secrets client.");
2851
- this.encryptionClient = await Encryption({
2852
- schemas: [this.secretsSchema],
2853
- config: {
2854
- workspaceCrn: this.config.workspaceCRN,
2855
- clientId: this.config.clientId,
2856
- clientKey: this.config.clientKey,
2857
- accessKey: this.config.accessKey,
2858
- keyset: { name: this.config.environment }
2859
- }
2860
- });
2861
- logger.debug("Successfully initialized the Secrets client.");
2862
- }
2863
- /**
2864
- * Get the authorization header for API requests
2865
- */
2866
- getAuthHeader() {
2867
- return `Bearer ${this.config.accessKey}`;
2868
- }
2869
- /**
2870
- * Make an API request with error handling.
2871
- *
2872
- * For GET requests, `params` are appended as URL query parameters.
2873
- * For POST requests, `body` is sent as JSON in the request body.
2874
- */
2875
- async apiRequest(method, path2, options) {
2876
- try {
2877
- let url = `${this.apiBaseUrl}${path2}`;
2878
- if (options?.params) {
2879
- const searchParams = new URLSearchParams(options.params);
2880
- url = `${url}?${searchParams.toString()}`;
2881
- }
2882
- logger.debug(`Secrets API request: ${method} ${path2}`);
2883
- const headers = {
2884
- "Content-Type": "application/json",
2885
- Authorization: this.getAuthHeader()
2886
- };
2887
- const response = await fetch(url, {
2888
- method,
2889
- headers,
2890
- body: options?.body ? JSON.stringify(options.body) : void 0
2891
- });
2892
- if (!response.ok) {
2893
- const errorText = await response.text();
2894
- let errorMessage = `API request failed with status ${response.status}`;
2895
- try {
2896
- const errorJson = JSON.parse(errorText);
2897
- errorMessage = errorJson.message || errorJson.error || errorMessage;
2898
- } catch {
2899
- errorMessage = errorText || errorMessage;
2900
- }
2901
- logger.error(`Secrets API error on ${method} ${path2}: ${errorMessage}`);
2902
- return {
2903
- failure: {
2904
- type: "ApiError",
2905
- message: errorMessage
2906
- }
2907
- };
2908
- }
2909
- logger.debug(`Secrets API request successful: ${method} ${path2}`);
2910
- const data = await response.json();
2911
- return { data };
2912
- } catch (error) {
2913
- const message = error instanceof Error ? error.message : "Unknown network error occurred";
2914
- logger.error(`Secrets network error on ${method} ${path2}: ${message}`);
2915
- return {
2916
- failure: {
2917
- type: "NetworkError",
2918
- message
2919
- }
2920
- };
2921
- }
2922
- }
2923
- /**
2924
- * Store an encrypted secret in the vault.
2925
- * The value is encrypted locally before being sent to the API.
2926
- *
2927
- * API: POST /api/secrets/set
2928
- *
2929
- * @param name - The name of the secret
2930
- * @param value - The plaintext value to encrypt and store
2931
- * @returns A Result containing the API response or an error
2932
- */
2933
- async set(name, value) {
2934
- logger.debug("Setting secret");
2935
- await this.ensureInitialized();
2936
- if (!this.encryptionClient) {
2937
- return {
2938
- failure: {
2939
- type: "ClientError",
2940
- message: "Failed to initialize Encryption client"
2941
- }
2942
- };
2943
- }
2944
- const encryptResult = await this.encryptionClient.encrypt(value, {
2945
- column: this.secretsSchema.value,
2946
- table: this.secretsSchema
2947
- });
2948
- if (encryptResult.failure) {
2949
- logger.error("Failed to encrypt secret");
2950
- return {
2951
- failure: {
2952
- type: "EncryptionError",
2953
- message: encryptResult.failure.message
2954
- }
2955
- };
2956
- }
2957
- const workspaceId = extractWorkspaceIdFromCrn(this.config.workspaceCRN);
2958
- return await this.apiRequest("POST", "/set", {
2959
- body: {
2960
- workspaceId,
2961
- environment: this.config.environment,
2962
- name,
2963
- encryptedValue: encryptedToPgComposite(encryptResult.data)
2964
- }
2965
- });
2966
- }
2967
- /**
2968
- * Retrieve and decrypt a secret from the vault.
2969
- * The secret is decrypted locally after retrieval.
2970
- *
2971
- * API: GET /api/secrets/get?workspaceId=...&environment=...&name=...
2972
- *
2973
- * @param name - The name of the secret to retrieve
2974
- * @returns A Result containing the decrypted value or an error
2975
- */
2976
- async get(name) {
2977
- logger.debug("Getting secret");
2978
- await this.ensureInitialized();
2979
- if (!this.encryptionClient) {
2980
- return {
2981
- failure: {
2982
- type: "ClientError",
2983
- message: "Failed to initialize Encryption client"
2984
- }
2985
- };
2986
- }
2987
- const workspaceId = extractWorkspaceIdFromCrn(this.config.workspaceCRN);
2988
- const apiResult = await this.apiRequest("GET", "/get", {
2989
- params: {
2990
- workspaceId,
2991
- environment: this.config.environment,
2992
- name
2993
- }
2994
- });
2995
- if (apiResult.failure) {
2996
- return apiResult;
2997
- }
2998
- const decryptResult = await this.encryptionClient.decrypt(
2999
- apiResult.data.encryptedValue.data
3000
- );
3001
- if (decryptResult.failure) {
3002
- logger.error("Failed to decrypt secret");
3003
- return {
3004
- failure: {
3005
- type: "DecryptionError",
3006
- message: decryptResult.failure.message
3007
- }
3008
- };
3009
- }
3010
- if (typeof decryptResult.data !== "string") {
3011
- logger.error("Decrypted secret value is not a string");
3012
- return {
3013
- failure: {
3014
- type: "DecryptionError",
3015
- message: "Decrypted value is not a string"
3016
- }
3017
- };
3018
- }
3019
- return { data: decryptResult.data };
3020
- }
3021
- /**
3022
- * Retrieve and decrypt many secrets from the vault.
3023
- * The secrets are decrypted locally after retrieval.
3024
- * This method only triggers a single network request to the ZeroKMS.
3025
- *
3026
- * API: GET /api/secrets/get-many?workspaceId=...&environment=...&names=name1,name2,...
3027
- *
3028
- * Constraints:
3029
- * - Minimum 2 secret names required
3030
- * - Maximum 100 secret names per request
3031
- *
3032
- * @param names - The names of the secrets to retrieve (min 2, max 100)
3033
- * @returns A Result containing an object mapping secret names to their decrypted values
3034
- */
3035
- async getMany(names) {
3036
- logger.debug(`Getting ${names.length} secrets.`);
3037
- await this.ensureInitialized();
3038
- if (!this.encryptionClient) {
3039
- return {
3040
- failure: {
3041
- type: "ClientError",
3042
- message: "Failed to initialize Encryption client"
3043
- }
3044
- };
3045
- }
3046
- if (names.length < 2) {
3047
- return {
3048
- failure: {
3049
- type: "ClientError",
3050
- message: "At least 2 secret names are required for getMany"
3051
- }
3052
- };
3053
- }
3054
- if (names.length > 100) {
3055
- return {
3056
- failure: {
3057
- type: "ClientError",
3058
- message: "Maximum 100 secret names per request"
3059
- }
3060
- };
3061
- }
3062
- const workspaceId = extractWorkspaceIdFromCrn(this.config.workspaceCRN);
3063
- const apiResult = await this.apiRequest(
3064
- "GET",
3065
- "/get-many",
3066
- {
3067
- params: {
3068
- workspaceId,
3069
- environment: this.config.environment,
3070
- names: names.join(",")
3071
- }
3072
- }
3073
- );
3074
- if (apiResult.failure) {
3075
- return apiResult;
3076
- }
3077
- const dataToDecrypt = apiResult.data.map((item) => ({
3078
- name: item.name,
3079
- value: item.encryptedValue.data
3080
- }));
3081
- const decryptResult = await this.encryptionClient.bulkDecryptModels(dataToDecrypt);
3082
- if (decryptResult.failure) {
3083
- logger.error(
3084
- `Failed to decrypt secrets: ${decryptResult.failure.message}`
3085
- );
3086
- return {
3087
- failure: {
3088
- type: "DecryptionError",
3089
- message: decryptResult.failure.message
3090
- }
3091
- };
3092
- }
3093
- const decryptedSecrets = decryptResult.data;
3094
- const secretsMap = {};
3095
- for (const secret of decryptedSecrets) {
3096
- if (secret.name && secret.value) {
3097
- secretsMap[secret.name] = secret.value;
3098
- }
3099
- }
3100
- return { data: secretsMap };
3101
- }
3102
- /**
3103
- * List all secrets in the environment.
3104
- * Only names and metadata are returned; values remain encrypted.
3105
- *
3106
- * API: GET /api/secrets/list?workspaceId=...&environment=...
3107
- *
3108
- * @returns A Result containing the list of secrets or an error
3109
- */
3110
- async list() {
3111
- logger.debug("Listing secrets.");
3112
- const workspaceId = extractWorkspaceIdFromCrn(this.config.workspaceCRN);
3113
- const apiResult = await this.apiRequest(
3114
- "GET",
3115
- "/list",
3116
- {
3117
- params: {
3118
- workspaceId,
3119
- environment: this.config.environment
3120
- }
3121
- }
3122
- );
3123
- if (apiResult.failure) {
3124
- return apiResult;
3125
- }
3126
- return { data: apiResult.data.secrets };
3127
- }
3128
- /**
3129
- * Delete a secret from the vault.
3130
- *
3131
- * API: POST /api/secrets/delete
3132
- *
3133
- * @param name - The name of the secret to delete
3134
- * @returns A Result containing the API response or an error
3135
- */
3136
- async delete(name) {
3137
- logger.debug("Deleting secret");
3138
- const workspaceId = extractWorkspaceIdFromCrn(this.config.workspaceCRN);
3139
- return await this.apiRequest("POST", "/delete", {
3140
- body: {
3141
- workspaceId,
3142
- environment: this.config.environment,
3143
- name
3144
- }
3145
- });
3146
- }
3147
- };
3148
2790
  // Annotate the CommonJS export names for ESM import in node:
3149
2791
  0 && (module.exports = {
3150
2792
  Encryption,
3151
- Secrets,
3152
2793
  encryptedColumn,
3153
2794
  encryptedField,
3154
2795
  encryptedTable,