@@ -69,8 +69,45 @@ export function cacheClients<Client extends KMS> (
69
69
const clientsCache : { [ key : string ] : Client | false } = { }
70
70
71
71
return ( region : string ) => {
72
- // undefined and false are both false ish, so check for the property
73
- if ( ! clientsCache . hasOwnProperty ( region ) ) clientsCache [ region ] = getClient ( region )
72
+ // Do not cache until KMS has been responded in the given region
73
+ if ( ! clientsCache . hasOwnProperty ( region ) ) return deferCache ( clientsCache , region , getClient ( region ) )
74
74
return clientsCache [ region ]
75
75
}
76
76
}
77
+
78
+ type KMSOperations = keyof KMS
79
+ /* It is possible that a malicious user can attempt a local resource
80
+ * DOS by sending ciphertext with a large number of spurious regions.
81
+ * This will fill the cache with regions and exhaust resources.
82
+ * To avoid this, a call succeeds in contacting KMS.
83
+ * This does *not* mean that this call is successful,
84
+ * only that the region is backed by a functional KMS service.
85
+ */
86
+ function deferCache < Client extends KMS > (
87
+ clientsCache : { [ key : string ] : Client | false } ,
88
+ region : string ,
89
+ client : Client | false
90
+ ) : Client | false {
91
+ if ( ! client ) {
92
+ clientsCache [ region ] = false
93
+ return false
94
+ }
95
+ const { encrypt, decrypt, generateDataKey } = client
96
+
97
+ return ( < KMSOperations [ ] > [ 'encrypt' , 'decrypt' , 'generateDataKey' ] ) . reduce ( wrapOperation , client )
98
+
99
+ function wrapOperation ( client : Client , name : KMSOperations ) : Client {
100
+ type params = Parameters < KMS [ typeof name ] >
101
+ type retValue = ReturnType < KMS [ typeof name ] >
102
+ const original = client [ name ]
103
+ client [ name ] = function ( ...args : params ) : retValue {
104
+ // @ts -ignore (there should be a TypeScript solution for this)
105
+ return original . apply ( client , args )
106
+ . then ( ( response : any ) => {
107
+ clientsCache [ region ] = Object . assign ( client , { encrypt, decrypt, generateDataKey } )
108
+ return response
109
+ } )
110
+ }
111
+ return client
112
+ }
113
+ }
0 commit comments