From 439bef82e40104e9b9f37e4e5633eec044c87912 Mon Sep 17 00:00:00 2001 From: Timo Stamm Date: Wed, 16 Apr 2025 17:29:28 +0200 Subject: [PATCH 1/2] Fix several issues in isHostAndPort, isHostname, and isUri --- .../buf/protovalidate/CustomOverload.java | 11 ++++++---- .../java/build/buf/protovalidate/Uri.java | 22 +++++++++---------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/main/java/build/buf/protovalidate/CustomOverload.java b/src/main/java/build/buf/protovalidate/CustomOverload.java index 94cc4819..fa244fbe 100644 --- a/src/main/java/build/buf/protovalidate/CustomOverload.java +++ b/src/main/java/build/buf/protovalidate/CustomOverload.java @@ -16,7 +16,6 @@ import com.google.common.primitives.Bytes; import java.util.HashSet; -import java.util.Locale; import java.util.Set; import java.util.regex.Pattern; import org.projectnessie.cel.common.types.BoolT; @@ -463,6 +462,10 @@ private static boolean isPort(String str) { return false; } + if (str.length() > 1 && str.charAt(0) == '0') { + return false; + } + for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); if ('0' <= c && c <= '9') { @@ -557,7 +560,7 @@ private static boolean isHostname(String val) { boolean allDigits = false; - String[] parts = str.toLowerCase(Locale.getDefault()).split("\\.", -1); + String[] parts = str.split("\\.", -1); // split hostname on '.' and validate each part for (String part : parts) { @@ -572,8 +575,8 @@ private static boolean isHostname(String val) { // for each character in part for (int i = 0; i < part.length(); i++) { char c = part.charAt(i); - // if the character is not a-z, 0-9, or '-', it is invalid - if ((c < 'a' || c > 'z') && (c < '0' || c > '9') && c != '-') { + // if the character is not a-z, A-Z, 0-9, or '-', it is invalid + if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9') && c != '-') { return false; } diff --git a/src/main/java/build/buf/protovalidate/Uri.java b/src/main/java/build/buf/protovalidate/Uri.java index 65fd63ed..fd101092 100644 --- a/src/main/java/build/buf/protovalidate/Uri.java +++ b/src/main/java/build/buf/protovalidate/Uri.java @@ -167,7 +167,7 @@ private boolean scheme() { if (this.alpha()) { while (this.alpha() || this.digit() || this.take('+') || this.take('-') || this.take('.')) {} - if (this.str.charAt(this.index) == ':') { + if (this.peek(':')) { return true; } } @@ -253,10 +253,8 @@ private boolean userinfo() { continue; } - if (this.index < this.str.length()) { - if (this.str.charAt(this.index) == '@') { - return true; - } + if (this.peek('@')) { + return true; } this.index = start; @@ -335,15 +333,11 @@ private boolean checkHostPctEncoded(String str) { *
host = IP-literal / IPv4address / reg-name.
    */
   private boolean host() {
-    if (this.index >= this.str.length()) {
-      return true;
-    }
-
     int start = this.index;
     this.pctEncodedFound = false;
 
     // Note: IPv4address is a subset of reg-name
-    if ((this.str.charAt(this.index) == '[' && this.ipLiteral()) || this.regName()) {
+    if ((this.peek('[') && this.ipLiteral()) || this.regName()) {
       if (this.pctEncodedFound) {
         String rawHost = this.str.substring(start, this.index);
         // RFC 3986:
@@ -536,7 +530,7 @@ private boolean regName() {
         return true;
       }
 
-      if (this.str.charAt(this.index) == ':') {
+      if (this.peek(':')) {
         return true;
       }
 
@@ -767,7 +761,7 @@ private boolean query() {
         continue;
       }
 
-      if (this.index == this.str.length() || this.str.charAt(this.index) == '#') {
+      if (this.peek('#') || this.index == this.str.length()) {
         return true;
       }
 
@@ -942,4 +936,8 @@ private boolean take(char c) {
 
     return false;
   }
+
+  private boolean peek(char c) {
+    return this.index < this.str.length() && this.str.charAt(this.index) == c;
+  }
 }

From 07c8921cce1e14b197a117fdeba3401ee79e21fa Mon Sep 17 00:00:00 2001
From: Timo Stamm 
Date: Wed, 16 Apr 2025 17:35:07 +0200
Subject: [PATCH 2/2] Format JavaDoc for consistency

---
 .../java/build/buf/protovalidate/Uri.java     | 38 +++++++++----------
 1 file changed, 19 insertions(+), 19 deletions(-)

diff --git a/src/main/java/build/buf/protovalidate/Uri.java b/src/main/java/build/buf/protovalidate/Uri.java
index fd101092..60a127e2 100644
--- a/src/main/java/build/buf/protovalidate/Uri.java
+++ b/src/main/java/build/buf/protovalidate/Uri.java
@@ -440,7 +440,7 @@ private boolean ipv6Address() {
   /**
    * Determines whether the current position is a valid IPv6addrz.
    *
-   * Parses the rule:
+   * 

Parses the rule: * *

IPv6addrz = IPv6address "%25" ZoneID
    */
@@ -459,7 +459,7 @@ private boolean ipv6Addrz() {
   /**
    * Determines whether the current position is a valid zone ID.
    *
-   * Parses the rule:
+   * 

Parses the rule: * *

ZoneID = 1*( unreserved / pct-encoded )
    */
@@ -480,7 +480,7 @@ private boolean zoneID() {
   /**
    * Determines whether the current position is a valid IPvFuture.
    *
-   * Parses the rule:
+   * 

Parses the rule: * *

IPvFuture  = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
    */
@@ -511,7 +511,7 @@ private boolean ipvFuture() {
   /**
    * Determines whether the current position is a valid reg-name.
    *
-   * Parses the rule:
+   * 

Parses the rule: * *

reg-name = *( unreserved / pct-encoded / sub-delims )
    *
@@ -564,7 +564,7 @@ private boolean isPathEnd() {
   /**
    * Determines whether the current position is a valid path-abempty.
    *
-   * Parses the rule:
+   * 

Parses the rule: * *

path-abempty = *( "/" segment )
    *
@@ -587,7 +587,7 @@ private boolean pathAbempty() {
   /**
    * Determines whether the current position is a valid path-absolute.
    *
-   * Parses the rule:
+   * 

Parses the rule: * *

path-absolute = "/" [ segment-nz *( "/" segment ) ]
    *
@@ -614,7 +614,7 @@ private boolean pathAbsolute() {
   /**
    * Determines whether the current position is a valid path-noscheme.
    *
-   * Parses the rule:
+   * 

Parses the rule: * *

path-noscheme = segment-nz-nc *( "/" segment )
    *
@@ -639,7 +639,7 @@ private boolean pathNoscheme() {
   /**
    * Determines whether the current position is a valid path-rootless.
    *
-   * Parses the rule:
+   * 

Parses the rule: * *

path-rootless = segment-nz *( "/" segment )
    *
@@ -664,7 +664,7 @@ private boolean pathRootless() {
   /**
    * Determines whether the current position is a valid path-empty.
    *
-   * Parses the rule:
+   * 

Parses the rule: * *

path-empty = 0
    *
@@ -677,7 +677,7 @@ private boolean pathEmpty() {
   /**
    * Determines whether the current position is a valid segment.
    *
-   * Parses the rule:
+   * 

Parses the rule: * *

segment = *pchar
    */
@@ -690,7 +690,7 @@ private boolean segment() {
   /**
    * Determines whether the current position is a valid segment-nz.
    *
-   * Parses the rule:
+   * 

Parses the rule: * *

segment-nz = 1*pchar
    */
@@ -710,7 +710,7 @@ private boolean segmentNz() {
   /**
    * Determines whether the current position is a valid segment-nz-nc.
    *
-   * Parses the rule:
+   * 

Parses the rule: * *

segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
    *                   ; non-zero-length segment without any colon ":"
@@ -732,7 +732,7 @@ private boolean segmentNzNc() {
   /**
    * Determines whether the current position is a valid pchar.
    *
-   * Parses the rule:
+   * 

Parses the rule: * *

pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
    */
@@ -747,7 +747,7 @@ private boolean pchar() {
   /**
    * Determines whether the current position is a valid query.
    *
-   * Parses the rule:
+   * 

Parses the rule: * *

query = *( pchar / "/" / "?" )
    *
@@ -774,7 +774,7 @@ private boolean query() {
   /**
    * Determines whether the current position is a valid fragment.
    *
-   * Parses the rule:
+   * 

Parses the rule: * *

fragment = *( pchar / "/" / "?" )
    *
@@ -801,7 +801,7 @@ private boolean fragment() {
   /**
    * Determines whether the current position is a valid pct-encoded.
    *
-   * Parses the rule:
+   * 

Parses the rule: * *

pct-encoded = "%"+HEXDIG+HEXDIG
    *
@@ -824,7 +824,7 @@ private boolean pctEncoded() {
   /**
    * Determines whether the current position is an unreserved character.
    *
-   * Parses the rule:
+   * 

Parses the rule: * *

unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
    */
@@ -840,7 +840,7 @@ private boolean unreserved() {
   /**
    * Determines whether the current position is a sub-delim.
    *
-   * Parses the rule:
+   * 

Parses the rule: * *

sub-delims  = "!" / "$" / "&" / "'" / "(" / ")"
    *                  / "*" / "+" / "," / ";" / "="
@@ -862,7 +862,7 @@ private boolean subDelims() {
   /**
    * Determines whether the current position is an alpha character.
    *
-   * Parses the rule:
+   * 

Parses the rule: * *

ALPHA =  %x41-5A / %x61-7A ; A-Z / a-z
    */