| 1132 | * ``` |
| 1133 | */ |
| 1134 | export function validatePaginationCursor( |
| 1135 | value: string | null | undefined, |
| 1136 | paramName = 'cursor', |
| 1137 | maxLength = 1024 |
| 1138 | ): ValidationResult { |
| 1139 | if (value === null || value === undefined || value === '') { |
| 1140 | return { |
| 1141 | isValid: false, |
| 1142 | error: `${paramName} is required`, |
| 1143 | } |
| 1144 | } |
| 1145 | |
| 1146 | if (value.length > maxLength) { |
| 1147 | logger.warn('Pagination cursor exceeds maximum length', { |
| 1148 | paramName, |
| 1149 | length: value.length, |
| 1150 | maxLength, |
| 1151 | }) |
| 1152 | return { |
| 1153 | isValid: false, |
| 1154 | error: `${paramName} exceeds maximum length of ${maxLength} characters`, |
| 1155 | } |
| 1156 | } |
| 1157 | |
| 1158 | if (/[\x00-\x1f\x7f]/.test(value) || value.includes('%00')) { |
| 1159 | logger.warn('Pagination cursor contains control characters', { paramName }) |
| 1160 | return { |
| 1161 | isValid: false, |
| 1162 | error: `${paramName} contains invalid characters`, |
| 1163 | } |
| 1164 | } |
| 1165 | |
| 1166 | const cursorPattern = /^[A-Za-z0-9+/=\-_.~%]+$/ |
| 1167 | if (!cursorPattern.test(value)) { |
| 1168 | logger.warn('Pagination cursor contains disallowed characters', { |
| 1169 | paramName, |
| 1170 | value: value.substring(0, 100), |
| 1171 | }) |
| 1172 | return { |
| 1173 | isValid: false, |
| 1174 | error: `${paramName} contains invalid characters`, |
| 1175 | } |
| 1176 | } |
| 1177 | |
| 1178 | return { isValid: true, sanitized: value } |
| 1179 | } |
| 1180 | |
| 1181 | const CALLBACK_URL_SERVER_BASE = 'https://callback-url-validator.invalid' |
| 1182 | |