mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 20:27:43 +00:00
Swap away from using hash checks
This commit is contained in:
+4
-11
@@ -1,4 +1,3 @@
|
|||||||
import crypto from "crypto";
|
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
|
|
||||||
import { scanContentAndGetFindings } from "@app/ee/services/secret-scanning/secret-scanning-queue/secret-scanning-fns";
|
import { scanContentAndGetFindings } from "@app/ee/services/secret-scanning/secret-scanning-queue/secret-scanning-fns";
|
||||||
@@ -24,6 +23,7 @@ import {
|
|||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { request } from "@app/lib/config/request";
|
import { request } from "@app/lib/config/request";
|
||||||
import { titleCaseToCamelCase } from "@app/lib/fn";
|
import { titleCaseToCamelCase } from "@app/lib/fn";
|
||||||
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { GitHubRepositoryRegex } from "@app/lib/regex";
|
import { GitHubRepositoryRegex } from "@app/lib/regex";
|
||||||
import {
|
import {
|
||||||
getBitbucketUser,
|
getBitbucketUser,
|
||||||
@@ -39,13 +39,6 @@ import {
|
|||||||
TQueueBitbucketResourceDiffScan
|
TQueueBitbucketResourceDiffScan
|
||||||
} from "./bitbucket-secret-scanning-types";
|
} from "./bitbucket-secret-scanning-types";
|
||||||
|
|
||||||
export function generateBitbucketWebhookSecret(serverSecret: string, dataSourceId: string) {
|
|
||||||
return crypto
|
|
||||||
.createHash("sha256")
|
|
||||||
.update(serverSecret + dataSourceId)
|
|
||||||
.digest("hex");
|
|
||||||
}
|
|
||||||
|
|
||||||
export const BitbucketSecretScanningFactory = () => {
|
export const BitbucketSecretScanningFactory = () => {
|
||||||
const initialize: TSecretScanningFactoryInitialize<
|
const initialize: TSecretScanningFactoryInitialize<
|
||||||
TBitbucketDataSourceInput,
|
TBitbucketDataSourceInput,
|
||||||
@@ -74,7 +67,7 @@ export const BitbucketSecretScanningFactory = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return callback({
|
return callback({
|
||||||
credentials: { webhookId: data.uuid }
|
credentials: { webhookId: data.uuid, webhookSecret: alphaNumericNanoId(64) }
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -84,7 +77,7 @@ export const BitbucketSecretScanningFactory = () => {
|
|||||||
TBitbucketDataSourceCredentials
|
TBitbucketDataSourceCredentials
|
||||||
> = async ({ dataSourceId, credentials, connection, payload }) => {
|
> = async ({ dataSourceId, credentials, connection, payload }) => {
|
||||||
const { email, apiToken } = connection.credentials;
|
const { email, apiToken } = connection.credentials;
|
||||||
const { webhookId } = credentials;
|
const { webhookId, webhookSecret } = credentials;
|
||||||
|
|
||||||
const authHeader = `Basic ${Buffer.from(`${email}:${apiToken}`).toString("base64")}`;
|
const authHeader = `Basic ${Buffer.from(`${email}:${apiToken}`).toString("base64")}`;
|
||||||
|
|
||||||
@@ -98,7 +91,7 @@ export const BitbucketSecretScanningFactory = () => {
|
|||||||
url: newWebhookUrl,
|
url: newWebhookUrl,
|
||||||
active: true,
|
active: true,
|
||||||
events: ["repo:push"],
|
events: ["repo:push"],
|
||||||
secret: generateBitbucketWebhookSecret(cfg.AUTH_SECRET, dataSourceId)
|
secret: webhookSecret
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
+2
-1
@@ -92,5 +92,6 @@ export const BitbucketFindingSchema = BaseSecretScanningFindingSchema.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const BitbucketDataSourceCredentialsSchema = z.object({
|
export const BitbucketDataSourceCredentialsSchema = z.object({
|
||||||
webhookId: z.string()
|
webhookId: z.string(),
|
||||||
|
webhookSecret: z.string()
|
||||||
});
|
});
|
||||||
|
|||||||
+45
-5
@@ -1,16 +1,27 @@
|
|||||||
|
import crypto from "crypto";
|
||||||
|
|
||||||
import { TSecretScanningV2DALFactory } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-dal";
|
import { TSecretScanningV2DALFactory } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-dal";
|
||||||
import { SecretScanningDataSource } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-enums";
|
import { SecretScanningDataSource } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-enums";
|
||||||
import { TSecretScanningV2QueueServiceFactory } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-queue";
|
import { TSecretScanningV2QueueServiceFactory } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-queue";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
|
|
||||||
import { TBitbucketDataSource, TBitbucketPushEvent } from "./bitbucket-secret-scanning-types";
|
import {
|
||||||
|
TBitbucketDataSource,
|
||||||
|
TBitbucketDataSourceCredentials,
|
||||||
|
TBitbucketPushEvent
|
||||||
|
} from "./bitbucket-secret-scanning-types";
|
||||||
|
|
||||||
export const bitbucketSecretScanningService = (
|
export const bitbucketSecretScanningService = (
|
||||||
secretScanningV2DAL: TSecretScanningV2DALFactory,
|
secretScanningV2DAL: TSecretScanningV2DALFactory,
|
||||||
secretScanningV2Queue: Pick<TSecretScanningV2QueueServiceFactory, "queueResourceDiffScan">
|
secretScanningV2Queue: Pick<TSecretScanningV2QueueServiceFactory, "queueResourceDiffScan">,
|
||||||
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">
|
||||||
) => {
|
) => {
|
||||||
const handlePushEvent = async (payload: TBitbucketPushEvent & { dataSourceId: string }) => {
|
const handlePushEvent = async (
|
||||||
const { push, repository } = payload;
|
payload: TBitbucketPushEvent & { dataSourceId: string; receivedSignature: string; bodyString: string }
|
||||||
|
) => {
|
||||||
|
const { push, repository, bodyString, receivedSignature } = payload;
|
||||||
|
|
||||||
if (!push?.changes?.length || !repository?.workspace?.uuid) {
|
if (!push?.changes?.length || !repository?.workspace?.uuid) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
@@ -35,9 +46,38 @@ export const bitbucketSecretScanningService = (
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
isAutoScanEnabled,
|
isAutoScanEnabled,
|
||||||
config: { includeRepos }
|
config: { includeRepos },
|
||||||
|
encryptedCredentials,
|
||||||
|
projectId
|
||||||
} = dataSource;
|
} = dataSource;
|
||||||
|
|
||||||
|
if (!encryptedCredentials) {
|
||||||
|
logger.info(
|
||||||
|
`secretScanningV2PushEvent: Bitbucket - Could not find encrypted credentials [dataSourceId=${dataSource.id}] [workspaceUuid=${repository.workspace.uuid}]`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { decryptor } = await kmsService.createCipherPairWithDataKey({
|
||||||
|
type: KmsDataKey.SecretManager,
|
||||||
|
projectId
|
||||||
|
});
|
||||||
|
|
||||||
|
const decryptedCredentials = decryptor({ cipherTextBlob: encryptedCredentials });
|
||||||
|
|
||||||
|
const credentials = JSON.parse(decryptedCredentials.toString()) as TBitbucketDataSourceCredentials;
|
||||||
|
|
||||||
|
const hmac = crypto.createHmac("sha256", credentials.webhookSecret);
|
||||||
|
hmac.update(bodyString);
|
||||||
|
const calculatedSignature = hmac.digest("hex");
|
||||||
|
|
||||||
|
if (calculatedSignature !== receivedSignature) {
|
||||||
|
logger.error(
|
||||||
|
`secretScanningV2PushEvent: Bitbucket - Invalid signature for webhook [dataSourceId=${dataSource.id}] [workspaceUuid=${repository.workspace.uuid}]`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isAutoScanEnabled) {
|
if (!isAutoScanEnabled) {
|
||||||
logger.info(
|
logger.info(
|
||||||
`secretScanningV2PushEvent: Bitbucket - ignoring due to auto scan disabled [dataSourceId=${dataSource.id}] [workspaceUuid=${repository.workspace.uuid}]`
|
`secretScanningV2PushEvent: Bitbucket - ignoring due to auto scan disabled [dataSourceId=${dataSource.id}] [workspaceUuid=${repository.workspace.uuid}]`
|
||||||
|
|||||||
@@ -19,8 +19,7 @@ export const BaseSecretScanningDataSourceSchema = ({
|
|||||||
// unique to provider
|
// unique to provider
|
||||||
type: true,
|
type: true,
|
||||||
connectionId: true,
|
connectionId: true,
|
||||||
config: true,
|
config: true
|
||||||
encryptedCredentials: true
|
|
||||||
}).extend({
|
}).extend({
|
||||||
type: z.literal(type),
|
type: z.literal(type),
|
||||||
connectionId: isConnectionRequired ? z.string().uuid() : z.null(),
|
connectionId: isConnectionRequired ? z.string().uuid() : z.null(),
|
||||||
|
|||||||
@@ -901,6 +901,6 @@ export const secretScanningV2ServiceFactory = ({
|
|||||||
findSecretScanningConfigByProjectId,
|
findSecretScanningConfigByProjectId,
|
||||||
upsertSecretScanningConfig,
|
upsertSecretScanningConfig,
|
||||||
github: githubSecretScanningService(secretScanningV2DAL, secretScanningV2Queue),
|
github: githubSecretScanningService(secretScanningV2DAL, secretScanningV2Queue),
|
||||||
bitbucket: bitbucketSecretScanningService(secretScanningV2DAL, secretScanningV2Queue)
|
bitbucket: bitbucketSecretScanningService(secretScanningV2DAL, secretScanningV2Queue, kmsService)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import crypto from "crypto";
|
|||||||
import { Probot } from "probot";
|
import { Probot } from "probot";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { generateBitbucketWebhookSecret } from "@app/ee/services/secret-scanning-v2/bitbucket/bitbucket-secret-scanning-factory";
|
|
||||||
import { TBitbucketPushEvent } from "@app/ee/services/secret-scanning-v2/bitbucket/bitbucket-secret-scanning-types";
|
import { TBitbucketPushEvent } from "@app/ee/services/secret-scanning-v2/bitbucket/bitbucket-secret-scanning-types";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
@@ -101,24 +100,17 @@ export const registerSecretScanningV2Webhooks = async (server: FastifyZodProvide
|
|||||||
return res.status(401).send({ message: "Unauthorized: Invalid signature format" });
|
return res.status(401).send({ message: "Unauthorized: Invalid signature format" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const cfg = getConfig();
|
|
||||||
|
|
||||||
const hmac = crypto.createHmac("sha256", generateBitbucketWebhookSecret(cfg.AUTH_SECRET, dataSourceId));
|
|
||||||
hmac.update(JSON.stringify(req.body));
|
|
||||||
const calculatedSignature = hmac.digest("hex");
|
|
||||||
|
|
||||||
const receivedSignature = signature.substring(expectedSignaturePrefix.length);
|
const receivedSignature = signature.substring(expectedSignaturePrefix.length);
|
||||||
|
|
||||||
if (calculatedSignature !== receivedSignature) {
|
|
||||||
logger.error("Invalid signature for Bitbucket webhook");
|
|
||||||
return res.status(401).send({ message: "Unauthorized: Invalid signature" });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dataSourceId) return res.status(400).send({ message: "Data Source ID is required" });
|
if (!dataSourceId) return res.status(400).send({ message: "Data Source ID is required" });
|
||||||
|
|
||||||
|
console.log("111");
|
||||||
|
|
||||||
await server.services.secretScanningV2.bitbucket.handlePushEvent({
|
await server.services.secretScanningV2.bitbucket.handlePushEvent({
|
||||||
...(req.body as TBitbucketPushEvent),
|
...(req.body as TBitbucketPushEvent),
|
||||||
dataSourceId
|
dataSourceId,
|
||||||
|
receivedSignature,
|
||||||
|
bodyString: JSON.stringify(req.body)
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.send("ok");
|
return res.send("ok");
|
||||||
|
|||||||
Reference in New Issue
Block a user