fix(OC): validate request token and move logic to one place

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
pull/53326/head
Ferdinand Thiessen 4 months ago
parent 84ab0dc97e
commit 8fe3a4195d
No known key found for this signature in database
GPG Key ID: 45FAE7268762B400
  1. 4
      core/src/OC/index.js
  2. 56
      core/src/OC/requesttoken.ts
  3. 2
      core/src/globals.js
  4. 4
      core/src/jquery/requesttoken.js
  5. 44
      core/src/tests/OC/requesttoken.spec.js
  6. 147
      core/src/tests/OC/requesttoken.spec.ts
  7. 407
      package-lock.json
  8. 1
      package.json
  9. 3
      tsconfig.json

@ -49,9 +49,7 @@ import {
getPort,
getProtocol,
} from './host.js'
import {
getToken as getRequestToken,
} from './requesttoken.ts'
import { getRequestToken } from './requesttoken.ts'
import {
hideMenus,
registerMenu,

@ -4,36 +4,46 @@
*/
import { emit } from '@nextcloud/event-bus'
import { generateUrl } from '@nextcloud/router'
/**
* @private
* @param {Document} global the document to read the initial value from
* @param {Function} emit the function to invoke for every new token
* @return {object}
* Get the current CSRF token.
*/
export const manageToken = (global, emit) => {
let token = global.getElementsByTagName('head')[0].getAttribute('data-requesttoken')
return {
getToken: () => token,
setToken: newToken => {
token = newToken
emit('csrf-token-update', {
token,
})
},
}
export function getRequestToken(): string {
return document.head.dataset.requesttoken!
}
const manageFromDocument = manageToken(document, emit)
/**
* @return {string}
* Set a new CSRF token (e.g. because of session refresh).
* This also emits an event bus event for the updated token.
*
* @param token - The new token
* @fires Error - If the passed token is not a potential valid token
*/
export const getToken = manageFromDocument.getToken
export function setRequestToken(token: string): void {
if (!token || typeof token !== 'string') {
throw new Error('Invalid CSRF token given', { cause: { token } })
}
document.head.dataset.requesttoken = token
emit('csrf-token-update', { token })
}
/**
* @param {string} newToken new token
* Fetch the request token from the API.
* This does also set it on the current context, see `setRequestToken`.
*
* @fires Error - If the request failed
*/
export const setToken = manageFromDocument.setToken
export async function fetchRequestToken(): Promise<string> {
const url = generateUrl('/csrftoken')
const response = await fetch(url)
if (!response.ok) {
throw new Error('Could not fetch CSRF token from API', { cause: response })
}
const { token } = await response.json()
setRequestToken(token)
return token
}

@ -29,7 +29,7 @@ import 'strengthify/strengthify.css'
import OC from './OC/index.js'
import OCP from './OCP/index.js'
import OCA from './OCA/index.js'
import { getToken as getRequestToken } from './OC/requesttoken.ts'
import { getRequestToken } from './OC/requesttoken.ts'
const warnIfNotTesting = function() {
if (window.TESTING === undefined) {

@ -5,11 +5,11 @@
import $ from 'jquery'
import { getToken } from '../OC/requesttoken.ts'
import { getRequestToken } from '../OC/requesttoken.ts'
$(document).on('ajaxSend', function(elm, xhr, settings) {
if (settings.crossDomain === false) {
xhr.setRequestHeader('requesttoken', getToken())
xhr.setRequestHeader('requesttoken', getRequestToken())
xhr.setRequestHeader('OCS-APIREQUEST', 'true')
}
})

@ -1,44 +0,0 @@
/**
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { beforeEach, describe, expect, test, vi } from 'vitest'
import { manageToken, setToken } from '../../OC/requesttoken.ts'
const eventbus = vi.hoisted(() => ({ emit: vi.fn() }))
vi.mock('@nextcloud/event-bus', () => eventbus)
describe('request token', () => {
let emit
let manager
const token = 'abc123'
beforeEach(() => {
emit = vi.fn()
const head = window.document.getElementsByTagName('head')[0]
head.setAttribute('data-requesttoken', token)
manager = manageToken(window.document, emit)
})
test('reads the token from the document', () => {
expect(manager.getToken()).toBe('abc123')
})
test('remembers the updated token', () => {
manager.setToken('bca321')
expect(manager.getToken()).toBe('bca321')
})
describe('@nextcloud/auth integration', () => {
test('fires off an event for @nextcloud/auth', () => {
setToken('123')
expect(eventbus.emit).toHaveBeenCalledWith('csrf-token-update', { token: '123' })
})
})
})

@ -0,0 +1,147 @@
/**
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { setupServer } from 'msw/node'
import { http, HttpResponse } from 'msw'
import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
import { fetchRequestToken, getRequestToken, setRequestToken } from '../../OC/requesttoken.ts'
const eventbus = vi.hoisted(() => ({ emit: vi.fn() }))
vi.mock('@nextcloud/event-bus', () => eventbus)
const server = setupServer()
describe('getRequestToken', () => {
it('can read the token from DOM', () => {
mockToken('tokenmock-123')
expect(getRequestToken()).toBe('tokenmock-123')
})
it('can handle missing token', () => {
mockToken(undefined)
expect(getRequestToken()).toBeUndefined()
})
})
describe('setRequestToken', () => {
beforeEach(() => {
vi.resetAllMocks()
})
it('does emit an event on change', () => {
setRequestToken('new-token')
expect(eventbus.emit).toBeCalledTimes(1)
expect(eventbus.emit).toBeCalledWith('csrf-token-update', { token: 'new-token' })
})
it('does set the new token to the DOM', () => {
setRequestToken('new-token')
expect(document.head.dataset.requesttoken).toBe('new-token')
})
it('does remember the new token', () => {
mockToken('old-token')
setRequestToken('new-token')
expect(getRequestToken()).toBe('new-token')
})
it('throws if the token is not a string', () => {
// @ts-expect-error mocking
expect(() => setRequestToken(123)).toThrowError('Invalid CSRF token given')
})
it('throws if the token is not valid', () => {
expect(() => setRequestToken('')).toThrowError('Invalid CSRF token given')
})
it('does not emit an event if the token is not valid', () => {
expect(() => setRequestToken('')).toThrowError('Invalid CSRF token given')
expect(eventbus.emit).not.toBeCalled()
})
})
describe('fetchRequestToken', () => {
const successfullCsrf = http.get('/index.php/csrftoken', () => {
return HttpResponse.json({ token: 'new-token' })
})
const forbiddenCsrf = http.get('/index.php/csrftoken', () => {
return HttpResponse.json([], { status: 403 })
})
const serverErrorCsrf = http.get('/index.php/csrftoken', () => {
return HttpResponse.json([], { status: 500 })
})
const networkErrorCsrf = http.get('/index.php/csrftoken', () => {
return new HttpResponse(null, { type: 'error' })
})
beforeAll(() => {
server.listen()
})
beforeEach(() => {
vi.resetAllMocks()
})
it('correctly parses response', async () => {
server.use(successfullCsrf)
mockToken('oldToken')
const token = await fetchRequestToken()
expect(token).toBe('new-token')
})
it('sets the token', async () => {
server.use(successfullCsrf)
mockToken('oldToken')
await fetchRequestToken()
expect(getRequestToken()).toBe('new-token')
})
it('does emit an event', async () => {
server.use(successfullCsrf)
await fetchRequestToken()
expect(eventbus.emit).toHaveBeenCalledOnce()
expect(eventbus.emit).toBeCalledWith('csrf-token-update', { token: 'new-token' })
})
it('handles 403 error due to invalid cookies', async () => {
server.use(forbiddenCsrf)
mockToken('oldToken')
await expect(() => fetchRequestToken()).rejects.toThrowError('Could not fetch CSRF token from API')
expect(getRequestToken()).toBe('oldToken')
})
it('handles server error', async () => {
server.use(serverErrorCsrf)
mockToken('oldToken')
await expect(() => fetchRequestToken()).rejects.toThrowError('Could not fetch CSRF token from API')
expect(getRequestToken()).toBe('oldToken')
})
it('handles network error', async () => {
server.use(networkErrorCsrf)
mockToken('oldToken')
await expect(() => fetchRequestToken()).rejects.toThrow()
expect(getRequestToken()).toBe('oldToken')
})
})
/**
* Mock the request token directly so we can test reading it.
*
* @param token - The CSRF token to mock
*/
function mockToken(token?: string) {
if (token === undefined) {
delete document.head.dataset.requesttoken
} else {
document.head.dataset.requesttoken = token
}
}

407
package-lock.json generated

@ -139,6 +139,7 @@
"karma-spec-reporter": "^0.0.36",
"karma-viewport": "^1.0.9",
"mime": "^4.0.7",
"msw": "^2.9.0",
"puppeteer": "^24.10.0",
"raw-loader": "^4.0.2",
"regextras": "^0.8.0",
@ -2141,6 +2142,83 @@
"node": ">=18"
}
},
"node_modules/@bundled-es-modules/cookie": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.1.tgz",
"integrity": "sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==",
"dev": true,
"license": "ISC",
"dependencies": {
"cookie": "^0.7.2"
}
},
"node_modules/@bundled-es-modules/statuses": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz",
"integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==",
"dev": true,
"license": "ISC",
"dependencies": {
"statuses": "^2.0.1"
}
},
"node_modules/@bundled-es-modules/statuses/node_modules/statuses": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/@bundled-es-modules/tough-cookie": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/@bundled-es-modules/tough-cookie/-/tough-cookie-0.1.6.tgz",
"integrity": "sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==",
"dev": true,
"license": "ISC",
"dependencies": {
"@types/tough-cookie": "^4.0.5",
"tough-cookie": "^4.1.4"
}
},
"node_modules/@bundled-es-modules/tough-cookie/node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/@bundled-es-modules/tough-cookie/node_modules/tough-cookie": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
"integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"psl": "^1.1.33",
"punycode": "^2.1.1",
"universalify": "^0.2.0",
"url-parse": "^1.5.3"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@bundled-es-modules/tough-cookie/node_modules/universalify": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/@buttercup/fetch": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/@buttercup/fetch/-/fetch-0.2.1.tgz",
@ -3367,6 +3445,112 @@
"license": "BSD-3-Clause",
"peer": true
},
"node_modules/@inquirer/confirm": {
"version": "5.1.12",
"resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.12.tgz",
"integrity": "sha512-dpq+ielV9/bqgXRUbNH//KsY6WEw9DrGPmipkpmgC1Y46cwuBTNx7PXFWTjc3MQ+urcc0QxoVHcMI0FW4Ok0hg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@inquirer/core": "^10.1.13",
"@inquirer/type": "^3.0.7"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@types/node": ">=18"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
}
}
},
"node_modules/@inquirer/core": {
"version": "10.1.13",
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.13.tgz",
"integrity": "sha512-1viSxebkYN2nJULlzCxES6G9/stgHSepZ9LqqfdIGPHj5OHhiBUXVS0a6R0bEC2A+VL4D9w6QB66ebCr6HGllA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@inquirer/figures": "^1.0.12",
"@inquirer/type": "^3.0.7",
"ansi-escapes": "^4.3.2",
"cli-width": "^4.1.0",
"mute-stream": "^2.0.0",
"signal-exit": "^4.1.0",
"wrap-ansi": "^6.2.0",
"yoctocolors-cjs": "^2.1.2"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@types/node": ">=18"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
}
}
},
"node_modules/@inquirer/core/node_modules/signal-exit": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@inquirer/core/node_modules/wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@inquirer/figures": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.12.tgz",
"integrity": "sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/@inquirer/type": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.7.tgz",
"integrity": "sha512-PfunHQcjwnju84L+ycmcMKB/pTPIngjUJvfnRhKY6FKPuYXlM4aQCb/nIdTFR6BEhMjFvngzvng/vBAJMZpLSA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@types/node": ">=18"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
}
}
},
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@ -3741,6 +3925,24 @@
"integrity": "sha512-WQ2gDll12T9WD34fdRFgQVgO8bag3gavrAgJ0frN4phlwdJARpE6gO1YvLEMJR0KKgoc+/Ea/A0Pp11I00xBvw==",
"license": "Apache-2.0"
},
"node_modules/@mswjs/interceptors": {
"version": "0.39.2",
"resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.39.2.tgz",
"integrity": "sha512-RuzCup9Ct91Y7V79xwCb146RaBRHZ7NBbrIUySumd1rpKqHL5OonaqrGIbug5hNwP/fRyxFMA6ISgw4FTtYFYg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@open-draft/deferred-promise": "^2.2.0",
"@open-draft/logger": "^0.3.0",
"@open-draft/until": "^2.0.0",
"is-node-process": "^1.2.0",
"outvariant": "^1.4.3",
"strict-event-emitter": "^0.5.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@napi-rs/wasm-runtime": {
"version": "0.2.8",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.8.tgz",
@ -4600,6 +4802,31 @@
"dev": true,
"license": "MIT"
},
"node_modules/@open-draft/deferred-promise": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz",
"integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==",
"dev": true,
"license": "MIT"
},
"node_modules/@open-draft/logger": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz",
"integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-node-process": "^1.2.0",
"outvariant": "^1.4.0"
}
},
"node_modules/@open-draft/until": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz",
"integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==",
"dev": true,
"license": "MIT"
},
"node_modules/@parcel/watcher": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz",
@ -6302,12 +6529,26 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/statuses": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.6.tgz",
"integrity": "sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/toastify-js": {
"version": "1.12.4",
"resolved": "https://registry.npmjs.org/@types/toastify-js/-/toastify-js-1.12.4.tgz",
"integrity": "sha512-zfZHU4tKffPCnZRe7pjv/eFKzTVHozKewFCKaCjZ4gFinKgJRz/t0bkZiMCXJxPhv/ZoeDGNOeRD09R0kQZ/nw==",
"license": "MIT"
},
"node_modules/@types/tough-cookie": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz",
"integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/trusted-types": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
@ -9636,6 +9877,16 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/cli-width": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz",
"integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">= 12"
}
},
"node_modules/clipboard": {
"version": "2.0.11",
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz",
@ -14888,6 +15139,16 @@
"license": "MIT",
"peer": true
},
"node_modules/graphql": {
"version": "16.11.0",
"resolved": "https://registry.npmjs.org/graphql/-/graphql-16.11.0.tgz",
"integrity": "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
}
},
"node_modules/handle-thing": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
@ -15161,6 +15422,13 @@
"he": "bin/he"
}
},
"node_modules/headers-polyfill": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz",
"integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==",
"dev": true,
"license": "MIT"
},
"node_modules/highlight.js": {
"version": "11.11.1",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz",
@ -16065,6 +16333,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-node-process": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz",
"integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==",
"dev": true,
"license": "MIT"
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@ -18772,6 +19047,78 @@
"dev": true,
"license": "MIT"
},
"node_modules/msw": {
"version": "2.10.2",
"resolved": "https://registry.npmjs.org/msw/-/msw-2.10.2.tgz",
"integrity": "sha512-RCKM6IZseZQCWcSWlutdf590M8nVfRHG1ImwzOtwz8IYxgT4zhUO0rfTcTvDGiaFE0Rhcc+h43lcF3Jc9gFtwQ==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@bundled-es-modules/cookie": "^2.0.1",
"@bundled-es-modules/statuses": "^1.0.1",
"@bundled-es-modules/tough-cookie": "^0.1.6",
"@inquirer/confirm": "^5.0.0",
"@mswjs/interceptors": "^0.39.1",
"@open-draft/deferred-promise": "^2.2.0",
"@open-draft/until": "^2.1.0",
"@types/cookie": "^0.6.0",
"@types/statuses": "^2.0.4",
"graphql": "^16.8.1",
"headers-polyfill": "^4.0.2",
"is-node-process": "^1.2.0",
"outvariant": "^1.4.3",
"path-to-regexp": "^6.3.0",
"picocolors": "^1.1.1",
"strict-event-emitter": "^0.5.1",
"type-fest": "^4.26.1",
"yargs": "^17.7.2"
},
"bin": {
"msw": "cli/index.js"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/mswjs"
},
"peerDependencies": {
"typescript": ">= 4.8.x"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/msw/node_modules/@types/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==",
"dev": true,
"license": "MIT"
},
"node_modules/msw/node_modules/path-to-regexp": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz",
"integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==",
"dev": true,
"license": "MIT"
},
"node_modules/msw/node_modules/type-fest": {
"version": "4.41.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
"integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
"dev": true,
"license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/multicast-dns": {
"version": "7.2.5",
"resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz",
@ -18787,6 +19134,16 @@
"multicast-dns": "cli.js"
}
},
"node_modules/mute-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz",
"integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==",
"dev": true,
"license": "ISC",
"engines": {
"node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/nan": {
"version": "2.22.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz",
@ -19421,6 +19778,13 @@
"dev": true,
"license": "MIT"
},
"node_modules/outvariant": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz",
"integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==",
"dev": true,
"license": "MIT"
},
"node_modules/p-cancelable": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-4.0.1.tgz",
@ -20601,6 +20965,29 @@
"dev": true,
"license": "ISC"
},
"node_modules/psl": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
"integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==",
"dev": true,
"license": "MIT",
"dependencies": {
"punycode": "^2.3.1"
},
"funding": {
"url": "https://github.com/sponsors/lupomontero"
}
},
"node_modules/psl/node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/public-encrypt": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
@ -23230,6 +23617,13 @@
"resolved": "git+ssh://git@github.com/nextcloud/strengthify.git#d78452649da2cd59df594a2a5c210cb7045ac899",
"license": "MIT"
},
"node_modules/strict-event-emitter": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz",
"integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==",
"dev": true,
"license": "MIT"
},
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
@ -27380,6 +27774,19 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/yoctocolors-cjs": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz",
"integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/zod": {
"version": "3.24.3",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz",

@ -170,6 +170,7 @@
"karma-spec-reporter": "^0.0.36",
"karma-viewport": "^1.0.9",
"mime": "^4.0.7",
"msw": "^2.9.0",
"puppeteer": "^24.10.0",
"raw-loader": "^4.0.2",
"regextras": "^0.8.0",

@ -3,12 +3,13 @@
"include": ["./apps/**/*.ts", "./apps/**/*.vue", "./core/**/*.ts", "./core/**/*.vue", "./*.d.ts"],
"exclude": ["./**/*.cy.ts"],
"compilerOptions": {
"lib": ["DOM", "ESNext"],
"types": ["node", "vue", "vue-router"],
"outDir": "./dist/",
"target": "ESNext",
"module": "ESNext",
// Set module resolution to bundler and `noEmit` to be able to set `allowImportingTsExtensions`, so we can import Typescript with .ts extension
"moduleResolution": "Bundler",
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"noEmit": true,
// Allow ts to import js files

Loading…
Cancel
Save