Skip to content

Commit b56334c

Browse files
miaulalalanextcloud-command
authored andcommitted
fix(core): use btoa() instead of window.Buffer.from() for base64 encoding
Signed-off-by: Anna Larch <anna@nextcloud.com> Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
1 parent f721cb2 commit b56334c

9 files changed

Lines changed: 124 additions & 22 deletions
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
7+
import { testSupportedBrowser } from '../../utils/RedirectUnsupportedBrowsers.js'
8+
9+
// Mock the router so generateUrl returns a predictable path
10+
vi.mock('@nextcloud/router', () => ({
11+
generateUrl: (path: string) => `/index.php${path}`,
12+
}))
13+
14+
// Mock the logger to suppress output
15+
vi.mock('../../logger.js', () => ({
16+
default: { debug: vi.fn() },
17+
}))
18+
19+
const browserStorage = vi.hoisted(() => ({ getItem: vi.fn(() => null) }))
20+
vi.mock('../../services/BrowserStorageService.js', () => ({ default: browserStorage }))
21+
22+
const supportedUA = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36'
23+
const unsupportedUA = 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)'
24+
25+
describe('testSupportedBrowser', () => {
26+
let originalLocation: Location
27+
28+
beforeEach(() => {
29+
originalLocation = window.location
30+
31+
// Reset the override flag
32+
browserStorage.getItem.mockReturnValue(null)
33+
34+
// Default to a path that isn't the unsupported-browser page
35+
Object.defineProperty(window, 'location', {
36+
configurable: true,
37+
writable: true,
38+
value: {
39+
href: 'http://localhost/apps/files',
40+
origin: 'http://localhost',
41+
pathname: '/apps/files',
42+
reload: vi.fn(),
43+
},
44+
})
45+
46+
vi.spyOn(window.history, 'pushState').mockImplementation(() => {})
47+
})
48+
49+
afterEach(() => {
50+
Object.defineProperty(window, 'location', {
51+
configurable: true,
52+
writable: true,
53+
value: originalLocation,
54+
})
55+
vi.restoreAllMocks()
56+
})
57+
58+
it('does nothing for a supported browser', () => {
59+
Object.defineProperty(window.navigator, 'userAgent', { configurable: true, value: supportedUA })
60+
61+
testSupportedBrowser()
62+
63+
expect(window.history.pushState).not.toHaveBeenCalled()
64+
expect(window.location.reload).not.toHaveBeenCalled()
65+
})
66+
67+
it('redirects an unsupported browser to the warning page', () => {
68+
Object.defineProperty(window.navigator, 'userAgent', { configurable: true, value: unsupportedUA })
69+
70+
testSupportedBrowser()
71+
72+
expect(window.history.pushState).toHaveBeenCalledOnce()
73+
const [, , url] = (window.history.pushState as ReturnType<typeof vi.fn>).mock.calls[0]
74+
expect(url).toMatch(/^\/index\.php\/unsupported\?redirect_url=/)
75+
expect(window.location.reload).toHaveBeenCalledOnce()
76+
})
77+
78+
it('encodes the redirect URL with btoa, not window.Buffer', () => {
79+
Object.defineProperty(window.navigator, 'userAgent', { configurable: true, value: unsupportedUA })
80+
81+
testSupportedBrowser()
82+
83+
const [, , url] = (window.history.pushState as ReturnType<typeof vi.fn>).mock.calls[0]
84+
const encoded = new URL(`http://localhost${url}`).searchParams.get('redirect_url')
85+
expect(encoded).toBe(btoa('/apps/files'))
86+
})
87+
88+
it('does not throw regardless of override flag state', () => {
89+
// isBrowserOverridden is read at module-load time so the mock won't flip it
90+
// retroactively — but we can at least assert the function never throws,
91+
// which is the regression guard for the window.Buffer removal.
92+
Object.defineProperty(window.navigator, 'userAgent', { configurable: true, value: unsupportedUA })
93+
expect(() => testSupportedBrowser()).not.toThrow()
94+
})
95+
96+
it('does not redirect when already on the unsupported-browser page', () => {
97+
Object.defineProperty(window.navigator, 'userAgent', { configurable: true, value: unsupportedUA })
98+
Object.defineProperty(window, 'location', {
99+
configurable: true,
100+
writable: true,
101+
value: {
102+
href: 'http://localhost/index.php/unsupported',
103+
origin: 'http://localhost',
104+
pathname: '/index.php/unsupported',
105+
reload: vi.fn(),
106+
},
107+
})
108+
109+
testSupportedBrowser()
110+
111+
expect(window.history.pushState).not.toHaveBeenCalled()
112+
expect(window.location.reload).not.toHaveBeenCalled()
113+
})
114+
})

core/src/utils/RedirectUnsupportedBrowsers.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export const testSupportedBrowser = function() {
3333
// redirect to the unsupported warning page
3434
if (window.location.pathname.indexOf(redirectPath) === -1) {
3535
const redirectUrl = window.location.href.replace(window.location.origin, '')
36-
const base64Param = Buffer.from(redirectUrl).toString('base64')
36+
const base64Param = btoa(redirectUrl)
3737
history.pushState(null, null, `${redirectPath}?redirect_url=${base64Param}`)
3838
window.location.reload()
3939
}

dist/7883-7883.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/7883-7883.js.license

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,15 @@ SPDX-License-Identifier: MIT
22
SPDX-License-Identifier: ISC
33
SPDX-License-Identifier: GPL-3.0-or-later
44
SPDX-License-Identifier: CC-BY-4.0
5-
SPDX-License-Identifier: BSD-3-Clause
65
SPDX-License-Identifier: Apache-2.0
76
SPDX-License-Identifier: AGPL-3.0-or-later
87
SPDX-FileCopyrightText: dangreen
98
SPDX-FileCopyrightText: baseline-browser-mapping developers
10-
SPDX-FileCopyrightText: T. Jameson Little <t.jameson.little@gmail.com>
119
SPDX-FileCopyrightText: Sergey Rubanov <chi187@gmail.com>
1210
SPDX-FileCopyrightText: Roman Shtylman <shtylman@gmail.com>
1311
SPDX-FileCopyrightText: Nextcloud GmbH and Nextcloud contributors
1412
SPDX-FileCopyrightText: Kilian Valkhof
1513
SPDX-FileCopyrightText: GitHub Inc.
16-
SPDX-FileCopyrightText: Feross Aboukhadijeh
1714
SPDX-FileCopyrightText: Dmitry Soshnikov
1815
SPDX-FileCopyrightText: Ben Briggs
1916
SPDX-FileCopyrightText: Andrey Sitnik <andrey@sitnik.ru>
@@ -41,9 +38,6 @@ This file is generated from multiple sources. Included packages:
4138
- @nextcloud/router
4239
- version: 3.1.0
4340
- license: GPL-3.0-or-later
44-
- base64-js
45-
- version: 1.5.1
46-
- license: MIT
4741
- baseline-browser-mapping
4842
- version: 2.9.9
4943
- license: Apache-2.0
@@ -59,12 +53,6 @@ This file is generated from multiple sources. Included packages:
5953
- electron-to-chromium
6054
- version: 1.5.267
6155
- license: ISC
62-
- ieee754
63-
- version: 1.2.1
64-
- license: BSD-3-Clause
65-
- buffer
66-
- version: 6.0.3
67-
- license: MIT
6856
- node-releases
6957
- version: 2.0.27
7058
- license: MIT

dist/7883-7883.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/core-unsupported-browser-redirect.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/core-unsupported-browser-redirect.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/core-unsupported-browser.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/core-unsupported-browser.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)