Skip to content

Commit

Permalink
fix(XMLHttpRequest): prevent invalid listener name access (#472)
Browse files Browse the repository at this point in the history
  • Loading branch information
hpohlmeyer authored Nov 7, 2023
1 parent 3258f5c commit fb8a400
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/Interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ export class Interceptor<Events extends InterceptorEventMap> {
return this
}

logger.info('adding "%s" event listener:', event, listener.name)
logger.info('adding "%s" event listener:', event, listener)

this.emitter.on(event, listener)
return this
Expand Down
4 changes: 2 additions & 2 deletions src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export class XMLHttpRequestController {
]

this.registerEvent(eventName, listener)
this.logger.info('addEventListener', eventName, listener.name)
this.logger.info('addEventListener', eventName, listener)

return invoke()
}
Expand Down Expand Up @@ -206,7 +206,7 @@ export class XMLHttpRequestController {
const nextEvents = prevEvents.concat(listener)
this.events.set(eventName, nextEvents)

this.logger.info('registered event "%s"', eventName, listener.name)
this.logger.info('registered event "%s"', eventName, listener)
}

/**
Expand Down
130 changes: 130 additions & 0 deletions test/modules/XMLHttpRequest/compliance/xhr-event-callback-null.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/**
* @note https://xhr.spec.whatwg.org/#event-handlers
*/
// @vitest-environment jsdom
import { it, expect, beforeAll, afterAll } from 'vitest'
import { HttpServer } from '@open-draft/test-server/http'
import { XMLHttpRequestInterceptor } from '../../../../src/interceptors/XMLHttpRequest'
import { createXMLHttpRequest } from '../../../helpers'

const interceptor = new XMLHttpRequestInterceptor()

const httpServer = new HttpServer((app) => {
app.get('/resource', (req, res) => {
res.send('hello')
})
app.get('/error', (req, res) => {
res.status(500).send('Internal Server Error')
})
app.get('/exception', (req, res) => {
throw new Error('Server error')
})
})

beforeAll(async () => {
interceptor.apply()
interceptor.on('request', ({ request }) => {
switch (true) {
case request.url.endsWith('/exception'): {
throw new Error('Network error')
}

case request.url.endsWith('/error'): {
return request.respondWith(
new Response('Internal Server Error', { status: 500 })
)
}

default:
return request.respondWith(new Response('hello'))
}
})

await httpServer.listen()
})

afterAll(async () => {
interceptor.dispose()
await httpServer.close()
})

it.each<[name: string, getUrl: () => string]>([
['passthrough', () => httpServer.https.url('/resource')],
['mocked', () => 'http://localhost/resource'],
])(
`does not fail when unsetting event handlers for a successful %s response`,
async (_, getUrl) => {
const url = getUrl()

const request = await createXMLHttpRequest((request) => {
request.open('GET', url)

request.onreadystatechange = null
request.onloadstart = null
request.onprogress = null
request.onload = null
request.onloadend = null
request.ontimeout = null

request.send()
})

expect(request.readyState).toBe(4)
expect(request.status).toBe(200)
expect(request.responseText).toBe('hello')
}
)

it.each<[name: string, getUrl: () => string]>([
['passthrough', () => httpServer.https.url('/error')],
['mocked', () => 'http://localhost/error'],
])(
`does not fail when unsetting event handlers for a %s error response`,
async (_, getUrl) => {
const url = getUrl()

const request = await createXMLHttpRequest((request) => {
request.open('GET', url)

request.onreadystatechange = null
request.onloadstart = null
request.onprogress = null
request.onload = null
request.onloadend = null
request.ontimeout = null

request.send()
})

expect(request.readyState).toBe(4)
expect(request.status).toBe(500)
expect(request.responseText).toBe('Internal Server Error')
}
)

it.each<[name: string, getUrl: () => string]>([
['passthrough', () => httpServer.https.url('/exception')],
['mocked', () => 'http://localhost/exception'],
])(
`does not fail when unsetting event handlers for a %s request error`,
async (_, getUrl) => {
const url = getUrl()

const request = await createXMLHttpRequest((request) => {
request.open('GET', url)

request.onreadystatechange = null
request.onloadstart = null
request.onprogress = null
request.onload = null
request.onloadend = null
request.ontimeout = null

request.send()
})

expect(request.readyState).toBe(4)
expect(request.status).toBe(0)
expect(request.responseText).toBe('')
}
)

0 comments on commit fb8a400

Please sign in to comment.