better

+62 -8
+62 -8
src/atpasser/model/types/string.py
··· 124 @classmethod 125 def _validate_did(cls, v: str) -> None: 126 """Validate DID format""" 127 - if not v.startswith("did:"): 128 - raise ValueError("DID must start with 'did:'") 129 if len(v) > 2048: 130 raise ValueError("DID too long, max 2048 chars") 131 132 @classmethod 133 def _validate_handle(cls, v: str) -> None: 134 """Validate handle format""" 135 - if not re.match(r"^[a-zA-Z0-9._-]+$", v): 136 raise ValueError("Handle contains invalid characters") 137 if len(v) > 253: 138 raise ValueError("Handle too long, max 253 chars") ··· 150 151 @classmethod 152 def _validate_at_uri(cls, v: str) -> None: 153 - """Validate AT-URI format""" 154 if not v.startswith("at://"): 155 raise ValueError("AT-URI must start with 'at://'") 156 - if len(v) > 8000: 157 - raise ValueError("AT-URI too long, max 8000 chars") 158 159 @classmethod 160 def _validate_cid(cls, v: str) -> None: ··· 169 """Validate NSID format""" 170 if len(v) > 317: 171 raise ValueError("NSID too long, max 317 chars") 172 - if not re.match(r"^[a-zA-Z0-9.-]+$", v): 173 raise ValueError("NSID contains invalid characters") 174 175 @classmethod ··· 177 """Validate TID format""" 178 if len(v) > 13: 179 raise ValueError("TID too long, max 13 chars") 180 - if not re.match(r"^[234567abcdefghijklmnopqrstuvwxyz]+$", v): 181 raise ValueError("TID contains invalid characters") 182 183 @classmethod ··· 185 """Validate record-key format""" 186 if len(v) > 512: 187 raise ValueError("Record key too long, max 512 chars") 188 if not re.match(r"^[a-zA-Z0-9._:%-~]+$", v): 189 raise ValueError("Record key contains invalid characters") 190
··· 124 @classmethod 125 def _validate_did(cls, v: str) -> None: 126 """Validate DID format""" 127 if len(v) > 2048: 128 raise ValueError("DID too long, max 2048 chars") 129 + if not re.match(r"^did:[a-z]+:[a-zA-Z0-9._:%-]*[a-zA-Z0-9._-]$", v): 130 + raise ValueError("Invalid URI format") 131 132 @classmethod 133 def _validate_handle(cls, v: str) -> None: 134 """Validate handle format""" 135 + if not re.match(r"^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$", v): 136 raise ValueError("Handle contains invalid characters") 137 if len(v) > 253: 138 raise ValueError("Handle too long, max 253 chars") ··· 150 151 @classmethod 152 def _validate_at_uri(cls, v: str) -> None: 153 + """ 154 + Validate AT-URI format according to AT Protocol specification. 155 + 156 + Args: 157 + v: AT-URI string to validate 158 + 159 + Raises: 160 + ValueError: If URI violates any of these rules: 161 + - Must start with 'at://' 162 + - Max length 8KB 163 + - No trailing slash 164 + - Authority must be valid DID or handle 165 + - Path segments must follow NSID/RKEY rules if present 166 + """ 167 if not v.startswith("at://"): 168 raise ValueError("AT-URI must start with 'at://'") 169 + if len(v) > 8192: # 8KB 170 + raise ValueError("AT-URI too long, max 8KB") 171 + if v.endswith('/'): 172 + raise ValueError("AT-URI cannot have trailing slash") 173 + 174 + # Split into parts 175 + parts = v[5:].split('/') # Skip 'at://' 176 + authority = parts[0] 177 + 178 + # Validate authority (DID or handle) 179 + if not authority: 180 + raise ValueError("AT-URI must have authority") 181 + 182 + if authority.startswith('did:'): 183 + # Basic DID format check - actual DID validation is done elsewhere 184 + if len(authority) > 2048: 185 + raise ValueError("DID too long") 186 + if ':' not in authority[4:]: 187 + raise ValueError("Invalid DID format") 188 + else: 189 + # Handle validation 190 + if not re.match(r'^[a-z0-9.-]+$', authority): 191 + raise ValueError("Invalid handle characters") 192 + if len(authority) > 253: 193 + raise ValueError("Handle too long") 194 + 195 + # Validate path segments if present 196 + if len(parts) > 1: 197 + if len(parts) > 3: 198 + raise ValueError("AT-URI path too deep") 199 + 200 + collection = parts[1] 201 + if not re.match(r'^[a-zA-Z0-9.-]+$', collection): 202 + raise ValueError("Invalid collection NSID") 203 + 204 + if len(parts) > 2: 205 + rkey = parts[2] 206 + if not rkey: 207 + raise ValueError("Record key cannot be empty") 208 + if not re.match(r'^[a-zA-Z0-9._:%-~]+$', rkey): 209 + raise ValueError("Invalid record key characters") 210 211 @classmethod 212 def _validate_cid(cls, v: str) -> None: ··· 221 """Validate NSID format""" 222 if len(v) > 317: 223 raise ValueError("NSID too long, max 317 chars") 224 + if not re.match(r"^[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(\.[a-zA-Z]([a-zA-Z0-9]{0,62})?)$", v): 225 raise ValueError("NSID contains invalid characters") 226 227 @classmethod ··· 229 """Validate TID format""" 230 if len(v) > 13: 231 raise ValueError("TID too long, max 13 chars") 232 + if not re.match(r"^[234567abcdefghij][234567abcdefghijklmnopqrstuvwxyz]{12}$", v): 233 raise ValueError("TID contains invalid characters") 234 235 @classmethod ··· 237 """Validate record-key format""" 238 if len(v) > 512: 239 raise ValueError("Record key too long, max 512 chars") 240 + if v == "." or v == "..": 241 + raise ValueError(f"Record key is {v}, which is not allowed") 242 if not re.match(r"^[a-zA-Z0-9._:%-~]+$", v): 243 raise ValueError("Record key contains invalid characters") 244