mirror of
https://github.com/nextcloud/server.git
synced 2026-05-17 02:49:55 -04:00
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>
This commit is contained in:
parent
f721cb2267
commit
b56334cd6d
9 changed files with 124 additions and 22 deletions
114
core/src/tests/utils/RedirectUnsupportedBrowsers.spec.ts
Normal file
114
core/src/tests/utils/RedirectUnsupportedBrowsers.spec.ts
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { testSupportedBrowser } from '../../utils/RedirectUnsupportedBrowsers.js'
|
||||
|
||||
// Mock the router so generateUrl returns a predictable path
|
||||
vi.mock('@nextcloud/router', () => ({
|
||||
generateUrl: (path: string) => `/index.php${path}`,
|
||||
}))
|
||||
|
||||
// Mock the logger to suppress output
|
||||
vi.mock('../../logger.js', () => ({
|
||||
default: { debug: vi.fn() },
|
||||
}))
|
||||
|
||||
const browserStorage = vi.hoisted(() => ({ getItem: vi.fn(() => null) }))
|
||||
vi.mock('../../services/BrowserStorageService.js', () => ({ default: browserStorage }))
|
||||
|
||||
const supportedUA = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36'
|
||||
const unsupportedUA = 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)'
|
||||
|
||||
describe('testSupportedBrowser', () => {
|
||||
let originalLocation: Location
|
||||
|
||||
beforeEach(() => {
|
||||
originalLocation = window.location
|
||||
|
||||
// Reset the override flag
|
||||
browserStorage.getItem.mockReturnValue(null)
|
||||
|
||||
// Default to a path that isn't the unsupported-browser page
|
||||
Object.defineProperty(window, 'location', {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: {
|
||||
href: 'http://localhost/apps/files',
|
||||
origin: 'http://localhost',
|
||||
pathname: '/apps/files',
|
||||
reload: vi.fn(),
|
||||
},
|
||||
})
|
||||
|
||||
vi.spyOn(window.history, 'pushState').mockImplementation(() => {})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
Object.defineProperty(window, 'location', {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: originalLocation,
|
||||
})
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
it('does nothing for a supported browser', () => {
|
||||
Object.defineProperty(window.navigator, 'userAgent', { configurable: true, value: supportedUA })
|
||||
|
||||
testSupportedBrowser()
|
||||
|
||||
expect(window.history.pushState).not.toHaveBeenCalled()
|
||||
expect(window.location.reload).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('redirects an unsupported browser to the warning page', () => {
|
||||
Object.defineProperty(window.navigator, 'userAgent', { configurable: true, value: unsupportedUA })
|
||||
|
||||
testSupportedBrowser()
|
||||
|
||||
expect(window.history.pushState).toHaveBeenCalledOnce()
|
||||
const [, , url] = (window.history.pushState as ReturnType<typeof vi.fn>).mock.calls[0]
|
||||
expect(url).toMatch(/^\/index\.php\/unsupported\?redirect_url=/)
|
||||
expect(window.location.reload).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('encodes the redirect URL with btoa, not window.Buffer', () => {
|
||||
Object.defineProperty(window.navigator, 'userAgent', { configurable: true, value: unsupportedUA })
|
||||
|
||||
testSupportedBrowser()
|
||||
|
||||
const [, , url] = (window.history.pushState as ReturnType<typeof vi.fn>).mock.calls[0]
|
||||
const encoded = new URL(`http://localhost${url}`).searchParams.get('redirect_url')
|
||||
expect(encoded).toBe(btoa('/apps/files'))
|
||||
})
|
||||
|
||||
it('does not throw regardless of override flag state', () => {
|
||||
// isBrowserOverridden is read at module-load time so the mock won't flip it
|
||||
// retroactively — but we can at least assert the function never throws,
|
||||
// which is the regression guard for the window.Buffer removal.
|
||||
Object.defineProperty(window.navigator, 'userAgent', { configurable: true, value: unsupportedUA })
|
||||
expect(() => testSupportedBrowser()).not.toThrow()
|
||||
})
|
||||
|
||||
it('does not redirect when already on the unsupported-browser page', () => {
|
||||
Object.defineProperty(window.navigator, 'userAgent', { configurable: true, value: unsupportedUA })
|
||||
Object.defineProperty(window, 'location', {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: {
|
||||
href: 'http://localhost/index.php/unsupported',
|
||||
origin: 'http://localhost',
|
||||
pathname: '/index.php/unsupported',
|
||||
reload: vi.fn(),
|
||||
},
|
||||
})
|
||||
|
||||
testSupportedBrowser()
|
||||
|
||||
expect(window.history.pushState).not.toHaveBeenCalled()
|
||||
expect(window.location.reload).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
|
@ -33,7 +33,7 @@ export const testSupportedBrowser = function() {
|
|||
// redirect to the unsupported warning page
|
||||
if (window.location.pathname.indexOf(redirectPath) === -1) {
|
||||
const redirectUrl = window.location.href.replace(window.location.origin, '')
|
||||
const base64Param = Buffer.from(redirectUrl).toString('base64')
|
||||
const base64Param = btoa(redirectUrl)
|
||||
history.pushState(null, null, `${redirectPath}?redirect_url=${base64Param}`)
|
||||
window.location.reload()
|
||||
}
|
||||
|
|
|
|||
4
dist/7883-7883.js
vendored
4
dist/7883-7883.js
vendored
File diff suppressed because one or more lines are too long
12
dist/7883-7883.js.license
vendored
12
dist/7883-7883.js.license
vendored
|
|
@ -2,18 +2,15 @@ SPDX-License-Identifier: MIT
|
|||
SPDX-License-Identifier: ISC
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
SPDX-License-Identifier: CC-BY-4.0
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
SPDX-FileCopyrightText: dangreen
|
||||
SPDX-FileCopyrightText: baseline-browser-mapping developers
|
||||
SPDX-FileCopyrightText: T. Jameson Little <t.jameson.little@gmail.com>
|
||||
SPDX-FileCopyrightText: Sergey Rubanov <chi187@gmail.com>
|
||||
SPDX-FileCopyrightText: Roman Shtylman <shtylman@gmail.com>
|
||||
SPDX-FileCopyrightText: Nextcloud GmbH and Nextcloud contributors
|
||||
SPDX-FileCopyrightText: Kilian Valkhof
|
||||
SPDX-FileCopyrightText: GitHub Inc.
|
||||
SPDX-FileCopyrightText: Feross Aboukhadijeh
|
||||
SPDX-FileCopyrightText: Dmitry Soshnikov
|
||||
SPDX-FileCopyrightText: Ben Briggs
|
||||
SPDX-FileCopyrightText: Andrey Sitnik <andrey@sitnik.ru>
|
||||
|
|
@ -41,9 +38,6 @@ This file is generated from multiple sources. Included packages:
|
|||
- @nextcloud/router
|
||||
- version: 3.1.0
|
||||
- license: GPL-3.0-or-later
|
||||
- base64-js
|
||||
- version: 1.5.1
|
||||
- license: MIT
|
||||
- baseline-browser-mapping
|
||||
- version: 2.9.9
|
||||
- license: Apache-2.0
|
||||
|
|
@ -59,12 +53,6 @@ This file is generated from multiple sources. Included packages:
|
|||
- electron-to-chromium
|
||||
- version: 1.5.267
|
||||
- license: ISC
|
||||
- ieee754
|
||||
- version: 1.2.1
|
||||
- license: BSD-3-Clause
|
||||
- buffer
|
||||
- version: 6.0.3
|
||||
- license: MIT
|
||||
- node-releases
|
||||
- version: 2.0.27
|
||||
- license: MIT
|
||||
|
|
|
|||
2
dist/7883-7883.js.map
vendored
2
dist/7883-7883.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/core-unsupported-browser-redirect.js
vendored
4
dist/core-unsupported-browser-redirect.js
vendored
|
|
@ -1,2 +1,2 @@
|
|||
(()=>{"use strict";var e,r,t,o={47210(e,r,t){var o=t(21777);t.nc=(0,o.aV)(),window.TESTING||OC?.config?.no_unsupported_browser_warning||window.addEventListener("DOMContentLoaded",async function(){const{testSupportedBrowser:e}=await Promise.all([t.e(4208),t.e(7883)]).then(t.bind(t,77883));e()})}},n={};function a(e){var r=n[e];if(void 0!==r)return r.exports;var t=n[e]={id:e,loaded:!1,exports:{}};return o[e].call(t.exports,t,t.exports,a),t.loaded=!0,t.exports}a.m=o,e=[],a.O=(r,t,o,n)=>{if(!t){var i=1/0;for(s=0;s<e.length;s++){for(var[t,o,n]=e[s],l=!0,d=0;d<t.length;d++)(!1&n||i>=n)&&Object.keys(a.O).every(e=>a.O[e](t[d]))?t.splice(d--,1):(l=!1,n<i&&(i=n));if(l){e.splice(s--,1);var c=o();void 0!==c&&(r=c)}}return r}n=n||0;for(var s=e.length;s>0&&e[s-1][2]>n;s--)e[s]=e[s-1];e[s]=[t,o,n]},a.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return a.d(r,{a:r}),r},a.d=(e,r)=>{for(var t in r)a.o(r,t)&&!a.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},a.f={},a.e=e=>Promise.all(Object.keys(a.f).reduce((r,t)=>(a.f[t](e,r),r),[])),a.u=e=>e+"-"+e+".js?v=f0b15d22d89c4648c25b",a.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r={},t="nextcloud:",a.l=(e,o,n,i)=>{if(r[e])r[e].push(o);else{var l,d;if(void 0!==n)for(var c=document.getElementsByTagName("script"),s=0;s<c.length;s++){var u=c[s];if(u.getAttribute("src")==e||u.getAttribute("data-webpack")==t+n){l=u;break}}l||(d=!0,(l=document.createElement("script")).charset="utf-8",a.nc&&l.setAttribute("nonce",a.nc),l.setAttribute("data-webpack",t+n),l.src=e),r[e]=[o];var p=(t,o)=>{l.onerror=l.onload=null,clearTimeout(f);var n=r[e];if(delete r[e],l.parentNode&&l.parentNode.removeChild(l),n&&n.forEach(e=>e(o)),t)return t(o)},f=setTimeout(p.bind(null,void 0,{type:"timeout",target:l}),12e4);l.onerror=p.bind(null,l.onerror),l.onload=p.bind(null,l.onload),d&&document.head.appendChild(l)}},a.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),a.j=3604,(()=>{var e;globalThis.importScripts&&(e=globalThis.location+"");var r=globalThis.document;if(!e&&r&&(r.currentScript&&"SCRIPT"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName("script");if(t.length)for(var o=t.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=t[o--].src}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),a.p=e})(),(()=>{a.b="undefined"!=typeof document&&document.baseURI||self.location.href;var e={3604:0};a.f.j=(r,t)=>{var o=a.o(e,r)?e[r]:void 0;if(0!==o)if(o)t.push(o[2]);else{var n=new Promise((t,n)=>o=e[r]=[t,n]);t.push(o[2]=n);var i=a.p+a.u(r),l=new Error;a.l(i,t=>{if(a.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var n=t&&("load"===t.type?"missing":t.type),i=t&&t.target&&t.target.src;l.message="Loading chunk "+r+" failed.\n("+n+": "+i+")",l.name="ChunkLoadError",l.type=n,l.request=i,o[1](l)}},"chunk-"+r,r)}},a.O.j=r=>0===e[r];var r=(r,t)=>{var o,n,[i,l,d]=t,c=0;if(i.some(r=>0!==e[r])){for(o in l)a.o(l,o)&&(a.m[o]=l[o]);if(d)var s=d(a)}for(r&&r(t);c<i.length;c++)n=i[c],a.o(e,n)&&e[n]&&e[n][0](),e[n]=0;return a.O(s)},t=globalThis.webpackChunknextcloud=globalThis.webpackChunknextcloud||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})(),a.nc=void 0;var i=a.O(void 0,[4208],()=>a(47210));i=a.O(i)})();
|
||||
//# sourceMappingURL=core-unsupported-browser-redirect.js.map?v=6acb671a13676e115e9a
|
||||
(()=>{"use strict";var e,r,t,o={47210(e,r,t){var o=t(21777);t.nc=(0,o.aV)(),window.TESTING||OC?.config?.no_unsupported_browser_warning||window.addEventListener("DOMContentLoaded",async function(){const{testSupportedBrowser:e}=await Promise.all([t.e(4208),t.e(7883)]).then(t.bind(t,77883));e()})}},n={};function a(e){var r=n[e];if(void 0!==r)return r.exports;var t=n[e]={id:e,loaded:!1,exports:{}};return o[e].call(t.exports,t,t.exports,a),t.loaded=!0,t.exports}a.m=o,e=[],a.O=(r,t,o,n)=>{if(!t){var i=1/0;for(c=0;c<e.length;c++){for(var[t,o,n]=e[c],l=!0,d=0;d<t.length;d++)(!1&n||i>=n)&&Object.keys(a.O).every(e=>a.O[e](t[d]))?t.splice(d--,1):(l=!1,n<i&&(i=n));if(l){e.splice(c--,1);var s=o();void 0!==s&&(r=s)}}return r}n=n||0;for(var c=e.length;c>0&&e[c-1][2]>n;c--)e[c]=e[c-1];e[c]=[t,o,n]},a.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return a.d(r,{a:r}),r},a.d=(e,r)=>{for(var t in r)a.o(r,t)&&!a.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},a.f={},a.e=e=>Promise.all(Object.keys(a.f).reduce((r,t)=>(a.f[t](e,r),r),[])),a.u=e=>e+"-"+e+".js?v=c913a312e3fb195d4cf7",a.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r={},t="nextcloud:",a.l=(e,o,n,i)=>{if(r[e])r[e].push(o);else{var l,d;if(void 0!==n)for(var s=document.getElementsByTagName("script"),c=0;c<s.length;c++){var u=s[c];if(u.getAttribute("src")==e||u.getAttribute("data-webpack")==t+n){l=u;break}}l||(d=!0,(l=document.createElement("script")).charset="utf-8",a.nc&&l.setAttribute("nonce",a.nc),l.setAttribute("data-webpack",t+n),l.src=e),r[e]=[o];var p=(t,o)=>{l.onerror=l.onload=null,clearTimeout(f);var n=r[e];if(delete r[e],l.parentNode&&l.parentNode.removeChild(l),n&&n.forEach(e=>e(o)),t)return t(o)},f=setTimeout(p.bind(null,void 0,{type:"timeout",target:l}),12e4);l.onerror=p.bind(null,l.onerror),l.onload=p.bind(null,l.onload),d&&document.head.appendChild(l)}},a.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),a.j=3604,(()=>{var e;globalThis.importScripts&&(e=globalThis.location+"");var r=globalThis.document;if(!e&&r&&(r.currentScript&&"SCRIPT"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName("script");if(t.length)for(var o=t.length-1;o>-1&&(!e||!/^http(s?):/.test(e));)e=t[o--].src}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),a.p=e})(),(()=>{a.b="undefined"!=typeof document&&document.baseURI||self.location.href;var e={3604:0};a.f.j=(r,t)=>{var o=a.o(e,r)?e[r]:void 0;if(0!==o)if(o)t.push(o[2]);else{var n=new Promise((t,n)=>o=e[r]=[t,n]);t.push(o[2]=n);var i=a.p+a.u(r),l=new Error;a.l(i,t=>{if(a.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var n=t&&("load"===t.type?"missing":t.type),i=t&&t.target&&t.target.src;l.message="Loading chunk "+r+" failed.\n("+n+": "+i+")",l.name="ChunkLoadError",l.type=n,l.request=i,o[1](l)}},"chunk-"+r,r)}},a.O.j=r=>0===e[r];var r=(r,t)=>{var o,n,[i,l,d]=t,s=0;if(i.some(r=>0!==e[r])){for(o in l)a.o(l,o)&&(a.m[o]=l[o]);if(d)var c=d(a)}for(r&&r(t);s<i.length;s++)n=i[s],a.o(e,n)&&e[n]&&e[n][0](),e[n]=0;return a.O(c)},t=globalThis.webpackChunknextcloud=globalThis.webpackChunknextcloud||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})(),a.nc=void 0;var i=a.O(void 0,[4208],()=>a(47210));i=a.O(i)})();
|
||||
//# sourceMappingURL=core-unsupported-browser-redirect.js.map?v=f3ea7a1661e7e0cec3ed
|
||||
File diff suppressed because one or more lines are too long
4
dist/core-unsupported-browser.js
vendored
4
dist/core-unsupported-browser.js
vendored
File diff suppressed because one or more lines are too long
2
dist/core-unsupported-browser.js.map
vendored
2
dist/core-unsupported-browser.js.map
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue