Skip to content

Fixes for hostname, port, and enum validation #83

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

Merged
merged 4 commits into from
Apr 8, 2025
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
31 changes: 0 additions & 31 deletions buf/validate/conformance/expected_failures.yaml
Original file line number Diff line number Diff line change
@@ -1,34 +1,3 @@
library/is_host_and_port:
- port_required/false/invalid/port_number_sign
# input: [type.googleapis.com/buf.validate.conformance.cases.IsHostAndPort]:{val:"example.com:+0"}
# want: validation error (1 violation)
# 1. constraint_id: "library.is_host_and_port"
# got: valid
- port_required/true/invalid/port_number_sign
# input: [type.googleapis.com/buf.validate.conformance.cases.IsHostAndPort]:{val:"example.com:+0" port_required:true}
# want: validation error (1 violation)
# 1. constraint_id: "library.is_host_and_port"
# got: valid
library/is_hostname:
- valid/label_interior_hyphen
# input: [type.googleapis.com/buf.validate.conformance.cases.IsHostname]:{val:"a-b.a--b"}
# want: valid
# got: validation error (1 violation)
# 1. constraint_id: "library.is_hostname"
# message: ""
standard_constraints/enum:
- defined_only/invalid/unknown
# input: [type.googleapis.com/buf.validate.conformance.cases.EnumDefined]:{val:2147483647}
# want: validation error (1 violation)
# 1. constraint_id: "enum.defined_only"
# message: "value must be one of the defined enum values"
# field: "val" elements:{field_number:1 field_name:"val" field_type:TYPE_ENUM}
# rule: "enum.defined_only" elements:{field_number:16 field_name:"enum" field_type:TYPE_MESSAGE} elements:{field_number:2 field_name:"defined_only" field_type:TYPE_BOOL}
# got: validation error (1 violation)
# 1. constraint_id: "enum.defined_only"
# message: "enum value must be defined"
# field: "val" elements:{field_number:1 field_name:"val" field_type:TYPE_ENUM}
# rule: "enum.defined_only" elements:{field_number:16 field_name:"enum" field_type:TYPE_MESSAGE} elements:{field_number:2 field_name:"defined_only" field_type:TYPE_BOOL}
standard_constraints/well_known_types/duration:
- gt_lt/exclusive/invalid/max
# input: [type.googleapis.com/buf.validate.conformance.cases.DurationExLTGT]:{val:{seconds:1}}
Expand Down
2 changes: 1 addition & 1 deletion buf/validate/internal/constraints.cc
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ absl::Status EnumConstraintRules::Validate(
if (field_->enum_type()->FindValueByNumber(value) == nullptr) {
Violation violation;
*violation.mutable_constraint_id() = "enum.defined_only";
*violation.mutable_message() = "enum value must be defined";
*violation.mutable_message() = "value must be one of the defined enum values";
*violation.mutable_field()->mutable_elements()->Add() = fieldPathElement(field_);
*violation.mutable_rule()->mutable_elements()->Add() = fieldPathElement(EnumRules::descriptor()->FindFieldByNumber(EnumRules::kDefinedOnlyFieldNumber));
*violation.mutable_rule()->mutable_elements()->Add() = fieldPathElement(FieldConstraints::descriptor()->FindFieldByNumber(FieldConstraints::kEnumFieldNumber));
Expand Down
42 changes: 23 additions & 19 deletions buf/validate/internal/extra_func.cc
Original file line number Diff line number Diff line change
Expand Up @@ -138,31 +138,26 @@ cel::CelValue endsWith(
return cel::CelValue::CreateBool(result);
}

bool IsHostname(std::string_view to_validate) {
if (to_validate.length() > 253) {
bool IsHostname(std::string_view toValidate) {
if (toValidate.length() > 253) {
return false;
}
static const re2::RE2 component_regex("^[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*$");
static const re2::RE2 all_digits("^[0-9]*$");
to_validate = absl::StripSuffix(to_validate, ".");
std::vector<std::string_view> split = absl::StrSplit(to_validate, '.');
if (split.size() < 2) {
return re2::RE2::FullMatch(to_validate, component_regex) &&
!re2::RE2::FullMatch(to_validate, all_digits);
}
if (re2::RE2::FullMatch(split[split.size() - 1], all_digits)) {
return false;
}
for (size_t i = 0; i < split.size(); i++) {
const std::string_view& part = split[i];
if (part.empty() || part.size() > 63) {
toValidate = absl::StripSuffix(toValidate, ".");
bool allDigits = false;
for (auto part : absl::StrSplit(toValidate, '.')) {
allDigits = true;
if (part.empty() || part.size() > 63 || absl::StartsWith(part, "-") ||
absl::EndsWith(part, "-")) {
return false;
}
if (!re2::RE2::FullMatch(part, component_regex)) {
return false;
for (auto ch : part) {
if ((ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z') && (ch < '0' || ch > '9') && ch != '-') {
return false;
}
allDigits = allDigits && ch >= '0' && ch <= '9';
}
}
return true;
return !allDigits;
}

cel::CelValue isHostname(google::protobuf::Arena* arena, cel::CelValue::StringHolder lhs) {
Expand Down Expand Up @@ -209,6 +204,15 @@ cel::CelValue isIP(google::protobuf::Arena* arena, cel::CelValue::StringHolder l

bool IsPort(const std::string_view str) {
uint32_t port;
if (str.empty()) {
return false;
}
for (auto c : str) {
if ('0' <= c && c <= '9') {
continue;
}
return false;
}
if (!absl::SimpleAtoi(str, &port)) {
return false;
}
Expand Down
Loading