@faststore/core 4.9.3-dev.1 → 4.9.3-dev.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,9 @@
1
1
 
2
- > @faststore/core@4.9.3-dev.0 generate /home/runner/work/faststore/faststore/packages/core
2
+ > @faststore/core@4.9.3-dev.1 generate /home/runner/work/faststore/faststore/packages/core
3
3
  > pnpm run gen-types && pnpm run cache-graphql
4
4
 
5
5
 
6
- > @faststore/core@4.9.3-dev.0 gen-types /home/runner/work/faststore/faststore/packages/core
6
+ > @faststore/core@4.9.3-dev.1 gen-types /home/runner/work/faststore/faststore/packages/core
7
7
  > node ../cli/bin/run generate-types .
8
8
 
9
9
  [STARTED] Parse Configuration
@@ -19,7 +19,7 @@
19
19
  [COMPLETED] Generate to @generated/
20
20
  [COMPLETED] Generate outputs
21
21
 
22
- > @faststore/core@4.9.3-dev.0 cache-graphql /home/runner/work/faststore/faststore/packages/core
22
+ > @faststore/core@4.9.3-dev.1 cache-graphql /home/runner/work/faststore/faststore/packages/core
23
23
  > node ../cli/bin/run cache-graphql --config=./discovery.config.default.js --queries=./@generated/persisted-documents.json
24
24
 
25
25
  [Info] - Config file location: /home/runner/work/faststore/faststore/packages/core/discovery.config.default.js
@@ -1,5 +1,5 @@
1
1
 
2
- > @faststore/core@4.9.3-dev.0 test /home/runner/work/faststore/faststore/packages/core
2
+ > @faststore/core@4.9.3-dev.1 test /home/runner/work/faststore/faststore/packages/core
3
3
  > vitest run
4
4
 
5
5
 
@@ -7,33 +7,27 @@
7
7
 
8
8
  Not implemented: navigation to another Document
9
9
  Not implemented: navigation to another Document
10
- ✓  browser  test/sdk/ui/useScrollRestoration.browser.test.ts (43 tests) 1198ms
11
10
  Browserslist: browsers data (caniuse-lite) is 8 months old. Please run:
12
11
  npx update-browserslist-db@latest
13
12
  Why you should do it regularly: https://github.com/browserslist/update-db#readme
14
- ✓  browser  test/components/account/ContractSwitcher.browser.test.tsx (22 tests) 4246ms
15
- ✓ shows an error state when the list fails to load  715ms
16
- ✓ requires selecting a contract before Confirm is enabled, then switches (REQ-03)  424ms
17
- ✓ keeps Confirm disabled until a different contract is selected  371ms
18
- ✓ keeps Confirm disabled when switching is unavailable  368ms
19
- ✓ keeps Confirm disabled when the selected contract is filtered out  345ms
20
- ✓ honors CMS-provided labels for the header, current session, search, footer, and available count  363ms
21
- ✓  browser  test/components/account/cards/MyAccountListCards.browser.test.tsx (16 tests) 3043ms
22
- ✓ shows the tab bar and renders Personal cards by default  1270ms
23
- ✓ paginates when there are more than 25 cards  355ms
24
- ✓  browser  test/components/account/orders/BuyingPolicyAlert.browser.test.tsx (13 tests) 3166ms
25
- ✓ opens the reject confirmation modal with the default English copy  1303ms
26
- ✓ opens the reject confirmation modal with CMS-provided labels  418ms
27
- ✓ shows the default English toast when a policy is rejected  314ms
28
- ✓  browser  test/components/CartRecommendationShelf/CartRecommendationShelf.browser.test.tsx (13 tests) 304ms
29
- ✓  browser  test/sdk/localization/useBindingSelector.browser.test.tsx (9 tests) 108ms
30
- ✓  browser  test/components/account/OrganizationDrawer.browser.test.tsx (10 tests) 2015ms
31
- ✓ renders the menu view with contract details and manage footer  994ms
32
- ✓ opens the contract switcher and returns to the menu on back  367ms
33
- ✓ resets to the menu when the drawer closes  329ms
34
- ✓  browser  test/sdk/session/reconcileSessionLocale.browser.test.ts (6 tests) 20ms
35
- ✓  browser  test/components/ui/ProductGallery/GalleryPageHeightLock.browser.test.tsx (8 tests) 99ms
36
- ✓  browser  test/sdk/recommendations/useRecommendationShelf.browser.test.tsx (12 tests) 71ms
13
+ ✓  browser  test/sdk/ui/useScrollRestoration.browser.test.ts (43 tests) 1011ms
14
+ ✓  browser  test/components/account/ContractSwitcher.browser.test.tsx (22 tests) 2790ms
15
+ ✓ shows an error state when the list fails to load  760ms
16
+ ✓ requires selecting a contract before Confirm is enabled, then switches (REQ-03)  308ms
17
+ ✓  browser  test/components/account/cards/MyAccountListCards.browser.test.tsx (16 tests) 3127ms
18
+ ✓ shows the tab bar and renders Personal cards by default  1504ms
19
+ ✓ paginates when there are more than 25 cards  504ms
20
+ ✓  browser  test/components/account/orders/BuyingPolicyAlert.browser.test.tsx (13 tests) 2487ms
21
+ ✓ opens the reject confirmation modal with the default English copy  805ms
22
+ ✓ opens the reject confirmation modal with CMS-provided labels  387ms
23
+ ✓  browser  test/components/CartRecommendationShelf/CartRecommendationShelf.browser.test.tsx (13 tests) 457ms
24
+ ✓  browser  test/sdk/session/reconcileSessionLocale.browser.test.ts (6 tests) 22ms
25
+ ✓  browser  test/components/account/OrganizationDrawer.browser.test.tsx (10 tests) 1931ms
26
+ ✓ renders the menu view with contract details and manage footer  1038ms
27
+ ✓ opens the contract switcher and returns to the menu on back  359ms
28
+ ✓  browser  test/sdk/localization/useBindingSelector.browser.test.tsx (9 tests) 121ms
29
+ ✓  browser  test/components/ui/ProductGallery/GalleryPageHeightLock.browser.test.tsx (8 tests) 83ms
30
+ ✓  browser  test/sdk/recommendations/useRecommendationShelf.browser.test.tsx (12 tests) 92ms
37
31
  stderr | test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx
38
32
  Error in optimistic validation: TypeError: Failed to parse URL from /api/graphql?operationName=ValidateCartMutation&operationHash=3c71b0d3fc2bade14df68abfa271858beb361b12
39
33
   at node:internal/deps/undici/undici:16416:13
@@ -63,11 +57,10 @@ Browserslist: browsers data (caniuse-lite) is 8 months old. Please run:
63
57
  stderr | test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx > RecommendationShelf > renders nothing when there is an error
64
58
  Error fetching recommendations undefined boom for campaign vrn:recommendations:acc:rec-top-items-v2:campaign-1
65
59
 
66
- ✓  browser  test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx (8 tests) 165ms
67
- ✓  browser  test/components/CartSidebar/CartSidebar.browser.test.tsx (5 tests) 75ms
68
- ✓  browser  test/components/sections/Account/AccountOrderDetails.browser.test.tsx (4 tests) 93ms
69
- ✓  browser  test/sdk/session/getInitialSession.browser.test.ts (7 tests) 11ms
70
- ✓  browser  test/components/navigation/Navbar.browser.test.tsx (3 tests) 92ms
60
+ ✓  browser  test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx (8 tests) 119ms
61
+ ✓  browser  test/sdk/session/getInitialSession.browser.test.ts (7 tests) 9ms
62
+ ✓  browser  test/components/CartSidebar/CartSidebar.browser.test.tsx (5 tests) 83ms
63
+ ✓  browser  test/components/sections/Account/AccountOrderDetails.browser.test.tsx (4 tests) 69ms
71
64
  stderr | test/pages/password-protection.browser.test.tsx > PasswordProtectionLogin > posts the password and redirects to the requested return path
72
65
  Warning: An update to PasswordProtectionLogin inside a test was not wrapped in act(...).
73
66
 
@@ -153,6 +146,8 @@ act(() => {
153
146
  This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
154
147
  at PasswordProtectionLogin (/home/runner/work/faststore/faststore/packages/core/src/pages/password-protection/index.tsx:19:69)
155
148
 
149
+ ✓  browser  test/components/navigation/Navbar.browser.test.tsx (3 tests) 100ms
150
+ ✓  browser  test/components/account/OrganizationDrawerFooter.browser.test.tsx (8 tests) 508ms
156
151
  stderr | test/pages/password-protection.browser.test.tsx > PasswordProtectionLogin > uses the fallback invalid-password message when API omits an error
157
152
  Warning: An update to PasswordProtectionLogin inside a test was not wrapped in act(...).
158
153
 
@@ -249,7 +244,6 @@ act(() => {
249
244
  This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
250
245
  at PasswordProtectionLogin (/home/runner/work/faststore/faststore/packages/core/src/pages/password-protection/index.tsx:19:69)
251
246
 
252
- ✓  browser  test/components/account/OrganizationDrawerFooter.browser.test.tsx (8 tests) 545ms
253
247
  stderr | test/pages/password-protection.browser.test.tsx > PasswordProtectionLogin > shows service unavailable when the login request fails
254
248
  Warning: An update to PasswordProtectionLogin inside a test was not wrapped in act(...).
255
249
 
@@ -298,8 +292,8 @@ act(() => {
298
292
  This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
299
293
  at PasswordProtectionLogin (/home/runner/work/faststore/faststore/packages/core/src/pages/password-protection/index.tsx:19:69)
300
294
 
301
- ✓  browser  test/pages/password-protection.browser.test.tsx (6 tests) 997ms
302
- ✓ renders the password protection form  459ms
295
+ ✓  browser  test/pages/password-protection.browser.test.tsx (6 tests) 983ms
296
+ ✓ renders the password protection form  410ms
303
297
  stderr | test/components/cms/RenderSections.browser.test.tsx
304
298
  Error in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
305
299
  at waitForSessionValidated (/home/runner/work/faststore/faststore/packages/core/src/sdk/cart/waitForSessionValidated.ts:16:32)
@@ -308,9 +302,9 @@ This ensures that you're testing the behavior the user would see in the browser.
308
302
  at a (/home/runner/work/faststore/faststore/packages/sdk/dist/es/index.mjs:357:23)
309
303
   at processTicksAndRejections (node:internal/process/task_queues:103:5)
310
304
 
311
- ✓  browser  test/sdk/account/useSwitchContract.browser.test.ts (5 tests) 54ms
312
- ✓  browser  test/components/navigation/NavbarSlider.browser.test.tsx (2 tests) 45ms
313
- ✓  browser  test/components/cms/RenderSections.browser.test.tsx (5 tests) 102ms
305
+ ✓  browser  test/sdk/account/useSwitchContract.browser.test.ts (5 tests) 56ms
306
+ ✓  browser  test/components/navigation/NavbarSlider.browser.test.tsx (2 tests) 42ms
307
+ ✓  browser  test/components/cms/RenderSections.browser.test.tsx (5 tests) 92ms
314
308
  stderr | test/sdk/localization/store-url.browser.test.ts
315
309
  Error in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
316
310
  at waitForSessionValidated (/home/runner/work/faststore/faststore/packages/core/src/sdk/cart/waitForSessionValidated.ts:16:32)
@@ -320,14 +314,14 @@ This ensures that you're testing the behavior the user would see in the browser.
320
314
   at processTicksAndRejections (node:internal/process/task_queues:103:5)
321
315
 
322
316
  ✓  browser  test/sdk/localization/store-url.browser.test.ts (3 tests) 6ms
323
- ✓  browser  test/components/account/Menu.browser.test.tsx (6 tests) 403ms
324
- ✓  browser  test/sdk/account/useAvailableContracts.browser.test.ts (6 tests) 28ms
325
- ✓  browser  test/components/templates/SearchPage/SearchWrapper.browser.test.tsx (3 tests) 42ms
326
- ✓  browser  test/sdk/recommendations/useRecommendationShelf.sessionFallback.browser.test.tsx (2 tests) 139ms
327
- ✓  browser  test/components/sections/Account/AccountListQuotes.browser.test.tsx (4 tests) 104ms
328
- ✓  browser  test/components/navigation/NavbarAccountArea.browser.test.tsx (4 tests) 72ms
329
- ✓  browser  test/components/CartRecommendationShelf/CartRecommendationProductCard.browser.test.tsx (3 tests) 49ms
330
- ✓  browser  test/sdk/account/changeContractToken.browser.test.ts (9 tests) 20ms
317
+ ✓  browser  test/components/account/Menu.browser.test.tsx (6 tests) 385ms
318
+ ✓  browser  test/sdk/account/useAvailableContracts.browser.test.ts (6 tests) 42ms
319
+ ✓  browser  test/components/templates/SearchPage/SearchWrapper.browser.test.tsx (3 tests) 35ms
320
+ ✓  browser  test/sdk/recommendations/useRecommendationShelf.sessionFallback.browser.test.tsx (2 tests) 128ms
321
+ ✓  browser  test/components/sections/Account/AccountListQuotes.browser.test.tsx (4 tests) 135ms
322
+ ✓  browser  test/components/navigation/NavbarAccountArea.browser.test.tsx (4 tests) 78ms
323
+ ✓  browser  test/sdk/account/changeContractToken.browser.test.ts (9 tests) 22ms
324
+ ✓  browser  test/components/CartRecommendationShelf/CartRecommendationProductCard.browser.test.tsx (3 tests) 54ms
331
325
  stderr | test/components/account/doLogout.browser.test.ts
332
326
  Error in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
333
327
  at waitForSessionValidated (/home/runner/work/faststore/faststore/packages/core/src/sdk/cart/waitForSessionValidated.ts:16:32)
@@ -336,9 +330,9 @@ This ensures that you're testing the behavior the user would see in the browser.
336
330
  at a (/home/runner/work/faststore/faststore/packages/sdk/dist/es/index.mjs:357:23)
337
331
   at processTicksAndRejections (node:internal/process/task_queues:103:5)
338
332
 
339
- ✓  browser  test/components/account/doLogout.browser.test.ts (1 test) 35ms
340
- ✓  browser  test/components/ui/LocalizationButton.browser.test.tsx (3 tests) 36ms
341
- ✓  browser  test/components/account/OrganizationDrawerHeader.browser.test.tsx (5 tests) 383ms
333
+ ✓  browser  test/components/account/doLogout.browser.test.ts (1 test) 39ms
334
+ ✓  browser  test/components/ui/LocalizationButton.browser.test.tsx (3 tests) 48ms
335
+ ✓  browser  test/components/account/OrganizationDrawerHeader.browser.test.tsx (5 tests) 359ms
342
336
  stderr | test/sdk/account/clearPersistedSessionState.browser.test.ts
343
337
  Error in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
344
338
  at waitForSessionValidated (/home/runner/work/faststore/faststore/packages/core/src/sdk/cart/waitForSessionValidated.ts:16:32)
@@ -347,14 +341,14 @@ This ensures that you're testing the behavior the user would see in the browser.
347
341
  at a (/home/runner/work/faststore/faststore/packages/sdk/dist/es/index.mjs:357:23)
348
342
   at processTicksAndRejections (node:internal/process/task_queues:103:5)
349
343
 
350
- ✓  browser  test/sdk/account/clearPersistedSessionState.browser.test.ts (4 tests) 14ms
351
- ✓  browser  test/components/sections/Account/AccountOrderDelivery.browser.test.tsx (2 tests) 103ms
352
- ✓  browser  test/components/account/orders/DeliveryOptionAccordion.browser.test.tsx (2 tests) 106ms
353
- ✓  browser  test/components/sections/Account/AccountOrderStatus.browser.test.tsx (3 tests) 127ms
354
- ✓  browser  test/components/region/useRegion.browser.test.ts (1 test) 18ms
355
- ✓  browser  test/components/sections/Account/AccountListCards.browser.test.tsx (3 tests) 94ms
356
- ✓  browser  test/components/sections/Account/AccountOrdersList.browser.test.tsx (2 tests) 31ms
357
- ✓  browser  test/components/account/orders/SummaryCard.browser.test.tsx (2 tests) 107ms
344
+ ✓  browser  test/sdk/account/clearPersistedSessionState.browser.test.ts (4 tests) 19ms
345
+ ✓  browser  test/components/sections/Account/AccountOrderDelivery.browser.test.tsx (2 tests) 102ms
346
+ ✓  browser  test/components/account/orders/DeliveryOptionAccordion.browser.test.tsx (2 tests) 104ms
347
+ ✓  browser  test/components/sections/Account/AccountOrderStatus.browser.test.tsx (3 tests) 144ms
348
+ ✓  browser  test/components/region/useRegion.browser.test.ts (1 test) 17ms
349
+ ✓  browser  test/components/sections/Account/AccountListCards.browser.test.tsx (3 tests) 92ms
350
+ ✓  browser  test/components/sections/Account/AccountOrdersList.browser.test.tsx (2 tests) 30ms
351
+ ✓  browser  test/components/account/orders/SummaryCard.browser.test.tsx (2 tests) 105ms
358
352
  stderr | test/sdk/session/sessionStore.browser.test.ts
359
353
  Error in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
360
354
  at waitForSessionValidated (/home/runner/work/faststore/faststore/packages/core/src/sdk/cart/waitForSessionValidated.ts:16:32)
@@ -363,9 +357,9 @@ This ensures that you're testing the behavior the user would see in the browser.
363
357
  at a (/home/runner/work/faststore/faststore/packages/sdk/dist/es/index.mjs:357:23)
364
358
   at processTicksAndRejections (node:internal/process/task_queues:103:5)
365
359
 
366
- ✓  browser  test/components/account/OrganizationSignInButton.browser.test.tsx (2 tests) 314ms
367
- ✓  browser  test/sdk/session/sessionStore.browser.test.ts (2 tests) 6ms
368
- ✓  browser  test/components/account/orders/DeliveryOptionAccordionDeliveryInfo.browser.test.tsx (1 test) 44ms
360
+ ✓  browser  test/sdk/session/sessionStore.browser.test.ts (2 tests) 8ms
361
+ ✓  browser  test/components/account/OrganizationSignInButton.browser.test.tsx (2 tests) 270ms
362
+ ✓  browser  test/components/account/orders/DeliveryOptionAccordionDeliveryInfo.browser.test.tsx (1 test) 31ms
369
363
  stderr | test/sdk/session/validateSession.browser.test.ts
370
364
  Error in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
371
365
  at waitForSessionValidated (/home/runner/work/faststore/faststore/packages/core/src/sdk/cart/waitForSessionValidated.ts:16:32)
@@ -375,13 +369,13 @@ This ensures that you're testing the behavior the user would see in the browser.
375
369
   at processTicksAndRejections (node:internal/process/task_queues:103:5)
376
370
 
377
371
  ✓  browser  test/sdk/session/validateSession.browser.test.ts (1 test) 7ms
378
- ✓  browser  test/components/account/orders/StatusCard.browser.test.tsx (2 tests) 123ms
379
- ✓  browser  test/utils/isLocalHost.browser.test.ts (3 tests) 5ms
380
- ✓  browser  test/sdk/session/reconcileSessionLocale.disabled.browser.test.ts (1 test) 4ms
381
- ✓  browser  test/components/account/orders/DeliveryCard.browser.test.tsx (1 test) 55ms
382
- ✓  browser  test/components/account/orders/DeliveryOptionAccordionProducts.browser.test.tsx (1 test) 56ms
383
- ✓  browser  test/components/skeletons/ProductGridSkeleton.browser.test.tsx (2 tests) 30ms
384
- ✓  node  test/utils/localization/bindingPaths.test.ts (71 tests) 29ms
372
+ ✓  browser  test/components/account/orders/StatusCard.browser.test.tsx (2 tests) 107ms
373
+ ✓  browser  test/utils/isLocalHost.browser.test.ts (3 tests) 4ms
374
+ ✓  browser  test/sdk/session/reconcileSessionLocale.disabled.browser.test.ts (1 test) 3ms
375
+ ✓  browser  test/components/account/orders/DeliveryCard.browser.test.tsx (1 test) 63ms
376
+ ✓  browser  test/components/account/orders/DeliveryOptionAccordionProducts.browser.test.tsx (1 test) 51ms
377
+ ✓  browser  test/components/skeletons/ProductGridSkeleton.browser.test.tsx (2 tests) 45ms
378
+ ✓  node  test/utils/localization/bindingPaths.test.ts (71 tests) 31ms
385
379
  stderr | test/sdk/account/refreshToken.browser.test.ts
386
380
  Error in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
387
381
  at waitForSessionValidated (/home/runner/work/faststore/faststore/packages/core/src/sdk/cart/waitForSessionValidated.ts:16:32)
@@ -390,13 +384,14 @@ This ensures that you're testing the behavior the user would see in the browser.
390
384
  at a (/home/runner/work/faststore/faststore/packages/sdk/dist/es/index.mjs:357:23)
391
385
   at processTicksAndRejections (node:internal/process/task_queues:103:5)
392
386
 
393
- ✓  browser  test/sdk/account/refreshToken.browser.test.ts (1 test) 8ms
394
- ✓  node  test/server/password-protection-service.test.ts (22 tests) 41ms
395
- ✓  node  test/pages/api/switch-contract.test.ts (25 tests) 89ms
396
- ✓  browser  test/components/account/orders/FilterFacetPendingApproval.browser.test.tsx (1 test) 223ms
397
- ✓  node  test/utils/match-url.test.ts (10 tests) 8ms
387
+ ✓  browser  test/sdk/account/refreshToken.browser.test.ts (1 test) 6ms
388
+ ✓  node  test/server/password-protection-service.test.ts (22 tests) 37ms
389
+ ✓  node  test/pages/api/switch-contract.test.ts (25 tests) 92ms
390
+ ✓  browser  test/components/account/orders/FilterFacetPendingApproval.browser.test.tsx (1 test) 230ms
391
+ ✓  node  test/utils/match-url.test.ts (10 tests) 9ms
398
392
  ✓  node  test/sdk/localization/useBindingSelector.test.tsx (22 tests) 15ms
399
- ✓  node  test/pages/api/unlock.test.ts (15 tests) 209ms
393
+ ✓  node  test/pages/api/unlock.test.ts (15 tests) 141ms
394
+ ✓  node  test/sdk/session/syncSalesChannelFromOrderForm.test.ts (14 tests) 12ms
400
395
  stderr | test/components/search/SearchInputComponent.test.tsx
401
396
  Warning: forwardRef render functions accept exactly two parameters: props and ref. Did you forget to use the ref parameter?
402
397
 
@@ -460,25 +455,24 @@ Warning: React does not recognize the `providerProps` prop on a DOM element. If
460
455
  at Suspense
461
456
  at Wrapper (/home/runner/work/faststore/faststore/packages/core/test/components/search/SearchInputComponent.test.tsx:106:20)
462
457
 
463
- ✓  node  test/sdk/session/syncSalesChannelFromOrderForm.test.ts (14 tests) 15ms
464
- ✓  node  test/components/search/SearchInputComponent.test.tsx (6 tests) 72ms
465
- ✓  node  test/sdk/localization/bindingSelector.test.ts (18 tests) 9ms
466
- ✓  node  test/components/quotes/MyAccountListQuotesTable.test.tsx (15 tests) 500ms
467
- ✓  node  test/sdk/search/useSearchHistory.test.ts (13 tests) 50ms
458
+ ✓  node  test/sdk/localization/bindingSelector.test.ts (18 tests) 12ms
459
+ ✓  node  test/components/search/SearchInputComponent.test.tsx (6 tests) 94ms
460
+ ✓  node  test/components/quotes/MyAccountListQuotesTable.test.tsx (15 tests) 474ms
461
+ ✓  node  test/sdk/search/useSearchHistory.test.ts (13 tests) 48ms
468
462
  stderr | test/pages/pvt/account/cards/index.test.ts > Cards page getServerSideProps > redirects to /pvt/account/403 when the query fails with 401/403
469
463
  { extensions: { status: 401 } }
470
464
 
471
465
  stderr | test/pages/pvt/account/cards/index.test.ts > Cards page getServerSideProps > renders the error state (no redirect) when the query fails with a non-auth status
472
466
  { extensions: { status: 502 } }
473
467
 
474
- ✓  node  test/pages/pvt/account/cards/index.test.ts (8 tests) 8ms
475
- ✓  node  test/components/quotes/MyAccountListQuotes.test.tsx (11 tests) 854ms
476
- ✓ navigates with the search term on blur  360ms
468
+ ✓  node  test/pages/pvt/account/cards/index.test.ts (8 tests) 11ms
469
+ ✓  node  test/components/quotes/MyAccountListQuotes.test.tsx (11 tests) 832ms
470
+ ✓ navigates with the search term on blur  358ms
477
471
  ✓ navigates without a label param when the search is cleared  360ms
478
- ✓  node  test/utils/cookieCacheBusting.test.ts (10 tests) 15ms
479
- ✓  node  test/components/quotes/quoteFilters.test.ts (26 tests) 34ms
480
- ✓  node  test/utils/productJsonLd.test.ts (36 tests) 16ms
481
- ✓  node  test/experimental/myAccountCmsServerSideProps.test.ts (5 tests) 11ms
472
+ ✓  node  test/components/quotes/quoteFilters.test.ts (26 tests) 29ms
473
+ ✓  node  test/utils/productJsonLd.test.ts (36 tests) 14ms
474
+ ✓  node  test/utils/cookieCacheBusting.test.ts (10 tests) 17ms
475
+ ✓  node  test/experimental/myAccountCmsServerSideProps.test.ts (5 tests) 22ms
482
476
  stderr | test/pages/api/graphql.test.ts > /api/graphql error status propagation > propagates the upstream 400 from a wrapped BadRequestError instead of 500
483
477
  Graphql execution returned with error: [
484
478
  {
@@ -588,74 +582,73 @@ Warning: React does not recognize the `providerProps` prop on a DOM element. If
588
582
  at trace (file:///home/runner/work/faststore/faststore/node_modules/.pnpm/vitest@4.0.18_@opentelemetry+api@1.9.0_@types+node@20.19.13_jiti@2.6.1_jsdom@27.4.0_sas_13be941c57d55f08f06eeaf1c180ddfe/node_modules/vitest/dist/chunks/test.B8ej_ZHS.js:239:21)
589
583
  at runTest (file:///home/runner/work/faststore/faststore/node_modules/.pnpm/@vitest+runner@4.0.18/node_modules/@vitest/runner/dist/index.js:1653:12)
590
584
 
591
- ✓  node  test/pages/api/graphql.test.ts (19 tests) 35ms
592
- ✓  node  test/utils/clearCookies.test.ts (20 tests) 23ms
593
- ✓  node  test/utils/multipleTemplates.test.ts (8 tests) 8ms
585
+ ✓  node  test/pages/api/graphql.test.ts (19 tests) 31ms
586
+ ✓  node  test/utils/clearCookies.test.ts (20 tests) 28ms
587
+ ✓  node  test/utils/multipleTemplates.test.ts (8 tests) 5ms
594
588
  stderr | test/sdk/account/useSetPassword.test.ts > useSetPassword > maps wrongcredentials error responses to the existing message
595
589
  Set password request failed: Bad Request
596
590
 
597
- ✓  node  test/sdk/account/useSetPassword.test.ts (6 tests) 47ms
598
- ✓  node  test/sdk/orderEntry/useOrderEntryOperation.test.ts (8 tests) 34ms
599
- ✓  node  test/utils/localization/localizeRedirectDestination.test.ts (11 tests) 6ms
600
- ✓  node  test/components/quotes/MyAccountQuotesFilterSlider.test.tsx (8 tests) 170ms
601
- ✓  node  test/components/quotes/MyAccountQuotesStatusSelector.test.tsx (14 tests) 197ms
591
+ ✓  node  test/sdk/account/useSetPassword.test.ts (6 tests) 31ms
592
+ ✓  node  test/sdk/orderEntry/useOrderEntryOperation.test.ts (8 tests) 43ms
593
+ ✓  node  test/utils/localization/localizeRedirectDestination.test.ts (11 tests) 7ms
594
+ ✓  node  test/components/quotes/MyAccountQuotesFilterSlider.test.tsx (8 tests) 226ms
595
+ ✓  node  test/components/quotes/MyAccountQuotesStatusSelector.test.tsx (14 tests) 190ms
602
596
  ✓  node  test/utils/localization/hreflang.test.ts (8 tests) 7ms
603
- ✓  node  test/sdk/account/getMyAccountRoutes.test.ts (13 tests) 11ms
604
- ✓  node  test/components/quotes/MyAccountQuotesSelectedTags.test.tsx (10 tests) 155ms
605
- ✓  node  test/sdk/redirects/index.test.ts (7 tests) 45ms
606
- ✓  node  test/sdk/orderEntry/useOrderEntryUpload.test.ts (8 tests) 62ms
607
- ✓  node  test/proxy.localization.test.ts (5 tests) 76ms
608
- ✓  node  test/sdk/orderEntry/useOrderEntry.test.ts (8 tests) 50ms
609
- ✓  node  test/components/account/orders/getDeliveryOptionLabel.test.ts (21 tests) 9ms
610
- ✓  node  test/sdk/graphql/retryPolicy.test.ts (18 tests) 21ms
611
- ✓  node  test/server/cms/myAccountDefaultSections.test.ts (9 tests) 7ms
612
- ✓  node  test/utils/productJsonLdRendering.test.tsx (5 tests) 15ms
613
- ✓  node  test/sdk/analytics/useStartRecommendationSession.test.ts (6 tests) 33ms
614
- ✓  node  test/sdk/orderEntry/useOrderFormItems.test.ts (7 tests) 26ms
615
- ✓  node  test/proxy.test.ts (4 tests) 18ms
616
- ✓  node  test/server/webops-api.test.ts (9 tests) 12ms
617
- ✓  node  test/components/account/orders/StatusCard.test.ts (3 tests) 19ms
597
+ ✓  node  test/sdk/account/getMyAccountRoutes.test.ts (13 tests) 8ms
598
+ ✓  node  test/components/quotes/MyAccountQuotesSelectedTags.test.tsx (10 tests) 124ms
599
+ ✓  node  test/sdk/redirects/index.test.ts (7 tests) 51ms
600
+ ✓  node  test/sdk/orderEntry/useOrderEntryUpload.test.ts (8 tests) 58ms
601
+ ✓  node  test/proxy.localization.test.ts (5 tests) 35ms
602
+ ✓  node  test/sdk/orderEntry/useOrderEntry.test.ts (8 tests) 49ms
603
+ ✓  node  test/components/account/orders/getDeliveryOptionLabel.test.ts (21 tests) 7ms
604
+ ✓  node  test/sdk/graphql/retryPolicy.test.ts (18 tests) 23ms
605
+ ✓  node  test/server/cms/myAccountDefaultSections.test.ts (9 tests) 8ms
606
+ ✓  node  test/utils/productJsonLdRendering.test.tsx (5 tests) 12ms
607
+ ✓  node  test/utils/trustedForwardedHost.test.ts (27 tests) 8ms
608
+ ✓  node  test/server/index.test.ts (7 tests) 746ms
609
+ ✓  node  test/proxy.test.ts (4 tests) 23ms
610
+ ✓  node  test/server/webops-api.test.ts (9 tests) 14ms
618
611
  ✓  node  test/sdk/localization/store-url.test.ts (4 tests) 5ms
619
- ✓  node  test/sdk/cart/waitForSessionValidated.test.ts (5 tests) 12ms
620
- ✓  node  test/server/content/service.test.ts (5 tests) 5ms
621
- ✓  node  test/sdk/account/refreshToken.test.ts (5 tests) 6ms
622
- ✓  node  test/server/cms/global.test.ts (3 tests) 4ms
623
- ✓  node  test/sdk/auth/useAuth.test.ts (5 tests) 30ms
624
- ✓  node  test/server/rootSpan.test.ts (4 tests) 41ms
612
+ ✓  node  test/sdk/analytics/useStartRecommendationSession.test.ts (6 tests) 77ms
613
+ ✓  node  test/components/account/orders/StatusCard.test.ts (3 tests) 32ms
614
+ ✓  node  test/sdk/orderEntry/useOrderFormItems.test.ts (7 tests) 41ms
615
+ ✓  node  test/sdk/cart/waitForSessionValidated.test.ts (5 tests) 13ms
616
+ ✓  node  test/sdk/account/refreshToken.test.ts (5 tests) 12ms
617
+ ✓  node  test/server/content/service.test.ts (5 tests) 4ms
618
+ ✓  node  test/server/cms/global.test.ts (3 tests) 6ms
619
+ ✓  node  test/sdk/auth/useAuth.test.ts (5 tests) 44ms
625
620
  ✓  node  test/utils/getRequestHostname.test.ts (12 tests) 5ms
626
- ✓  node  test/pages/api/preview.test.ts (2 tests) 7ms
627
- ✓  node  test/components/account/Layout.test.tsx (6 tests) 49ms
628
- ✓  node  test/components/search/SearchInput.test.ts (6 tests) 7ms
629
- ✓  node  test/utils/validateSessionRefreshToken.test.ts (6 tests) 4ms
630
- ✓  node  test/server/index.test.ts (7 tests) 5149ms
631
- ✓ should return a valid merged GraphQL schema  302ms
632
- ✓ should handle options and execute  4558ms
621
+ ✓  node  test/components/account/Layout.test.tsx (6 tests) 77ms
622
+ ✓  node  test/server/rootSpan.test.ts (4 tests) 48ms
623
+ ✓  node  test/pages/api/preview.test.ts (2 tests) 9ms
624
+ ✓  node  test/components/search/SearchInput.test.ts (6 tests) 13ms
625
+ ✓  node  test/utils/validateSessionRefreshToken.test.ts (6 tests) 6ms
633
626
  ✓  node  test/sdk/recommendations/vrn.test.ts (5 tests) 6ms
634
- ✓  node  test/components/region/useRegion.test.ts (1 test) 29ms
635
- ✓  node  test/utils/retry.test.ts (5 tests) 16ms
636
- ✓  node  test/server/cms/index.test.ts (2 tests) 6ms
637
- ✓  node  test/sdk/account/getB2BSessionClaims.test.ts (4 tests) 4ms
638
- ✓  node  test/utils/userOrderStatus.test.ts (4 tests) 6ms
639
- ✓  node  test/sdk/recommendations/useRecommendationUserId.test.ts (4 tests) 187ms
627
+ ✓  node  test/components/region/useRegion.test.ts (1 test) 24ms
628
+ ✓  node  test/server/cms/index.test.ts (2 tests) 4ms
629
+ ✓  node  test/utils/retry.test.ts (5 tests) 17ms
630
+ ✓  node  test/sdk/account/getB2BSessionClaims.test.ts (4 tests) 7ms
631
+ ✓  node  test/sdk/recommendations/useRecommendationUserId.test.ts (4 tests) 194ms
632
+ ✓  node  test/utils/userOrderStatus.test.ts (4 tests) 5ms
640
633
  ✓  node  test/utils/unlockResponse.test.ts (11 tests) 5ms
641
634
  ✓  node  test/sdk/recommendations/useRecommendations.test.ts (2 tests) 6ms
642
- ✓  node  test/components/account/OrganizationDrawerBody.test.tsx (3 tests) 49ms
643
- ✓  node  test/utils/isLocalHost.test.ts (7 tests) 4ms
635
+ ✓  node  test/components/account/OrganizationDrawerBody.test.tsx (3 tests) 43ms
636
+ ✓  node  test/utils/isLocalHost.test.ts (7 tests) 5ms
644
637
  ✓  node  test/sdk/session/toSessionInput.test.ts (3 tests) 5ms
645
- ✓  node  test/components/quotes/MyAccountQuoteStatusBadge.test.tsx (4 tests) 37ms
646
- ✓  node  test/components/account/orders/getOrderTotalLabel.test.ts (6 tests) 5ms
638
+ ✓  node  test/components/quotes/MyAccountQuoteStatusBadge.test.tsx (4 tests) 49ms
639
+ ✓  node  test/components/account/orders/getOrderTotalLabel.test.ts (6 tests) 4ms
647
640
  ✓  node  test/components/account/orders/listOrdersLabels.test.ts (2 tests) 3ms
648
641
  ✓  node  test/utils/getCookie.test.ts (4 tests) 5ms
649
- ✓  node  test/sdk/session/isSignInAreaResolved.test.ts (3 tests) 5ms
650
- ✓  node  test/components/auth/ProfileChallenge.test.tsx (3 tests) 34ms
651
- ✓  node  test/utils/filterChannel.test.ts (2 tests) 5ms
652
- ✓  node  test/sdk/analytics/utils.test.ts (2 tests) 5ms
653
- ✓  node  test/instrumentation.test.ts (3 tests) 7ms
642
+ ✓  node  test/sdk/session/isSignInAreaResolved.test.ts (3 tests) 4ms
643
+ ✓  node  test/utils/filterChannel.test.ts (2 tests) 4ms
644
+ ✓  node  test/components/auth/ProfileChallenge.test.tsx (3 tests) 43ms
645
+ ✓  node  test/instrumentation.test.ts (3 tests) 5ms
646
+ ✓  node  test/sdk/analytics/utils.test.ts (2 tests) 3ms
654
647
  ✓  node  test/components/account/orders/orderDetailsLabels.test.ts (1 test) 4ms
655
- ✓  node  test/sdk/account/clearPersistedSessionState.test.ts (1 test) 10ms
648
+ ✓  node  test/sdk/account/clearPersistedSessionState.test.ts (1 test) 8ms
656
649
 
657
-  Test Files  129 passed (129)
658
-  Tests  1005 passed (1005)
659
-  Start at  19:28:12
660
-  Duration  64.24s (transform 13.31s, setup 0ms, import 54.17s, tests 28.07s, environment 78.66s)
650
+  Test Files  130 passed (130)
651
+  Tests  1032 passed (1032)
652
+  Start at  00:35:08
653
+  Duration  62.92s (transform 13.32s, setup 0ms, import 56.50s, tests 21.42s, environment 78.13s)
661
654
 
package/CHANGELOG.md CHANGED
@@ -3,6 +3,18 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [4.9.3-dev.2](https://github.com/vtex/faststore/compare/v4.9.3-dev.1...v4.9.3-dev.2) (2026-10-07)
7
+
8
+ ### Bug Fixes
9
+
10
+ - address security & functional concerns from dk review ([d0b6106](https://github.com/vtex/faststore/commit/d0b610688ec551d8105c74750927be4ab1af7224))
11
+ - **core:** harden x-forwarded-host handling and drop orderNumber fallback ([c1faa35](https://github.com/vtex/faststore/commit/c1faa35fe070ff8b51d4f07a5f1f33da4b17d836)), closes [#3178](https://github.com/vtex/faststore/issues/3178)
12
+ - **core:** ignore multi-value x-forwarded-host and cover port and case ([b81d5d9](https://github.com/vtex/faststore/commit/b81d5d91721f9f8205251ad39e607b094386083c))
13
+ - **core:** log ignored x-forwarded-host and cover CR/LF hostnames ([adaa816](https://github.com/vtex/faststore/commit/adaa81608b7e72759272e1d6d23ca6257b9a4d59))
14
+ - **core:** make preview cookies host-only instead of rewriting their domain ([8e4901e](https://github.com/vtex/faststore/commit/8e4901ec7d127336bc27c0ae8e52c9cadea1a5a3))
15
+ - **core:** reject hostnames that would inject Set-Cookie attributes ([059a437](https://github.com/vtex/faststore/commit/059a4377c3b1b8f66aeed2b73ce130e1ab827f77))
16
+ - resolve ValidateCartMutation infinite loop in preview environments (SFS-3034) ([1cc9a5e](https://github.com/vtex/faststore/commit/1cc9a5efb1617f3d032623c4ee5f687ad5e2ccd9)), closes [#3178](https://github.com/vtex/faststore/issues/3178) [#3178](https://github.com/vtex/faststore/issues/3178) [#3190](https://github.com/vtex/faststore/issues/3190) [#3188](https://github.com/vtex/faststore/issues/3188) [#1467584](https://github.com/vtex/faststore/issues/1467584)
17
+
6
18
  ## [4.9.3-dev.1](https://github.com/vtex/faststore/compare/v4.9.3-dev.0...v4.9.3-dev.1) (2026-10-06)
7
19
 
8
20
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faststore/core",
3
- "version": "4.9.3-dev.1",
3
+ "version": "4.9.3-dev.2",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -71,11 +71,11 @@
71
71
  "style-loader": "^3.3.1",
72
72
  "swr": "^2.2.5",
73
73
  "use-sync-external-store": "^1.6.0",
74
- "@faststore/api": "4.9.3-dev.1",
75
- "@faststore/diagnostics": "4.9.3-dev.1",
76
- "@faststore/lighthouse": "4.9.3-dev.1",
77
- "@faststore/sdk": "4.9.3-dev.1",
78
- "@faststore/ui": "4.9.3-dev.1"
74
+ "@faststore/api": "4.9.3-dev.2",
75
+ "@faststore/diagnostics": "4.9.3-dev.2",
76
+ "@faststore/lighthouse": "4.9.3-dev.2",
77
+ "@faststore/sdk": "4.9.3-dev.2",
78
+ "@faststore/ui": "4.9.3-dev.2"
79
79
  },
80
80
  "devDependencies": {
81
81
  "@cypress/code-coverage": "^3.12.1",
@@ -11,6 +11,11 @@ import discoveryConfig from 'discovery.config'
11
11
  import { getJWTAutCookie } from 'src/utils/getCookie'
12
12
  import { getRequestHostname } from 'src/utils/getRequestHostname'
13
13
  import { isLocalHost } from 'src/utils/isLocalHost'
14
+ import {
15
+ isHostAllowed,
16
+ removeCookieDomain,
17
+ singleForwardedHost,
18
+ } from 'src/utils/trustedForwardedHost'
14
19
  import { shouldForceRefreshTokenForValidateSession } from 'src/utils/validateSessionRefreshToken'
15
20
  import { execute } from '../../server'
16
21
  import { logger } from '@faststore/diagnostics'
@@ -56,22 +61,10 @@ const shouldReplaceCookieDomain = ({
56
61
  }
57
62
 
58
63
  /**
59
- * Determines if host is eligible for domain normalization.
60
- */
61
- const isAllowedHost = ({
62
- host,
63
- allowList,
64
- }: {
65
- host: string
66
- allowList: string[]
67
- }) => {
68
- const normalizedHost = host.toLowerCase()
69
-
70
- return allowList.some((suffix) => normalizedHost.endsWith(suffix))
71
- }
72
-
73
- /**
74
- * Ensure the cookie domain matches the current host so the browser can store it.
64
+ * On allowlisted hosts (previews, localhost) the upstream cookie domain does not
65
+ * match the browser host, so the Domain attribute is dropped and the cookie
66
+ * becomes host-only. The host is never written into the cookie, so a forged
67
+ * x-forwarded-host cannot choose the cookie scope.
75
68
  */
76
69
  const normalizeSetCookieDomain = ({
77
70
  request,
@@ -85,20 +78,31 @@ const normalizeSetCookieDomain = ({
85
78
  return setCookie
86
79
  }
87
80
 
88
- const host = getRequestHostname(request.headers.host)
81
+ // The preview ingress forwards a client-supplied x-forwarded-host as is, so
82
+ // it only decides whether the Domain is dropped, never which domain is used.
83
+ const forwardedHeader = request.headers['x-forwarded-host']
84
+ const forwardedHost = getRequestHostname(singleForwardedHost(forwardedHeader))
85
+ if (forwardedHeader && !forwardedHost) {
86
+ // The value is client-controllable, so it is not logged.
87
+ OTELLogger('warn', 'Ignoring multi-value or malformed x-forwarded-host')
88
+ }
89
+ const host =
90
+ forwardedHost && isHostAllowed(forwardedHost, ALLOWED_HOST_SUFFIXES)
91
+ ? forwardedHost
92
+ : getRequestHostname(request.headers.host)
89
93
  if (!host) {
90
94
  return setCookie
91
95
  }
92
96
  const cookieDomain = domainMatch[1]
93
97
 
94
98
  if (
95
- !isAllowedHost({ host, allowList: ALLOWED_HOST_SUFFIXES }) ||
99
+ !isHostAllowed(host, ALLOWED_HOST_SUFFIXES) ||
96
100
  !shouldReplaceCookieDomain({ cookieDomain, host })
97
101
  ) {
98
102
  return setCookie
99
103
  }
100
104
 
101
- return setCookie.replace(MATCH_DOMAIN_REGEXP, `; domain=${host}`)
105
+ return removeCookieDomain(setCookie)
102
106
  }
103
107
 
104
108
  const parseRequest = (request: NextApiRequest) => {
@@ -0,0 +1,50 @@
1
+ // The result is written verbatim into a Set-Cookie Domain attribute, so
2
+ // characters such as ";" or spaces would inject cookie attributes.
3
+ const HOSTNAME_PATTERN = /^[a-z0-9.-]+$/
4
+
5
+ /**
6
+ * An entry starting with "." matches subdomains only; any other entry matches
7
+ * the host itself and its subdomains. Matching is anchored on a label boundary,
8
+ * so "evil-localhost" never matches "localhost".
9
+ */
10
+ export function isHostAllowed(host: string, allowList: string[]) {
11
+ const normalizedHost = host.toLowerCase()
12
+
13
+ if (!HOSTNAME_PATTERN.test(normalizedHost)) {
14
+ return false
15
+ }
16
+
17
+ return allowList.some((entry) =>
18
+ entry.startsWith('.')
19
+ ? normalizedHost.endsWith(entry)
20
+ : normalizedHost === entry || normalizedHost.endsWith(`.${entry}`)
21
+ )
22
+ }
23
+
24
+ /**
25
+ * Returns the `x-forwarded-host` value only when exactly one is present.
26
+ *
27
+ * Which hop of a proxy chain is trustworthy is not known here: with several
28
+ * values the leftmost one is whatever the client sent. So a multi-value header
29
+ * (comma-joined by Node, or an array) is ignored and the caller falls back to
30
+ * `host`. A single value still assumes the ingress overwrites the header.
31
+ */
32
+ export function singleForwardedHost(
33
+ header: string | string[] | undefined
34
+ ): string | undefined {
35
+ if (Array.isArray(header)) {
36
+ return header.length === 1 ? singleForwardedHost(header[0]) : undefined
37
+ }
38
+
39
+ const value = header?.trim()
40
+
41
+ return value && !value.includes(',') ? value : undefined
42
+ }
43
+
44
+ /**
45
+ * Drops every Domain attribute so the cookie becomes host-only: the browser
46
+ * scopes it to the exact host it requested, whatever header picked this path.
47
+ */
48
+ export function removeCookieDomain(setCookie: string): string {
49
+ return setCookie.replaceAll(/;\s*domain=[^;]*/gi, '')
50
+ }
@@ -0,0 +1,105 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import {
4
+ singleForwardedHost,
5
+ isHostAllowed,
6
+ removeCookieDomain,
7
+ } from '../../src/utils/trustedForwardedHost'
8
+
9
+ const ALLOW_LIST = ['localhost', '.vtex.app', '.localhost']
10
+
11
+ describe('isHostAllowed', () => {
12
+ it.each([
13
+ 'localhost',
14
+ 'store.vtex.app',
15
+ 'sfj-1--acc.preview.vtex.app',
16
+ 'a.localhost',
17
+ ])('allows %s', (host) => {
18
+ expect(isHostAllowed(host, ALLOW_LIST)).toBe(true)
19
+ })
20
+
21
+ it.each([
22
+ 'evil-localhost',
23
+ 'notlocalhost',
24
+ 'evil-vtex.app',
25
+ 'vtex.app.evil.com',
26
+ 'evil.com',
27
+ ])('rejects %s', (host) => {
28
+ expect(isHostAllowed(host, ALLOW_LIST)).toBe(false)
29
+ })
30
+ })
31
+
32
+ describe('isHostAllowed normalization', () => {
33
+ it('is case-insensitive', () => {
34
+ expect(isHostAllowed('Store.VTEX.app', ALLOW_LIST)).toBe(true)
35
+ expect(isHostAllowed('LOCALHOST', ALLOW_LIST)).toBe(true)
36
+ })
37
+
38
+ it.each([
39
+ 'x;max-age=0;y.vtex.app',
40
+ 'a b.vtex.app',
41
+ 'x"y.vtex.app',
42
+ 'x=y.vtex.app',
43
+ 'x,y.vtex.app',
44
+ 'x\r\nSet-Cookie: evil=1.vtex.app',
45
+ 'x\ny.vtex.app',
46
+ 'x\ry.vtex.app',
47
+ ])('rejects %s, which would inject cookie attributes', (host) => {
48
+ expect(isHostAllowed(host, ALLOW_LIST)).toBe(false)
49
+ })
50
+
51
+ it('accepts punycode hostnames', () => {
52
+ expect(isHostAllowed('xn--lja-bma.vtex.app', ALLOW_LIST)).toBe(true)
53
+ })
54
+
55
+ it('expects a hostname: a port is not stripped here', () => {
56
+ expect(isHostAllowed('store.vtex.app:8080', ALLOW_LIST)).toBe(false)
57
+ expect(isHostAllowed('evil.com:vtex.app', ALLOW_LIST)).toBe(false)
58
+ })
59
+ })
60
+
61
+ describe('singleForwardedHost', () => {
62
+ it('returns the value when there is exactly one', () => {
63
+ expect(singleForwardedHost('a.vtex.app')).toBe('a.vtex.app')
64
+ expect(singleForwardedHost(' a.vtex.app ')).toBe('a.vtex.app')
65
+ expect(singleForwardedHost(['a.vtex.app'])).toBe('a.vtex.app')
66
+ })
67
+
68
+ it('returns undefined when missing or empty', () => {
69
+ expect(singleForwardedHost(undefined)).toBeUndefined()
70
+ expect(singleForwardedHost('')).toBeUndefined()
71
+ expect(singleForwardedHost([])).toBeUndefined()
72
+ })
73
+
74
+ it('ignores multi-value headers instead of picking one', () => {
75
+ expect(singleForwardedHost('a.vtex.app, evil.com')).toBeUndefined()
76
+ expect(singleForwardedHost('evil.com, a.vtex.app')).toBeUndefined()
77
+ expect(singleForwardedHost(['a.vtex.app', 'evil.com'])).toBeUndefined()
78
+ })
79
+ })
80
+
81
+ describe('removeCookieDomain', () => {
82
+ it('drops the Domain attribute and keeps the rest', () => {
83
+ expect(
84
+ removeCookieDomain(
85
+ 'checkout.vtex.com=__ofid=abc; expires=Sun, 04 Apr 2027 18:31:26 GMT; domain=acc.vtexcommercestable.com.br; path=/; secure; samesite=lax; httponly'
86
+ )
87
+ ).toBe(
88
+ 'checkout.vtex.com=__ofid=abc; expires=Sun, 04 Apr 2027 18:31:26 GMT; path=/; secure; samesite=lax; httponly'
89
+ )
90
+ })
91
+
92
+ it('is case-insensitive and removes repeated Domain attributes', () => {
93
+ expect(removeCookieDomain('a=1; Domain=x.com; DOMAIN=y.com; Path=/')).toBe(
94
+ 'a=1; Path=/'
95
+ )
96
+ })
97
+
98
+ it('does not touch a cookie whose name is domain', () => {
99
+ expect(removeCookieDomain('domain=1; path=/')).toBe('domain=1; path=/')
100
+ })
101
+
102
+ it('keeps cookies without a Domain attribute unchanged', () => {
103
+ expect(removeCookieDomain('a=1; path=/')).toBe('a=1; path=/')
104
+ })
105
+ })