|
1 | | -import { isPathTraversal, isUnsafeFilePath, validateMimeTypeAndExtensionMatch, validateVectorStorePath } from './validator' |
| 1 | +import { isPathTraversal, isUnsafeFilePath, isValidURL, validateMimeTypeAndExtensionMatch, validateVectorStorePath } from './validator' |
2 | 2 | import path from 'path' |
3 | 3 | import { getUserHome } from './utils' |
4 | 4 |
|
@@ -466,3 +466,60 @@ describe('validateVectorStorePath', () => { |
466 | 466 | }) |
467 | 467 | }) |
468 | 468 | }) |
| 469 | + |
| 470 | +describe('isValidURL', () => { |
| 471 | + describe('accepts valid http/https URLs', () => { |
| 472 | + it.each([ |
| 473 | + ['bare http host', 'http://localhost:3000'], |
| 474 | + ['https with path', 'https://flowise.example.com/api'], |
| 475 | + ['http with port and path', 'http://192.168.1.1:3000/api/v1'], |
| 476 | + ['https with query string', 'https://example.com/search?q=hello'] |
| 477 | + ])('should accept %s', (_desc, url) => { |
| 478 | + expect(isValidURL(url)).toBe(true) |
| 479 | + }) |
| 480 | + }) |
| 481 | + |
| 482 | + describe('rejects non-http(s) protocols', () => { |
| 483 | + it.each([ |
| 484 | + ['file protocol', 'file:///etc/passwd'], |
| 485 | + ['javascript protocol', 'javascript:alert(1)'], |
| 486 | + ['ftp protocol', 'ftp://example.com'], |
| 487 | + ['data URI', 'data:text/html,<script>alert(1)</script>'] |
| 488 | + ])('should reject %s', (_desc, url) => { |
| 489 | + expect(isValidURL(url)).toBe(false) |
| 490 | + }) |
| 491 | + }) |
| 492 | + |
| 493 | + describe('rejects URLs with hash fragments (CVE-2022-24785 bypass entry point)', () => { |
| 494 | + it.each([ |
| 495 | + ['plain hash', 'http://localhost:3000/#section'], |
| 496 | + ['hash with injection payload', 'https://evil.com/#";\nrequire("child_process").exec("id");//'], |
| 497 | + ['hash with quote escape', 'http://localhost:3000/#";malicious;//'] |
| 498 | + ])('should reject %s', (_desc, url) => { |
| 499 | + expect(isValidURL(url)).toBe(false) |
| 500 | + }) |
| 501 | + }) |
| 502 | + |
| 503 | + describe('rejects URLs containing JS string-breaking characters', () => { |
| 504 | + it.each([ |
| 505 | + ['double quote', 'http://localhost:3000/path"suffix'], |
| 506 | + ['single quote', "http://localhost:3000/path'suffix"], |
| 507 | + ['backslash', 'http://localhost:3000/path\\suffix'], |
| 508 | + ['newline', 'http://localhost:3000/path\nsuffix'], |
| 509 | + ['carriage return', 'http://localhost:3000/path\rsuffix'], |
| 510 | + ['tab', 'http://localhost:3000/path\tsuffix'] |
| 511 | + ])('should reject URL with %s', (_desc, url) => { |
| 512 | + expect(isValidURL(url)).toBe(false) |
| 513 | + }) |
| 514 | + }) |
| 515 | + |
| 516 | + describe('rejects malformed or empty inputs', () => { |
| 517 | + it.each([ |
| 518 | + ['empty string', ''], |
| 519 | + ['not a URL', 'not-a-url'], |
| 520 | + ['relative path', '/api/v1/prediction/abc'] |
| 521 | + ])('should reject %s', (_desc, url) => { |
| 522 | + expect(isValidURL(url)).toBe(false) |
| 523 | + }) |
| 524 | + }) |
| 525 | +}) |
0 commit comments