mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Make webhooks work, add workspace selection, rename BitBucket to
Bitbucket
This commit is contained in:
@@ -1,16 +1,16 @@
|
|||||||
import { registerSecretScanningEndpoints } from "@app/ee/routes/v2/secret-scanning-v2-routers/secret-scanning-v2-endpoints";
|
import { registerSecretScanningEndpoints } from "@app/ee/routes/v2/secret-scanning-v2-routers/secret-scanning-v2-endpoints";
|
||||||
import {
|
import {
|
||||||
BitBucketDataSourceSchema,
|
BitbucketDataSourceSchema,
|
||||||
CreateBitBucketDataSourceSchema,
|
CreateBitbucketDataSourceSchema,
|
||||||
UpdateBitBucketDataSourceSchema
|
UpdateBitbucketDataSourceSchema
|
||||||
} from "@app/ee/services/secret-scanning-v2/bitbucket";
|
} from "@app/ee/services/secret-scanning-v2/bitbucket";
|
||||||
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";
|
||||||
|
|
||||||
export const registerBitBucketSecretScanningRouter = async (server: FastifyZodProvider) =>
|
export const registerBitbucketSecretScanningRouter = async (server: FastifyZodProvider) =>
|
||||||
registerSecretScanningEndpoints({
|
registerSecretScanningEndpoints({
|
||||||
type: SecretScanningDataSource.BitBucket,
|
type: SecretScanningDataSource.Bitbucket,
|
||||||
server,
|
server,
|
||||||
responseSchema: BitBucketDataSourceSchema,
|
responseSchema: BitbucketDataSourceSchema,
|
||||||
createSchema: CreateBitBucketDataSourceSchema,
|
createSchema: CreateBitbucketDataSourceSchema,
|
||||||
updateSchema: UpdateBitBucketDataSourceSchema
|
updateSchema: UpdateBitbucketDataSourceSchema
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
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 { registerBitBucketSecretScanningRouter } from "./bitbucket-secret-scanning-router";
|
import { registerBitbucketSecretScanningRouter } from "./bitbucket-secret-scanning-router";
|
||||||
import { registerGitHubSecretScanningRouter } from "./github-secret-scanning-router";
|
import { registerGitHubSecretScanningRouter } from "./github-secret-scanning-router";
|
||||||
|
|
||||||
export * from "./secret-scanning-v2-router";
|
export * from "./secret-scanning-v2-router";
|
||||||
@@ -10,5 +10,5 @@ export const SECRET_SCANNING_REGISTER_ROUTER_MAP: Record<
|
|||||||
(server: FastifyZodProvider) => Promise<void>
|
(server: FastifyZodProvider) => Promise<void>
|
||||||
> = {
|
> = {
|
||||||
[SecretScanningDataSource.GitHub]: registerGitHubSecretScanningRouter,
|
[SecretScanningDataSource.GitHub]: registerGitHubSecretScanningRouter,
|
||||||
[SecretScanningDataSource.BitBucket]: registerBitBucketSecretScanningRouter
|
[SecretScanningDataSource.Bitbucket]: registerBitbucketSecretScanningRouter
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { SecretScanningConfigsSchema } from "@app/db/schemas";
|
import { SecretScanningConfigsSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
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 { GitHubDataSourceListItemSchema } from "@app/ee/services/secret-scanning-v2/github";
|
||||||
import {
|
import {
|
||||||
SecretScanningFindingStatus,
|
SecretScanningFindingStatus,
|
||||||
@@ -24,7 +24,7 @@ import { AuthMode } from "@app/services/auth/auth-type";
|
|||||||
|
|
||||||
const SecretScanningDataSourceOptionsSchema = z.discriminatedUnion("type", [
|
const SecretScanningDataSourceOptionsSchema = z.discriminatedUnion("type", [
|
||||||
GitHubDataSourceListItemSchema,
|
GitHubDataSourceListItemSchema,
|
||||||
BitBucketDataSourceListItemSchema
|
BitbucketDataSourceListItemSchema
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const registerSecretScanningV2Router = async (server: FastifyZodProvider) => {
|
export const registerSecretScanningV2Router = async (server: FastifyZodProvider) => {
|
||||||
|
|||||||
@@ -18,33 +18,33 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
|||||||
environmentsUsed: 0,
|
environmentsUsed: 0,
|
||||||
identityLimit: null,
|
identityLimit: null,
|
||||||
identitiesUsed: 0,
|
identitiesUsed: 0,
|
||||||
dynamicSecret: false,
|
dynamicSecret: true,
|
||||||
secretVersioning: true,
|
secretVersioning: true,
|
||||||
pitRecovery: false,
|
pitRecovery: true,
|
||||||
ipAllowlisting: false,
|
ipAllowlisting: true,
|
||||||
rbac: false,
|
rbac: true,
|
||||||
githubOrgSync: false,
|
githubOrgSync: true,
|
||||||
customRateLimits: false,
|
customRateLimits: false,
|
||||||
customAlerts: false,
|
customAlerts: true,
|
||||||
secretAccessInsights: false,
|
secretAccessInsights: true,
|
||||||
auditLogs: false,
|
auditLogs: true,
|
||||||
auditLogsRetentionDays: 0,
|
auditLogsRetentionDays: 3,
|
||||||
auditLogStreams: false,
|
auditLogStreams: true,
|
||||||
auditLogStreamLimit: 3,
|
auditLogStreamLimit: 3,
|
||||||
samlSSO: false,
|
samlSSO: true,
|
||||||
hsm: false,
|
hsm: true,
|
||||||
oidcSSO: false,
|
oidcSSO: true,
|
||||||
scim: false,
|
scim: true,
|
||||||
ldap: false,
|
ldap: true,
|
||||||
groups: false,
|
groups: true,
|
||||||
status: null,
|
status: null,
|
||||||
trial_end: null,
|
trial_end: null,
|
||||||
has_used_trial: true,
|
has_used_trial: true,
|
||||||
secretApproval: false,
|
secretApproval: true,
|
||||||
secretRotation: false,
|
secretRotation: true,
|
||||||
caCrl: false,
|
caCrl: true,
|
||||||
instanceUserManagement: false,
|
instanceUserManagement: true,
|
||||||
externalKms: false,
|
externalKms: true,
|
||||||
rateLimits: {
|
rateLimits: {
|
||||||
readLimit: 60,
|
readLimit: 60,
|
||||||
writeLimit: 200,
|
writeLimit: 200,
|
||||||
@@ -52,13 +52,13 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
|||||||
},
|
},
|
||||||
pkiEst: false,
|
pkiEst: false,
|
||||||
enforceMfa: false,
|
enforceMfa: false,
|
||||||
projectTemplates: false,
|
projectTemplates: true,
|
||||||
kmip: false,
|
kmip: true,
|
||||||
gateway: false,
|
gateway: true,
|
||||||
sshHostGroups: false,
|
sshHostGroups: true,
|
||||||
secretScanning: false,
|
enterpriseAppConnections: true,
|
||||||
enterpriseSecretSyncs: false,
|
enterpriseSecretSyncs: true,
|
||||||
enterpriseAppConnections: false
|
secretScanning: true
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setupLicenseRequestWithStore = (
|
export const setupLicenseRequestWithStore = (
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { TSecretScanningDataSourceListItem } from "@app/ee/services/secret-scann
|
|||||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
|
|
||||||
export const BITBUCKET_SECRET_SCANNING_DATA_SOURCE_LIST_OPTION: TSecretScanningDataSourceListItem = {
|
export const BITBUCKET_SECRET_SCANNING_DATA_SOURCE_LIST_OPTION: TSecretScanningDataSourceListItem = {
|
||||||
name: "BitBucket",
|
name: "Bitbucket",
|
||||||
type: SecretScanningDataSource.BitBucket,
|
type: SecretScanningDataSource.Bitbucket,
|
||||||
connection: AppConnection.BitBucket
|
connection: AppConnection.Bitbucket
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ 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";
|
||||||
import { SecretMatch } from "@app/ee/services/secret-scanning/secret-scanning-queue/secret-scanning-queue-types";
|
import { SecretMatch } from "@app/ee/services/secret-scanning/secret-scanning-queue/secret-scanning-queue-types";
|
||||||
import {
|
import {
|
||||||
SecretScanningDataSource,
|
|
||||||
SecretScanningFindingSeverity,
|
SecretScanningFindingSeverity,
|
||||||
SecretScanningResource
|
SecretScanningResource
|
||||||
} from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-enums";
|
} from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-enums";
|
||||||
@@ -20,54 +19,106 @@ import {
|
|||||||
TSecretScanningFactoryListRawResources,
|
TSecretScanningFactoryListRawResources,
|
||||||
TSecretScanningFactoryPostInitialization
|
TSecretScanningFactoryPostInitialization
|
||||||
} from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-types";
|
} 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 { request } from "@app/lib/config/request";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
|
||||||
import { titleCaseToCamelCase } from "@app/lib/fn";
|
import { titleCaseToCamelCase } from "@app/lib/fn";
|
||||||
import { GitHubRepositoryRegex } from "@app/lib/regex";
|
import { GitHubRepositoryRegex } from "@app/lib/regex";
|
||||||
import {
|
import {
|
||||||
getBitBucketUser,
|
getBitbucketUser,
|
||||||
listBitBucketRepositories,
|
listBitbucketRepositories,
|
||||||
TBitBucketConnection
|
TBitbucketConnection
|
||||||
} from "@app/services/app-connection/bitbucket";
|
} 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 = () => {
|
export const BitbucketSecretScanningFactory = () => {
|
||||||
const initialize: TSecretScanningFactoryInitialize<TBitBucketConnection> = async (
|
const initialize: TSecretScanningFactoryInitialize<TBitbucketConnection> = async (
|
||||||
{ connection, secretScanningV2DAL },
|
{ connection, payload },
|
||||||
callback
|
callback
|
||||||
) => {
|
) => {
|
||||||
// TODO(andrey): Swap for something proper
|
const { email, apiToken } = connection.credentials;
|
||||||
const externalId = connection.credentials.email;
|
const authHeader = `Basic ${Buffer.from(`${email}:${apiToken}`).toString("base64")}`;
|
||||||
|
|
||||||
const existingDataSource = await secretScanningV2DAL.dataSources.findOne({
|
const { data } = await request.post<{ uuid: string }>(
|
||||||
externalId,
|
`${IntegrationUrls.BITBUCKET_API_URL}/2.0/workspaces/${payload.config.workspaceSlug}/hooks`,
|
||||||
type: SecretScanningDataSource.BitBucket
|
{
|
||||||
});
|
description: "Infisical webhook for push events",
|
||||||
|
url: `https://tunnel.util.lol/secret-scanning/webhooks/bitbucket`, // TODO(andrey): Swap to ${cfg.SITE_URL}
|
||||||
if (existingDataSource)
|
active: true,
|
||||||
throw new BadRequestError({
|
events: ["repo:push"]
|
||||||
message: `A Data Source already exists for this BitBucket Radar Connection in the Project with ID "${existingDataSource.projectId}"`
|
},
|
||||||
});
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: authHeader,
|
||||||
|
Accept: "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return callback({
|
return callback({
|
||||||
externalId
|
credentials: { webhookId: data.uuid }
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const postInitialization: TSecretScanningFactoryPostInitialization<TBitBucketConnection> = async () => {
|
const postInitialization: TSecretScanningFactoryPostInitialization<TBitbucketConnection> = async ({
|
||||||
// no post-initialization required
|
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
|
dataSource
|
||||||
) => {
|
) => {
|
||||||
const {
|
const {
|
||||||
connection,
|
connection,
|
||||||
config: { includeRepos }
|
config: { includeRepos, workspaceSlug }
|
||||||
} = dataSource;
|
} = dataSource;
|
||||||
|
|
||||||
const repos = await listBitBucketRepositories(connection);
|
const repos = await listBitbucketRepositories(connection, workspaceSlug);
|
||||||
|
|
||||||
const filteredRepos: typeof repos = [];
|
const filteredRepos: typeof repos = [];
|
||||||
if (includeRepos.includes("*")) {
|
if (includeRepos.includes("*")) {
|
||||||
@@ -83,7 +134,7 @@ export const BitBucketSecretScanningFactory = () => {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const getFullScanPath: TSecretScanningFactoryGetFullScanPath<TBitBucketDataSourceWithConnection> = async ({
|
const getFullScanPath: TSecretScanningFactoryGetFullScanPath<TBitbucketDataSourceWithConnection> = async ({
|
||||||
dataSource,
|
dataSource,
|
||||||
resourceName,
|
resourceName,
|
||||||
tempFolder
|
tempFolder
|
||||||
@@ -97,10 +148,10 @@ export const BitBucketSecretScanningFactory = () => {
|
|||||||
const repoPath = join(tempFolder, "repo.git");
|
const repoPath = join(tempFolder, "repo.git");
|
||||||
|
|
||||||
if (!GitHubRepositoryRegex.test(resourceName)) {
|
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({
|
await cloneRepository({
|
||||||
cloneUrl: `https://${encodeURIComponent(username)}:${apiToken}@bitbucket.org/${resourceName}.git`,
|
cloneUrl: `https://${encodeURIComponent(username)}:${apiToken}@bitbucket.org/${resourceName}.git`,
|
||||||
@@ -111,18 +162,18 @@ export const BitBucketSecretScanningFactory = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getDiffScanResourcePayload: TSecretScanningFactoryGetDiffScanResourcePayload<
|
const getDiffScanResourcePayload: TSecretScanningFactoryGetDiffScanResourcePayload<
|
||||||
TQueueBitBucketResourceDiffScan["payload"]
|
TQueueBitbucketResourceDiffScan["payload"]
|
||||||
> = ({ repository }) => {
|
> = ({ repository, dataSourceId }) => {
|
||||||
return {
|
return {
|
||||||
name: repository.full_name,
|
name: repository.full_name,
|
||||||
externalId: repository.id.toString(),
|
externalId: dataSourceId,
|
||||||
type: SecretScanningResource.Repository
|
type: SecretScanningResource.Repository
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDiffScanFindingsPayload: TSecretScanningFactoryGetDiffScanFindingsPayload<
|
const getDiffScanFindingsPayload: TSecretScanningFactoryGetDiffScanFindingsPayload<
|
||||||
TBitBucketDataSourceWithConnection,
|
TBitbucketDataSourceWithConnection,
|
||||||
TQueueBitBucketResourceDiffScan["payload"]
|
TQueueBitbucketResourceDiffScan["payload"]
|
||||||
> = async ({ dataSource, payload, resourceName, configPath }) => {
|
> = async ({ dataSource, payload, resourceName, configPath }) => {
|
||||||
const {
|
const {
|
||||||
connection: {
|
connection: {
|
||||||
@@ -130,81 +181,86 @@ export const BitBucketSecretScanningFactory = () => {
|
|||||||
}
|
}
|
||||||
} = dataSource;
|
} = dataSource;
|
||||||
|
|
||||||
const { commits, repository } = payload;
|
const { push, repository } = payload;
|
||||||
|
|
||||||
const allFindings: SecretMatch[] = [];
|
const allFindings: SecretMatch[] = [];
|
||||||
|
|
||||||
const authHeader = `Basic ${Buffer.from(`${email}:${apiToken}`).toString("base64")}`;
|
const authHeader = `Basic ${Buffer.from(`${email}:${apiToken}`).toString("base64")}`;
|
||||||
|
|
||||||
for (const commit of commits) {
|
for (const change of push.changes) {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
for (const commit of change.commits) {
|
||||||
const { data: diffstat } = await request.get<{
|
// eslint-disable-next-line no-await-in-loop
|
||||||
values: {
|
const { data: diffstat } = await request.get<{
|
||||||
status: "added" | "modified" | "removed" | "renamed";
|
values: {
|
||||||
new?: { path: string };
|
status: "added" | "modified" | "removed" | "renamed";
|
||||||
old?: { path: string };
|
new?: { path: string };
|
||||||
}[];
|
old?: { path: string };
|
||||||
}>(`https://api.bitbucket.org/2.0/repositories/${repository.full_name}/diffstat/${commit.id}`, {
|
}[];
|
||||||
headers: {
|
}>(`${IntegrationUrls.BITBUCKET_API_URL}/2.0/repositories/${repository.full_name}/diffstat/${commit.hash}`, {
|
||||||
Authorization: authHeader,
|
headers: {
|
||||||
Accept: "application/json"
|
Authorization: authHeader,
|
||||||
}
|
Accept: "application/json"
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line no-continue
|
// eslint-disable-next-line no-continue
|
||||||
if (!diffstat.values) continue;
|
if (!diffstat.values) continue;
|
||||||
|
|
||||||
for (const file of diffstat.values) {
|
for (const file of diffstat.values) {
|
||||||
if ((file.status === "added" || file.status === "modified") && file.new?.path) {
|
if ((file.status === "added" || file.status === "modified") && file.new?.path) {
|
||||||
const filePath = file.new.path;
|
const filePath = file.new.path;
|
||||||
|
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
const { data: patch } = await request.get<string>(
|
const { data: patch } = await request.get<string>(
|
||||||
`https://api.bitbucket.org/2.0/repositories/${repository.full_name}/diff/${commit.id}`,
|
`https://api.bitbucket.org/2.0/repositories/${repository.full_name}/diff/${commit.hash}`,
|
||||||
{
|
{
|
||||||
params: {
|
params: {
|
||||||
path: filePath
|
path: filePath
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: authHeader
|
Authorization: authHeader
|
||||||
},
|
},
|
||||||
responseType: "text"
|
responseType: "text"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// eslint-disable-next-line no-continue
|
// eslint-disable-next-line no-continue
|
||||||
if (!patch) continue;
|
if (!patch) continue;
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line no-await-in-loop
|
||||||
const findings = await scanContentAndGetFindings(replaceNonChangesWithNewlines(`\n${patch}`), configPath);
|
const findings = await scanContentAndGetFindings(replaceNonChangesWithNewlines(`\n${patch}`), configPath);
|
||||||
|
|
||||||
const adjustedFindings = findings.map((finding) => {
|
const adjustedFindings = findings.map((finding) => {
|
||||||
const startLine = convertPatchLineToFileLineNumber(patch, finding.StartLine);
|
const startLine = convertPatchLineToFileLineNumber(patch, finding.StartLine);
|
||||||
const endLine =
|
const endLine =
|
||||||
finding.StartLine === finding.EndLine
|
finding.StartLine === finding.EndLine
|
||||||
? startLine
|
? startLine
|
||||||
: convertPatchLineToFileLineNumber(patch, finding.EndLine);
|
: convertPatchLineToFileLineNumber(patch, finding.EndLine);
|
||||||
const startColumn = finding.StartColumn - 1; // subtract 1 for +
|
const startColumn = finding.StartColumn - 1; // subtract 1 for +
|
||||||
const endColumn = finding.EndColumn - 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 {
|
return {
|
||||||
...finding,
|
...finding,
|
||||||
StartLine: startLine,
|
StartLine: startLine,
|
||||||
EndLine: endLine,
|
EndLine: endLine,
|
||||||
StartColumn: startColumn,
|
StartColumn: startColumn,
|
||||||
EndColumn: endColumn,
|
EndColumn: endColumn,
|
||||||
File: filePath,
|
File: filePath,
|
||||||
Commit: commit.id,
|
Commit: commit.hash,
|
||||||
Author: commit.author.name,
|
Author: authorName,
|
||||||
Email: commit.author.email ?? "",
|
Email: authorEmail,
|
||||||
Message: commit.message,
|
Message: commit.message,
|
||||||
Fingerprint: `${commit.id}:${filePath}:${finding.RuleID}:${startLine}:${startColumn}`,
|
Fingerprint: `${commit.hash}:${filePath}:${finding.RuleID}:${startLine}:${startColumn}`,
|
||||||
Date: commit.timestamp,
|
Date: commit.date,
|
||||||
Link: `https://bitbucket.org/${resourceName}/src/${commit.id}/${filePath}#lines-${startLine}`
|
Link: `https://bitbucket.org/${resourceName}/src/${commit.hash}/${filePath}#lines-${startLine}`
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
allFindings.push(...adjustedFindings);
|
allFindings.push(...adjustedFindings);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,12 @@ import { SecretScanningDataSources } from "@app/lib/api-docs";
|
|||||||
import { GitHubRepositoryRegex } from "@app/lib/regex";
|
import { GitHubRepositoryRegex } from "@app/lib/regex";
|
||||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
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
|
includeRepos: z
|
||||||
.array(
|
.array(
|
||||||
z
|
z
|
||||||
@@ -30,58 +35,62 @@ export const BitBucketDataSourceConfigSchema = z.object({
|
|||||||
.describe(SecretScanningDataSources.CONFIG.BITBUCKET.includeRepos)
|
.describe(SecretScanningDataSources.CONFIG.BITBUCKET.includeRepos)
|
||||||
});
|
});
|
||||||
|
|
||||||
export const BitBucketDataSourceSchema = BaseSecretScanningDataSourceSchema({
|
export const BitbucketDataSourceSchema = BaseSecretScanningDataSourceSchema({
|
||||||
type: SecretScanningDataSource.BitBucket,
|
type: SecretScanningDataSource.Bitbucket,
|
||||||
isConnectionRequired: true
|
isConnectionRequired: true
|
||||||
})
|
})
|
||||||
.extend({
|
.extend({
|
||||||
config: BitBucketDataSourceConfigSchema
|
config: BitbucketDataSourceConfigSchema
|
||||||
})
|
})
|
||||||
.describe(
|
.describe(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
title: "BitBucket"
|
title: "Bitbucket"
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
export const CreateBitBucketDataSourceSchema = BaseCreateSecretScanningDataSourceSchema({
|
export const CreateBitbucketDataSourceSchema = BaseCreateSecretScanningDataSourceSchema({
|
||||||
type: SecretScanningDataSource.BitBucket,
|
type: SecretScanningDataSource.Bitbucket,
|
||||||
isConnectionRequired: true
|
isConnectionRequired: true
|
||||||
})
|
})
|
||||||
.extend({
|
.extend({
|
||||||
config: BitBucketDataSourceConfigSchema
|
config: BitbucketDataSourceConfigSchema
|
||||||
})
|
})
|
||||||
.describe(
|
.describe(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
title: "BitBucket"
|
title: "Bitbucket"
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
export const UpdateBitBucketDataSourceSchema = BaseUpdateSecretScanningDataSourceSchema(
|
export const UpdateBitbucketDataSourceSchema = BaseUpdateSecretScanningDataSourceSchema(
|
||||||
SecretScanningDataSource.BitBucket
|
SecretScanningDataSource.Bitbucket
|
||||||
)
|
)
|
||||||
.extend({
|
.extend({
|
||||||
config: BitBucketDataSourceConfigSchema.optional()
|
config: BitbucketDataSourceConfigSchema.optional()
|
||||||
})
|
})
|
||||||
.describe(
|
.describe(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
title: "BitBucket"
|
title: "Bitbucket"
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
export const BitBucketDataSourceListItemSchema = z
|
export const BitbucketDataSourceListItemSchema = z
|
||||||
.object({
|
.object({
|
||||||
name: z.literal("BitBucket"),
|
name: z.literal("Bitbucket"),
|
||||||
connection: z.literal(AppConnection.BitBucket),
|
connection: z.literal(AppConnection.Bitbucket),
|
||||||
type: z.literal(SecretScanningDataSource.BitBucket)
|
type: z.literal(SecretScanningDataSource.Bitbucket)
|
||||||
})
|
})
|
||||||
.describe(
|
.describe(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
title: "BitBucket"
|
title: "Bitbucket"
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
export const BitBucketFindingSchema = BaseSecretScanningFindingSchema.extend({
|
export const BitbucketFindingSchema = BaseSecretScanningFindingSchema.extend({
|
||||||
resourceType: z.literal(SecretScanningResource.Repository),
|
resourceType: z.literal(SecretScanningResource.Repository),
|
||||||
dataSourceType: z.literal(SecretScanningDataSource.BitBucket),
|
dataSourceType: z.literal(SecretScanningDataSource.Bitbucket),
|
||||||
details: GitRepositoryScanFindingDetailsSchema
|
details: GitRepositoryScanFindingDetailsSchema
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const BitbucketDataSourceCredentialsSchema = z.object({
|
||||||
|
webhookId: z.string()
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import { PushEvent } from "@octokit/webhooks-types";
|
|
||||||
|
|
||||||
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 { TBitBucketDataSource } from "./bitbucket-secret-scanning-types";
|
import { TBitbucketDataSource, TBitbucketPushEvent } from "./bitbucket-secret-scanning-types";
|
||||||
|
|
||||||
export const bitBucketSecretScanningService = (
|
export const bitBucketSecretScanningService = (
|
||||||
secretScanningV2DAL: TSecretScanningV2DALFactory,
|
secretScanningV2DAL: TSecretScanningV2DALFactory,
|
||||||
@@ -14,18 +12,18 @@ export const bitBucketSecretScanningService = (
|
|||||||
const handleInstallationDeletedEvent = async (installationId: number) => {
|
const handleInstallationDeletedEvent = async (installationId: number) => {
|
||||||
const dataSource = await secretScanningV2DAL.dataSources.findOne({
|
const dataSource = await secretScanningV2DAL.dataSources.findOne({
|
||||||
externalId: String(installationId),
|
externalId: String(installationId),
|
||||||
type: SecretScanningDataSource.BitBucket
|
type: SecretScanningDataSource.Bitbucket
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!dataSource) {
|
if (!dataSource) {
|
||||||
logger.error(
|
logger.error(
|
||||||
`secretScanningV2RemoveEvent: BitBucket - Could not find data source [installationId=${installationId}]`
|
`secretScanningV2RemoveEvent: Bitbucket - Could not find data source [installationId=${installationId}]`
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(
|
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, {
|
await secretScanningV2DAL.dataSources.updateById(dataSource.id, {
|
||||||
@@ -33,24 +31,26 @@ export const bitBucketSecretScanningService = (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePushEvent = async (payload: PushEvent) => {
|
const handlePushEvent = async (payload: TBitbucketPushEvent & { dataSourceId: string }) => {
|
||||||
const { commits, repository, installation } = payload;
|
const { push, repository } = payload;
|
||||||
|
|
||||||
if (!commits || !repository || !installation) {
|
if (!push?.changes?.length || !repository?.workspace?.uuid) {
|
||||||
logger.warn(
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataSource = (await secretScanningV2DAL.dataSources.findOne({
|
const dataSource = (await secretScanningV2DAL.dataSources.findOne({
|
||||||
externalId: String(installation.id),
|
externalId: payload.dataSourceId,
|
||||||
type: SecretScanningDataSource.BitBucket
|
type: SecretScanningDataSource.Bitbucket
|
||||||
})) as TBitBucketDataSource | undefined;
|
})) as TBitbucketDataSource | undefined;
|
||||||
|
|
||||||
if (!dataSource) {
|
if (!dataSource) {
|
||||||
logger.error(
|
logger.error(
|
||||||
`secretScanningV2PushEvent: BitBucket - Could not find data source [installationId=${installation.id}]`
|
`secretScanningV2PushEvent: Bitbucket - Could not find data source [workspaceUuid=${repository.workspace.uuid}]`
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -62,20 +62,20 @@ export const bitBucketSecretScanningService = (
|
|||||||
|
|
||||||
if (!isAutoScanEnabled) {
|
if (!isAutoScanEnabled) {
|
||||||
logger.info(
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (includeRepos.includes("*") || includeRepos.includes(repository.full_name)) {
|
if (includeRepos.includes("*") || includeRepos.includes(repository.full_name)) {
|
||||||
await secretScanningV2Queue.queueResourceDiffScan({
|
await secretScanningV2Queue.queueResourceDiffScan({
|
||||||
dataSourceType: SecretScanningDataSource.BitBucket,
|
dataSourceType: SecretScanningDataSource.Bitbucket,
|
||||||
payload,
|
payload,
|
||||||
dataSourceId: dataSource.id
|
dataSourceId: dataSource.id
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
logger.info(
|
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}]`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,31 +1,84 @@
|
|||||||
import { PushEvent } from "@octokit/webhooks-types";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
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 { TBitBucketConnection } from "@app/services/app-connection/bitbucket";
|
import { TBitbucketConnection } from "@app/services/app-connection/bitbucket";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BitBucketDataSourceListItemSchema,
|
BitbucketDataSourceCredentialsSchema,
|
||||||
BitBucketDataSourceSchema,
|
BitbucketDataSourceListItemSchema,
|
||||||
BitBucketFindingSchema,
|
BitbucketDataSourceSchema,
|
||||||
CreateBitBucketDataSourceSchema
|
BitbucketFindingSchema,
|
||||||
|
CreateBitbucketDataSourceSchema
|
||||||
} from "./bitbucket-secret-scanning-schemas";
|
} 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 & {
|
export type TBitbucketFinding = z.infer<typeof BitbucketFindingSchema>;
|
||||||
connection: TBitBucketConnection;
|
|
||||||
|
export type TBitbucketDataSourceWithConnection = TBitbucketDataSource & {
|
||||||
|
connection: TBitbucketConnection;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TQueueBitBucketResourceDiffScan = {
|
export type TBitbucketPushEventRepository = {
|
||||||
dataSourceType: SecretScanningDataSource.BitBucket;
|
full_name: string;
|
||||||
payload: PushEvent;
|
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;
|
dataSourceId: string;
|
||||||
resourceId: string;
|
resourceId: string;
|
||||||
scanId: string;
|
scanId: string;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export enum SecretScanningDataSource {
|
export enum SecretScanningDataSource {
|
||||||
GitHub = "github",
|
GitHub = "github",
|
||||||
BitBucket = "bitbucket"
|
Bitbucket = "bitbucket"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SecretScanningScanStatus {
|
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 { GitHubSecretScanningFactory } from "@app/ee/services/secret-scanning-v2/github/github-secret-scanning-factory";
|
||||||
|
|
||||||
import { SecretScanningDataSource } from "./secret-scanning-v2-enums";
|
import { SecretScanningDataSource } from "./secret-scanning-v2-enums";
|
||||||
@@ -17,5 +17,5 @@ type TSecretScanningFactoryImplementation = TSecretScanningFactory<
|
|||||||
|
|
||||||
export const SECRET_SCANNING_FACTORY_MAP: Record<SecretScanningDataSource, TSecretScanningFactoryImplementation> = {
|
export const SECRET_SCANNING_FACTORY_MAP: Record<SecretScanningDataSource, TSecretScanningFactoryImplementation> = {
|
||||||
[SecretScanningDataSource.GitHub]: GitHubSecretScanningFactory as 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> = {
|
const SECRET_SCANNING_SOURCE_LIST_OPTIONS: Record<SecretScanningDataSource, TSecretScanningDataSourceListItem> = {
|
||||||
[SecretScanningDataSource.GitHub]: GITHUB_SECRET_SCANNING_DATA_SOURCE_LIST_OPTION,
|
[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 = () => {
|
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> = {
|
export const SECRET_SCANNING_DATA_SOURCE_NAME_MAP: Record<SecretScanningDataSource, string> = {
|
||||||
[SecretScanningDataSource.GitHub]: "GitHub",
|
[SecretScanningDataSource.GitHub]: "GitHub",
|
||||||
[SecretScanningDataSource.BitBucket]: "BitBucket"
|
[SecretScanningDataSource.Bitbucket]: "Bitbucket"
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SECRET_SCANNING_DATA_SOURCE_CONNECTION_MAP: Record<SecretScanningDataSource, AppConnection> = {
|
export const SECRET_SCANNING_DATA_SOURCE_CONNECTION_MAP: Record<SecretScanningDataSource, AppConnection> = {
|
||||||
[SecretScanningDataSource.GitHub]: AppConnection.GitHubRadar,
|
[SecretScanningDataSource.GitHub]: AppConnection.GitHubRadar,
|
||||||
[SecretScanningDataSource.BitBucket]: AppConnection.BitBucket
|
[SecretScanningDataSource.Bitbucket]: AppConnection.Bitbucket
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AUTO_SYNC_DESCRIPTION_HELPER: Record<SecretScanningDataSource, { verb: string; noun: string }> = {
|
export const AUTO_SYNC_DESCRIPTION_HELPER: Record<SecretScanningDataSource, { verb: string; noun: string }> = {
|
||||||
[SecretScanningDataSource.GitHub]: { verb: "push", noun: "repositories" },
|
[SecretScanningDataSource.GitHub]: { verb: "push", noun: "repositories" },
|
||||||
[SecretScanningDataSource.BitBucket]: { verb: "push", noun: "repositories" }
|
[SecretScanningDataSource.Bitbucket]: { verb: "push", noun: "repositories" }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,12 +5,13 @@ import {
|
|||||||
TSecretScanningScans
|
TSecretScanningScans
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
import {
|
import {
|
||||||
TBitBucketDataSource,
|
TBitbucketDataSource,
|
||||||
TBitBucketDataSourceInput,
|
TBitbucketDataSourceCredentials,
|
||||||
TBitBucketDataSourceListItem,
|
TBitbucketDataSourceInput,
|
||||||
TBitBucketDataSourceWithConnection,
|
TBitbucketDataSourceListItem,
|
||||||
TBitBucketFinding,
|
TBitbucketDataSourceWithConnection,
|
||||||
TQueueBitBucketResourceDiffScan
|
TBitbucketFinding,
|
||||||
|
TQueueBitbucketResourceDiffScan
|
||||||
} from "@app/ee/services/secret-scanning-v2/bitbucket";
|
} from "@app/ee/services/secret-scanning-v2/bitbucket";
|
||||||
import {
|
import {
|
||||||
TGitHubDataSource,
|
TGitHubDataSource,
|
||||||
@@ -27,7 +28,7 @@ import {
|
|||||||
SecretScanningScanStatus
|
SecretScanningScanStatus
|
||||||
} from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-enums";
|
} 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 & {
|
export type TSecretScanningDataSourceWithDetails = TSecretScanningDataSource & {
|
||||||
lastScannedAt?: Date | null;
|
lastScannedAt?: Date | null;
|
||||||
@@ -51,13 +52,15 @@ export type TSecretScanningScanWithDetails = TSecretScanningScans & {
|
|||||||
|
|
||||||
export type TSecretScanningDataSourceWithConnection =
|
export type TSecretScanningDataSourceWithConnection =
|
||||||
| TGitHubDataSourceWithConnection
|
| 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 = {
|
export type TListSecretScanningDataSourcesByProjectId = {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
@@ -109,7 +112,7 @@ export type TQueueSecretScanningDataSourceFullScan = {
|
|||||||
scanId: string;
|
scanId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TQueueSecretScanningResourceDiffScan = TQueueGitHubResourceDiffScan | TQueueBitBucketResourceDiffScan;
|
export type TQueueSecretScanningResourceDiffScan = TQueueGitHubResourceDiffScan | TQueueBitbucketResourceDiffScan;
|
||||||
|
|
||||||
export type TQueueSecretScanningSendNotification = {
|
export type TQueueSecretScanningSendNotification = {
|
||||||
dataSource: TSecretScanningDataSources;
|
dataSource: TSecretScanningDataSources;
|
||||||
@@ -149,7 +152,7 @@ export type TSecretScanningDataSourceRaw = NonNullable<
|
|||||||
|
|
||||||
export type TSecretScanningFactoryInitialize<
|
export type TSecretScanningFactoryInitialize<
|
||||||
T extends TSecretScanningDataSourceWithConnection["connection"] | undefined = undefined,
|
T extends TSecretScanningDataSourceWithConnection["connection"] | undefined = undefined,
|
||||||
C extends TSecretScanningDataSourceCredentials = undefined
|
C extends TSecretScanningDataSourceCredentials = TDataSourceCredentialsSchema
|
||||||
> = (
|
> = (
|
||||||
params: {
|
params: {
|
||||||
payload: TCreateSecretScanningDataSourceDTO;
|
payload: TCreateSecretScanningDataSourceDTO;
|
||||||
@@ -161,7 +164,7 @@ export type TSecretScanningFactoryInitialize<
|
|||||||
|
|
||||||
export type TSecretScanningFactoryPostInitialization<
|
export type TSecretScanningFactoryPostInitialization<
|
||||||
T extends TSecretScanningDataSourceWithConnection["connection"] | undefined = undefined,
|
T extends TSecretScanningDataSourceWithConnection["connection"] | undefined = undefined,
|
||||||
C extends TSecretScanningDataSourceCredentials = undefined
|
C extends TSecretScanningDataSourceCredentials = TDataSourceCredentialsSchema
|
||||||
> = (params: {
|
> = (params: {
|
||||||
payload: TCreateSecretScanningDataSourceDTO;
|
payload: TCreateSecretScanningDataSourceDTO;
|
||||||
connection: T;
|
connection: T;
|
||||||
@@ -196,4 +199,4 @@ export type TUpsertSecretScanningConfigDTO = {
|
|||||||
content: string | null;
|
content: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TSecretScanningDataSourceCredentials = undefined;
|
export type TSecretScanningDataSourceCredentials = TDataSourceCredentialsSchema;
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { z } from "zod";
|
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";
|
import { GitHubDataSourceSchema, GitHubFindingSchema } from "@app/ee/services/secret-scanning-v2/github";
|
||||||
|
|
||||||
export const SecretScanningDataSourceSchema = z.discriminatedUnion("type", [
|
export const SecretScanningDataSourceSchema = z.discriminatedUnion("type", [
|
||||||
GitHubDataSourceSchema,
|
GitHubDataSourceSchema,
|
||||||
BitBucketDataSourceSchema
|
BitbucketDataSourceSchema
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const SecretScanningFindingSchema = z.discriminatedUnion("dataSourceType", [
|
export const SecretScanningFindingSchema = z.discriminatedUnion("dataSourceType", [
|
||||||
GitHubFindingSchema,
|
GitHubFindingSchema,
|
||||||
BitBucketFindingSchema
|
BitbucketFindingSchema
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -2270,8 +2270,8 @@ export const AppConnections = {
|
|||||||
accessTokenType: "The type of token used to connect with GitLab."
|
accessTokenType: "The type of token used to connect with GitLab."
|
||||||
},
|
},
|
||||||
BITBUCKET: {
|
BITBUCKET: {
|
||||||
email: "The email used to access BitBucket.",
|
email: "The email used to access Bitbucket.",
|
||||||
apiToken: "The API token used to access BitBucket."
|
apiToken: "The API token used to access Bitbucket."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -2634,6 +2634,7 @@ export const SecretScanningDataSources = {
|
|||||||
includeRepos: 'The repositories to include when scanning. Defaults to all repositories (["*"]).'
|
includeRepos: 'The repositories to include when scanning. Defaults to all repositories (["*"]).'
|
||||||
},
|
},
|
||||||
BITBUCKET: {
|
BITBUCKET: {
|
||||||
|
workspaceSlug: "The workspace to scan.",
|
||||||
includeRepos: 'The repositories to include when scanning. Defaults to all repositories (["*"]).'
|
includeRepos: 'The repositories to include when scanning. Defaults to all repositories (["*"]).'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import type { EmitterWebhookEventName } from "@octokit/webhooks/dist-types/types";
|
import type { EmitterWebhookEventName } from "@octokit/webhooks/dist-types/types";
|
||||||
import { PushEvent } from "@octokit/webhooks-types";
|
import { PushEvent } from "@octokit/webhooks-types";
|
||||||
import { Probot } from "probot";
|
import { Probot } from "probot";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
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";
|
||||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
import { writeLimit } from "@app/server/config/rateLimiter";
|
||||||
@@ -64,5 +66,31 @@ export const registerSecretScanningV2Webhooks = async (server: FastifyZodProvide
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO(andrey): Register a webhook for BitBucket
|
// bitbucket push event webhook
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/bitbucket",
|
||||||
|
schema: {
|
||||||
|
querystring: z.object({
|
||||||
|
dataSourceId: z.string().min(1, { message: "Data Source ID is required" })
|
||||||
|
})
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
handler: async (req, res) => {
|
||||||
|
// TODO(andrey): Verify IP is from bitbucket
|
||||||
|
|
||||||
|
const { dataSourceId } = req.query;
|
||||||
|
|
||||||
|
if (!dataSourceId) return res.status(400).send({ message: "Data Source ID is required" });
|
||||||
|
|
||||||
|
await server.services.secretScanningV2.bitbucket.handlePushEvent({
|
||||||
|
...(req.body as TBitbucketPushEvent),
|
||||||
|
dataSourceId
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.send("ok");
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ import {
|
|||||||
SanitizedAzureKeyVaultConnectionSchema
|
SanitizedAzureKeyVaultConnectionSchema
|
||||||
} from "@app/services/app-connection/azure-key-vault";
|
} from "@app/services/app-connection/azure-key-vault";
|
||||||
import {
|
import {
|
||||||
BitBucketConnectionListItemSchema,
|
BitbucketConnectionListItemSchema,
|
||||||
SanitizedBitBucketConnectionSchema
|
SanitizedBitbucketConnectionSchema
|
||||||
} from "@app/services/app-connection/bitbucket";
|
} from "@app/services/app-connection/bitbucket";
|
||||||
import {
|
import {
|
||||||
CamundaConnectionListItemSchema,
|
CamundaConnectionListItemSchema,
|
||||||
@@ -121,7 +121,7 @@ const SanitizedAppConnectionSchema = z.union([
|
|||||||
...SanitizedFlyioConnectionSchema.options,
|
...SanitizedFlyioConnectionSchema.options,
|
||||||
...SanitizedGitLabConnectionSchema.options,
|
...SanitizedGitLabConnectionSchema.options,
|
||||||
...SanitizedCloudflareConnectionSchema.options,
|
...SanitizedCloudflareConnectionSchema.options,
|
||||||
...SanitizedBitBucketConnectionSchema.options
|
...SanitizedBitbucketConnectionSchema.options
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
|
const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
|
||||||
@@ -154,7 +154,7 @@ const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
|
|||||||
FlyioConnectionListItemSchema,
|
FlyioConnectionListItemSchema,
|
||||||
GitLabConnectionListItemSchema,
|
GitLabConnectionListItemSchema,
|
||||||
CloudflareConnectionListItemSchema,
|
CloudflareConnectionListItemSchema,
|
||||||
BitBucketConnectionListItemSchema
|
BitbucketConnectionListItemSchema
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const registerAppConnectionRouter = async (server: FastifyZodProvider) => {
|
export const registerAppConnectionRouter = async (server: FastifyZodProvider) => {
|
||||||
|
|||||||
@@ -4,25 +4,53 @@ import { readLimit } from "@app/server/config/rateLimiter";
|
|||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
import {
|
import {
|
||||||
CreateBitBucketConnectionSchema,
|
CreateBitbucketConnectionSchema,
|
||||||
SanitizedBitBucketConnectionSchema,
|
SanitizedBitbucketConnectionSchema,
|
||||||
UpdateBitBucketConnectionSchema
|
UpdateBitbucketConnectionSchema
|
||||||
} from "@app/services/app-connection/bitbucket";
|
} from "@app/services/app-connection/bitbucket";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
|
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
|
||||||
|
|
||||||
export const registerBitBucketConnectionRouter = async (server: FastifyZodProvider) => {
|
export const registerBitbucketConnectionRouter = async (server: FastifyZodProvider) => {
|
||||||
registerAppConnectionEndpoints({
|
registerAppConnectionEndpoints({
|
||||||
app: AppConnection.BitBucket,
|
app: AppConnection.Bitbucket,
|
||||||
server,
|
server,
|
||||||
sanitizedResponseSchema: SanitizedBitBucketConnectionSchema,
|
sanitizedResponseSchema: SanitizedBitbucketConnectionSchema,
|
||||||
createSchema: CreateBitBucketConnectionSchema,
|
createSchema: CreateBitbucketConnectionSchema,
|
||||||
updateSchema: UpdateBitBucketConnectionSchema
|
updateSchema: UpdateBitbucketConnectionSchema
|
||||||
});
|
});
|
||||||
|
|
||||||
// The below endpoints are not exposed and for Infisical App use
|
// The below endpoints are not exposed and for Infisical App use
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: `/:connectionId/workspaces`,
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
connectionId: z.string().uuid()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
workspaces: z.object({ slug: z.string() }).array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const {
|
||||||
|
params: { connectionId }
|
||||||
|
} = req;
|
||||||
|
|
||||||
|
const workspaces = await server.services.appConnection.bitbucket.listWorkspaces(connectionId, req.permission);
|
||||||
|
|
||||||
|
return { workspaces };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: `/:connectionId/repositories`,
|
url: `/:connectionId/repositories`,
|
||||||
@@ -33,17 +61,26 @@ export const registerBitBucketConnectionRouter = async (server: FastifyZodProvid
|
|||||||
params: z.object({
|
params: z.object({
|
||||||
connectionId: z.string().uuid()
|
connectionId: z.string().uuid()
|
||||||
}),
|
}),
|
||||||
|
querystring: z.object({
|
||||||
|
workspaceSlug: z.string()
|
||||||
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
repositories: z.object({ id: z.string(), name: z.string() }).array()
|
repositories: z.object({ slug: z.string(), full_name: z.string() }).array()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const { connectionId } = req.params;
|
const {
|
||||||
|
params: { connectionId },
|
||||||
|
query: { workspaceSlug }
|
||||||
|
} = req;
|
||||||
|
|
||||||
const repositories = await server.services.appConnection.bitbucket.listRepositories(connectionId, req.permission);
|
const repositories = await server.services.appConnection.bitbucket.listRepositories(
|
||||||
|
{ connectionId, workspaceSlug },
|
||||||
|
req.permission
|
||||||
|
);
|
||||||
|
|
||||||
return { repositories };
|
return { repositories };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { registerAzureAppConfigurationConnectionRouter } from "./azure-app-confi
|
|||||||
import { registerAzureClientSecretsConnectionRouter } from "./azure-client-secrets-connection-router";
|
import { registerAzureClientSecretsConnectionRouter } from "./azure-client-secrets-connection-router";
|
||||||
import { registerAzureDevOpsConnectionRouter } from "./azure-devops-connection-router";
|
import { registerAzureDevOpsConnectionRouter } from "./azure-devops-connection-router";
|
||||||
import { registerAzureKeyVaultConnectionRouter } from "./azure-key-vault-connection-router";
|
import { registerAzureKeyVaultConnectionRouter } from "./azure-key-vault-connection-router";
|
||||||
import { registerBitBucketConnectionRouter } from "./bitbucket-connection-router";
|
import { registerBitbucketConnectionRouter } from "./bitbucket-connection-router";
|
||||||
import { registerCamundaConnectionRouter } from "./camunda-connection-router";
|
import { registerCamundaConnectionRouter } from "./camunda-connection-router";
|
||||||
import { registerCloudflareConnectionRouter } from "./cloudflare-connection-router";
|
import { registerCloudflareConnectionRouter } from "./cloudflare-connection-router";
|
||||||
import { registerDatabricksConnectionRouter } from "./databricks-connection-router";
|
import { registerDatabricksConnectionRouter } from "./databricks-connection-router";
|
||||||
@@ -64,5 +64,5 @@ export const APP_CONNECTION_REGISTER_ROUTER_MAP: Record<AppConnection, (server:
|
|||||||
[AppConnection.Flyio]: registerFlyioConnectionRouter,
|
[AppConnection.Flyio]: registerFlyioConnectionRouter,
|
||||||
[AppConnection.GitLab]: registerGitLabConnectionRouter,
|
[AppConnection.GitLab]: registerGitLabConnectionRouter,
|
||||||
[AppConnection.Cloudflare]: registerCloudflareConnectionRouter,
|
[AppConnection.Cloudflare]: registerCloudflareConnectionRouter,
|
||||||
[AppConnection.BitBucket]: registerBitBucketConnectionRouter
|
[AppConnection.Bitbucket]: registerBitbucketConnectionRouter
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export enum AppConnection {
|
|||||||
Flyio = "flyio",
|
Flyio = "flyio",
|
||||||
GitLab = "gitlab",
|
GitLab = "gitlab",
|
||||||
Cloudflare = "cloudflare",
|
Cloudflare = "cloudflare",
|
||||||
BitBucket = "bitbucket"
|
Bitbucket = "bitbucket"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum AWSRegion {
|
export enum AWSRegion {
|
||||||
|
|||||||
@@ -51,9 +51,9 @@ import {
|
|||||||
validateAzureKeyVaultConnectionCredentials
|
validateAzureKeyVaultConnectionCredentials
|
||||||
} from "./azure-key-vault";
|
} from "./azure-key-vault";
|
||||||
import {
|
import {
|
||||||
BitBucketConnectionMethod,
|
BitbucketConnectionMethod,
|
||||||
getBitBucketConnectionListItem,
|
getBitbucketConnectionListItem,
|
||||||
validateBitBucketConnectionCredentials
|
validateBitbucketConnectionCredentials
|
||||||
} from "./bitbucket";
|
} from "./bitbucket";
|
||||||
import { CamundaConnectionMethod, getCamundaConnectionListItem, validateCamundaConnectionCredentials } from "./camunda";
|
import { CamundaConnectionMethod, getCamundaConnectionListItem, validateCamundaConnectionCredentials } from "./camunda";
|
||||||
import { CloudflareConnectionMethod } from "./cloudflare/cloudflare-connection-enum";
|
import { CloudflareConnectionMethod } from "./cloudflare/cloudflare-connection-enum";
|
||||||
@@ -142,7 +142,7 @@ export const listAppConnectionOptions = () => {
|
|||||||
getFlyioConnectionListItem(),
|
getFlyioConnectionListItem(),
|
||||||
getGitLabConnectionListItem(),
|
getGitLabConnectionListItem(),
|
||||||
getCloudflareConnectionListItem(),
|
getCloudflareConnectionListItem(),
|
||||||
getBitBucketConnectionListItem()
|
getBitbucketConnectionListItem()
|
||||||
].sort((a, b) => a.name.localeCompare(b.name));
|
].sort((a, b) => a.name.localeCompare(b.name));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -223,7 +223,7 @@ export const validateAppConnectionCredentials = async (
|
|||||||
[AppConnection.Flyio]: validateFlyioConnectionCredentials as TAppConnectionCredentialsValidator,
|
[AppConnection.Flyio]: validateFlyioConnectionCredentials as TAppConnectionCredentialsValidator,
|
||||||
[AppConnection.GitLab]: validateGitLabConnectionCredentials as TAppConnectionCredentialsValidator,
|
[AppConnection.GitLab]: validateGitLabConnectionCredentials as TAppConnectionCredentialsValidator,
|
||||||
[AppConnection.Cloudflare]: validateCloudflareConnectionCredentials as TAppConnectionCredentialsValidator,
|
[AppConnection.Cloudflare]: validateCloudflareConnectionCredentials as TAppConnectionCredentialsValidator,
|
||||||
[AppConnection.BitBucket]: validateBitBucketConnectionCredentials as TAppConnectionCredentialsValidator
|
[AppConnection.Bitbucket]: validateBitbucketConnectionCredentials as TAppConnectionCredentialsValidator
|
||||||
};
|
};
|
||||||
|
|
||||||
return VALIDATE_APP_CONNECTION_CREDENTIALS_MAP[appConnection.app](appConnection);
|
return VALIDATE_APP_CONNECTION_CREDENTIALS_MAP[appConnection.app](appConnection);
|
||||||
@@ -260,7 +260,7 @@ export const getAppConnectionMethodName = (method: TAppConnection["method"]) =>
|
|||||||
case VercelConnectionMethod.ApiToken:
|
case VercelConnectionMethod.ApiToken:
|
||||||
case OnePassConnectionMethod.ApiToken:
|
case OnePassConnectionMethod.ApiToken:
|
||||||
case CloudflareConnectionMethod.APIToken:
|
case CloudflareConnectionMethod.APIToken:
|
||||||
case BitBucketConnectionMethod.ApiToken:
|
case BitbucketConnectionMethod.ApiToken:
|
||||||
return "API Token";
|
return "API Token";
|
||||||
case PostgresConnectionMethod.UsernameAndPassword:
|
case PostgresConnectionMethod.UsernameAndPassword:
|
||||||
case MsSqlConnectionMethod.UsernameAndPassword:
|
case MsSqlConnectionMethod.UsernameAndPassword:
|
||||||
@@ -341,7 +341,7 @@ export const TRANSITION_CONNECTION_CREDENTIALS_TO_PLATFORM: Record<
|
|||||||
[AppConnection.Flyio]: platformManagedCredentialsNotSupported,
|
[AppConnection.Flyio]: platformManagedCredentialsNotSupported,
|
||||||
[AppConnection.GitLab]: platformManagedCredentialsNotSupported,
|
[AppConnection.GitLab]: platformManagedCredentialsNotSupported,
|
||||||
[AppConnection.Cloudflare]: platformManagedCredentialsNotSupported,
|
[AppConnection.Cloudflare]: platformManagedCredentialsNotSupported,
|
||||||
[AppConnection.BitBucket]: platformManagedCredentialsNotSupported
|
[AppConnection.Bitbucket]: platformManagedCredentialsNotSupported
|
||||||
};
|
};
|
||||||
|
|
||||||
export const enterpriseAppCheck = async (
|
export const enterpriseAppCheck = async (
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export const APP_CONNECTION_NAME_MAP: Record<AppConnection, string> = {
|
|||||||
[AppConnection.Flyio]: "Fly.io",
|
[AppConnection.Flyio]: "Fly.io",
|
||||||
[AppConnection.GitLab]: "GitLab",
|
[AppConnection.GitLab]: "GitLab",
|
||||||
[AppConnection.Cloudflare]: "Cloudflare",
|
[AppConnection.Cloudflare]: "Cloudflare",
|
||||||
[AppConnection.BitBucket]: "BitBucket"
|
[AppConnection.Bitbucket]: "Bitbucket"
|
||||||
};
|
};
|
||||||
|
|
||||||
export const APP_CONNECTION_PLAN_MAP: Record<AppConnection, AppConnectionPlanType> = {
|
export const APP_CONNECTION_PLAN_MAP: Record<AppConnection, AppConnectionPlanType> = {
|
||||||
@@ -63,5 +63,5 @@ export const APP_CONNECTION_PLAN_MAP: Record<AppConnection, AppConnectionPlanTyp
|
|||||||
[AppConnection.Flyio]: AppConnectionPlanType.Regular,
|
[AppConnection.Flyio]: AppConnectionPlanType.Regular,
|
||||||
[AppConnection.GitLab]: AppConnectionPlanType.Regular,
|
[AppConnection.GitLab]: AppConnectionPlanType.Regular,
|
||||||
[AppConnection.Cloudflare]: AppConnectionPlanType.Regular,
|
[AppConnection.Cloudflare]: AppConnectionPlanType.Regular,
|
||||||
[AppConnection.BitBucket]: AppConnectionPlanType.Regular
|
[AppConnection.Bitbucket]: AppConnectionPlanType.Regular
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ import { azureClientSecretsConnectionService } from "./azure-client-secrets/azur
|
|||||||
import { ValidateAzureDevOpsConnectionCredentialsSchema } from "./azure-devops/azure-devops-schemas";
|
import { ValidateAzureDevOpsConnectionCredentialsSchema } from "./azure-devops/azure-devops-schemas";
|
||||||
import { azureDevOpsConnectionService } from "./azure-devops/azure-devops-service";
|
import { azureDevOpsConnectionService } from "./azure-devops/azure-devops-service";
|
||||||
import { ValidateAzureKeyVaultConnectionCredentialsSchema } from "./azure-key-vault";
|
import { ValidateAzureKeyVaultConnectionCredentialsSchema } from "./azure-key-vault";
|
||||||
import { ValidateBitBucketConnectionCredentialsSchema } from "./bitbucket";
|
import { ValidateBitbucketConnectionCredentialsSchema } from "./bitbucket";
|
||||||
import { bitBucketConnectionService } from "./bitbucket/bitbucket-connection-service";
|
import { bitBucketConnectionService } from "./bitbucket/bitbucket-connection-service";
|
||||||
import { ValidateCamundaConnectionCredentialsSchema } from "./camunda";
|
import { ValidateCamundaConnectionCredentialsSchema } from "./camunda";
|
||||||
import { camundaConnectionService } from "./camunda/camunda-connection-service";
|
import { camundaConnectionService } from "./camunda/camunda-connection-service";
|
||||||
@@ -122,7 +122,7 @@ const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TValidateAp
|
|||||||
[AppConnection.Flyio]: ValidateFlyioConnectionCredentialsSchema,
|
[AppConnection.Flyio]: ValidateFlyioConnectionCredentialsSchema,
|
||||||
[AppConnection.GitLab]: ValidateGitLabConnectionCredentialsSchema,
|
[AppConnection.GitLab]: ValidateGitLabConnectionCredentialsSchema,
|
||||||
[AppConnection.Cloudflare]: ValidateCloudflareConnectionCredentialsSchema,
|
[AppConnection.Cloudflare]: ValidateCloudflareConnectionCredentialsSchema,
|
||||||
[AppConnection.BitBucket]: ValidateBitBucketConnectionCredentialsSchema
|
[AppConnection.Bitbucket]: ValidateBitbucketConnectionCredentialsSchema
|
||||||
};
|
};
|
||||||
|
|
||||||
export const appConnectionServiceFactory = ({
|
export const appConnectionServiceFactory = ({
|
||||||
|
|||||||
@@ -57,10 +57,10 @@ import {
|
|||||||
TValidateAzureKeyVaultConnectionCredentialsSchema
|
TValidateAzureKeyVaultConnectionCredentialsSchema
|
||||||
} from "./azure-key-vault";
|
} from "./azure-key-vault";
|
||||||
import {
|
import {
|
||||||
TBitBucketConnection,
|
TBitbucketConnection,
|
||||||
TBitBucketConnectionConfig,
|
TBitbucketConnectionConfig,
|
||||||
TBitBucketConnectionInput,
|
TBitbucketConnectionInput,
|
||||||
TValidateBitBucketConnectionCredentialsSchema
|
TValidateBitbucketConnectionCredentialsSchema
|
||||||
} from "./bitbucket";
|
} from "./bitbucket";
|
||||||
import {
|
import {
|
||||||
TCamundaConnection,
|
TCamundaConnection,
|
||||||
@@ -202,7 +202,7 @@ export type TAppConnection = { id: string } & (
|
|||||||
| TFlyioConnection
|
| TFlyioConnection
|
||||||
| TGitLabConnection
|
| TGitLabConnection
|
||||||
| TCloudflareConnection
|
| TCloudflareConnection
|
||||||
| TBitBucketConnection
|
| TBitbucketConnection
|
||||||
);
|
);
|
||||||
|
|
||||||
export type TAppConnectionRaw = NonNullable<Awaited<ReturnType<TAppConnectionDALFactory["findById"]>>>;
|
export type TAppConnectionRaw = NonNullable<Awaited<ReturnType<TAppConnectionDALFactory["findById"]>>>;
|
||||||
@@ -239,7 +239,7 @@ export type TAppConnectionInput = { id: string } & (
|
|||||||
| TFlyioConnectionInput
|
| TFlyioConnectionInput
|
||||||
| TGitLabConnectionInput
|
| TGitLabConnectionInput
|
||||||
| TCloudflareConnectionInput
|
| TCloudflareConnectionInput
|
||||||
| TBitBucketConnectionInput
|
| TBitbucketConnectionInput
|
||||||
);
|
);
|
||||||
|
|
||||||
export type TSqlConnectionInput =
|
export type TSqlConnectionInput =
|
||||||
@@ -284,7 +284,7 @@ export type TAppConnectionConfig =
|
|||||||
| TFlyioConnectionConfig
|
| TFlyioConnectionConfig
|
||||||
| TGitLabConnectionConfig
|
| TGitLabConnectionConfig
|
||||||
| TCloudflareConnectionConfig
|
| TCloudflareConnectionConfig
|
||||||
| TBitBucketConnectionConfig;
|
| TBitbucketConnectionConfig;
|
||||||
|
|
||||||
export type TValidateAppConnectionCredentialsSchema =
|
export type TValidateAppConnectionCredentialsSchema =
|
||||||
| TValidateAwsConnectionCredentialsSchema
|
| TValidateAwsConnectionCredentialsSchema
|
||||||
@@ -316,7 +316,7 @@ export type TValidateAppConnectionCredentialsSchema =
|
|||||||
| TValidateFlyioConnectionCredentialsSchema
|
| TValidateFlyioConnectionCredentialsSchema
|
||||||
| TValidateGitLabConnectionCredentialsSchema
|
| TValidateGitLabConnectionCredentialsSchema
|
||||||
| TValidateCloudflareConnectionCredentialsSchema
|
| TValidateCloudflareConnectionCredentialsSchema
|
||||||
| TValidateBitBucketConnectionCredentialsSchema;
|
| TValidateBitbucketConnectionCredentialsSchema;
|
||||||
|
|
||||||
export type TListAwsConnectionKmsKeys = {
|
export type TListAwsConnectionKmsKeys = {
|
||||||
connectionId: string;
|
connectionId: string;
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export enum BitBucketConnectionMethod {
|
export enum BitbucketConnectionMethod {
|
||||||
ApiToken = "api-token"
|
ApiToken = "api-token"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,18 +5,23 @@ import { BadRequestError } from "@app/lib/errors";
|
|||||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||||
|
|
||||||
import { BitBucketConnectionMethod } from "./bitbucket-connection-enums";
|
import { BitbucketConnectionMethod } from "./bitbucket-connection-enums";
|
||||||
import { TBitBucketConnection, TBitBucketConnectionConfig, TBitBucketRepo } from "./bitbucket-connection-types";
|
import {
|
||||||
|
TBitbucketConnection,
|
||||||
|
TBitbucketConnectionConfig,
|
||||||
|
TBitbucketRepo,
|
||||||
|
TBitbucketWorkspace
|
||||||
|
} from "./bitbucket-connection-types";
|
||||||
|
|
||||||
export const getBitBucketConnectionListItem = () => {
|
export const getBitbucketConnectionListItem = () => {
|
||||||
return {
|
return {
|
||||||
name: "BitBucket" as const,
|
name: "Bitbucket" as const,
|
||||||
app: AppConnection.BitBucket as const,
|
app: AppConnection.Bitbucket as const,
|
||||||
methods: Object.values(BitBucketConnectionMethod) as [BitBucketConnectionMethod.ApiToken]
|
methods: Object.values(BitbucketConnectionMethod) as [BitbucketConnectionMethod.ApiToken]
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getBitBucketUser = async ({ email, apiToken }: { email: string; apiToken: string }) => {
|
export const getBitbucketUser = async ({ email, apiToken }: { email: string; apiToken: string }) => {
|
||||||
try {
|
try {
|
||||||
const { data } = await request.get<{ username: string }>(`${IntegrationUrls.BITBUCKET_API_URL}/2.0/user`, {
|
const { data } = await request.get<{ username: string }>(`${IntegrationUrls.BITBUCKET_API_URL}/2.0/user`, {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -38,12 +43,17 @@ export const getBitBucketUser = async ({ email, apiToken }: { email: string; api
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const validateBitBucketConnectionCredentials = async (config: TBitBucketConnectionConfig) => {
|
export const validateBitbucketConnectionCredentials = async (config: TBitbucketConnectionConfig) => {
|
||||||
await getBitBucketUser(config.credentials);
|
await getBitbucketUser(config.credentials);
|
||||||
return config.credentials;
|
return config.credentials;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const listBitBucketRepositories = async (appConnection: TBitBucketConnection) => {
|
interface BitbucketWorkspacesResponse {
|
||||||
|
values: TBitbucketWorkspace[];
|
||||||
|
next?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const listBitbucketWorkspaces = async (appConnection: TBitbucketConnection) => {
|
||||||
const { email, apiToken } = appConnection.credentials;
|
const { email, apiToken } = appConnection.credentials;
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
@@ -51,19 +61,52 @@ export const listBitBucketRepositories = async (appConnection: TBitBucketConnect
|
|||||||
Accept: "application/json"
|
Accept: "application/json"
|
||||||
};
|
};
|
||||||
|
|
||||||
let allRepos: TBitBucketRepo[] = [];
|
let allWorkspaces: TBitbucketWorkspace[] = [];
|
||||||
let nextUrl: string | undefined = `${IntegrationUrls.BITBUCKET_API_URL}/2.0/repositories?role=member&pagelen=100`;
|
let nextUrl: string | undefined = `${IntegrationUrls.BITBUCKET_API_URL}/2.0/workspaces?pagelen=100`;
|
||||||
|
let iterationCount = 0;
|
||||||
|
|
||||||
|
// Limit to 10 iterations, fetching at most 10 * 100 = 1000 workspaces
|
||||||
|
while (nextUrl && iterationCount < 10) {
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
const { data }: { data: BitbucketWorkspacesResponse } = await request.get<BitbucketWorkspacesResponse>(nextUrl, {
|
||||||
|
headers
|
||||||
|
});
|
||||||
|
|
||||||
|
allWorkspaces = allWorkspaces.concat(data.values.map((workspace) => ({ slug: workspace.slug })));
|
||||||
|
nextUrl = data.next;
|
||||||
|
iterationCount += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return allWorkspaces;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface BitbucketRepositoriesResponse {
|
||||||
|
values: TBitbucketRepo[];
|
||||||
|
next?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const listBitbucketRepositories = async (appConnection: TBitbucketConnection, workspaceSlug: string) => {
|
||||||
|
const { email, apiToken } = appConnection.credentials;
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
Authorization: `Basic ${Buffer.from(`${email}:${apiToken}`).toString("base64")}`,
|
||||||
|
Accept: "application/json"
|
||||||
|
};
|
||||||
|
|
||||||
|
let allRepos: TBitbucketRepo[] = [];
|
||||||
|
let nextUrl: string | undefined =
|
||||||
|
`${IntegrationUrls.BITBUCKET_API_URL}/2.0/repositories/${workspaceSlug}?pagelen=100`;
|
||||||
let iterationCount = 0;
|
let iterationCount = 0;
|
||||||
|
|
||||||
// Limit to 10 iterations, fetching at most 10 * 100 = 1000 repositories
|
// Limit to 10 iterations, fetching at most 10 * 100 = 1000 repositories
|
||||||
while (nextUrl && iterationCount < 10) {
|
while (nextUrl && iterationCount < 10) {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
const { data }: { data: { values: TBitBucketRepo[]; next?: string } } = await request.get<{
|
const { data }: { data: BitbucketRepositoriesResponse } = await request.get<BitbucketRepositoriesResponse>(
|
||||||
values: TBitBucketRepo[];
|
nextUrl,
|
||||||
next?: string;
|
{
|
||||||
}>(nextUrl, {
|
headers
|
||||||
headers
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
allRepos = allRepos.concat(data.values);
|
allRepos = allRepos.concat(data.values);
|
||||||
nextUrl = data.next;
|
nextUrl = data.next;
|
||||||
|
|||||||
@@ -8,54 +8,54 @@ import {
|
|||||||
GenericUpdateAppConnectionFieldsSchema
|
GenericUpdateAppConnectionFieldsSchema
|
||||||
} from "@app/services/app-connection/app-connection-schemas";
|
} from "@app/services/app-connection/app-connection-schemas";
|
||||||
|
|
||||||
import { BitBucketConnectionMethod } from "./bitbucket-connection-enums";
|
import { BitbucketConnectionMethod } from "./bitbucket-connection-enums";
|
||||||
|
|
||||||
export const BitBucketConnectionAccessTokenCredentialsSchema = z.object({
|
export const BitbucketConnectionAccessTokenCredentialsSchema = z.object({
|
||||||
apiToken: z.string().trim().min(1, "API Token required").describe(AppConnections.CREDENTIALS.BITBUCKET.apiToken),
|
apiToken: z.string().trim().min(1, "API Token required").describe(AppConnections.CREDENTIALS.BITBUCKET.apiToken),
|
||||||
email: z.string().email().trim().min(1, "Email required").describe(AppConnections.CREDENTIALS.BITBUCKET.email)
|
email: z.string().email().trim().min(1, "Email required").describe(AppConnections.CREDENTIALS.BITBUCKET.email)
|
||||||
});
|
});
|
||||||
|
|
||||||
const BaseBitBucketConnectionSchema = BaseAppConnectionSchema.extend({ app: z.literal(AppConnection.BitBucket) });
|
const BaseBitbucketConnectionSchema = BaseAppConnectionSchema.extend({ app: z.literal(AppConnection.Bitbucket) });
|
||||||
|
|
||||||
export const BitBucketConnectionSchema = BaseBitBucketConnectionSchema.extend({
|
export const BitbucketConnectionSchema = BaseBitbucketConnectionSchema.extend({
|
||||||
method: z.literal(BitBucketConnectionMethod.ApiToken),
|
method: z.literal(BitbucketConnectionMethod.ApiToken),
|
||||||
credentials: BitBucketConnectionAccessTokenCredentialsSchema
|
credentials: BitbucketConnectionAccessTokenCredentialsSchema
|
||||||
});
|
});
|
||||||
|
|
||||||
export const SanitizedBitBucketConnectionSchema = z.discriminatedUnion("method", [
|
export const SanitizedBitbucketConnectionSchema = z.discriminatedUnion("method", [
|
||||||
BaseBitBucketConnectionSchema.extend({
|
BaseBitbucketConnectionSchema.extend({
|
||||||
method: z.literal(BitBucketConnectionMethod.ApiToken),
|
method: z.literal(BitbucketConnectionMethod.ApiToken),
|
||||||
credentials: BitBucketConnectionAccessTokenCredentialsSchema.pick({
|
credentials: BitbucketConnectionAccessTokenCredentialsSchema.pick({
|
||||||
email: true
|
email: true
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const ValidateBitBucketConnectionCredentialsSchema = z.discriminatedUnion("method", [
|
export const ValidateBitbucketConnectionCredentialsSchema = z.discriminatedUnion("method", [
|
||||||
z.object({
|
z.object({
|
||||||
method: z
|
method: z
|
||||||
.literal(BitBucketConnectionMethod.ApiToken)
|
.literal(BitbucketConnectionMethod.ApiToken)
|
||||||
.describe(AppConnections.CREATE(AppConnection.BitBucket).method),
|
.describe(AppConnections.CREATE(AppConnection.Bitbucket).method),
|
||||||
credentials: BitBucketConnectionAccessTokenCredentialsSchema.describe(
|
credentials: BitbucketConnectionAccessTokenCredentialsSchema.describe(
|
||||||
AppConnections.CREATE(AppConnection.BitBucket).credentials
|
AppConnections.CREATE(AppConnection.Bitbucket).credentials
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const CreateBitBucketConnectionSchema = ValidateBitBucketConnectionCredentialsSchema.and(
|
export const CreateBitbucketConnectionSchema = ValidateBitbucketConnectionCredentialsSchema.and(
|
||||||
GenericCreateAppConnectionFieldsSchema(AppConnection.BitBucket)
|
GenericCreateAppConnectionFieldsSchema(AppConnection.Bitbucket)
|
||||||
);
|
);
|
||||||
|
|
||||||
export const UpdateBitBucketConnectionSchema = z
|
export const UpdateBitbucketConnectionSchema = z
|
||||||
.object({
|
.object({
|
||||||
credentials: BitBucketConnectionAccessTokenCredentialsSchema.optional().describe(
|
credentials: BitbucketConnectionAccessTokenCredentialsSchema.optional().describe(
|
||||||
AppConnections.UPDATE(AppConnection.BitBucket).credentials
|
AppConnections.UPDATE(AppConnection.Bitbucket).credentials
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.and(GenericUpdateAppConnectionFieldsSchema(AppConnection.BitBucket));
|
.and(GenericUpdateAppConnectionFieldsSchema(AppConnection.Bitbucket));
|
||||||
|
|
||||||
export const BitBucketConnectionListItemSchema = z.object({
|
export const BitbucketConnectionListItemSchema = z.object({
|
||||||
name: z.literal("BitBucket"),
|
name: z.literal("Bitbucket"),
|
||||||
app: z.literal(AppConnection.BitBucket),
|
app: z.literal(AppConnection.Bitbucket),
|
||||||
methods: z.nativeEnum(BitBucketConnectionMethod).array()
|
methods: z.nativeEnum(BitbucketConnectionMethod).array()
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,25 +1,33 @@
|
|||||||
import { OrgServiceActor } from "@app/lib/types";
|
import { OrgServiceActor } from "@app/lib/types";
|
||||||
|
|
||||||
import { AppConnection } from "../app-connection-enums";
|
import { AppConnection } from "../app-connection-enums";
|
||||||
import { listBitBucketRepositories } from "./bitbucket-connection-fns";
|
import { listBitbucketRepositories, listBitbucketWorkspaces } from "./bitbucket-connection-fns";
|
||||||
import { TBitBucketConnection } from "./bitbucket-connection-types";
|
import { TBitbucketConnection, TGetBitbucketRepositoriesDTO } from "./bitbucket-connection-types";
|
||||||
|
|
||||||
type TGetAppConnectionFunc = (
|
type TGetAppConnectionFunc = (
|
||||||
app: AppConnection,
|
app: AppConnection,
|
||||||
connectionId: string,
|
connectionId: string,
|
||||||
actor: OrgServiceActor
|
actor: OrgServiceActor
|
||||||
) => Promise<TBitBucketConnection>;
|
) => Promise<TBitbucketConnection>;
|
||||||
|
|
||||||
export const bitBucketConnectionService = (getAppConnection: TGetAppConnectionFunc) => {
|
export const bitBucketConnectionService = (getAppConnection: TGetAppConnectionFunc) => {
|
||||||
const listRepositories = async (connectionId: string, actor: OrgServiceActor) => {
|
const listWorkspaces = async (connectionId: string, actor: OrgServiceActor) => {
|
||||||
const appConnection = await getAppConnection(AppConnection.BitBucket, connectionId, actor);
|
const appConnection = await getAppConnection(AppConnection.Bitbucket, connectionId, actor);
|
||||||
|
const workspaces = await listBitbucketWorkspaces(appConnection);
|
||||||
|
return workspaces;
|
||||||
|
};
|
||||||
|
|
||||||
const repositories = await listBitBucketRepositories(appConnection);
|
const listRepositories = async (
|
||||||
|
{ connectionId, workspaceSlug }: TGetBitbucketRepositoriesDTO,
|
||||||
return repositories.map((repo) => ({ id: repo.slug, name: repo.full_name }));
|
actor: OrgServiceActor
|
||||||
|
) => {
|
||||||
|
const appConnection = await getAppConnection(AppConnection.Bitbucket, connectionId, actor);
|
||||||
|
const repositories = await listBitbucketRepositories(appConnection, workspaceSlug);
|
||||||
|
return repositories;
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
listWorkspaces,
|
||||||
listRepositories
|
listRepositories
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,27 +4,36 @@ import { DiscriminativePick } from "@app/lib/types";
|
|||||||
|
|
||||||
import { AppConnection } from "../app-connection-enums";
|
import { AppConnection } from "../app-connection-enums";
|
||||||
import {
|
import {
|
||||||
BitBucketConnectionSchema,
|
BitbucketConnectionSchema,
|
||||||
CreateBitBucketConnectionSchema,
|
CreateBitbucketConnectionSchema,
|
||||||
ValidateBitBucketConnectionCredentialsSchema
|
ValidateBitbucketConnectionCredentialsSchema
|
||||||
} from "./bitbucket-connection-schemas";
|
} from "./bitbucket-connection-schemas";
|
||||||
|
|
||||||
export type TBitBucketConnection = z.infer<typeof BitBucketConnectionSchema>;
|
export type TBitbucketConnection = z.infer<typeof BitbucketConnectionSchema>;
|
||||||
|
|
||||||
export type TBitBucketConnectionInput = z.infer<typeof CreateBitBucketConnectionSchema> & {
|
export type TBitbucketConnectionInput = z.infer<typeof CreateBitbucketConnectionSchema> & {
|
||||||
app: AppConnection.BitBucket;
|
app: AppConnection.Bitbucket;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TValidateBitBucketConnectionCredentialsSchema = typeof ValidateBitBucketConnectionCredentialsSchema;
|
export type TValidateBitbucketConnectionCredentialsSchema = typeof ValidateBitbucketConnectionCredentialsSchema;
|
||||||
|
|
||||||
export type TBitBucketConnectionConfig = DiscriminativePick<
|
export type TBitbucketConnectionConfig = DiscriminativePick<
|
||||||
TBitBucketConnectionInput,
|
TBitbucketConnectionInput,
|
||||||
"method" | "app" | "credentials"
|
"method" | "app" | "credentials"
|
||||||
> & {
|
> & {
|
||||||
orgId: string;
|
orgId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TBitBucketRepo = {
|
export type TGetBitbucketRepositoriesDTO = {
|
||||||
|
connectionId: string;
|
||||||
|
workspaceSlug: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TBitbucketWorkspace = {
|
||||||
|
slug: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TBitbucketRepo = {
|
||||||
full_name: string; // workspace-slug/repo-slug
|
full_name: string; // workspace-slug/repo-slug
|
||||||
slug: string;
|
slug: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -814,9 +814,9 @@ const getAppsCloudflareWorkers = async ({ accessToken, accountId }: { accessToke
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return list of repositories for the BitBucket integration based on provided BitBucket workspace
|
* Return list of repositories for the Bitbucket integration based on provided Bitbucket workspace
|
||||||
*/
|
*/
|
||||||
const getAppsBitBucket = async ({ accessToken, workspaceSlug }: { accessToken: string; workspaceSlug?: string }) => {
|
const getAppsBitbucket = async ({ accessToken, workspaceSlug }: { accessToken: string; workspaceSlug?: string }) => {
|
||||||
interface RepositoriesResponse {
|
interface RepositoriesResponse {
|
||||||
size: number;
|
size: number;
|
||||||
page: number;
|
page: number;
|
||||||
@@ -1302,7 +1302,7 @@ export const getApps = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
case Integrations.BITBUCKET:
|
case Integrations.BITBUCKET:
|
||||||
return getAppsBitBucket({
|
return getAppsBitbucket({
|
||||||
accessToken,
|
accessToken,
|
||||||
workspaceSlug
|
workspaceSlug
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -342,7 +342,7 @@ export const getIntegrationOptions = async () => {
|
|||||||
{
|
{
|
||||||
name: "Bitbucket",
|
name: "Bitbucket",
|
||||||
slug: "bitbucket",
|
slug: "bitbucket",
|
||||||
image: "BitBucket.png",
|
image: "Bitbucket.png",
|
||||||
isAvailable: true,
|
isAvailable: true,
|
||||||
type: "oauth",
|
type: "oauth",
|
||||||
clientId: appCfg.CLIENT_ID_BITBUCKET,
|
clientId: appCfg.CLIENT_ID_BITBUCKET,
|
||||||
|
|||||||
@@ -3921,9 +3921,9 @@ const syncSecretsCloudflareWorkers = async ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sync/push [secrets] to BitBucket repo with name [integration.app]
|
* Sync/push [secrets] to Bitbucket repo with name [integration.app]
|
||||||
*/
|
*/
|
||||||
const syncSecretsBitBucket = async ({
|
const syncSecretsBitbucket = async ({
|
||||||
integration,
|
integration,
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken
|
||||||
@@ -4832,7 +4832,7 @@ export const syncIntegrationSecrets = async ({
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case Integrations.BITBUCKET:
|
case Integrations.BITBUCKET:
|
||||||
await syncSecretsBitBucket({
|
await syncSecretsBitbucket({
|
||||||
integration,
|
integration,
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ type ExchangeCodeGitlabResponse = {
|
|||||||
created_at: number;
|
created_at: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ExchangeCodeBitBucketResponse = {
|
type ExchangeCodeBitbucketResponse = {
|
||||||
access_token: string;
|
access_token: string;
|
||||||
token_type: string;
|
token_type: string;
|
||||||
expires_in: number;
|
expires_in: number;
|
||||||
@@ -392,10 +392,10 @@ const exchangeCodeGitlab = async ({ code, url }: { code: string; url?: string })
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return [accessToken], [accessExpiresAt], and [refreshToken] for BitBucket
|
* Return [accessToken], [accessExpiresAt], and [refreshToken] for Bitbucket
|
||||||
* code-token exchange
|
* code-token exchange
|
||||||
*/
|
*/
|
||||||
const exchangeCodeBitBucket = async ({ code }: { code: string }) => {
|
const exchangeCodeBitbucket = async ({ code }: { code: string }) => {
|
||||||
const accessExpiresAt = new Date();
|
const accessExpiresAt = new Date();
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
if (!appCfg.CLIENT_SECRET_BITBUCKET || !appCfg.CLIENT_ID_BITBUCKET) {
|
if (!appCfg.CLIENT_SECRET_BITBUCKET || !appCfg.CLIENT_ID_BITBUCKET) {
|
||||||
@@ -403,7 +403,7 @@ const exchangeCodeBitBucket = async ({ code }: { code: string }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const res = (
|
const res = (
|
||||||
await request.post<ExchangeCodeBitBucketResponse>(
|
await request.post<ExchangeCodeBitbucketResponse>(
|
||||||
IntegrationUrls.BITBUCKET_TOKEN_URL,
|
IntegrationUrls.BITBUCKET_TOKEN_URL,
|
||||||
new URLSearchParams({
|
new URLSearchParams({
|
||||||
grant_type: "authorization_code",
|
grant_type: "authorization_code",
|
||||||
@@ -490,7 +490,7 @@ export const exchangeCode = async ({
|
|||||||
url
|
url
|
||||||
});
|
});
|
||||||
case Integrations.BITBUCKET:
|
case Integrations.BITBUCKET:
|
||||||
return exchangeCodeBitBucket({
|
return exchangeCodeBitbucket({
|
||||||
code
|
code
|
||||||
});
|
});
|
||||||
default:
|
default:
|
||||||
@@ -524,7 +524,7 @@ type RefreshTokenGitLabResponse = {
|
|||||||
created_at: number;
|
created_at: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
type RefreshTokenBitBucketResponse = {
|
type RefreshTokenBitbucketResponse = {
|
||||||
access_token: string;
|
access_token: string;
|
||||||
token_type: string;
|
token_type: string;
|
||||||
expires_in: number;
|
expires_in: number;
|
||||||
@@ -653,9 +653,9 @@ const exchangeRefreshGitLab = async ({ refreshToken, url }: { url?: string | nul
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Return new access token by exchanging refresh token [refreshToken] for the
|
* Return new access token by exchanging refresh token [refreshToken] for the
|
||||||
* BitBucket integration
|
* Bitbucket integration
|
||||||
*/
|
*/
|
||||||
const exchangeRefreshBitBucket = async ({ refreshToken }: { refreshToken: string }) => {
|
const exchangeRefreshBitbucket = async ({ refreshToken }: { refreshToken: string }) => {
|
||||||
const accessExpiresAt = new Date();
|
const accessExpiresAt = new Date();
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
if (!appCfg.CLIENT_SECRET_BITBUCKET || !appCfg.CLIENT_ID_BITBUCKET) {
|
if (!appCfg.CLIENT_SECRET_BITBUCKET || !appCfg.CLIENT_ID_BITBUCKET) {
|
||||||
@@ -664,7 +664,7 @@ const exchangeRefreshBitBucket = async ({ refreshToken }: { refreshToken: string
|
|||||||
const {
|
const {
|
||||||
data
|
data
|
||||||
}: {
|
}: {
|
||||||
data: RefreshTokenBitBucketResponse;
|
data: RefreshTokenBitbucketResponse;
|
||||||
} = await request.post(
|
} = await request.post(
|
||||||
IntegrationUrls.BITBUCKET_TOKEN_URL,
|
IntegrationUrls.BITBUCKET_TOKEN_URL,
|
||||||
new URLSearchParams({
|
new URLSearchParams({
|
||||||
@@ -794,7 +794,7 @@ export const exchangeRefresh = async (
|
|||||||
url
|
url
|
||||||
});
|
});
|
||||||
case Integrations.BITBUCKET:
|
case Integrations.BITBUCKET:
|
||||||
return exchangeRefreshBitBucket({
|
return exchangeRefreshBitbucket({
|
||||||
refreshToken
|
refreshToken
|
||||||
});
|
});
|
||||||
case Integrations.GCP_SECRET_MANAGER:
|
case Integrations.GCP_SECRET_MANAGER:
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ Missing an integration? [Throw in a request](https://github.com/Infisical/infisi
|
|||||||
| [Azure Key Vault](/integrations/cloud/azure-key-vault) | Cloud | Available |
|
| [Azure Key Vault](/integrations/cloud/azure-key-vault) | Cloud | Available |
|
||||||
| [GCP Secret Manager](/integrations/cloud/gcp-secret-manager) | Cloud | Available |
|
| [GCP Secret Manager](/integrations/cloud/gcp-secret-manager) | Cloud | Available |
|
||||||
| [Windmill](/integrations/cloud/windmill) | Cloud | Available |
|
| [Windmill](/integrations/cloud/windmill) | Cloud | Available |
|
||||||
| [BitBucket](/integrations/cicd/bitbucket) | CI/CD | Available |
|
| [Bitbucket](/integrations/cicd/bitbucket) | CI/CD | Available |
|
||||||
| [Codefresh](/integrations/cicd/codefresh) | CI/CD | Available |
|
| [Codefresh](/integrations/cicd/codefresh) | CI/CD | Available |
|
||||||
| [GitHub Actions](/integrations/cicd/githubactions) | CI/CD | Available |
|
| [GitHub Actions](/integrations/cicd/githubactions) | CI/CD | Available |
|
||||||
| [GitLab](/integrations/cicd/gitlab) | CI/CD | Available |
|
| [GitLab](/integrations/cicd/gitlab) | CI/CD | Available |
|
||||||
|
|||||||
@@ -669,11 +669,11 @@ To help you sync secrets from Infisical to services such as Github and Gitlab, I
|
|||||||
|
|
||||||
<Accordion title="Bitbucket">
|
<Accordion title="Bitbucket">
|
||||||
<ParamField query="CLIENT_ID_BITBUCKET" type="string" default="none" optional>
|
<ParamField query="CLIENT_ID_BITBUCKET" type="string" default="none" optional>
|
||||||
OAuth2 client ID for BitBucket integration
|
OAuth2 client ID for Bitbucket integration
|
||||||
</ParamField>
|
</ParamField>
|
||||||
|
|
||||||
<ParamField query="CLIENT_SECRET_BITBUCKET" type="string" default="none" optional>
|
<ParamField query="CLIENT_SECRET_BITBUCKET" type="string" default="none" optional>
|
||||||
OAuth2 client secret for BitBucket integration
|
OAuth2 client secret for Bitbucket integration
|
||||||
</ParamField>
|
</ParamField>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { Controller, useFormContext, useWatch } from "react-hook-form";
|
import { Controller, useFormContext, useWatch } from "react-hook-form";
|
||||||
import { MultiValue } from "react-select";
|
import { MultiValue, SingleValue } from "react-select";
|
||||||
import { faCircleInfo } from "@fortawesome/free-solid-svg-icons";
|
import { faCircleInfo } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import { FilterableSelect, FormControl, Select, SelectItem, Tooltip } from "@app/components/v2";
|
import { FilterableSelect, FormControl, Select, SelectItem, Tooltip } from "@app/components/v2";
|
||||||
import {
|
import {
|
||||||
TBitBucketRepo,
|
TBitbucketRepo,
|
||||||
useBitBucketConnectionListRepositories
|
TBitbucketWorkspace,
|
||||||
|
useBitbucketConnectionListRepositories,
|
||||||
|
useBitbucketConnectionListWorkspaces
|
||||||
} from "@app/hooks/api/appConnections/bitbucket";
|
} from "@app/hooks/api/appConnections/bitbucket";
|
||||||
import { SecretScanningDataSource } from "@app/hooks/api/secretScanningV2";
|
import { SecretScanningDataSource } from "@app/hooks/api/secretScanningV2";
|
||||||
|
|
||||||
@@ -19,18 +21,25 @@ enum ScanMethod {
|
|||||||
SelectRepositories = "select-repositories"
|
SelectRepositories = "select-repositories"
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BitBucketDataSourceConfigFields = () => {
|
export const BitbucketDataSourceConfigFields = () => {
|
||||||
const { control, watch, setValue } = useFormContext<
|
const { control, watch, setValue } = useFormContext<
|
||||||
TSecretScanningDataSourceForm & {
|
TSecretScanningDataSourceForm & {
|
||||||
type: SecretScanningDataSource.BitBucket;
|
type: SecretScanningDataSource.Bitbucket;
|
||||||
}
|
}
|
||||||
>();
|
>();
|
||||||
|
|
||||||
const connectionId = useWatch({ control, name: "connection.id" });
|
const connectionId = useWatch({ control, name: "connection.id" });
|
||||||
const isUpdate = Boolean(watch("id"));
|
const isUpdate = Boolean(watch("id"));
|
||||||
|
|
||||||
|
const selectedWorkspaceSlug = useWatch({ control, name: "config.workspaceSlug" });
|
||||||
|
|
||||||
|
const { data: workspaces, isPending: areWorkspacesLoading } =
|
||||||
|
useBitbucketConnectionListWorkspaces(connectionId, { enabled: Boolean(connectionId) });
|
||||||
|
|
||||||
const { data: repositories, isPending: areRepositoriesLoading } =
|
const { data: repositories, isPending: areRepositoriesLoading } =
|
||||||
useBitBucketConnectionListRepositories(connectionId, { enabled: Boolean(connectionId) });
|
useBitbucketConnectionListRepositories(connectionId, selectedWorkspaceSlug, {
|
||||||
|
enabled: Boolean(connectionId) && Boolean(selectedWorkspaceSlug) // Enable only if both are present
|
||||||
|
});
|
||||||
|
|
||||||
const includeRepos = watch("config.includeRepos");
|
const includeRepos = watch("config.includeRepos");
|
||||||
|
|
||||||
@@ -51,10 +60,50 @@ export const BitBucketDataSourceConfigFields = () => {
|
|||||||
isUpdate={isUpdate}
|
isUpdate={isUpdate}
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
if (scanMethod === ScanMethod.SelectRepositories) {
|
if (scanMethod === ScanMethod.SelectRepositories) {
|
||||||
|
setValue("config.workspaceSlug", "");
|
||||||
setValue("config.includeRepos", []);
|
setValue("config.includeRepos", []);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<Controller
|
||||||
|
name="config.workspaceSlug"
|
||||||
|
control={control}
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
label="Workspace"
|
||||||
|
helperText={
|
||||||
|
<Tooltip
|
||||||
|
className="max-w-md"
|
||||||
|
content={<>Ensure that your connection has the correct permissions.</>}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<span>Don't see the workspaces you're looking for?</span>{" "}
|
||||||
|
<FontAwesomeIcon icon={faCircleInfo} className="text-mineshaft-400" />
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<FilterableSelect
|
||||||
|
menuPlacement="top"
|
||||||
|
isLoading={areWorkspacesLoading && Boolean(connectionId)}
|
||||||
|
isDisabled={!connectionId}
|
||||||
|
value={value ? { slug: value } : null}
|
||||||
|
onChange={(newValue) => {
|
||||||
|
onChange((newValue as SingleValue<TBitbucketWorkspace>)?.slug);
|
||||||
|
if (scanMethod === ScanMethod.SelectRepositories) {
|
||||||
|
setValue("config.includeRepos", []);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
options={workspaces}
|
||||||
|
placeholder="Select workspace..."
|
||||||
|
getOptionLabel={(option) => option.slug}
|
||||||
|
getOptionValue={(option) => option.slug}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<FormControl label="Scan Repositories">
|
<FormControl label="Scan Repositories">
|
||||||
<Select
|
<Select
|
||||||
value={scanMethod}
|
value={scanMethod}
|
||||||
@@ -78,7 +127,7 @@ export const BitBucketDataSourceConfigFields = () => {
|
|||||||
{scanMethod === ScanMethod.SelectRepositories && (
|
{scanMethod === ScanMethod.SelectRepositories && (
|
||||||
<Controller
|
<Controller
|
||||||
name="config.includeRepos"
|
name="config.includeRepos"
|
||||||
defaultValue={["*"]}
|
// defaultValue={["*"]} // This defaultValue is handled by the useEffect above
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl
|
||||||
@@ -100,18 +149,22 @@ export const BitBucketDataSourceConfigFields = () => {
|
|||||||
<FilterableSelect
|
<FilterableSelect
|
||||||
menuPlacement="top"
|
menuPlacement="top"
|
||||||
isLoading={areRepositoriesLoading && Boolean(connectionId)}
|
isLoading={areRepositoriesLoading && Boolean(connectionId)}
|
||||||
isDisabled={!connectionId}
|
// Disable if no connection or no workspace slug is selected for fetching repositories
|
||||||
|
isDisabled={!connectionId || !selectedWorkspaceSlug}
|
||||||
isMulti
|
isMulti
|
||||||
value={repositories?.filter((repository) => value.includes(repository.name))}
|
// Ensure 'value' is treated as a string array for filtering
|
||||||
|
value={repositories?.filter((repository) =>
|
||||||
|
(value as string[]).includes(repository.full_name)
|
||||||
|
)}
|
||||||
onChange={(newValue) => {
|
onChange={(newValue) => {
|
||||||
onChange(
|
onChange(
|
||||||
newValue ? (newValue as MultiValue<TBitBucketRepo>).map((p) => p.name) : null
|
newValue ? (newValue as MultiValue<TBitbucketRepo>).map((p) => p.full_name) : [] // Ensure it's an empty array if cleared
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
options={repositories}
|
options={repositories}
|
||||||
placeholder="Select repositories..."
|
placeholder="Select repositories..."
|
||||||
getOptionLabel={(option) => option.name}
|
getOptionLabel={(option) => option.full_name}
|
||||||
getOptionValue={(option) => option.name}
|
getOptionValue={(option) => option.full_name}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import { RESOURCE_DESCRIPTION_HELPER } from "@app/helpers/secretScanningV2";
|
|||||||
import { SecretScanningDataSource } from "@app/hooks/api/secretScanningV2";
|
import { SecretScanningDataSource } from "@app/hooks/api/secretScanningV2";
|
||||||
|
|
||||||
import { TSecretScanningDataSourceForm } from "../schemas";
|
import { TSecretScanningDataSourceForm } from "../schemas";
|
||||||
import { BitBucketDataSourceConfigFields } from "./BitBucketDataSourceConfigFields";
|
import { BitbucketDataSourceConfigFields } from "./BitbucketDataSourceConfigFields";
|
||||||
import { GitHubDataSourceConfigFields } from "./GitHubDataSourceConfigFields";
|
import { GitHubDataSourceConfigFields } from "./GitHubDataSourceConfigFields";
|
||||||
|
|
||||||
const COMPONENT_MAP: Record<SecretScanningDataSource, React.FC> = {
|
const COMPONENT_MAP: Record<SecretScanningDataSource, React.FC> = {
|
||||||
[SecretScanningDataSource.GitHub]: GitHubDataSourceConfigFields,
|
[SecretScanningDataSource.GitHub]: GitHubDataSourceConfigFields,
|
||||||
[SecretScanningDataSource.BitBucket]: BitBucketDataSourceConfigFields
|
[SecretScanningDataSource.Bitbucket]: BitbucketDataSourceConfigFields
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SecretScanningDataSourceConfigFields = () => {
|
export const SecretScanningDataSourceConfigFields = () => {
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import { SecretScanningDataSource } from "@app/hooks/api/secretScanningV2";
|
|||||||
import { TSecretScanningDataSourceForm } from "../schemas";
|
import { TSecretScanningDataSourceForm } from "../schemas";
|
||||||
import { SecretScanningDataSourceConfigReviewSection } from "./shared";
|
import { SecretScanningDataSourceConfigReviewSection } from "./shared";
|
||||||
|
|
||||||
export const BitBucketDataSourceReviewFields = () => {
|
export const BitbucketDataSourceReviewFields = () => {
|
||||||
const { watch } = useFormContext<
|
const { watch } = useFormContext<
|
||||||
TSecretScanningDataSourceForm & {
|
TSecretScanningDataSourceForm & {
|
||||||
type: SecretScanningDataSource.BitBucket;
|
type: SecretScanningDataSource.Bitbucket;
|
||||||
}
|
}
|
||||||
>();
|
>();
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import { GenericFieldLabel } from "@app/components/v2";
|
|||||||
import { SecretScanningDataSource } from "@app/hooks/api/secretScanningV2";
|
import { SecretScanningDataSource } from "@app/hooks/api/secretScanningV2";
|
||||||
|
|
||||||
import { TSecretScanningDataSourceForm } from "../schemas";
|
import { TSecretScanningDataSourceForm } from "../schemas";
|
||||||
import { BitBucketDataSourceReviewFields } from "./BitBucketDataSourceReviewFields";
|
import { BitbucketDataSourceReviewFields } from "./BitbucketDataSourceReviewFields";
|
||||||
import { GitHubDataSourceReviewFields } from "./GitHubDataSourceReviewFields";
|
import { GitHubDataSourceReviewFields } from "./GitHubDataSourceReviewFields";
|
||||||
|
|
||||||
const COMPONENT_MAP: Record<SecretScanningDataSource, React.FC> = {
|
const COMPONENT_MAP: Record<SecretScanningDataSource, React.FC> = {
|
||||||
[SecretScanningDataSource.GitHub]: GitHubDataSourceReviewFields,
|
[SecretScanningDataSource.GitHub]: GitHubDataSourceReviewFields,
|
||||||
[SecretScanningDataSource.BitBucket]: BitBucketDataSourceReviewFields
|
[SecretScanningDataSource.Bitbucket]: BitbucketDataSourceReviewFields
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SecretScanningDataSourceReviewFields = () => {
|
export const SecretScanningDataSourceReviewFields = () => {
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ import { SecretScanningDataSource } from "@app/hooks/api/secretScanningV2";
|
|||||||
|
|
||||||
import { BaseSecretScanningDataSourceSchema } from "./base-secret-scanning-data-source-schema";
|
import { BaseSecretScanningDataSourceSchema } from "./base-secret-scanning-data-source-schema";
|
||||||
|
|
||||||
export const BitBucketDataSourceSchema = z
|
export const BitbucketDataSourceSchema = z
|
||||||
.object({
|
.object({
|
||||||
type: z.literal(SecretScanningDataSource.BitBucket),
|
type: z.literal(SecretScanningDataSource.Bitbucket),
|
||||||
config: z.object({
|
config: z.object({
|
||||||
|
workspaceSlug: z.string().min(1, "Workspace Required").max(128),
|
||||||
includeRepos: z
|
includeRepos: z
|
||||||
.string()
|
.string()
|
||||||
.array()
|
.array()
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { BitBucketDataSourceSchema } from "./bitbucket-data-source-schema";
|
import { BitbucketDataSourceSchema } from "./bitbucket-data-source-schema";
|
||||||
import { GitHubDataSourceSchema } from "./github-data-source-schema";
|
import { GitHubDataSourceSchema } from "./github-data-source-schema";
|
||||||
|
|
||||||
export const SecretScanningDataSourceSchema = z.discriminatedUnion("type", [
|
export const SecretScanningDataSourceSchema = z.discriminatedUnion("type", [
|
||||||
GitHubDataSourceSchema,
|
GitHubDataSourceSchema,
|
||||||
BitBucketDataSourceSchema
|
BitbucketDataSourceSchema
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export type TSecretScanningDataSourceForm = z.infer<typeof SecretScanningDataSourceSchema>;
|
export type TSecretScanningDataSourceForm = z.infer<typeof SecretScanningDataSourceSchema>;
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ import {
|
|||||||
VercelConnectionMethod,
|
VercelConnectionMethod,
|
||||||
WindmillConnectionMethod
|
WindmillConnectionMethod
|
||||||
} from "@app/hooks/api/appConnections/types";
|
} from "@app/hooks/api/appConnections/types";
|
||||||
import { BitBucketConnectionMethod } from "@app/hooks/api/appConnections/types/bitbucket-connection";
|
import { BitbucketConnectionMethod } from "@app/hooks/api/appConnections/types/bitbucket-connection";
|
||||||
import { HerokuConnectionMethod } from "@app/hooks/api/appConnections/types/heroku-connection";
|
import { HerokuConnectionMethod } from "@app/hooks/api/appConnections/types/heroku-connection";
|
||||||
import { OCIConnectionMethod } from "@app/hooks/api/appConnections/types/oci-connection";
|
import { OCIConnectionMethod } from "@app/hooks/api/appConnections/types/oci-connection";
|
||||||
import { RenderConnectionMethod } from "@app/hooks/api/appConnections/types/render-connection";
|
import { RenderConnectionMethod } from "@app/hooks/api/appConnections/types/render-connection";
|
||||||
@@ -90,7 +90,7 @@ export const APP_CONNECTION_MAP: Record<
|
|||||||
[AppConnection.Flyio]: { name: "Fly.io", image: "Flyio.svg" },
|
[AppConnection.Flyio]: { name: "Fly.io", image: "Flyio.svg" },
|
||||||
[AppConnection.Gitlab]: { name: "GitLab", image: "GitLab.png" },
|
[AppConnection.Gitlab]: { name: "GitLab", image: "GitLab.png" },
|
||||||
[AppConnection.Cloudflare]: { name: "Cloudflare", image: "Cloudflare.png" },
|
[AppConnection.Cloudflare]: { name: "Cloudflare", image: "Cloudflare.png" },
|
||||||
[AppConnection.BitBucket]: { name: "BitBucket", image: "BitBucket.png" }
|
[AppConnection.Bitbucket]: { name: "Bitbucket", image: "Bitbucket.png" }
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAppConnectionMethodDetails = (method: TAppConnection["method"]) => {
|
export const getAppConnectionMethodDetails = (method: TAppConnection["method"]) => {
|
||||||
@@ -122,7 +122,7 @@ export const getAppConnectionMethodDetails = (method: TAppConnection["method"])
|
|||||||
case VercelConnectionMethod.ApiToken:
|
case VercelConnectionMethod.ApiToken:
|
||||||
case OnePassConnectionMethod.ApiToken:
|
case OnePassConnectionMethod.ApiToken:
|
||||||
case CloudflareConnectionMethod.ApiToken:
|
case CloudflareConnectionMethod.ApiToken:
|
||||||
case BitBucketConnectionMethod.ApiToken:
|
case BitbucketConnectionMethod.ApiToken:
|
||||||
return { name: "API Token", icon: faKey };
|
return { name: "API Token", icon: faKey };
|
||||||
case PostgresConnectionMethod.UsernameAndPassword:
|
case PostgresConnectionMethod.UsernameAndPassword:
|
||||||
case MsSqlConnectionMethod.UsernameAndPassword:
|
case MsSqlConnectionMethod.UsernameAndPassword:
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ export const SECRET_SCANNING_DATA_SOURCE_MAP: Record<
|
|||||||
image: "GitHub.png",
|
image: "GitHub.png",
|
||||||
size: 45
|
size: 45
|
||||||
},
|
},
|
||||||
[SecretScanningDataSource.BitBucket]: {
|
[SecretScanningDataSource.Bitbucket]: {
|
||||||
name: "BitBucket",
|
name: "Bitbucket",
|
||||||
image: "BitBucket.png",
|
image: "Bitbucket.png",
|
||||||
size: 45
|
size: 45
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -32,7 +32,7 @@ export const SECRET_SCANNING_DATA_SOURCE_CONNECTION_MAP: Record<
|
|||||||
AppConnection
|
AppConnection
|
||||||
> = {
|
> = {
|
||||||
[SecretScanningDataSource.GitHub]: AppConnection.GitHubRadar,
|
[SecretScanningDataSource.GitHub]: AppConnection.GitHubRadar,
|
||||||
[SecretScanningDataSource.BitBucket]: AppConnection.BitBucket
|
[SecretScanningDataSource.Bitbucket]: AppConnection.Bitbucket
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RESOURCE_DESCRIPTION_HELPER: Record<
|
export const RESOURCE_DESCRIPTION_HELPER: Record<
|
||||||
@@ -52,7 +52,7 @@ export const RESOURCE_DESCRIPTION_HELPER: Record<
|
|||||||
pluralTitle: "Repositories",
|
pluralTitle: "Repositories",
|
||||||
singularTitle: "Repository"
|
singularTitle: "Repository"
|
||||||
},
|
},
|
||||||
[SecretScanningDataSource.BitBucket]: {
|
[SecretScanningDataSource.Bitbucket]: {
|
||||||
verb: "push",
|
verb: "push",
|
||||||
pluralNoun: "repositories",
|
pluralNoun: "repositories",
|
||||||
singularNoun: "repository",
|
singularNoun: "repository",
|
||||||
|
|||||||
@@ -3,31 +3,67 @@ import { useQuery, UseQueryOptions } from "@tanstack/react-query";
|
|||||||
import { apiRequest } from "@app/config/request";
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
import { appConnectionKeys } from "../queries";
|
import { appConnectionKeys } from "../queries";
|
||||||
import { TBitBucketConnectionListRepositoriesResponse, TBitBucketRepo } from "./types";
|
import {
|
||||||
|
TBitbucketConnectionListRepositoriesResponse,
|
||||||
|
TBitbucketConnectionListWorkspacesResponse,
|
||||||
|
TBitbucketRepo,
|
||||||
|
TBitbucketWorkspace
|
||||||
|
} from "./types";
|
||||||
|
|
||||||
const bitBucketConnectionKeys = {
|
const bitBucketConnectionKeys = {
|
||||||
all: [...appConnectionKeys.all, "bitbucket"] as const,
|
all: [...appConnectionKeys.all, "bitbucket"] as const,
|
||||||
listRepos: (connectionId: string) =>
|
listRepos: (connectionId: string, workspaceSlug: string) =>
|
||||||
[...bitBucketConnectionKeys.all, "repos", connectionId] as const
|
[...bitBucketConnectionKeys.all, "repos", connectionId, workspaceSlug] as const,
|
||||||
|
listWorkspaces: (connectionId: string) =>
|
||||||
|
[...bitBucketConnectionKeys.all, "workspaces", connectionId] as const
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useBitBucketConnectionListRepositories = (
|
export const useBitbucketConnectionListWorkspaces = (
|
||||||
connectionId: string,
|
connectionId: string,
|
||||||
options?: Omit<
|
options?: Omit<
|
||||||
UseQueryOptions<
|
UseQueryOptions<
|
||||||
TBitBucketRepo[],
|
TBitbucketWorkspace[],
|
||||||
unknown,
|
unknown,
|
||||||
TBitBucketRepo[],
|
TBitbucketWorkspace[],
|
||||||
|
ReturnType<typeof bitBucketConnectionKeys.listWorkspaces>
|
||||||
|
>,
|
||||||
|
"queryKey" | "queryFn"
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: bitBucketConnectionKeys.listWorkspaces(connectionId),
|
||||||
|
queryFn: async () => {
|
||||||
|
const { data } = await apiRequest.get<TBitbucketConnectionListWorkspacesResponse>(
|
||||||
|
`/api/v1/app-connections/bitbucket/${connectionId}/workspaces`
|
||||||
|
);
|
||||||
|
|
||||||
|
return data.workspaces;
|
||||||
|
},
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useBitbucketConnectionListRepositories = (
|
||||||
|
connectionId: string,
|
||||||
|
workspaceSlug: string,
|
||||||
|
options?: Omit<
|
||||||
|
UseQueryOptions<
|
||||||
|
TBitbucketRepo[],
|
||||||
|
unknown,
|
||||||
|
TBitbucketRepo[],
|
||||||
|
// The ReturnType here will be a supertype of the actual queryKey, which is acceptable
|
||||||
ReturnType<typeof bitBucketConnectionKeys.listRepos>
|
ReturnType<typeof bitBucketConnectionKeys.listRepos>
|
||||||
>,
|
>,
|
||||||
"queryKey" | "queryFn"
|
"queryKey" | "queryFn"
|
||||||
>
|
>
|
||||||
) => {
|
) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: bitBucketConnectionKeys.listRepos(connectionId),
|
// Append workspaceSlug to the existing query key for unique caching
|
||||||
|
queryKey: bitBucketConnectionKeys.listRepos(connectionId, workspaceSlug),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const { data } = await apiRequest.get<TBitBucketConnectionListRepositoriesResponse>(
|
const { data } = await apiRequest.get<TBitbucketConnectionListRepositoriesResponse>(
|
||||||
`/api/v1/app-connections/bitbucket/${connectionId}/repositories`
|
// Include workspaceSlug in the API endpoint path
|
||||||
|
`/api/v1/app-connections/bitbucket/${connectionId}/repositories?workspaceSlug=${workspaceSlug}`
|
||||||
);
|
);
|
||||||
|
|
||||||
return data.repositories;
|
return data.repositories;
|
||||||
|
|||||||
@@ -1,8 +1,16 @@
|
|||||||
export type TBitBucketRepo = {
|
export type TBitbucketWorkspace = {
|
||||||
id: string;
|
slug: string;
|
||||||
name: string; // workspace-slug/repo-slug
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TBitBucketConnectionListRepositoriesResponse = {
|
export type TBitbucketRepo = {
|
||||||
repositories: TBitBucketRepo[];
|
slug: string;
|
||||||
|
full_name: string; // workspace-slug/repo-slug
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TBitbucketConnectionListWorkspacesResponse = {
|
||||||
|
workspaces: TBitbucketWorkspace[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TBitbucketConnectionListRepositoriesResponse = {
|
||||||
|
repositories: TBitbucketRepo[];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -28,5 +28,5 @@ export enum AppConnection {
|
|||||||
Flyio = "flyio",
|
Flyio = "flyio",
|
||||||
Gitlab = "gitlab",
|
Gitlab = "gitlab",
|
||||||
Cloudflare = "cloudflare",
|
Cloudflare = "cloudflare",
|
||||||
BitBucket = "bitbucket"
|
Bitbucket = "bitbucket"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,8 +132,8 @@ export type TCloudflareConnectionOption = TAppConnectionOptionBase & {
|
|||||||
app: AppConnection.Cloudflare;
|
app: AppConnection.Cloudflare;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TBitBucketConnectionOption = TAppConnectionOptionBase & {
|
export type TBitbucketConnectionOption = TAppConnectionOptionBase & {
|
||||||
app: AppConnection.BitBucket;
|
app: AppConnection.Bitbucket;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TAppConnectionOption =
|
export type TAppConnectionOption =
|
||||||
@@ -164,7 +164,7 @@ export type TAppConnectionOption =
|
|||||||
| TFlyioConnectionOption
|
| TFlyioConnectionOption
|
||||||
| TGitlabConnectionOption
|
| TGitlabConnectionOption
|
||||||
| TCloudflareConnectionOption
|
| TCloudflareConnectionOption
|
||||||
| TBitBucketConnectionOption;
|
| TBitbucketConnectionOption;
|
||||||
|
|
||||||
export type TAppConnectionOptionMap = {
|
export type TAppConnectionOptionMap = {
|
||||||
[AppConnection.AWS]: TAwsConnectionOption;
|
[AppConnection.AWS]: TAwsConnectionOption;
|
||||||
@@ -196,5 +196,5 @@ export type TAppConnectionOptionMap = {
|
|||||||
[AppConnection.Flyio]: TFlyioConnectionOption;
|
[AppConnection.Flyio]: TFlyioConnectionOption;
|
||||||
[AppConnection.Gitlab]: TGitlabConnectionOption;
|
[AppConnection.Gitlab]: TGitlabConnectionOption;
|
||||||
[AppConnection.Cloudflare]: TCloudflareConnectionOption;
|
[AppConnection.Cloudflare]: TCloudflareConnectionOption;
|
||||||
[AppConnection.BitBucket]: TBitBucketConnectionOption;
|
[AppConnection.Bitbucket]: TBitbucketConnectionOption;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||||
import { TRootAppConnection } from "@app/hooks/api/appConnections/types/root-connection";
|
import { TRootAppConnection } from "@app/hooks/api/appConnections/types/root-connection";
|
||||||
|
|
||||||
export enum BitBucketConnectionMethod {
|
export enum BitbucketConnectionMethod {
|
||||||
ApiToken = "api-token"
|
ApiToken = "api-token"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TBitBucketConnection = TRootAppConnection & { app: AppConnection.BitBucket } & {
|
export type TBitbucketConnection = TRootAppConnection & { app: AppConnection.Bitbucket } & {
|
||||||
method: BitBucketConnectionMethod.ApiToken;
|
method: BitbucketConnectionMethod.ApiToken;
|
||||||
credentials: {
|
credentials: {
|
||||||
email: string;
|
email: string;
|
||||||
apiToken: string;
|
apiToken: string;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { TAzureAppConfigurationConnection } from "./azure-app-configuration-conn
|
|||||||
import { TAzureClientSecretsConnection } from "./azure-client-secrets-connection";
|
import { TAzureClientSecretsConnection } from "./azure-client-secrets-connection";
|
||||||
import { TAzureDevOpsConnection } from "./azure-devops-connection";
|
import { TAzureDevOpsConnection } from "./azure-devops-connection";
|
||||||
import { TAzureKeyVaultConnection } from "./azure-key-vault-connection";
|
import { TAzureKeyVaultConnection } from "./azure-key-vault-connection";
|
||||||
import { TBitBucketConnection } from "./bitbucket-connection";
|
import { TBitbucketConnection } from "./bitbucket-connection";
|
||||||
import { TCamundaConnection } from "./camunda-connection";
|
import { TCamundaConnection } from "./camunda-connection";
|
||||||
import { TCloudflareConnection } from "./cloudflare-connection";
|
import { TCloudflareConnection } from "./cloudflare-connection";
|
||||||
import { TDatabricksConnection } from "./databricks-connection";
|
import { TDatabricksConnection } from "./databricks-connection";
|
||||||
@@ -92,7 +92,7 @@ export type TAppConnection =
|
|||||||
| TFlyioConnection
|
| TFlyioConnection
|
||||||
| TGitLabConnection
|
| TGitLabConnection
|
||||||
| TCloudflareConnection
|
| TCloudflareConnection
|
||||||
| TBitBucketConnection;
|
| TBitbucketConnection;
|
||||||
|
|
||||||
export type TAvailableAppConnection = Pick<TAppConnection, "name" | "id">;
|
export type TAvailableAppConnection = Pick<TAppConnection, "name" | "id">;
|
||||||
|
|
||||||
@@ -149,5 +149,5 @@ export type TAppConnectionMap = {
|
|||||||
[AppConnection.Flyio]: TFlyioConnection;
|
[AppConnection.Flyio]: TFlyioConnection;
|
||||||
[AppConnection.Gitlab]: TGitLabConnection;
|
[AppConnection.Gitlab]: TGitLabConnection;
|
||||||
[AppConnection.Cloudflare]: TCloudflareConnection;
|
[AppConnection.Cloudflare]: TCloudflareConnection;
|
||||||
[AppConnection.BitBucket]: TBitBucketConnection;
|
[AppConnection.Bitbucket]: TBitbucketConnection;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export {
|
|||||||
useDeleteIntegrationAuth,
|
useDeleteIntegrationAuth,
|
||||||
useDeleteIntegrationAuths,
|
useDeleteIntegrationAuths,
|
||||||
useGetIntegrationAuthApps,
|
useGetIntegrationAuthApps,
|
||||||
useGetIntegrationAuthBitBucketWorkspaces,
|
useGetIntegrationAuthBitbucketWorkspaces,
|
||||||
useGetIntegrationAuthById,
|
useGetIntegrationAuthById,
|
||||||
useGetIntegrationAuthChecklyGroups,
|
useGetIntegrationAuthChecklyGroups,
|
||||||
useGetIntegrationAuthCircleCIOrganizations,
|
useGetIntegrationAuthCircleCIOrganizations,
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import { TReactQueryOptions } from "@app/types/reactQuery";
|
|||||||
import { workspaceKeys } from "../workspace";
|
import { workspaceKeys } from "../workspace";
|
||||||
import {
|
import {
|
||||||
App,
|
App,
|
||||||
BitBucketEnvironment,
|
BitbucketEnvironment,
|
||||||
BitBucketWorkspace,
|
BitbucketWorkspace,
|
||||||
ChecklyGroup,
|
ChecklyGroup,
|
||||||
CircleCIOrganization,
|
CircleCIOrganization,
|
||||||
Environment,
|
Environment,
|
||||||
@@ -98,9 +98,9 @@ const integrationAuthKeys = {
|
|||||||
integrationAuthId: string;
|
integrationAuthId: string;
|
||||||
appId: string;
|
appId: string;
|
||||||
}) => [{ integrationAuthId, appId }, "integrationAuthRailwayServices"] as const,
|
}) => [{ integrationAuthId, appId }, "integrationAuthRailwayServices"] as const,
|
||||||
getIntegrationAuthBitBucketWorkspaces: (integrationAuthId: string) =>
|
getIntegrationAuthBitbucketWorkspaces: (integrationAuthId: string) =>
|
||||||
[{ integrationAuthId }, "integrationAuthBitbucketWorkspaces"] as const,
|
[{ integrationAuthId }, "integrationAuthBitbucketWorkspaces"] as const,
|
||||||
getIntegrationAuthBitBucketEnvironments: (
|
getIntegrationAuthBitbucketEnvironments: (
|
||||||
integrationAuthId: string,
|
integrationAuthId: string,
|
||||||
workspaceSlug: string,
|
workspaceSlug: string,
|
||||||
repoSlug: string
|
repoSlug: string
|
||||||
@@ -446,23 +446,23 @@ const fetchIntegrationAuthRailwayServices = async ({
|
|||||||
return services;
|
return services;
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchIntegrationAuthBitBucketWorkspaces = async (integrationAuthId: string) => {
|
const fetchIntegrationAuthBitbucketWorkspaces = async (integrationAuthId: string) => {
|
||||||
const {
|
const {
|
||||||
data: { workspaces }
|
data: { workspaces }
|
||||||
} = await apiRequest.get<{ workspaces: BitBucketWorkspace[] }>(
|
} = await apiRequest.get<{ workspaces: BitbucketWorkspace[] }>(
|
||||||
`/api/v1/integration-auth/${integrationAuthId}/bitbucket/workspaces`
|
`/api/v1/integration-auth/${integrationAuthId}/bitbucket/workspaces`
|
||||||
);
|
);
|
||||||
return workspaces;
|
return workspaces;
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchIntegrationAuthBitBucketEnvironments = async (
|
const fetchIntegrationAuthBitbucketEnvironments = async (
|
||||||
integrationAuthId: string,
|
integrationAuthId: string,
|
||||||
workspaceSlug: string,
|
workspaceSlug: string,
|
||||||
repoSlug: string
|
repoSlug: string
|
||||||
) => {
|
) => {
|
||||||
const {
|
const {
|
||||||
data: { environments }
|
data: { environments }
|
||||||
} = await apiRequest.get<{ environments: BitBucketEnvironment[] }>(
|
} = await apiRequest.get<{ environments: BitbucketEnvironment[] }>(
|
||||||
`/api/v1/integration-auth/${integrationAuthId}/bitbucket/environments`,
|
`/api/v1/integration-auth/${integrationAuthId}/bitbucket/environments`,
|
||||||
{
|
{
|
||||||
params: {
|
params: {
|
||||||
@@ -833,10 +833,10 @@ export const useGetIntegrationAuthRailwayServices = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetIntegrationAuthBitBucketWorkspaces = (integrationAuthId: string) => {
|
export const useGetIntegrationAuthBitbucketWorkspaces = (integrationAuthId: string) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: integrationAuthKeys.getIntegrationAuthBitBucketWorkspaces(integrationAuthId),
|
queryKey: integrationAuthKeys.getIntegrationAuthBitbucketWorkspaces(integrationAuthId),
|
||||||
queryFn: () => fetchIntegrationAuthBitBucketWorkspaces(integrationAuthId),
|
queryFn: () => fetchIntegrationAuthBitbucketWorkspaces(integrationAuthId),
|
||||||
enabled: true
|
enabled: true
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -858,7 +858,7 @@ export const useGetIntegrationAuthOctopusDeployScopeValues = (
|
|||||||
...options
|
...options
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useGetIntegrationAuthBitBucketEnvironments = (
|
export const useGetIntegrationAuthBitbucketEnvironments = (
|
||||||
{
|
{
|
||||||
integrationAuthId,
|
integrationAuthId,
|
||||||
workspaceSlug,
|
workspaceSlug,
|
||||||
@@ -871,13 +871,13 @@ export const useGetIntegrationAuthBitBucketEnvironments = (
|
|||||||
options?: TReactQueryOptions["options"]
|
options?: TReactQueryOptions["options"]
|
||||||
) => {
|
) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: integrationAuthKeys.getIntegrationAuthBitBucketEnvironments(
|
queryKey: integrationAuthKeys.getIntegrationAuthBitbucketEnvironments(
|
||||||
integrationAuthId,
|
integrationAuthId,
|
||||||
workspaceSlug,
|
workspaceSlug,
|
||||||
repoSlug
|
repoSlug
|
||||||
),
|
),
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
fetchIntegrationAuthBitBucketEnvironments(integrationAuthId, workspaceSlug, repoSlug),
|
fetchIntegrationAuthBitbucketEnvironments(integrationAuthId, workspaceSlug, repoSlug),
|
||||||
...options
|
...options
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -78,13 +78,13 @@ export type Service = {
|
|||||||
serviceId: string;
|
serviceId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BitBucketWorkspace = {
|
export type BitbucketWorkspace = {
|
||||||
uuid: string;
|
uuid: string;
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BitBucketEnvironment = {
|
export type BitbucketEnvironment = {
|
||||||
uuid: string;
|
uuid: string;
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export enum SecretScanningDataSource {
|
export enum SecretScanningDataSource {
|
||||||
GitHub = "github",
|
GitHub = "github",
|
||||||
BitBucket = "bitbucket"
|
Bitbucket = "bitbucket"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SecretScanningScanStatus {
|
export enum SecretScanningScanStatus {
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
|||||||
import { SecretScanningDataSource } from "../enums";
|
import { SecretScanningDataSource } from "../enums";
|
||||||
import { TSecretScanningDataSourceBase } from "./shared";
|
import { TSecretScanningDataSourceBase } from "./shared";
|
||||||
|
|
||||||
export type TBitBucketDataSource = TSecretScanningDataSourceBase & {
|
export type TBitbucketDataSource = TSecretScanningDataSourceBase & {
|
||||||
type: SecretScanningDataSource.BitBucket;
|
type: SecretScanningDataSource.Bitbucket;
|
||||||
config: {
|
config: {
|
||||||
includeRepos: string[];
|
includeRepos: string[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TBitBucketDataSourceOption = {
|
export type TBitbucketDataSourceOption = {
|
||||||
name: string;
|
name: string;
|
||||||
type: SecretScanningDataSource.BitBucket;
|
type: SecretScanningDataSource.Bitbucket;
|
||||||
connection: AppConnection.BitBucket;
|
connection: AppConnection.Bitbucket;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ import {
|
|||||||
SecretScanningScanStatus,
|
SecretScanningScanStatus,
|
||||||
SecretScanningScanType
|
SecretScanningScanType
|
||||||
} from "../enums";
|
} from "../enums";
|
||||||
import { TBitBucketDataSource, TBitBucketDataSourceOption } from "./bitbucket-data-source";
|
import { TBitbucketDataSource, TBitbucketDataSourceOption } from "./bitbucket-data-source";
|
||||||
import { TGitHubDataSource, TGitHubDataSourceOption } from "./github-data-source";
|
import { TGitHubDataSource, TGitHubDataSourceOption } from "./github-data-source";
|
||||||
|
|
||||||
export type TSecretScanningDataSource = TGitHubDataSource | TBitBucketDataSource;
|
export type TSecretScanningDataSource = TGitHubDataSource | TBitbucketDataSource;
|
||||||
|
|
||||||
export type TSecretScanningDataSourceWithDetails = TSecretScanningDataSource & {
|
export type TSecretScanningDataSourceWithDetails = TSecretScanningDataSource & {
|
||||||
lastScannedAt: string | null;
|
lastScannedAt: string | null;
|
||||||
@@ -24,7 +24,7 @@ export type TListSecretScanningDataSources = {
|
|||||||
dataSources: TSecretScanningDataSourceWithDetails[];
|
dataSources: TSecretScanningDataSourceWithDetails[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TSecretScanningDataSourceOption = TGitHubDataSourceOption | TBitBucketDataSourceOption;
|
export type TSecretScanningDataSourceOption = TGitHubDataSourceOption | TBitbucketDataSourceOption;
|
||||||
|
|
||||||
export type TListSecretScanningDataSourceOptions = {
|
export type TListSecretScanningDataSourceOptions = {
|
||||||
dataSourceOptions: TSecretScanningDataSourceOption[];
|
dataSourceOptions: TSecretScanningDataSourceOption[];
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { AzureAppConfigurationConnectionForm } from "./AzureAppConfigurationConn
|
|||||||
import { AzureClientSecretsConnectionForm } from "./AzureClientSecretsConnectionForm";
|
import { AzureClientSecretsConnectionForm } from "./AzureClientSecretsConnectionForm";
|
||||||
import { AzureDevOpsConnectionForm } from "./AzureDevOpsConnectionForm";
|
import { AzureDevOpsConnectionForm } from "./AzureDevOpsConnectionForm";
|
||||||
import { AzureKeyVaultConnectionForm } from "./AzureKeyVaultConnectionForm";
|
import { AzureKeyVaultConnectionForm } from "./AzureKeyVaultConnectionForm";
|
||||||
import { BitBucketConnectionForm } from "./BitBucketConnectionForm";
|
import { BitbucketConnectionForm } from "./BitbucketConnectionForm";
|
||||||
import { CamundaConnectionForm } from "./CamundaConnectionForm";
|
import { CamundaConnectionForm } from "./CamundaConnectionForm";
|
||||||
import { CloudflareConnectionForm } from "./CloudflareConnectionForm";
|
import { CloudflareConnectionForm } from "./CloudflareConnectionForm";
|
||||||
import { DatabricksConnectionForm } from "./DatabricksConnectionForm";
|
import { DatabricksConnectionForm } from "./DatabricksConnectionForm";
|
||||||
@@ -135,8 +135,8 @@ const CreateForm = ({ app, onComplete }: CreateFormProps) => {
|
|||||||
return <GitLabConnectionForm onSubmit={onSubmit} />;
|
return <GitLabConnectionForm onSubmit={onSubmit} />;
|
||||||
case AppConnection.Cloudflare:
|
case AppConnection.Cloudflare:
|
||||||
return <CloudflareConnectionForm onSubmit={onSubmit} />;
|
return <CloudflareConnectionForm onSubmit={onSubmit} />;
|
||||||
case AppConnection.BitBucket:
|
case AppConnection.Bitbucket:
|
||||||
return <BitBucketConnectionForm onSubmit={onSubmit} />;
|
return <BitbucketConnectionForm onSubmit={onSubmit} />;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unhandled App ${app}`);
|
throw new Error(`Unhandled App ${app}`);
|
||||||
}
|
}
|
||||||
@@ -231,8 +231,8 @@ const UpdateForm = ({ appConnection, onComplete }: UpdateFormProps) => {
|
|||||||
return <GitLabConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
|
return <GitLabConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
|
||||||
case AppConnection.Cloudflare:
|
case AppConnection.Cloudflare:
|
||||||
return <CloudflareConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
|
return <CloudflareConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
|
||||||
case AppConnection.BitBucket:
|
case AppConnection.Bitbucket:
|
||||||
return <BitBucketConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
|
return <BitbucketConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unhandled App ${(appConnection as TAppConnection).app}`);
|
throw new Error(`Unhandled App ${(appConnection as TAppConnection).app}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
SelectItem
|
SelectItem
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections";
|
import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections";
|
||||||
import { BitBucketConnectionMethod, TBitBucketConnection } from "@app/hooks/api/appConnections";
|
import { BitbucketConnectionMethod, TBitbucketConnection } from "@app/hooks/api/appConnections";
|
||||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -21,17 +21,17 @@ import {
|
|||||||
} from "./GenericAppConnectionFields";
|
} from "./GenericAppConnectionFields";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
appConnection?: TBitBucketConnection;
|
appConnection?: TBitbucketConnection;
|
||||||
onSubmit: (formData: FormData) => void;
|
onSubmit: (formData: FormData) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const rootSchema = genericAppConnectionFieldsSchema.extend({
|
const rootSchema = genericAppConnectionFieldsSchema.extend({
|
||||||
app: z.literal(AppConnection.BitBucket)
|
app: z.literal(AppConnection.Bitbucket)
|
||||||
});
|
});
|
||||||
|
|
||||||
const formSchema = z.discriminatedUnion("method", [
|
const formSchema = z.discriminatedUnion("method", [
|
||||||
rootSchema.extend({
|
rootSchema.extend({
|
||||||
method: z.literal(BitBucketConnectionMethod.ApiToken),
|
method: z.literal(BitbucketConnectionMethod.ApiToken),
|
||||||
credentials: z.object({
|
credentials: z.object({
|
||||||
email: z.string().email().trim().min(1, "Email required"),
|
email: z.string().email().trim().min(1, "Email required"),
|
||||||
apiToken: z.string().trim().min(1, "API Token required")
|
apiToken: z.string().trim().min(1, "API Token required")
|
||||||
@@ -41,14 +41,14 @@ const formSchema = z.discriminatedUnion("method", [
|
|||||||
|
|
||||||
type FormData = z.infer<typeof formSchema>;
|
type FormData = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
export const BitBucketConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
export const BitbucketConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
||||||
const isUpdate = Boolean(appConnection);
|
const isUpdate = Boolean(appConnection);
|
||||||
|
|
||||||
const form = useForm<FormData>({
|
const form = useForm<FormData>({
|
||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
defaultValues: appConnection ?? {
|
defaultValues: appConnection ?? {
|
||||||
app: AppConnection.BitBucket,
|
app: AppConnection.Bitbucket,
|
||||||
method: BitBucketConnectionMethod.ApiToken
|
method: BitbucketConnectionMethod.ApiToken
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ export const BitBucketConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
|||||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl
|
||||||
tooltipText={`The method you would like to use to connect with ${
|
tooltipText={`The method you would like to use to connect with ${
|
||||||
APP_CONNECTION_MAP[AppConnection.BitBucket].name
|
APP_CONNECTION_MAP[AppConnection.Bitbucket].name
|
||||||
}. This field cannot be changed after creation.`}
|
}. This field cannot be changed after creation.`}
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
isError={Boolean(error?.message)}
|
isError={Boolean(error?.message)}
|
||||||
@@ -82,7 +82,7 @@ export const BitBucketConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
|||||||
position="popper"
|
position="popper"
|
||||||
dropdownContainerClassName="max-w-none"
|
dropdownContainerClassName="max-w-none"
|
||||||
>
|
>
|
||||||
{Object.values(BitBucketConnectionMethod).map((method) => {
|
{Object.values(BitbucketConnectionMethod).map((method) => {
|
||||||
return (
|
return (
|
||||||
<SelectItem value={method} key={method}>
|
<SelectItem value={method} key={method}>
|
||||||
{getAppConnectionMethodDetails(method).name}{" "}
|
{getAppConnectionMethodDetails(method).name}{" "}
|
||||||
@@ -130,7 +130,7 @@ export const BitBucketConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
|||||||
isLoading={isSubmitting}
|
isLoading={isSubmitting}
|
||||||
isDisabled={isSubmitting || !isDirty}
|
isDisabled={isSubmitting || !isDirty}
|
||||||
>
|
>
|
||||||
{isUpdate ? "Update Credentials" : "Connect to BitBucket"}
|
{isUpdate ? "Update Credentials" : "Connect to Bitbucket"}
|
||||||
</Button>
|
</Button>
|
||||||
<ModalClose asChild>
|
<ModalClose asChild>
|
||||||
<Button colorSchema="secondary" variant="plain">
|
<Button colorSchema="secondary" variant="plain">
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ import { useWorkspace } from "@app/context";
|
|||||||
import {
|
import {
|
||||||
useCreateIntegration,
|
useCreateIntegration,
|
||||||
useGetIntegrationAuthApps,
|
useGetIntegrationAuthApps,
|
||||||
useGetIntegrationAuthBitBucketWorkspaces
|
useGetIntegrationAuthBitbucketWorkspaces
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { useGetIntegrationAuthBitBucketEnvironments } from "@app/hooks/api/integrationAuth/queries";
|
import { useGetIntegrationAuthBitbucketEnvironments } from "@app/hooks/api/integrationAuth/queries";
|
||||||
import { IntegrationsListPageTabs } from "@app/types/integrations";
|
import { IntegrationsListPageTabs } from "@app/types/integrations";
|
||||||
|
|
||||||
enum BitbucketScope {
|
enum BitbucketScope {
|
||||||
@@ -99,7 +99,7 @@ export const BitbucketConfigurePage = () => {
|
|||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
|
||||||
const { data: bitbucketWorkspaces, isPending: isBitbucketWorkspacesLoading } =
|
const { data: bitbucketWorkspaces, isPending: isBitbucketWorkspacesLoading } =
|
||||||
useGetIntegrationAuthBitBucketWorkspaces((integrationAuthId as string) ?? "");
|
useGetIntegrationAuthBitbucketWorkspaces((integrationAuthId as string) ?? "");
|
||||||
|
|
||||||
const { data: bitbucketRepos, isPending: isBitbucketReposLoading } = useGetIntegrationAuthApps({
|
const { data: bitbucketRepos, isPending: isBitbucketReposLoading } = useGetIntegrationAuthApps({
|
||||||
integrationAuthId: (integrationAuthId as string) ?? "",
|
integrationAuthId: (integrationAuthId as string) ?? "",
|
||||||
@@ -107,7 +107,7 @@ export const BitbucketConfigurePage = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { data: bitbucketEnvironments, isPending: isBitbucketEnvironmentsLoading } =
|
const { data: bitbucketEnvironments, isPending: isBitbucketEnvironmentsLoading } =
|
||||||
useGetIntegrationAuthBitBucketEnvironments(
|
useGetIntegrationAuthBitbucketEnvironments(
|
||||||
{
|
{
|
||||||
integrationAuthId: (integrationAuthId as string) ?? "",
|
integrationAuthId: (integrationAuthId as string) ?? "",
|
||||||
workspaceSlug: bitBucketWorkspace?.slug ?? "",
|
workspaceSlug: bitBucketWorkspace?.slug ?? "",
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { GenericFieldLabel } from "@app/components/v2";
|
import { GenericFieldLabel } from "@app/components/v2";
|
||||||
import { TBitBucketDataSource } from "@app/hooks/api/secretScanningV2/types/bitbucket-data-source";
|
import { TBitbucketDataSource } from "@app/hooks/api/secretScanningV2/types/bitbucket-data-source";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
dataSource: TBitBucketDataSource;
|
dataSource: TBitbucketDataSource;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BitBucketDataSourceConfigDisplay = ({ dataSource }: Props) => {
|
export const BitbucketDataSourceConfigDisplay = ({ dataSource }: Props) => {
|
||||||
const {
|
const {
|
||||||
config: { includeRepos }
|
config: { includeRepos }
|
||||||
} = dataSource;
|
} = dataSource;
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import {
|
|||||||
TSecretScanningDataSource
|
TSecretScanningDataSource
|
||||||
} from "@app/hooks/api/secretScanningV2";
|
} from "@app/hooks/api/secretScanningV2";
|
||||||
|
|
||||||
|
import { BitbucketDataSourceConfigDisplay } from "./BitbucketDataSourceConfigDisplay";
|
||||||
import { GitHubDataSourceConfigDisplay } from "./GitHubDataSourceConfigDisplay";
|
import { GitHubDataSourceConfigDisplay } from "./GitHubDataSourceConfigDisplay";
|
||||||
import { BitBucketDataSourceConfigDisplay } from "./BitBucketDataSourceConfigDisplay";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
dataSource: TSecretScanningDataSource;
|
dataSource: TSecretScanningDataSource;
|
||||||
@@ -14,8 +14,8 @@ export const DataSourceConfigDisplay = ({ dataSource }: Props) => {
|
|||||||
switch (dataSource.type) {
|
switch (dataSource.type) {
|
||||||
case SecretScanningDataSource.GitHub:
|
case SecretScanningDataSource.GitHub:
|
||||||
return <GitHubDataSourceConfigDisplay dataSource={dataSource} />;
|
return <GitHubDataSourceConfigDisplay dataSource={dataSource} />;
|
||||||
case SecretScanningDataSource.BitBucket:
|
case SecretScanningDataSource.Bitbucket:
|
||||||
return <BitBucketDataSourceConfigDisplay dataSource={dataSource} />;
|
return <BitbucketDataSourceConfigDisplay dataSource={dataSource} />;
|
||||||
default:
|
default:
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unhandled dataSource type ${(dataSource as TSecretScanningDataSource).type}`
|
`Unhandled dataSource type ${(dataSource as TSecretScanningDataSource).type}`
|
||||||
|
|||||||
Reference in New Issue
Block a user