diff --git a/backend/src/controllers/v1/webhookController.ts b/backend/src/controllers/v1/webhookController.ts index 66b847b62..09175ebfe 100644 --- a/backend/src/controllers/v1/webhookController.ts +++ b/backend/src/controllers/v1/webhookController.ts @@ -6,7 +6,11 @@ import { getWebhookPayload, triggerWebhookRequest } from "../../services/Webhook import { BadRequestError, ResourceNotFoundError } from "../../utils/errors"; import { EEAuditLogService } from "../../ee/services"; import { EventType } from "../../ee/models"; -import { ALGORITHM_AES_256_GCM, ENCODING_SCHEME_BASE64 } from "../../variables"; +import { + ALGORITHM_AES_256_GCM, + ENCODING_SCHEME_BASE64, + ENCODING_SCHEME_UTF8 +} from "../../variables"; import { validateRequest } from "../../helpers/validation"; import * as reqValidator from "../../validation/webhooks"; import { @@ -15,6 +19,7 @@ import { getUserProjectPermissions } from "../../ee/services/ProjectRoleService"; import { ForbiddenError } from "@casl/ability"; +import { encryptSymmetric128BitHexKeyUTF8 } from "../../utils/crypto"; export const createWebhook = async (req: Request, res: Response) => { const { @@ -31,20 +36,31 @@ export const createWebhook = async (req: Request, res: Response) => { workspace: workspaceId, environment, secretPath, - url: webhookUrl, - algorithm: ALGORITHM_AES_256_GCM, - keyEncoding: ENCODING_SCHEME_BASE64 + url: webhookUrl }); if (webhookSecretKey) { - let encryptionKey = await getRootEncryptionKey(); - if (!encryptionKey) { - encryptionKey = await getEncryptionKey(); + const encryptionKey = await getEncryptionKey(); + const rootEncryptionKey = await getRootEncryptionKey(); + + if (rootEncryptionKey) { + const { ciphertext, iv, tag } = client.encryptSymmetric(webhookSecretKey, rootEncryptionKey); + webhook.iv = iv; + webhook.tag = tag; + webhook.encryptedSecretKey = ciphertext; + webhook.algorithm = ALGORITHM_AES_256_GCM; + webhook.keyEncoding = ENCODING_SCHEME_BASE64; + } else if (encryptionKey) { + const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8({ + plaintext: webhookSecretKey, + key: encryptionKey + }); + webhook.iv = iv; + webhook.tag = tag; + webhook.encryptedSecretKey = ciphertext; + webhook.algorithm = ALGORITHM_AES_256_GCM; + webhook.keyEncoding = ENCODING_SCHEME_UTF8; } - const { ciphertext, iv, tag } = client.encryptSymmetric(webhookSecretKey, encryptionKey); - webhook.iv = iv; - webhook.tag = tag; - webhook.encryptedSecretKey = ciphertext; } await webhook.save(); diff --git a/backend/src/models/webhooks.ts b/backend/src/models/webhooks.ts index 845c6ada2..bef5e795a 100644 --- a/backend/src/models/webhooks.ts +++ b/backend/src/models/webhooks.ts @@ -65,13 +65,11 @@ const WebhookSchema = new Schema( // the encryption algorithm used type: String, enum: [ALGORITHM_AES_256_GCM], - required: true, select: false }, keyEncoding: { type: String, enum: [ENCODING_SCHEME_UTF8, ENCODING_SCHEME_BASE64], - required: true, select: false } }, @@ -80,4 +78,4 @@ const WebhookSchema = new Schema( } ); -export const Webhook = model("Webhook", WebhookSchema); \ No newline at end of file +export const Webhook = model("Webhook", WebhookSchema); diff --git a/backend/src/services/WebhookService.ts b/backend/src/services/WebhookService.ts index 7506e25c1..cc2106a1c 100644 --- a/backend/src/services/WebhookService.ts +++ b/backend/src/services/WebhookService.ts @@ -2,26 +2,42 @@ import axios from "axios"; import crypto from "crypto"; import { Types } from "mongoose"; import picomatch from "picomatch"; -import { client, getRootEncryptionKey } from "../config"; +import { client, getEncryptionKey, getRootEncryptionKey } from "../config"; import { IWebhook, Webhook } from "../models"; +import { decryptSymmetric128BitHexKeyUTF8 } from "../utils/crypto"; +import { ENCODING_SCHEME_BASE64, ENCODING_SCHEME_UTF8 } from "../variables"; export const triggerWebhookRequest = async ( - { url, encryptedSecretKey, iv, tag }: IWebhook, + { url, encryptedSecretKey, iv, tag, keyEncoding }: IWebhook, payload: Record ) => { const headers: Record = {}; payload["timestamp"] = Date.now(); if (encryptedSecretKey) { + const encryptionKey = await getEncryptionKey(); const rootEncryptionKey = await getRootEncryptionKey(); - const secretKey = client.decryptSymmetric(encryptedSecretKey, rootEncryptionKey, iv, tag); - const webhookSign = crypto - .createHmac("sha256", secretKey) - .update(JSON.stringify(payload)) - .digest("hex"); - headers["x-infisical-signature"] = `t=${payload["timestamp"]};${webhookSign}`; + let secretKey; + if (rootEncryptionKey && keyEncoding === ENCODING_SCHEME_BASE64) { + // case: encoding scheme is base64 + secretKey = client.decryptSymmetric(encryptedSecretKey, rootEncryptionKey, iv, tag); + } else if (encryptionKey && keyEncoding === ENCODING_SCHEME_UTF8) { + // case: encoding scheme is utf8 + secretKey = decryptSymmetric128BitHexKeyUTF8({ + ciphertext: encryptedSecretKey, + iv: iv, + tag: tag, + key: encryptionKey + }); + } + if (secretKey) { + const webhookSign = crypto + .createHmac("sha256", secretKey) + .update(JSON.stringify(payload)) + .digest("hex"); + headers["x-infisical-signature"] = `t=${payload["timestamp"]};${webhookSign}`; + } } - const req = await axios.post(url, payload, { headers }); return req; };