diff --git a/apps/sim/lib/api-key/crypto.ts b/apps/sim/lib/api-key/crypto.ts index 3cac7ee0f5c..aeee9097218 100644 --- a/apps/sim/lib/api-key/crypto.ts +++ b/apps/sim/lib/api-key/crypto.ts @@ -36,16 +36,17 @@ export async function encryptApiKey(apiKey: string): Promise<{ encrypted: string } const iv = randomBytes(16) - const cipher = createCipheriv('aes-256-gcm', key, iv) + const cipher = createCipheriv('aes-256-gcm', key, iv, { authTagLength: 16 }) let encrypted = cipher.update(apiKey, 'utf8', 'hex') encrypted += cipher.final('hex') const authTag = cipher.getAuthTag() + const ivHex = iv.toString('hex') // Format: iv:encrypted:authTag return { - encrypted: `${iv.toString('hex')}:${encrypted}:${authTag.toString('hex')}`, - iv: iv.toString('hex'), + encrypted: `${ivHex}:${encrypted}:${authTag.toString('hex')}`, + iv: ivHex, } } @@ -55,8 +56,10 @@ export async function encryptApiKey(apiKey: string): Promise<{ encrypted: string * @returns A promise that resolves to an object containing the decrypted API key */ export async function decryptApiKey(encryptedValue: string): Promise<{ decrypted: string }> { + const parts = encryptedValue.split(':') + // Check if this is actually encrypted (contains colons) - if (!encryptedValue.includes(':') || encryptedValue.split(':').length !== 3) { + if (parts.length !== 3) { // This is a plain text key, return as-is return { decrypted: encryptedValue } } @@ -68,10 +71,9 @@ export async function decryptApiKey(encryptedValue: string): Promise<{ decrypted return { decrypted: encryptedValue } } - const parts = encryptedValue.split(':') const ivHex = parts[0] - const authTagHex = parts[parts.length - 1] - const encrypted = parts.slice(1, -1).join(':') + const authTagHex = parts[2] + const encrypted = parts[1] if (!ivHex || !encrypted || !authTagHex) { throw new Error('Invalid encrypted API key format. Expected "iv:encrypted:authTag"') @@ -81,7 +83,7 @@ export async function decryptApiKey(encryptedValue: string): Promise<{ decrypted const authTag = Buffer.from(authTagHex, 'hex') try { - const decipher = createDecipheriv('aes-256-gcm', key, iv) + const decipher = createDecipheriv('aes-256-gcm', key, iv, { authTagLength: 16 }) decipher.setAuthTag(authTag) let decrypted = decipher.update(encrypted, 'hex', 'utf8') diff --git a/apps/sim/lib/core/security/encryption.ts b/apps/sim/lib/core/security/encryption.ts index ab4fcdab71d..811a60ada48 100644 --- a/apps/sim/lib/core/security/encryption.ts +++ b/apps/sim/lib/core/security/encryption.ts @@ -21,16 +21,17 @@ export async function encryptSecret(secret: string): Promise<{ encrypted: string const iv = randomBytes(16) const key = getEncryptionKey() - const cipher = createCipheriv('aes-256-gcm', key, iv) + const cipher = createCipheriv('aes-256-gcm', key, iv, { authTagLength: 16 }) let encrypted = cipher.update(secret, 'utf8', 'hex') encrypted += cipher.final('hex') const authTag = cipher.getAuthTag() + const ivHex = iv.toString('hex') // Format: iv:encrypted:authTag return { - encrypted: `${iv.toString('hex')}:${encrypted}:${authTag.toString('hex')}`, - iv: iv.toString('hex'), + encrypted: `${ivHex}:${encrypted}:${authTag.toString('hex')}`, + iv: ivHex, } } @@ -54,15 +55,17 @@ export async function decryptSecret(encryptedValue: string): Promise<{ decrypted const authTag = Buffer.from(authTagHex, 'hex') try { - const decipher = createDecipheriv('aes-256-gcm', key, iv) + const decipher = createDecipheriv('aes-256-gcm', key, iv, { authTagLength: 16 }) decipher.setAuthTag(authTag) let decrypted = decipher.update(encrypted, 'hex', 'utf8') decrypted += decipher.final('utf8') return { decrypted } - } catch (error: any) { - logger.error('Decryption error:', { error: error.message }) + } catch (error: unknown) { + logger.error('Decryption error:', { + error: error instanceof Error ? error.message : 'Unknown error', + }) throw error } } diff --git a/bun.lock b/bun.lock index 181248e1ee7..e773f35d67e 100644 --- a/bun.lock +++ b/bun.lock @@ -10,7 +10,7 @@ "glob": "13.0.0", "husky": "9.1.7", "lint-staged": "16.0.0", - "turbo": "2.9.1", + "turbo": "2.9.3", }, }, "apps/docs": { @@ -1486,17 +1486,17 @@ "@trigger.dev/sdk": ["@trigger.dev/sdk@4.4.3", "", { "dependencies": { "@opentelemetry/api": "1.9.0", "@opentelemetry/semantic-conventions": "1.36.0", "@trigger.dev/core": "4.4.3", "chalk": "^5.2.0", "cronstrue": "^2.21.0", "debug": "^4.3.4", "evt": "^2.4.13", "slug": "^6.0.0", "ulid": "^2.3.0", "uncrypto": "^0.1.3", "uuid": "^9.0.0", "ws": "^8.11.0" }, "peerDependencies": { "ai": "^4.2.0 || ^5.0.0 || ^6.0.0", "zod": "^3.0.0 || ^4.0.0" }, "optionalPeers": ["ai"] }, "sha512-ghJkak+PTBJJ9HiHMcnahJmzjsgCzYiIHu5Qj5R7I9q5LS6i7mkx169rB/tOE9HLadd4HSu3yYA5DrH4wXhZuw=="], - "@turbo/darwin-64": ["@turbo/darwin-64@2.9.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-d1zTcIf6VWT7cdfjhi0X36C2PRsUi2HdEwYzVgkLHmuuYtL+1Y1Zu3JdlouoB/NjG2vX3q4NnKLMNhDOEweoIg=="], + "@turbo/darwin-64": ["@turbo/darwin-64@2.9.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-P8foouaP+y/p+hhEGBoZpzMbpVvUMwPjDpcy6wN7EYfvvyISD1USuV27qWkczecihwuPJzQ1lDBuL8ERcavTyg=="], - "@turbo/darwin-arm64": ["@turbo/darwin-arm64@2.9.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-AwJ4mA++Kpem33Lcov093hS1LrgqbKxqq5FCReoqsA8ayEG6eAJAo8ItDd9qQTdBiXxZH8GHCspLAMIe1t3Xyw=="], + "@turbo/darwin-arm64": ["@turbo/darwin-arm64@2.9.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-SIzEkvtNdzdI50FJDaIQ6kQGqgSSdFPcdn0wqmmONN6iGKjy6hsT+EH99GP65FsfV7DLZTh2NmtTIRl2kdoz5Q=="], - "@turbo/linux-64": ["@turbo/linux-64@2.9.1", "", { "os": "linux", "cpu": "x64" }, "sha512-HT9SjKkjEw9uvlgly/qwCGEm4wOXOwQPSPS+wkg+/O1Qan3F1uU/0PFYzxl3m4lfuV3CP9wr2Dq5dPrUX+B9Ag=="], + "@turbo/linux-64": ["@turbo/linux-64@2.9.3", "", { "os": "linux", "cpu": "x64" }, "sha512-pLRwFmcHHNBvsCySLS6OFabr/07kDT2pxEt/k6eBf/3asiVQZKJ7Rk88AafQx2aYA641qek4RsXvYO3JYpiBug=="], - "@turbo/linux-arm64": ["@turbo/linux-arm64@2.9.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-+4s5GZs3kjxc1KMhLBhoQy4UBkXjOhgidA9ipNllkA4JLivSqUCuOgU1Xbyp6vzYrsqHJ9vvwo/2mXgEtD6ZHg=="], + "@turbo/linux-arm64": ["@turbo/linux-arm64@2.9.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-gy6ApUroC2Nzv+qjGtE/uPNkhHAFU4c8God+zd5Aiv9L9uBgHlxVJpHT3XWl5xwlJZ2KWuMrlHTaS5kmNB+q1Q=="], - "@turbo/windows-64": ["@turbo/windows-64@2.9.1", "", { "os": "win32", "cpu": "x64" }, "sha512-ZO7GCyQd5HV564XWHc9KysjanFfM3DmnWquyEByu+hQMq42g9OMU/fYOCfHS6Xj2aXkIg2FHJeRV+iAck2YrbQ=="], + "@turbo/windows-64": ["@turbo/windows-64@2.9.3", "", { "os": "win32", "cpu": "x64" }, "sha512-d0YelTX6hAsB7kIEtGB3PzIzSfAg3yDoUlHwuwJc3adBXUsyUIs0YLG+1NNtuhcDOUGnWQeKUoJ2pGWvbpRj7w=="], - "@turbo/windows-arm64": ["@turbo/windows-arm64@2.9.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-BjX2fdz38mBb/H94JXrD5cJ+mEq8NmsCbYdC42JzQebJ0X8EdNgyFoEhOydPGViOmaRmhhdZnPZKKn6wahSpcA=="], + "@turbo/windows-arm64": ["@turbo/windows-arm64@2.9.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-/08CwpKJl3oRY8nOlh2YgilZVJDHsr60XTNxRhuDeuFXONpUZ5X+Nv65izbG/xBew9qxcJFbDX9/sAmAX+ITcQ=="], "@tweenjs/tween.js": ["@tweenjs/tween.js@23.1.3", "", {}, "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA=="], @@ -3632,7 +3632,7 @@ "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="], - "turbo": ["turbo@2.9.1", "", { "optionalDependencies": { "@turbo/darwin-64": "2.9.1", "@turbo/darwin-arm64": "2.9.1", "@turbo/linux-64": "2.9.1", "@turbo/linux-arm64": "2.9.1", "@turbo/windows-64": "2.9.1", "@turbo/windows-arm64": "2.9.1" }, "bin": { "turbo": "bin/turbo" } }, "sha512-TO9du8MwLTAKoXcGezekh9cPJabJUb0+8KxtpMR6kXdRASrmJ8qXf2GkVbCREgzbMQakzfNcux9cZtxheDY4RQ=="], + "turbo": ["turbo@2.9.3", "", { "optionalDependencies": { "@turbo/darwin-64": "2.9.3", "@turbo/darwin-arm64": "2.9.3", "@turbo/linux-64": "2.9.3", "@turbo/linux-arm64": "2.9.3", "@turbo/windows-64": "2.9.3", "@turbo/windows-arm64": "2.9.3" }, "bin": { "turbo": "bin/turbo" } }, "sha512-J/VUvsGRykPb9R8Kh8dHVBOqioDexLk9BhLCU/ZybRR+HN9UR3cURdazFvNgMDt9zPP8TF6K73Z+tplfmi0PqQ=="], "tweetnacl": ["tweetnacl@0.14.5", "", {}, "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="], diff --git a/package.json b/package.json index 84fff1afa0b..9cd7279b997 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "glob": "13.0.0", "husky": "9.1.7", "lint-staged": "16.0.0", - "turbo": "2.9.1" + "turbo": "2.9.3" }, "lint-staged": { "*.{js,jsx,ts,tsx,json,css,scss}": [ diff --git a/packages/db/scripts/migrate-block-api-keys-to-byok.ts b/packages/db/scripts/migrate-block-api-keys-to-byok.ts index 06a507a5e92..88ee98e2204 100644 --- a/packages/db/scripts/migrate-block-api-keys-to-byok.ts +++ b/packages/db/scripts/migrate-block-api-keys-to-byok.ts @@ -125,7 +125,7 @@ function getEncryptionKeyBuffer(): Buffer { async function encryptSecret(secret: string): Promise { const iv = randomBytes(16) const key = getEncryptionKeyBuffer() - const cipher = createCipheriv('aes-256-gcm', key, iv) + const cipher = createCipheriv('aes-256-gcm', key, iv, { authTagLength: 16 }) let encrypted = cipher.update(secret, 'utf8', 'hex') encrypted += cipher.final('hex') const authTag = cipher.getAuthTag() @@ -146,7 +146,7 @@ async function decryptSecret(encryptedValue: string): Promise { const iv = Buffer.from(ivHex, 'hex') const authTag = Buffer.from(authTagHex, 'hex') - const decipher = createDecipheriv('aes-256-gcm', key, iv) + const decipher = createDecipheriv('aes-256-gcm', key, iv, { authTagLength: 16 }) decipher.setAuthTag(authTag) let decrypted = decipher.update(encrypted, 'hex', 'utf8')