@@ -62,20 +62,26 @@ public static SiftPattern<Fragment> uuid() {
6262 /**
6363 * Matches a valid IPv4 address.
6464 * <p>
65- * Validates that each octet is strictly between 0 and 255.
66- * Prevents matching invalid IPs like {@code 256.100.50.25}.
65+ * Validates that each octet is strictly between 0 and 255 and rejects octets with
66+ * leading zeros (e.g., {@code 01}, {@code 001}). This is intentional: several network
67+ * libraries interpret zero-padded octets as octal numbers, which is a known source of
68+ * IP-confusion / SSRF issues (see e.g. CVE-2021-29921).
6769 *
6870 * @return A SiftPattern representing an IPv4 address.
6971 */
7072 public static SiftPattern <Fragment > ipv4 () {
71- // Octet logic: 25[0-5] OR 2[0-4][0-9] OR [01]?[ 0-9][0-9]?
73+ // Octet logic (no leading zeros) : 25[0-5] | 2[0-4][0-9] | 1[0-9]{2} | [1-9][ 0-9] | [0-9]
7274 SiftPattern <Fragment > octet = anyOf (
7375 // 250-255
7476 Sift .fromAnywhere ().character ('2' ).followedBy ('5' ).then ().exactly (1 ).range ('0' , '5' ),
7577 // 200-249
7678 Sift .fromAnywhere ().character ('2' ).then ().exactly (1 ).range ('0' , '4' ).then ().exactly (1 ).digits (),
77- // 0-199
78- Sift .fromAnywhere ().optional ().range ('0' , '1' ).then ().exactly (1 ).digits ().then ().optional ().digits ()
79+ // 100-199
80+ Sift .fromAnywhere ().character ('1' ).then ().exactly (2 ).digits (),
81+ // 10-99
82+ Sift .fromAnywhere ().range ('1' , '9' ).then ().exactly (1 ).digits (),
83+ // 0-9
84+ Sift .fromAnywhere ().digits ()
7985 );
8086
8187 return Sift .fromAnywhere ()
@@ -220,12 +226,13 @@ public static SiftPattern<Fragment> isoDate() {
220226 * Matches a structural International Bank Account Number (IBAN).
221227 * <p>
222228 * <b>Important:</b> This pattern performs <i>syntactic</i> validation of the IBAN format.
223- * It ensures the string matches the international standard shape:
229+ * It ensures the string matches the international standard shape (ISO 13616) :
224230 * <ul>
225- * <li>2 letters for the Country Code</li>
231+ * <li>2 <b>uppercase</b> letters for the Country Code</li>
226232 * <li>2 digits for the Check Digits</li>
227- * <li>Between 11 and 30 alphanumeric characters for the Basic Bank Account Number (BBAN)</li>
233+ * <li>Between 11 and 30 <b>uppercase</b> alphanumeric characters for the Basic Bank Account Number (BBAN)</li>
228234 * </ul>
235+ * Lowercase input is rejected because ISO 13616 mandates uppercase representation.
229236 * <p>
230237 * It does <b>not</b> perform <i>semantic</i> validation, such as:
231238 * <ul>
@@ -240,9 +247,10 @@ public static SiftPattern<Fragment> isoDate() {
240247 */
241248 public static SiftPattern <Fragment > iban () {
242249 return Sift .fromAnywhere ()
243- .exactly (2 ).letters () // Country Code
250+ .exactly (2 ).upperCaseLetters () // Country Code (ISO 13616: uppercase)
244251 .then ().exactly (2 ).digits () // Check Digits
245- .then ().between (11 , 30 ).alphanumeric () // Basic Bank Account Number
252+ .then ().between (11 , 30 ).range ('A' , 'Z' )
253+ .including ('0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' ) // BBAN
246254 .preventBacktracking ();
247255 }
248256
0 commit comments