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

Added forward slash validation for forward and reverse zone #1134

Merged
merged 11 commits into from
Sep 13, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import scala.util.matching.Regex
Object to house common domain validations
*/
object DomainValidations {
val validReverseFQDNRegex: Regex =
"""^(?:([0-9a-zA-Z\-\/_]{1,63}|[0-9a-zA-Z\-\/_]{1}[0-9a-zA-Z\-\/_]{0,61}[0-9a-zA-Z\-\/_]{1}|[*.]{2}[0-9a-zA-Z\-\/_]{0,60}[0-9a-zA-Z\-\/_]{1})\.)*$""".r
val validFQDNRegex: Regex =
"""^(?:([0-9a-zA-Z_]{1,63}|[0-9a-zA-Z_]{1}[0-9a-zA-Z\-\/_]{0,61}[0-9a-zA-Z_]{1}|[*.]{2}[0-9a-zA-Z\-\/_]{0,60}[0-9a-zA-Z_]{1})\.)*$""".r
val validIpv4Regex: Regex =
Expand Down Expand Up @@ -60,7 +62,35 @@ object DomainValidations {
def validateHostName(name: Fqdn): ValidatedNel[DomainValidationError, Fqdn] =
validateHostName(name.fqdn).map(_ => name)

def validateReverseHostName(name: Fqdn): ValidatedNel[DomainValidationError, Fqdn] =
validateReverseHostName(name.fqdn).map(_ => name)

def validateHostName(name: String): ValidatedNel[DomainValidationError, String] = {
/*
Label rules are as follows (from RFC 952; detailed in RFC 1034):
- Starts with a letter, or digit, or underscore or asterisk (as of RFC 1123)
- Interior contains letter, digit or hyphen, or underscore
- Ends with a letter or digit, or underscore
All possible labels permutations:
- A single letter/digit: [0-9a-zA-Z]{1}
- A combination of 1-63 letters/digits: [0-9a-zA-Z]{1,63}
- A single letter/digit followed by up to 61 letters, digits, hyphens or slashes
and ending with a letter/digit:[0-9a-zA-Z]{1}[0-9a-zA-Z\-]{0,61}[0-9a-zA-Z]{1}
- A wildcard and dot character (*.) followed by up to 60 letters, digits, hyphens
and ending with a letter/digit:[*.]{2}[0-9a-zA-Z\-_]{0,60}[0-9a-zA-Z_]{1}
A valid domain name is a series of one or more <label>s,
joined by dots and terminating on a zero-length <label> (ie. dot)
*/
val checkRegex = validFQDNRegex
.findFirstIn(name)
.map(_.validNel)
.getOrElse(InvalidDomainName(name).invalidNel)
val checkLength = validateStringLength(name, Some(HOST_MIN_LENGTH), HOST_MAX_LENGTH)

checkRegex.combine(checkLength).map(_ => name)
}

def validateReverseHostName(name: String): ValidatedNel[DomainValidationError, String] = {
nspadaccino marked this conversation as resolved.
Show resolved Hide resolved
/*
Label rules are as follows (from RFC 952; detailed in RFC 1034):
- Starts with a letter, or digit, or underscore or asterisk (as of RFC 1123)
Expand All @@ -76,7 +106,7 @@ object DomainValidations {
A valid domain name is a series of one or more <label>s,
joined by dots/slashes and terminating on a zero-length <label> (ie. dot)
*/
val checkRegex = validFQDNRegex
val checkRegex = validReverseFQDNRegex
.findFirstIn(name)
.map(_.validNel)
.getOrElse(InvalidDomainName(name).invalidNel)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ class BatchChangeValidations(
isApproved: Boolean
): SingleValidation[Unit] = {
val validTTL = addChangeInput.ttl.map(validateTTL(_).asUnit).getOrElse(().valid)
val validRecord = validateRecordData(addChangeInput.record)
val validRecord = validateRecordData(addChangeInput.record, addChangeInput)
val validInput = validateInputName(addChangeInput, isApproved)

validTTL |+| validRecord |+| validInput
Expand All @@ -222,19 +222,29 @@ class BatchChangeValidations(
isApproved: Boolean
): SingleValidation[Unit] = {
val validRecord = deleteRRSetChangeInput.record match {
case Some(recordData) => validateRecordData(recordData)
case Some(recordData) => validateRecordData(recordData, deleteRRSetChangeInput)
case None => ().validNel
}
val validInput = validateInputName(deleteRRSetChangeInput, isApproved)

validRecord |+| validInput
}

def validateRecordData(record: RecordData): SingleValidation[Unit] =
def validateRecordData(record: RecordData,change: ChangeInput): SingleValidation[Unit] =
record match {
case a: AData => validateIpv4Address(a.address).asUnit
case aaaa: AAAAData => validateIpv6Address(aaaa.address).asUnit
case cname: CNAMEData => validateHostName(cname.cname).asUnit
case cname: CNAMEData =>
/*
To validate the zone is reverse
*/
val isIPv4: Boolean = change.inputName.toLowerCase.endsWith("in-addr.arpa.")
val isIPv6: Boolean = change.inputName.toLowerCase.endsWith("ip6.arpa.")
val isReverse: Boolean = isIPv4 || isIPv6
isReverse match {
case true => validateReverseHostName(cname.cname).asUnit
case false => validateHostName(cname.cname).asUnit
}
case ptr: PTRData => validateHostName(ptr.ptrdname).asUnit
case txt: TXTData => validateTxtTextLength(txt.text).asUnit
case mx: MXData =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2666,4 +2666,21 @@ class BatchChangeValidationsSpec
result(3) shouldBe valid
result(4) shouldBe valid
}

property("validateAddChangeInput: should fail for a CNAME addChangeInput with forward slash for forward zone") {
val cnameWithForwardSlash = AddChangeInput("cname.ok.", RecordType.CNAME, ttl, CNAMEData(Fqdn("cname/")))
val result = validateAddChangeInput(cnameWithForwardSlash, false)
result should haveInvalid[DomainValidationError](InvalidDomainName("cname/."))
}
property("validateAddChangeInput: should succeed for a valid CNAME addChangeInput without forward slash for forward zone") {
val cname = AddChangeInput("cname.ok.", RecordType.CNAME, ttl, CNAMEData(Fqdn("cname")))
val result = validateAddChangeInput(cname, false)
result shouldBe valid
}
property("validateAddChangeInput: should succeed for a valid CNAME addChangeInput with forward slash for reverse zone") {
val cnameWithForwardSlash = AddChangeInput("2.0.192.in-addr.arpa.", RecordType.CNAME, ttl, CNAMEData(Fqdn("cname/")))
val result = validateAddChangeInput(cnameWithForwardSlash, false)
result shouldBe valid
}

}