feature: secret scanning architecture and github data source (wip)

This commit is contained in:
Scott Wilson
2025-05-28 22:21:03 -07:00
parent 931ee1e8da
commit e6c97510ca
231 changed files with 11443 additions and 1048 deletions
@@ -1,6 +1,7 @@
export enum AppConnection {
AWS = "aws",
GitHub = "github",
GitHubRadar = "github-radar",
GCP = "gcp",
AzureKeyVault = "azure-key-vault",
AzureAppConfiguration = "azure-app-configuration",
@@ -0,0 +1,2 @@
export * from "./queries";
export * from "./types";
@@ -0,0 +1,40 @@
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { appConnectionKeys } from "@app/hooks/api/appConnections";
import {
TGitHubRadarConnectionListRepositoriesResponse,
TGitHubRadarConnectionRepository
} from "./types";
const githubRadarConnectionKeys = {
all: [...appConnectionKeys.all, "github-radar"] as const,
listRepositories: (connectionId: string) =>
[...githubRadarConnectionKeys.all, "repositories", connectionId] as const
};
export const useGitHubRadarConnectionListRepositories = (
connectionId: string,
options?: Omit<
UseQueryOptions<
TGitHubRadarConnectionRepository[],
unknown,
TGitHubRadarConnectionRepository[],
ReturnType<typeof githubRadarConnectionKeys.listRepositories>
>,
"queryKey" | "queryFn"
>
) => {
return useQuery({
queryKey: githubRadarConnectionKeys.listRepositories(connectionId),
queryFn: async () => {
const { data } = await apiRequest.get<TGitHubRadarConnectionListRepositoriesResponse>(
`/api/v1/app-connections/github-radar/${connectionId}/repositories`
);
return data.repositories;
},
...options
});
};
@@ -0,0 +1,14 @@
export type TGitHubRadarConnectionOrganization = {
login: string;
id: number;
};
export type TGitHubRadarConnectionRepository = {
id: number;
name: string;
owner: TGitHubRadarConnectionOrganization;
};
export type TGitHubRadarConnectionListRepositoriesResponse = {
repositories: TGitHubRadarConnectionRepository[];
};
@@ -17,6 +17,11 @@ export type TGitHubConnectionOption = TAppConnectionOptionBase & {
appClientSlug?: string;
};
export type TGitHubRadarConnectionOption = TAppConnectionOptionBase & {
app: AppConnection.GitHubRadar;
appClientSlug?: string;
};
export type TGcpConnectionOption = TAppConnectionOptionBase & {
app: AppConnection.GCP;
};
@@ -111,6 +116,7 @@ export type TAppConnectionOption =
export type TAppConnectionOptionMap = {
[AppConnection.AWS]: TAwsConnectionOption;
[AppConnection.GitHub]: TGitHubConnectionOption;
[AppConnection.GitHubRadar]: TGitHubRadarConnectionOption;
[AppConnection.GCP]: TGcpConnectionOption;
[AppConnection.AzureKeyVault]: TAzureKeyVaultConnectionOption;
[AppConnection.AzureAppConfiguration]: TAzureAppConfigurationConnectionOption;
@@ -0,0 +1,14 @@
import { AppConnection } from "@app/hooks/api/appConnections/enums";
import { TRootAppConnection } from "@app/hooks/api/appConnections/types/root-connection";
export enum GitHubRadarConnectionMethod {
App = "github-app"
}
export type TGitHubRadarConnection = TRootAppConnection & { app: AppConnection.GitHubRadar } & {
method: GitHubRadarConnectionMethod.App;
credentials: {
code: string;
installationId: string;
};
};
@@ -9,6 +9,7 @@ import { TCamundaConnection } from "./camunda-connection";
import { TDatabricksConnection } from "./databricks-connection";
import { TGcpConnection } from "./gcp-connection";
import { TGitHubConnection } from "./github-connection";
import { TGitHubRadarConnection } from "./github-radar-connection";
import { THCVaultConnection } from "./hc-vault-connection";
import { THumanitecConnection } from "./humanitec-connection";
import { TLdapConnection } from "./ldap-connection";
@@ -29,6 +30,7 @@ export * from "./camunda-connection";
export * from "./databricks-connection";
export * from "./gcp-connection";
export * from "./github-connection";
export * from "./github-radar-connection";
export * from "./hc-vault-connection";
export * from "./humanitec-connection";
export * from "./ldap-connection";
@@ -43,6 +45,7 @@ export * from "./windmill-connection";
export type TAppConnection =
| TAwsConnection
| TGitHubConnection
| TGitHubRadarConnection
| TGcpConnection
| TAzureKeyVaultConnection
| TAzureAppConfigurationConnection
@@ -89,6 +92,7 @@ export type TDeleteAppConnectionDTO = {
export type TAppConnectionMap = {
[AppConnection.AWS]: TAwsConnection;
[AppConnection.GitHub]: TGitHubConnection;
[AppConnection.GitHubRadar]: TGitHubRadarConnection;
[AppConnection.GCP]: TGcpConnection;
[AppConnection.AzureKeyVault]: TAzureKeyVaultConnection;
[AppConnection.AzureAppConfiguration]: TAzureAppConfigurationConnection;
+12 -1
View File
@@ -189,7 +189,18 @@ export const eventToNameMap: { [K in EventType]: string } = {
[EventType.ADD_IDENTITY_LDAP_AUTH]: "Attached LDAP Auth to identity",
[EventType.UPDATE_IDENTITY_LDAP_AUTH]: "Updated LDAP Auth for identity",
[EventType.GET_IDENTITY_LDAP_AUTH]: "Retrieved LDAP Auth for identity",
[EventType.REVOKE_IDENTITY_LDAP_AUTH]: "Revoked LDAP Auth for identity"
[EventType.REVOKE_IDENTITY_LDAP_AUTH]: "Revoked LDAP Auth for identity",
[EventType.SECRET_SCANNING_DATA_SOURCE_LIST]: "List Secret Scanning Data Sources",
[EventType.SECRET_SCANNING_DATA_SOURCE_CREATE]: "Create Secret Scanning Data Source",
[EventType.SECRET_SCANNING_DATA_SOURCE_UPDATE]: "Update Secret Scanning Data Source",
[EventType.SECRET_SCANNING_DATA_SOURCE_DELETE]: "Delete Secret Scanning Data Source",
[EventType.SECRET_SCANNING_DATA_SOURCE_GET]: "Get Secret Scanning Data Source",
[EventType.SECRET_SCANNING_DATA_SOURCE_SCAN]: "San Scanning Data Source",
[EventType.SECRET_SCANNING_RESOURCE_LIST]: "List Secret Scanning Resources",
[EventType.SECRET_SCANNING_SCAN_LIST]: "List Secret Scanning Scans",
[EventType.SECRET_SCANNING_FINDING_LIST]: "List Secret Scanning Findings",
[EventType.SECRET_SCANNING_FINDING_UPDATE]: "Update Secret Scanning Finding Status"
};
export const userAgentTypeToNameMap: { [K in UserAgentType]: string } = {
+12 -1
View File
@@ -183,5 +183,16 @@ export enum EventType {
MICROSOFT_TEAMS_WORKFLOW_INTEGRATION_CHECK_INSTALLATION_STATUS = "microsoft-teams-workflow-integration-check-installation-status",
MICROSOFT_TEAMS_WORKFLOW_INTEGRATION_GET_TEAMS = "microsoft-teams-workflow-integration-get-teams",
MICROSOFT_TEAMS_WORKFLOW_INTEGRATION_GET = "microsoft-teams-workflow-integration-get",
MICROSOFT_TEAMS_WORKFLOW_INTEGRATION_LIST = "microsoft-teams-workflow-integration-list"
MICROSOFT_TEAMS_WORKFLOW_INTEGRATION_LIST = "microsoft-teams-workflow-integration-list",
SECRET_SCANNING_DATA_SOURCE_LIST = "secret-scanning-data-source-list",
SECRET_SCANNING_DATA_SOURCE_CREATE = "secret-scanning-data-source-create",
SECRET_SCANNING_DATA_SOURCE_UPDATE = "secret-scanning-data-source-update",
SECRET_SCANNING_DATA_SOURCE_DELETE = "secret-scanning-data-source-delete",
SECRET_SCANNING_DATA_SOURCE_GET = "secret-scanning-data-source-get",
SECRET_SCANNING_DATA_SOURCE_SCAN = "secret-scanning-data-source-scan",
SECRET_SCANNING_RESOURCE_LIST = "secret-scanning-resource-list",
SECRET_SCANNING_SCAN_LIST = "secret-scanning-scan-list",
SECRET_SCANNING_FINDING_LIST = "secret-scanning-finding-list",
SECRET_SCANNING_FINDING_UPDATE = "secret-scanning-finding-update"
}
@@ -0,0 +1,33 @@
export enum SecretScanningDataSource {
GitHub = "github"
}
export enum SecretScanningScanStatus {
Completed = "completed",
Failed = "failed",
Queued = "queued",
Scanning = "scanning"
}
export enum SecretScanningScanType {
FullScan = "full-scan",
DiffScan = "diff-scan"
}
export enum SecretScanningFindingStatus {
Resolved = "resolved",
Unresolved = "unresolved",
FalsePositive = "false-positive",
Ignore = "ignore"
}
export enum SecretScanningResource {
Repository = "repository",
Project = "project"
}
export enum SecretScanningFindingSeverity {
High = "high",
Medium = "medium",
Low = "low"
}
@@ -0,0 +1,4 @@
export * from "./enums";
export * from "./mutations";
export * from "./queries";
export * from "./types";
@@ -0,0 +1,134 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { secretScanningV2Keys } from "./queries";
import {
TCreateSecretScanningDataSourceDTO,
TDeleteSecretScanningDataSourceDTO,
TSecretScanningDataSourceResponse,
TSecretScanningFindingResponse,
TTriggerSecretScanningDataSourceDTO,
TUpdateSecretScanningDataSourceDTO,
TUpdateSecretScanningFinding
} from "./types";
export const useCreateSecretScanningDataSource = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ type, ...params }: TCreateSecretScanningDataSourceDTO) => {
const { data } = await apiRequest.post<TSecretScanningDataSourceResponse>(
`/api/v2/secret-scanning/data-sources/${type}`,
params
);
return data.dataSource;
},
onSuccess: (_, { projectId }) =>
queryClient.invalidateQueries({
queryKey: secretScanningV2Keys.listDataSources(projectId)
})
});
};
export const useUpdateSecretScanningDataSource = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ type, dataSourceId, ...params }: TUpdateSecretScanningDataSourceDTO) => {
const { data } = await apiRequest.patch<TSecretScanningDataSourceResponse>(
`/api/v2/secret-scanning/data-sources/${type}/${dataSourceId}`,
params
);
return data.dataSource;
},
onSuccess: (_, { projectId, dataSourceId }) => {
queryClient.invalidateQueries({
queryKey: secretScanningV2Keys.listDataSources(projectId)
});
queryClient.invalidateQueries({
queryKey: secretScanningV2Keys.dataSourceById(dataSourceId)
});
queryClient.invalidateQueries({
queryKey: secretScanningV2Keys.listResources(dataSourceId)
});
queryClient.invalidateQueries({
queryKey: secretScanningV2Keys.listScans(dataSourceId)
});
}
});
};
export const useDeleteSecretScanningDataSource = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ type, dataSourceId }: TDeleteSecretScanningDataSourceDTO) => {
const { data } = await apiRequest.delete<TSecretScanningDataSourceResponse>(
`/api/v2/secret-scanning/data-sources/${type}/${dataSourceId}`
);
return data.dataSource;
},
onSuccess: (_, { projectId, dataSourceId }) => {
queryClient.invalidateQueries({
queryKey: secretScanningV2Keys.listDataSources(projectId)
});
queryClient.invalidateQueries({
queryKey: secretScanningV2Keys.dataSourceById(dataSourceId)
});
queryClient.invalidateQueries({
queryKey: secretScanningV2Keys.listResources(dataSourceId)
});
queryClient.invalidateQueries({
queryKey: secretScanningV2Keys.listScans(dataSourceId)
});
}
});
};
export const useTriggerSecretScanningDataSource = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ type, dataSourceId, resourceId }: TTriggerSecretScanningDataSourceDTO) => {
const { data } = await apiRequest.post<TSecretScanningDataSourceResponse>(
`/api/v2/secret-scanning/data-sources/${type}/${dataSourceId}${resourceId ? `/resources/${resourceId}` : ""}/scan`
);
return data.dataSource;
},
onSuccess: (_, { projectId, dataSourceId }) => {
queryClient.invalidateQueries({
queryKey: secretScanningV2Keys.listDataSources(projectId)
});
queryClient.invalidateQueries({
queryKey: secretScanningV2Keys.dataSourceById(dataSourceId)
});
queryClient.invalidateQueries({
queryKey: secretScanningV2Keys.listResources(dataSourceId)
});
queryClient.invalidateQueries({
queryKey: secretScanningV2Keys.listScans(dataSourceId)
});
}
});
};
export const useUpdateSecretScanningFinding = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ findingId, ...params }: TUpdateSecretScanningFinding) => {
const { data } = await apiRequest.patch<TSecretScanningFindingResponse>(
`/api/v2/secret-scanning/findings/${findingId}`,
params
);
return data.finding;
},
onSuccess: (_, { projectId }) => {
queryClient.invalidateQueries({
queryKey: secretScanningV2Keys.listFindings(projectId)
});
// TODO: data source queries
}
});
};
@@ -0,0 +1,218 @@
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import {
TGetSecretScanningDataSource,
TGetSecretScanningUnresolvedFindingsResponse,
TListSecretScanningDataSourceOptions,
TListSecretScanningDataSources,
TListSecretScanningFindingsResponse,
TListSecretScanningResourcesResponse,
TListSecretScanningScansResponse,
TSecretScanningDataSource,
TSecretScanningDataSourceOption,
TSecretScanningDataSourceResponse,
TSecretScanningDataSourceWithDetails,
TSecretScanningFinding,
TSecretScanningResourceWithDetails,
TSecretScanningScanWithDetails
} from "./types";
export const secretScanningV2Keys = {
all: ["secret-scanning-v2"] as const,
dataSource: () => [...secretScanningV2Keys.all, "data-source"] as const,
dataSourceOptions: () => [...secretScanningV2Keys.dataSource(), "options"] as const,
dataSourceById: (dataSourceId: string) => [
...secretScanningV2Keys.dataSource(),
"byId",
dataSourceId
],
listDataSources: (projectId: string) => [...secretScanningV2Keys.dataSource(), "list", projectId],
listResources: (dataSourceId: string) => [
...secretScanningV2Keys.dataSource(),
"resources",
dataSourceId
],
listScans: (dataSourceId: string) => [
...secretScanningV2Keys.dataSource(),
"scans",
dataSourceId
],
finding: () => [...secretScanningV2Keys.all, "finding"] as const,
findingCount: (projectId: string) =>
[...secretScanningV2Keys.finding(), "count", projectId] as const,
listFindings: (projectId: string) => [...secretScanningV2Keys.finding(), "list", projectId]
};
export const useSecretScanningDataSourceOptions = (
options?: Omit<
UseQueryOptions<
TSecretScanningDataSourceOption[],
unknown,
TSecretScanningDataSourceOption[],
ReturnType<typeof secretScanningV2Keys.dataSourceOptions>
>,
"queryKey" | "queryFn"
>
) => {
return useQuery({
queryKey: secretScanningV2Keys.dataSourceOptions(),
queryFn: async () => {
const { data } = await apiRequest.get<TListSecretScanningDataSourceOptions>(
"/api/v2/secret-scanning/data-sources/options"
);
return data.dataSourceOptions;
},
...options
});
};
export const useListSecretScanningDataSources = (
projectId: string,
options?: Omit<
UseQueryOptions<
TSecretScanningDataSourceWithDetails[],
unknown,
TSecretScanningDataSourceWithDetails[],
ReturnType<typeof secretScanningV2Keys.listDataSources>
>,
"queryKey" | "queryFn"
>
) => {
return useQuery({
queryKey: secretScanningV2Keys.listDataSources(projectId),
queryFn: async () => {
const { data } = await apiRequest.get<TListSecretScanningDataSources>(
"/api/v2/secret-scanning/data-sources-dashboard",
{ params: { projectId } }
);
return data.dataSources;
},
...options
});
};
export const useGetSecretScanningDataSource = (
{ dataSourceId, type }: TGetSecretScanningDataSource,
options?: Omit<
UseQueryOptions<
TSecretScanningDataSource,
unknown,
TSecretScanningDataSource,
ReturnType<typeof secretScanningV2Keys.dataSourceById>
>,
"queryKey" | "queryFn"
>
) => {
return useQuery({
queryKey: secretScanningV2Keys.dataSourceById(dataSourceId),
queryFn: async () => {
const { data } = await apiRequest.get<TSecretScanningDataSourceResponse>(
`/api/v2/secret-scanning/data-sources/${type}/${dataSourceId}`
);
return data.dataSource;
},
...options
});
};
export const useListSecretScanningResources = (
{ dataSourceId, type }: TGetSecretScanningDataSource,
options?: Omit<
UseQueryOptions<
TSecretScanningResourceWithDetails[],
unknown,
TSecretScanningResourceWithDetails[],
ReturnType<typeof secretScanningV2Keys.listResources>
>,
"queryKey" | "queryFn"
>
) => {
return useQuery({
queryKey: secretScanningV2Keys.listResources(dataSourceId),
queryFn: async () => {
const { data } = await apiRequest.get<TListSecretScanningResourcesResponse>(
`/api/v2/secret-scanning/data-sources/${type}/${dataSourceId}/resources-dashboard`
);
return data.resources;
},
...options
});
};
export const useListSecretScanningScans = (
{ dataSourceId, type }: TGetSecretScanningDataSource,
options?: Omit<
UseQueryOptions<
TSecretScanningScanWithDetails[],
unknown,
TSecretScanningScanWithDetails[],
ReturnType<typeof secretScanningV2Keys.listScans>
>,
"queryKey" | "queryFn"
>
) => {
return useQuery({
queryKey: secretScanningV2Keys.listScans(dataSourceId),
queryFn: async () => {
const { data } = await apiRequest.get<TListSecretScanningScansResponse>(
`/api/v2/secret-scanning/data-sources/${type}/${dataSourceId}/scans-dashboard`
);
return data.scans;
},
...options
});
};
export const useGetSecretScanningUnresolvedFindingCount = (
projectId: string,
options?: Omit<
UseQueryOptions<number, unknown, number, ReturnType<typeof secretScanningV2Keys.findingCount>>,
"queryKey" | "queryFn"
>
) => {
return useQuery({
queryKey: secretScanningV2Keys.findingCount(projectId),
queryFn: async () => {
const { data } = await apiRequest.get<TGetSecretScanningUnresolvedFindingsResponse>(
"/api/v2/secret-scanning/unresolved-findings-count",
{ params: { projectId } }
);
return data.unresolvedFindings;
},
...options
});
};
export const useListSecretScanningFindings = (
projectId: string,
options?: Omit<
UseQueryOptions<
TSecretScanningFinding[],
unknown,
TSecretScanningFinding[],
ReturnType<typeof secretScanningV2Keys.listFindings>
>,
"queryKey" | "queryFn"
>
) => {
return useQuery({
queryKey: secretScanningV2Keys.listFindings(projectId),
queryFn: async () => {
const { data } = await apiRequest.get<TListSecretScanningFindingsResponse>(
"/api/v2/secret-scanning/findings",
{ params: { projectId } }
);
return data.findings;
},
...options
});
};
@@ -0,0 +1,17 @@
import { AppConnection } from "@app/hooks/api/appConnections/enums";
import { SecretScanningDataSource } from "../enums";
import { TSecretScanningDataSourceBase } from "./shared";
export type TGitHubDataSource = TSecretScanningDataSourceBase & {
type: SecretScanningDataSource.GitHub;
config: {
includeRepos: string[];
};
};
export type TGitHubDataSourceOption = {
name: string;
type: SecretScanningDataSource.GitHub;
connection: AppConnection.GitHub;
};
@@ -0,0 +1,157 @@
import { DiscriminativePick } from "@app/types";
import {
SecretScanningDataSource,
SecretScanningFindingSeverity,
SecretScanningFindingStatus,
SecretScanningResource,
SecretScanningScanStatus,
SecretScanningScanType
} from "../enums";
import { TGitHubDataSource, TGitHubDataSourceOption } from "./github-data-source";
export type TSecretScanningDataSource = TGitHubDataSource;
export type TSecretScanningDataSourceWithDetails = TSecretScanningDataSource & {
lastScannedAt: string | null;
lastScanStatus: SecretScanningScanStatus | null;
lastScanStatusMessage: string | null;
unresolvedFindings: number | null;
};
export type TListSecretScanningDataSources = {
dataSources: TSecretScanningDataSourceWithDetails[];
};
export type TSecretScanningDataSourceOption = TGitHubDataSourceOption;
export type TListSecretScanningDataSourceOptions = {
dataSourceOptions: TSecretScanningDataSourceOption[];
};
export type TSecretScanningDataSourceResponse = { dataSource: TSecretScanningDataSource };
export type TCreateSecretScanningDataSourceDTO = DiscriminativePick<
TSecretScanningDataSource,
"name" | "config" | "description" | "connectionId" | "type" | "isAutoScanEnabled" | "projectId"
>;
export type TUpdateSecretScanningDataSourceDTO = Partial<
Omit<TCreateSecretScanningDataSourceDTO, "type" | "connectionId" | "projectId">
> & {
type: SecretScanningDataSource;
dataSourceId: string;
// required for query invalidation
projectId: string;
};
export type TDeleteSecretScanningDataSourceDTO = {
type: SecretScanningDataSource;
dataSourceId: string;
// required for query invalidation
projectId: string;
};
export type TTriggerSecretScanningDataSourceDTO = {
type: SecretScanningDataSource;
dataSourceId: string;
resourceId?: string;
// required for query invalidation
projectId: string;
};
export type TUpdateSecretScanningFinding = {
findingId: string;
status: SecretScanningFindingStatus;
remarks?: string | null;
// required for query invalidation
projectId: string;
};
export type TSecretScanningFindingResponse = {
finding: TSecretScanningFinding;
};
export type TGetSecretScanningDataSource = {
dataSourceId: string;
type: SecretScanningDataSource;
};
export type TSecretScanningResourceWithDetails = {
id: string;
dataSourceId: string;
type: SecretScanningResource;
externalId: string;
name: string;
createdAt: string;
updatedAt: string;
lastScannedAt: string;
lastScanStatus: SecretScanningScanStatus;
lastScanStatusMessage: string | null;
unresolvedFindings: number;
};
export type TListSecretScanningResourcesResponse = {
resources: TSecretScanningResourceWithDetails[];
};
export type TSecretScanningScanWithDetails = {
id: string;
createdAt: string;
resourceId: string;
type: SecretScanningScanType;
status: SecretScanningScanStatus;
statusMessage?: string | null;
unresolvedFindings: number;
resolvedFindings: number;
resourceName: string;
};
export type TListSecretScanningScansResponse = {
scans: TSecretScanningScanWithDetails[];
};
export type TGetSecretScanningUnresolvedFindingsResponse = {
unresolvedFindings: number;
};
export type TSecretScanningFinding = {
id: string;
dataSourceName: string;
dataSourceType: SecretScanningDataSource;
resourceName: string;
resourceType: SecretScanningResource;
rule: string;
severity: SecretScanningFindingSeverity;
status: SecretScanningFindingStatus;
remarks?: string;
fingerprint: string;
// TODO scott: this will need to be type differentiated once we add other scan types
details: {
description: string;
startLine: number;
endLine: number;
startColumn: number;
endColumn: number;
file: string;
link: string;
symlinkFile: string;
commit: string;
entropy: string;
author: string;
email: string;
date: string;
message: string;
tags: string[];
ruleID: string;
fingerprint: string;
};
projectId: string;
scanId: string;
createdAt: string;
updatedAt: string;
};
export type TListSecretScanningFindingsResponse = {
findings: TSecretScanningFinding[];
};
@@ -0,0 +1 @@
export * from "./secret-scanning-data-source-base";
@@ -0,0 +1,17 @@
import { AppConnection } from "@app/hooks/api/appConnections/enums";
export type TSecretScanningDataSourceBase = {
id: string;
name: string;
description?: string | null;
connectionId?: string | null;
createdAt: string;
updatedAt: string;
projectId: string;
connection: {
app: AppConnection;
id: string;
name: string;
};
isAutoScanEnabled: boolean;
};
+2 -1
View File
@@ -12,7 +12,8 @@ export enum ProjectType {
SecretManager = "secret-manager",
CertificateManager = "cert-manager",
KMS = "kms",
SSH = "ssh"
SSH = "ssh",
SecretScanning = "secret-scanning"
}
export enum ProjectUserMembershipTemporaryMode {
+4 -2
View File
@@ -6,16 +6,18 @@ import { useDebounce } from "@app/hooks/useDebounce";
export const usePagination = <T extends string>(
initialOrderBy: T,
{
initPerPage = 100
initPerPage = 100,
initSearch = ""
}: {
initPerPage?: number;
initSearch?: string;
} = {}
) => {
const [page, setPage] = useState(1);
const [perPage, setPerPage] = useState(initPerPage);
const [orderDirection, setOrderDirection] = useState(OrderByDirection.ASC);
const [orderBy, setOrderBy] = useState<T>(initialOrderBy);
const [search, setSearch] = useState("");
const [search, setSearch] = useState(initSearch);
const [debouncedSearch] = useDebounce(search);
const offset = (page - 1) * perPage;