Skip to content

Commit 531ce97

Browse files
authored
fix: better dependency injection (#496)
Sinon is nice, but can not support native ES6 modules. Because these modules are immutable on import. Since the backen-factory and the synchronousRandomValues function both share the same `window` object, this can be trivially accomplished. The other advantage is that the test are _much_ simpler.
1 parent b09fd78 commit 531ce97

File tree

5 files changed

+22
-105
lines changed

5 files changed

+22
-105
lines changed

modules/web-crypto-backend/src/backend-factory.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
supportsSubtleCrypto,
88
supportsZeroByteGCM,
99
} from '@aws-crypto/supports-web-crypto'
10-
import { synchronousRandomValues as randomValues } from './synchronous_random_values'
10+
import { generateSynchronousRandomValues } from './synchronous_random_values'
1111
import promisifyMsSubtleCrypto from './promisify-ms-crypto'
1212

1313
type MaybeSubtleCrypto = SubtleCrypto | false
@@ -26,6 +26,7 @@ export type MixedSupportWebCryptoBackend = {
2626

2727
export function webCryptoBackendFactory(window: Window) {
2828
const fallbackRequiredPromise = windowRequiresFallback(window)
29+
const randomValues = generateSynchronousRandomValues(window)
2930
let webCryptoFallbackPromise: Promise<SubtleCrypto> | false = false
3031

3132
return { getWebCryptoBackend, configureFallback }

modules/web-crypto-backend/src/synchronous_random_values.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,22 @@ import { locateWindow } from '@aws-sdk/util-locate-window'
99
* For example constructors need to be synchronous.
1010
* The AWS JS SDK uses IRandomValues to have a consistent interface.
1111
*/
12-
export function synchronousRandomValues(byteLength: number): Uint8Array {
13-
// Find the global scope for this runtime
14-
const globalScope = locateWindow()
12+
export const synchronousRandomValues = generateSynchronousRandomValues(
13+
locateWindow()
14+
)
1515

16-
if (supportsSecureRandom(globalScope)) {
17-
return globalScope.crypto.getRandomValues(new Uint8Array(byteLength))
18-
} else if (isMsWindow(globalScope)) {
19-
const values = new Uint8Array(byteLength)
20-
globalScope.msCrypto.getRandomValues(values)
21-
return values
22-
}
16+
export function generateSynchronousRandomValues(
17+
globalScope: Window
18+
): (byteLength: number) => Uint8Array {
19+
return function synchronousRandomValues(byteLength: number): Uint8Array {
20+
if (supportsSecureRandom(globalScope)) {
21+
return globalScope.crypto.getRandomValues(new Uint8Array(byteLength))
22+
} else if (isMsWindow(globalScope)) {
23+
const values = new Uint8Array(byteLength)
24+
globalScope.msCrypto.getRandomValues(values)
25+
return values
26+
}
2327

24-
throw new Error(`Unable to locate a secure random source.`)
28+
throw new Error(`Unable to locate a secure random source.`)
29+
}
2530
}

modules/web-crypto-backend/test/backend-factory.test.ts

Lines changed: 0 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import * as chai from 'chai'
77
import chaiAsPromised from 'chai-as-promised'
8-
import sinon from 'sinon'
98
// import 'mocha'
109
import {
1110
pluckSubtleCrypto,
@@ -14,7 +13,6 @@ import {
1413
getNonZeroByteBackend,
1514
getZeroByteSubtle,
1615
} from '../src/backend-factory'
17-
import * as browserWindow from '@aws-sdk/util-locate-window'
1816

1917
import * as fixtures from './fixtures'
2018
chai.use(chaiAsPromised)
@@ -55,47 +53,28 @@ describe('windowRequiresFallback', () => {
5553
describe('webCryptoBackendFactory', () => {
5654
describe('configureFallback', () => {
5755
it('returns a valid and configured fallback.', async () => {
58-
const { locateWindow } = browserWindow
59-
sinon
60-
.stub(browserWindow, 'locateWindow')
61-
.returns(fixtures.fakeWindowNoWebCrypto)
62-
6356
const { configureFallback } = webCryptoBackendFactory(
6457
fixtures.fakeWindowNoWebCrypto
6558
)
6659
const test = await configureFallback(
6760
fixtures.subtleFallbackSupportsZeroByteGCM
6861
)
6962

70-
// @ts-ignore
71-
browserWindow.locateWindow = locateWindow
7263
expect(test === fixtures.subtleFallbackSupportsZeroByteGCM).to.equal(true)
7364
})
7465

7566
it('Precondition: If a fallback is not required, do not configure one.', async () => {
76-
const { locateWindow } = browserWindow
77-
sinon
78-
.stub(browserWindow, 'locateWindow')
79-
.returns(fixtures.fakeWindowWebCryptoSupportsZeroByteGCM)
80-
8167
const { configureFallback } = webCryptoBackendFactory(
8268
fixtures.fakeWindowWebCryptoSupportsZeroByteGCM
8369
)
8470
const test = await configureFallback(
8571
fixtures.subtleFallbackSupportsZeroByteGCM
8672
)
87-
// @ts-ignore
88-
browserWindow.locateWindow = locateWindow
8973

9074
expect(test).to.equal(undefined)
9175
})
9276

9377
it('Precondition: Can not reconfigure fallback.', async () => {
94-
const { locateWindow } = browserWindow
95-
sinon
96-
.stub(browserWindow, 'locateWindow')
97-
.returns(fixtures.fakeWindowWebCryptoOnlyRandomSource)
98-
9978
const { configureFallback } = webCryptoBackendFactory(
10079
fixtures.fakeWindowWebCryptoOnlyRandomSource
10180
)
@@ -104,61 +83,35 @@ describe('webCryptoBackendFactory', () => {
10483
await expect(
10584
configureFallback(fixtures.subtleFallbackSupportsZeroByteGCM)
10685
).to.rejectedWith(Error)
107-
108-
// @ts-ignore
109-
browserWindow.locateWindow = locateWindow
11086
})
11187

11288
it('Precondition: Fallback must look like it supports the required operations.', async () => {
113-
const { locateWindow } = browserWindow
114-
sinon
115-
.stub(browserWindow, 'locateWindow')
116-
.returns(fixtures.fakeWindowWebCryptoOnlyRandomSource)
117-
11889
const { configureFallback } = webCryptoBackendFactory(
11990
fixtures.fakeWindowWebCryptoOnlyRandomSource
12091
)
12192

12293
await expect(
12394
configureFallback(fixtures.subtleFallbackNoWebCrypto)
12495
).to.rejectedWith(Error)
125-
126-
// @ts-ignore
127-
browserWindow.locateWindow = locateWindow
12896
})
12997

13098
it('Postcondition: The fallback must specifically support ZeroByteGCM.', async () => {
131-
const { locateWindow } = browserWindow
132-
sinon
133-
.stub(browserWindow, 'locateWindow')
134-
.returns(fixtures.fakeWindowWebCryptoOnlyRandomSource)
135-
13699
const { configureFallback } = webCryptoBackendFactory(
137100
fixtures.fakeWindowWebCryptoOnlyRandomSource
138101
)
139102

140103
await expect(
141104
configureFallback(fixtures.subtleFallbackZeroByteEncryptFail)
142105
).to.rejectedWith(Error)
143-
144-
// @ts-ignore
145-
browserWindow.locateWindow = locateWindow
146106
})
147107
})
148108

149109
describe('getWebCryptoBackend', () => {
150110
it('getWebCryptoBackend returns subtle and randomValues', async () => {
151-
const { locateWindow } = browserWindow
152-
sinon
153-
.stub(browserWindow, 'locateWindow')
154-
.returns(fixtures.fakeWindowWebCryptoSupportsZeroByteGCM)
155-
156111
const { getWebCryptoBackend } = webCryptoBackendFactory(
157112
fixtures.fakeWindowWebCryptoSupportsZeroByteGCM
158113
)
159114
const test = await getWebCryptoBackend()
160-
// @ts-ignore
161-
browserWindow.locateWindow = locateWindow
162115

163116
expect(test)
164117
.to.have.property('subtle')
@@ -169,54 +122,27 @@ describe('webCryptoBackendFactory', () => {
169122
})
170123

171124
it('Precondition: Access to a secure random source is required.', async () => {
172-
const { locateWindow } = browserWindow
173-
sinon
174-
.stub(browserWindow, 'locateWindow')
175-
.returns(fixtures.fakeWindowNoWebCrypto)
176-
177125
const { getWebCryptoBackend } = webCryptoBackendFactory(
178126
fixtures.fakeWindowNoWebCrypto
179127
)
180128
await expect(getWebCryptoBackend()).to.rejectedWith(Error)
181-
182-
// @ts-ignore
183-
browserWindow.locateWindow = locateWindow
184129
})
185130

186131
it('Postcondition: If no SubtleCrypto exists, a fallback must configured.', async () => {
187-
const { locateWindow } = browserWindow
188-
sinon
189-
.stub(browserWindow, 'locateWindow')
190-
.returns(fixtures.fakeWindowWebCryptoOnlyRandomSource)
191-
192132
const { getWebCryptoBackend } = webCryptoBackendFactory(
193133
fixtures.fakeWindowWebCryptoOnlyRandomSource
194134
)
195135
await expect(getWebCryptoBackend()).to.rejectedWith(Error)
196-
// @ts-ignore
197-
browserWindow.locateWindow = locateWindow
198136
})
199137

200138
it('Postcondition: If a a subtle backend exists and a fallback is required, one must be configured.', async () => {
201-
const { locateWindow } = browserWindow
202-
sinon
203-
.stub(browserWindow, 'locateWindow')
204-
.returns(fixtures.fakeWindowWebCryptoZeroByteEncryptFail)
205-
206139
const { getWebCryptoBackend } = webCryptoBackendFactory(
207140
fixtures.fakeWindowWebCryptoZeroByteEncryptFail
208141
)
209142
await expect(getWebCryptoBackend()).to.rejectedWith(Error)
210-
// @ts-ignore
211-
browserWindow.locateWindow = locateWindow
212143
})
213144

214145
it('getWebCryptoBackend returns configured fallback subtle and randomValues', async () => {
215-
const { locateWindow } = browserWindow
216-
sinon
217-
.stub(browserWindow, 'locateWindow')
218-
.returns(fixtures.fakeWindowWebCryptoOnlyRandomSource)
219-
220146
const {
221147
getWebCryptoBackend,
222148
configureFallback,
@@ -226,8 +152,6 @@ describe('webCryptoBackendFactory', () => {
226152
// I _also_ test its ability to await the configuration.
227153
configureFallback(fixtures.subtleFallbackSupportsZeroByteGCM) // eslint-disable-line @typescript-eslint/no-floating-promises
228154
const test = await getWebCryptoBackend()
229-
// @ts-ignore
230-
browserWindow.locateWindow = locateWindow
231155

232156
expect(test)
233157
.to.have.property('subtle')
@@ -236,11 +160,6 @@ describe('webCryptoBackendFactory', () => {
236160
})
237161

238162
it('getWebCryptoBackend returns MixedSupportWebCryptoBackend', async () => {
239-
const { locateWindow } = browserWindow
240-
sinon
241-
.stub(browserWindow, 'locateWindow')
242-
.returns(fixtures.fakeWindowWebCryptoZeroByteEncryptFail)
243-
244163
const {
245164
getWebCryptoBackend,
246165
configureFallback,
@@ -252,8 +171,6 @@ describe('webCryptoBackendFactory', () => {
252171
// I _also_ test its ability to await the configuration.
253172
configureFallback(fixtures.subtleFallbackSupportsZeroByteGCM) // eslint-disable-line @typescript-eslint/no-floating-promises
254173
const test = await getWebCryptoBackend()
255-
// @ts-ignore
256-
browserWindow.locateWindow = locateWindow
257174

258175
expect(test)
259176
.to.have.property('nonZeroByteSubtle')

modules/web-crypto-backend/test/synchronous_random_values.test.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44
/* eslint-env mocha */
55

66
import { expect } from 'chai'
7+
import { generateSynchronousRandomValues } from '../src/synchronous_random_values'
78
import { synchronousRandomValues } from '../src/index'
8-
import sinon from 'sinon'
9-
import * as browserWindow from '@aws-sdk/util-locate-window'
109
import * as fixtures from './fixtures'
1110

1211
describe('synchronousRandomValues', () => {
@@ -17,18 +16,14 @@ describe('synchronousRandomValues', () => {
1716
})
1817

1918
it('should return msCrypto random values', () => {
20-
const { locateWindow } = browserWindow
21-
sinon
22-
.stub(browserWindow, 'locateWindow')
23-
.returns(fixtures.fakeWindowIE11OnComplete)
19+
const synchronousRandomValues = generateSynchronousRandomValues(
20+
fixtures.fakeWindowIE11OnComplete
21+
)
2422

2523
const test = synchronousRandomValues(5)
2624
expect(test).to.be.instanceOf(Uint8Array)
2725
expect(test).lengthOf(5)
2826
// The random is a stub, so I know the value
2927
expect(test).to.deep.equal(new Uint8Array(5).fill(1))
30-
31-
// @ts-ignore
32-
browserWindow.locateWindow = locateWindow
3328
})
3429
})

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,6 @@
125125
"nyc": "^15.1.0",
126126
"prettier": "^2.0.4",
127127
"rimraf": "^3.0.2",
128-
"sinon": "^9.0.2",
129128
"source-map-support": "^0.5.19",
130129
"ts-loader": "^8.0.6",
131130
"ts-node": "^8.10.2",

0 commit comments

Comments
 (0)