( extension: string | null | undefined, allowedExtensions: readonly string[], paramName = 'file extension' )
| 447 | * ``` |
| 448 | */ |
| 449 | export function validateFileExtension( |
| 450 | extension: string | null | undefined, |
| 451 | allowedExtensions: readonly string[], |
| 452 | paramName = 'file extension' |
| 453 | ): ValidationResult { |
| 454 | if (extension === null || extension === undefined || extension === '') { |
| 455 | return { |
| 456 | isValid: false, |
| 457 | error: `${paramName} is required`, |
| 458 | } |
| 459 | } |
| 460 | |
| 461 | const ext = extension.startsWith('.') ? extension.slice(1) : extension |
| 462 | const normalizedExt = ext.toLowerCase() |
| 463 | |
| 464 | if (!allowedExtensions.map((e) => e.toLowerCase()).includes(normalizedExt)) { |
| 465 | logger.warn('File extension not in allowed list', { |
| 466 | paramName, |
| 467 | extension: ext, |
| 468 | allowedExtensions, |
| 469 | }) |
| 470 | return { |
| 471 | isValid: false, |
| 472 | error: `${paramName} must be one of: ${allowedExtensions.join(', ')}`, |
| 473 | } |
| 474 | } |
| 475 | |
| 476 | return { isValid: true, sanitized: normalizedExt } |
| 477 | } |
| 478 | |
| 479 | /** |
| 480 | * Validates Microsoft Graph API resource IDs |
no test coverage detected