mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 20:25:43 +00:00
Make webhooks work, add workspace selection, rename BitBucket to
Bitbucket
This commit is contained in:
+8
-8
@@ -1,16 +1,16 @@
|
||||
import { registerSecretScanningEndpoints } from "@app/ee/routes/v2/secret-scanning-v2-routers/secret-scanning-v2-endpoints";
|
||||
import {
|
||||
BitBucketDataSourceSchema,
|
||||
CreateBitBucketDataSourceSchema,
|
||||
UpdateBitBucketDataSourceSchema
|
||||
BitbucketDataSourceSchema,
|
||||
CreateBitbucketDataSourceSchema,
|
||||
UpdateBitbucketDataSourceSchema
|
||||
} from "@app/ee/services/secret-scanning-v2/bitbucket";
|
||||
import { SecretScanningDataSource } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-enums";
|
||||
|
||||
export const registerBitBucketSecretScanningRouter = async (server: FastifyZodProvider) =>
|
||||
export const registerBitbucketSecretScanningRouter = async (server: FastifyZodProvider) =>
|
||||
registerSecretScanningEndpoints({
|
||||
type: SecretScanningDataSource.BitBucket,
|
||||
type: SecretScanningDataSource.Bitbucket,
|
||||
server,
|
||||
responseSchema: BitBucketDataSourceSchema,
|
||||
createSchema: CreateBitBucketDataSourceSchema,
|
||||
updateSchema: UpdateBitBucketDataSourceSchema
|
||||
responseSchema: BitbucketDataSourceSchema,
|
||||
createSchema: CreateBitbucketDataSourceSchema,
|
||||
updateSchema: UpdateBitbucketDataSourceSchema
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SecretScanningDataSource } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-enums";
|
||||
|
||||
import { registerBitBucketSecretScanningRouter } from "./bitbucket-secret-scanning-router";
|
||||
import { registerBitbucketSecretScanningRouter } from "./bitbucket-secret-scanning-router";
|
||||
import { registerGitHubSecretScanningRouter } from "./github-secret-scanning-router";
|
||||
|
||||
export * from "./secret-scanning-v2-router";
|
||||
@@ -10,5 +10,5 @@ export const SECRET_SCANNING_REGISTER_ROUTER_MAP: Record<
|
||||
(server: FastifyZodProvider) => Promise<void>
|
||||
> = {
|
||||
[SecretScanningDataSource.GitHub]: registerGitHubSecretScanningRouter,
|
||||
[SecretScanningDataSource.BitBucket]: registerBitBucketSecretScanningRouter
|
||||
[SecretScanningDataSource.Bitbucket]: registerBitbucketSecretScanningRouter
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import { z } from "zod";
|
||||
|
||||
import { SecretScanningConfigsSchema } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { BitBucketDataSourceListItemSchema } from "@app/ee/services/secret-scanning-v2/bitbucket";
|
||||
import { BitbucketDataSourceListItemSchema } from "@app/ee/services/secret-scanning-v2/bitbucket";
|
||||
import { GitHubDataSourceListItemSchema } from "@app/ee/services/secret-scanning-v2/github";
|
||||
import {
|
||||
SecretScanningFindingStatus,
|
||||
@@ -24,7 +24,7 @@ import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
const SecretScanningDataSourceOptionsSchema = z.discriminatedUnion("type", [
|
||||
GitHubDataSourceListItemSchema,
|
||||
BitBucketDataSourceListItemSchema
|
||||
BitbucketDataSourceListItemSchema
|
||||
]);
|
||||
|
||||
export const registerSecretScanningV2Router = async (server: FastifyZodProvider) => {
|
||||
|
||||
@@ -18,33 +18,33 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
||||
environmentsUsed: 0,
|
||||
identityLimit: null,
|
||||
identitiesUsed: 0,
|
||||
dynamicSecret: false,
|
||||
dynamicSecret: true,
|
||||
secretVersioning: true,
|
||||
pitRecovery: false,
|
||||
ipAllowlisting: false,
|
||||
rbac: false,
|
||||
githubOrgSync: false,
|
||||
pitRecovery: true,
|
||||
ipAllowlisting: true,
|
||||
rbac: true,
|
||||
githubOrgSync: true,
|
||||
customRateLimits: false,
|
||||
customAlerts: false,
|
||||
secretAccessInsights: false,
|
||||
auditLogs: false,
|
||||
auditLogsRetentionDays: 0,
|
||||
auditLogStreams: false,
|
||||
customAlerts: true,
|
||||
secretAccessInsights: true,
|
||||
auditLogs: true,
|
||||
auditLogsRetentionDays: 3,
|
||||
auditLogStreams: true,
|
||||
auditLogStreamLimit: 3,
|
||||
samlSSO: false,
|
||||
hsm: false,
|
||||
oidcSSO: false,
|
||||
scim: false,
|
||||
ldap: false,
|
||||
groups: false,
|
||||
samlSSO: true,
|
||||
hsm: true,
|
||||
oidcSSO: true,
|
||||
scim: true,
|
||||
ldap: true,
|
||||
groups: true,
|
||||
status: null,
|
||||
trial_end: null,
|
||||
has_used_trial: true,
|
||||
secretApproval: false,
|
||||
secretRotation: false,
|
||||
caCrl: false,
|
||||
instanceUserManagement: false,
|
||||
externalKms: false,
|
||||
secretApproval: true,
|
||||
secretRotation: true,
|
||||
caCrl: true,
|
||||
instanceUserManagement: true,
|
||||
externalKms: true,
|
||||
rateLimits: {
|
||||
readLimit: 60,
|
||||
writeLimit: 200,
|
||||
@@ -52,13 +52,13 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
||||
},
|
||||
pkiEst: false,
|
||||
enforceMfa: false,
|
||||
projectTemplates: false,
|
||||
kmip: false,
|
||||
gateway: false,
|
||||
sshHostGroups: false,
|
||||
secretScanning: false,
|
||||
enterpriseSecretSyncs: false,
|
||||
enterpriseAppConnections: false
|
||||
projectTemplates: true,
|
||||
kmip: true,
|
||||
gateway: true,
|
||||
sshHostGroups: true,
|
||||
enterpriseAppConnections: true,
|
||||
enterpriseSecretSyncs: true,
|
||||
secretScanning: true
|
||||
});
|
||||
|
||||
export const setupLicenseRequestWithStore = (
|
||||
|
||||
+3
-3
@@ -3,7 +3,7 @@ import { TSecretScanningDataSourceListItem } from "@app/ee/services/secret-scann
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
|
||||
export const BITBUCKET_SECRET_SCANNING_DATA_SOURCE_LIST_OPTION: TSecretScanningDataSourceListItem = {
|
||||
name: "BitBucket",
|
||||
type: SecretScanningDataSource.BitBucket,
|
||||
connection: AppConnection.BitBucket
|
||||
name: "Bitbucket",
|
||||
type: SecretScanningDataSource.Bitbucket,
|
||||
connection: AppConnection.Bitbucket
|
||||
};
|
||||
|
||||
+152
-96
@@ -3,7 +3,6 @@ import { join } from "path";
|
||||
import { scanContentAndGetFindings } from "@app/ee/services/secret-scanning/secret-scanning-queue/secret-scanning-fns";
|
||||
import { SecretMatch } from "@app/ee/services/secret-scanning/secret-scanning-queue/secret-scanning-queue-types";
|
||||
import {
|
||||
SecretScanningDataSource,
|
||||
SecretScanningFindingSeverity,
|
||||
SecretScanningResource
|
||||
} from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-enums";
|
||||
@@ -20,54 +19,106 @@ import {
|
||||
TSecretScanningFactoryListRawResources,
|
||||
TSecretScanningFactoryPostInitialization
|
||||
} from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-types";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { titleCaseToCamelCase } from "@app/lib/fn";
|
||||
import { GitHubRepositoryRegex } from "@app/lib/regex";
|
||||
import {
|
||||
getBitBucketUser,
|
||||
listBitBucketRepositories,
|
||||
TBitBucketConnection
|
||||
getBitbucketUser,
|
||||
listBitbucketRepositories,
|
||||
TBitbucketConnection
|
||||
} from "@app/services/app-connection/bitbucket";
|
||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||
|
||||
import { TBitBucketDataSourceWithConnection, TQueueBitBucketResourceDiffScan } from "./bitbucket-secret-scanning-types";
|
||||
import { TBitbucketDataSourceWithConnection, TQueueBitbucketResourceDiffScan } from "./bitbucket-secret-scanning-types";
|
||||
|
||||
export const BitBucketSecretScanningFactory = () => {
|
||||
const initialize: TSecretScanningFactoryInitialize<TBitBucketConnection> = async (
|
||||
{ connection, secretScanningV2DAL },
|
||||
export const BitbucketSecretScanningFactory = () => {
|
||||
const initialize: TSecretScanningFactoryInitialize<TBitbucketConnection> = async (
|
||||
{ connection, payload },
|
||||
callback
|
||||
) => {
|
||||
// TODO(andrey): Swap for something proper
|
||||
const externalId = connection.credentials.email;
|
||||
const { email, apiToken } = connection.credentials;
|
||||
const authHeader = `Basic ${Buffer.from(`${email}:${apiToken}`).toString("base64")}`;
|
||||
|
||||
const existingDataSource = await secretScanningV2DAL.dataSources.findOne({
|
||||
externalId,
|
||||
type: SecretScanningDataSource.BitBucket
|
||||
});
|
||||
|
||||
if (existingDataSource)
|
||||
throw new BadRequestError({
|
||||
message: `A Data Source already exists for this BitBucket Radar Connection in the Project with ID "${existingDataSource.projectId}"`
|
||||
});
|
||||
const { data } = await request.post<{ uuid: string }>(
|
||||
`${IntegrationUrls.BITBUCKET_API_URL}/2.0/workspaces/${payload.config.workspaceSlug}/hooks`,
|
||||
{
|
||||
description: "Infisical webhook for push events",
|
||||
url: `https://tunnel.util.lol/secret-scanning/webhooks/bitbucket`, // TODO(andrey): Swap to ${cfg.SITE_URL}
|
||||
active: true,
|
||||
events: ["repo:push"]
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: authHeader,
|
||||
Accept: "application/json"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return callback({
|
||||
externalId
|
||||
credentials: { webhookId: data.uuid }
|
||||
});
|
||||
};
|
||||
|
||||
const postInitialization: TSecretScanningFactoryPostInitialization<TBitBucketConnection> = async () => {
|
||||
// no post-initialization required
|
||||
const postInitialization: TSecretScanningFactoryPostInitialization<TBitbucketConnection> = async ({
|
||||
dataSourceId,
|
||||
credentials,
|
||||
connection,
|
||||
payload
|
||||
}) => {
|
||||
const { email, apiToken } = connection.credentials;
|
||||
const { webhookId } = credentials;
|
||||
|
||||
const authHeader = `Basic ${Buffer.from(`${email}:${apiToken}`).toString("base64")}`;
|
||||
|
||||
const cfg = getConfig();
|
||||
const newWebhookUrl = `${cfg.SITE_URL}/secret-scanning/webhooks/bitbucket?dataSourceId=${dataSourceId}`;
|
||||
|
||||
await request.put(
|
||||
`${IntegrationUrls.BITBUCKET_API_URL}/2.0/workspaces/${payload.config.workspaceSlug}/hooks/${webhookId}`,
|
||||
{
|
||||
description: "Infisical webhook for push events",
|
||||
url: newWebhookUrl,
|
||||
active: true,
|
||||
events: ["repo:push"]
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: authHeader,
|
||||
Accept: "application/json"
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const listRawResources: TSecretScanningFactoryListRawResources<TBitBucketDataSourceWithConnection> = async (
|
||||
// TODO(andrey): Hook this up
|
||||
const postDeletion: any = async ({ credentials, connection, payload }) => {
|
||||
const { email, apiToken } = connection.credentials;
|
||||
const { webhookId } = credentials;
|
||||
|
||||
const authHeader = `Basic ${Buffer.from(`${email}:${apiToken}`).toString("base64")}`;
|
||||
|
||||
await request.delete(
|
||||
`${IntegrationUrls.BITBUCKET_API_URL}/2.0/workspaces/${payload.config.workspaceSlug}/hooks/${webhookId}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: authHeader,
|
||||
Accept: "application/json"
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const listRawResources: TSecretScanningFactoryListRawResources<TBitbucketDataSourceWithConnection> = async (
|
||||
dataSource
|
||||
) => {
|
||||
const {
|
||||
connection,
|
||||
config: { includeRepos }
|
||||
config: { includeRepos, workspaceSlug }
|
||||
} = dataSource;
|
||||
|
||||
const repos = await listBitBucketRepositories(connection);
|
||||
const repos = await listBitbucketRepositories(connection, workspaceSlug);
|
||||
|
||||
const filteredRepos: typeof repos = [];
|
||||
if (includeRepos.includes("*")) {
|
||||
@@ -83,7 +134,7 @@ export const BitBucketSecretScanningFactory = () => {
|
||||
}));
|
||||
};
|
||||
|
||||
const getFullScanPath: TSecretScanningFactoryGetFullScanPath<TBitBucketDataSourceWithConnection> = async ({
|
||||
const getFullScanPath: TSecretScanningFactoryGetFullScanPath<TBitbucketDataSourceWithConnection> = async ({
|
||||
dataSource,
|
||||
resourceName,
|
||||
tempFolder
|
||||
@@ -97,10 +148,10 @@ export const BitBucketSecretScanningFactory = () => {
|
||||
const repoPath = join(tempFolder, "repo.git");
|
||||
|
||||
if (!GitHubRepositoryRegex.test(resourceName)) {
|
||||
throw new Error("Invalid BitBucket repository name");
|
||||
throw new Error("Invalid Bitbucket repository name");
|
||||
}
|
||||
|
||||
const { username } = await getBitBucketUser({ email, apiToken });
|
||||
const { username } = await getBitbucketUser({ email, apiToken });
|
||||
|
||||
await cloneRepository({
|
||||
cloneUrl: `https://${encodeURIComponent(username)}:${apiToken}@bitbucket.org/${resourceName}.git`,
|
||||
@@ -111,18 +162,18 @@ export const BitBucketSecretScanningFactory = () => {
|
||||
};
|
||||
|
||||
const getDiffScanResourcePayload: TSecretScanningFactoryGetDiffScanResourcePayload<
|
||||
TQueueBitBucketResourceDiffScan["payload"]
|
||||
> = ({ repository }) => {
|
||||
TQueueBitbucketResourceDiffScan["payload"]
|
||||
> = ({ repository, dataSourceId }) => {
|
||||
return {
|
||||
name: repository.full_name,
|
||||
externalId: repository.id.toString(),
|
||||
externalId: dataSourceId,
|
||||
type: SecretScanningResource.Repository
|
||||
};
|
||||
};
|
||||
|
||||
const getDiffScanFindingsPayload: TSecretScanningFactoryGetDiffScanFindingsPayload<
|
||||
TBitBucketDataSourceWithConnection,
|
||||
TQueueBitBucketResourceDiffScan["payload"]
|
||||
TBitbucketDataSourceWithConnection,
|
||||
TQueueBitbucketResourceDiffScan["payload"]
|
||||
> = async ({ dataSource, payload, resourceName, configPath }) => {
|
||||
const {
|
||||
connection: {
|
||||
@@ -130,81 +181,86 @@ export const BitBucketSecretScanningFactory = () => {
|
||||
}
|
||||
} = dataSource;
|
||||
|
||||
const { commits, repository } = payload;
|
||||
const { push, repository } = payload;
|
||||
|
||||
const allFindings: SecretMatch[] = [];
|
||||
|
||||
const authHeader = `Basic ${Buffer.from(`${email}:${apiToken}`).toString("base64")}`;
|
||||
|
||||
for (const commit of commits) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const { data: diffstat } = await request.get<{
|
||||
values: {
|
||||
status: "added" | "modified" | "removed" | "renamed";
|
||||
new?: { path: string };
|
||||
old?: { path: string };
|
||||
}[];
|
||||
}>(`https://api.bitbucket.org/2.0/repositories/${repository.full_name}/diffstat/${commit.id}`, {
|
||||
headers: {
|
||||
Authorization: authHeader,
|
||||
Accept: "application/json"
|
||||
}
|
||||
});
|
||||
for (const change of push.changes) {
|
||||
for (const commit of change.commits) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const { data: diffstat } = await request.get<{
|
||||
values: {
|
||||
status: "added" | "modified" | "removed" | "renamed";
|
||||
new?: { path: string };
|
||||
old?: { path: string };
|
||||
}[];
|
||||
}>(`${IntegrationUrls.BITBUCKET_API_URL}/2.0/repositories/${repository.full_name}/diffstat/${commit.hash}`, {
|
||||
headers: {
|
||||
Authorization: authHeader,
|
||||
Accept: "application/json"
|
||||
}
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-continue
|
||||
if (!diffstat.values) continue;
|
||||
// eslint-disable-next-line no-continue
|
||||
if (!diffstat.values) continue;
|
||||
|
||||
for (const file of diffstat.values) {
|
||||
if ((file.status === "added" || file.status === "modified") && file.new?.path) {
|
||||
const filePath = file.new.path;
|
||||
for (const file of diffstat.values) {
|
||||
if ((file.status === "added" || file.status === "modified") && file.new?.path) {
|
||||
const filePath = file.new.path;
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const { data: patch } = await request.get<string>(
|
||||
`https://api.bitbucket.org/2.0/repositories/${repository.full_name}/diff/${commit.id}`,
|
||||
{
|
||||
params: {
|
||||
path: filePath
|
||||
},
|
||||
headers: {
|
||||
Authorization: authHeader
|
||||
},
|
||||
responseType: "text"
|
||||
}
|
||||
);
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const { data: patch } = await request.get<string>(
|
||||
`https://api.bitbucket.org/2.0/repositories/${repository.full_name}/diff/${commit.hash}`,
|
||||
{
|
||||
params: {
|
||||
path: filePath
|
||||
},
|
||||
headers: {
|
||||
Authorization: authHeader
|
||||
},
|
||||
responseType: "text"
|
||||
}
|
||||
);
|
||||
|
||||
// eslint-disable-next-line no-continue
|
||||
if (!patch) continue;
|
||||
// eslint-disable-next-line no-continue
|
||||
if (!patch) continue;
|
||||
|
||||
// eslint-disable-next-line
|
||||
const findings = await scanContentAndGetFindings(replaceNonChangesWithNewlines(`\n${patch}`), configPath);
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const findings = await scanContentAndGetFindings(replaceNonChangesWithNewlines(`\n${patch}`), configPath);
|
||||
|
||||
const adjustedFindings = findings.map((finding) => {
|
||||
const startLine = convertPatchLineToFileLineNumber(patch, finding.StartLine);
|
||||
const endLine =
|
||||
finding.StartLine === finding.EndLine
|
||||
? startLine
|
||||
: convertPatchLineToFileLineNumber(patch, finding.EndLine);
|
||||
const startColumn = finding.StartColumn - 1; // subtract 1 for +
|
||||
const endColumn = finding.EndColumn - 1; // subtract 1 for +
|
||||
const adjustedFindings = findings.map((finding) => {
|
||||
const startLine = convertPatchLineToFileLineNumber(patch, finding.StartLine);
|
||||
const endLine =
|
||||
finding.StartLine === finding.EndLine
|
||||
? startLine
|
||||
: convertPatchLineToFileLineNumber(patch, finding.EndLine);
|
||||
const startColumn = finding.StartColumn - 1; // subtract 1 for +
|
||||
const endColumn = finding.EndColumn - 1; // subtract 1 for +
|
||||
const authorName = commit.author.user?.display_name || commit.author.raw.split(" <")[0];
|
||||
const emailMatch = commit.author.raw.match(/<(.*)>/);
|
||||
const authorEmail = emailMatch?.[1] ?? "";
|
||||
|
||||
return {
|
||||
...finding,
|
||||
StartLine: startLine,
|
||||
EndLine: endLine,
|
||||
StartColumn: startColumn,
|
||||
EndColumn: endColumn,
|
||||
File: filePath,
|
||||
Commit: commit.id,
|
||||
Author: commit.author.name,
|
||||
Email: commit.author.email ?? "",
|
||||
Message: commit.message,
|
||||
Fingerprint: `${commit.id}:${filePath}:${finding.RuleID}:${startLine}:${startColumn}`,
|
||||
Date: commit.timestamp,
|
||||
Link: `https://bitbucket.org/${resourceName}/src/${commit.id}/${filePath}#lines-${startLine}`
|
||||
};
|
||||
});
|
||||
return {
|
||||
...finding,
|
||||
StartLine: startLine,
|
||||
EndLine: endLine,
|
||||
StartColumn: startColumn,
|
||||
EndColumn: endColumn,
|
||||
File: filePath,
|
||||
Commit: commit.hash,
|
||||
Author: authorName,
|
||||
Email: authorEmail,
|
||||
Message: commit.message,
|
||||
Fingerprint: `${commit.hash}:${filePath}:${finding.RuleID}:${startLine}:${startColumn}`,
|
||||
Date: commit.date,
|
||||
Link: `https://bitbucket.org/${resourceName}/src/${commit.hash}/${filePath}#lines-${startLine}`
|
||||
};
|
||||
});
|
||||
|
||||
allFindings.push(...adjustedFindings);
|
||||
allFindings.push(...adjustedFindings);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+29
-20
@@ -15,7 +15,12 @@ import { SecretScanningDataSources } from "@app/lib/api-docs";
|
||||
import { GitHubRepositoryRegex } from "@app/lib/regex";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
|
||||
export const BitBucketDataSourceConfigSchema = z.object({
|
||||
export const BitbucketDataSourceConfigSchema = z.object({
|
||||
workspaceSlug: z
|
||||
.string()
|
||||
.min(1, "Workspace slug required")
|
||||
.max(128)
|
||||
.describe(SecretScanningDataSources.CONFIG.BITBUCKET.workspaceSlug),
|
||||
includeRepos: z
|
||||
.array(
|
||||
z
|
||||
@@ -30,58 +35,62 @@ export const BitBucketDataSourceConfigSchema = z.object({
|
||||
.describe(SecretScanningDataSources.CONFIG.BITBUCKET.includeRepos)
|
||||
});
|
||||
|
||||
export const BitBucketDataSourceSchema = BaseSecretScanningDataSourceSchema({
|
||||
type: SecretScanningDataSource.BitBucket,
|
||||
export const BitbucketDataSourceSchema = BaseSecretScanningDataSourceSchema({
|
||||
type: SecretScanningDataSource.Bitbucket,
|
||||
isConnectionRequired: true
|
||||
})
|
||||
.extend({
|
||||
config: BitBucketDataSourceConfigSchema
|
||||
config: BitbucketDataSourceConfigSchema
|
||||
})
|
||||
.describe(
|
||||
JSON.stringify({
|
||||
title: "BitBucket"
|
||||
title: "Bitbucket"
|
||||
})
|
||||
);
|
||||
|
||||
export const CreateBitBucketDataSourceSchema = BaseCreateSecretScanningDataSourceSchema({
|
||||
type: SecretScanningDataSource.BitBucket,
|
||||
export const CreateBitbucketDataSourceSchema = BaseCreateSecretScanningDataSourceSchema({
|
||||
type: SecretScanningDataSource.Bitbucket,
|
||||
isConnectionRequired: true
|
||||
})
|
||||
.extend({
|
||||
config: BitBucketDataSourceConfigSchema
|
||||
config: BitbucketDataSourceConfigSchema
|
||||
})
|
||||
.describe(
|
||||
JSON.stringify({
|
||||
title: "BitBucket"
|
||||
title: "Bitbucket"
|
||||
})
|
||||
);
|
||||
|
||||
export const UpdateBitBucketDataSourceSchema = BaseUpdateSecretScanningDataSourceSchema(
|
||||
SecretScanningDataSource.BitBucket
|
||||
export const UpdateBitbucketDataSourceSchema = BaseUpdateSecretScanningDataSourceSchema(
|
||||
SecretScanningDataSource.Bitbucket
|
||||
)
|
||||
.extend({
|
||||
config: BitBucketDataSourceConfigSchema.optional()
|
||||
config: BitbucketDataSourceConfigSchema.optional()
|
||||
})
|
||||
.describe(
|
||||
JSON.stringify({
|
||||
title: "BitBucket"
|
||||
title: "Bitbucket"
|
||||
})
|
||||
);
|
||||
|
||||
export const BitBucketDataSourceListItemSchema = z
|
||||
export const BitbucketDataSourceListItemSchema = z
|
||||
.object({
|
||||
name: z.literal("BitBucket"),
|
||||
connection: z.literal(AppConnection.BitBucket),
|
||||
type: z.literal(SecretScanningDataSource.BitBucket)
|
||||
name: z.literal("Bitbucket"),
|
||||
connection: z.literal(AppConnection.Bitbucket),
|
||||
type: z.literal(SecretScanningDataSource.Bitbucket)
|
||||
})
|
||||
.describe(
|
||||
JSON.stringify({
|
||||
title: "BitBucket"
|
||||
title: "Bitbucket"
|
||||
})
|
||||
);
|
||||
|
||||
export const BitBucketFindingSchema = BaseSecretScanningFindingSchema.extend({
|
||||
export const BitbucketFindingSchema = BaseSecretScanningFindingSchema.extend({
|
||||
resourceType: z.literal(SecretScanningResource.Repository),
|
||||
dataSourceType: z.literal(SecretScanningDataSource.BitBucket),
|
||||
dataSourceType: z.literal(SecretScanningDataSource.Bitbucket),
|
||||
details: GitRepositoryScanFindingDetailsSchema
|
||||
});
|
||||
|
||||
export const BitbucketDataSourceCredentialsSchema = z.object({
|
||||
webhookId: z.string()
|
||||
});
|
||||
|
||||
+17
-17
@@ -1,11 +1,9 @@
|
||||
import { PushEvent } from "@octokit/webhooks-types";
|
||||
|
||||
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 { TSecretScanningV2QueueServiceFactory } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-queue";
|
||||
import { logger } from "@app/lib/logger";
|
||||
|
||||
import { TBitBucketDataSource } from "./bitbucket-secret-scanning-types";
|
||||
import { TBitbucketDataSource, TBitbucketPushEvent } from "./bitbucket-secret-scanning-types";
|
||||
|
||||
export const bitBucketSecretScanningService = (
|
||||
secretScanningV2DAL: TSecretScanningV2DALFactory,
|
||||
@@ -14,18 +12,18 @@ export const bitBucketSecretScanningService = (
|
||||
const handleInstallationDeletedEvent = async (installationId: number) => {
|
||||
const dataSource = await secretScanningV2DAL.dataSources.findOne({
|
||||
externalId: String(installationId),
|
||||
type: SecretScanningDataSource.BitBucket
|
||||
type: SecretScanningDataSource.Bitbucket
|
||||
});
|
||||
|
||||
if (!dataSource) {
|
||||
logger.error(
|
||||
`secretScanningV2RemoveEvent: BitBucket - Could not find data source [installationId=${installationId}]`
|
||||
`secretScanningV2RemoveEvent: Bitbucket - Could not find data source [installationId=${installationId}]`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`secretScanningV2RemoveEvent: BitBucket - installation deleted [installationId=${installationId}] [dataSourceId=${dataSource.id}]`
|
||||
`secretScanningV2RemoveEvent: Bitbucket - installation deleted [installationId=${installationId}] [dataSourceId=${dataSource.id}]`
|
||||
);
|
||||
|
||||
await secretScanningV2DAL.dataSources.updateById(dataSource.id, {
|
||||
@@ -33,24 +31,26 @@ export const bitBucketSecretScanningService = (
|
||||
});
|
||||
};
|
||||
|
||||
const handlePushEvent = async (payload: PushEvent) => {
|
||||
const { commits, repository, installation } = payload;
|
||||
const handlePushEvent = async (payload: TBitbucketPushEvent & { dataSourceId: string }) => {
|
||||
const { push, repository } = payload;
|
||||
|
||||
if (!commits || !repository || !installation) {
|
||||
if (!push?.changes?.length || !repository?.workspace?.uuid) {
|
||||
logger.warn(
|
||||
`secretScanningV2PushEvent: BitBucket - Insufficient data [commits=${commits?.length ?? 0}] [repository=${repository.name}] [installationId=${installation?.id}]`
|
||||
`secretScanningV2PushEvent: Bitbucket - Insufficient data [changes=${
|
||||
push?.changes?.length ?? 0
|
||||
}] [repository=${repository?.name}] [workspaceUuid=${repository?.workspace?.uuid}]`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const dataSource = (await secretScanningV2DAL.dataSources.findOne({
|
||||
externalId: String(installation.id),
|
||||
type: SecretScanningDataSource.BitBucket
|
||||
})) as TBitBucketDataSource | undefined;
|
||||
externalId: payload.dataSourceId,
|
||||
type: SecretScanningDataSource.Bitbucket
|
||||
})) as TBitbucketDataSource | undefined;
|
||||
|
||||
if (!dataSource) {
|
||||
logger.error(
|
||||
`secretScanningV2PushEvent: BitBucket - Could not find data source [installationId=${installation.id}]`
|
||||
`secretScanningV2PushEvent: Bitbucket - Could not find data source [workspaceUuid=${repository.workspace.uuid}]`
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -62,20 +62,20 @@ export const bitBucketSecretScanningService = (
|
||||
|
||||
if (!isAutoScanEnabled) {
|
||||
logger.info(
|
||||
`secretScanningV2PushEvent: BitBucket - ignoring due to auto scan disabled [dataSourceId=${dataSource.id}] [installationId=${installation.id}]`
|
||||
`secretScanningV2PushEvent: Bitbucket - ignoring due to auto scan disabled [dataSourceId=${dataSource.id}] [workspaceUuid=${repository.workspace.uuid}]`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (includeRepos.includes("*") || includeRepos.includes(repository.full_name)) {
|
||||
await secretScanningV2Queue.queueResourceDiffScan({
|
||||
dataSourceType: SecretScanningDataSource.BitBucket,
|
||||
dataSourceType: SecretScanningDataSource.Bitbucket,
|
||||
payload,
|
||||
dataSourceId: dataSource.id
|
||||
});
|
||||
} else {
|
||||
logger.info(
|
||||
`secretScanningV2PushEvent: BitBucket - ignoring due to repository not being present in config [installationId=${installation.id}] [dataSourceId=${dataSource.id}]`
|
||||
`secretScanningV2PushEvent: Bitbucket - ignoring due to repository not being present in config [workspaceUuid=${repository.workspace.uuid}] [dataSourceId=${dataSource.id}]`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
+68
-15
@@ -1,31 +1,84 @@
|
||||
import { PushEvent } from "@octokit/webhooks-types";
|
||||
import { z } from "zod";
|
||||
|
||||
import { SecretScanningDataSource } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-enums";
|
||||
import { TBitBucketConnection } from "@app/services/app-connection/bitbucket";
|
||||
import { TBitbucketConnection } from "@app/services/app-connection/bitbucket";
|
||||
|
||||
import {
|
||||
BitBucketDataSourceListItemSchema,
|
||||
BitBucketDataSourceSchema,
|
||||
BitBucketFindingSchema,
|
||||
CreateBitBucketDataSourceSchema
|
||||
BitbucketDataSourceCredentialsSchema,
|
||||
BitbucketDataSourceListItemSchema,
|
||||
BitbucketDataSourceSchema,
|
||||
BitbucketFindingSchema,
|
||||
CreateBitbucketDataSourceSchema
|
||||
} from "./bitbucket-secret-scanning-schemas";
|
||||
|
||||
export type TBitBucketDataSource = z.infer<typeof BitBucketDataSourceSchema>;
|
||||
export type TBitbucketDataSource = z.infer<typeof BitbucketDataSourceSchema>;
|
||||
|
||||
export type TBitBucketDataSourceInput = z.infer<typeof CreateBitBucketDataSourceSchema>;
|
||||
export type TBitbucketDataSourceInput = z.infer<typeof CreateBitbucketDataSourceSchema>;
|
||||
|
||||
export type TBitBucketDataSourceListItem = z.infer<typeof BitBucketDataSourceListItemSchema>;
|
||||
export type TBitbucketDataSourceListItem = z.infer<typeof BitbucketDataSourceListItemSchema>;
|
||||
|
||||
export type TBitBucketFinding = z.infer<typeof BitBucketFindingSchema>;
|
||||
export type TBitbucketDataSourceCredentials = z.infer<typeof BitbucketDataSourceCredentialsSchema>;
|
||||
|
||||
export type TBitBucketDataSourceWithConnection = TBitBucketDataSource & {
|
||||
connection: TBitBucketConnection;
|
||||
export type TBitbucketFinding = z.infer<typeof BitbucketFindingSchema>;
|
||||
|
||||
export type TBitbucketDataSourceWithConnection = TBitbucketDataSource & {
|
||||
connection: TBitbucketConnection;
|
||||
};
|
||||
|
||||
export type TQueueBitBucketResourceDiffScan = {
|
||||
dataSourceType: SecretScanningDataSource.BitBucket;
|
||||
payload: PushEvent;
|
||||
export type TBitbucketPushEventRepository = {
|
||||
full_name: string;
|
||||
name: string;
|
||||
workspace: {
|
||||
slug: string;
|
||||
uuid: string;
|
||||
};
|
||||
uuid: string;
|
||||
};
|
||||
|
||||
export type TBitbucketPushEventCommit = {
|
||||
hash: string;
|
||||
message: string;
|
||||
author: {
|
||||
raw: string;
|
||||
user?: {
|
||||
display_name: string;
|
||||
uuid: string;
|
||||
nickname: string;
|
||||
};
|
||||
};
|
||||
date: string;
|
||||
};
|
||||
|
||||
export type TBitbucketPushEventChange = {
|
||||
new?: {
|
||||
name: string;
|
||||
type: string;
|
||||
};
|
||||
old?: {
|
||||
name: string;
|
||||
type: string;
|
||||
};
|
||||
created: boolean;
|
||||
closed: boolean;
|
||||
forced: boolean;
|
||||
commits: TBitbucketPushEventCommit[];
|
||||
};
|
||||
|
||||
export type TBitbucketPushEvent = {
|
||||
push: {
|
||||
changes: TBitbucketPushEventChange[];
|
||||
};
|
||||
repository: TBitbucketPushEventRepository;
|
||||
actor: {
|
||||
display_name: string;
|
||||
uuid: string;
|
||||
nickname: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type TQueueBitbucketResourceDiffScan = {
|
||||
dataSourceType: SecretScanningDataSource.Bitbucket;
|
||||
payload: TBitbucketPushEvent & { dataSourceId: string };
|
||||
dataSourceId: string;
|
||||
resourceId: string;
|
||||
scanId: string;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export enum SecretScanningDataSource {
|
||||
GitHub = "github",
|
||||
BitBucket = "bitbucket"
|
||||
Bitbucket = "bitbucket"
|
||||
}
|
||||
|
||||
export enum SecretScanningScanStatus {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BitBucketSecretScanningFactory } from "@app/ee/services/secret-scanning-v2/bitbucket/bitbucket-secret-scanning-factory";
|
||||
import { BitbucketSecretScanningFactory } from "@app/ee/services/secret-scanning-v2/bitbucket/bitbucket-secret-scanning-factory";
|
||||
import { GitHubSecretScanningFactory } from "@app/ee/services/secret-scanning-v2/github/github-secret-scanning-factory";
|
||||
|
||||
import { SecretScanningDataSource } from "./secret-scanning-v2-enums";
|
||||
@@ -17,5 +17,5 @@ type TSecretScanningFactoryImplementation = TSecretScanningFactory<
|
||||
|
||||
export const SECRET_SCANNING_FACTORY_MAP: Record<SecretScanningDataSource, TSecretScanningFactoryImplementation> = {
|
||||
[SecretScanningDataSource.GitHub]: GitHubSecretScanningFactory as TSecretScanningFactoryImplementation,
|
||||
[SecretScanningDataSource.BitBucket]: BitBucketSecretScanningFactory as TSecretScanningFactoryImplementation
|
||||
[SecretScanningDataSource.Bitbucket]: BitbucketSecretScanningFactory as TSecretScanningFactoryImplementation
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ import { TCloneRepository, TGetFindingsPayload, TSecretScanningDataSourceListIte
|
||||
|
||||
const SECRET_SCANNING_SOURCE_LIST_OPTIONS: Record<SecretScanningDataSource, TSecretScanningDataSourceListItem> = {
|
||||
[SecretScanningDataSource.GitHub]: GITHUB_SECRET_SCANNING_DATA_SOURCE_LIST_OPTION,
|
||||
[SecretScanningDataSource.BitBucket]: BITBUCKET_SECRET_SCANNING_DATA_SOURCE_LIST_OPTION
|
||||
[SecretScanningDataSource.Bitbucket]: BITBUCKET_SECRET_SCANNING_DATA_SOURCE_LIST_OPTION
|
||||
};
|
||||
|
||||
export const listSecretScanningDataSourceOptions = () => {
|
||||
|
||||
@@ -3,15 +3,15 @@ import { AppConnection } from "@app/services/app-connection/app-connection-enums
|
||||
|
||||
export const SECRET_SCANNING_DATA_SOURCE_NAME_MAP: Record<SecretScanningDataSource, string> = {
|
||||
[SecretScanningDataSource.GitHub]: "GitHub",
|
||||
[SecretScanningDataSource.BitBucket]: "BitBucket"
|
||||
[SecretScanningDataSource.Bitbucket]: "Bitbucket"
|
||||
};
|
||||
|
||||
export const SECRET_SCANNING_DATA_SOURCE_CONNECTION_MAP: Record<SecretScanningDataSource, AppConnection> = {
|
||||
[SecretScanningDataSource.GitHub]: AppConnection.GitHubRadar,
|
||||
[SecretScanningDataSource.BitBucket]: AppConnection.BitBucket
|
||||
[SecretScanningDataSource.Bitbucket]: AppConnection.Bitbucket
|
||||
};
|
||||
|
||||
export const AUTO_SYNC_DESCRIPTION_HELPER: Record<SecretScanningDataSource, { verb: string; noun: string }> = {
|
||||
[SecretScanningDataSource.GitHub]: { verb: "push", noun: "repositories" },
|
||||
[SecretScanningDataSource.BitBucket]: { verb: "push", noun: "repositories" }
|
||||
[SecretScanningDataSource.Bitbucket]: { verb: "push", noun: "repositories" }
|
||||
};
|
||||
|
||||
@@ -5,12 +5,13 @@ import {
|
||||
TSecretScanningScans
|
||||
} from "@app/db/schemas";
|
||||
import {
|
||||
TBitBucketDataSource,
|
||||
TBitBucketDataSourceInput,
|
||||
TBitBucketDataSourceListItem,
|
||||
TBitBucketDataSourceWithConnection,
|
||||
TBitBucketFinding,
|
||||
TQueueBitBucketResourceDiffScan
|
||||
TBitbucketDataSource,
|
||||
TBitbucketDataSourceCredentials,
|
||||
TBitbucketDataSourceInput,
|
||||
TBitbucketDataSourceListItem,
|
||||
TBitbucketDataSourceWithConnection,
|
||||
TBitbucketFinding,
|
||||
TQueueBitbucketResourceDiffScan
|
||||
} from "@app/ee/services/secret-scanning-v2/bitbucket";
|
||||
import {
|
||||
TGitHubDataSource,
|
||||
@@ -27,7 +28,7 @@ import {
|
||||
SecretScanningScanStatus
|
||||
} from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-enums";
|
||||
|
||||
export type TSecretScanningDataSource = TGitHubDataSource | TBitBucketDataSource;
|
||||
export type TSecretScanningDataSource = TGitHubDataSource | TBitbucketDataSource;
|
||||
|
||||
export type TSecretScanningDataSourceWithDetails = TSecretScanningDataSource & {
|
||||
lastScannedAt?: Date | null;
|
||||
@@ -51,13 +52,15 @@ export type TSecretScanningScanWithDetails = TSecretScanningScans & {
|
||||
|
||||
export type TSecretScanningDataSourceWithConnection =
|
||||
| TGitHubDataSourceWithConnection
|
||||
| TBitBucketDataSourceWithConnection;
|
||||
| TBitbucketDataSourceWithConnection;
|
||||
|
||||
export type TSecretScanningDataSourceInput = TGitHubDataSourceInput | TBitBucketDataSourceInput;
|
||||
export type TSecretScanningDataSourceInput = TGitHubDataSourceInput | TBitbucketDataSourceInput;
|
||||
|
||||
export type TSecretScanningDataSourceListItem = TGitHubDataSourceListItem | TBitBucketDataSourceListItem;
|
||||
export type TSecretScanningDataSourceListItem = TGitHubDataSourceListItem | TBitbucketDataSourceListItem;
|
||||
|
||||
export type TSecretScanningFinding = TGitHubFinding | TBitBucketFinding;
|
||||
export type TDataSourceCredentialsSchema = TBitbucketDataSourceCredentials | undefined;
|
||||
|
||||
export type TSecretScanningFinding = TGitHubFinding | TBitbucketFinding;
|
||||
|
||||
export type TListSecretScanningDataSourcesByProjectId = {
|
||||
projectId: string;
|
||||
@@ -109,7 +112,7 @@ export type TQueueSecretScanningDataSourceFullScan = {
|
||||
scanId: string;
|
||||
};
|
||||
|
||||
export type TQueueSecretScanningResourceDiffScan = TQueueGitHubResourceDiffScan | TQueueBitBucketResourceDiffScan;
|
||||
export type TQueueSecretScanningResourceDiffScan = TQueueGitHubResourceDiffScan | TQueueBitbucketResourceDiffScan;
|
||||
|
||||
export type TQueueSecretScanningSendNotification = {
|
||||
dataSource: TSecretScanningDataSources;
|
||||
@@ -149,7 +152,7 @@ export type TSecretScanningDataSourceRaw = NonNullable<
|
||||
|
||||
export type TSecretScanningFactoryInitialize<
|
||||
T extends TSecretScanningDataSourceWithConnection["connection"] | undefined = undefined,
|
||||
C extends TSecretScanningDataSourceCredentials = undefined
|
||||
C extends TSecretScanningDataSourceCredentials = TDataSourceCredentialsSchema
|
||||
> = (
|
||||
params: {
|
||||
payload: TCreateSecretScanningDataSourceDTO;
|
||||
@@ -161,7 +164,7 @@ export type TSecretScanningFactoryInitialize<
|
||||
|
||||
export type TSecretScanningFactoryPostInitialization<
|
||||
T extends TSecretScanningDataSourceWithConnection["connection"] | undefined = undefined,
|
||||
C extends TSecretScanningDataSourceCredentials = undefined
|
||||
C extends TSecretScanningDataSourceCredentials = TDataSourceCredentialsSchema
|
||||
> = (params: {
|
||||
payload: TCreateSecretScanningDataSourceDTO;
|
||||
connection: T;
|
||||
@@ -196,4 +199,4 @@ export type TUpsertSecretScanningConfigDTO = {
|
||||
content: string | null;
|
||||
};
|
||||
|
||||
export type TSecretScanningDataSourceCredentials = undefined;
|
||||
export type TSecretScanningDataSourceCredentials = TDataSourceCredentialsSchema;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { BitBucketDataSourceSchema, BitBucketFindingSchema } from "@app/ee/services/secret-scanning-v2/bitbucket";
|
||||
import { BitbucketDataSourceSchema, BitbucketFindingSchema } from "@app/ee/services/secret-scanning-v2/bitbucket";
|
||||
import { GitHubDataSourceSchema, GitHubFindingSchema } from "@app/ee/services/secret-scanning-v2/github";
|
||||
|
||||
export const SecretScanningDataSourceSchema = z.discriminatedUnion("type", [
|
||||
GitHubDataSourceSchema,
|
||||
BitBucketDataSourceSchema
|
||||
BitbucketDataSourceSchema
|
||||
]);
|
||||
|
||||
export const SecretScanningFindingSchema = z.discriminatedUnion("dataSourceType", [
|
||||
GitHubFindingSchema,
|
||||
BitBucketFindingSchema
|
||||
BitbucketFindingSchema
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user