Merge pull request #3677 from Infisical/secret-scanning-v2-pt-1

feature(secret-scanning-v2): secret scanning architecture and github data source
This commit is contained in:
Scott Wilson
2025-06-03 16:34:29 -07:00
committed by GitHub
344 changed files with 13130 additions and 1118 deletions
@@ -87,7 +87,7 @@ export const GroupsSection = () => {
<GroupTable handlePopUpOpen={handlePopUpOpen} />
<DeleteActionModal
isOpen={popUp.deleteGroup.isOpen}
title={`Are you sure want to remove the group ${
title={`Are you sure you want to remove the group ${
(popUp?.deleteGroup?.data as { name: string })?.name || ""
} from the project?`}
onChange={(isOpen) => handlePopUpToggle("deleteGroup", isOpen)}
@@ -432,7 +432,7 @@ export const IdentityTab = withProjectPermission(
<IdentityModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
<DeleteActionModal
isOpen={popUp.deleteIdentity.isOpen}
title={`Are you sure want to remove ${
title={`Are you sure you want to remove ${
(popUp?.deleteIdentity?.data as { name: string })?.name || ""
} from the project?`}
onChange={(isOpen) => handlePopUpToggle("deleteIdentity", isOpen)}
@@ -195,7 +195,7 @@ export const ProjectRoleList = () => {
<RoleModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
<DeleteActionModal
isOpen={popUp.deleteRole.isOpen}
title={`Are you sure want to delete ${
title={`Are you sure you want to delete ${
(popUp?.deleteRole?.data as TProjectRole)?.name || " "
} role?`}
deleteKey={(popUp?.deleteRole?.data as TProjectRole)?.slug || ""}
@@ -0,0 +1,38 @@
import { createFileRoute, linkOptions, stripSearchParams } from "@tanstack/react-router";
import { zodValidator } from "@tanstack/zod-adapter";
import { z } from "zod";
import { ProjectAccessControlTabs } from "@app/types/project";
import { AccessControlPage } from "./AccessControlPage";
const AccessControlPageQuerySchema = z.object({
selectedTab: z.nativeEnum(ProjectAccessControlTabs).catch(ProjectAccessControlTabs.Member),
requesterEmail: z.string().catch("")
});
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/secret-scanning/$projectId/_secret-scanning-layout/access-management"
)({
component: AccessControlPage,
validateSearch: zodValidator(AccessControlPageQuerySchema),
search: {
middlewares: [stripSearchParams({ requesterEmail: "" })]
},
beforeLoad: ({ context, params }) => {
return {
breadcrumbs: [
...context.breadcrumbs,
{
label: "Access Control",
link: linkOptions({
to: "/secret-scanning/$projectId/access-management",
params: {
projectId: params.projectId
}
})
}
]
};
}
});
@@ -184,7 +184,7 @@ const Page = () => {
/>
<DeleteActionModal
isOpen={popUp.deleteIdentity.isOpen}
title={`Are you sure want to remove ${identityMembershipDetails?.identity?.name} from the project?`}
title={`Are you sure you want to remove ${identityMembershipDetails?.identity?.name} from the project?`}
onChange={(isOpen) => handlePopUpToggle("deleteIdentity", isOpen)}
deleteKey="remove"
onDeleteApproved={() => onRemoveIdentitySubmit()}
@@ -0,0 +1,28 @@
import { createFileRoute, linkOptions } from "@tanstack/react-router";
import { IdentityDetailsByIDPage } from "./IdentityDetailsByIDPage";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/secret-scanning/$projectId/_secret-scanning-layout/identities/$identityId"
)({
component: IdentityDetailsByIDPage,
beforeLoad: ({ context, params }) => {
return {
breadcrumbs: [
...context.breadcrumbs,
{
label: "Access Control",
link: linkOptions({
to: "/secret-scanning/$projectId/access-management",
params: {
projectId: params.projectId
}
})
},
{
label: "Identities"
}
]
};
}
});
@@ -28,6 +28,7 @@ import {
useGetWorkspaceUserDetails
} from "@app/hooks/api";
import { ActorType } from "@app/hooks/api/auditLogs/enums";
import { ProjectType } from "@app/hooks/api/workspace/types";
import { MemberProjectAdditionalPrivilegeSection } from "./components/MemberProjectAdditionalPrivilegeSection";
import { MemberRoleDetailsSection } from "./components/MemberRoleDetailsSection";
@@ -70,7 +71,21 @@ export const Page = () => {
type: "success",
text: "User privilege assumption has started"
});
window.location.href = `/${currentWorkspace.type}/${currentWorkspace.id}/overview`;
let page: string;
switch (currentWorkspace.type) {
case ProjectType.SecretScanning:
page = "data-sources";
break;
case ProjectType.CertificateManager:
page = "subscribers";
break;
default:
page = "overview";
}
window.location.href = `/${currentWorkspace.type}/${currentWorkspace.id}/${page}`;
}
}
);
@@ -0,0 +1,28 @@
import { createFileRoute, linkOptions } from "@tanstack/react-router";
import { MemberDetailsByIDPage } from "./MemberDetailsByIDPage";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/secret-scanning/$projectId/_secret-scanning-layout/members/$membershipId"
)({
component: MemberDetailsByIDPage,
beforeLoad: ({ context, params }) => {
return {
breadcrumbs: [
...context.breadcrumbs,
{
label: "Access Control",
link: linkOptions({
to: "/secret-scanning/$projectId/access-management",
params: {
projectId: params.projectId
}
})
},
{
label: "Users"
}
]
};
}
});
@@ -170,7 +170,7 @@ const Page = () => {
<RoleModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
<DeleteActionModal
isOpen={popUp.deleteRole.isOpen}
title={`Are you sure want to delete the project role ${data?.name ?? ""}?`}
title={`Are you sure you want to delete the project role ${data?.name ?? ""}?`}
onChange={(isOpen) => handlePopUpToggle("deleteRole", isOpen)}
deleteKey="confirm"
onDeleteApproved={() => onDeleteRoleSubmit()}
@@ -21,6 +21,9 @@ import {
ProjectPermissionPkiTemplateActions,
ProjectPermissionSecretActions,
ProjectPermissionSecretRotationActions,
ProjectPermissionSecretScanningConfigActions,
ProjectPermissionSecretScanningDataSourceActions,
ProjectPermissionSecretScanningFindingActions,
ProjectPermissionSecretSyncActions,
ProjectPermissionSshHostActions,
TPermissionCondition,
@@ -98,6 +101,26 @@ const SecretRotationPolicyActionSchema = z.object({
[ProjectPermissionSecretRotationActions.RotateSecrets]: z.boolean().optional()
});
const SecretScanningDataSourcePolicyActionSchema = z.object({
[ProjectPermissionSecretScanningDataSourceActions.Read]: z.boolean().optional(),
[ProjectPermissionSecretScanningDataSourceActions.Create]: z.boolean().optional(),
[ProjectPermissionSecretScanningDataSourceActions.Edit]: z.boolean().optional(),
[ProjectPermissionSecretScanningDataSourceActions.Delete]: z.boolean().optional(),
[ProjectPermissionSecretScanningDataSourceActions.ReadScans]: z.boolean().optional(),
[ProjectPermissionSecretScanningDataSourceActions.ReadResources]: z.boolean().optional(),
[ProjectPermissionSecretScanningDataSourceActions.TriggerScans]: z.boolean().optional()
});
const SecretScanningFindingPolicyActionSchema = z.object({
[ProjectPermissionSecretScanningFindingActions.Read]: z.boolean().optional(),
[ProjectPermissionSecretScanningFindingActions.Update]: z.boolean().optional()
});
const SecretScanningConfigPolicyActionSchema = z.object({
[ProjectPermissionSecretScanningConfigActions.Read]: z.boolean().optional(),
[ProjectPermissionSecretScanningConfigActions.Update]: z.boolean().optional()
});
const KmipPolicyActionSchema = z.object({
[ProjectPermissionKmipActions.ReadClients]: z.boolean().optional(),
[ProjectPermissionKmipActions.CreateClients]: z.boolean().optional(),
@@ -296,7 +319,13 @@ export const projectRoleFormSchema = z.object({
[ProjectPermissionSub.Kms]: GeneralPolicyActionSchema.array().default([]),
[ProjectPermissionSub.Cmek]: CmekPolicyActionSchema.array().default([]),
[ProjectPermissionSub.SecretSyncs]: SecretSyncPolicyActionSchema.array().default([]),
[ProjectPermissionSub.Kmip]: KmipPolicyActionSchema.array().default([])
[ProjectPermissionSub.Kmip]: KmipPolicyActionSchema.array().default([]),
[ProjectPermissionSub.SecretScanningDataSources]:
SecretScanningDataSourcePolicyActionSchema.array().default([]),
[ProjectPermissionSub.SecretScanningFindings]:
SecretScanningFindingPolicyActionSchema.array().default([]),
[ProjectPermissionSub.SecretScanningConfigs]:
SecretScanningConfigPolicyActionSchema.array().default([])
})
.partial()
.optional()
@@ -746,6 +775,71 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => {
formVal[subject]![0][ProjectPermissionSecretSyncActions.ImportSecrets] = true;
if (canRemoveSecrets)
formVal[subject]![0][ProjectPermissionSecretSyncActions.RemoveSecrets] = true;
return;
}
if (subject === ProjectPermissionSub.SecretScanningDataSources) {
const canRead = action.includes(ProjectPermissionSecretScanningDataSourceActions.Read);
const canEdit = action.includes(ProjectPermissionSecretScanningDataSourceActions.Edit);
const canDelete = action.includes(ProjectPermissionSecretScanningDataSourceActions.Delete);
const canCreate = action.includes(ProjectPermissionSecretScanningDataSourceActions.Create);
const canReadScans = action.includes(
ProjectPermissionSecretScanningDataSourceActions.ReadScans
);
const canReadResources = action.includes(
ProjectPermissionSecretScanningDataSourceActions.ReadResources
);
const canTriggerScans = action.includes(
ProjectPermissionSecretScanningDataSourceActions.TriggerScans
);
if (!formVal[subject]) formVal[subject] = [{}];
// from above statement we are sure it won't be undefined
if (canRead)
formVal[subject]![0][ProjectPermissionSecretScanningDataSourceActions.Read] = true;
if (canEdit)
formVal[subject]![0][ProjectPermissionSecretScanningDataSourceActions.Edit] = true;
if (canCreate)
formVal[subject]![0][ProjectPermissionSecretScanningDataSourceActions.Create] = true;
if (canDelete)
formVal[subject]![0][ProjectPermissionSecretScanningDataSourceActions.Delete] = true;
if (canReadScans)
formVal[subject]![0][ProjectPermissionSecretScanningDataSourceActions.ReadScans] = true;
if (canReadResources)
formVal[subject]![0][ProjectPermissionSecretScanningDataSourceActions.ReadResources] = true;
if (canTriggerScans)
formVal[subject]![0][ProjectPermissionSecretScanningDataSourceActions.TriggerScans] = true;
return;
}
if (subject === ProjectPermissionSub.SecretScanningFindings) {
const canRead = action.includes(ProjectPermissionSecretScanningFindingActions.Read);
const canUpdate = action.includes(ProjectPermissionSecretScanningFindingActions.Update);
if (!formVal[subject]) formVal[subject] = [{}];
// from above statement we are sure it won't be undefined
if (canRead) formVal[subject]![0][ProjectPermissionSecretScanningFindingActions.Read] = true;
if (canUpdate)
formVal[subject]![0][ProjectPermissionSecretScanningFindingActions.Update] = true;
return;
}
if (subject === ProjectPermissionSub.SecretScanningConfigs) {
const canRead = action.includes(ProjectPermissionSecretScanningConfigActions.Read);
const canUpdate = action.includes(ProjectPermissionSecretScanningConfigActions.Update);
if (!formVal[subject]) formVal[subject] = [{}];
// from above statement we are sure it won't be undefined
if (canRead) formVal[subject]![0][ProjectPermissionSecretScanningConfigActions.Read] = true;
if (canUpdate)
formVal[subject]![0][ProjectPermissionSecretScanningConfigActions.Update] = true;
return;
}
if (subject === ProjectPermissionSub.SshHosts) {
@@ -770,6 +864,8 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => {
conditions: conditions ? convertCaslConditionToFormOperator(conditions) : [],
inverted
});
return;
}
if (subject === ProjectPermissionSub.PkiSubscribers) {
@@ -1318,6 +1414,65 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = {
value: ProjectPermissionKmipActions.GenerateClientCertificates
}
]
},
[ProjectPermissionSub.SecretScanningDataSources]: {
title: "Secret Scanning Data Sources",
actions: [
{
label: "Read Data Sources",
value: ProjectPermissionSecretScanningDataSourceActions.Read
},
{
label: "Create Data Sources",
value: ProjectPermissionSecretScanningDataSourceActions.Create
},
{
label: "Modify Data Sources",
value: ProjectPermissionSecretScanningDataSourceActions.Edit
},
{
label: "Delete Data Sources",
value: ProjectPermissionSecretScanningDataSourceActions.Delete
},
{
label: "Read Resources",
value: ProjectPermissionSecretScanningDataSourceActions.ReadResources
},
{
label: "Read Scans",
value: ProjectPermissionSecretScanningDataSourceActions.ReadScans
},
{
label: "Trigger Scans",
value: ProjectPermissionSecretScanningDataSourceActions.TriggerScans
}
]
},
[ProjectPermissionSub.SecretScanningFindings]: {
title: "Secret Scanning Findings",
actions: [
{
label: "Read Findings",
value: ProjectPermissionSecretScanningFindingActions.Read
},
{
label: "Update Findings",
value: ProjectPermissionSecretScanningFindingActions.Update
}
]
},
[ProjectPermissionSub.SecretScanningConfigs]: {
title: "Secret Scanning Config",
actions: [
{
label: "Read Config",
value: ProjectPermissionSecretScanningConfigActions.Read
},
{
label: "Update Config",
value: ProjectPermissionSecretScanningConfigActions.Update
}
]
}
};
@@ -1371,6 +1526,12 @@ const SshPermissionSubjects = (enabled = false) => ({
[ProjectPermissionSub.SshHostGroups]: enabled
});
const SecretScanningSubject = (enabled = false) => ({
[ProjectPermissionSub.SecretScanningDataSources]: enabled,
[ProjectPermissionSub.SecretScanningFindings]: enabled,
[ProjectPermissionSub.SecretScanningConfigs]: enabled
});
// scott: this structure ensures we don't forget to add project permissions to their relevant project type
export const ProjectTypePermissionSubjects: Record<
ProjectType,
@@ -1381,27 +1542,39 @@ export const ProjectTypePermissionSubjects: Record<
...SecretsManagerPermissionSubjects(true),
...KmsPermissionSubjects(),
...CertificateManagerPermissionSubjects(),
...SshPermissionSubjects()
...SshPermissionSubjects(),
...SecretScanningSubject()
},
[ProjectType.KMS]: {
...SharedPermissionSubjects,
...KmsPermissionSubjects(true),
...SecretsManagerPermissionSubjects(),
...CertificateManagerPermissionSubjects(),
...SshPermissionSubjects()
...SshPermissionSubjects(),
...SecretScanningSubject()
},
[ProjectType.CertificateManager]: {
...SharedPermissionSubjects,
...CertificateManagerPermissionSubjects(true),
...KmsPermissionSubjects(),
...SecretsManagerPermissionSubjects(),
...SshPermissionSubjects()
...SshPermissionSubjects(),
...SecretScanningSubject()
},
[ProjectType.SSH]: {
...SharedPermissionSubjects,
...SshPermissionSubjects(true),
...CertificateManagerPermissionSubjects(),
...KmsPermissionSubjects(),
...SecretsManagerPermissionSubjects(),
...SecretScanningSubject()
},
[ProjectType.SecretScanning]: {
...SharedPermissionSubjects,
...SecretScanningSubject(true),
...SshPermissionSubjects(),
...CertificateManagerPermissionSubjects(),
...KmsPermissionSubjects(),
...SecretsManagerPermissionSubjects()
}
};
@@ -1614,6 +1787,56 @@ export const RoleTemplates: Record<ProjectType, RoleTemplate[]> = {
},
projectManagerTemplate()
],
[ProjectType.SecretScanning]: [
{
id: "scanning-viewer",
name: "Secret Scanning Viewing Policies",
description: "Grants read access to data sources and findings",
permissions: [
{
subject: ProjectPermissionSub.SecretScanningDataSources,
actions: [
ProjectPermissionSecretScanningDataSourceActions.Read,
ProjectPermissionSecretScanningDataSourceActions.ReadResources,
ProjectPermissionSecretScanningDataSourceActions.ReadScans
]
},
{
subject: ProjectPermissionSub.SecretScanningFindings,
actions: [ProjectPermissionSecretScanningFindingActions.Read]
},
{
subject: ProjectPermissionSub.SecretScanningConfigs,
actions: [ProjectPermissionSecretScanningConfigActions.Read]
}
]
},
{
id: "scanning-editor",
name: "Secret Scanning Editing Policies",
description: "Grants read and edit access to data sources and findings",
permissions: [
{
subject: ProjectPermissionSub.SecretScanningDataSources,
actions: Object.values(ProjectPermissionSecretScanningDataSourceActions)
},
{
subject: ProjectPermissionSub.SecretScanningFindings,
actions: Object.values(ProjectPermissionSecretScanningFindingActions)
},
{
subject: ProjectPermissionSub.SecretScanningConfigs,
actions: [ProjectPermissionSecretScanningConfigActions.Read]
}
]
},
projectManagerTemplate([
{
subject: ProjectPermissionSub.SecretScanningConfigs,
actions: Object.values(ProjectPermissionSecretScanningConfigActions)
}
])
],
[ProjectType.SecretManager]: [
{
id: "secret-viewer",
@@ -0,0 +1,28 @@
import { createFileRoute, linkOptions } from "@tanstack/react-router";
import { RoleDetailsBySlugPage } from "./RoleDetailsBySlugPage";
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/secret-scanning/$projectId/_secret-scanning-layout/roles/$roleSlug"
)({
component: RoleDetailsBySlugPage,
beforeLoad: ({ context, params }) => {
return {
breadcrumbs: [
...context.breadcrumbs,
{
label: "Access Control",
link: linkOptions({
to: "/secret-scanning/$projectId/access-management",
params: {
projectId: params.projectId
}
})
},
{
label: "Roles"
}
]
};
}
});