mirror of
https://github.com/netbirdio/dashboard.git
synced 2026-01-26 01:21:04 +00:00
Add process posture check (#378)
* Add process posture check * Add support for separate linux and mac paths
This commit is contained in:
@@ -13,6 +13,7 @@ export interface InputProps
|
|||||||
icon?: React.ReactNode;
|
icon?: React.ReactNode;
|
||||||
error?: string;
|
error?: string;
|
||||||
errorTooltip?: boolean;
|
errorTooltip?: boolean;
|
||||||
|
errorTooltipPosition?: "top" | "top-right";
|
||||||
}
|
}
|
||||||
|
|
||||||
const inputVariants = cva("", {
|
const inputVariants = cva("", {
|
||||||
@@ -49,6 +50,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|||||||
maxWidthClass = "",
|
maxWidthClass = "",
|
||||||
error,
|
error,
|
||||||
errorTooltip = false,
|
errorTooltip = false,
|
||||||
|
errorTooltipPosition = "top",
|
||||||
...props
|
...props
|
||||||
},
|
},
|
||||||
ref,
|
ref,
|
||||||
@@ -105,9 +107,12 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|||||||
</div>
|
</div>
|
||||||
{error && errorTooltip && (
|
{error && errorTooltip && (
|
||||||
<div
|
<div
|
||||||
className={
|
className={cn(
|
||||||
"absolute right-0 top-2 h-[0px] w-full flex items-center pr-3 justify-center"
|
errorTooltipPosition == "top" &&
|
||||||
}
|
"absolute right-0 top-2 h-[0px] w-full flex items-center pr-3 justify-center",
|
||||||
|
errorTooltipPosition == "top-right" &&
|
||||||
|
"absolute -right-6 top-2 h-[0px] w-full flex items-center pr-3 justify-end",
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<FullTooltip
|
<FullTooltip
|
||||||
content={
|
content={
|
||||||
@@ -120,7 +125,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
interactive={false}
|
interactive={false}
|
||||||
align={"center"}
|
align={errorTooltipPosition == "top" ? "center" : "end"}
|
||||||
side={"top"}
|
side={"top"}
|
||||||
keepOpen={true}
|
keepOpen={true}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export interface PostureCheck {
|
|||||||
os_version_check?: OperatingSystemVersionCheck;
|
os_version_check?: OperatingSystemVersionCheck;
|
||||||
geo_location_check?: GeoLocationCheck;
|
geo_location_check?: GeoLocationCheck;
|
||||||
peer_network_range_check?: PeerNetworkRangeCheck;
|
peer_network_range_check?: PeerNetworkRangeCheck;
|
||||||
|
process_check?: ProcessCheck;
|
||||||
};
|
};
|
||||||
policies?: Policy[];
|
policies?: Policy[];
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
@@ -53,6 +54,17 @@ export interface PeerNetworkRangeCheck {
|
|||||||
action: "allow" | "deny";
|
action: "allow" | "deny";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ProcessCheck {
|
||||||
|
processes: Process[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Process {
|
||||||
|
id: string;
|
||||||
|
linux_path?: string;
|
||||||
|
mac_path?: string;
|
||||||
|
windows_path?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const windowsKernelVersions: SelectOption[] = [
|
export const windowsKernelVersions: SelectOption[] = [
|
||||||
{ value: "5.0", label: "Windows 2000" },
|
{ value: "5.0", label: "Windows 2000" },
|
||||||
{ value: "5.1", label: "Windows XP" },
|
{ value: "5.1", label: "Windows XP" },
|
||||||
|
|||||||
310
src/modules/posture-checks/checks/PostureCheckProcess.tsx
Normal file
310
src/modules/posture-checks/checks/PostureCheckProcess.tsx
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
import Button from "@components/Button";
|
||||||
|
import HelpText from "@components/HelpText";
|
||||||
|
import InlineLink from "@components/InlineLink";
|
||||||
|
import { Input } from "@components/Input";
|
||||||
|
import { Label } from "@components/Label";
|
||||||
|
import { ModalClose, ModalFooter } from "@components/modal/Modal";
|
||||||
|
import Paragraph from "@components/Paragraph";
|
||||||
|
import { cn, validator } from "@utils/helpers";
|
||||||
|
import { isEmpty, uniqueId } from "lodash";
|
||||||
|
import {
|
||||||
|
ExternalLinkIcon,
|
||||||
|
MinusCircleIcon,
|
||||||
|
PlusCircle,
|
||||||
|
ServerCogIcon,
|
||||||
|
TerminalIcon,
|
||||||
|
} from "lucide-react";
|
||||||
|
import * as React from "react";
|
||||||
|
import { useMemo, useState } from "react";
|
||||||
|
import AppleIcon from "@/assets/icons/AppleIcon";
|
||||||
|
import WindowsIcon from "@/assets/icons/WindowsIcon";
|
||||||
|
import { Process, ProcessCheck } from "@/interfaces/PostureCheck";
|
||||||
|
import { PostureCheckCard } from "@/modules/posture-checks/ui/PostureCheckCard";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
value?: ProcessCheck;
|
||||||
|
onChange: (value: ProcessCheck | undefined) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PostureCheckProcess = ({ value, onChange }: Props) => {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PostureCheckCard
|
||||||
|
open={open}
|
||||||
|
setOpen={setOpen}
|
||||||
|
key={open ? 1 : 0}
|
||||||
|
active={value?.processes && value?.processes?.length > 0}
|
||||||
|
title={"Process"}
|
||||||
|
description={
|
||||||
|
"Restrict access in your network based on running processes of a peer."
|
||||||
|
}
|
||||||
|
icon={<ServerCogIcon size={18} />}
|
||||||
|
iconClass={"bg-gradient-to-tr from-nb-gray-500 to-nb-gray-300"}
|
||||||
|
modalWidthClass={"max-w-xl"}
|
||||||
|
onReset={() => onChange(undefined)}
|
||||||
|
>
|
||||||
|
<CheckContent
|
||||||
|
value={value}
|
||||||
|
onChange={(v) => {
|
||||||
|
onChange(v);
|
||||||
|
setOpen(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</PostureCheckCard>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CheckContent = ({ value, onChange }: Props) => {
|
||||||
|
const [processes, setProcesses] = useState<Process[]>(
|
||||||
|
value?.processes
|
||||||
|
? value.processes.map((p) => {
|
||||||
|
return {
|
||||||
|
id: uniqueId("process"),
|
||||||
|
linux_path: p?.linux_path || "",
|
||||||
|
mac_path: p?.mac_path || "",
|
||||||
|
windows_path: p?.windows_path || "",
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
id: uniqueId("process"),
|
||||||
|
linux_path: "",
|
||||||
|
mac_path: "",
|
||||||
|
windows_path: "",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleProcessChange = (
|
||||||
|
id: string,
|
||||||
|
linux_path: string,
|
||||||
|
mac_path: string,
|
||||||
|
windows_path: string,
|
||||||
|
) => {
|
||||||
|
const newProcesses = processes.map((p) =>
|
||||||
|
p.id === id ? { ...p, linux_path, mac_path, windows_path } : p,
|
||||||
|
);
|
||||||
|
setProcesses(newProcesses);
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeProcess = (id: string) => {
|
||||||
|
const newProcesses = processes.filter((p) => p.id !== id);
|
||||||
|
setProcesses(newProcesses);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addProcess = () => {
|
||||||
|
setProcesses([
|
||||||
|
...processes,
|
||||||
|
{
|
||||||
|
id: uniqueId("process"),
|
||||||
|
linux_path: "",
|
||||||
|
mac_path: "",
|
||||||
|
windows_path: "",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const pathErrors = useMemo(() => {
|
||||||
|
if (processes && processes.length > 0) {
|
||||||
|
return processes.map((p) => {
|
||||||
|
return {
|
||||||
|
id: p.id,
|
||||||
|
errorMacPath: p?.mac_path
|
||||||
|
? validator.isValidUnixFilePath(p?.mac_path || "")
|
||||||
|
? ""
|
||||||
|
: "Please enter a valid macOS file path"
|
||||||
|
: "",
|
||||||
|
errorLinuxPath: p?.linux_path
|
||||||
|
? validator.isValidUnixFilePath(p?.linux_path || "")
|
||||||
|
? ""
|
||||||
|
: "Please enter a valid Unix file path"
|
||||||
|
: "",
|
||||||
|
errorWindowsPath: p?.windows_path
|
||||||
|
? validator.isValidWindowsFilePath(p?.windows_path || "")
|
||||||
|
? ""
|
||||||
|
: "Please enter a valid Windows file path"
|
||||||
|
: "",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}, [processes]);
|
||||||
|
|
||||||
|
const hasErrorsOrIsEmpty = useMemo(() => {
|
||||||
|
if (processes.length === 0) return true;
|
||||||
|
const hasOnlyEmptyPaths = processes.some(
|
||||||
|
(p) => p.linux_path === "" && p.mac_path === "" && p.windows_path === "",
|
||||||
|
);
|
||||||
|
const hasPathErrors = pathErrors.some(
|
||||||
|
(e) =>
|
||||||
|
e.errorLinuxPath !== "" ||
|
||||||
|
e.errorMacPath !== "" ||
|
||||||
|
e.errorWindowsPath !== "",
|
||||||
|
);
|
||||||
|
return hasOnlyEmptyPaths || hasPathErrors;
|
||||||
|
}, [processes, pathErrors]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={"flex flex-col px-8 gap-2 pb-6"}>
|
||||||
|
<div className={"flex justify-between items-start gap-10 mt-2"}>
|
||||||
|
<div>
|
||||||
|
<Label>Processes</Label>
|
||||||
|
<HelpText className={""}>
|
||||||
|
Add the path of an executable file of the process. You can define
|
||||||
|
a path for Linux, macOS and Windows. Peers will only be allowed to
|
||||||
|
connect if the process is running on their system.
|
||||||
|
</HelpText>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{processes.length > 0 && (
|
||||||
|
<div className={"mb-2 flex flex-col gap-4 w-full "}>
|
||||||
|
{processes.map((p) => {
|
||||||
|
return (
|
||||||
|
<div key={p.id} className={"flex gap-2 items-center"}>
|
||||||
|
<div className={"w-full flex flex-col gap-1.5"}>
|
||||||
|
<Input
|
||||||
|
customPrefix={<TerminalIcon size={16} />}
|
||||||
|
placeholder={"/usr/local/bin/netbird"}
|
||||||
|
value={p.linux_path}
|
||||||
|
error={
|
||||||
|
pathErrors.find((e) => e.id === p.id)?.errorLinuxPath
|
||||||
|
}
|
||||||
|
errorTooltip={true}
|
||||||
|
errorTooltipPosition={"top-right"}
|
||||||
|
className={"w-full"}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleProcessChange(
|
||||||
|
p.id,
|
||||||
|
e.target.value,
|
||||||
|
p?.mac_path || "",
|
||||||
|
p?.windows_path || "",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
customPrefix={
|
||||||
|
<AppleIcon
|
||||||
|
size={16}
|
||||||
|
className={cn(
|
||||||
|
pathErrors.find((e) => e.id === p.id)
|
||||||
|
?.errorMacPath && "fill-red-500",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
placeholder={
|
||||||
|
"/Applications/NetBird.app/Contents/MacOS/netbird"
|
||||||
|
}
|
||||||
|
value={p.mac_path}
|
||||||
|
error={
|
||||||
|
pathErrors.find((e) => e.id === p.id)?.errorMacPath
|
||||||
|
}
|
||||||
|
errorTooltip={true}
|
||||||
|
errorTooltipPosition={"top-right"}
|
||||||
|
className={"w-full"}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleProcessChange(
|
||||||
|
p.id,
|
||||||
|
p?.linux_path || "",
|
||||||
|
e.target.value,
|
||||||
|
p?.windows_path || "",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
customPrefix={
|
||||||
|
<WindowsIcon
|
||||||
|
size={16}
|
||||||
|
className={cn(
|
||||||
|
pathErrors.find((e) => e.id === p.id)
|
||||||
|
?.errorWindowsPath && "fill-red-500",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
placeholder={`C:\\ProgramData\\NetBird\\netbird.exe`}
|
||||||
|
value={p.windows_path}
|
||||||
|
errorTooltip={true}
|
||||||
|
errorTooltipPosition={"top-right"}
|
||||||
|
error={
|
||||||
|
pathErrors.find((e) => e.id === p.id)?.errorWindowsPath
|
||||||
|
}
|
||||||
|
className={"w-full"}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleProcessChange(
|
||||||
|
p.id,
|
||||||
|
p?.linux_path || "",
|
||||||
|
p?.mac_path || "",
|
||||||
|
e.target.value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
className={"h-[42px]"}
|
||||||
|
variant={"default-outline"}
|
||||||
|
onClick={() => removeProcess(p.id)}
|
||||||
|
>
|
||||||
|
<MinusCircleIcon size={15} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
variant={"dotted"}
|
||||||
|
size={"sm"}
|
||||||
|
onClick={addProcess}
|
||||||
|
className={"mt-1"}
|
||||||
|
>
|
||||||
|
<PlusCircle size={16} />
|
||||||
|
Add Process
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<ModalFooter className={"items-center"}>
|
||||||
|
<div className={"w-full"}>
|
||||||
|
<Paragraph className={"text-sm mt-auto"}>
|
||||||
|
Learn more about
|
||||||
|
<InlineLink
|
||||||
|
href={
|
||||||
|
"https://docs.netbird.io/how-to/manage-posture-checks#process-check"
|
||||||
|
}
|
||||||
|
target={"_blank"}
|
||||||
|
>
|
||||||
|
Process Check
|
||||||
|
<ExternalLinkIcon size={12} />
|
||||||
|
</InlineLink>
|
||||||
|
</Paragraph>
|
||||||
|
</div>
|
||||||
|
<div className={"flex gap-3 w-full justify-end"}>
|
||||||
|
<ModalClose asChild={true}>
|
||||||
|
<Button variant={"secondary"}>Cancel</Button>
|
||||||
|
</ModalClose>
|
||||||
|
<Button
|
||||||
|
variant={"primary"}
|
||||||
|
disabled={hasErrorsOrIsEmpty}
|
||||||
|
onClick={() => {
|
||||||
|
if (isEmpty(processes)) {
|
||||||
|
onChange(undefined);
|
||||||
|
} else {
|
||||||
|
onChange({
|
||||||
|
processes: processes.filter(
|
||||||
|
(p) =>
|
||||||
|
p.linux_path !== "" ||
|
||||||
|
p.mac_path !== "" ||
|
||||||
|
p.windows_path !== "",
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</ModalFooter>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
112
src/modules/posture-checks/checks/tooltips/ProcessTooltip.tsx
Normal file
112
src/modules/posture-checks/checks/tooltips/ProcessTooltip.tsx
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import Badge from "@components/Badge";
|
||||||
|
import FullTooltip from "@components/FullTooltip";
|
||||||
|
import { ScrollArea } from "@components/ScrollArea";
|
||||||
|
import { tryGetProcessNameFromPath } from "@utils/helpers";
|
||||||
|
import { TerminalIcon } from "lucide-react";
|
||||||
|
import * as React from "react";
|
||||||
|
import AppleIcon from "@/assets/icons/AppleIcon";
|
||||||
|
import WindowsIcon from "@/assets/icons/WindowsIcon";
|
||||||
|
import { ProcessCheck } from "@/interfaces/PostureCheck";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
check?: ProcessCheck;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
};
|
||||||
|
export const ProcessTooltip = ({ check, children }: Props) => {
|
||||||
|
return check ? (
|
||||||
|
<FullTooltip
|
||||||
|
className={"w-full min-w-0"}
|
||||||
|
interactive={true}
|
||||||
|
contentClassName={"p-0"}
|
||||||
|
content={
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
"text-neutral-300 text-sm max-w-xs flex flex-col gap-1 min-w-0"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className={"px-4 pt-3"}>
|
||||||
|
<span>
|
||||||
|
<span className={"text-green-500 font-semibold"}>Allow only</span>{" "}
|
||||||
|
peers which are running the following processes
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ScrollArea
|
||||||
|
className={
|
||||||
|
"max-h-[275px] overflow-y-auto flex flex-col px-4 min-w-0"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className={"flex flex-col gap-3 mt-1 text-xs mb-3.5 min-w-0"}>
|
||||||
|
{check.processes.map((p, index) => {
|
||||||
|
return (
|
||||||
|
<div className={"flex-col flex gap-1 min-w-0"} key={index}>
|
||||||
|
{p?.linux_path && (
|
||||||
|
<Badge
|
||||||
|
variant={"gray"}
|
||||||
|
useHover={false}
|
||||||
|
className={"justify-start font-medium text-xs min-w-0"}
|
||||||
|
>
|
||||||
|
<span className={"mr-1.5"}>
|
||||||
|
<TerminalIcon size={12} />
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={"truncate inline-block "}
|
||||||
|
title={p?.linux_path}
|
||||||
|
>
|
||||||
|
{tryGetProcessNameFromPath(p?.linux_path) ||
|
||||||
|
"Unknown path"}
|
||||||
|
</span>
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{p?.mac_path && (
|
||||||
|
<Badge
|
||||||
|
variant={"gray"}
|
||||||
|
useHover={false}
|
||||||
|
className={"justify-start font-medium text-xs min-w-0"}
|
||||||
|
>
|
||||||
|
<span className={"mr-1.5"}>
|
||||||
|
<AppleIcon size={12} />
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={"truncate inline-block "}
|
||||||
|
title={p?.mac_path}
|
||||||
|
>
|
||||||
|
{tryGetProcessNameFromPath(p?.mac_path) ||
|
||||||
|
"Unknown path"}
|
||||||
|
</span>
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{p?.windows_path && (
|
||||||
|
<Badge
|
||||||
|
variant={"gray"}
|
||||||
|
useHover={false}
|
||||||
|
className={"justify-start font-medium text-xs min-w-0"}
|
||||||
|
>
|
||||||
|
<span className={"mr-1.5"}>
|
||||||
|
<WindowsIcon size={12} />
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={"truncate inline-block"}
|
||||||
|
title={p?.windows_path}
|
||||||
|
>
|
||||||
|
{tryGetProcessNameFromPath(p?.windows_path) ||
|
||||||
|
"Unknown path"}
|
||||||
|
</span>
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</FullTooltip>
|
||||||
|
) : (
|
||||||
|
children
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -24,6 +24,7 @@ import { PostureCheckGeoLocation } from "@/modules/posture-checks/checks/Posture
|
|||||||
import { PostureCheckNetBirdVersion } from "@/modules/posture-checks/checks/PostureCheckNetBirdVersion";
|
import { PostureCheckNetBirdVersion } from "@/modules/posture-checks/checks/PostureCheckNetBirdVersion";
|
||||||
import { PostureCheckOperatingSystem } from "@/modules/posture-checks/checks/PostureCheckOperatingSystem";
|
import { PostureCheckOperatingSystem } from "@/modules/posture-checks/checks/PostureCheckOperatingSystem";
|
||||||
import { PostureCheckPeerNetworkRange } from "@/modules/posture-checks/checks/PostureCheckPeerNetworkRange";
|
import { PostureCheckPeerNetworkRange } from "@/modules/posture-checks/checks/PostureCheckPeerNetworkRange";
|
||||||
|
import { PostureCheckProcess } from "@/modules/posture-checks/checks/PostureCheckProcess";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -58,6 +59,9 @@ export default function PostureCheckModal({
|
|||||||
const [peerNetworkRangeCheck, setPeerNetworkRangeCheck] = useState(
|
const [peerNetworkRangeCheck, setPeerNetworkRangeCheck] = useState(
|
||||||
postureCheck?.checks.peer_network_range_check || undefined,
|
postureCheck?.checks.peer_network_range_check || undefined,
|
||||||
);
|
);
|
||||||
|
const [processCheck, setProcessCheck] = useState(
|
||||||
|
postureCheck?.checks.process_check || undefined,
|
||||||
|
);
|
||||||
|
|
||||||
const validateOSCheck = (osCheck?: OperatingSystemVersionCheck) => {
|
const validateOSCheck = (osCheck?: OperatingSystemVersionCheck) => {
|
||||||
if (!osCheck) return;
|
if (!osCheck) return;
|
||||||
@@ -98,6 +102,7 @@ export default function PostureCheckModal({
|
|||||||
geo_location_check: validateLocationCheck(geoLocationCheck),
|
geo_location_check: validateLocationCheck(geoLocationCheck),
|
||||||
os_version_check: validateOSCheck(osVersionCheck),
|
os_version_check: validateOSCheck(osVersionCheck),
|
||||||
peer_network_range_check: peerNetworkRangeCheck,
|
peer_network_range_check: peerNetworkRangeCheck,
|
||||||
|
process_check: processCheck,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -133,7 +138,8 @@ export default function PostureCheckModal({
|
|||||||
!!nbVersionCheck ||
|
!!nbVersionCheck ||
|
||||||
!!geoLocationCheck ||
|
!!geoLocationCheck ||
|
||||||
!!osVersionCheck ||
|
!!osVersionCheck ||
|
||||||
!!peerNetworkRangeCheck;
|
!!peerNetworkRangeCheck ||
|
||||||
|
!!processCheck;
|
||||||
const canCreate = !isEmpty(name) && isAtLeastOneCheckEnabled;
|
const canCreate = !isEmpty(name) && isAtLeastOneCheckEnabled;
|
||||||
|
|
||||||
const [tab, setTab] = useState("checks");
|
const [tab, setTab] = useState("checks");
|
||||||
@@ -187,13 +193,17 @@ export default function PostureCheckModal({
|
|||||||
value={geoLocationCheck}
|
value={geoLocationCheck}
|
||||||
onChange={setGeoLocationCheckCheck}
|
onChange={setGeoLocationCheckCheck}
|
||||||
/>
|
/>
|
||||||
|
<PostureCheckPeerNetworkRange
|
||||||
|
value={peerNetworkRangeCheck}
|
||||||
|
onChange={setPeerNetworkRangeCheck}
|
||||||
|
/>
|
||||||
<PostureCheckOperatingSystem
|
<PostureCheckOperatingSystem
|
||||||
value={osVersionCheck}
|
value={osVersionCheck}
|
||||||
onChange={setOsVersionCheck}
|
onChange={setOsVersionCheck}
|
||||||
/>
|
/>
|
||||||
<PostureCheckPeerNetworkRange
|
<PostureCheckProcess
|
||||||
value={peerNetworkRangeCheck}
|
value={processCheck}
|
||||||
onChange={setPeerNetworkRangeCheck}
|
onChange={setProcessCheck}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { cn } from "@utils/helpers";
|
import { cn } from "@utils/helpers";
|
||||||
import { Disc3Icon, FlagIcon, NetworkIcon } from "lucide-react";
|
import { Disc3Icon, FlagIcon, NetworkIcon, ServerCogIcon } from "lucide-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import NetBirdIcon from "@/assets/icons/NetBirdIcon";
|
import NetBirdIcon from "@/assets/icons/NetBirdIcon";
|
||||||
import { PostureCheck } from "@/interfaces/PostureCheck";
|
import { PostureCheck } from "@/interfaces/PostureCheck";
|
||||||
@@ -7,6 +7,7 @@ import { GeoLocationTooltip } from "@/modules/posture-checks/checks/tooltips/Geo
|
|||||||
import { NetBirdVersionTooltip } from "@/modules/posture-checks/checks/tooltips/NetBirdVersionTooltip";
|
import { NetBirdVersionTooltip } from "@/modules/posture-checks/checks/tooltips/NetBirdVersionTooltip";
|
||||||
import { OperatingSystemTooltip } from "@/modules/posture-checks/checks/tooltips/OperatingSystemTooltip";
|
import { OperatingSystemTooltip } from "@/modules/posture-checks/checks/tooltips/OperatingSystemTooltip";
|
||||||
import { PeerNetworkRangeTooltip } from "@/modules/posture-checks/checks/tooltips/PeerNetworkRangeTooltip";
|
import { PeerNetworkRangeTooltip } from "@/modules/posture-checks/checks/tooltips/PeerNetworkRangeTooltip";
|
||||||
|
import { ProcessTooltip } from "@/modules/posture-checks/checks/tooltips/ProcessTooltip";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
check: PostureCheck;
|
check: PostureCheck;
|
||||||
@@ -71,6 +72,18 @@ export const PostureCheckChecksCell = ({ check }: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
</PeerNetworkRangeTooltip>
|
</PeerNetworkRangeTooltip>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{check.checks.process_check && (
|
||||||
|
<ProcessTooltip check={check.checks.process_check}>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"bg-gradient-to-tr from-nb-gray-500 to-nb-gray-300 h-8 w-8 rounded-full flex items-center justify-center relative z-[8] hover:scale-[1.1] transition-all",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ServerCogIcon size={14} />
|
||||||
|
</div>
|
||||||
|
</ProcessTooltip>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -49,7 +49,10 @@ export const PostureCheckPolicyUsageCell = ({ check }: Props) => {
|
|||||||
interactive={false}
|
interactive={false}
|
||||||
>
|
>
|
||||||
<Badge
|
<Badge
|
||||||
onClick={() => router.push("/access-control")}
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
router.push("/access-control");
|
||||||
|
}}
|
||||||
variant={"gray"}
|
variant={"gray"}
|
||||||
useHover={!!(check.policies && check.policies?.length > 0)}
|
useHover={!!(check.policies && check.policies?.length > 0)}
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|||||||
@@ -74,8 +74,37 @@ export const validator = {
|
|||||||
/^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/;
|
/^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/;
|
||||||
return semverRegex.test(version);
|
return semverRegex.test(version);
|
||||||
},
|
},
|
||||||
|
isValidUnixFilePath: (path: string) => {
|
||||||
|
const endsWithSlash = path.endsWith("/");
|
||||||
|
const unixPathRegex = /^\/(?:[^/]+\/)*[^/]+$/;
|
||||||
|
const isValid = unixPathRegex.test(path);
|
||||||
|
return isValid && !endsWithSlash;
|
||||||
|
},
|
||||||
|
isValidWindowsFilePath: (path: string) => {
|
||||||
|
const endsWithBackSlash = path.endsWith("\\");
|
||||||
|
const windowsPathRegex =
|
||||||
|
/^[a-zA-Z]:\\(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*$/;
|
||||||
|
const isValid = windowsPathRegex.test(path);
|
||||||
|
return isValid && !endsWithBackSlash;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export function isInt(n: number) {
|
export function isInt(n: number) {
|
||||||
return n % 1 === 0;
|
return n % 1 === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function tryGetProcessNameFromPath(path: string) {
|
||||||
|
try {
|
||||||
|
const canSplitByForwardSlash = path.includes("/");
|
||||||
|
const canSplitByBackSlash = path.includes("\\");
|
||||||
|
const byForwardSlash = canSplitByForwardSlash
|
||||||
|
? path.split("/").pop()
|
||||||
|
: undefined;
|
||||||
|
const byBackSlash = canSplitByBackSlash
|
||||||
|
? path.split("\\").pop()
|
||||||
|
: undefined;
|
||||||
|
return byForwardSlash || byBackSlash || path;
|
||||||
|
} catch (e) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user