From eb70e87be1053ed0b3d33841fac200712af030f8 Mon Sep 17 00:00:00 2001 From: Mcat12 Date: Sat, 6 Jul 2019 18:06:16 -0700 Subject: [PATCH] Restrict the CIDR values to what dnsmasq and the API accepts The check is not 100% accurate, as it allows > 32 sized CIDRs for IPv4 addresses, but it is good enough for the frontend checks. Signed-off-by: Mcat12 --- scripts/make-fake-data.js | 2 +- .../ConditionalForwardingSettings.tsx | 15 ++-- src/components/settings/DNSInfo.tsx | 21 +++++- src/util/__tests__/validate.test.tsx | 70 +++++++++++++++++++ src/util/validate.tsx | 20 ++++++ 5 files changed, 121 insertions(+), 7 deletions(-) diff --git a/scripts/make-fake-data.js b/scripts/make-fake-data.js index d4af560..c492d53 100644 --- a/scripts/make-fake-data.js +++ b/scripts/make-fake-data.js @@ -382,7 +382,7 @@ function getDNSInfo() { .word() .toLowerCase() .split(" ", 2)[0], - cidr: faker.random.number({ min: 1, max: 32 }) + cidr: faker.random.arrayElement([8, 16, 24, 32]) } }; } diff --git a/src/components/settings/ConditionalForwardingSettings.tsx b/src/components/settings/ConditionalForwardingSettings.tsx index bf58d49..23547c3 100644 --- a/src/components/settings/ConditionalForwardingSettings.tsx +++ b/src/components/settings/ConditionalForwardingSettings.tsx @@ -23,6 +23,7 @@ export interface ConditionalForwardingSettingsProps { settings: ConditionalForwardingObject; onUpdate: (settings: ConditionalForwardingObject) => void; isRouterIpValid: boolean; + isCidrValid: boolean; isDomainValid: boolean; t: i18next.TFunction; } @@ -31,6 +32,7 @@ const ConditionalForwardingSettings = ({ settings, onUpdate, isRouterIpValid, + isCidrValid, isDomainValid, t }: ConditionalForwardingSettingsProps) => ( @@ -67,12 +69,17 @@ const ConditionalForwardingSettings = ({ { - const cidr = parseInt(e.target.value); + let cidr = parseInt(e.target.value); - // Only allow numbers - if (isNaN(cidr)) { + if (e.target.value.length === 0) { + // Use -1 as an internal representation of the empty string. + // It will show the form as invalid and make the text field + // function as the user expects it to. + cidr = -1; + } else if (isNaN(cidr)) { return; } diff --git a/src/components/settings/DNSInfo.tsx b/src/components/settings/DNSInfo.tsx index 6b87b79..f7c7b62 100644 --- a/src/components/settings/DNSInfo.tsx +++ b/src/components/settings/DNSInfo.tsx @@ -23,7 +23,13 @@ import ConditionalForwardingSettings, { } from "./ConditionalForwardingSettings"; import DnsOptionSettings, { DnsOptionsObject } from "./DnsOptionSettings"; import Alert, { AlertType } from "../common/Alert"; -import { isValidHostname, isValidIpv4, isValidIpv6 } from "../../util/validate"; +import { + isValidHostname, + isValidIpv4, + isValidIpv4Cidr, + isValidIpv6, + isValidIpv6Cidr +} from "../../util/validate"; export interface DNSInfoState { alertMessage: string; @@ -195,6 +201,11 @@ class DNSInfo extends Component { address => isValidIpv4(address) || isValidIpv6(address) ); + const isCidrValid = this.isCFSettingValid( + this.state.conditionalForwarding.cidr.toString(), + cidr => isValidIpv4Cidr(cidr) || isValidIpv6Cidr(cidr) + ); + const isDomainValid = this.isCFSettingValid( this.state.conditionalForwarding.domain, isValidHostname @@ -226,6 +237,7 @@ class DNSInfo extends Component { settings={this.state.conditionalForwarding} onUpdate={this.handleConditionalForwardingUpdate} isRouterIpValid={isRouterIpValid} + isCidrValid={isCidrValid} isDomainValid={isDomainValid} t={t} /> @@ -239,7 +251,12 @@ class DNSInfo extends Component { diff --git a/src/util/__tests__/validate.test.tsx b/src/util/__tests__/validate.test.tsx index 2b91f08..1677ef9 100644 --- a/src/util/__tests__/validate.test.tsx +++ b/src/util/__tests__/validate.test.tsx @@ -12,8 +12,10 @@ import { isPositiveNumber, isValidDomain, isValidIpv4, + isValidIpv4Cidr, isValidIpv4OptionalPort, isValidIpv6, + isValidIpv6Cidr, isValidIpv6OptionalPort, isValidRegex } from "../validate"; @@ -230,6 +232,40 @@ describe("Testing the validation functions", () => { }); }); + describe("isValidIpv4Cidr", () => { + it("passes 8", () => { + expect(isValidIpv4Cidr("8")).toBe(true); + }); + + it("passes 16", () => { + expect(isValidIpv4Cidr("16")).toBe(true); + }); + + it("passes 24", () => { + expect(isValidIpv4Cidr("24")).toBe(true); + }); + + it("passes 32", () => { + expect(isValidIpv4Cidr("32")).toBe(true); + }); + + it("fails -1", () => { + expect(isValidIpv4Cidr("-1")).toBe(false); + }); + + it("fails 0", () => { + expect(isValidIpv4Cidr("0")).toBe(false); + }); + + it("fails 28", () => { + expect(isValidIpv4Cidr("28")).toBe(false); + }); + + it("fails 40", () => { + expect(isValidIpv4Cidr("40")).toBe(false); + }); + }); + describe("isValidIpv6", () => { it("passes 1fff:0:a88:85a3::ac1f", () => { expect(isValidIpv6("1fff:0:a88:85a3::ac1f")).toBe(true); @@ -291,4 +327,38 @@ describe("Testing the validation functions", () => { expect(isValidIpv6OptionalPort("[::1]")).toBe(false); }); }); + + describe("isValidIpv6Cidr", () => { + it("passes 8", () => { + expect(isValidIpv6Cidr("8")).toBe(true); + }); + + it("passes 64", () => { + expect(isValidIpv6Cidr("64")).toBe(true); + }); + + it("passes 56", () => { + expect(isValidIpv6Cidr("56")).toBe(true); + }); + + it("passes 128", () => { + expect(isValidIpv6Cidr("128")).toBe(true); + }); + + it("fails -1", () => { + expect(isValidIpv6Cidr("-1")).toBe(false); + }); + + it("fails 0", () => { + expect(isValidIpv6Cidr("0")).toBe(false); + }); + + it("fails 63", () => { + expect(isValidIpv6Cidr("63")).toBe(false); + }); + + it("fails 150", () => { + expect(isValidIpv6Cidr("150")).toBe(false); + }); + }); }); diff --git a/src/util/validate.tsx b/src/util/validate.tsx index 1efa46b..712258d 100644 --- a/src/util/validate.tsx +++ b/src/util/validate.tsx @@ -95,6 +95,15 @@ export function isValidIpv4OptionalPort(address: string): boolean { ); } +/** + * Check if the string is a valid IPv4 CIDR number which dnsmasq accepts + * + * @param cidr The string to check + */ +export function isValidIpv4Cidr(cidr: string): boolean { + return ["8", "16", "24", "32"].includes(cidr); +} + /** * Check if the string is a valid IPv6 address. This function does not do a * thorough check of the IPv6 address; the backend will check it in detail. @@ -118,3 +127,14 @@ export function isValidIpv6(address: string): boolean { export function isValidIpv6OptionalPort(address: string): boolean { return /^(\[[a-fA-F0-9:]+]:\d+|[a-fA-F0-9:]+)$/.test(address); } + +/** + * Check if the string is a valid IPv6 CIDR number which dnsmasq accepts + * + * @param cidr The string to check + */ +export function isValidIpv6Cidr(cidr: string): boolean { + const cidrNum = parseInt(cidr); + + return !isNaN(cidrNum) && cidrNum > 0 && cidrNum <= 128 && cidrNum % 4 === 0; +}