diff --git a/.gitignore b/.gitignore index b1a77be..c841360 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,4 @@ .eslintcache .DS_Store -src/run.ts \ No newline at end of file +run.ts \ No newline at end of file diff --git a/README.md b/README.md index 4aa2398..75891db 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,10 @@ Example to decrypt an encrypted file with a GCP-KMS keyring: import { readFile } from "node:fs/promises"; import { GoogleKmsKeyDecryptor } from "@figedi/sops/kms"; import { SopsClient } from "@figedi/sops"; +import { KeyManagementServiceClient } from '@google-cloud/kms'; const run = async () => { - - const decryptor = await GoogleKmsKeyDecryptor.create('', ''); + const decryptor = new GoogleKmsKeyDecryptor(new KeyManagementServiceClient({ projectId: '', keyFilename: '' })); const sopsClient = new SopsClient(decryptor); const testFile = await readFile('', { encoding: 'utf-8'}) diff --git a/package-lock.json b/package-lock.json index 7920248..63719d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,7 @@ "version": "1.1.2", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "@types/lodash": "^4.17.10", - "lodash": "^4.17.21", + "es-toolkit": "^1.25.2", "yaml": "^2.6.0" }, "devDependencies": { @@ -1091,11 +1090,6 @@ "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "dev": true }, - "node_modules/@types/lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-YpS0zzoduEhuOWjAotS6A5AVCva7X4lVlYLF0FYHAY9sdraBfnatttHItlWeZdGhuEkf+OzMNg2ZYAx8t+52uQ==" - }, "node_modules/@types/long": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", @@ -1592,6 +1586,11 @@ "once": "^1.4.0" } }, + "node_modules/es-toolkit": { + "version": "1.25.2", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.25.2.tgz", + "integrity": "sha512-zEh2aJUwnlDwashas6JN+oFVN08F2s2qBaEwTo6EOACjO9PdPH4eGRBZC2JP/3SDLeANiMTEtVnOGhoG7GwZcA==" + }, "node_modules/esbuild": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", @@ -2233,11 +2232,6 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", diff --git a/package.json b/package.json index c285068..6d76718 100644 --- a/package.json +++ b/package.json @@ -35,8 +35,7 @@ "vitest": "^2.1.3" }, "dependencies": { - "@types/lodash": "^4.17.10", - "lodash": "^4.17.21", + "es-toolkit": "^1.25.2", "yaml": "^2.6.0" }, "optionalDependencies": { diff --git a/src/decryptors/google-kms/google-kms-key-decryptor.spec.ts b/src/decryptors/google-kms/google-kms-key-decryptor.spec.ts index 996e398..b2b583a 100644 --- a/src/decryptors/google-kms/google-kms-key-decryptor.spec.ts +++ b/src/decryptors/google-kms/google-kms-key-decryptor.spec.ts @@ -1,13 +1,13 @@ import type { KeyManagementServiceClient } from '@google-cloud/kms'; import { beforeAll, describe, expect, it } from 'vitest'; -import { CheckSumMismatchError } from '../../errors'; -import { uncoverPaths } from '../../helpers'; -import { getEncryptedSecret, getEncryptedSecretWrongMac, getUnencryptedSecret } from '../../shared.specFiles/fixtures'; -import { encryptJson } from '../../shared.specFiles/sops.spec-utils'; -import { SopsClient } from '../../sops-client'; -import { IKeyDecryptor } from '../../types'; -import { GoogleKmsKeyDecryptor } from './google-kms-key-decryptor'; -import { setupStubbedKms } from './shared.specFiles/kms.stub'; +import { CheckSumMismatchError } from '../../errors.js'; +import { uncoverPaths } from '../../helpers.js'; +import { getEncryptedSecret, getEncryptedSecretWrongMac, getUnencryptedSecret } from '../../shared.specFiles/fixtures/secret.fixture.js'; +import { encryptJson } from '../../shared.specFiles/sops.spec-utils.js'; +import { SopsClient } from '../../sops-client.js'; +import type { IKeyDecryptor } from '../../types.js'; +import { GoogleKmsKeyDecryptor } from './google-kms-key-decryptor.js'; +import { setupStubbedKms } from './shared.specFiles/kms.stub.js'; describe('SopsClient with KmsKeyDecryptor', () => { describe('specs tooling', () => { @@ -16,7 +16,7 @@ describe('SopsClient with KmsKeyDecryptor', () => { theThing: '42', }; const { key, iv, kms } = setupStubbedKms('random-password'); - const sopsClient = new SopsClient(GoogleKmsKeyDecryptor.createWithKmsClient(kms)); + const sopsClient = new SopsClient(new GoogleKmsKeyDecryptor(kms)); const encryptedData = encryptJson(key, iv, data); const decryptedData = await sopsClient.decrypt(encryptedData); @@ -64,7 +64,7 @@ describe('SopsClient with KmsKeyDecryptor', () => { key = deps.key; iv = deps.iv; kms = deps.kms; - keyDecryptor = GoogleKmsKeyDecryptor.createWithKmsClient(kms); + keyDecryptor = new GoogleKmsKeyDecryptor(kms); sopsClient = new SopsClient(keyDecryptor); }); diff --git a/src/decryptors/google-kms/google-kms-key-decryptor.ts b/src/decryptors/google-kms/google-kms-key-decryptor.ts index 5a77ea1..f76b10d 100644 --- a/src/decryptors/google-kms/google-kms-key-decryptor.ts +++ b/src/decryptors/google-kms/google-kms-key-decryptor.ts @@ -1,32 +1,9 @@ -import { stat } from 'node:fs/promises'; import type { KeyManagementServiceClient } from '@google-cloud/kms'; -import { resolvePathOrObj } from '../../helpers'; -import { IKeyDecryptor, ISopsEncryptedJSON } from '../../types'; +import { resolvePathOrObj } from '../../helpers.js'; +import type { IKeyDecryptor, ISopsEncryptedJSON } from '../../types.js'; export class GoogleKmsKeyDecryptor implements IKeyDecryptor { - private constructor(private client: KeyManagementServiceClient) {} - - public static async create(projectId?: string, serviceAccountPath?: string) { - const mod = await import('@google-cloud/kms'); - // in k8s, there might a service-account mounted - if (serviceAccountPath) { - try { - stat(serviceAccountPath); - return new GoogleKmsKeyDecryptor(new mod.KeyManagementServiceClient({ projectId, keyFilename: serviceAccountPath })); - } catch (e) { - if (e.code !== 'ENOENT') { - throw e; - } - } - } - - // implicit authorization for non-k8s envs - return new GoogleKmsKeyDecryptor(new mod.KeyManagementServiceClient({ projectId })); - } - - public static createWithKmsClient(client: KeyManagementServiceClient): IKeyDecryptor { - return new GoogleKmsKeyDecryptor(client); - } + constructor(private client: KeyManagementServiceClient) {} public async canDecrypt(objOrPath: string | ISopsEncryptedJSON) { const { content } = await resolvePathOrObj(objOrPath); diff --git a/src/decryptors/google-kms/index.ts b/src/decryptors/google-kms/index.ts index 2cd9d6c..625321d 100644 --- a/src/decryptors/google-kms/index.ts +++ b/src/decryptors/google-kms/index.ts @@ -1,2 +1,2 @@ -export * from './shared.specFiles/kms.stub'; -export * from './google-kms-key-decryptor'; +export * from './shared.specFiles/kms.stub.js'; +export * from './google-kms-key-decryptor.js'; diff --git a/src/helpers.ts b/src/helpers.ts index 1456272..5d7506b 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -1,12 +1,12 @@ import { readFile, stat } from 'node:fs/promises'; import { isAbsolute } from 'node:path'; -import { chunk, isArray, isPlainObject } from 'lodash'; +import { chunk, isPlainObject } from 'es-toolkit'; import yaml from 'yaml'; -import { ISopsEncryptedJSON } from './types'; +import type { ISopsEncryptedJSON } from './types.js'; export const uncoverPaths = (tree: any): any[][] => { const doUncover = (t: any, walkedPaths: (string | number)[] = []): any[][] => { - if (isArray(t)) { + if (Array.isArray(t)) { return t.reduce((acc, v, k) => [...acc, ...doUncover(v, [...walkedPaths, k])], []); } if (isPlainObject(t)) { diff --git a/src/index.ts b/src/index.ts index a207a89..1c44e29 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ -export * from './sops-client'; -export * from './types'; -export * from './errors'; +export * from './sops-client.js'; +export * from './types.js'; +export * from './errors.js'; // @note test files are exported under different alias diff --git a/src/kms.ts b/src/kms.ts index 8509952..dc1e30b 100644 --- a/src/kms.ts +++ b/src/kms.ts @@ -1,2 +1,2 @@ // export-alias for named import @figedi/sops/kms (includes spec-files) -export * from './decryptors/google-kms'; +export * from './decryptors/google-kms/index.js'; diff --git a/src/shared.specFiles/fixtures/index.ts b/src/shared.specFiles/fixtures/index.ts index 82fe1be..091c48b 100644 --- a/src/shared.specFiles/fixtures/index.ts +++ b/src/shared.specFiles/fixtures/index.ts @@ -1 +1 @@ -export * from './secret.fixture'; +export * from './secret.fixture.js'; diff --git a/src/shared.specFiles/fixtures/secret.fixture.ts b/src/shared.specFiles/fixtures/secret.fixture.ts index 7b3d2d4..c253d1f 100644 --- a/src/shared.specFiles/fixtures/secret.fixture.ts +++ b/src/shared.specFiles/fixtures/secret.fixture.ts @@ -1,7 +1,13 @@ -import { ISopsEncryptedJSON } from '../../types'; -import { encryptJson } from '../sops.spec-utils'; -import baseJson = require('./secrets.json'); +import { readFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { ISopsEncryptedJSON } from '../../types.js'; +import { encryptJson } from '../sops.spec-utils.js'; +// @ts-expect-error target is not commonjs 🤷‍♂️ +const __dirname = import.meta.dirname ?? dirname(fileURLToPath(import.meta.url)); + +const baseJson = JSON.parse(readFileSync(join(__dirname, './secrets.json'), { encoding: 'utf-8' })); export const getUnencryptedSecret = (): Record => baseJson; export const getEncryptedSecret = (key: Buffer, iv: Buffer): ISopsEncryptedJSON => encryptJson(key, iv, baseJson); diff --git a/src/shared.specFiles/sops.spec-utils.ts b/src/shared.specFiles/sops.spec-utils.ts index 6e03a9f..4569712 100644 --- a/src/shared.specFiles/sops.spec-utils.ts +++ b/src/shared.specFiles/sops.spec-utils.ts @@ -1,7 +1,7 @@ import { createCipheriv, createHash } from 'node:crypto'; -import { set } from 'lodash'; -import { uncoverPaths } from '../helpers'; -import { ISopsEncryptedJSON } from '../types'; +import { set } from 'es-toolkit/compat'; +import { uncoverPaths } from '../helpers.js'; +import { ISopsEncryptedJSON } from '../types.js'; /** * Encrypts a given string-value (e.g. a stringified object) in a sops-like fashion: diff --git a/src/sops-client.ts b/src/sops-client.ts index ddaf348..1a8a4ef 100644 --- a/src/sops-client.ts +++ b/src/sops-client.ts @@ -1,8 +1,9 @@ import { createDecipheriv, createHash } from 'node:crypto'; -import { omit, set } from 'lodash'; -import { CheckSumMismatchError, SopsKeyNotSupportedError } from './errors'; -import { resolvePathOrObj, uncoverPaths } from './helpers'; -import { IKeyDecryptor, ISopsEncryptedJSON } from './types'; +import { omit } from 'es-toolkit'; +import { set } from 'es-toolkit/compat'; +import { CheckSumMismatchError, SopsKeyNotSupportedError } from './errors.js'; +import { resolvePathOrObj, uncoverPaths } from './helpers.js'; +import type { IKeyDecryptor, ISopsEncryptedJSON } from './types.js'; export class SopsClient { constructor(private keyDecryptor: IKeyDecryptor) {} @@ -51,7 +52,7 @@ export class SopsClient { }; private decryptSopsJsonWithKey = async >(key: Buffer, sopsJson: ISopsEncryptedJSON): Promise => { - const paths = uncoverPaths(omit(sopsJson, 'sops')); + const paths = uncoverPaths(omit(sopsJson, ['sops'])); const digest = createHash('sha512'); const decryptedJson = paths.reduce((acc, [nodePath, scalarValue]) => { diff --git a/src/test.ts b/src/test.ts index df2241b..50ce5c4 100644 --- a/src/test.ts +++ b/src/test.ts @@ -1,2 +1,2 @@ // export-alias for named import @figedi/sops/test -export * from './shared.specFiles/sops.spec-utils'; +export * from './shared.specFiles/sops.spec-utils.js'; diff --git a/tsconfig.json b/tsconfig.json index b4c8a88..1a135f2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,8 @@ { "compilerOptions": { - "module": "commonjs", + "module": "NodeNext", "target": "esnext", + "moduleResolution": "NodeNext", "rootDir": "./src", "sourceRoot": "./src", "outDir": "./dist", @@ -15,7 +16,6 @@ "noImplicitReturns": true, "forceConsistentCasingInFileNames": true, "esModuleInterop": true, - "resolveJsonModule": true, "skipLibCheck": true, "pretty": true, "typeRoots": [ diff --git a/tsup.config.ts b/tsup.config.ts index 8b7b95e..643f2f2 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -1,14 +1,13 @@ -import { defineConfig } from "tsup"; +import { defineConfig } from 'tsup'; export default defineConfig({ - entry: ["src/index.ts", "src/test.ts", "src/kms.ts"], + entry: ['src/index.ts', 'src/test.ts', 'src/kms.ts'], splitting: false, sourcemap: true, clean: true, dts: true, - outDir: "dist", - target: "es2022", - format: ["cjs", "esm"], - tsconfig: "./tsconfig.json", - onSuccess: "tsc --project tsconfig.json --emitDeclarationOnly --declaration", + outDir: 'dist', + target: 'es2022', + format: ['cjs', 'esm'], + tsconfig: './tsconfig.json', });