browser-session-cookie-store.test.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import { mkdtemp, readFile, rm, stat, writeFile } from 'node:fs/promises'
  2. import { tmpdir } from 'node:os'
  3. import { join } from 'node:path'
  4. import type { Cookie, CookiesSetDetails } from 'electron'
  5. import { afterEach, describe, expect, it } from 'vitest'
  6. import {
  7. BrowserSessionCookieStore,
  8. type BrowserCookies,
  9. } from '../../packages/desktop/src/main/browser/browser-session-cookie-store'
  10. const roots: string[] = []
  11. afterEach(async () => {
  12. await Promise.all(roots.splice(0).map(root => rm(root, { recursive: true, force: true })))
  13. })
  14. describe('desktop browser session cookie store', () => {
  15. it('persists Google session cookies without OS encryption and restores their semantics', async () => {
  16. const root = await mkdtemp(join(tmpdir(), 'hermes-browser-session-cookies-'))
  17. roots.push(root)
  18. const persistentExpiration = Date.now() / 1_000 + 86_400
  19. const sourceCookies: Cookie[] = [{
  20. name: '__Host-session',
  21. value: 'host-session-secret',
  22. domain: 'accounts.google.com',
  23. path: '/',
  24. hostOnly: true,
  25. httpOnly: true,
  26. secure: true,
  27. sameSite: 'lax',
  28. session: true,
  29. }, {
  30. name: 'domain-session',
  31. value: 'domain-session-secret',
  32. domain: '.google.com',
  33. path: '/signin',
  34. hostOnly: false,
  35. httpOnly: true,
  36. secure: true,
  37. sameSite: 'no_restriction',
  38. session: true,
  39. }, {
  40. name: 'persistent',
  41. value: 'normal-cookie-secret',
  42. domain: '.google.com',
  43. path: '/',
  44. secure: true,
  45. sameSite: 'lax',
  46. session: false,
  47. expirationDate: persistentExpiration,
  48. }]
  49. const source: BrowserCookies = {
  50. async get() {
  51. return sourceCookies
  52. },
  53. async set() {},
  54. }
  55. const store = new BrowserSessionCookieStore()
  56. await store.persist(root, source)
  57. const serialized = await readFile(store.filePath(root), 'utf8')
  58. expect(serialized).toContain('host-session-secret')
  59. expect(serialized).toContain('domain-session-secret')
  60. expect(serialized).toContain('normal-cookie-secret')
  61. if (process.platform !== 'win32') expect((await stat(store.filePath(root))).mode & 0o077).toBe(0)
  62. const restored: CookiesSetDetails[] = []
  63. const destination: BrowserCookies = {
  64. async get() {
  65. return []
  66. },
  67. async set(details) {
  68. restored.push(details)
  69. },
  70. }
  71. await expect(store.restore(root, destination)).resolves.toEqual({ restored: 3, failed: 0 })
  72. expect(restored).toEqual([
  73. {
  74. url: 'https://accounts.google.com/',
  75. name: '__Host-session',
  76. value: 'host-session-secret',
  77. path: '/',
  78. secure: true,
  79. httpOnly: true,
  80. sameSite: 'lax',
  81. },
  82. {
  83. url: 'https://google.com/signin',
  84. name: 'domain-session',
  85. value: 'domain-session-secret',
  86. domain: '.google.com',
  87. path: '/signin',
  88. secure: true,
  89. httpOnly: true,
  90. sameSite: 'no_restriction',
  91. },
  92. {
  93. url: 'https://google.com/',
  94. name: 'persistent',
  95. value: 'normal-cookie-secret',
  96. domain: '.google.com',
  97. path: '/',
  98. secure: true,
  99. httpOnly: false,
  100. sameSite: 'lax',
  101. expirationDate: persistentExpiration,
  102. },
  103. ])
  104. expect(restored.slice(0, 2).every(cookie => cookie.expirationDate === undefined)).toBe(true)
  105. })
  106. it('removes stale plaintext and legacy encrypted snapshots after the last cookie is deleted', async () => {
  107. const root = await mkdtemp(join(tmpdir(), 'hermes-browser-session-cookie-clear-'))
  108. roots.push(root)
  109. const store = new BrowserSessionCookieStore()
  110. const cookies: BrowserCookies = {
  111. async get() {
  112. return [{
  113. name: 'session',
  114. value: 'secret',
  115. domain: 'example.com',
  116. sameSite: 'lax',
  117. session: true,
  118. }]
  119. },
  120. async set() {},
  121. }
  122. await store.persist(root, cookies)
  123. await writeFile(join(root, '.hermes-session-cookies.enc'), 'legacy')
  124. cookies.get = async () => []
  125. await store.persist(root, cookies)
  126. await expect(stat(store.filePath(root))).rejects.toMatchObject({ code: 'ENOENT' })
  127. await expect(stat(join(root, '.hermes-session-cookies.enc'))).rejects.toMatchObject({ code: 'ENOENT' })
  128. })
  129. })
粤ICP备19079148号