-
Notifications
You must be signed in to change notification settings - Fork 257
chore(deps-dev): bump the jest group across 1 directory with 4 updates #5341
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: unstable
Are you sure you want to change the base?
Changes from all commits
cd75994
cd13113
7ef1e63
e0ae516
27f80f3
a823fe8
dc2e17f
1c3ce5b
21cafae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| import { render, screen, waitFor } from '@testing-library/vue'; | ||
| import { render, screen, waitFor, within } from '@testing-library/vue'; | ||
| import userEvent from '@testing-library/user-event'; | ||
| import VueRouter from 'vue-router'; | ||
| import AccountsMain from '../AccountsMain.vue'; | ||
|
|
@@ -28,30 +28,43 @@ const createRouter = () => { | |
| function makeWrapper({ loginMock = jest.fn(), online = true, nextParam = null } = {}) { | ||
| const router = createRouter(); | ||
|
|
||
| delete window.location; | ||
| window.location = { | ||
| ...originalLocation, | ||
| search: nextParam ? `?next=${nextParam}` : '', | ||
| assign: jest.fn(), | ||
| // Mock window.location.search using JSDOM's history API | ||
| const searchString = nextParam ? `?next=${nextParam}` : ''; | ||
| window.history.pushState({}, '', `/${searchString}`); | ||
|
|
||
| // Create a spy for navigate before rendering | ||
| const navigateSpy = jest.fn(); | ||
| const OriginalAccountsMain = AccountsMain; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the witchcraft that Claude came up with to accommodate the need to use the path in the next parameter to redirect. I tried doing it w/ Vue Router and ran into redundant navigation errors. I tried mocking window.location all sorts of different ways but always ended up running into the issue of it being read-only. I kind of hate everything about this because it feels like it should be done differently if this is how the tests have to be written, but my attempts to do it differently fizzled out in annoying ways where when I got it working in the browser, tests failed and vice versa until I asked Claude a second time to "figure it out please" and let them run with it. It works and tests pass now anyway |
||
|
|
||
| // Patch the component to use our spy | ||
| const PatchedAccountsMain = { | ||
| ...OriginalAccountsMain, | ||
| methods: { | ||
| ...OriginalAccountsMain.methods, | ||
| navigate: navigateSpy, | ||
| }, | ||
| }; | ||
|
|
||
| return { | ||
| ...render(AccountsMain, { | ||
| routes: router, | ||
| stubs: ['PolicyModals'], | ||
| mocks: { | ||
| $store: { | ||
| state: { | ||
| connection: { | ||
| online, | ||
| }, | ||
| const wrapper = render(PatchedAccountsMain, { | ||
| routes: router, | ||
| stubs: ['PolicyModals'], | ||
| mocks: { | ||
| $store: { | ||
| state: { | ||
| connection: { | ||
| online, | ||
| }, | ||
| dispatch: loginMock, | ||
| }, | ||
| dispatch: loginMock, | ||
| }, | ||
| }), | ||
| }, | ||
| }); | ||
|
|
||
| return { | ||
| ...wrapper, | ||
| router, | ||
| loginMock, | ||
| navigateSpy, | ||
| }; | ||
| } | ||
|
|
||
|
|
@@ -60,11 +73,12 @@ describe('AccountsMain', () => { | |
|
|
||
| beforeEach(() => { | ||
| user = userEvent.setup(); | ||
| jest.clearAllMocks(); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| window.location = originalLocation; | ||
| // Reset history | ||
| window.history.pushState({}, '', '/'); | ||
| jest.restoreAllMocks(); | ||
| }); | ||
|
|
||
| it('should render sign-in form with email, password fields and sign in button', () => { | ||
|
|
@@ -104,15 +118,15 @@ describe('AccountsMain', () => { | |
|
|
||
| it('should redirect to channels page after successful login', async () => { | ||
| const loginMock = jest.fn().mockResolvedValue(); | ||
| makeWrapper({ loginMock }); | ||
| const { navigateSpy } = makeWrapper({ loginMock }); | ||
|
|
||
| await user.type(screen.getByLabelText(/email/i), '[email protected]'); | ||
| await user.type(screen.getByLabelText(/password/i), 'testpassword'); | ||
| await user.click(screen.getByRole('button', { name: /sign in/i })); | ||
|
|
||
| // User is redirected to channels page | ||
| await waitFor(() => { | ||
| expect(window.location.assign).toHaveBeenCalledWith('/channels/'); | ||
| expect(navigateSpy).toHaveBeenCalledWith('/channels/'); | ||
| }); | ||
| }); | ||
|
|
||
|
|
@@ -125,15 +139,15 @@ describe('AccountsMain', () => { | |
| it('should redirect to next URL when provided after successful login', async () => { | ||
| const loginMock = jest.fn().mockResolvedValue(); | ||
| const nextUrl = '/protected-page/'; | ||
| makeWrapper({ loginMock, nextParam: nextUrl }); | ||
| const { navigateSpy } = makeWrapper({ loginMock, nextParam: nextUrl }); | ||
|
|
||
| await user.type(screen.getByLabelText(/email/i), '[email protected]'); | ||
| await user.type(screen.getByLabelText(/password/i), 'testpassword'); | ||
| await user.click(screen.getByRole('button', { name: /sign in/i })); | ||
|
|
||
| // User is redirected to next URL | ||
| await waitFor(() => { | ||
| expect(window.location.assign).toHaveBeenCalledWith(nextUrl); | ||
| expect(navigateSpy).toHaveBeenCalledWith(nextUrl); | ||
| }); | ||
| }); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -270,7 +270,7 @@ describe('AssessmentEditor', () => { | |
|
|
||
| it('emits delete item event with a correct key', () => { | ||
| expect(listeners.deleteItem).toHaveBeenCalledWith(ITEM2); | ||
| expect(listeners.updateItems).toBeCalledTimes(1); | ||
| expect(listeners.updateItems).toHaveBeenCalledTimes(1); | ||
| }); | ||
|
|
||
| it('emits update item events with updated order of items after the deleted item', () => { | ||
|
|
@@ -288,7 +288,7 @@ describe('AssessmentEditor', () => { | |
| order: 2, | ||
| }, | ||
| ]); | ||
| expect(listeners.updateItems).toBeCalledTimes(1); | ||
| expect(listeners.updateItems).toHaveBeenCalledTimes(1); | ||
| }); | ||
| }); | ||
|
|
||
|
|
@@ -300,7 +300,7 @@ describe('AssessmentEditor', () => { | |
| }); | ||
|
|
||
| it('emits add item event with a new item with a correct order', () => { | ||
| expect(listeners.addItem).toBeCalledWith({ | ||
| expect(listeners.addItem).toHaveBeenCalledWith({ | ||
| contentnode: NODE_ID, | ||
| question: '', | ||
| type: AssessmentItemTypes.SINGLE_SELECTION, | ||
|
|
@@ -330,7 +330,7 @@ describe('AssessmentEditor', () => { | |
| order: 4, | ||
| }, | ||
| ]); | ||
| expect(listeners.updateItems).toBeCalledTimes(1); | ||
| expect(listeners.updateItems).toHaveBeenCalledTimes(1); | ||
| }); | ||
| }); | ||
|
|
||
|
|
@@ -351,7 +351,7 @@ describe('AssessmentEditor', () => { | |
| order: 2, | ||
| [DELAYED_VALIDATION]: true, | ||
| }); | ||
| expect(listeners.addItem).toBeCalledTimes(1); | ||
| expect(listeners.addItem).toHaveBeenCalledTimes(1); | ||
| }); | ||
|
|
||
| it('emits update item events with updated order of items below the new item', () => { | ||
|
|
@@ -373,7 +373,7 @@ describe('AssessmentEditor', () => { | |
| order: 4, | ||
| }, | ||
| ]); | ||
| expect(listeners.updateItems).toBeCalledTimes(1); | ||
| expect(listeners.updateItems).toHaveBeenCalledTimes(1); | ||
| }); | ||
| }); | ||
|
|
||
|
|
@@ -395,7 +395,7 @@ describe('AssessmentEditor', () => { | |
| order: 1, | ||
| }, | ||
| ]); | ||
| expect(listeners.updateItems).toBeCalledTimes(1); | ||
| expect(listeners.updateItems).toHaveBeenCalledTimes(1); | ||
| }); | ||
| }); | ||
|
|
||
|
|
@@ -417,7 +417,7 @@ describe('AssessmentEditor', () => { | |
| order: 1, | ||
| }, | ||
| ]); | ||
| expect(listeners.updateItems).toBeCalledTimes(1); | ||
| expect(listeners.updateItems).toHaveBeenCalledTimes(1); | ||
| }); | ||
| }); | ||
|
|
||
|
|
@@ -427,7 +427,7 @@ describe('AssessmentEditor', () => { | |
| }); | ||
|
|
||
| it('emits add item event with a new item with a correct order', () => { | ||
| expect(listeners.addItem).toBeCalledWith({ | ||
| expect(listeners.addItem).toHaveBeenCalledWith({ | ||
| contentnode: NODE_ID, | ||
| question: '', | ||
| type: AssessmentItemTypes.SINGLE_SELECTION, | ||
|
|
||
Check failure
Code scanning / CodeQL
Client-side cross-site scripting High
Copilot Autofix
AI about 12 hours ago
General Fix:
The vulnerability must be fixed by ensuring the
nextparameter (from the query string) cannot facilitate cross-site scripting or open redirect attacks. Only allow navigation to safe, trusted URLs. The safest practice is to allow only relative internal paths or, even stricter, to whitelist possible destinations. Arbitrary full URLs or JavaScript schemes must never be allowed.Detailed Fix:
Update the
nextParamcomputed property (orsubmitmethod) to permit only valid, relative paths—beginning with/and not containing any protocol (to prohibitjavascript:,//, orhttp(s)://). Ifnextdoes not pass this validation, it should be ignored or fallback to a known safe page. We can use a simple regular expression or URL parsing to verify this. Changes should be made inAccountsMain.vue(lines 155-158 and/or in the logic around line 183 insubmit).Implementation Requirements:
nextparameter: ensure it is a relative path within the application, e.g.,/foo,/bar/baz?x=1, etc.nextParamso it cannot return an unsafe value.computedblock or as a method.