-
Notifications
You must be signed in to change notification settings - Fork 252
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
add sendPayloadChecksums config option and implement Bugsnag-Integrity header #2221
base: next
Are you sure you want to change the base?
Conversation
class FixJSDOMEnvironment extends JSDOMEnvironment { | ||
constructor (...args) { | ||
super(...args) | ||
|
||
this.global.TextEncoder = TextEncoder | ||
this.global.TextDecoder = TextDecoder | ||
this.global.crypto = { | ||
subtle: crypto.webcrypto.subtle | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More info: https://mswjs.io/docs/migrations/1.x-to-2.x/#requestresponsetextencoder-is-not-defined-jest
We could also use https://github.com/mswjs/jest-fixed-jsdom but would still need to manually add the crypto APIs
I think this is fairly simple and clear
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For future reference, when we come to upgrade jest, it will need rewriting like this:
const { TextDecoder, TextEncoder } = require('node:util')
const crypto = require('crypto')
const JSDOMEnvironment = require('jest-environment-jsdom').default
function FixJSDOMEnvironment (...args) {
var _this = Reflect.construct(JSDOMEnvironment, args)
_this.global.TextEncoder = TextEncoder
_this.global.TextDecoder = TextDecoder
Object.defineProperty(_this.global, 'crypto', {
value: {
subtle: crypto.webcrypto.subtle
}
})
return _this
}
FixJSDOMEnvironment.prototype = Object.create(JSDOMEnvironment.prototype)
FixJSDOMEnvironment.prototype.constructor = FixJSDOMEnvironment
module.exports = FixJSDOMEnvironment
This is because JSDOMEnvironment
is an ES6 class but we transpile classes in our code down to es5. This is how I managed to get it to work having an es5 class extend from an es6 class
@@ -1,19 +1,37 @@ | |||
import payload from '@bugsnag/core/lib/json-payload' | |||
|
|||
const delivery = (client, fetch = global.fetch) => ({ | |||
async function addIntegrityHeader (windowOrWorkerGlobalScope, requestBody, headers) { | |||
if (windowOrWorkerGlobalScope.isSecureContext && windowOrWorkerGlobalScope.crypto && windowOrWorkerGlobalScope.crypto.subtle && windowOrWorkerGlobalScope.crypto.subtle.digest && typeof TextEncoder === 'function') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
packages/delivery-fetch/delivery.js
Outdated
const hashBuffer = await windowOrWorkerGlobalScope.crypto.subtle.digest('SHA-1', msgUint8) | ||
const hashArray = Array.from(new Uint8Array(hashBuffer)) | ||
const hashHex = hashArray | ||
.map((b) => b.toString(16).padStart(2, '0')) | ||
.join('') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -36,7 +36,7 @@ export const Bugsnag = { | |||
// configure a client with user supplied options | |||
const bugsnag = new Client(opts, schema, internalPlugins, { name, version, url }) | |||
|
|||
bugsnag._setDelivery(delivery) | |||
bugsnag._setDelivery(client => delivery(client, undefined, self)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same way the global self
is passed to plugin, see above, e.g. pluginWindowUnhandledRejection(self)
This reverts commit c06add0.
set Bugsnag-Integrity header in delivery-xml-http-request
@@ -4,38 +4,55 @@ const DONE = window.XMLHttpRequest.DONE | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reworked the mocking in this test suite to get a callback on the xhr send where I could call done. The existing mocking wasn't able to handle the awaiting of the getIntegrity promise.
it('includes the integrity header by default', (done) => { | ||
const onSessionSend = (session: MockXHR) => { | ||
expect(session.open).toHaveBeenCalledWith('POST', 'https://sessions.bugsnag.com') | ||
expect(session.setRequestHeader).toHaveBeenCalledWith('Bugsnag-Integrity', expect.any(String)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I spent a lot of time here trying to mock out cuid to get a stable value but for reasons I don't understand jest.mock('@bugsnag/cuid')
was just not doing anything. It does work if I update jest to v29 but that causes a load of other problems elsewhere and it was too much work to upgrade for this, hence the expect.any(String)
.
Note for when we upgrade jest, a stable value can be obtained with:
jest.mock('@bugsnag/cuid', () => () => 'abc123')
jest.useFakeTimers()
jest.setSystemTime(new Date(...))
const hashArray = Array.from(new Uint8Array(hashBuffer)) | ||
const hashHex = hashArray | ||
.map((b) => b.toString(16).padStart(2, '0')) | ||
.join('') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Goal
Add Bugsnag-Integrity request header (where required APIs are available)
Design
Changeset
Bugsnag-Integrity
in delivery-fetch, which is used in@bugsnag/web-worker
.Testing