mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 09:30:09 +00:00
feature: secret scanning pt2 and address initial feedback
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -125,7 +125,7 @@ export enum ProjectPermissionSecretScanningDataSourceActions {
|
||||
|
||||
export enum ProjectPermissionSecretScanningFindingActions {
|
||||
Read = "read-findings",
|
||||
Resolve = "resolve-findings"
|
||||
Update = "update-findings"
|
||||
}
|
||||
|
||||
export enum PermissionConditionOperators {
|
||||
|
||||
@@ -196,7 +196,8 @@ export const eventToNameMap: { [K in EventType]: string } = {
|
||||
[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_DATA_SOURCE_TRIGGER_SCAN]: "Trigger Secret Scanning Data Source Scan",
|
||||
[EventType.SECRET_SCANNING_DATA_SOURCE_SCAN]: "Secret Scanning Data Source Scan",
|
||||
[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",
|
||||
|
||||
@@ -190,6 +190,7 @@ export enum EventType {
|
||||
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_TRIGGER_SCAN = "secret-scanning-data-source-trigger-scan",
|
||||
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",
|
||||
|
||||
+1
@@ -14,4 +14,5 @@ export type TSecretScanningDataSourceBase = {
|
||||
name: string;
|
||||
};
|
||||
isAutoScanEnabled: boolean;
|
||||
isDisconnected: boolean;
|
||||
};
|
||||
|
||||
@@ -50,4 +50,5 @@ export type SubscriptionPlan = {
|
||||
enforceMfa: boolean;
|
||||
projectTemplates: boolean;
|
||||
kmip: boolean;
|
||||
secretScanning: boolean;
|
||||
};
|
||||
|
||||
@@ -77,6 +77,7 @@ export const ProjectLayout = () => {
|
||||
const { data: unresolvedFindings } = useGetSecretScanningUnresolvedFindingCount(workspaceId, {
|
||||
enabled:
|
||||
isSecretScanning &&
|
||||
subscription.secretScanning &&
|
||||
permission.can(
|
||||
ProjectPermissionSecretScanningFindingActions.Read,
|
||||
ProjectPermissionSub.SecretScanningFindings
|
||||
@@ -264,7 +265,7 @@ export const ProjectLayout = () => {
|
||||
}}
|
||||
>
|
||||
{({ isActive }) => (
|
||||
<MenuItem isSelected={isActive} icon="jigsaw-puzzle">
|
||||
<MenuItem isSelected={isActive} icon="blocks">
|
||||
Data Sources
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
+15
-1
@@ -5,6 +5,7 @@ import { Button } from "@app/components/v2";
|
||||
import { useProjectPermission, useWorkspace } from "@app/context";
|
||||
import { useRemoveAssumeProjectPrivilege } from "@app/hooks/api";
|
||||
import { ActorType } from "@app/hooks/api/auditLogs/enums";
|
||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||
|
||||
export const AssumePrivilegeModeBanner = () => {
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
@@ -35,7 +36,20 @@ export const AssumePrivilegeModeBanner = () => {
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
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}`;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -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}`;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
+49
-3
@@ -104,7 +104,7 @@ const SecretScanningDataSourcePolicyActionSchema = z.object({
|
||||
|
||||
const SecretScanningFindingPolicyActionSchema = z.object({
|
||||
[ProjectPermissionSecretScanningFindingActions.Read]: z.boolean().optional(),
|
||||
[ProjectPermissionSecretScanningFindingActions.Resolve]: z.boolean().optional()
|
||||
[ProjectPermissionSecretScanningFindingActions.Update]: z.boolean().optional()
|
||||
});
|
||||
|
||||
const KmipPolicyActionSchema = z.object({
|
||||
@@ -731,6 +731,52 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => {
|
||||
formVal[subject]![0][ProjectPermissionSecretSyncActions.RemoveSecrets] = true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (subject === ProjectPermissionSub.SshHosts) {
|
||||
if (!formVal[subject]) formVal[subject] = [];
|
||||
|
||||
@@ -1313,8 +1359,8 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = {
|
||||
value: ProjectPermissionSecretScanningFindingActions.Read
|
||||
},
|
||||
{
|
||||
label: "Resolve Finding Status",
|
||||
value: ProjectPermissionSecretScanningFindingActions.Resolve
|
||||
label: "Update Findings",
|
||||
value: ProjectPermissionSecretScanningFindingActions.Update
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
-1
@@ -82,7 +82,6 @@ const PageContent = () => {
|
||||
<p className="text-3xl font-semibold text-white">{dataSource.name}</p>
|
||||
<p className="leading-3 text-bunker-300">{details.name} Data Source</p>
|
||||
</div>
|
||||
{/* <SecretSyncActionTriggers dataSource={dataSource} /> */}
|
||||
</div>
|
||||
<div className="flex justify-center">
|
||||
<div className="mr-4 flex w-72 flex-col gap-4">
|
||||
|
||||
+22
-4
@@ -1,10 +1,10 @@
|
||||
import { faBan, faCheck, faEdit } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faBan, faCheck, faEdit, faPlugCircleXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import { EditSecretScanningDataSourceModal } from "@app/components/secret-scanning";
|
||||
import { GenericFieldLabel } from "@app/components/secret-syncs";
|
||||
import { Badge, IconButton } from "@app/components/v2";
|
||||
import { Badge, IconButton, Tooltip } from "@app/components/v2";
|
||||
import { ProjectPermissionSub } from "@app/context";
|
||||
import { ProjectPermissionSecretScanningDataSourceActions } from "@app/context/ProjectPermissionContext/types";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
@@ -18,13 +18,31 @@ type Props = {
|
||||
export const SecretScanningDataSourceSection = ({ dataSource }: Props) => {
|
||||
const { handlePopUpToggle, popUp, handlePopUpOpen } = usePopUp(["editDataSource"] as const);
|
||||
|
||||
const { name, description, connection, isAutoScanEnabled } = dataSource;
|
||||
const { name, description, connection, isAutoScanEnabled, isDisconnected } = dataSource;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex w-full flex-col gap-3 rounded-lg border border-mineshaft-600 bg-mineshaft-900 px-4 py-3">
|
||||
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-2">
|
||||
<h3 className="font-semibold text-mineshaft-100">Details</h3>
|
||||
<div className="mr-2 flex flex-1 items-center justify-between">
|
||||
<h3 className="font-semibold text-mineshaft-100">Details</h3>
|
||||
{isDisconnected && (
|
||||
<Tooltip
|
||||
className="text-xs"
|
||||
content="The external data source has been removed and can no longer be scanned. Delete this data source and re-initialize the connection."
|
||||
>
|
||||
<div className="ml-auto">
|
||||
<Badge
|
||||
variant="danger"
|
||||
className="flex h-5 w-min items-center gap-1.5 whitespace-nowrap"
|
||||
>
|
||||
<FontAwesomeIcon icon={faPlugCircleXmark} />
|
||||
<span>Disconnected</span>
|
||||
</Badge>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionSecretScanningDataSourceActions.Edit}
|
||||
a={ProjectPermissionSub.SecretScanningDataSources}
|
||||
|
||||
+1
-1
@@ -82,7 +82,7 @@ export const SecretScanningScanRow = ({ scan }: Props) => {
|
||||
</div>
|
||||
</Td>
|
||||
<Td className="whitespace-nowrap">
|
||||
{type === SecretScanningScanType.FullScan ? "Full scan" : "Diff scan"}
|
||||
{type === SecretScanningScanType.FullScan ? "Full Scan" : "Diff Scan"}
|
||||
</Td>
|
||||
<Td>
|
||||
{
|
||||
|
||||
+20
-9
@@ -1,7 +1,12 @@
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import { PageHeader } from "@app/components/v2";
|
||||
import {
|
||||
ProjectPermissionSecretScanningDataSourceActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/context/ProjectPermissionContext/types";
|
||||
|
||||
import { SecretScanningDataSourcesSection } from "./components";
|
||||
|
||||
@@ -12,17 +17,23 @@ export const SecretScanningDataSourcesPage = () => {
|
||||
<Helmet>
|
||||
<title>{t("common.head-title", { title: "Secret Scanning" })}</title>
|
||||
</Helmet>
|
||||
<div className="h-full bg-bunker-800">
|
||||
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||
<div className="mx-auto mb-6 w-full max-w-7xl">
|
||||
<PageHeader
|
||||
title="Data Sources"
|
||||
description="Manage your Secret Scanning data sources."
|
||||
/>
|
||||
<SecretScanningDataSourcesSection />
|
||||
<ProjectPermissionCan
|
||||
renderGuardBanner
|
||||
I={ProjectPermissionSecretScanningDataSourceActions.Read}
|
||||
a={ProjectPermissionSub.SecretScanningDataSources}
|
||||
>
|
||||
<div className="h-full bg-bunker-800">
|
||||
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||
<div className="mx-auto mb-6 w-full max-w-7xl">
|
||||
<PageHeader
|
||||
title="Data Sources"
|
||||
description="Manage your Secret Scanning data sources."
|
||||
/>
|
||||
<SecretScanningDataSourcesSection />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ProjectPermissionCan>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
+20
-2
@@ -7,6 +7,7 @@ import {
|
||||
faEllipsisV,
|
||||
faExpand,
|
||||
faInfoCircle,
|
||||
faPlugCircleXmark,
|
||||
faSearch,
|
||||
faToggleOff,
|
||||
faToggleOn,
|
||||
@@ -72,7 +73,8 @@ export const SecretScanningDataSourceRow = ({
|
||||
unresolvedFindings,
|
||||
lastScannedAt,
|
||||
lastScanStatus,
|
||||
lastScanStatusMessage
|
||||
lastScanStatusMessage,
|
||||
isDisconnected
|
||||
} = dataSource;
|
||||
|
||||
const sourceDetails = SECRET_SCANNING_DATA_SOURCE_MAP[type];
|
||||
@@ -199,7 +201,7 @@ export const SecretScanningDataSourceRow = ({
|
||||
) : (
|
||||
<span className="text-mineshaft-400">No scans</span>
|
||||
)}
|
||||
{!isAutoScanEnabled && (
|
||||
{!isAutoScanEnabled && !isDisconnected && (
|
||||
<Tooltip
|
||||
className="text-xs"
|
||||
content={`Auto-Scan is disabled. Scans will not be automatically triggered when a ${autoScanDescription.verb} occurs to ${autoScanDescription.pluralNoun} associated with this data source`}
|
||||
@@ -212,6 +214,22 @@ export const SecretScanningDataSourceRow = ({
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
{isDisconnected && (
|
||||
<Tooltip
|
||||
className="text-xs"
|
||||
content="The external data source has been removed and can no longer be scanned. Delete this data source and re-initialize the connection."
|
||||
>
|
||||
<div className="ml-auto">
|
||||
<Badge
|
||||
variant="danger"
|
||||
className="flex h-5 w-min items-center gap-1.5 whitespace-nowrap"
|
||||
>
|
||||
<FontAwesomeIcon icon={faPlugCircleXmark} />
|
||||
<span>Disconnected</span>
|
||||
</Badge>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
|
||||
+21
-3
@@ -1,10 +1,11 @@
|
||||
import { faArrowUpRightFromSquare, faBookOpen, faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import { CreateSecretScanningDataSourceModal } from "@app/components/secret-scanning";
|
||||
import { Button, Spinner } from "@app/components/v2";
|
||||
import { ProjectPermissionSub, useWorkspace } from "@app/context";
|
||||
import { ProjectPermissionSub, useSubscription, useWorkspace } from "@app/context";
|
||||
import { ProjectPermissionSecretScanningDataSourceActions } from "@app/context/ProjectPermissionContext/types";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { useListSecretScanningDataSources } from "@app/hooks/api/secretScanningV2";
|
||||
@@ -12,7 +13,12 @@ import { useListSecretScanningDataSources } from "@app/hooks/api/secretScanningV
|
||||
import { SecretScanningDataSourcesTable } from "./SecretScanningDataSourcesTable";
|
||||
|
||||
export const SecretScanningDataSourcesSection = () => {
|
||||
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["addDataSource"] as const);
|
||||
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([
|
||||
"addDataSource",
|
||||
"upgradePlan"
|
||||
] as const);
|
||||
|
||||
const { subscription } = useSubscription();
|
||||
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
|
||||
@@ -63,7 +69,14 @@ export const SecretScanningDataSourcesSection = () => {
|
||||
colorSchema="secondary"
|
||||
type="submit"
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
onClick={() => handlePopUpOpen("addDataSource")}
|
||||
onClick={() => {
|
||||
if (!subscription.secretScanning) {
|
||||
handlePopUpOpen("upgradePlan");
|
||||
return;
|
||||
}
|
||||
|
||||
handlePopUpOpen("addDataSource");
|
||||
}}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
Add Data Source
|
||||
@@ -77,6 +90,11 @@ export const SecretScanningDataSourcesSection = () => {
|
||||
isOpen={popUp.addDataSource.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("addDataSource", isOpen)}
|
||||
/>
|
||||
<UpgradePlanModal
|
||||
isOpen={popUp.upgradePlan.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||
text="You can create Data Sources by upgrading to Infisical's Enterprise plan."
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
+2
-2
@@ -3,9 +3,9 @@ import {
|
||||
faArrowDown,
|
||||
faArrowUp,
|
||||
faCheckCircle,
|
||||
faCubesStacked,
|
||||
faFilter,
|
||||
faMagnifyingGlass,
|
||||
faPuzzlePiece,
|
||||
faSearch
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
@@ -368,7 +368,7 @@ export const SecretScanningDataSourcesTable = ({ dataSources }: Props) => {
|
||||
? "No data sources match search..."
|
||||
: "This project has no data sources configured"
|
||||
}
|
||||
icon={dataSources.length ? faSearch : faPuzzlePiece}
|
||||
icon={dataSources.length ? faSearch : faCubesStacked}
|
||||
/>
|
||||
)}
|
||||
</TableContainer>
|
||||
|
||||
+15
-6
@@ -1,7 +1,10 @@
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import { PageHeader } from "@app/components/v2";
|
||||
import { ProjectPermissionSub } from "@app/context";
|
||||
import { ProjectPermissionSecretScanningFindingActions } from "@app/context/ProjectPermissionContext/types";
|
||||
|
||||
import { SecretScanningFindingsSection } from "./components";
|
||||
|
||||
@@ -12,14 +15,20 @@ export const SecretScanningFindingsPage = () => {
|
||||
<Helmet>
|
||||
<title>{t("common.head-title", { title: "Secret Scanning" })}</title>
|
||||
</Helmet>
|
||||
<div className="h-full bg-bunker-800">
|
||||
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||
<div className="mx-auto mb-6 w-full max-w-7xl">
|
||||
<PageHeader title="Findings" description="View Secret Leaks across your project." />
|
||||
<SecretScanningFindingsSection />
|
||||
<ProjectPermissionCan
|
||||
renderGuardBanner
|
||||
I={ProjectPermissionSecretScanningFindingActions.Read}
|
||||
a={ProjectPermissionSub.SecretScanningFindings}
|
||||
>
|
||||
<div className="h-full bg-bunker-800">
|
||||
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||
<div className="mx-auto mb-6 w-full max-w-7xl">
|
||||
<PageHeader title="Findings" description="View Secret Leaks across your project." />
|
||||
<SecretScanningFindingsSection />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ProjectPermissionCan>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
+30
-12
@@ -5,6 +5,7 @@ import { format } from "date-fns";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
@@ -18,6 +19,10 @@ import {
|
||||
Tooltip,
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import {
|
||||
ProjectPermissionSecretScanningFindingActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/context/ProjectPermissionContext/types";
|
||||
import {
|
||||
SECRET_SCANNING_DATA_SOURCE_MAP,
|
||||
SECRET_SCANNING_FINDING_STATUS_ICON_MAP
|
||||
@@ -191,19 +196,32 @@ export const SecretScanningFindingRow = ({ finding, onUpdate }: Props) => {
|
||||
{details.link}
|
||||
</a>
|
||||
</GenericFieldLabel>
|
||||
|
||||
<Button
|
||||
onClick={() => onUpdate(finding)}
|
||||
colorSchema="secondary"
|
||||
leftIcon={
|
||||
<FontAwesomeIcon
|
||||
className={SECRET_SCANNING_FINDING_STATUS_ICON_MAP[status].className}
|
||||
icon={SECRET_SCANNING_FINDING_STATUS_ICON_MAP[status].icon}
|
||||
/>
|
||||
}
|
||||
<div className="col-span-full flex items-center border-t border-mineshaft-500" />
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionSecretScanningFindingActions.Update}
|
||||
a={ProjectPermissionSub.SecretScanningFindings}
|
||||
>
|
||||
Update Status
|
||||
</Button>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
onClick={() => onUpdate(finding)}
|
||||
colorSchema="secondary"
|
||||
isDisabled={!isAllowed}
|
||||
leftIcon={
|
||||
<FontAwesomeIcon
|
||||
className={SECRET_SCANNING_FINDING_STATUS_ICON_MAP[status].className}
|
||||
icon={SECRET_SCANNING_FINDING_STATUS_ICON_MAP[status].icon}
|
||||
/>
|
||||
}
|
||||
>
|
||||
Update Status
|
||||
</Button>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
{Boolean(remarks) && (
|
||||
<GenericFieldLabel className="col-span-3" label="Remarks">
|
||||
{remarks}
|
||||
</GenericFieldLabel>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Td>
|
||||
|
||||
+1
-1
@@ -133,7 +133,7 @@ export const SecretScanningUpdateFindingModal = ({ finding, isOpen, onOpenChange
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
|
||||
<ModalContent title="Resolve Finding" subTitle="Mark this finding as resolved">
|
||||
<ModalContent title="Update Finding" subTitle="Update the status or leave remarks">
|
||||
<Content finding={finding} onComplete={() => onOpenChange(false)} />
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
||||
Reference in New Issue
Block a user