Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add better input validation for setup-keys, nameserver and routes #373

Merged
merged 7 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/components/Notification.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { IconCircleX } from "@tabler/icons-react";
import type { ErrorResponse } from "@utils/api";
import { cn } from "@utils/helpers";
import classNames from "classnames";
Expand Down Expand Up @@ -88,7 +89,7 @@ export default function Notification<T>({
{loading ? (
<Loader2 size={14} className={"animate-spin"} />
) : error ? (
<XIcon size={14} />
<IconCircleX size={24} />
) : (
icon || <CheckIcon size={14} />
)}
Expand Down
2 changes: 1 addition & 1 deletion src/contexts/RoutesProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const RoutesContext = React.createContext(
);

export default function RoutesProvider({ children }: Props) {
const routeRequest = useApiCall<Route>("/routes");
const routeRequest = useApiCall<Route>("/routes", true);
const { mutate } = useSWRConfig();

const updateRoute = async (
Expand Down
20 changes: 14 additions & 6 deletions src/modules/dns-nameservers/NameserverModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export function NameserverModalContent({
preset,
cell,
}: ModalProps) {
const nsRequest = useApiCall<NameserverGroup>("/dns/nameservers");
const nsRequest = useApiCall<NameserverGroup>("/dns/nameservers", true);
const { mutate } = useSWRConfig();

const isUpdate = useMemo(() => {
Expand Down Expand Up @@ -233,24 +233,31 @@ export function NameserverModalContent({
return domains.some((d) => d.name === "");
}, [domains]);

const nameLengthError = useMemo(() => {
if (name.length > 40) return "Name should be less than 40 characters";
return "";
}, [name]);

const hasAnyError = useMemo(() => {
return (
hasNSErrors ||
nsError ||
domainError ||
name == "" ||
nameservers.length == 0 ||
hasDomainErrors ||
groups.length == 0
groups.length == 0 ||
nameLengthError !== "" ||
name == ""
);
}, [
nsError,
domainError,
name,
nameservers,
groups,
hasNSErrors,
hasDomainErrors,
nameLengthError,
name,
]);

return (
Expand Down Expand Up @@ -427,6 +434,7 @@ export function NameserverModalContent({
<Input
autoFocus={true}
tabIndex={0}
error={nameLengthError}
placeholder={"e.g., Public DNS"}
value={name}
onChange={(e) => setName(e.target.value)}
Expand Down Expand Up @@ -516,7 +524,7 @@ function NameserverInput({
const validCIDR = cidr.isValidAddress(ip);
if (!validCIDR) {
onError && onError(true);
return "Please enter a valid CIDR, e.g., 192.168.1.0/24";
return "Please enter a valid IP, e.g., 192.168.1.0";
}
onError && onError(false);
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand All @@ -532,7 +540,7 @@ function NameserverInput({
<div className={"w-full"}>
<Input
customPrefix={"IP"}
placeholder={"e.g., 172.16.0.0/16"}
placeholder={"e.g., 172.16.0.0"}
maxWidthClass={"w-full"}
value={ip}
className={"font-mono !text-[13px]"}
Expand Down
42 changes: 34 additions & 8 deletions src/modules/routes/RouteModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,17 +183,37 @@ export function RouteModalContent({
(cidrError && cidrError.length > 1) ||
(peerTab === "peer-group" && routingPeerGroups.length == 0) ||
(peerTab === "routing-peer" && !routingPeer) ||
groups.length == 0
groups.length == 0 ||
networkRange == ""
);
}, [cidrError, peerTab, routingPeerGroups.length, routingPeer, groups]);
}, [
cidrError,
peerTab,
routingPeerGroups.length,
routingPeer,
groups,
networkRange,
]);

const isNameEntered = useMemo(() => {
return !(networkIdentifier == "");
const networkIdentifierError = useMemo(() => {
return (networkIdentifier?.length || 0) > 40
? "Network Identifier must be less than 40 characters"
: "";
}, [networkIdentifier]);

const metricError = useMemo(() => {
return parseInt(metric) < 1 || parseInt(metric) > 9999
? "Metric must be between 1 and 9999"
: "";
}, [metric]);

const isNameEntered = useMemo(() => {
return networkIdentifier != "" && networkIdentifierError == "";
}, [networkIdentifier, networkIdentifierError]);

const canCreateOrSave = useMemo(() => {
return isNetworkEntered && isNameEntered;
}, [isNetworkEntered, isNameEntered]);
return isNetworkEntered && isNameEntered && metricError == "";
}, [isNetworkEntered, isNameEntered, metricError]);

return (
<ModalContent maxWidthClass={"max-w-xl"}>
Expand Down Expand Up @@ -250,7 +270,10 @@ export function RouteModalContent({
/>
Name & Description
</TabsTrigger>
<TabsTrigger value={"settings"} disabled={!canCreateOrSave}>
<TabsTrigger
value={"settings"}
disabled={!isNetworkEntered || !isNameEntered}
>
<Settings2
size={16}
className={
Expand Down Expand Up @@ -340,6 +363,7 @@ export function RouteModalContent({
Add a unique network identifier that is assigned to each device.
</HelpText>
<Input
error={networkIdentifierError}
autoFocus={true}
tabIndex={0}
ref={nameRef}
Expand Down Expand Up @@ -406,6 +430,8 @@ export function RouteModalContent({
max={9999}
maxWidthClass={"max-w-[200px]"}
value={metric}
error={metricError}
errorTooltip={true}
type={"number"}
onChange={(e) => setMetric(e.target.value)}
customPrefix={
Expand Down Expand Up @@ -469,7 +495,7 @@ export function RouteModalContent({
<Button
variant={"primary"}
onClick={() => setTab("settings")}
disabled={!canCreateOrSave}
disabled={!isNameEntered || !isNetworkEntered}
>
Continue
</Button>
Expand Down
13 changes: 11 additions & 2 deletions src/modules/routes/RouteUpdateModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,14 +197,21 @@ function RouteUpdateModalContent({ onSuccess, route, cell }: ModalProps) {
.filter((p) => p != undefined) as string[];
}, [groupedRoute]);

const metricError = useMemo(() => {
return parseInt(metric.toString()) < 1 || parseInt(metric.toString()) > 9999
? "Metric must be between 1 and 9999"
: "";
}, [metric]);

// Is button disabled
const isDisabled = useMemo(() => {
return (
(peerTab === "peer-group" && routingPeerGroups.length == 0) ||
(peerTab === "routing-peer" && !routingPeer) ||
groups.length == 0
groups.length == 0 ||
metricError !== ""
);
}, [peerTab, routingPeerGroups.length, routingPeer, groups]);
}, [peerTab, routingPeerGroups.length, routingPeer, groups, metricError]);

const [tab, setTab] = useState(
cell && cell == "metric" ? "settings" : "network",
Expand Down Expand Up @@ -352,6 +359,8 @@ function RouteUpdateModalContent({ onSuccess, route, cell }: ModalProps) {
max={9999}
maxWidthClass={"max-w-[200px]"}
value={metric}
error={metricError}
errorTooltip={true}
type={"number"}
onChange={(e) => setMetric(e.target.value)}
customPrefix={
Expand Down
16 changes: 13 additions & 3 deletions src/modules/setup-keys/SetupKeyModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ type ModalProps = {
};

export function SetupKeyModalContent({ onSuccess }: ModalProps) {
const setupKeyRequest = useApiCall<SetupKey>("/setup-keys");
const setupKeyRequest = useApiCall<SetupKey>("/setup-keys", true);
const { mutate } = useSWRConfig();

const [name, setName] = useState("");
Expand All @@ -143,10 +143,18 @@ export function SetupKeyModalContent({ onSuccess }: ModalProps) {
return reusable ? "Unlimited" : "1";
}, [reusable]);

const expiresInError = useMemo(() => {
const expires = parseInt(expiresIn);
if (expires < 1 || expires > 365) {
return "Days should be between 1 and 365";
}
return "";
}, [expiresIn]);

const isDisabled = useMemo(() => {
const trimmedName = trim(name);
return trimmedName.length === 0;
}, [name]);
return trimmedName.length === 0 || expiresInError.length > 0;
}, [name, expiresInError]);

const submit = () => {
if (!selectedGroups) return;
Expand Down Expand Up @@ -245,6 +253,8 @@ export function SetupKeyModalContent({ onSuccess }: ModalProps) {
min={1}
max={365}
value={expiresIn}
error={expiresInError}
errorTooltip={true}
type={"number"}
onChange={(e) => setExpiresIn(e.target.value)}
customPrefix={
Expand Down
6 changes: 3 additions & 3 deletions src/utils/api.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,13 @@ export function useApiErrorHandling(ignoreError = false) {
return login(currentPath);
}
if (err.code == 401 && err.message == "token invalid") {
return setError(err);
setError(err);
}
if (err.code == 500 && err.message == "internal server error") {
return setError(err);
setError(err);
}
if (err.code > 400 && err.code <= 500) {
return setError(err);
setError(err);
}

return Promise.reject(err);
Expand Down
Loading