url.client.spec.ts 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
  1. import { describe, expect, it } from 'vitest'
  2. import { parseBrowserAddress } from '../src/client/browser/url.ts'
  3. const APP = 'https://dsh.example'
  4. describe('Browser address policy', () => {
  5. it('normalizes host names and HTTPS addresses', () => {
  6. expect(parseBrowserAddress('example.com/path', APP)).toEqual({
  7. ok: true, target: { kind: 'https', url: 'https://example.com/path', title: 'example.com' },
  8. })
  9. expect(parseBrowserAddress('https://docs.example/a?q=1#x', APP)).toEqual({
  10. ok: true, target: { kind: 'https', url: 'https://docs.example/a?q=1#x', title: 'docs.example' },
  11. })
  12. expect(parseBrowserAddress('example.com:8443/path', APP)).toEqual({
  13. ok: true, target: { kind: 'https', url: 'https://example.com:8443/path', title: 'example.com' },
  14. })
  15. })
  16. it('accepts HTTP including loopback hosts', () => {
  17. expect(parseBrowserAddress('http://localhost:5173/app', APP)).toEqual({
  18. ok: true, target: { kind: 'http', url: 'http://localhost:5173/app', title: 'localhost' },
  19. })
  20. expect(parseBrowserAddress('http://127.42.0.9/', APP)).toMatchObject({ ok: true, target: { kind: 'http' } })
  21. expect(parseBrowserAddress('http://[::1]:8080/', APP)).toMatchObject({ ok: true, target: { kind: 'http' } })
  22. expect(parseBrowserAddress('http://example.com/', APP)).toEqual({
  23. ok: true, target: { kind: 'http', url: 'http://example.com/', title: 'example.com' },
  24. })
  25. expect(parseBrowserAddress('http://128.0.0.1/', APP)).toMatchObject({ ok: true, target: { kind: 'http' } })
  26. expect(parseBrowserAddress('http:/example.com/path', APP)).toEqual({
  27. ok: true, target: { kind: 'http', url: 'http://example.com/path', title: 'example.com' },
  28. })
  29. expect(parseBrowserAddress('https:/example.com/path', APP)).toEqual({
  30. ok: true, target: { kind: 'https', url: 'https://example.com/path', title: 'example.com' },
  31. })
  32. })
  33. it('rejects every undeclared or privileged form', () => {
  34. expect(parseBrowserAddress('', APP)).toEqual({ ok: false, reason: 'empty' })
  35. expect(parseBrowserAddress('javascript:alert(1)', APP)).toEqual({ ok: false, reason: 'protocol' })
  36. expect(parseBrowserAddress('https://user:secret@example.com', APP)).toEqual({ ok: false, reason: 'credentials' })
  37. expect(parseBrowserAddress(`${APP}/session`, APP)).toEqual({ ok: false, reason: 'application-origin' })
  38. expect(parseBrowserAddress(`https://${'a'.repeat(17_000)}.example`, APP)).toEqual({ ok: false, reason: 'invalid' })
  39. expect(parseBrowserAddress('file:///work/index.html', APP)).toEqual({ ok: false, reason: 'protocol' })
  40. expect(parseBrowserAddress('file:////server/share/index.html', APP)).toEqual({ ok: false, reason: 'protocol' })
  41. expect(parseBrowserAddress('file:/work/index.html', APP)).toEqual({ ok: false, reason: 'protocol' })
  42. expect(parseBrowserAddress('ftp:/example.com/file', APP)).toEqual({ ok: false, reason: 'protocol' })
  43. expect(parseBrowserAddress(':::', APP)).toEqual({ ok: false, reason: 'invalid' })
  44. expect(parseBrowserAddress('https://example.test', 'not an origin')).toMatchObject({ ok: true })
  45. expect(parseBrowserAddress('https://example.test')).toMatchObject({ ok: true })
  46. expect(parseBrowserAddress('https://example.test', 'null')).toMatchObject({ ok: true })
  47. })
  48. })